diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md index 4c9ff55526..2fd3687db2 100644 --- a/.github/ISSUE_TEMPLATE.md +++ b/.github/ISSUE_TEMPLATE.md @@ -3,7 +3,7 @@ I acknowledge that: - I have updated: - - To the latest version of the app (stable is v0.10.11) + - To the latest version of the app (stable is v0.10.12) - All extensions - I have tried the troubleshooting guide: https://tachiyomi.org/help/guides/troubleshooting-problems/ - If this is an issue with an extension, that I should be opening an issue in https://github.com/tachiyomiorg/tachiyomi-extensions diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index 084d65863b..2f1066bd9a 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -10,7 +10,7 @@ labels: "bug" I acknowledge that: - I have updated: - - To the latest version of the app (stable is v0.10.11) + - To the latest version of the app (stable is v0.10.12) - All extensions - I have tried the troubleshooting guide: https://tachiyomi.org/help/guides/troubleshooting-problems/ - If this is an issue with an extension, that I should be opening an issue in https://github.com/tachiyomiorg/tachiyomi-extensions diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md index 27f3d04cce..e868274e4c 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -10,7 +10,7 @@ labels: "feature" I acknowledge that: - I have updated: - - To the latest version of the app (stable is v0.10.11) + - To the latest version of the app (stable is v0.10.12) - All extensions - If this is an issue with an extension, that I should be opening an issue in https://github.com/tachiyomiorg/tachiyomi-extensions - I have searched the existing issues and this is new ticket **NOT** a duplicate or related to another open issue diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 875e771385..242e1aed72 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -29,8 +29,8 @@ android { minSdkVersion(AndroidConfig.minSdk) targetSdkVersion(AndroidConfig.targetSdk) testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" - versionCode = 58 - versionName = "0.10.11" + versionCode = 60 + versionName = "0.10.12" buildConfigField("String", "COMMIT_COUNT", "\"${getCommitCount()}\"") buildConfigField("String", "COMMIT_SHA", "\"${getGitSha()}\"") @@ -163,7 +163,7 @@ dependencies { implementation("org.conscrypt:conscrypt-android:2.5.1") // JSON - val kotlinSerializationVersion = "1.1.0" + val kotlinSerializationVersion = "1.2.0" implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:$kotlinSerializationVersion") implementation("org.jetbrains.kotlinx:kotlinx-serialization-protobuf:$kotlinSerializationVersion") implementation("com.google.code.gson:gson:2.8.6") @@ -184,7 +184,7 @@ dependencies { implementation("androidx.sqlite:sqlite-ktx:2.1.0") implementation("com.github.inorichi.storio:storio-common:8be19de@aar") implementation("com.github.inorichi.storio:storio-sqlite:8be19de@aar") - implementation("io.requery:sqlite-android:3.33.0") + implementation("com.github.requery:sqlite-android:3.35.5") // Preferences implementation("com.github.tfcporciuncula.flow-preferences:flow-preferences:1.3.4") @@ -198,10 +198,9 @@ dependencies { implementation("com.github.inorichi.injekt:injekt-core:65b0440") // Image library - val glideVersion = "4.12.0" - implementation("com.github.bumptech.glide:glide:$glideVersion") - implementation("com.github.bumptech.glide:okhttp3-integration:$glideVersion") - kapt("com.github.bumptech.glide:compiler:$glideVersion") + val coilVersion = "1.2.0" + implementation("io.coil-kt:coil:$coilVersion") + implementation("io.coil-kt:coil-gif:$coilVersion") implementation("com.github.tachiyomiorg:subsampling-scale-image-view:547d9c0") @@ -278,7 +277,8 @@ tasks { "-Xuse-experimental=kotlinx.coroutines.FlowPreview", "-Xuse-experimental=kotlinx.coroutines.ExperimentalCoroutinesApi", "-Xuse-experimental=kotlinx.coroutines.InternalCoroutinesApi", - "-Xuse-experimental=kotlinx.serialization.ExperimentalSerializationApi" + "-Xuse-experimental=kotlinx.serialization.ExperimentalSerializationApi", + "-Xuse-experimental=coil.annotation.ExperimentalCoilApi", ) } diff --git a/app/src/main/java/eu/kanade/tachiyomi/App.kt b/app/src/main/java/eu/kanade/tachiyomi/App.kt index 6f236a0d7f..c8fefff864 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/App.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/App.kt @@ -1,16 +1,25 @@ package eu.kanade.tachiyomi +import android.app.ActivityManager import android.app.Application import android.content.Context import android.content.res.Configuration import android.os.Build +import androidx.core.content.getSystemService import androidx.lifecycle.Lifecycle import androidx.lifecycle.LifecycleObserver import androidx.lifecycle.OnLifecycleEvent import androidx.lifecycle.ProcessLifecycleOwner import androidx.multidex.MultiDex +import coil.ImageLoader +import coil.ImageLoaderFactory +import coil.decode.GifDecoder +import coil.decode.ImageDecoderDecoder +import eu.kanade.tachiyomi.data.coil.ByteBufferFetcher +import eu.kanade.tachiyomi.data.coil.MangaCoverFetcher import eu.kanade.tachiyomi.data.notification.Notifications import eu.kanade.tachiyomi.data.preference.PreferencesHelper +import eu.kanade.tachiyomi.network.NetworkHelper import eu.kanade.tachiyomi.ui.security.SecureActivityDelegate import eu.kanade.tachiyomi.util.system.LocaleHelper import org.acra.ACRA @@ -20,6 +29,7 @@ import org.acra.sender.HttpSender import org.conscrypt.Conscrypt import timber.log.Timber import uy.kohesive.injekt.Injekt +import uy.kohesive.injekt.api.get import uy.kohesive.injekt.injectLazy import java.security.Security @@ -31,7 +41,7 @@ import java.security.Security uri = BuildConfig.ACRA_URI, httpMethod = HttpSender.Method.PUT ) -open class App : Application(), LifecycleObserver { +open class App : Application(), LifecycleObserver, ImageLoaderFactory { private val preferences: PreferencesHelper by injectLazy() @@ -67,6 +77,23 @@ open class App : Application(), LifecycleObserver { LocaleHelper.updateConfiguration(this, newConfig, true) } + override fun newImageLoader(): ImageLoader { + return ImageLoader.Builder(this).apply { + componentRegistry { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { + add(ImageDecoderDecoder(this@App)) + } else { + add(GifDecoder()) + } + add(ByteBufferFetcher()) + add(MangaCoverFetcher()) + } + okHttpClient(Injekt.get().coilClient) + crossfade(300) + allowRgb565(getSystemService()!!.isLowRamDevice) + }.build() + } + @OnLifecycleEvent(Lifecycle.Event.ON_STOP) @Suppress("unused") fun onAppBackgrounded() { diff --git a/app/src/main/java/eu/kanade/tachiyomi/Migrations.kt b/app/src/main/java/eu/kanade/tachiyomi/Migrations.kt index dcb4146667..a693d74968 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/Migrations.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/Migrations.kt @@ -1,5 +1,6 @@ package eu.kanade.tachiyomi +import android.os.Build import androidx.core.content.edit import androidx.preference.PreferenceManager import eu.kanade.tachiyomi.data.backup.BackupCreatorJob @@ -11,6 +12,7 @@ import eu.kanade.tachiyomi.data.updater.UpdaterJob import eu.kanade.tachiyomi.extension.ExtensionUpdateJob import eu.kanade.tachiyomi.network.PREF_DOH_CLOUDFLARE import eu.kanade.tachiyomi.ui.library.LibrarySort +import eu.kanade.tachiyomi.ui.reader.setting.OrientationType import eu.kanade.tachiyomi.util.system.toast import eu.kanade.tachiyomi.widget.ExtendedNavigationView import uy.kohesive.injekt.Injekt @@ -141,7 +143,44 @@ object Migrations { } if (oldVersion < 59) { // Reset rotation to Free after replacing Lock - preferences.rotation().set(1) + val prefs = PreferenceManager.getDefaultSharedPreferences(context) + if (prefs.contains("pref_rotation_type_key")) { + prefs.edit { + putInt("pref_rotation_type_key", 1) + } + } + + // Disable update check for Android 5.x users + if (BuildConfig.INCLUDE_UPDATER && Build.VERSION.SDK_INT <= Build.VERSION_CODES.M) { + UpdaterJob.cancelTask(context) + } + } + if (oldVersion < 60) { + // Re-enable update check that was prevously accidentally disabled for M + if (BuildConfig.INCLUDE_UPDATER && Build.VERSION.SDK_INT == Build.VERSION_CODES.M) { + UpdaterJob.setupTask(context) + } + + // Migrate Rotation and Viewer values to default values for viewer_flags + val prefs = PreferenceManager.getDefaultSharedPreferences(context) + val newOrientation = when (prefs.getInt("pref_rotation_type_key", 1)) { + 1 -> OrientationType.FREE.flagValue + 2 -> OrientationType.PORTRAIT.flagValue + 3 -> OrientationType.LANDSCAPE.flagValue + 4 -> OrientationType.LOCKED_PORTRAIT.flagValue + 5 -> OrientationType.LOCKED_LANDSCAPE.flagValue + else -> OrientationType.FREE.flagValue + } + + // Reading mode flag and prefValue is the same value + val newReadingMode = prefs.getInt("pref_default_viewer_key", 1) + + prefs.edit { + putInt("pref_default_orientation_type_key", newOrientation) + remove("pref_rotation_type_key") + putInt("pref_default_reading_mode_key", newReadingMode) + remove("pref_default_viewer_key") + } } return true } diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/backup/full/models/BackupManga.kt b/app/src/main/java/eu/kanade/tachiyomi/data/backup/full/models/BackupManga.kt index 8e863ad745..77013480bc 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/backup/full/models/BackupManga.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/backup/full/models/BackupManga.kt @@ -25,7 +25,7 @@ data class BackupManga( // @ProtoNumber(11) val lastUpdate: Long = 0, 1.x value, not used in 0.x // @ProtoNumber(12) val lastInit: Long = 0, 1.x value, not used in 0.x @ProtoNumber(13) var dateAdded: Long = 0, - @ProtoNumber(14) var viewer: Int = 0, + @ProtoNumber(14) var viewer: Int = 0, // Replaced by viewer_flags // @ProtoNumber(15) val flags: Int = 0, 1.x value, not used in 0.x @ProtoNumber(16) var chapters: List = emptyList(), @ProtoNumber(17) var categories: List = emptyList(), @@ -34,6 +34,7 @@ data class BackupManga( @ProtoNumber(100) var favorite: Boolean = true, @ProtoNumber(101) var chapterFlags: Int = 0, @ProtoNumber(102) var history: List = emptyList(), + @ProtoNumber(103) var viewer_flags: Int? = null ) { fun getMangaImpl(): MangaImpl { return MangaImpl().apply { @@ -48,7 +49,7 @@ data class BackupManga( favorite = this@BackupManga.favorite source = this@BackupManga.source date_added = this@BackupManga.dateAdded - viewer = this@BackupManga.viewer + viewer_flags = this@BackupManga.viewer_flags ?: this@BackupManga.viewer chapter_flags = this@BackupManga.chapterFlags } } @@ -79,7 +80,8 @@ data class BackupManga( favorite = manga.favorite, source = manga.source, dateAdded = manga.date_added, - viewer = manga.viewer, + viewer = manga.readingModeType, + viewer_flags = manga.viewer_flags, chapterFlags = manga.chapter_flags ) } diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/backup/legacy/serializer/MangaTypeAdapter.kt b/app/src/main/java/eu/kanade/tachiyomi/data/backup/legacy/serializer/MangaTypeAdapter.kt index b902cbb5b7..aaab4c76ff 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/backup/legacy/serializer/MangaTypeAdapter.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/backup/legacy/serializer/MangaTypeAdapter.kt @@ -16,7 +16,7 @@ object MangaTypeAdapter { value(it.url) value(it.title) value(it.source) - value(it.viewer) + value(it.viewer_flags) value(it.chapter_flags) endArray() } @@ -27,7 +27,7 @@ object MangaTypeAdapter { manga.url = nextString() manga.title = nextString() manga.source = nextLong() - manga.viewer = nextInt() + manga.viewer_flags = nextInt() manga.chapter_flags = nextInt() endArray() manga diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/cache/CoverCache.kt b/app/src/main/java/eu/kanade/tachiyomi/data/cache/CoverCache.kt index 4d1a006070..fbdfc52bd0 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/cache/CoverCache.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/cache/CoverCache.kt @@ -1,6 +1,7 @@ package eu.kanade.tachiyomi.data.cache import android.content.Context +import coil.imageLoader import eu.kanade.tachiyomi.data.database.models.Manga import eu.kanade.tachiyomi.util.storage.DiskUtil import java.io.File @@ -99,6 +100,13 @@ class CoverCache(private val context: Context) { } } + /** + * Clear coil's memory cache. + */ + fun clearMemoryCache() { + context.imageLoader.memoryCache.clear() + } + private fun getCacheDir(dir: String): File { return context.getExternalFilesDir(dir) ?: File(context.filesDir, dir).also { it.mkdirs() } diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/coil/ByteBufferFetcher.kt b/app/src/main/java/eu/kanade/tachiyomi/data/coil/ByteBufferFetcher.kt new file mode 100644 index 0000000000..78d9c7ecd3 --- /dev/null +++ b/app/src/main/java/eu/kanade/tachiyomi/data/coil/ByteBufferFetcher.kt @@ -0,0 +1,25 @@ +package eu.kanade.tachiyomi.data.coil + +import coil.bitmap.BitmapPool +import coil.decode.DataSource +import coil.decode.Options +import coil.fetch.FetchResult +import coil.fetch.Fetcher +import coil.fetch.SourceResult +import coil.size.Size +import okio.buffer +import okio.source +import java.io.ByteArrayInputStream +import java.nio.ByteBuffer + +class ByteBufferFetcher : Fetcher { + override suspend fun fetch(pool: BitmapPool, data: ByteBuffer, size: Size, options: Options): FetchResult { + return SourceResult( + source = ByteArrayInputStream(data.array()).source().buffer(), + mimeType = null, + dataSource = DataSource.MEMORY + ) + } + + override fun key(data: ByteBuffer): String? = null +} diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/coil/MangaCoverFetcher.kt b/app/src/main/java/eu/kanade/tachiyomi/data/coil/MangaCoverFetcher.kt new file mode 100644 index 0000000000..6aae106cd8 --- /dev/null +++ b/app/src/main/java/eu/kanade/tachiyomi/data/coil/MangaCoverFetcher.kt @@ -0,0 +1,172 @@ +package eu.kanade.tachiyomi.data.coil + +import coil.bitmap.BitmapPool +import coil.decode.DataSource +import coil.decode.Options +import coil.fetch.FetchResult +import coil.fetch.Fetcher +import coil.fetch.SourceResult +import coil.network.HttpException +import coil.request.get +import coil.size.Size +import eu.kanade.tachiyomi.data.cache.CoverCache +import eu.kanade.tachiyomi.data.database.models.Manga +import eu.kanade.tachiyomi.network.NetworkHelper +import eu.kanade.tachiyomi.network.await +import eu.kanade.tachiyomi.source.SourceManager +import eu.kanade.tachiyomi.source.online.HttpSource +import okhttp3.CacheControl +import okhttp3.Call +import okhttp3.Request +import okhttp3.Response +import okhttp3.ResponseBody +import okio.buffer +import okio.sink +import okio.source +import uy.kohesive.injekt.Injekt +import uy.kohesive.injekt.api.get +import uy.kohesive.injekt.injectLazy +import java.io.File +import java.util.Date + +/** + * Coil component that fetches [Manga] cover while using the cached file in disk when available. + * + * Available request parameter: + * - [USE_CUSTOM_COVER]: Use custom cover if set by user, default is true + */ +class MangaCoverFetcher : Fetcher { + private val coverCache: CoverCache by injectLazy() + private val sourceManager: SourceManager by injectLazy() + private val defaultClient = Injekt.get().coilClient + + override fun key(data: Manga): String? { + if (data.thumbnail_url.isNullOrBlank()) return null + return data.thumbnail_url!! + } + + override suspend fun fetch(pool: BitmapPool, data: Manga, size: Size, options: Options): FetchResult { + // Use custom cover if exists + val useCustomCover = options.parameters[USE_CUSTOM_COVER] as? Boolean ?: true + val customCoverFile = coverCache.getCustomCoverFile(data) + if (useCustomCover && customCoverFile.exists()) { + return fileLoader(customCoverFile) + } + + val cover = data.thumbnail_url + return when (getResourceType(cover)) { + Type.URL -> httpLoader(data, options) + Type.File -> fileLoader(data) + null -> error("Invalid image") + } + } + + private suspend fun httpLoader(manga: Manga, options: Options): FetchResult { + val coverFile = coverCache.getCoverFile(manga) ?: error("No cover specified") + + // Use previously cached cover if exist + if (coverFile.exists() && options.diskCachePolicy.readEnabled) { + if (!manga.favorite) { + coverFile.setLastModified(Date().time) + } + return fileLoader(coverFile) + } + + val (response, body) = awaitGetCall(manga, options) + if (!response.isSuccessful) { + body.close() + throw HttpException(response) + } + + // Write to disk for future use + if (options.diskCachePolicy.writeEnabled) { + response.peekBody(Long.MAX_VALUE).source().use { input -> + val tmpFile = File(coverFile.absolutePath + "_tmp") + tmpFile.parentFile?.mkdirs() + tmpFile.sink().buffer().use { output -> + output.writeAll(input) + } + if (coverFile.exists()) { + coverFile.delete() + } + tmpFile.renameTo(coverFile) + } + } + + return SourceResult( + source = body.source(), + mimeType = "image/*", + dataSource = if (response.cacheResponse != null) DataSource.DISK else DataSource.NETWORK + ) + } + + private suspend fun awaitGetCall(manga: Manga, options: Options): Pair { + val call = getCall(manga, options) + val response = call.await() + return response to checkNotNull(response.body) { "Null response source" } + } + + private fun getCall(manga: Manga, options: Options): Call { + val source = sourceManager.get(manga.source) as? HttpSource + val client = source?.client ?: defaultClient + + val newClient = client.newBuilder().build() + + val request = Request.Builder().url(manga.thumbnail_url!!).also { + if (source != null) { + it.headers(source.headers) + } + + val networkRead = options.networkCachePolicy.readEnabled + val diskRead = options.diskCachePolicy.readEnabled + when { + !networkRead && diskRead -> { + it.cacheControl(CacheControl.FORCE_CACHE) + } + networkRead && !diskRead -> if (options.diskCachePolicy.writeEnabled) { + it.cacheControl(CacheControl.FORCE_NETWORK) + } else { + it.cacheControl(CACHE_CONTROL_FORCE_NETWORK_NO_CACHE) + } + !networkRead && !diskRead -> { + // This causes the request to fail with a 504 Unsatisfiable Request. + it.cacheControl(CACHE_CONTROL_NO_NETWORK_NO_CACHE) + } + } + }.build() + + return newClient.newCall(request) + } + + private fun fileLoader(manga: Manga): FetchResult { + return fileLoader(File(manga.thumbnail_url!!.substringAfter("file://"))) + } + + private fun fileLoader(file: File): FetchResult { + return SourceResult( + source = file.source().buffer(), + mimeType = "image/*", + dataSource = DataSource.DISK + ) + } + + private fun getResourceType(cover: String?): Type? { + return when { + cover.isNullOrEmpty() -> null + cover.startsWith("http") || cover.startsWith("Custom-", true) -> Type.URL + cover.startsWith("/") || cover.startsWith("file://") -> Type.File + else -> null + } + } + + private enum class Type { + File, URL + } + + companion object { + const val USE_CUSTOM_COVER = "use_custom_cover" + + private val CACHE_CONTROL_FORCE_NETWORK_NO_CACHE = CacheControl.Builder().noCache().noStore().build() + private val CACHE_CONTROL_NO_NETWORK_NO_CACHE = CacheControl.Builder().noCache().onlyIfCached().build() + } +} diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/database/mappers/MangaTypeMapping.kt b/app/src/main/java/eu/kanade/tachiyomi/data/database/mappers/MangaTypeMapping.kt index 37ea8cb297..7392a170ac 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/database/mappers/MangaTypeMapping.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/database/mappers/MangaTypeMapping.kt @@ -63,7 +63,7 @@ class MangaPutResolver : DefaultPutResolver() { COL_FAVORITE to obj.favorite, COL_LAST_UPDATE to obj.last_update, COL_INITIALIZED to obj.initialized, - COL_VIEWER to obj.viewer, + COL_VIEWER to obj.viewer_flags, COL_CHAPTER_FLAGS to obj.chapter_flags, COL_COVER_LAST_MODIFIED to obj.cover_last_modified, COL_DATE_ADDED to obj.date_added @@ -85,7 +85,7 @@ interface BaseMangaGetResolver { favorite = cursor.getInt(cursor.getColumnIndex(COL_FAVORITE)) == 1 last_update = cursor.getLong(cursor.getColumnIndex(COL_LAST_UPDATE)) initialized = cursor.getInt(cursor.getColumnIndex(COL_INITIALIZED)) == 1 - viewer = cursor.getInt(cursor.getColumnIndex(COL_VIEWER)) + viewer_flags = cursor.getInt(cursor.getColumnIndex(COL_VIEWER)) chapter_flags = cursor.getInt(cursor.getColumnIndex(COL_CHAPTER_FLAGS)) cover_last_modified = cursor.getLong(cursor.getColumnIndex(COL_COVER_LAST_MODIFIED)) date_added = cursor.getLong(cursor.getColumnIndex(COL_DATE_ADDED)) diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/database/models/Manga.kt b/app/src/main/java/eu/kanade/tachiyomi/data/database/models/Manga.kt index 164ecc1019..f0cfd444fa 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/database/models/Manga.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/database/models/Manga.kt @@ -1,6 +1,8 @@ package eu.kanade.tachiyomi.data.database.models import eu.kanade.tachiyomi.source.model.SManga +import eu.kanade.tachiyomi.ui.reader.setting.OrientationType +import eu.kanade.tachiyomi.ui.reader.setting.ReadingModeType import tachiyomi.source.model.MangaInfo interface Manga : SManga { @@ -15,78 +17,90 @@ interface Manga : SManga { var date_added: Long - var viewer: Int + var viewer_flags: Int var chapter_flags: Int var cover_last_modified: Long fun setChapterOrder(order: Int) { - setFlags(order, SORT_MASK) + setChapterFlags(order, CHAPTER_SORT_MASK) } fun sortDescending(): Boolean { - return chapter_flags and SORT_MASK == SORT_DESC + return chapter_flags and CHAPTER_SORT_MASK == CHAPTER_SORT_DESC } fun getGenres(): List? { return genre?.split(", ")?.map { it.trim() } } - private fun setFlags(flag: Int, mask: Int) { + private fun setChapterFlags(flag: Int, mask: Int) { chapter_flags = chapter_flags and mask.inv() or (flag and mask) } + private fun setViewerFlags(flag: Int, mask: Int) { + viewer_flags = viewer_flags and mask.inv() or (flag and mask) + } + // Used to display the chapter's title one way or another var displayMode: Int - get() = chapter_flags and DISPLAY_MASK - set(mode) = setFlags(mode, DISPLAY_MASK) + get() = chapter_flags and CHAPTER_DISPLAY_MASK + set(mode) = setChapterFlags(mode, CHAPTER_DISPLAY_MASK) var readFilter: Int - get() = chapter_flags and READ_MASK - set(filter) = setFlags(filter, READ_MASK) + get() = chapter_flags and CHAPTER_READ_MASK + set(filter) = setChapterFlags(filter, CHAPTER_READ_MASK) var downloadedFilter: Int - get() = chapter_flags and DOWNLOADED_MASK - set(filter) = setFlags(filter, DOWNLOADED_MASK) + get() = chapter_flags and CHAPTER_DOWNLOADED_MASK + set(filter) = setChapterFlags(filter, CHAPTER_DOWNLOADED_MASK) var bookmarkedFilter: Int - get() = chapter_flags and BOOKMARKED_MASK - set(filter) = setFlags(filter, BOOKMARKED_MASK) + get() = chapter_flags and CHAPTER_BOOKMARKED_MASK + set(filter) = setChapterFlags(filter, CHAPTER_BOOKMARKED_MASK) var sorting: Int - get() = chapter_flags and SORTING_MASK - set(sort) = setFlags(sort, SORTING_MASK) + get() = chapter_flags and CHAPTER_SORTING_MASK + set(sort) = setChapterFlags(sort, CHAPTER_SORTING_MASK) - companion object { + var readingModeType: Int + get() = viewer_flags and ReadingModeType.MASK + set(readingMode) = setViewerFlags(readingMode, ReadingModeType.MASK) - const val SORT_DESC = 0x00000000 - const val SORT_ASC = 0x00000001 - const val SORT_MASK = 0x00000001 + var orientationType: Int + get() = viewer_flags and OrientationType.MASK + set(rotationType) = setViewerFlags(rotationType, OrientationType.MASK) + + companion object { // Generic filter that does not filter anything const val SHOW_ALL = 0x00000000 - const val SHOW_UNREAD = 0x00000002 - const val SHOW_READ = 0x00000004 - const val READ_MASK = 0x00000006 + const val CHAPTER_SORT_DESC = 0x00000000 + const val CHAPTER_SORT_ASC = 0x00000001 + const val CHAPTER_SORT_MASK = 0x00000001 + + const val CHAPTER_SHOW_UNREAD = 0x00000002 + const val CHAPTER_SHOW_READ = 0x00000004 + const val CHAPTER_READ_MASK = 0x00000006 - const val SHOW_DOWNLOADED = 0x00000008 - const val SHOW_NOT_DOWNLOADED = 0x00000010 - const val DOWNLOADED_MASK = 0x00000018 + const val CHAPTER_SHOW_DOWNLOADED = 0x00000008 + const val CHAPTER_SHOW_NOT_DOWNLOADED = 0x00000010 + const val CHAPTER_DOWNLOADED_MASK = 0x00000018 - const val SHOW_BOOKMARKED = 0x00000020 - const val SHOW_NOT_BOOKMARKED = 0x00000040 - const val BOOKMARKED_MASK = 0x00000060 + const val CHAPTER_SHOW_BOOKMARKED = 0x00000020 + const val CHAPTER_SHOW_NOT_BOOKMARKED = 0x00000040 + const val CHAPTER_BOOKMARKED_MASK = 0x00000060 - const val SORTING_SOURCE = 0x00000000 - const val SORTING_NUMBER = 0x00000100 - const val SORTING_UPLOAD_DATE = 0x00000200 - const val SORTING_MASK = 0x00000300 + const val CHAPTER_SORTING_SOURCE = 0x00000000 + const val CHAPTER_SORTING_NUMBER = 0x00000100 + const val CHAPTER_SORTING_UPLOAD_DATE = 0x00000200 + const val CHAPTER_SORTING_MASK = 0x00000300 - const val DISPLAY_NAME = 0x00000000 - const val DISPLAY_NUMBER = 0x00100000 - const val DISPLAY_MASK = 0x00100000 + const val CHAPTER_DISPLAY_NAME = 0x00000000 + const val CHAPTER_DISPLAY_NUMBER = 0x00100000 + const val CHAPTER_DISPLAY_MASK = 0x00100000 fun create(source: Long): Manga = MangaImpl().apply { this.source = source diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/database/models/MangaImpl.kt b/app/src/main/java/eu/kanade/tachiyomi/data/database/models/MangaImpl.kt index 61d7879523..7e27238407 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/database/models/MangaImpl.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/database/models/MangaImpl.kt @@ -30,7 +30,7 @@ open class MangaImpl : Manga { override var initialized: Boolean = false - override var viewer: Int = 0 + override var viewer_flags: Int = 0 override var chapter_flags: Int = 0 diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/database/queries/MangaQueries.kt b/app/src/main/java/eu/kanade/tachiyomi/data/database/queries/MangaQueries.kt index ec305d8d06..ad3a96214f 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/database/queries/MangaQueries.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/database/queries/MangaQueries.kt @@ -12,7 +12,6 @@ import eu.kanade.tachiyomi.data.database.resolvers.MangaFavoritePutResolver import eu.kanade.tachiyomi.data.database.resolvers.MangaFlagsPutResolver import eu.kanade.tachiyomi.data.database.resolvers.MangaLastUpdatedPutResolver import eu.kanade.tachiyomi.data.database.resolvers.MangaTitlePutResolver -import eu.kanade.tachiyomi.data.database.resolvers.MangaViewerPutResolver import eu.kanade.tachiyomi.data.database.tables.CategoryTable import eu.kanade.tachiyomi.data.database.tables.ChapterTable import eu.kanade.tachiyomi.data.database.tables.MangaCategoryTable @@ -78,14 +77,24 @@ interface MangaQueries : DbProvider { fun insertMangas(mangas: List) = db.put().objects(mangas).prepare() - fun updateFlags(manga: Manga) = db.put() + fun updateChapterFlags(manga: Manga) = db.put() .`object`(manga) - .withPutResolver(MangaFlagsPutResolver()) + .withPutResolver(MangaFlagsPutResolver(MangaTable.COL_CHAPTER_FLAGS, Manga::chapter_flags)) .prepare() - fun updateFlags(mangas: List) = db.put() - .objects(mangas) - .withPutResolver(MangaFlagsPutResolver(true)) + fun updateChapterFlags(manga: List) = db.put() + .objects(manga) + .withPutResolver(MangaFlagsPutResolver(MangaTable.COL_CHAPTER_FLAGS, Manga::chapter_flags, true)) + .prepare() + + fun updateViewerFlags(manga: Manga) = db.put() + .`object`(manga) + .withPutResolver(MangaFlagsPutResolver(MangaTable.COL_VIEWER, Manga::viewer_flags)) + .prepare() + + fun updateViewerFlags(manga: List) = db.put() + .objects(manga) + .withPutResolver(MangaFlagsPutResolver(MangaTable.COL_VIEWER, Manga::viewer_flags, true)) .prepare() fun updateLastUpdated(manga: Manga) = db.put() @@ -98,11 +107,6 @@ interface MangaQueries : DbProvider { .withPutResolver(MangaFavoritePutResolver()) .prepare() - fun updateMangaViewer(manga: Manga) = db.put() - .`object`(manga) - .withPutResolver(MangaViewerPutResolver()) - .prepare() - fun updateMangaTitle(manga: Manga) = db.put() .`object`(manga) .withPutResolver(MangaTitlePutResolver()) diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/database/resolvers/MangaFlagsPutResolver.kt b/app/src/main/java/eu/kanade/tachiyomi/data/database/resolvers/MangaFlagsPutResolver.kt index b342e35bb4..e19a4c889e 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/database/resolvers/MangaFlagsPutResolver.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/database/resolvers/MangaFlagsPutResolver.kt @@ -8,8 +8,9 @@ import com.pushtorefresh.storio.sqlite.queries.UpdateQuery import eu.kanade.tachiyomi.data.database.inTransactionReturn import eu.kanade.tachiyomi.data.database.models.Manga import eu.kanade.tachiyomi.data.database.tables.MangaTable +import kotlin.reflect.KProperty1 -class MangaFlagsPutResolver(private val updateAll: Boolean = false) : PutResolver() { +class MangaFlagsPutResolver(private val colName: String, private val fieldGetter: KProperty1, private val updateAll: Boolean = false) : PutResolver() { override fun performPut(db: StorIOSQLite, manga: Manga) = db.inTransactionReturn { val updateQuery = mapToUpdateQuery(manga) @@ -37,6 +38,6 @@ class MangaFlagsPutResolver(private val updateAll: Boolean = false) : PutResolve fun mapToContentValues(manga: Manga) = contentValuesOf( - MangaTable.COL_CHAPTER_FLAGS to manga.chapter_flags + colName to fieldGetter.get(manga) ) } diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/database/resolvers/MangaViewerPutResolver.kt b/app/src/main/java/eu/kanade/tachiyomi/data/database/resolvers/MangaViewerPutResolver.kt deleted file mode 100644 index 5f465eb308..0000000000 --- a/app/src/main/java/eu/kanade/tachiyomi/data/database/resolvers/MangaViewerPutResolver.kt +++ /dev/null @@ -1,32 +0,0 @@ -package eu.kanade.tachiyomi.data.database.resolvers - -import androidx.core.content.contentValuesOf -import com.pushtorefresh.storio.sqlite.StorIOSQLite -import com.pushtorefresh.storio.sqlite.operations.put.PutResolver -import com.pushtorefresh.storio.sqlite.operations.put.PutResult -import com.pushtorefresh.storio.sqlite.queries.UpdateQuery -import eu.kanade.tachiyomi.data.database.inTransactionReturn -import eu.kanade.tachiyomi.data.database.models.Manga -import eu.kanade.tachiyomi.data.database.tables.MangaTable - -class MangaViewerPutResolver : PutResolver() { - - override fun performPut(db: StorIOSQLite, manga: Manga) = db.inTransactionReturn { - val updateQuery = mapToUpdateQuery(manga) - val contentValues = mapToContentValues(manga) - - val numberOfRowsUpdated = db.lowLevel().update(updateQuery, contentValues) - PutResult.newUpdateResult(numberOfRowsUpdated, updateQuery.table()) - } - - fun mapToUpdateQuery(manga: Manga) = UpdateQuery.builder() - .table(MangaTable.TABLE) - .where("${MangaTable.COL_ID} = ?") - .whereArgs(manga.id) - .build() - - fun mapToContentValues(manga: Manga) = - contentValuesOf( - MangaTable.COL_VIEWER to manga.viewer - ) -} diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/glide/FileFetcher.kt b/app/src/main/java/eu/kanade/tachiyomi/data/glide/FileFetcher.kt deleted file mode 100644 index a54c3cedec..0000000000 --- a/app/src/main/java/eu/kanade/tachiyomi/data/glide/FileFetcher.kt +++ /dev/null @@ -1,60 +0,0 @@ -package eu.kanade.tachiyomi.data.glide - -import android.content.ContentValues.TAG -import android.util.Log -import com.bumptech.glide.Priority -import com.bumptech.glide.load.DataSource -import com.bumptech.glide.load.data.DataFetcher -import timber.log.Timber -import java.io.File -import java.io.FileInputStream -import java.io.FileNotFoundException -import java.io.IOException -import java.io.InputStream - -open class FileFetcher(private val filePath: String = "") : DataFetcher { - - private var data: InputStream? = null - - override fun loadData(priority: Priority, callback: DataFetcher.DataCallback) { - loadFromFile(callback) - } - - private fun loadFromFile(callback: DataFetcher.DataCallback) { - loadFromFile(File(filePath), callback) - } - - protected fun loadFromFile(file: File, callback: DataFetcher.DataCallback) { - try { - data = FileInputStream(file) - } catch (e: FileNotFoundException) { - if (Log.isLoggable(TAG, Log.DEBUG)) { - Timber.d(e, "Failed to open file") - } - callback.onLoadFailed(e) - return - } - - callback.onDataReady(data) - } - - override fun cleanup() { - try { - data?.close() - } catch (e: IOException) { - // Ignored. - } - } - - override fun cancel() { - // Do nothing. - } - - override fun getDataClass(): Class { - return InputStream::class.java - } - - override fun getDataSource(): DataSource { - return DataSource.LOCAL - } -} diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/glide/LibraryMangaCustomCoverFetcher.kt b/app/src/main/java/eu/kanade/tachiyomi/data/glide/LibraryMangaCustomCoverFetcher.kt deleted file mode 100644 index 3d04f40c26..0000000000 --- a/app/src/main/java/eu/kanade/tachiyomi/data/glide/LibraryMangaCustomCoverFetcher.kt +++ /dev/null @@ -1,25 +0,0 @@ -package eu.kanade.tachiyomi.data.glide - -import com.bumptech.glide.Priority -import com.bumptech.glide.load.data.DataFetcher -import eu.kanade.tachiyomi.data.cache.CoverCache -import eu.kanade.tachiyomi.data.database.models.Manga -import java.io.File -import java.io.InputStream -import java.lang.Exception - -open class LibraryMangaCustomCoverFetcher( - private val manga: Manga, - private val coverCache: CoverCache -) : FileFetcher() { - - override fun loadData(priority: Priority, callback: DataFetcher.DataCallback) { - getCustomCoverFile()?.let { - loadFromFile(it, callback) - } ?: callback.onLoadFailed(Exception("Custom cover file not found")) - } - - protected fun getCustomCoverFile(): File? { - return coverCache.getCustomCoverFile(manga).takeIf { it.exists() } - } -} diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/glide/LibraryMangaUrlFetcher.kt b/app/src/main/java/eu/kanade/tachiyomi/data/glide/LibraryMangaUrlFetcher.kt deleted file mode 100644 index 5d40dd6a4c..0000000000 --- a/app/src/main/java/eu/kanade/tachiyomi/data/glide/LibraryMangaUrlFetcher.kt +++ /dev/null @@ -1,86 +0,0 @@ -package eu.kanade.tachiyomi.data.glide - -import com.bumptech.glide.Priority -import com.bumptech.glide.load.data.DataFetcher -import eu.kanade.tachiyomi.data.cache.CoverCache -import eu.kanade.tachiyomi.data.database.models.Manga -import java.io.File -import java.io.FileNotFoundException -import java.io.InputStream - -/** - * A [DataFetcher] for loading a cover of a library manga. - * It tries to load the cover from our custom cache, and if it's not found, it fallbacks to network - * and copies the result to the cache. - * - * @param networkFetcher the network fetcher for this cover. - * @param manga the manga of the cover to load. - * @param file the file where this cover should be. It may exists or not. - */ -class LibraryMangaUrlFetcher( - private val networkFetcher: DataFetcher, - private val manga: Manga, - private val coverCache: CoverCache -) : LibraryMangaCustomCoverFetcher(manga, coverCache) { - - override fun loadData(priority: Priority, callback: DataFetcher.DataCallback) { - getCustomCoverFile()?.let { - loadFromFile(it, callback) - return - } - - val cover = coverCache.getCoverFile(manga) - if (cover == null) { - callback.onLoadFailed(Exception("Null thumbnail url")) - return - } - - if (!cover.exists()) { - networkFetcher.loadData( - priority, - object : DataFetcher.DataCallback { - override fun onDataReady(data: InputStream?) { - if (data != null) { - val tmpFile = File(cover.path + ".tmp") - try { - // Retrieve destination stream, create parent folders if needed. - val output = try { - tmpFile.outputStream() - } catch (e: FileNotFoundException) { - tmpFile.parentFile!!.mkdirs() - tmpFile.outputStream() - } - - // Copy the file and rename to the original. - data.use { output.use { data.copyTo(output) } } - tmpFile.renameTo(cover) - loadFromFile(cover, callback) - } catch (e: Exception) { - tmpFile.delete() - callback.onLoadFailed(e) - } - } else { - callback.onLoadFailed(Exception("Null data")) - } - } - - override fun onLoadFailed(e: Exception) { - callback.onLoadFailed(e) - } - } - ) - } else { - loadFromFile(cover, callback) - } - } - - override fun cleanup() { - super.cleanup() - networkFetcher.cleanup() - } - - override fun cancel() { - super.cancel() - networkFetcher.cancel() - } -} diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/glide/MangaThumbnail.kt b/app/src/main/java/eu/kanade/tachiyomi/data/glide/MangaThumbnail.kt deleted file mode 100644 index ae0057eb4a..0000000000 --- a/app/src/main/java/eu/kanade/tachiyomi/data/glide/MangaThumbnail.kt +++ /dev/null @@ -1,15 +0,0 @@ -package eu.kanade.tachiyomi.data.glide - -import com.bumptech.glide.load.Key -import eu.kanade.tachiyomi.data.database.models.Manga -import java.security.MessageDigest - -data class MangaThumbnail(val manga: Manga, val coverLastModified: Long) : Key { - val key = manga.url + coverLastModified - - override fun updateDiskCacheKey(messageDigest: MessageDigest) { - messageDigest.update(key.toByteArray(Key.CHARSET)) - } -} - -fun Manga.toMangaThumbnail() = MangaThumbnail(this, cover_last_modified) diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/glide/MangaThumbnailModelLoader.kt b/app/src/main/java/eu/kanade/tachiyomi/data/glide/MangaThumbnailModelLoader.kt deleted file mode 100644 index b3cf79a151..0000000000 --- a/app/src/main/java/eu/kanade/tachiyomi/data/glide/MangaThumbnailModelLoader.kt +++ /dev/null @@ -1,134 +0,0 @@ -package eu.kanade.tachiyomi.data.glide - -import com.bumptech.glide.integration.okhttp3.OkHttpStreamFetcher -import com.bumptech.glide.load.Options -import com.bumptech.glide.load.model.GlideUrl -import com.bumptech.glide.load.model.Headers -import com.bumptech.glide.load.model.LazyHeaders -import com.bumptech.glide.load.model.ModelLoader -import com.bumptech.glide.load.model.ModelLoaderFactory -import com.bumptech.glide.load.model.MultiModelLoaderFactory -import eu.kanade.tachiyomi.data.cache.CoverCache -import eu.kanade.tachiyomi.data.database.models.Manga -import eu.kanade.tachiyomi.network.NetworkHelper -import eu.kanade.tachiyomi.source.SourceManager -import eu.kanade.tachiyomi.source.online.HttpSource -import eu.kanade.tachiyomi.util.isLocal -import uy.kohesive.injekt.Injekt -import uy.kohesive.injekt.api.get -import uy.kohesive.injekt.injectLazy -import java.io.InputStream - -/** - * A class for loading a cover associated with a [Manga] that can be present in our own cache. - * Coupled with [LibraryMangaUrlFetcher], this class allows to implement the following flow: - * - * - Check in RAM LRU. - * - Check in disk LRU. - * - Check in this module. - * - Fetch from the network connection. - * - * @param context the application context. - */ -class MangaThumbnailModelLoader : ModelLoader { - - /** - * Cover cache where persistent covers are stored. - */ - private val coverCache: CoverCache by injectLazy() - - /** - * Source manager. - */ - private val sourceManager: SourceManager by injectLazy() - - /** - * Default network client. - */ - private val defaultClient = Injekt.get().client - - /** - * Map where request headers are stored for a source. - */ - private val cachedHeaders = hashMapOf() - - /** - * Factory class for creating [MangaThumbnailModelLoader] instances. - */ - class Factory : ModelLoaderFactory { - - override fun build(multiFactory: MultiModelLoaderFactory): ModelLoader { - return MangaThumbnailModelLoader() - } - - override fun teardown() {} - } - - override fun handles(model: MangaThumbnail): Boolean { - return true - } - - /** - * Returns a fetcher for the given manga or null if the url is empty. - * - * @param mangaThumbnail the model. - * @param width the width of the view where the resource will be loaded. - * @param height the height of the view where the resource will be loaded. - */ - override fun buildLoadData( - mangaThumbnail: MangaThumbnail, - width: Int, - height: Int, - options: Options - ): ModelLoader.LoadData? { - val manga = mangaThumbnail.manga - val url = manga.thumbnail_url - - if (url.isNullOrEmpty()) { - return if (!manga.favorite || manga.isLocal()) { - null - } else { - ModelLoader.LoadData(mangaThumbnail, LibraryMangaCustomCoverFetcher(manga, coverCache)) - } - } - - if (url.startsWith("http", true)) { - val source = sourceManager.get(manga.source) as? HttpSource - val glideUrl = GlideUrl(url, getHeaders(manga, source)) - - // Get the resource fetcher for this request url. - val networkFetcher = OkHttpStreamFetcher(source?.client ?: defaultClient, glideUrl) - - if (!manga.favorite) { - return ModelLoader.LoadData(glideUrl, networkFetcher) - } - - val libraryFetcher = LibraryMangaUrlFetcher(networkFetcher, manga, coverCache) - - // Return an instance of the fetcher providing the needed elements. - return ModelLoader.LoadData(mangaThumbnail, libraryFetcher) - } else { - // Return an instance of the fetcher providing the needed elements. - return ModelLoader.LoadData(mangaThumbnail, FileFetcher(url.removePrefix("file://"))) - } - } - - /** - * Returns the request headers for a source copying its OkHttp headers and caching them. - * - * @param manga the model. - */ - private fun getHeaders(manga: Manga, source: HttpSource?): Headers { - if (source == null) return LazyHeaders.DEFAULT - - return cachedHeaders.getOrPut(manga.source) { - LazyHeaders.Builder().apply { - val nullStr: String? = null - setHeader("User-Agent", nullStr) - for ((key, value) in source.headers.toMultimap()) { - addHeader(key, value[0]) - } - }.build() - } - } -} diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/glide/PassthroughModelLoader.kt b/app/src/main/java/eu/kanade/tachiyomi/data/glide/PassthroughModelLoader.kt deleted file mode 100644 index dd6d546f8c..0000000000 --- a/app/src/main/java/eu/kanade/tachiyomi/data/glide/PassthroughModelLoader.kt +++ /dev/null @@ -1,72 +0,0 @@ -package eu.kanade.tachiyomi.data.glide - -import com.bumptech.glide.Priority -import com.bumptech.glide.load.DataSource -import com.bumptech.glide.load.Options -import com.bumptech.glide.load.data.DataFetcher -import com.bumptech.glide.load.model.ModelLoader -import com.bumptech.glide.load.model.ModelLoaderFactory -import com.bumptech.glide.load.model.MultiModelLoaderFactory -import com.bumptech.glide.signature.ObjectKey -import java.io.IOException -import java.io.InputStream - -class PassthroughModelLoader : ModelLoader { - - override fun buildLoadData( - model: InputStream, - width: Int, - height: Int, - options: Options - ): ModelLoader.LoadData? { - return ModelLoader.LoadData(ObjectKey(model), Fetcher(model)) - } - - override fun handles(model: InputStream): Boolean { - return true - } - - class Fetcher(private val stream: InputStream) : DataFetcher { - - override fun getDataClass(): Class { - return InputStream::class.java - } - - override fun cleanup() { - try { - stream.close() - } catch (e: IOException) { - // Do nothing - } - } - - override fun getDataSource(): DataSource { - return DataSource.LOCAL - } - - override fun cancel() { - // Do nothing - } - - override fun loadData( - priority: Priority, - callback: DataFetcher.DataCallback - ) { - callback.onDataReady(stream) - } - } - - /** - * Factory class for creating [PassthroughModelLoader] instances. - */ - class Factory : ModelLoaderFactory { - - override fun build( - multiFactory: MultiModelLoaderFactory - ): ModelLoader { - return PassthroughModelLoader() - } - - override fun teardown() {} - } -} diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/glide/TachiGlideModule.kt b/app/src/main/java/eu/kanade/tachiyomi/data/glide/TachiGlideModule.kt deleted file mode 100644 index dca2e08797..0000000000 --- a/app/src/main/java/eu/kanade/tachiyomi/data/glide/TachiGlideModule.kt +++ /dev/null @@ -1,55 +0,0 @@ -package eu.kanade.tachiyomi.data.glide - -import android.content.Context -import android.graphics.drawable.Drawable -import com.bumptech.glide.Glide -import com.bumptech.glide.GlideBuilder -import com.bumptech.glide.Registry -import com.bumptech.glide.annotation.GlideModule -import com.bumptech.glide.integration.okhttp3.OkHttpUrlLoader -import com.bumptech.glide.load.DecodeFormat -import com.bumptech.glide.load.engine.cache.InternalCacheDiskCacheFactory -import com.bumptech.glide.load.model.GlideUrl -import com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions -import com.bumptech.glide.module.AppGlideModule -import com.bumptech.glide.request.RequestOptions -import eu.kanade.tachiyomi.network.NetworkHelper -import uy.kohesive.injekt.Injekt -import uy.kohesive.injekt.api.get -import java.io.InputStream - -/** - * Class used to update Glide module settings - */ -@GlideModule -class TachiGlideModule : AppGlideModule() { - - override fun applyOptions(context: Context, builder: GlideBuilder) { - builder.setDiskCache(InternalCacheDiskCacheFactory(context, 50 * 1024 * 1024)) - builder.setDefaultRequestOptions(RequestOptions().format(DecodeFormat.PREFER_RGB_565)) - builder.setDefaultTransitionOptions( - Drawable::class.java, - DrawableTransitionOptions.withCrossFade() - ) - } - - override fun registerComponents(context: Context, glide: Glide, registry: Registry) { - val networkFactory = OkHttpUrlLoader.Factory(Injekt.get().client) - - registry.replace( - GlideUrl::class.java, - InputStream::class.java, - networkFactory - ) - registry.append( - MangaThumbnail::class.java, - InputStream::class.java, - MangaThumbnailModelLoader.Factory() - ) - registry.append( - InputStream::class.java, - InputStream::class.java, - PassthroughModelLoader.Factory() - ) - } -} diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/library/LibraryUpdateNotifier.kt b/app/src/main/java/eu/kanade/tachiyomi/data/library/LibraryUpdateNotifier.kt index ab292bc4ea..d760fce94f 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/library/LibraryUpdateNotifier.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/library/LibraryUpdateNotifier.kt @@ -6,19 +6,22 @@ import android.content.Context import android.content.Intent import android.graphics.Bitmap import android.graphics.BitmapFactory +import android.graphics.drawable.BitmapDrawable import android.net.Uri import androidx.core.app.NotificationCompat import androidx.core.app.NotificationManagerCompat -import com.bumptech.glide.Glide +import coil.imageLoader +import coil.request.ImageRequest +import coil.transform.CircleCropTransformation import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.data.database.models.Chapter import eu.kanade.tachiyomi.data.database.models.Manga -import eu.kanade.tachiyomi.data.glide.toMangaThumbnail import eu.kanade.tachiyomi.data.notification.NotificationReceiver import eu.kanade.tachiyomi.data.notification.Notifications import eu.kanade.tachiyomi.data.preference.PreferencesHelper import eu.kanade.tachiyomi.ui.main.MainActivity import eu.kanade.tachiyomi.util.lang.chop +import eu.kanade.tachiyomi.util.lang.launchUI import eu.kanade.tachiyomi.util.system.notification import eu.kanade.tachiyomi.util.system.notificationBuilder import eu.kanade.tachiyomi.util.system.notificationManager @@ -165,14 +168,17 @@ class LibraryUpdateNotifier(private val context: Context) { // Per-manga notification if (!preferences.hideNotificationContent()) { - updates.forEach { (manga, chapters) -> - notify(manga.id.hashCode(), createNewChaptersNotification(manga, chapters)) + launchUI { + updates.forEach { (manga, chapters) -> + notify(manga.id.hashCode(), createNewChaptersNotification(manga, chapters)) + } } } } } - private fun createNewChaptersNotification(manga: Manga, chapters: Array): Notification { + private suspend fun createNewChaptersNotification(manga: Manga, chapters: Array): Notification { + val icon = getMangaIcon(manga) return context.notification(Notifications.CHANNEL_NEW_CHAPTERS) { setContentTitle(manga.title) @@ -182,7 +188,6 @@ class LibraryUpdateNotifier(private val context: Context) { setSmallIcon(R.drawable.ic_tachi) - val icon = getMangaIcon(manga) if (icon != null) { setLargeIcon(icon) } @@ -226,23 +231,14 @@ class LibraryUpdateNotifier(private val context: Context) { context.notificationManager.cancel(Notifications.ID_LIBRARY_PROGRESS) } - private fun getMangaIcon(manga: Manga): Bitmap? { - return try { - Glide.with(context) - .asBitmap() - .load(manga.toMangaThumbnail()) - .dontTransform() - .centerCrop() - .circleCrop() - .override( - NOTIF_ICON_SIZE, - NOTIF_ICON_SIZE - ) - .submit() - .get() - } catch (e: Exception) { - null - } + private suspend fun getMangaIcon(manga: Manga): Bitmap? { + val request = ImageRequest.Builder(context) + .data(manga) + .transformations(CircleCropTransformation()) + .size(NOTIF_ICON_SIZE) + .build() + val drawable = context.imageLoader.execute(request).drawable + return (drawable as? BitmapDrawable)?.bitmap } private fun getNewChaptersDescription(chapters: Array): String { diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/library/LibraryUpdateService.kt b/app/src/main/java/eu/kanade/tachiyomi/data/library/LibraryUpdateService.kt index e6215e4c82..86a2b81a12 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/library/LibraryUpdateService.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/library/LibraryUpdateService.kt @@ -386,6 +386,7 @@ class LibraryUpdateService( } } + coverCache.clearMemoryCache() notifier.cancelProgressNotification() } diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferenceKeys.kt b/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferenceKeys.kt index 9ef69e07ba..abf35341aa 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferenceKeys.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferenceKeys.kt @@ -15,8 +15,6 @@ object PreferenceKeys { const val hideBottomBar = "pref_hide_bottom_bar_on_scroll" - const val rotation = "pref_rotation_type_key" - const val enableTransitions = "pref_enable_transitions_key" const val doubleTapAnimationSpeed = "pref_double_tap_anim_speed" @@ -51,7 +49,9 @@ object PreferenceKeys { const val colorFilterMode = "color_filter_mode" - const val defaultViewer = "pref_default_viewer_key" + const val defaultReadingMode = "pref_default_reading_mode_key" + + const val defaultOrientationType = "pref_default_orientation_type_key" const val imageScaleType = "pref_image_scale_type_key" @@ -109,6 +109,8 @@ object PreferenceKeys { const val downloadOnlyOverWifi = "pref_download_only_over_wifi_key" + const val folderPerManga = "create_folder_per_manga" + const val numberOfBackups = "backup_slots" const val backupInterval = "backup_interval" diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferencesHelper.kt b/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferencesHelper.kt index 0768487ba1..91ccd83f0d 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferencesHelper.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferencesHelper.kt @@ -12,6 +12,8 @@ import eu.kanade.tachiyomi.data.database.models.Manga import eu.kanade.tachiyomi.data.preference.PreferenceValues.DisplayMode import eu.kanade.tachiyomi.data.track.TrackService import eu.kanade.tachiyomi.data.track.anilist.Anilist +import eu.kanade.tachiyomi.ui.reader.setting.OrientationType +import eu.kanade.tachiyomi.ui.reader.setting.ReadingModeType import eu.kanade.tachiyomi.widget.ExtendedNavigationView import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.onEach @@ -85,8 +87,6 @@ class PreferencesHelper(val context: Context) { fun themeDark() = flowPrefs.getEnum(Keys.themeDark, Values.DarkThemeVariant.default) - fun rotation() = flowPrefs.getInt(Keys.rotation, 1) - fun pageTransitions() = flowPrefs.getBoolean(Keys.enableTransitions, true) fun doubleTapAnimSpeed() = flowPrefs.getInt(Keys.doubleTapAnimationSpeed, 500) @@ -121,7 +121,9 @@ class PreferencesHelper(val context: Context) { fun colorFilterMode() = flowPrefs.getInt(Keys.colorFilterMode, 0) - fun defaultViewer() = prefs.getInt(Keys.defaultViewer, 2) + fun defaultReadingMode() = prefs.getInt(Keys.defaultReadingMode, ReadingModeType.RIGHT_TO_LEFT.flagValue) + + fun defaultOrientationType() = prefs.getInt(Keys.defaultOrientationType, OrientationType.FREE.flagValue) fun imageScaleType() = flowPrefs.getInt(Keys.imageScaleType, 1) @@ -203,6 +205,8 @@ class PreferencesHelper(val context: Context) { fun downloadOnlyOverWifi() = prefs.getBoolean(Keys.downloadOnlyOverWifi, true) + fun folderPerManga() = prefs.getBoolean(Keys.folderPerManga, false) + fun numberOfBackups() = flowPrefs.getInt(Keys.numberOfBackups, 1) fun backupInterval() = flowPrefs.getInt(Keys.backupInterval, 0) @@ -289,11 +293,11 @@ class PreferencesHelper(val context: Context) { fun filterChapterByBookmarked() = prefs.getInt(Keys.defaultChapterFilterByBookmarked, Manga.SHOW_ALL) - fun sortChapterBySourceOrNumber() = prefs.getInt(Keys.defaultChapterSortBySourceOrNumber, Manga.SORTING_SOURCE) + fun sortChapterBySourceOrNumber() = prefs.getInt(Keys.defaultChapterSortBySourceOrNumber, Manga.CHAPTER_SORTING_SOURCE) - fun displayChapterByNameOrNumber() = prefs.getInt(Keys.defaultChapterDisplayByNameOrNumber, Manga.DISPLAY_NAME) + fun displayChapterByNameOrNumber() = prefs.getInt(Keys.defaultChapterDisplayByNameOrNumber, Manga.CHAPTER_DISPLAY_NAME) - fun sortChapterByAscendingOrDescending() = prefs.getInt(Keys.defaultChapterSortByAscendingOrDescending, Manga.SORT_DESC) + fun sortChapterByAscendingOrDescending() = prefs.getInt(Keys.defaultChapterSortByAscendingOrDescending, Manga.CHAPTER_SORT_DESC) fun incognitoMode() = flowPrefs.getBoolean(Keys.incognitoMode, false) @@ -306,7 +310,7 @@ class PreferencesHelper(val context: Context) { putInt(Keys.defaultChapterFilterByBookmarked, manga.bookmarkedFilter) putInt(Keys.defaultChapterSortBySourceOrNumber, manga.sorting) putInt(Keys.defaultChapterDisplayByNameOrNumber, manga.displayMode) - putInt(Keys.defaultChapterSortByAscendingOrDescending, if (manga.sortDescending()) Manga.SORT_DESC else Manga.SORT_ASC) + putInt(Keys.defaultChapterSortByAscendingOrDescending, if (manga.sortDescending()) Manga.CHAPTER_SORT_DESC else Manga.CHAPTER_SORT_ASC) } } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/network/CloudflareInterceptor.kt b/app/src/main/java/eu/kanade/tachiyomi/network/CloudflareInterceptor.kt index 49ddc16fb7..d26b3a0e22 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/network/CloudflareInterceptor.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/network/CloudflareInterceptor.kt @@ -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.WebSettings @@ -114,10 +113,7 @@ class CloudflareInterceptor(private val context: Context) : Interceptor { latch.countDown() } - // HTTP error codes are only received since M - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && - url == origRequestUrl && !challengeFound - ) { + if (url == origRequestUrl && !challengeFound) { // The first request didn't return the challenge, abort. latch.countDown() } diff --git a/app/src/main/java/eu/kanade/tachiyomi/network/NetworkHelper.kt b/app/src/main/java/eu/kanade/tachiyomi/network/NetworkHelper.kt index 4b729fdeaf..4d26a8d4ce 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/network/NetworkHelper.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/network/NetworkHelper.kt @@ -1,6 +1,7 @@ package eu.kanade.tachiyomi.network import android.content.Context +import coil.util.CoilUtils import eu.kanade.tachiyomi.BuildConfig import eu.kanade.tachiyomi.data.preference.PreferencesHelper import okhttp3.Cache @@ -20,28 +21,32 @@ class NetworkHelper(context: Context) { val cookieManager = AndroidCookieJar() - val client by lazy { - val builder = OkHttpClient.Builder() - .cookieJar(cookieManager) - .cache(Cache(cacheDir, cacheSize)) - .connectTimeout(30, TimeUnit.SECONDS) - .readTimeout(30, TimeUnit.SECONDS) - .addInterceptor(UserAgentInterceptor()) - - if (BuildConfig.DEBUG) { - val httpLoggingInterceptor = HttpLoggingInterceptor().apply { - level = HttpLoggingInterceptor.Level.HEADERS + private val baseClientBuilder: OkHttpClient.Builder + get() { + val builder = OkHttpClient.Builder() + .cookieJar(cookieManager) + .connectTimeout(30, TimeUnit.SECONDS) + .readTimeout(30, TimeUnit.SECONDS) + .addInterceptor(UserAgentInterceptor()) + + if (BuildConfig.DEBUG) { + val httpLoggingInterceptor = HttpLoggingInterceptor().apply { + level = HttpLoggingInterceptor.Level.HEADERS + } + builder.addInterceptor(httpLoggingInterceptor) + } + + when (preferences.dohProvider()) { + PREF_DOH_CLOUDFLARE -> builder.dohCloudflare() + PREF_DOH_GOOGLE -> builder.dohGoogle() } - builder.addInterceptor(httpLoggingInterceptor) - } - when (preferences.dohProvider()) { - PREF_DOH_CLOUDFLARE -> builder.dohCloudflare() - PREF_DOH_GOOGLE -> builder.dohGoogle() + return builder } - builder.build() - } + val client by lazy { baseClientBuilder.cache(Cache(cacheDir, cacheSize)).build() } + + val coilClient by lazy { baseClientBuilder.cache(CoilUtils.createDefaultCache(context)).build() } val cloudflareClient by lazy { client.newBuilder() diff --git a/app/src/main/java/eu/kanade/tachiyomi/network/OkHttpExtensions.kt b/app/src/main/java/eu/kanade/tachiyomi/network/OkHttpExtensions.kt index f50c71a937..099289f545 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/network/OkHttpExtensions.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/network/OkHttpExtensions.kt @@ -9,6 +9,7 @@ import okhttp3.MediaType.Companion.toMediaType import okhttp3.OkHttpClient import okhttp3.Request import okhttp3.Response +import okhttp3.internal.closeQuietly import rx.Observable import rx.Producer import rx.Subscription @@ -70,7 +71,9 @@ suspend fun Call.await(): Response { return } - continuation.resume(response) + continuation.resume(response) { + response.body?.closeQuietly() + } } override fun onFailure(call: Call, e: IOException) { diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/base/controller/ConductorExtensions.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/base/controller/ConductorExtensions.kt index ce9fbb586a..a91acb5ea0 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/base/controller/ConductorExtensions.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/base/controller/ConductorExtensions.kt @@ -2,7 +2,6 @@ package eu.kanade.tachiyomi.ui.base.controller import android.content.Intent import android.content.pm.PackageManager.PERMISSION_GRANTED -import android.os.Build import androidx.core.content.ContextCompat import androidx.core.net.toUri import com.bluelinelabs.conductor.Controller @@ -21,11 +20,9 @@ fun Router.popControllerWithTag(tag: String): Boolean { fun Controller.requestPermissionsSafe(permissions: Array, requestCode: Int) { val activity = activity ?: return - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - permissions.forEach { permission -> - if (ContextCompat.checkSelfPermission(activity, permission) != PERMISSION_GRANTED) { - requestPermissions(arrayOf(permission), requestCode) - } + permissions.forEach { permission -> + if (ContextCompat.checkSelfPermission(activity, permission) != PERMISSION_GRANTED) { + requestPermissions(arrayOf(permission), requestCode) } } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/extension/ExtensionHolder.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/extension/ExtensionHolder.kt index cdec1a95bc..7f5a7158a3 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/extension/ExtensionHolder.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/extension/ExtensionHolder.kt @@ -1,9 +1,10 @@ package eu.kanade.tachiyomi.ui.browse.extension import android.view.View +import coil.clear +import coil.load import eu.davidea.viewholders.FlexibleViewHolder import eu.kanade.tachiyomi.R -import eu.kanade.tachiyomi.data.glide.GlideApp import eu.kanade.tachiyomi.data.preference.PreferencesHelper import eu.kanade.tachiyomi.databinding.ExtensionCardItemBinding import eu.kanade.tachiyomi.extension.model.Extension @@ -41,11 +42,9 @@ class ExtensionHolder(view: View, val adapter: ExtensionAdapter) : else -> "" }.toUpperCase() - GlideApp.with(itemView.context).clear(binding.image) + binding.image.clear() if (extension is Extension.Available) { - GlideApp.with(itemView.context) - .load(extension.iconUrl) - .into(binding.image) + binding.image.load(extension.iconUrl) } else { extension.getApplicationIcon(itemView.context)?.let { binding.image.setImageDrawable(it) } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/manga/MigrationMangaHolder.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/manga/MigrationMangaHolder.kt index c979f755ef..af63f683ca 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/manga/MigrationMangaHolder.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/manga/MigrationMangaHolder.kt @@ -1,14 +1,11 @@ package eu.kanade.tachiyomi.ui.browse.migration.manga import android.view.View -import com.bumptech.glide.load.engine.DiskCacheStrategy -import com.bumptech.glide.load.resource.bitmap.CenterCrop -import com.bumptech.glide.load.resource.bitmap.RoundedCorners -import com.bumptech.glide.request.RequestOptions +import coil.clear +import coil.loadAny +import coil.transform.RoundedCornersTransformation import eu.davidea.viewholders.FlexibleViewHolder import eu.kanade.tachiyomi.R -import eu.kanade.tachiyomi.data.glide.GlideApp -import eu.kanade.tachiyomi.data.glide.toMangaThumbnail import eu.kanade.tachiyomi.databinding.SourceListItemBinding class MigrationMangaHolder( @@ -28,15 +25,10 @@ class MigrationMangaHolder( binding.title.text = item.manga.title // Update the cover. - GlideApp.with(itemView.context).clear(binding.thumbnail) - - val radius = itemView.context.resources.getDimensionPixelSize(R.dimen.card_radius) - val requestOptions = RequestOptions().transform(CenterCrop(), RoundedCorners(radius)) - GlideApp.with(itemView.context) - .load(item.manga.toMangaThumbnail()) - .diskCacheStrategy(DiskCacheStrategy.RESOURCE) - .apply(requestOptions) - .dontAnimate() - .into(binding.thumbnail) + val radius = itemView.context.resources.getDimension(R.dimen.card_radius) + binding.thumbnail.clear() + binding.thumbnail.loadAny(item.manga) { + transformations(RoundedCornersTransformation(radius)) + } } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/search/SearchPresenter.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/search/SearchPresenter.kt index c1a254e095..059f7afeba 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/search/SearchPresenter.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/search/SearchPresenter.kt @@ -149,9 +149,9 @@ class SearchPresenter( // Update reading preferences manga.chapter_flags = prevManga.chapter_flags - db.updateFlags(manga).executeAsBlocking() - manga.viewer = prevManga.viewer - db.updateMangaViewer(manga).executeAsBlocking() + db.updateChapterFlags(manga).executeAsBlocking() + manga.viewer_flags = prevManga.viewer_flags + db.updateViewerFlags(manga).executeAsBlocking() // Update date added if (replace) { diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/SourceComfortableGridHolder.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/SourceComfortableGridHolder.kt index 7b2236e32d..62787a684c 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/SourceComfortableGridHolder.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/SourceComfortableGridHolder.kt @@ -1,11 +1,14 @@ package eu.kanade.tachiyomi.ui.browse.source.browse import android.view.View -import com.bumptech.glide.load.engine.DiskCacheStrategy +import coil.clear +import coil.imageLoader +import coil.request.CachePolicy +import coil.request.ImageRequest +import coil.transition.CrossfadeTransition import eu.davidea.flexibleadapter.FlexibleAdapter +import eu.kanade.tachiyomi.data.coil.MangaCoverFetcher import eu.kanade.tachiyomi.data.database.models.Manga -import eu.kanade.tachiyomi.data.glide.GlideApp -import eu.kanade.tachiyomi.data.glide.toMangaThumbnail import eu.kanade.tachiyomi.databinding.SourceComfortableGridItemBinding import eu.kanade.tachiyomi.widget.StateImageViewTarget @@ -42,14 +45,18 @@ class SourceComfortableGridHolder(private val view: View, private val adapter: F // For rounded corners binding.card.clipToOutline = true - GlideApp.with(view.context).clear(binding.thumbnail) + binding.thumbnail.clear() if (!manga.thumbnail_url.isNullOrEmpty()) { - GlideApp.with(view.context) - .load(manga.toMangaThumbnail()) - .diskCacheStrategy(DiskCacheStrategy.DATA) - .centerCrop() - .placeholder(android.R.color.transparent) - .into(StateImageViewTarget(binding.thumbnail, binding.progress)) + val crossfadeDuration = view.context.imageLoader.defaults.transition.let { + if (it is CrossfadeTransition) it.durationMillis else 0 + } + val request = ImageRequest.Builder(view.context) + .data(manga) + .setParameter(MangaCoverFetcher.USE_CUSTOM_COVER, false) + .diskCachePolicy(CachePolicy.DISABLED) + .target(StateImageViewTarget(binding.thumbnail, binding.progress, crossfadeDuration)) + .build() + itemView.context.imageLoader.enqueue(request) } } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/SourceGridHolder.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/SourceGridHolder.kt index d4ed34155f..74ab0fd5e7 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/SourceGridHolder.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/SourceGridHolder.kt @@ -1,11 +1,14 @@ package eu.kanade.tachiyomi.ui.browse.source.browse import android.view.View -import com.bumptech.glide.load.engine.DiskCacheStrategy +import coil.clear +import coil.imageLoader +import coil.request.CachePolicy +import coil.request.ImageRequest +import coil.transition.CrossfadeTransition import eu.davidea.flexibleadapter.FlexibleAdapter +import eu.kanade.tachiyomi.data.coil.MangaCoverFetcher import eu.kanade.tachiyomi.data.database.models.Manga -import eu.kanade.tachiyomi.data.glide.GlideApp -import eu.kanade.tachiyomi.data.glide.toMangaThumbnail import eu.kanade.tachiyomi.databinding.SourceComfortableGridItemBinding import eu.kanade.tachiyomi.widget.StateImageViewTarget @@ -42,14 +45,18 @@ open class SourceGridHolder(private val view: View, private val adapter: Flexibl // For rounded corners binding.card.clipToOutline = true - GlideApp.with(view.context).clear(binding.thumbnail) + binding.thumbnail.clear() if (!manga.thumbnail_url.isNullOrEmpty()) { - GlideApp.with(view.context) - .load(manga.toMangaThumbnail()) - .diskCacheStrategy(DiskCacheStrategy.DATA) - .centerCrop() - .placeholder(android.R.color.transparent) - .into(StateImageViewTarget(binding.thumbnail, binding.progress)) + val crossfadeDuration = view.context.imageLoader.defaults.transition.let { + if (it is CrossfadeTransition) it.durationMillis else 0 + } + val request = ImageRequest.Builder(view.context) + .data(manga) + .setParameter(MangaCoverFetcher.USE_CUSTOM_COVER, false) + .diskCachePolicy(CachePolicy.DISABLED) + .target(StateImageViewTarget(binding.thumbnail, binding.progress, crossfadeDuration)) + .build() + itemView.context.imageLoader.enqueue(request) } } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/SourceListHolder.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/SourceListHolder.kt index 9b9af026bd..5b98f5b481 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/SourceListHolder.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/SourceListHolder.kt @@ -1,15 +1,14 @@ package eu.kanade.tachiyomi.ui.browse.source.browse import android.view.View -import com.bumptech.glide.load.engine.DiskCacheStrategy -import com.bumptech.glide.load.resource.bitmap.CenterCrop -import com.bumptech.glide.load.resource.bitmap.RoundedCorners -import com.bumptech.glide.request.RequestOptions +import coil.clear +import coil.loadAny +import coil.request.CachePolicy +import coil.transform.RoundedCornersTransformation import eu.davidea.flexibleadapter.FlexibleAdapter import eu.kanade.tachiyomi.R +import eu.kanade.tachiyomi.data.coil.MangaCoverFetcher import eu.kanade.tachiyomi.data.database.models.Manga -import eu.kanade.tachiyomi.data.glide.GlideApp -import eu.kanade.tachiyomi.data.glide.toMangaThumbnail import eu.kanade.tachiyomi.databinding.SourceListItemBinding import eu.kanade.tachiyomi.util.system.getResourceColor @@ -46,18 +45,14 @@ class SourceListHolder(private val view: View, adapter: FlexibleAdapter<*>) : } override fun setImage(manga: Manga) { - GlideApp.with(view.context).clear(binding.thumbnail) - + binding.thumbnail.clear() if (!manga.thumbnail_url.isNullOrEmpty()) { - val radius = view.context.resources.getDimensionPixelSize(R.dimen.card_radius) - val requestOptions = RequestOptions().transform(CenterCrop(), RoundedCorners(radius)) - GlideApp.with(view.context) - .load(manga.toMangaThumbnail()) - .diskCacheStrategy(DiskCacheStrategy.DATA) - .apply(requestOptions) - .dontAnimate() - .placeholder(android.R.color.transparent) - .into(binding.thumbnail) + val radius = view.context.resources.getDimension(R.dimen.card_radius) + binding.thumbnail.loadAny(manga) { + setParameter(MangaCoverFetcher.USE_CUSTOM_COVER, false) + transformations(RoundedCornersTransformation(radius)) + diskCachePolicy(CachePolicy.DISABLED) + } } } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/globalsearch/GlobalSearchCardHolder.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/globalsearch/GlobalSearchCardHolder.kt index 8b0310f1fe..d7c83b0631 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/globalsearch/GlobalSearchCardHolder.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/globalsearch/GlobalSearchCardHolder.kt @@ -1,11 +1,14 @@ package eu.kanade.tachiyomi.ui.browse.source.globalsearch import android.view.View -import com.bumptech.glide.load.engine.DiskCacheStrategy +import coil.clear +import coil.imageLoader +import coil.request.CachePolicy +import coil.request.ImageRequest +import coil.transition.CrossfadeTransition import eu.davidea.viewholders.FlexibleViewHolder +import eu.kanade.tachiyomi.data.coil.MangaCoverFetcher import eu.kanade.tachiyomi.data.database.models.Manga -import eu.kanade.tachiyomi.data.glide.GlideApp -import eu.kanade.tachiyomi.data.glide.toMangaThumbnail import eu.kanade.tachiyomi.databinding.GlobalSearchControllerCardItemBinding import eu.kanade.tachiyomi.widget.StateImageViewTarget @@ -42,15 +45,18 @@ class GlobalSearchCardHolder(view: View, adapter: GlobalSearchCardAdapter) : } fun setImage(manga: Manga) { - GlideApp.with(itemView.context).clear(binding.cover) + binding.cover.clear() if (!manga.thumbnail_url.isNullOrEmpty()) { - GlideApp.with(itemView.context) - .load(manga.toMangaThumbnail()) - .diskCacheStrategy(DiskCacheStrategy.DATA) - .centerCrop() - .skipMemoryCache(true) - .placeholder(android.R.color.transparent) - .into(StateImageViewTarget(binding.cover, binding.progress)) + val crossfadeDuration = itemView.context.imageLoader.defaults.transition.let { + if (it is CrossfadeTransition) it.durationMillis else 0 + } + val request = ImageRequest.Builder(itemView.context) + .data(manga) + .setParameter(MangaCoverFetcher.USE_CUSTOM_COVER, false) + .diskCachePolicy(CachePolicy.DISABLED) + .target(StateImageViewTarget(binding.cover, binding.progress, crossfadeDuration)) + .build() + itemView.context.imageLoader.enqueue(request) } } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryComfortableGridHolder.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryComfortableGridHolder.kt index 96aa02150d..bb7684554e 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryComfortableGridHolder.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryComfortableGridHolder.kt @@ -3,11 +3,10 @@ package eu.kanade.tachiyomi.ui.library import android.view.View import androidx.core.view.isVisible import androidx.recyclerview.widget.RecyclerView -import com.bumptech.glide.load.engine.DiskCacheStrategy +import coil.clear +import coil.loadAny import eu.davidea.flexibleadapter.FlexibleAdapter import eu.davidea.flexibleadapter.items.IFlexible -import eu.kanade.tachiyomi.data.glide.GlideApp -import eu.kanade.tachiyomi.data.glide.toMangaThumbnail import eu.kanade.tachiyomi.databinding.SourceComfortableGridItemBinding import eu.kanade.tachiyomi.util.isLocal @@ -57,12 +56,7 @@ class LibraryComfortableGridHolder( binding.card.clipToOutline = true // Update the cover. - GlideApp.with(view.context).clear(binding.thumbnail) - GlideApp.with(view.context) - .load(item.manga.toMangaThumbnail()) - .diskCacheStrategy(DiskCacheStrategy.RESOURCE) - .centerCrop() - .dontAnimate() - .into(binding.thumbnail) + binding.thumbnail.clear() + binding.thumbnail.loadAny(item.manga) } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryCompactGridHolder.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryCompactGridHolder.kt index 851223a184..bcf0c254ee 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryCompactGridHolder.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryCompactGridHolder.kt @@ -2,10 +2,9 @@ package eu.kanade.tachiyomi.ui.library import android.view.View import androidx.core.view.isVisible -import com.bumptech.glide.load.engine.DiskCacheStrategy +import coil.clear +import coil.loadAny import eu.davidea.flexibleadapter.FlexibleAdapter -import eu.kanade.tachiyomi.data.glide.GlideApp -import eu.kanade.tachiyomi.data.glide.toMangaThumbnail import eu.kanade.tachiyomi.databinding.SourceCompactGridItemBinding import eu.kanade.tachiyomi.util.isLocal @@ -55,12 +54,7 @@ open class LibraryCompactGridHolder( binding.card.clipToOutline = true // Update the cover. - GlideApp.with(view.context).clear(binding.thumbnail) - GlideApp.with(view.context) - .load(item.manga.toMangaThumbnail()) - .diskCacheStrategy(DiskCacheStrategy.RESOURCE) - .centerCrop() - .dontAnimate() - .into(binding.thumbnail) + binding.thumbnail.clear() + binding.thumbnail.loadAny(item.manga) } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryListHolder.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryListHolder.kt index d7071f32b6..3e67016acd 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryListHolder.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryListHolder.kt @@ -2,14 +2,11 @@ package eu.kanade.tachiyomi.ui.library import android.view.View import androidx.core.view.isVisible -import com.bumptech.glide.load.engine.DiskCacheStrategy -import com.bumptech.glide.load.resource.bitmap.CenterCrop -import com.bumptech.glide.load.resource.bitmap.RoundedCorners -import com.bumptech.glide.request.RequestOptions +import coil.clear +import coil.loadAny +import coil.transform.RoundedCornersTransformation import eu.davidea.flexibleadapter.FlexibleAdapter import eu.kanade.tachiyomi.R -import eu.kanade.tachiyomi.data.glide.GlideApp -import eu.kanade.tachiyomi.data.glide.toMangaThumbnail import eu.kanade.tachiyomi.databinding.SourceListItemBinding import eu.kanade.tachiyomi.util.isLocal @@ -62,15 +59,10 @@ class LibraryListHolder( } // Update the cover. - GlideApp.with(itemView.context).clear(binding.thumbnail) - - val radius = view.context.resources.getDimensionPixelSize(R.dimen.card_radius) - val requestOptions = RequestOptions().transform(CenterCrop(), RoundedCorners(radius)) - GlideApp.with(itemView.context) - .load(item.manga.toMangaThumbnail()) - .diskCacheStrategy(DiskCacheStrategy.RESOURCE) - .apply(requestOptions) - .dontAnimate() - .into(binding.thumbnail) + val radius = view.context.resources.getDimension(R.dimen.card_radius) + binding.thumbnail.clear() + binding.thumbnail.loadAny(item.manga) { + transformations(RoundedCornersTransformation(radius)) + } } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaController.kt index 2e5b3f9e9b..c12d8aaa53 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaController.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaController.kt @@ -298,23 +298,17 @@ class MangaController : fab.setOnClickListener { val item = presenter.getNextUnreadChapter() if (item != null) { - // Create animation listener - val revealAnimationListener: Animator.AnimatorListener = object : AnimatorListenerAdapter() { - override fun onAnimationStart(animation: Animator?) { - openChapter(item.chapter, true) - } - } - // Get coordinates and start animation actionFab?.getCoordinates()?.let { coordinates -> - if (!binding.revealView.showRevealEffect( - coordinates.x, - coordinates.y, - revealAnimationListener - ) - ) { - openChapter(item.chapter) - } + binding.revealView.showRevealEffect( + coordinates.x, + coordinates.y, + object : AnimatorListenerAdapter() { + override fun onAnimationStart(animation: Animator?) { + openChapter(item.chapter, true) + } + } + ) } } else { view?.context?.toast(R.string.no_next_chapter) diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaPresenter.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaPresenter.kt index 7dd1f653fa..8e1a6b8d30 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaPresenter.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaPresenter.kt @@ -284,6 +284,7 @@ class MangaPresenter( } else if (manga.favorite) { coverCache.setCustomCoverToCache(manga, it) manga.updateCoverLastModified(db) + coverCache.clearMemoryCache() } } } @@ -300,6 +301,7 @@ class MangaPresenter( .fromCallable { coverCache.deleteCustomCover(manga) manga.updateCoverLastModified(db) + coverCache.clearMemoryCache() } .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) @@ -434,15 +436,15 @@ class MangaPresenter( fun getChapterSort(): (Chapter, Chapter) -> Int { return when (manga.sorting) { - Manga.SORTING_SOURCE -> when (sortDescending()) { + Manga.CHAPTER_SORTING_SOURCE -> when (sortDescending()) { true -> { c1, c2 -> c1.source_order.compareTo(c2.source_order) } false -> { c1, c2 -> c2.source_order.compareTo(c1.source_order) } } - Manga.SORTING_NUMBER -> when (sortDescending()) { + Manga.CHAPTER_SORTING_NUMBER -> when (sortDescending()) { true -> { c1, c2 -> c2.chapter_number.compareTo(c1.chapter_number) } false -> { c1, c2 -> c1.chapter_number.compareTo(c2.chapter_number) } } - Manga.SORTING_UPLOAD_DATE -> when (sortDescending()) { + Manga.CHAPTER_SORTING_UPLOAD_DATE -> when (sortDescending()) { true -> { c1, c2 -> c2.date_upload.compareTo(c1.date_upload) } false -> { c1, c2 -> c1.date_upload.compareTo(c2.date_upload) } } @@ -562,8 +564,8 @@ class MangaPresenter( * Reverses the sorting and requests an UI update. */ fun reverseSortOrder() { - manga.setChapterOrder(if (sortDescending()) Manga.SORT_ASC else Manga.SORT_DESC) - db.updateFlags(manga).executeAsBlocking() + manga.setChapterOrder(if (sortDescending()) Manga.CHAPTER_SORT_ASC else Manga.CHAPTER_SORT_DESC) + db.updateChapterFlags(manga).executeAsBlocking() refreshChapters() } @@ -574,10 +576,10 @@ class MangaPresenter( fun setUnreadFilter(state: State) { manga.readFilter = when (state) { State.IGNORE -> Manga.SHOW_ALL - State.INCLUDE -> Manga.SHOW_UNREAD - State.EXCLUDE -> Manga.SHOW_READ + State.INCLUDE -> Manga.CHAPTER_SHOW_UNREAD + State.EXCLUDE -> Manga.CHAPTER_SHOW_READ } - db.updateFlags(manga).executeAsBlocking() + db.updateChapterFlags(manga).executeAsBlocking() refreshChapters() } @@ -588,10 +590,10 @@ class MangaPresenter( fun setDownloadedFilter(state: State) { manga.downloadedFilter = when (state) { State.IGNORE -> Manga.SHOW_ALL - State.INCLUDE -> Manga.SHOW_DOWNLOADED - State.EXCLUDE -> Manga.SHOW_NOT_DOWNLOADED + State.INCLUDE -> Manga.CHAPTER_SHOW_DOWNLOADED + State.EXCLUDE -> Manga.CHAPTER_SHOW_NOT_DOWNLOADED } - db.updateFlags(manga).executeAsBlocking() + db.updateChapterFlags(manga).executeAsBlocking() refreshChapters() } @@ -602,10 +604,10 @@ class MangaPresenter( fun setBookmarkedFilter(state: State) { manga.bookmarkedFilter = when (state) { State.IGNORE -> Manga.SHOW_ALL - State.INCLUDE -> Manga.SHOW_BOOKMARKED - State.EXCLUDE -> Manga.SHOW_NOT_BOOKMARKED + State.INCLUDE -> Manga.CHAPTER_SHOW_BOOKMARKED + State.EXCLUDE -> Manga.CHAPTER_SHOW_NOT_BOOKMARKED } - db.updateFlags(manga).executeAsBlocking() + db.updateChapterFlags(manga).executeAsBlocking() refreshChapters() } @@ -615,7 +617,7 @@ class MangaPresenter( */ fun setDisplayMode(mode: Int) { manga.displayMode = mode - db.updateFlags(manga).executeAsBlocking() + db.updateChapterFlags(manga).executeAsBlocking() } /** @@ -624,7 +626,7 @@ class MangaPresenter( */ fun setSorting(sort: Int) { manga.sorting = sort - db.updateFlags(manga).executeAsBlocking() + db.updateChapterFlags(manga).executeAsBlocking() refreshChapters() } @@ -643,8 +645,8 @@ class MangaPresenter( return State.INCLUDE } return when (manga.downloadedFilter) { - Manga.SHOW_DOWNLOADED -> State.INCLUDE - Manga.SHOW_NOT_DOWNLOADED -> State.EXCLUDE + Manga.CHAPTER_SHOW_DOWNLOADED -> State.INCLUDE + Manga.CHAPTER_SHOW_NOT_DOWNLOADED -> State.EXCLUDE else -> State.IGNORE } } @@ -654,8 +656,8 @@ class MangaPresenter( */ fun onlyBookmarked(): State { return when (manga.bookmarkedFilter) { - Manga.SHOW_BOOKMARKED -> State.INCLUDE - Manga.SHOW_NOT_BOOKMARKED -> State.EXCLUDE + Manga.CHAPTER_SHOW_BOOKMARKED -> State.INCLUDE + Manga.CHAPTER_SHOW_NOT_BOOKMARKED -> State.EXCLUDE else -> State.IGNORE } } @@ -665,8 +667,8 @@ class MangaPresenter( */ fun onlyUnread(): State { return when (manga.readFilter) { - Manga.SHOW_UNREAD -> State.INCLUDE - Manga.SHOW_READ -> State.EXCLUDE + Manga.CHAPTER_SHOW_UNREAD -> State.INCLUDE + Manga.CHAPTER_SHOW_READ -> State.EXCLUDE else -> State.IGNORE } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/chapter/ChapterHolder.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/chapter/ChapterHolder.kt index 789d6a75cc..3e9588d9a0 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/chapter/ChapterHolder.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/chapter/ChapterHolder.kt @@ -29,7 +29,7 @@ class ChapterHolder( val chapter = item.chapter binding.chapterTitle.text = when (manga.displayMode) { - Manga.DISPLAY_NUMBER -> { + Manga.CHAPTER_DISPLAY_NUMBER -> { val number = adapter.decimalFormat.format(chapter.chapter_number.toDouble()) itemView.context.getString(R.string.display_mode_chapter, number) } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/chapter/ChaptersSettingsSheet.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/chapter/ChaptersSettingsSheet.kt index 4a23cad6d2..884840551c 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/chapter/ChaptersSettingsSheet.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/chapter/ChaptersSettingsSheet.kt @@ -152,11 +152,11 @@ class ChaptersSettingsSheet( } source.state = - if (sorting == Manga.SORTING_SOURCE) order else Item.MultiSort.SORT_NONE + if (sorting == Manga.CHAPTER_SORTING_SOURCE) order else Item.MultiSort.SORT_NONE chapterNum.state = - if (sorting == Manga.SORTING_NUMBER) order else Item.MultiSort.SORT_NONE + if (sorting == Manga.CHAPTER_SORTING_NUMBER) order else Item.MultiSort.SORT_NONE uploadDate.state = - if (sorting == Manga.SORTING_UPLOAD_DATE) order else Item.MultiSort.SORT_NONE + if (sorting == Manga.CHAPTER_SORTING_UPLOAD_DATE) order else Item.MultiSort.SORT_NONE } override fun onItemClicked(item: Item) { @@ -175,9 +175,9 @@ class ChaptersSettingsSheet( } when (item) { - source -> presenter.setSorting(Manga.SORTING_SOURCE) - chapterNum -> presenter.setSorting(Manga.SORTING_NUMBER) - uploadDate -> presenter.setSorting(Manga.SORTING_UPLOAD_DATE) + source -> presenter.setSorting(Manga.CHAPTER_SORTING_SOURCE) + chapterNum -> presenter.setSorting(Manga.CHAPTER_SORTING_NUMBER) + uploadDate -> presenter.setSorting(Manga.CHAPTER_SORTING_UPLOAD_DATE) else -> throw Exception("Unknown sorting") } @@ -209,8 +209,8 @@ class ChaptersSettingsSheet( override fun initModels() { val mode = presenter.manga.displayMode - displayTitle.checked = mode == Manga.DISPLAY_NAME - displayChapterNum.checked = mode == Manga.DISPLAY_NUMBER + displayTitle.checked = mode == Manga.CHAPTER_DISPLAY_NAME + displayChapterNum.checked = mode == Manga.CHAPTER_DISPLAY_NUMBER } override fun onItemClicked(item: Item) { @@ -221,8 +221,8 @@ class ChaptersSettingsSheet( item.checked = true when (item) { - displayTitle -> presenter.setDisplayMode(Manga.DISPLAY_NAME) - displayChapterNum -> presenter.setDisplayMode(Manga.DISPLAY_NUMBER) + displayTitle -> presenter.setDisplayMode(Manga.CHAPTER_DISPLAY_NAME) + displayChapterNum -> presenter.setDisplayMode(Manga.CHAPTER_DISPLAY_NUMBER) else -> throw NotImplementedError("Unknown display mode") } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/info/MangaInfoHeaderAdapter.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/info/MangaInfoHeaderAdapter.kt index ee3e6ddb32..dc5f4c37bd 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/info/MangaInfoHeaderAdapter.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/info/MangaInfoHeaderAdapter.kt @@ -1,18 +1,13 @@ package eu.kanade.tachiyomi.ui.manga.info -import android.graphics.PorterDuff -import android.os.Build import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import androidx.core.view.isVisible import androidx.recyclerview.widget.RecyclerView -import com.bumptech.glide.load.engine.DiskCacheStrategy +import coil.loadAny import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.data.database.models.Manga -import eu.kanade.tachiyomi.data.glide.GlideApp -import eu.kanade.tachiyomi.data.glide.MangaThumbnail -import eu.kanade.tachiyomi.data.glide.toMangaThumbnail import eu.kanade.tachiyomi.data.track.TrackManager import eu.kanade.tachiyomi.databinding.MangaInfoHeaderBinding import eu.kanade.tachiyomi.source.Source @@ -21,7 +16,6 @@ import eu.kanade.tachiyomi.source.model.SManga import eu.kanade.tachiyomi.source.online.HttpSource import eu.kanade.tachiyomi.ui.manga.MangaController import eu.kanade.tachiyomi.util.system.copyToClipboard -import eu.kanade.tachiyomi.util.system.getResourceColor import eu.kanade.tachiyomi.util.view.setChips import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.merge @@ -47,7 +41,6 @@ class MangaInfoHeaderAdapter( private lateinit var binding: MangaInfoHeaderBinding private var initialLoad: Boolean = true - private var currentMangaThumbnail: MangaThumbnail? = null override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): HeaderViewHolder { binding = MangaInfoHeaderBinding.inflate(LayoutInflater.from(parent.context), parent, false) @@ -249,17 +242,8 @@ class MangaInfoHeaderAdapter( setFavoriteButtonState(manga.favorite) // Set cover if changed. - val mangaThumbnail = manga.toMangaThumbnail() - if (mangaThumbnail != currentMangaThumbnail) { - currentMangaThumbnail = mangaThumbnail - listOf(binding.mangaCover, binding.backdrop) - .forEach { - GlideApp.with(view.context) - .load(mangaThumbnail) - .diskCacheStrategy(DiskCacheStrategy.RESOURCE) - .centerCrop() - .into(it) - } + listOf(binding.mangaCover, binding.backdrop).forEach { + it.loadAny(manga) } // Manga info section @@ -304,18 +288,6 @@ class MangaInfoHeaderAdapter( initialLoad = false } } - - // backgroundTint attribute doesn't work properly on Android 5 - if (Build.VERSION.SDK_INT == Build.VERSION_CODES.LOLLIPOP) { - listOf(binding.backdropOverlay, binding.mangaInfoToggleMoreScrim) - .forEach { - @Suppress("DEPRECATION") - it.background.setColorFilter( - view.context.getResourceColor(android.R.attr.colorBackground), - PorterDuff.Mode.SRC_ATOP - ) - } - } } private fun showMangaInfo(visible: Boolean) { diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/track/TrackSearchAdapter.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/track/TrackSearchAdapter.kt index 2ee8ba3526..af5c92b02b 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/track/TrackSearchAdapter.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/track/TrackSearchAdapter.kt @@ -5,9 +5,9 @@ import android.view.View import android.view.ViewGroup import android.widget.ArrayAdapter import androidx.core.view.isVisible -import com.bumptech.glide.load.engine.DiskCacheStrategy +import coil.clear +import coil.load import eu.kanade.tachiyomi.R -import eu.kanade.tachiyomi.data.glide.GlideApp import eu.kanade.tachiyomi.data.track.model.TrackSearch import eu.kanade.tachiyomi.databinding.TrackSearchItemBinding import eu.kanade.tachiyomi.util.view.inflate @@ -46,13 +46,9 @@ class TrackSearchAdapter(context: Context) : fun onSetValues(track: TrackSearch) { binding.trackSearchTitle.text = track.title binding.trackSearchSummary.text = track.summary - GlideApp.with(view.context).clear(binding.trackSearchCover) + binding.trackSearchCover.clear() if (track.cover_url.isNotEmpty()) { - GlideApp.with(view.context) - .load(track.cover_url) - .diskCacheStrategy(DiskCacheStrategy.RESOURCE) - .centerCrop() - .into(binding.trackSearchCover) + binding.trackSearchCover.load(track.cover_url) } val hasStatus = track.publishing_status.isNotBlank() diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderActivity.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderActivity.kt index 71ce36ef04..8f7461bbcd 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderActivity.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderActivity.kt @@ -31,7 +31,6 @@ import eu.kanade.tachiyomi.data.database.models.Manga import eu.kanade.tachiyomi.data.notification.NotificationReceiver import eu.kanade.tachiyomi.data.notification.Notifications import eu.kanade.tachiyomi.data.preference.PreferencesHelper -import eu.kanade.tachiyomi.data.preference.asImmediateFlow import eu.kanade.tachiyomi.data.preference.toggle import eu.kanade.tachiyomi.databinding.ReaderActivityBinding import eu.kanade.tachiyomi.ui.base.activity.BaseRxActivity @@ -63,7 +62,6 @@ import eu.kanade.tachiyomi.util.view.setTooltip import eu.kanade.tachiyomi.util.view.showBar import eu.kanade.tachiyomi.widget.SimpleAnimationListener import eu.kanade.tachiyomi.widget.SimpleSeekBarListener -import kotlinx.coroutines.delay import kotlinx.coroutines.flow.drop import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach @@ -358,12 +356,12 @@ class ReaderActivity : BaseRxActivity() setOnClickListener { popupMenu( - items = ReadingModeType.values().map { it.prefValue to it.stringRes }, - selectedItemId = presenter.getMangaViewer(resolveDefault = false), + items = ReadingModeType.values().map { it.flagValue to it.stringRes }, + selectedItemId = presenter.getMangaReadingMode(resolveDefault = false), ) { val newReadingMode = ReadingModeType.fromPreference(itemId) - presenter.setMangaViewer(newReadingMode.prefValue) + presenter.setMangaReadingMode(newReadingMode.flagValue) menuToggleToast?.cancel() if (!preferences.showReadingMode()) { @@ -379,28 +377,28 @@ class ReaderActivity : BaseRxActivity() setOnClickListener { popupMenu( - items = OrientationType.values().map { it.prefValue to it.stringRes }, - selectedItemId = preferences.rotation().get(), + items = OrientationType.values().map { it.flagValue to it.stringRes }, + selectedItemId = presenter.manga?.orientationType + ?: preferences.defaultOrientationType(), ) { val newOrientation = OrientationType.fromPreference(itemId) - preferences.rotation().set(newOrientation.prefValue) - setOrientation(newOrientation.flag) + presenter.setMangaOrientationType(newOrientation.flagValue) + + updateOrientationShortcut(newOrientation.flagValue) menuToggleToast?.cancel() menuToggleToast = toast(newOrientation.stringRes) } } } - preferences.rotation().asImmediateFlow { updateRotationShortcut(it) } - .launchIn(lifecycleScope) // Crop borders with(binding.actionCropBorders) { setTooltip(R.string.pref_crop_borders) setOnClickListener { - val isPagerType = ReadingModeType.isPagerType(presenter.getMangaViewer()) + val isPagerType = ReadingModeType.isPagerType(presenter.getMangaReadingMode()) if (isPagerType) { preferences.cropBorders().toggle() } else { @@ -431,13 +429,13 @@ class ReaderActivity : BaseRxActivity() } } - private fun updateRotationShortcut(preference: Int) { + private fun updateOrientationShortcut(preference: Int) { val orientation = OrientationType.fromPreference(preference) binding.actionRotation.setImageResource(orientation.iconRes) } private fun updateCropBordersShortcut() { - val isPagerType = ReadingModeType.isPagerType(presenter.getMangaViewer()) + val isPagerType = ReadingModeType.isPagerType(presenter.getMangaReadingMode()) val enabled = if (isPagerType) { preferences.cropBorders().get() } else { @@ -529,10 +527,10 @@ class ReaderActivity : BaseRxActivity() fun setManga(manga: Manga) { val prevViewer = viewer - val viewerMode = ReadingModeType.fromPreference(presenter.getMangaViewer(resolveDefault = false)) + val viewerMode = ReadingModeType.fromPreference(presenter.getMangaReadingMode(resolveDefault = false)) binding.actionReadingMode.setImageResource(viewerMode.iconRes) - val newViewer = when (presenter.getMangaViewer()) { + val newViewer = when (presenter.getMangaReadingMode()) { ReadingModeType.LEFT_TO_RIGHT.prefValue -> L2RPagerViewer(this) ReadingModeType.VERTICAL.prefValue -> VerticalPagerViewer(this) ReadingModeType.WEBTOON.prefValue -> WebtoonViewer(this) @@ -540,6 +538,8 @@ class ReaderActivity : BaseRxActivity() else -> R2LPagerViewer(this) } + setOrientation(presenter.getMangaOrientationType()) + // Destroy previous viewer if there was one if (prevViewer != null) { prevViewer.destroy() @@ -549,7 +549,7 @@ class ReaderActivity : BaseRxActivity() binding.viewerContainer.addView(newViewer.getView()) if (preferences.showReadingMode()) { - showReadingModeToast(presenter.getMangaViewer()) + showReadingModeToast(presenter.getMangaReadingMode()) } binding.toolbar.title = manga.title @@ -777,7 +777,7 @@ class ReaderActivity : BaseRxActivity() /** * Forces the user preferred [orientation] on the activity. */ - private fun setOrientation(orientation: Int) { + fun setOrientation(orientation: Int) { val newOrientation = OrientationType.fromPreference(orientation) if (newOrientation.flag != requestedOrientation) { requestedOrientation = newOrientation.flag @@ -793,14 +793,6 @@ class ReaderActivity : BaseRxActivity() * Initializes the reader subscriptions. */ init { - preferences.rotation().asImmediateFlow { setOrientation(it) } - .drop(1) - .onEach { - delay(250) - setOrientation(it) - } - .launchIn(lifecycleScope) - preferences.readerTheme().asFlow() .drop(1) // We only care about updates .onEach { recreate() } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderPresenter.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderPresenter.kt index 6a20fe2eec..c318c95961 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderPresenter.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderPresenter.kt @@ -20,6 +20,8 @@ import eu.kanade.tachiyomi.ui.reader.loader.ChapterLoader import eu.kanade.tachiyomi.ui.reader.model.ReaderChapter import eu.kanade.tachiyomi.ui.reader.model.ReaderPage import eu.kanade.tachiyomi.ui.reader.model.ViewerChapters +import eu.kanade.tachiyomi.ui.reader.setting.OrientationType +import eu.kanade.tachiyomi.ui.reader.setting.ReadingModeType import eu.kanade.tachiyomi.util.isLocal import eu.kanade.tachiyomi.util.lang.byteSize import eu.kanade.tachiyomi.util.lang.launchIO @@ -101,13 +103,13 @@ class ReaderPresenter( return@filter false } else if (preferences.skipFiltered()) { if ( - (manga.readFilter == Manga.SHOW_READ && !it.read) || - (manga.readFilter == Manga.SHOW_UNREAD && it.read) || + (manga.readFilter == Manga.CHAPTER_SHOW_READ && !it.read) || + (manga.readFilter == Manga.CHAPTER_SHOW_UNREAD && it.read) || ( - manga.downloadedFilter == Manga.SHOW_DOWNLOADED && + manga.downloadedFilter == Manga.CHAPTER_SHOW_DOWNLOADED && !downloadManager.isChapterDownloaded(it, manga) ) || - (manga.bookmarkedFilter == Manga.SHOW_BOOKMARKED && !it.bookmark) + (manga.bookmarkedFilter == Manga.CHAPTER_SHOW_BOOKMARKED && !it.bookmark) ) { return@filter false } @@ -127,9 +129,9 @@ class ReaderPresenter( } when (manga.sorting) { - Manga.SORTING_SOURCE -> ChapterLoadBySource().get(chaptersForReader) - Manga.SORTING_NUMBER -> ChapterLoadByNumber().get(chaptersForReader, selectedChapter) - Manga.SORTING_UPLOAD_DATE -> ChapterLoadByUploadDate().get(chaptersForReader) + Manga.CHAPTER_SORTING_SOURCE -> ChapterLoadBySource().get(chaptersForReader) + Manga.CHAPTER_SORTING_NUMBER -> ChapterLoadByNumber().get(chaptersForReader, selectedChapter) + Manga.CHAPTER_SORTING_UPLOAD_DATE -> ChapterLoadByUploadDate().get(chaptersForReader) else -> error("Unknown sorting method") }.map(::ReaderChapter) } @@ -489,18 +491,21 @@ class ReaderPresenter( /** * Returns the viewer position used by this manga or the default one. */ - fun getMangaViewer(resolveDefault: Boolean = true): Int { - val manga = manga ?: return preferences.defaultViewer() - return if (resolveDefault && manga.viewer == 0) preferences.defaultViewer() else manga.viewer + fun getMangaReadingMode(resolveDefault: Boolean = true): Int { + val default = preferences.defaultReadingMode() + return when { + resolveDefault && manga?.readingModeType == ReadingModeType.DEFAULT.flagValue -> default + else -> manga?.readingModeType ?: default + } } /** * Updates the viewer position for the open manga. */ - fun setMangaViewer(viewer: Int) { + fun setMangaReadingMode(readingModeType: Int) { val manga = manga ?: return - manga.viewer = viewer - db.updateMangaViewer(manga).executeAsBlocking() + manga.readingModeType = readingModeType + db.updateViewerFlags(manga).executeAsBlocking() Observable.timer(250, TimeUnit.MILLISECONDS, AndroidSchedulers.mainThread()) .subscribeFirst({ view, _ -> @@ -517,6 +522,36 @@ class ReaderPresenter( }) } + /** + * Returns the orientation type used by this manga or the default one. + */ + fun getMangaOrientationType(): Int { + val default = preferences.defaultOrientationType() + return when (manga?.orientationType) { + OrientationType.DEFAULT.flagValue -> default + else -> manga?.orientationType ?: default + } + } + + /** + * Updates the orientation type for the open manga. + */ + fun setMangaOrientationType(rotationType: Int) { + val manga = manga ?: return + manga.orientationType = rotationType + db.updateViewerFlags(manga).executeAsBlocking() + + Timber.i("Manga orientation is ${manga.orientationType}") + + Observable.timer(250, TimeUnit.MILLISECONDS, AndroidSchedulers.mainThread()) + .subscribeFirst({ view, _ -> + val currChapters = viewerChaptersRelay.value + if (currChapters != null) { + view.setOrientation(getMangaOrientationType()) + } + }) + } + /** * Saves the image of this [page] in the given [directory] and returns the file location. */ @@ -556,11 +591,14 @@ class ReaderPresenter( notifier.onClear() // Pictures directory. - val destDir = File( - Environment.getExternalStorageDirectory().absolutePath + - File.separator + Environment.DIRECTORY_PICTURES + - File.separator + context.getString(R.string.app_name) - ) + val baseDir = Environment.getExternalStorageDirectory().absolutePath + + File.separator + Environment.DIRECTORY_PICTURES + + File.separator + context.getString(R.string.app_name) + val destDir = if (preferences.folderPerManga()) { + File(baseDir + File.separator + manga.title) + } else { + File(baseDir) + } // Copy file in background. Observable.fromCallable { saveImage(page, destDir, manga) } @@ -621,6 +659,7 @@ class ReaderPresenter( if (manga.favorite) { coverCache.setCustomCoverToCache(manga, stream()) manga.updateCoverLastModified(db) + coverCache.clearMemoryCache() SetAsCoverResult.Success } else { SetAsCoverResult.AddToLibraryFirst diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/SaveImageNotifier.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/SaveImageNotifier.kt index 2814b412d5..98e8443040 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/SaveImageNotifier.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/SaveImageNotifier.kt @@ -2,10 +2,12 @@ package eu.kanade.tachiyomi.ui.reader import android.content.Context import android.graphics.Bitmap +import android.graphics.drawable.BitmapDrawable import androidx.core.app.NotificationCompat -import com.bumptech.glide.load.engine.DiskCacheStrategy +import coil.imageLoader +import coil.request.CachePolicy +import coil.request.ImageRequest import eu.kanade.tachiyomi.R -import eu.kanade.tachiyomi.data.glide.GlideApp import eu.kanade.tachiyomi.data.notification.NotificationHandler import eu.kanade.tachiyomi.data.notification.NotificationReceiver import eu.kanade.tachiyomi.data.notification.Notifications @@ -30,25 +32,25 @@ class SaveImageNotifier(private val context: Context) { get() = Notifications.ID_DOWNLOAD_IMAGE /** - * Called when image download/copy is complete. This method must be called in a background - * thread. + * Called when image download/copy is complete. * * @param file image file containing downloaded page image. */ fun onComplete(file: File) { - val bitmap = GlideApp.with(context) - .asBitmap() - .load(file) - .diskCacheStrategy(DiskCacheStrategy.NONE) - .skipMemoryCache(true) - .submit(720, 1280) - .get() - - if (bitmap != null) { - showCompleteNotification(file, bitmap) - } else { - onError(null) - } + val request = ImageRequest.Builder(context) + .data(file) + .memoryCachePolicy(CachePolicy.DISABLED) + .size(720, 1280) + .target( + onSuccess = { result -> + showCompleteNotification(file, (result as BitmapDrawable).bitmap) + }, + onError = { + onError(null) + } + ) + .build() + context.imageLoader.enqueue(request) } private fun showCompleteNotification(file: File, image: Bitmap) { diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/setting/OrientationType.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/setting/OrientationType.kt index 007b5e27a6..805c9452d4 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/setting/OrientationType.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/setting/OrientationType.kt @@ -5,16 +5,20 @@ import androidx.annotation.DrawableRes import androidx.annotation.StringRes import eu.kanade.tachiyomi.R -enum class OrientationType(val prefValue: Int, val flag: Int, @StringRes val stringRes: Int, @DrawableRes val iconRes: Int) { - FREE(1, ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED, R.string.rotation_free, R.drawable.ic_screen_rotation_24dp), - PORTRAIT(2, ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT, R.string.rotation_portrait, R.drawable.ic_stay_current_portrait_24dp), - LANDSCAPE(3, ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE, R.string.rotation_landscape, R.drawable.ic_stay_current_landscape_24dp), - LOCKED_PORTRAIT(4, ActivityInfo.SCREEN_ORIENTATION_PORTRAIT, R.string.rotation_force_portrait, R.drawable.ic_screen_lock_portrait_24dp), - LOCKED_LANDSCAPE(5, ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE, R.string.rotation_force_landscape, R.drawable.ic_screen_lock_landscape_24dp), - ; +enum class OrientationType(val prefValue: Int, val flag: Int, @StringRes val stringRes: Int, @DrawableRes val iconRes: Int, val flagValue: Int) { + // TODO Default icon + DEFAULT(0, ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED, R.string.default_rotation_type, R.drawable.ic_screen_rotation_24dp, 0x00000000), + FREE(1, ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED, R.string.rotation_free, R.drawable.ic_screen_rotation_24dp, 0x00000008), + PORTRAIT(2, ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT, R.string.rotation_portrait, R.drawable.ic_stay_current_portrait_24dp, 0x00000010), + LANDSCAPE(3, ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE, R.string.rotation_landscape, R.drawable.ic_stay_current_landscape_24dp, 0x00000018), + LOCKED_PORTRAIT(4, ActivityInfo.SCREEN_ORIENTATION_PORTRAIT, R.string.rotation_force_portrait, R.drawable.ic_screen_lock_portrait_24dp, 0x00000020), + LOCKED_LANDSCAPE(5, ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE, R.string.rotation_force_landscape, R.drawable.ic_screen_lock_landscape_24dp, 0x00000028); companion object { - fun fromPreference(preference: Int): OrientationType = - values().find { it.prefValue == preference } ?: FREE + const val MASK = 0x00000038 + + fun fromPreference(preference: Int?): OrientationType = values().find { it.flagValue == preference } ?: FREE + + fun fromSpinner(position: Int?) = values().find { value -> value.prefValue == position } ?: DEFAULT } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/setting/ReaderGeneralSettings.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/setting/ReaderGeneralSettings.kt index 152ce33c70..96c66b76b9 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/setting/ReaderGeneralSettings.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/setting/ReaderGeneralSettings.kt @@ -32,7 +32,6 @@ class ReaderGeneralSettings @JvmOverloads constructor(context: Context, attrs: A * Init general reader preferences. */ private fun initGeneralPreferences() { - binding.rotationMode.bindToPreference(preferences.rotation(), 1) binding.backgroundColor.bindToIntPreference(preferences.readerTheme(), R.array.reader_themes_values) binding.showPageNumber.bindToPreference(preferences.showPageNumber()) binding.fullscreen.bindToPreference(preferences.fullscreen()) diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/setting/ReaderReadingModeSettings.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/setting/ReaderReadingModeSettings.kt index b309794b14..58b35ddcbd 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/setting/ReaderReadingModeSettings.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/setting/ReaderReadingModeSettings.kt @@ -43,16 +43,23 @@ class ReaderReadingModeSettings @JvmOverloads constructor(context: Context, attr */ private fun initGeneralPreferences() { binding.viewer.onItemSelectedListener = { position -> - (context as ReaderActivity).presenter.setMangaViewer(position) + val readingModeType = ReadingModeType.fromSpinner(position) + (context as ReaderActivity).presenter.setMangaReadingMode(readingModeType.flagValue) - val mangaViewer = (context as ReaderActivity).presenter.getMangaViewer() - if (mangaViewer == ReadingModeType.WEBTOON.prefValue || mangaViewer == ReadingModeType.CONTINUOUS_VERTICAL.prefValue) { + val mangaViewer = (context as ReaderActivity).presenter.getMangaReadingMode() + if (mangaViewer == ReadingModeType.WEBTOON.flagValue || mangaViewer == ReadingModeType.CONTINUOUS_VERTICAL.flagValue) { initWebtoonPreferences() } else { initPagerPreferences() } } - binding.viewer.setSelection((context as ReaderActivity).presenter.manga?.viewer ?: 0) + binding.viewer.setSelection((context as ReaderActivity).presenter.manga?.readingModeType.let { ReadingModeType.fromPreference(it).prefValue }) + + binding.rotationMode.onItemSelectedListener = { position -> + val rotationType = OrientationType.fromSpinner(position) + (context as ReaderActivity).presenter.setMangaOrientationType(rotationType.flagValue) + } + binding.rotationMode.setSelection((context as ReaderActivity).presenter.manga?.orientationType.let { OrientationType.fromPreference(it).prefValue }) } /** diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/setting/ReadingModeType.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/setting/ReadingModeType.kt index 9365466458..d38b398774 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/setting/ReadingModeType.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/setting/ReadingModeType.kt @@ -4,21 +4,25 @@ import androidx.annotation.DrawableRes import androidx.annotation.StringRes import eu.kanade.tachiyomi.R -enum class ReadingModeType(val prefValue: Int, @StringRes val stringRes: Int, @DrawableRes val iconRes: Int) { - DEFAULT(0, R.string.default_viewer, R.drawable.ic_reader_default_24dp), - LEFT_TO_RIGHT(1, R.string.left_to_right_viewer, R.drawable.ic_reader_ltr_24dp), - RIGHT_TO_LEFT(2, R.string.right_to_left_viewer, R.drawable.ic_reader_rtl_24dp), - VERTICAL(3, R.string.vertical_viewer, R.drawable.ic_reader_vertical_24dp), - WEBTOON(4, R.string.webtoon_viewer, R.drawable.ic_reader_webtoon_24dp), - CONTINUOUS_VERTICAL(5, R.string.vertical_plus_viewer, R.drawable.ic_reader_continuous_vertical_24dp), +enum class ReadingModeType(val prefValue: Int, @StringRes val stringRes: Int, @DrawableRes val iconRes: Int, val flagValue: Int) { + DEFAULT(0, R.string.default_viewer, R.drawable.ic_reader_default_24dp, 0x00000000), + LEFT_TO_RIGHT(1, R.string.left_to_right_viewer, R.drawable.ic_reader_ltr_24dp, 0x00000001), + RIGHT_TO_LEFT(2, R.string.right_to_left_viewer, R.drawable.ic_reader_rtl_24dp, 0x00000002), + VERTICAL(3, R.string.vertical_viewer, R.drawable.ic_reader_vertical_24dp, 0x00000003), + WEBTOON(4, R.string.webtoon_viewer, R.drawable.ic_reader_webtoon_24dp, 0x00000004), + CONTINUOUS_VERTICAL(5, R.string.vertical_plus_viewer, R.drawable.ic_reader_continuous_vertical_24dp, 0x00000005), ; companion object { - fun fromPreference(preference: Int): ReadingModeType = values().find { it.prefValue == preference } ?: DEFAULT + const val MASK = 0x00000007 + + fun fromPreference(preference: Int?): ReadingModeType = values().find { it.flagValue == preference } ?: DEFAULT fun isPagerType(preference: Int): Boolean { val mode = fromPreference(preference) return mode == LEFT_TO_RIGHT || mode == RIGHT_TO_LEFT || mode == VERTICAL } + + fun fromSpinner(position: Int?) = values().find { value -> value.prefValue == position } ?: DEFAULT } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/pager/PagerPageHolder.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/pager/PagerPageHolder.kt index 2a9cf66351..f5abc27285 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/pager/PagerPageHolder.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/pager/PagerPageHolder.kt @@ -2,7 +2,7 @@ package eu.kanade.tachiyomi.ui.reader.viewer.pager import android.annotation.SuppressLint import android.graphics.PointF -import android.graphics.drawable.Drawable +import android.graphics.drawable.Animatable import android.view.GestureDetector import android.view.Gravity import android.view.MotionEvent @@ -14,19 +14,13 @@ import android.widget.ImageView import android.widget.LinearLayout import android.widget.TextView import androidx.core.view.isVisible -import com.bumptech.glide.load.DataSource -import com.bumptech.glide.load.engine.DiskCacheStrategy -import com.bumptech.glide.load.engine.GlideException -import com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions -import com.bumptech.glide.load.resource.gif.GifDrawable -import com.bumptech.glide.request.RequestListener -import com.bumptech.glide.request.target.Target -import com.bumptech.glide.request.transition.NoTransition +import coil.imageLoader +import coil.request.CachePolicy +import coil.request.ImageRequest import com.davemorrissey.labs.subscaleview.ImageSource import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView import com.github.chrisbanes.photoview.PhotoView import eu.kanade.tachiyomi.R -import eu.kanade.tachiyomi.data.glide.GlideApp import eu.kanade.tachiyomi.source.model.Page import eu.kanade.tachiyomi.ui.reader.model.InsertPage import eu.kanade.tachiyomi.ui.reader.model.ReaderPage @@ -41,6 +35,7 @@ import rx.Subscription import rx.android.schedulers.AndroidSchedulers import rx.schedulers.Schedulers import java.io.InputStream +import java.nio.ByteBuffer import java.util.concurrent.TimeUnit /** @@ -480,38 +475,24 @@ class PagerPageHolder( * Extension method to set a [stream] into this ImageView. */ private fun ImageView.setImage(stream: InputStream) { - GlideApp.with(this) - .load(stream) - .skipMemoryCache(true) - .diskCacheStrategy(DiskCacheStrategy.NONE) - .transition(DrawableTransitionOptions.with(NoTransition.getFactory())) - .listener( - object : RequestListener { - override fun onLoadFailed( - e: GlideException?, - model: Any?, - target: Target?, - isFirstResource: Boolean - ): Boolean { - onImageDecodeError() - return false - } - - override fun onResourceReady( - resource: Drawable?, - model: Any?, - target: Target?, - dataSource: DataSource?, - isFirstResource: Boolean - ): Boolean { - if (resource is GifDrawable) { - resource.setLoopCount(GifDrawable.LOOP_INTRINSIC) - } - onImageDecoded() - return false + val request = ImageRequest.Builder(context) + .data(ByteBuffer.wrap(stream.readBytes())) + .memoryCachePolicy(CachePolicy.DISABLED) + .diskCachePolicy(CachePolicy.DISABLED) + .target( + onSuccess = { result -> + if (result is Animatable) { + result.start() } + setImageDrawable(result) + onImageDecoded() + }, + onError = { + onImageDecodeError() } ) - .into(this) + .crossfade(false) + .build() + context.imageLoader.enqueue(request) } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/webtoon/WebtoonPageHolder.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/webtoon/WebtoonPageHolder.kt index fc72f09748..5092572b25 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/webtoon/WebtoonPageHolder.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/webtoon/WebtoonPageHolder.kt @@ -2,7 +2,7 @@ package eu.kanade.tachiyomi.ui.reader.viewer.webtoon import android.annotation.SuppressLint import android.content.res.Resources -import android.graphics.drawable.Drawable +import android.graphics.drawable.Animatable import android.view.Gravity import android.view.ViewGroup import android.view.ViewGroup.LayoutParams.MATCH_PARENT @@ -14,18 +14,13 @@ import android.widget.TextView import androidx.appcompat.widget.AppCompatButton import androidx.appcompat.widget.AppCompatImageView import androidx.core.view.isVisible -import com.bumptech.glide.load.DataSource -import com.bumptech.glide.load.engine.DiskCacheStrategy -import com.bumptech.glide.load.engine.GlideException -import com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions -import com.bumptech.glide.load.resource.gif.GifDrawable -import com.bumptech.glide.request.RequestListener -import com.bumptech.glide.request.target.Target -import com.bumptech.glide.request.transition.NoTransition +import coil.clear +import coil.imageLoader +import coil.request.CachePolicy +import coil.request.ImageRequest import com.davemorrissey.labs.subscaleview.ImageSource import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView import eu.kanade.tachiyomi.R -import eu.kanade.tachiyomi.data.glide.GlideApp import eu.kanade.tachiyomi.source.model.Page import eu.kanade.tachiyomi.ui.reader.model.ReaderPage import eu.kanade.tachiyomi.ui.reader.viewer.ReaderProgressBar @@ -37,6 +32,7 @@ import rx.Subscription import rx.android.schedulers.AndroidSchedulers import rx.schedulers.Schedulers import java.io.InputStream +import java.nio.ByteBuffer import java.util.concurrent.TimeUnit /** @@ -146,7 +142,7 @@ class WebtoonPageHolder( removeDecodeErrorLayout() subsamplingImageView?.recycle() subsamplingImageView?.isVisible = false - imageView?.let { GlideApp.with(frame).clear(it) } + imageView?.clear() imageView?.isVisible = false progressBar.setProgress(0) } @@ -512,38 +508,24 @@ class WebtoonPageHolder( * Extension method to set a [stream] into this ImageView. */ private fun ImageView.setImage(stream: InputStream) { - GlideApp.with(this) - .load(stream) - .skipMemoryCache(true) - .diskCacheStrategy(DiskCacheStrategy.NONE) - .transition(DrawableTransitionOptions.with(NoTransition.getFactory())) - .listener( - object : RequestListener { - override fun onLoadFailed( - e: GlideException?, - model: Any?, - target: Target?, - isFirstResource: Boolean - ): Boolean { - onImageDecodeError() - return false - } - - override fun onResourceReady( - resource: Drawable?, - model: Any?, - target: Target?, - dataSource: DataSource?, - isFirstResource: Boolean - ): Boolean { - if (resource is GifDrawable) { - resource.setLoopCount(GifDrawable.LOOP_INTRINSIC) - } - onImageDecoded() - return false + val request = ImageRequest.Builder(context) + .data(ByteBuffer.wrap(stream.readBytes())) + .memoryCachePolicy(CachePolicy.DISABLED) + .diskCachePolicy(CachePolicy.DISABLED) + .target( + onSuccess = { result -> + if (result is Animatable) { + result.start() } + setImageDrawable(result) + onImageDecoded() + }, + onError = { + onImageDecodeError() } ) - .into(this) + .crossfade(false) + .build() + context.imageLoader.enqueue(request) } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/recent/history/HistoryHolder.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/recent/history/HistoryHolder.kt index 3d10d931d0..8661cb845e 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/recent/history/HistoryHolder.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/recent/history/HistoryHolder.kt @@ -1,15 +1,12 @@ package eu.kanade.tachiyomi.ui.recent.history import android.view.View -import com.bumptech.glide.load.engine.DiskCacheStrategy -import com.bumptech.glide.load.resource.bitmap.CenterCrop -import com.bumptech.glide.load.resource.bitmap.RoundedCorners -import com.bumptech.glide.request.RequestOptions +import coil.clear +import coil.loadAny +import coil.transform.RoundedCornersTransformation import eu.davidea.viewholders.FlexibleViewHolder import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.data.database.models.MangaChapterHistory -import eu.kanade.tachiyomi.data.glide.GlideApp -import eu.kanade.tachiyomi.data.glide.toMangaThumbnail import eu.kanade.tachiyomi.databinding.HistoryItemBinding import eu.kanade.tachiyomi.util.lang.toTimestampString import java.util.Date @@ -68,15 +65,11 @@ class HistoryHolder( binding.mangaSubtitle.text = Date(history.last_read).toTimestampString() } - val radius = itemView.context.resources.getDimensionPixelSize(R.dimen.card_radius) - val requestOptions = RequestOptions().transform(CenterCrop(), RoundedCorners(radius)) - // Set cover - GlideApp.with(itemView.context).clear(binding.cover) - GlideApp.with(itemView.context) - .load(manga.toMangaThumbnail()) - .diskCacheStrategy(DiskCacheStrategy.RESOURCE) - .apply(requestOptions) - .into(binding.cover) + val radius = itemView.context.resources.getDimension(R.dimen.card_radius) + binding.cover.clear() + binding.cover.loadAny(item.manga) { + transformations(RoundedCornersTransformation(radius)) + } } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/recent/history/HistoryPresenter.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/recent/history/HistoryPresenter.kt index 2890e5f00d..0193d3df61 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/recent/history/HistoryPresenter.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/recent/history/HistoryPresenter.kt @@ -122,9 +122,9 @@ class HistoryPresenter : BasePresenter() { } val sortFunction: (Chapter, Chapter) -> Int = when (manga.sorting) { - Manga.SORTING_SOURCE -> { c1, c2 -> c2.source_order.compareTo(c1.source_order) } - Manga.SORTING_NUMBER -> { c1, c2 -> c1.chapter_number.compareTo(c2.chapter_number) } - Manga.SORTING_UPLOAD_DATE -> { c1, c2 -> c1.date_upload.compareTo(c2.date_upload) } + Manga.CHAPTER_SORTING_SOURCE -> { c1, c2 -> c2.source_order.compareTo(c1.source_order) } + Manga.CHAPTER_SORTING_NUMBER -> { c1, c2 -> c1.chapter_number.compareTo(c2.chapter_number) } + Manga.CHAPTER_SORTING_UPLOAD_DATE -> { c1, c2 -> c1.date_upload.compareTo(c2.date_upload) } else -> throw NotImplementedError("Unknown sorting method") } @@ -133,8 +133,8 @@ class HistoryPresenter : BasePresenter() { val currChapterIndex = chapters.indexOfFirst { chapter.id == it.id } return when (manga.sorting) { - Manga.SORTING_SOURCE -> chapters.getOrNull(currChapterIndex + 1) - Manga.SORTING_NUMBER -> { + Manga.CHAPTER_SORTING_SOURCE -> chapters.getOrNull(currChapterIndex + 1) + Manga.CHAPTER_SORTING_NUMBER -> { val chapterNumber = chapter.chapter_number ((currChapterIndex + 1) until chapters.size) @@ -144,7 +144,7 @@ class HistoryPresenter : BasePresenter() { it.chapter_number <= chapterNumber + 1 } } - Manga.SORTING_UPLOAD_DATE -> { + Manga.CHAPTER_SORTING_UPLOAD_DATE -> { chapters.drop(currChapterIndex + 1) .firstOrNull { it.date_upload >= chapter.date_upload } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/recent/updates/UpdatesHolder.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/recent/updates/UpdatesHolder.kt index e0afdcbdce..e3226de05b 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/recent/updates/UpdatesHolder.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/recent/updates/UpdatesHolder.kt @@ -2,13 +2,10 @@ package eu.kanade.tachiyomi.ui.recent.updates import android.view.View import androidx.core.view.isVisible -import com.bumptech.glide.load.engine.DiskCacheStrategy -import com.bumptech.glide.load.resource.bitmap.CenterCrop -import com.bumptech.glide.load.resource.bitmap.RoundedCorners -import com.bumptech.glide.request.RequestOptions +import coil.clear +import coil.loadAny +import coil.transform.RoundedCornersTransformation import eu.kanade.tachiyomi.R -import eu.kanade.tachiyomi.data.glide.GlideApp -import eu.kanade.tachiyomi.data.glide.toMangaThumbnail import eu.kanade.tachiyomi.databinding.UpdatesItemBinding import eu.kanade.tachiyomi.source.LocalSource import eu.kanade.tachiyomi.ui.manga.chapter.base.BaseChapterHolder @@ -58,15 +55,10 @@ class UpdatesHolder(private val view: View, private val adapter: UpdatesAdapter) binding.download.setState(item.status, item.progress) // Set cover - GlideApp.with(itemView.context).clear(binding.mangaCover) - - val radius = itemView.context.resources.getDimensionPixelSize(R.dimen.card_radius) - val requestOptions = RequestOptions().transform(CenterCrop(), RoundedCorners(radius)) - GlideApp.with(itemView.context) - .load(item.manga.toMangaThumbnail()) - .diskCacheStrategy(DiskCacheStrategy.RESOURCE) - .apply(requestOptions) - .dontAnimate() - .into(binding.mangaCover) + val radius = itemView.context.resources.getDimension(R.dimen.card_radius) + binding.mangaCover.clear() + binding.mangaCover.loadAny(item.manga) { + transformations(RoundedCornersTransformation(radius)) + } } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsAdvancedController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsAdvancedController.kt index 47bb095a1b..3b11796002 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsAdvancedController.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsAdvancedController.kt @@ -4,7 +4,6 @@ import android.annotation.SuppressLint import android.app.Dialog import android.content.ActivityNotFoundException import android.content.Intent -import android.os.Build import android.os.Bundle import android.provider.Settings import androidx.core.net.toUri @@ -67,27 +66,25 @@ class SettingsAdvancedController : SettingsController() { } } - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - preference { - key = "pref_disable_battery_optimization" - titleRes = R.string.pref_disable_battery_optimization - summaryRes = R.string.pref_disable_battery_optimization_summary + preference { + key = "pref_disable_battery_optimization" + titleRes = R.string.pref_disable_battery_optimization + summaryRes = R.string.pref_disable_battery_optimization_summary - onClick { - val packageName: String = context.packageName - if (!context.powerManager.isIgnoringBatteryOptimizations(packageName)) { - try { - val intent = Intent().apply { - action = Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS - data = "package:$packageName".toUri() - } - startActivity(intent) - } catch (e: ActivityNotFoundException) { - context.toast(R.string.battery_optimization_setting_activity_not_found) + onClick { + val packageName: String = context.packageName + if (!context.powerManager.isIgnoringBatteryOptimizations(packageName)) { + try { + val intent = Intent().apply { + action = Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS + data = "package:$packageName".toUri() } - } else { - context.toast(R.string.battery_optimization_disabled) + startActivity(intent) + } catch (e: ActivityNotFoundException) { + context.toast(R.string.battery_optimization_setting_activity_not_found) } + } else { + context.toast(R.string.battery_optimization_disabled) } } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsController.kt index 0254576521..fed170be1a 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsController.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsController.kt @@ -23,7 +23,9 @@ import eu.kanade.tachiyomi.data.preference.PreferencesHelper import eu.kanade.tachiyomi.ui.base.controller.BaseController import eu.kanade.tachiyomi.ui.base.controller.RootController import eu.kanade.tachiyomi.util.system.getResourceColor +import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.MainScope +import kotlinx.coroutines.cancel import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.api.get @@ -31,10 +33,11 @@ abstract class SettingsController : PreferenceController() { var preferenceKey: String? = null val preferences: PreferencesHelper = Injekt.get() - val viewScope = MainScope() + lateinit var viewScope: CoroutineScope private var themedContext: Context? = null override fun onCreateView(inflater: LayoutInflater, container: ViewGroup, savedInstanceState: Bundle?): View { + viewScope = MainScope() val view = super.onCreateView(inflater, container, savedInstanceState) if (this is RootController) { @@ -79,6 +82,7 @@ abstract class SettingsController : PreferenceController() { override fun onDestroyView(view: View) { super.onDestroyView(view) + viewScope.cancel() themedContext = null } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsReaderController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsReaderController.kt index 6524d3b81a..e2ef97a564 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsReaderController.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsReaderController.kt @@ -5,6 +5,8 @@ import androidx.preference.PreferenceScreen import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.data.preference.PreferenceValues.TappingInvertMode import eu.kanade.tachiyomi.data.preference.asImmediateFlow +import eu.kanade.tachiyomi.ui.reader.setting.OrientationType +import eu.kanade.tachiyomi.ui.reader.setting.ReadingModeType import eu.kanade.tachiyomi.util.preference.defaultValue import eu.kanade.tachiyomi.util.preference.entriesRes import eu.kanade.tachiyomi.util.preference.intListPreference @@ -23,7 +25,7 @@ class SettingsReaderController : SettingsController() { titleRes = R.string.pref_category_reader intListPreference { - key = Keys.defaultViewer + key = Keys.defaultReadingMode titleRes = R.string.pref_viewer_type entriesRes = arrayOf( R.string.left_to_right_viewer, @@ -32,8 +34,9 @@ class SettingsReaderController : SettingsController() { R.string.webtoon_viewer, R.string.vertical_plus_viewer ) - entryValues = arrayOf("1", "2", "3", "4", "5") - defaultValue = "2" + entryValues = ReadingModeType.values().drop(1) + .map { value -> "${value.flagValue}" }.toTypedArray() + defaultValue = "${ReadingModeType.RIGHT_TO_LEFT.flagValue}" summary = "%s" } intListPreference { @@ -74,7 +77,7 @@ class SettingsReaderController : SettingsController() { titleRes = R.string.pref_category_display intListPreference { - key = Keys.rotation + key = Keys.defaultOrientationType titleRes = R.string.pref_rotation_type entriesRes = arrayOf( R.string.rotation_free, @@ -83,8 +86,9 @@ class SettingsReaderController : SettingsController() { R.string.rotation_force_portrait, R.string.rotation_force_landscape, ) - entryValues = arrayOf("1", "2", "3", "4", "5") - defaultValue = "1" + entryValues = OrientationType.values().drop(1) + .map { value -> "${value.flagValue}" }.toTypedArray() + defaultValue = "${OrientationType.FREE.flagValue}" summary = "%s" } intListPreference { @@ -297,11 +301,6 @@ class SettingsReaderController : SettingsController() { titleRes = R.string.pref_read_with_tapping defaultValue = true } - switchPreference { - key = Keys.readWithLongTap - titleRes = R.string.pref_read_with_long_tap - defaultValue = true - } switchPreference { key = Keys.readWithVolumeKeys titleRes = R.string.pref_read_with_volume_keys @@ -315,5 +314,21 @@ class SettingsReaderController : SettingsController() { preferences.readWithVolumeKeys().asImmediateFlow { isVisible = it }.launchIn(viewScope) } } + + preferenceCategory { + titleRes = R.string.pref_reader_actions + + switchPreference { + key = Keys.readWithLongTap + titleRes = R.string.pref_read_with_long_tap + defaultValue = true + } + switchPreference { + key = Keys.folderPerManga + titleRes = R.string.pref_create_folder_per_manga + summaryRes = R.string.pref_create_folder_per_manga_summary + defaultValue = false + } + } } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/util/chapter/ChapterSettingsHelper.kt b/app/src/main/java/eu/kanade/tachiyomi/util/chapter/ChapterSettingsHelper.kt index cccb214d6c..b0b850e0b6 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/util/chapter/ChapterSettingsHelper.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/util/chapter/ChapterSettingsHelper.kt @@ -17,7 +17,8 @@ object ChapterSettingsHelper { fun setGlobalSettings(manga: Manga?) { manga?.let { prefs.setChapterSettingsDefault(it) - db.updateFlags(it).executeAsBlocking() + db.updateChapterFlags(it).executeAsBlocking() + db.updateViewerFlags(it).executeAsBlocking() } } @@ -34,7 +35,8 @@ object ChapterSettingsHelper { setChapterOrder(prefs.sortChapterByAscendingOrDescending()) } - db.updateFlags(manga).executeAsBlocking() + db.updateChapterFlags(manga).executeAsBlocking() + db.updateViewerFlags(manga).executeAsBlocking() } /** @@ -54,7 +56,8 @@ object ChapterSettingsHelper { manga } - db.updateFlags(updatedMangas).executeAsBlocking() + db.updateChapterFlags(updatedMangas).executeAsBlocking() + db.updateViewerFlags(updatedMangas).executeAsBlocking() } } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/util/lang/StringExtensions.kt b/app/src/main/java/eu/kanade/tachiyomi/util/lang/StringExtensions.kt index 728615942b..ab8c1c933b 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/util/lang/StringExtensions.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/util/lang/StringExtensions.kt @@ -7,7 +7,7 @@ import kotlin.math.floor * Replaces the given string to have at most [count] characters using [replacement] at its end. * If [replacement] is longer than [count] an exception will be thrown when `length > count`. */ -fun String.chop(count: Int, replacement: String = "..."): String { +fun String.chop(count: Int, replacement: String = "…"): String { return if (length > count) { take(count - replacement.length) + replacement } else { diff --git a/app/src/main/java/eu/kanade/tachiyomi/util/system/BiometricUtil.kt b/app/src/main/java/eu/kanade/tachiyomi/util/system/BiometricUtil.kt index c7e397bef4..64f734e9eb 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/util/system/BiometricUtil.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/util/system/BiometricUtil.kt @@ -33,7 +33,7 @@ object BiometricUtil { * Returns whether the device is secured with a PIN, pattern or password. */ private fun isLegacySecured(context: Context): Boolean { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && Build.VERSION.SDK_INT <= Build.VERSION_CODES.Q) { + if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.Q) { if (context.keyguardManager.isDeviceSecure) { return true } diff --git a/app/src/main/java/eu/kanade/tachiyomi/util/system/WebViewClientCompat.kt b/app/src/main/java/eu/kanade/tachiyomi/util/system/WebViewClientCompat.kt index 8d2eea7cf0..5ccbb4f7e5 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/util/system/WebViewClientCompat.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/util/system/WebViewClientCompat.kt @@ -54,7 +54,6 @@ abstract class WebViewClientCompat : WebViewClient() { return shouldInterceptRequestCompat(view, url) } - @TargetApi(Build.VERSION_CODES.M) final override fun onReceivedError( view: WebView, request: WebResourceRequest, @@ -78,7 +77,6 @@ abstract class WebViewClientCompat : WebViewClient() { onReceivedErrorCompat(view, errorCode, description, failingUrl, failingUrl == view.url) } - @TargetApi(Build.VERSION_CODES.M) final override fun onReceivedHttpError( view: WebView, request: WebResourceRequest, diff --git a/app/src/main/java/eu/kanade/tachiyomi/widget/ElevationAppBarLayout.kt b/app/src/main/java/eu/kanade/tachiyomi/widget/ElevationAppBarLayout.kt index 66057589fb..ad0e52e03b 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/widget/ElevationAppBarLayout.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/widget/ElevationAppBarLayout.kt @@ -3,7 +3,6 @@ package eu.kanade.tachiyomi.widget import android.animation.ObjectAnimator import android.animation.StateListAnimator import android.content.Context -import android.os.Build import android.util.AttributeSet import com.google.android.material.R import com.google.android.material.appbar.AppBarLayout @@ -20,12 +19,7 @@ class ElevationAppBarLayout @JvmOverloads constructor( } fun enableElevation(liftOnScroll: Boolean) { - if (Build.VERSION.SDK_INT == Build.VERSION_CODES.LOLLIPOP) { - // Delay to avoid crash - post { setElevation(liftOnScroll) } - } else { - setElevation(liftOnScroll) - } + setElevation(liftOnScroll) } private fun setElevation(liftOnScroll: Boolean) { diff --git a/app/src/main/java/eu/kanade/tachiyomi/widget/RevealAnimationView.kt b/app/src/main/java/eu/kanade/tachiyomi/widget/RevealAnimationView.kt index 8201d3b4ac..6bd1546f19 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/widget/RevealAnimationView.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/widget/RevealAnimationView.kt @@ -49,10 +49,8 @@ class RevealAnimationView @JvmOverloads constructor(context: Context, attrs: Att * @param centerX x starting point * @param centerY y starting point * @param listener animation listener - * - * @return sdk version lower then 21 */ - fun showRevealEffect(centerX: Int, centerY: Int, listener: Animator.AnimatorListener): Boolean { + fun showRevealEffect(centerX: Int, centerY: Int, listener: Animator.AnimatorListener) { this.isVisible = true val height = this.height @@ -71,6 +69,5 @@ class RevealAnimationView @JvmOverloads constructor(context: Context, attrs: Att anim.addListener(listener) anim.start() - return true } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/widget/StateImageViewTarget.kt b/app/src/main/java/eu/kanade/tachiyomi/widget/StateImageViewTarget.kt index a488002306..0600e5d619 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/widget/StateImageViewTarget.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/widget/StateImageViewTarget.kt @@ -3,61 +3,41 @@ package eu.kanade.tachiyomi.widget import android.graphics.drawable.Drawable import android.view.View import android.widget.ImageView -import android.widget.ImageView.ScaleType -import androidx.appcompat.content.res.AppCompatResources import androidx.core.view.isVisible -import com.bumptech.glide.request.target.ImageViewTarget -import com.bumptech.glide.request.transition.Transition -import eu.kanade.tachiyomi.R -import eu.kanade.tachiyomi.util.system.getResourceColor +import coil.drawable.CrossfadeDrawable +import coil.target.ImageViewTarget /** - * A glide target to display an image with an optional view to show while loading and a configurable - * error drawable. + * A Coil target to display an image with an optional view to show while loading. * - * @param view the view where the image will be loaded - * @param progress an optional view to show when the image is loading. - * @param errorDrawableRes the error drawable resource to show. - * @param errorScaleType the scale type for the error drawable, [ScaleType.CENTER] by default. + * @param target the view where the image will be loaded + * @param progress the view to show when the image is loading. + * @param crossfadeDuration duration in millisecond to crossfade the result drawable */ class StateImageViewTarget( - view: ImageView, - val progress: View? = null, - private val errorDrawableRes: Int = R.drawable.ic_broken_image_grey_24dp, - private val errorScaleType: ScaleType = ScaleType.CENTER -) : ImageViewTarget(view) { - - private var resource: Drawable? = null - - private val imageScaleType = view.scaleType - - override fun setResource(resource: Drawable?) { - view.setImageDrawable(resource) - } - - override fun onLoadStarted(placeholder: Drawable?) { - progress?.isVisible = true - super.onLoadStarted(placeholder) - } - - override fun onLoadFailed(errorDrawable: Drawable?) { - progress?.isVisible = false - view.scaleType = errorScaleType - - val vector = AppCompatResources.getDrawable(view.context, errorDrawableRes) - vector?.setTint(view.context.getResourceColor(R.attr.colorOnBackground, 0.38f)) - view.setImageDrawable(vector) + private val target: ImageView, + private val progress: View, + private val crossfadeDuration: Int = 0 +) : ImageViewTarget(target) { + override fun onStart(placeholder: Drawable?) { + progress.isVisible = true } - override fun onLoadCleared(placeholder: Drawable?) { - progress?.isVisible = false - super.onLoadCleared(placeholder) + override fun onSuccess(result: Drawable) { + progress.isVisible = false + if (crossfadeDuration > 0) { + val crossfadeResult = CrossfadeDrawable(target.drawable, result, durationMillis = crossfadeDuration) + target.setImageDrawable(crossfadeResult) + crossfadeResult.start() + } else { + target.setImageDrawable(result) + } } - override fun onResourceReady(resource: Drawable, transition: Transition?) { - progress?.isVisible = false - view.scaleType = imageScaleType - super.onResourceReady(resource, transition) - this.resource = resource + override fun onError(error: Drawable?) { + progress.isVisible = false + if (error != null) { + target.setImageDrawable(error) + } } } diff --git a/app/src/main/res/drawable-v26/sc_collections_bookmark_48dp.xml b/app/src/main/res/drawable-v26/sc_collections_bookmark_48dp.xml new file mode 100644 index 0000000000..f2fb02a3e5 --- /dev/null +++ b/app/src/main/res/drawable-v26/sc_collections_bookmark_48dp.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + + diff --git a/app/src/main/res/drawable-v26/sc_explore_48dp.xml b/app/src/main/res/drawable-v26/sc_explore_48dp.xml new file mode 100644 index 0000000000..9a574c0644 --- /dev/null +++ b/app/src/main/res/drawable-v26/sc_explore_48dp.xml @@ -0,0 +1,19 @@ + + + + + + + + + + + diff --git a/app/src/main/res/drawable-v26/sc_history_48dp.xml b/app/src/main/res/drawable-v26/sc_history_48dp.xml new file mode 100644 index 0000000000..4ce51f848c --- /dev/null +++ b/app/src/main/res/drawable-v26/sc_history_48dp.xml @@ -0,0 +1,19 @@ + + + + + + + + + + + diff --git a/app/src/main/res/drawable-v26/sc_new_releases_48dp.xml b/app/src/main/res/drawable-v26/sc_new_releases_48dp.xml new file mode 100644 index 0000000000..9b8fd1cb61 --- /dev/null +++ b/app/src/main/res/drawable-v26/sc_new_releases_48dp.xml @@ -0,0 +1,19 @@ + + + + + + + + + + + diff --git a/app/src/main/res/drawable/sc_collections_bookmark_48dp.xml b/app/src/main/res/drawable/sc_collections_bookmark_48dp.xml index c16085854c..b7da6fdc35 100644 --- a/app/src/main/res/drawable/sc_collections_bookmark_48dp.xml +++ b/app/src/main/res/drawable/sc_collections_bookmark_48dp.xml @@ -6,17 +6,17 @@ android:viewportWidth="48"> diff --git a/app/src/main/res/drawable/sc_explore_48dp.xml b/app/src/main/res/drawable/sc_explore_48dp.xml index 911db2d81a..6617825eee 100644 --- a/app/src/main/res/drawable/sc_explore_48dp.xml +++ b/app/src/main/res/drawable/sc_explore_48dp.xml @@ -6,14 +6,14 @@ android:viewportWidth="48"> diff --git a/app/src/main/res/drawable/sc_history_48dp.xml b/app/src/main/res/drawable/sc_history_48dp.xml index 76d656f30c..8a2b52150a 100644 --- a/app/src/main/res/drawable/sc_history_48dp.xml +++ b/app/src/main/res/drawable/sc_history_48dp.xml @@ -6,14 +6,14 @@ android:viewportWidth="48"> diff --git a/app/src/main/res/drawable/sc_new_releases_48dp.xml b/app/src/main/res/drawable/sc_new_releases_48dp.xml index 82ce2ac6f1..338211308d 100644 --- a/app/src/main/res/drawable/sc_new_releases_48dp.xml +++ b/app/src/main/res/drawable/sc_new_releases_48dp.xml @@ -6,14 +6,14 @@ android:viewportWidth="48"> diff --git a/app/src/main/res/layout/manga_info_header.xml b/app/src/main/res/layout/manga_info_header.xml index 7e44508e77..3f2eaa4072 100644 --- a/app/src/main/res/layout/manga_info_header.xml +++ b/app/src/main/res/layout/manga_info_header.xml @@ -17,6 +17,7 @@ android:layout_height="0dp" android:layout_marginBottom="44dp" android:alpha="0.2" + android:scaleType="centerCrop" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" @@ -50,6 +51,7 @@ android:background="@drawable/rounded_rectangle" android:contentDescription="@string/description_cover" android:maxWidth="100dp" + android:scaleType="centerCrop" tools:src="@mipmap/ic_launcher" /> - - + + + app:title="@string/pref_category_reading_mode" /> + + diff --git a/app/src/main/res/layout/source_compact_grid_item.xml b/app/src/main/res/layout/source_compact_grid_item.xml index 0641f9fe08..7ccc0ccdbc 100644 --- a/app/src/main/res/layout/source_compact_grid_item.xml +++ b/app/src/main/res/layout/source_compact_grid_item.xml @@ -17,6 +17,7 @@ android:layout_width="match_parent" android:layout_height="match_parent" android:background="?attr/colorSurface" + android:scaleType="centerCrop" tools:ignore="ContentDescription" tools:src="@mipmap/ic_launcher" /> diff --git a/app/src/main/res/layout/source_list_item.xml b/app/src/main/res/layout/source_list_item.xml index 7fb8528b74..63c1272db9 100644 --- a/app/src/main/res/layout/source_list_item.xml +++ b/app/src/main/res/layout/source_list_item.xml @@ -15,6 +15,7 @@ android:layout_height="@dimen/material_component_lists_single_line_with_avatar_height" android:layout_gravity="center_vertical" android:padding="8dp" + android:scaleType="centerCrop" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" diff --git a/app/src/main/res/layout/updates_item.xml b/app/src/main/res/layout/updates_item.xml index 600597b30d..7abe059320 100644 --- a/app/src/main/res/layout/updates_item.xml +++ b/app/src/main/res/layout/updates_item.xml @@ -14,6 +14,7 @@ android:layout_marginStart="16dp" android:layout_marginTop="8dp" android:layout_marginBottom="8dp" + android:scaleType="centerCrop" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintDimensionRatio="h,1:1" app:layout_constraintStart_toStartOf="parent" diff --git a/app/src/main/res/values-am/strings.xml b/app/src/main/res/values-am/strings.xml index b211f2c679..78512cb61a 100644 --- a/app/src/main/res/values-am/strings.xml +++ b/app/src/main/res/values-am/strings.xml @@ -227,7 +227,6 @@ ሁልጊዜ የምዕራፍ ሽግግርን ያሳዩ የመሬት ገጽታን ያስገድዱ የግዳጅ ምስል - ቆልፍ ፍርይ ማሽከርከር በፍጥነት diff --git a/app/src/main/res/values-ar/strings.xml b/app/src/main/res/values-ar/strings.xml index 8e2d6e233c..0ed5d7e344 100644 --- a/app/src/main/res/values-ar/strings.xml +++ b/app/src/main/res/values-ar/strings.xml @@ -137,7 +137,6 @@ المركز الدوران حر - مغلق الوضع الرأسي اﻹجباري الوضع الأفقي اﻹجباري موقع التنزيل @@ -522,7 +521,6 @@ متبقي %1$s عناصر يقوم بترشيح المانجا ضمن مكتبتك - عرض ملاحظات النسخة تضمين المصادر المثبتة فقط المساحة الجانبية عمودي مستمر diff --git a/app/src/main/res/values-b+es+419/strings.xml b/app/src/main/res/values-b+es+419/strings.xml index f4ae4342b1..dd8c96d488 100644 --- a/app/src/main/res/values-b+es+419/strings.xml +++ b/app/src/main/res/values-b+es+419/strings.xml @@ -129,7 +129,6 @@ R Forzar landscape Forzar portrait - Bloqueado Libre Rotación Rápido @@ -421,7 +420,6 @@ Actualizado a v%1$s Buscar actualizaciones Licencias de código abierto - Notas de versiones recientes Qué hay de nuevo Hora de compilación Versión @@ -652,4 +650,6 @@ Cada 8 horas Cada 4 horas Mostrar cantidad de elementos + Ninguno + Mostrar errores \ No newline at end of file diff --git a/app/src/main/res/values-bg/strings.xml b/app/src/main/res/values-bg/strings.xml index b620508751..a11e262a51 100644 --- a/app/src/main/res/values-bg/strings.xml +++ b/app/src/main/res/values-bg/strings.xml @@ -126,7 +126,6 @@ Центрирано Въртене Включено - Заключено Портретно Пейзажно R @@ -475,7 +474,6 @@ Само изтеглени Провери за ъпдейт Лицензи за отворен код - Известия за предвартилени билдове Уебсайт Оптимизацията на батерията е изключена Помага при фонови обновявания на библиотеката и резервни копия diff --git a/app/src/main/res/values-bn/strings.xml b/app/src/main/res/values-bn/strings.xml index ded47414de..2a76c883d8 100644 --- a/app/src/main/res/values-bn/strings.xml +++ b/app/src/main/res/values-bn/strings.xml @@ -141,7 +141,6 @@ মাঝে ঘূর্ণন মুক্ত - বন্ধ সোঁজাসুজি রাখুন আড়াআড়ি রাখুন @@ -605,7 +604,6 @@ হালনাগাদ করা হয়েছে v%1$s তে হালনাগাদ এর জন্য অনুসন্ধান করুন ওপেন সোর্স লাইসেন্স - প্রিভিউ বিল্ড বিজ্ঞপ্তি কি কি নতুন ওয়েবসাইট ইতিহাস সাফ করুন diff --git a/app/src/main/res/values-ca/strings.xml b/app/src/main/res/values-ca/strings.xml index 333e22e792..428fb8d2a6 100644 --- a/app/src/main/res/values-ca/strings.xml +++ b/app/src/main/res/values-ca/strings.xml @@ -167,7 +167,6 @@ Ràpida Rotació Lliure - Bloca Força vertical Força horitzontal R @@ -447,7 +446,6 @@ Per a 1 títol Per a %d títols - Avisos de compilacions de previsualització Tema clar Per defecte Blau clar diff --git a/app/src/main/res/values-cs/strings.xml b/app/src/main/res/values-cs/strings.xml index cb9002f0a1..7c3b963dde 100644 --- a/app/src/main/res/values-cs/strings.xml +++ b/app/src/main/res/values-cs/strings.xml @@ -94,7 +94,6 @@ Zprava doleva Otáčení Volné - Zamknuté Na výšku Na šířku R diff --git a/app/src/main/res/values-cv/strings.xml b/app/src/main/res/values-cv/strings.xml index ecee0e53a2..982304830d 100644 --- a/app/src/main/res/values-cv/strings.xml +++ b/app/src/main/res/values-cv/strings.xml @@ -271,7 +271,6 @@ Тӑрӑлӑх мар Тӑрӑхскер Урлӑскер - Ҫаклатнӑскер Ирӗклӗ Урлӑ-тӑрӑх Энимсӗр @@ -330,7 +329,6 @@ Усӑҫ али Уйӑру меслечӗ Кирек мӗнле йӑнӑшсене тӳрлетме пулӑшать. Нимӗнле харкам пӗлӗм те ярӑнмасть - Малтанлӑх верссисем ҫинчен ҫырни Йӑнӑшлӑ тиев вырӑнӗ Тиенӗ Тиенӗсене ҫеҫ diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index b5797f7326..4b0c2f17e9 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -135,9 +135,8 @@ Mitte Ausrichtung Frei - Gesperrt - Hochformat erzwingen - Querformat erzwingen + Hochformat sperren + Querformat sperren R G B @@ -197,7 +196,7 @@ Angemeldet Anmeldung fehlgeschlagen Unbekannter Fehler - Titel oder Autor … + Titel oder Autor… Kategorie wird aktualisiert Für diese Quelle musst du dich anmelden Wähle eine Quelle @@ -205,7 +204,7 @@ Keine weiteren Ergebnisse Lokale Quelle Standard kann nicht zusammen mit anderen Kategorien gewählt werden - Der Manga wurde Ihrer Bibliothek hinzugefügt + Der Manga wurde der Bibliothek hinzugefügt Dieser Manga wurde aus der Datenbank entfernt. Infos Beschreibung @@ -447,7 +446,6 @@ Für 1 Titel Für %d Titel - Preview-Build-Notizen Helldesign Standard Hellblau @@ -501,7 +499,7 @@ Nach Aktualisierungen suchen Zuletzt genutzt Anleitung für lokale Quellen - %02d Min. %02d Sk. + %02d Min. %02d Sek. Filtert alle Manga in deiner Bibliothek 1 verbleibend @@ -675,4 +673,6 @@ Fehler anzeigen Diese Android-Version wird nicht mehr unterstützt Kopieren in die Zwischenablage fehlgeschlagen + Querformat + Porträt \ No newline at end of file diff --git a/app/src/main/res/values-el/strings.xml b/app/src/main/res/values-el/strings.xml index 47b7ae4a30..03745d9a75 100644 --- a/app/src/main/res/values-el/strings.xml +++ b/app/src/main/res/values-el/strings.xml @@ -168,9 +168,8 @@ Γρήγορη Περιστροφή Ελεύθερο - Κλείδωμα - Υποχρεωτικά κατακόρυφα - Υποχρεωτικά οριζόντια + Κλειδωμένο κατακόρυφα + Κλειδωμένο οριζόντια R G B @@ -391,7 +390,6 @@ Προσθήκη παρακολούθησης Καρφιτσωμένα Πατήστε ξανά για έξοδο - Ανακοινώσεις έκδοσης Preview %02d λεπτά, %02d δευτερόλεπτα Επιλογή αντίστροφου Ενημερώσεις επεκτάσεων @@ -633,7 +631,7 @@ Τα διαπιστευτήρια σύνδεσης MAL δεν βρέθηκαν Διάταξη πλοήγησης Άκρη - Kindle-ish + Σαν Kindle Σχήματος L Προεπιλογή Αρχεία καταγραφής σφαλμάτων @@ -675,4 +673,6 @@ Εμφάνιση σφαλμάτων Αυτή η έκδοση Android δεν υποστηρίζεται πλέον Απέτυχε η αντιγραφή στο πρόχειρο + Οριζόντια + Κατακόρυφα \ No newline at end of file diff --git a/app/src/main/res/values-eo/strings.xml b/app/src/main/res/values-eo/strings.xml index 4a265d0f96..b71d7a6abe 100644 --- a/app/src/main/res/values-eo/strings.xml +++ b/app/src/main/res/values-eo/strings.xml @@ -189,7 +189,6 @@ Blu Verd Ruĝ - Ŝlosita Libera Orientiĝo Rapida diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index e2f2f57fd4..501425c7f5 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -60,7 +60,7 @@ Vertical Horizontal Predeterminado - Frecuencia de actualización de biblioteca + Frecuencia de actualización Manual Cada hora Cada 2 horas @@ -69,7 +69,7 @@ Cada 12 horas Cada día Cada 2 días - Restricciones de actualización de biblioteca + Restricciones de actualización Actualizar solo cuando se cumplan las condiciones Cargando Solo actualizar manga no finalizada @@ -108,9 +108,8 @@ Centro Rotación Libre - Bloqueado Forzar retrato - Forzar apaisado + Forzar en paisaje Ubicación de descargas Descargar sólo a través de Wi-Fi @@ -129,7 +128,7 @@ Cookies borradas Borrar redundancia de la base de datos Borrar el historial de mangas que no están guardados en la biblioteca - ¿Lo confirma\? Se perderán los capítulos leídos y el progreso de los mangas que no estén en la biblioteca + ¿Estás seguro\? Los capítulos leídos y el progreso del título que no esté en la biblioteca se perderá Entradas eliminadas Versión @@ -157,7 +156,7 @@ Descripción En publicación Desconocido - Licenciado + En hiatus Eliminar de biblioteca Capítulos @@ -251,8 +250,8 @@ Alfabéticamente Total de capítulos Último leído - Añadir marcador - Quitar marcador + Marcar Capitulo + Desmarcar capítulo Añadir No hay ninguna conexión de red disponible Descarga en pausa @@ -408,7 +407,7 @@ Subexponer / Aclarar Quemar / Oscurecer Ayuda - Orden de actualización de la biblioteca + Orden de actualización No se han encontrado resultados Selecciona una fuente para migrar Atrás @@ -419,11 +418,11 @@ Obsoleto Esta extensión ya no está disponible. Formato de fecha - Actualizaciones + Actualizacion Global Cerrar sesión Has cerrado sesión Leyendo actualmente - En pausa + Pausado ¿Cerrar sesión de %1$s\? Quiero leer Más @@ -479,7 +478,6 @@ Para 1 título Para %d títulos - Nota de versiones recientes Tema claro Predeterminado Azul y blanco @@ -533,7 +531,7 @@ Fecha indicada invalida Ultímo usado Comprobar las actualizaciones - Instruccion local de fuente + Guía de fuente local Queda 1 Quedan %1$s @@ -680,4 +678,32 @@ Por fecha de subida Rastreado Derecha e Izquierda + Se restaurarán los datos del archivo de respaldo. +\n +\n Deberá instalar las extensiones que falten e iniciar sesión en los servicios de seguimiento después para usarlas. + Esta versión de Android ya no es compatible + No se pudo copiar al portapapeles + DNS sobre HTTPS + Los manga de las categorías excluidas no se descargarán incluso si también están en las categorías incluidas. + Auto-descarga + Paisaje + Retrato + Derecha + Izquierda + Proximo + Anterior + Si la ubicación de la división de página dual no coincide con la dirección de lectura + Invertir la ubicación de la división de página dual + División de página dual + Mostrar zonas de tap cuando se abre el lector + Mostrar superposición de diseño de navegación + Excluir: %s + Incluir: %s + Ninguno + Los manga de las categorías excluidas no se actualizarán incluso si también están en las categorías incluidas. + Cada 8 horas + Cada 4 horas + Mostrar Errores + Mostrar número de artículos + Fecha obtenida \ No newline at end of file diff --git a/app/src/main/res/values-fa/strings.xml b/app/src/main/res/values-fa/strings.xml index 4510a08864..7664ff9a2d 100644 --- a/app/src/main/res/values-fa/strings.xml +++ b/app/src/main/res/values-fa/strings.xml @@ -260,7 +260,6 @@ قرمز تحمیل حالت افقی تحمیل حالت عمودی - قفل آزاد چرخش سریع @@ -470,7 +469,6 @@ ارسال گزارش های خرابی بررسی برای به آپدیت‌ها مجوزهای منبع باز - اطلاعیه های نسخه پیش نمایش زمان ساخت نسخهٔ وب سایت diff --git a/app/src/main/res/values-fi/strings.xml b/app/src/main/res/values-fi/strings.xml index 8b79fc9e50..1ce534d4e9 100644 --- a/app/src/main/res/values-fi/strings.xml +++ b/app/src/main/res/values-fi/strings.xml @@ -147,7 +147,7 @@ Kehittyneet Tietoja Kohteita per rivi - Pystysuunnassa + Pystysuunta Vaakatasossa Oletus Päivitystiheys @@ -234,9 +234,8 @@ Nopea Rotaatio Vapaa - Lukitse - Pakota pystysuunta - Pakota vaakatso + Lukittu pystysuunta + Lukittu vaakataso Punainen Vihreä Sininen @@ -447,7 +446,6 @@ Yhdelle sarjalle %d sarjalle - Esikatsele koontiversion ilmoituksia Vaalea teema Oletus Vaaleansininen @@ -675,4 +673,6 @@ Näytä virheet Tätä Android-versiota ei enää tueta Kopiointi leikepöydälle epäonnistui + Vaakatasossa + Pystysuunnassa \ No newline at end of file diff --git a/app/src/main/res/values-fil/strings.xml b/app/src/main/res/values-fil/strings.xml index 09eb77c610..c3e7f7ce16 100644 --- a/app/src/main/res/values-fil/strings.xml +++ b/app/src/main/res/values-fil/strings.xml @@ -169,7 +169,6 @@ A Piliting nakahiga Piliting nakatayo - Nakasara Malaya Pag-ikot Mabilis @@ -536,7 +535,6 @@ In-update patungong bersyon %1$s Tumingin ng update Mga lisensiyang open-source - Mga abiso sa panilip na gawa (preview build) Anong bago Oras ng paggawa (build time) Bersyon diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index 3d4616e336..cd99861acc 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -123,9 +123,8 @@ Centre Rotation Libre - Verrouillée - Forcer portrait - Forcer paysage + Bloqué sur portrait + Bloqué sur paysage R V B @@ -381,7 +380,7 @@ Auteur Sélectionner les données à inclure Migrer - Information complémentaire + Informations de l\'extension Toutes Fiable Non fiable @@ -487,7 +486,6 @@ Réorganiser Plus récent Plus ancien - Notes de version Thème clair Déplacer au début Déplacer à la fin @@ -709,4 +707,6 @@ Afficher les erreurs Cette version d\'Android n\'est plus supportée Échec de la copie dans le presse-papiers + Paysage + Portrait \ No newline at end of file diff --git a/app/src/main/res/values-gl/strings.xml b/app/src/main/res/values-gl/strings.xml index d34d822509..8edc4dd118 100644 --- a/app/src/main/res/values-gl/strings.xml +++ b/app/src/main/res/values-gl/strings.xml @@ -32,7 +32,6 @@ R Forzar horizontal Forzar vertical - Bloqueada Libre Rotación Rápido diff --git a/app/src/main/res/values-he/strings.xml b/app/src/main/res/values-he/strings.xml index 25b8e66694..94816b15fb 100644 --- a/app/src/main/res/values-he/strings.xml +++ b/app/src/main/res/values-he/strings.xml @@ -294,7 +294,6 @@ שנה שם קטגוריה ערוך קטגוריות הוסף קטגוריה - הודעות זמן בנייה גרסה אופטימיזציית סוללה כבר מושבתת @@ -348,7 +347,6 @@ אדום כפה מצב מאוזן כפה מצב מאונך - נעול חופשי סיבוב מהירה diff --git a/app/src/main/res/values-hi/strings.xml b/app/src/main/res/values-hi/strings.xml index 0739f45f2c..bbc3af7522 100644 --- a/app/src/main/res/values-hi/strings.xml +++ b/app/src/main/res/values-hi/strings.xml @@ -141,7 +141,6 @@ मध्यविंदु नियमित आवर्तन मुक्त - ताला मजबूर चित्र मजबूर लैंडस्केप R @@ -447,7 +446,6 @@ 1 शीर्षक के लिए %d शीर्षक के लिए - पूर्वावलोकन बनाएँ नोटिस लाइट थीम डिफ़ॉल्ट हल्का नीला diff --git a/app/src/main/res/values-hr/strings.xml b/app/src/main/res/values-hr/strings.xml index 2374e52b4c..e645ea8259 100644 --- a/app/src/main/res/values-hr/strings.xml +++ b/app/src/main/res/values-hr/strings.xml @@ -55,7 +55,7 @@ Učestalost ažuriranja Globalno ažuriranje Standardno - Polegnuto + Ležeće Uspravno Broj elemenata po retku Prikaz @@ -215,7 +215,7 @@ Pretpredzadnje poglavlje Predzadnje poglavlje Zadnje pročitano poglavlje - Isključeno + Deaktivirano Prilagođeno mjesto Nakon čitanja Nakon ručnog označivanja kao pročitano @@ -233,9 +233,8 @@ B G R - Prisili vodoravno - Prisili okomito - Zaključaj + Zaključaj ležeće + Zaključaj uspravno Slobodno Okretanje Brzo @@ -256,7 +255,7 @@ Dekoder slika Pregled stranica Kontinuirano okomito - Webtoon + Digitalni strip Okomito S desna na lijevo S lijeva na desno @@ -466,7 +465,6 @@ Pošalji izvještaj o padu programa Traži nove verzije Licence otvorenog koda - Pregledaj napomene uz izdanje Datum izdanja Verzija Web-stranica @@ -685,4 +683,8 @@ Manga u isključenim kategorijama neće se ažurirati čak niti ako se također nalaze u uključenim kategorijama. Datum preuzimanja Prikaži greške + Ove verzija androida više nije podržana + Kopiranje nije uspješno + Ležeće + Uspravno \ No newline at end of file diff --git a/app/src/main/res/values-hu/strings.xml b/app/src/main/res/values-hu/strings.xml index 62a1fbe593..0316c88eb2 100644 --- a/app/src/main/res/values-hu/strings.xml +++ b/app/src/main/res/values-hu/strings.xml @@ -120,7 +120,7 @@ Megjelenítési mód Megjelenés Rendezés - Letöltődik + Letöltés Várakozik Összes kijelölése Mégse @@ -208,7 +208,7 @@ Új verzió érhető el! Letöltési hiba Letöltés kész - Ímélcím + E-mail cím NSFW (18+) források Az alsó sáv elrejtése görgetéskor Fejezetszám @@ -277,8 +277,8 @@ Leírás Infó Bejelentkezve - Eltávolítva a könyvtárból - A könyvtárban + Eltávolítás a könyvtárból + Könyvtárban Olvasás Adat Biztonsági mentés létrehozva @@ -341,4 +341,43 @@ Alapértelmezett Másolás Áttelepítés + Letöltött + %1$s. fejezet + Összeomlási naplók + A Tachiyomi működéséhez WebView szükséges + Manga borító + %1$s. fejezetek + A(z) %1$s. fejezet és %2$d egyéb fejezetek + %1$s:%2$s, %3$d. oldal + Újraolvasás + Érvénytelen letöltési hely + Biztosan törölni akarja a kijelölt fejezeteket\? + Olvasatlan + Összes + Egyéni + Következő 10 fejezet + Következő 5 fejezet + Következő fejezet + A fejezetek nem érhetőek el + Szünetel + Manga könyvtárhoz adása\? + Forrás nincs telepítve: %1$s + Folyamatban van + Utoljára használt + Letöltött fejezetek + Kategóriák frissítése + Cím vagy szerző… + Ismeretlen hiba + Sikertelen bejelentkezés + Mostmár ki van jelentkezve + Kijelentkezés + Jelszó + Felhasználónév + Sütik törölve + A változtatások érvénybe lépéséhez újra kell indítania az applikációt + Érvénytelen backup fájl + Él + Könyvtár frissítése + Nem található fejezet + Licenszelt \ No newline at end of file diff --git a/app/src/main/res/values-in/strings.xml b/app/src/main/res/values-in/strings.xml index 1c39ee3cbb..13c9b21509 100644 --- a/app/src/main/res/values-in/strings.xml +++ b/app/src/main/res/values-in/strings.xml @@ -137,7 +137,6 @@ Tengah Rotasi Bebas - Kunci Paksa tegak Paksa menyamping R @@ -435,7 +434,6 @@ Nonaktifkan optimisasi baterai Optimalisasi baterai sudah dimatikan Alamat surel - Pemberitahuan Terang standar Amankan layar Keluar dari %1$s\? diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml index 398b79f474..a333d5a1aa 100644 --- a/app/src/main/res/values-it/strings.xml +++ b/app/src/main/res/values-it/strings.xml @@ -122,7 +122,6 @@ Centro Orientamento Libero - Bloccato Blocca verticale Blocca orizzontale R @@ -264,7 +263,7 @@ Ricerca aggiornamenti… Scarica aggiornamento - Download in corso… + Scaricamento… Download completato Errore di download Aggiornamento disponibile! @@ -482,7 +481,6 @@ Controllando nuove pubblicazioni Controlla il sito in WebView Indirizzo e-mail - Anteprima avvisi di versione Ottimizzazione batteria già disattivata Facilita gli aggiornamenti in background e i backup Disattiva ottimizzazione batteria @@ -504,7 +502,7 @@ Salta capitoli filtrati Fissa Aggiungi monitoraggio - In libreria + Nella libreria Aggiungi alla libreria Fissato 25% @@ -706,4 +704,6 @@ Mostra schema di navigazione Mostra zone di tocco quando il lettore viene aperto Mostra errori + Questa versione di Android non è più supportata + Copia negli appunti fallito \ No newline at end of file diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml index 85efe40028..4342438103 100644 --- a/app/src/main/res/values-ja/strings.xml +++ b/app/src/main/res/values-ja/strings.xml @@ -141,7 +141,6 @@ 速い 回転 自動回転 - ロック 横向き画面を強制 R G @@ -429,7 +428,6 @@ ダウンロード済みのみ 更新を確認 オープンソースライセンス - プレビュービルドの更新情報 Webサイト デバイス設定を開きませんでした バッテリー最適化は既に無効です diff --git a/app/src/main/res/values-ka-rGE/strings.xml b/app/src/main/res/values-ka-rGE/strings.xml index e9dc8c7d87..49e6440665 100644 --- a/app/src/main/res/values-ka-rGE/strings.xml +++ b/app/src/main/res/values-ka-rGE/strings.xml @@ -204,7 +204,6 @@ სწარფი გადმოტრიალება თავისუფალი - დაფიქსირებული პორტრეტის რეჟიმი პეიზაჟის რეჟიმი R @@ -481,7 +480,6 @@ გამორთე ბატარეის ოპტიმიზაცია ეხმარება უკანა ფონში მიმდინარე ბიბლიოთეკის განახლებებსდა რეზერვს აწყობის დრო - აწყობის განცხადებების გადახედვა ღია კოდის ლიცენზია მიმაგრებული ლოკალური წყაროს გიდი diff --git a/app/src/main/res/values-kn/strings.xml b/app/src/main/res/values-kn/strings.xml index 47292f1b2c..12825f6714 100644 --- a/app/src/main/res/values-kn/strings.xml +++ b/app/src/main/res/values-kn/strings.xml @@ -192,7 +192,6 @@ R ಬಲವಂತದ ಅಗಲಚಿತ್ರ ಬಲವಂತದ ಉದ್ದಚಿತ್ರ - ಲಾಕ್ ಉಚಿತ ತಿರುಗಿಸು ವೇಗವಾಗಿ @@ -491,7 +490,6 @@ ಕ್ರ್ಯಾಶ್ ವರದಿಗಳನ್ನು ಕಳುಹಿಸಿ ನವೀಕರಣಗಳಿಗಾಗಿ ಪರಿಶೀಲಿಸಿ ಓಪನ್ ಸೋರ್ಸ್ ಪರವಾನಗಿಗಳು - ಬಿಲ್ಡ್ ಪ್ರಕಟಣೆಗಳ ಪೂರ್ವವೀಕ್ಷಣೆ ನಿರ್ಮಿಸಿದ ಸಮಯ ಆವೃತ್ತಿ ವೆಬ್ಸೈಟ್ diff --git a/app/src/main/res/values-ko/strings.xml b/app/src/main/res/values-ko/strings.xml index c763fd2d11..6e124fd000 100644 --- a/app/src/main/res/values-ko/strings.xml +++ b/app/src/main/res/values-ko/strings.xml @@ -152,7 +152,6 @@ 빠름 회전 자유 - 잠금 세로로 고정 가로로 고정 Wi-Fi만 사용해서 다운로드 diff --git a/app/src/main/res/values-ms/strings.xml b/app/src/main/res/values-ms/strings.xml index dc26f8e4f5..34748f0d1f 100644 --- a/app/src/main/res/values-ms/strings.xml +++ b/app/src/main/res/values-ms/strings.xml @@ -101,7 +101,7 @@ Kemas kini bab selepas dibaca Skrin permulaan Bahasa - Sistem asal + Lalai Kategori lalai Sentiasa tanya Skrin penuh @@ -140,9 +140,8 @@ Tengah Putaran skrin Bebas - Kunci - Hanya potret - Hanya landskap + Kekal potret + Kekal landskap R G B @@ -152,7 +151,7 @@ Selepas ditandakan sebagai dibaca secara manual Setelah membaca Lokasi tersuai - Di nyahkan + Dinyahkan Bab terakhir dibaca Bab kedua terakhir Bab ketiga terakhir @@ -212,7 +211,7 @@ Sumber lokal Lain Lalai tidak boleh dipilih bersama kategori lain - Ditambah ke pustaka + Manga ini telah ditambahkan ke Pustaka anda Carian keseluruhan… Terkini Semak imbas @@ -229,7 +228,7 @@ Bab %1$s Dimuat turun Diaturkan - Muat turun dalam progres + Memuat turun Memuat turun (%1$d/%2$d) Ralat Ditangguh @@ -244,7 +243,7 @@ 5 bab seterusnya 10 bab seterusnya Semua - Muat turun yang belum di baca + Belum dibaca Adakah anda pasti ingin memadamkan bab yang dipilih\? Penjejakan Sedang baca @@ -274,7 +273,7 @@ Bab %1$s Bab seterusnya tidak dijumpai Bab sebelumnya tidak dijumpai - Imej tidak dapat di muatkan + Imej tidak dapat dimuatkan Guna imej ini sebagai muka hadapan\? Memuat turun bab tidak berjaya. Anda boleh mencuba lagi di bahagian muat turun Progres kemas kini: %1$d/%2$d @@ -319,7 +318,7 @@ Kemaskini Pasang Masih menunggu - Menuat turun + Memuat turun Memasang Dipasang Dipercayai @@ -391,9 +390,9 @@ Log keluar daripada %1$s\? Log keluar Anda telah log keluar - Sedang di baca + Sedang baca Ditangguh - Hendak di baca + Ingin baca Lain-lain Bab terkini Buka bab @@ -408,7 +407,7 @@ AMOLED Uruskan pemberitahuan Keselamatan - Kunci dengan biometrik + Memerlukan buka kunci Kunci apabila terbiar Selalu Tidak @@ -443,7 +442,6 @@ Untuk %d tajuk - Notis binaan Preview Tema terang Lalai Biru terang @@ -663,4 +661,6 @@ Tunjuk ralat Versi Android ini tidak lagi disokong Gagal menyalin ke papan keratan + Landskap + Potret \ No newline at end of file diff --git a/app/src/main/res/values-my/strings.xml b/app/src/main/res/values-my/strings.xml index d295adb6fb..470eda44ce 100644 --- a/app/src/main/res/values-my/strings.xml +++ b/app/src/main/res/values-my/strings.xml @@ -95,7 +95,7 @@ Tachiyomi ကိုဖွင့်ပါ မှတ်တမ်း ခြေရာခံမှု - အခန်းများ + အပိုင်းများ Manga အမျိုးအစားများ အမျိုးအစားများမရှိသေးပါ။သင့်စာအုပ်စင်ကို အမျိုးအစားခွဲခြားမှုပြုလုပ်ရန် အပေါင်းခလုပ်ကိုနှိပ်ပါ။ @@ -129,4 +129,13 @@ ပြန်ရယူမည် ရှေ့သို့ နောက်သို့ + Library ကိုအပ်ဒိတ်လုပ်နေပါတယ် + ပုံသေအမျိုးအစား + မည်သည့်အပိုင်းမျှမရှိပါ + အသုံးမပြုချိန်တွင်သော့ခတ်မည် + ထုတ်ဝေမှုပြီးစီး + လိုင်စင်လုပ်ပြီး + မသိရှိပါ + Ongoing + နာမည် \ No newline at end of file diff --git a/app/src/main/res/values-nb-rNO/strings.xml b/app/src/main/res/values-nb-rNO/strings.xml index 2ead141cbc..dcedc062ff 100644 --- a/app/src/main/res/values-nb-rNO/strings.xml +++ b/app/src/main/res/values-nb-rNO/strings.xml @@ -163,7 +163,6 @@ Rask Rotasjon Fri - Låst Tving stående Tving liggende R @@ -445,7 +444,6 @@ Mindre Mer Festet - Merknader Nettside Sikkerhetskopiering mislyktes Ingen @@ -673,4 +671,8 @@ Last ned automatisk Manga i utelukkede kategorier vil ikke bli oppdatert selv om de også er i inkluderte kategorier. Vis feil + Klarte ikke å kopiere til utklippstavle + Denne Android-versjonen støttes ikke lenger + Liggende + Stående \ No newline at end of file diff --git a/app/src/main/res/values-nl/strings.xml b/app/src/main/res/values-nl/strings.xml index a1ff18f653..255b8a6e74 100644 --- a/app/src/main/res/values-nl/strings.xml +++ b/app/src/main/res/values-nl/strings.xml @@ -114,7 +114,6 @@ Midden Rotatie Vrij - Vergrendeld R G B @@ -480,7 +479,6 @@ Alleen gedownloade Controleer op updates Open-sourcelicenties - Voorbeeldversie mededelingen Website Batterijoptimalisatie is al uitgeschakeld Helpt bij het bijwerken van bibliotheek en back-ups op de achtergrond @@ -673,4 +671,6 @@ Datum opgehaald Geen Fouten weergeven + Deze Android versie wordt niet meer ondersteund + Mislukt om naar het klemboord te kopiëren \ No newline at end of file diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml index 1db50ea65d..7f27654de1 100644 --- a/app/src/main/res/values-pl/strings.xml +++ b/app/src/main/res/values-pl/strings.xml @@ -150,7 +150,7 @@ Orientacja pionowa Orientacja pozioma Domyślnie - Częstotliwość aktualizacji biblioteki + Częstotliwość aktualizacji Ręcznie Co godzinę Co 2 godziny @@ -161,7 +161,7 @@ Co 2 dni Co tydzień Wszystko - Warunki aktualizacji biblioteki + Warunki aktualizacji Aktualizuje tylko, kiedy spełnione są te warunki Ładowanie Aktualizuj tylko nieukończone mangi @@ -206,9 +206,8 @@ Od środka Orientacja ekranu Dowolna - Zablokowana - Wymuś pionową - Wymuś poziomą + Wymuś pionowo + Wymuś poziomo R G B @@ -375,7 +374,7 @@ Rozjaśnienie Ściemnienie Pomoc - Kolejność aktualizacji biblioteki + Kolejność aktualizacji Wstecz Ekran Nie znaleziono wyników @@ -527,7 +526,6 @@ Plakietki nieprzeczytanych rozdziałów Przeglądaj rozdziały Dodaj śledzenie - Uwagi dotyczące kompilacji podglądowej Niepowodzenie otwarcia ustawień urządzenia Odśwież okładki Przywracanie kopii zapasowej zakończone niepowodzeniem @@ -691,4 +689,12 @@ Wyklucz: %s Uwzględnij: %s Brak + Ta wersja Androida nie jest już wspierana + Nie udało się skopiować do schowka + Tytuły w wykluczonych kategoriach nie będą aktualizowane, nawet jeśli są również w uwzględnionych kategoriach. + Tytuły w wykluczonych kategoriach nie będą pobierane, nawet jeśli są również w uwzględnionych kategoriach. + Automatyczne pobieranie + Poziomo + Pionowo + Pokaż błędy \ No newline at end of file diff --git a/app/src/main/res/values-pt-rBR/strings.xml b/app/src/main/res/values-pt-rBR/strings.xml index 0d3fb88457..69a5bfaa89 100644 --- a/app/src/main/res/values-pt-rBR/strings.xml +++ b/app/src/main/res/values-pt-rBR/strings.xml @@ -137,9 +137,8 @@ Centro Orientação Automática - Bloqueada - Forçar retrato - Forçar paisagem + Retrato bloqueado + Paisagem bloqueado R G B @@ -447,7 +446,6 @@ Para 1 título Para %d títulos - Avisos da compilação de preview Tema claro Padrão Azul claro @@ -675,4 +673,6 @@ Mostrar erros Esta versão do Android não é mais suportada Erro ao copiar para a área de transferência + Paisagem + Retrato \ No newline at end of file diff --git a/app/src/main/res/values-pt/strings.xml b/app/src/main/res/values-pt/strings.xml index 7a66459718..648c6741f6 100644 --- a/app/src/main/res/values-pt/strings.xml +++ b/app/src/main/res/values-pt/strings.xml @@ -108,7 +108,6 @@ Centro Rotação Livre - Bloqueado Forçar retrato Forçar paisagem @@ -472,7 +471,6 @@ Para 1 título Para %d títulos - Avisos de build de preview Tema claro Padrão Azul claro diff --git a/app/src/main/res/values-ro/strings.xml b/app/src/main/res/values-ro/strings.xml index f3b7f3a82c..8f1e807342 100644 --- a/app/src/main/res/values-ro/strings.xml +++ b/app/src/main/res/values-ro/strings.xml @@ -168,7 +168,6 @@ Rapid Rotație Liberă - Blocată Forțată portret Forțată peisaj R @@ -451,7 +450,6 @@ Pentru %d titluri Pentru %d titluri - Previzualizați notificările de construire Temă deschisă Implicit Albastru deschis diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index 1f2a07cbac..ae646d66ab 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -5,7 +5,7 @@ Закладка главы Отмена Удалить - Отобразить + Вид Компактная сетка Список Режим отображения @@ -19,9 +19,9 @@ Удалить фильтр Не прочитано Установить - Отметить как прочитанное - Пометить как непрочитанное - Отметить предыдущее как прочитанное + Отметить как \"Прочитано\" + Пометить как \"Не прочитано\" + Отметить предыдущее как \"Прочитано\" Добавить в категории Следующая глава Следующая непрочитанная @@ -53,7 +53,7 @@ Черный Дата сборки Возникла ошибка при удалении кэша - Кэш очищен. %1$d файл(ов/а) был(о) удален(о) + Кэш очищен. %1$d файлов было удалено Загружено Загружается Загружается (%1$d/%2$d) @@ -71,7 +71,7 @@ G B Завершено - Вы уверены что хотите удалить выбранн(ые/ую) глав(ы/у)\? + Вы уверены что хотите удалить выбранные главы\? Использовать эту картинку как обложку\? Куки удалены Обложка обновлена @@ -92,14 +92,14 @@ Всё Загрузчик Нет сетевого соединения - Страница отсутствует в каталоге + Страница отсутствует в директории Страница не загружена Нет Wi-Fi соединения Ошибка - Не могу скачать главу из-за непревиденной ошибки - Загружен(о) %1$d%% + Невозможно скачать главу из-за непревиденной ошибки + Загружено %1$d%% Невозможно скачать главы. Можете попробовать еще раз в разделе загрузок - Непрочитано + Не прочитано Загружается… Заброшено Категория с таким именем уже существует! @@ -108,7 +108,7 @@ Выбрать файл резервной копии Выбрать обложку Четвёртая от прочитанной главы - Ваша библиотека пуста. Добавьте тайтлы в библиотеку из Поиска. + Ваша библиотека пуста. Добавьте тайтлы в библиотеку из \"Поисковик\". Нет загрузок Нет новых обновлений В последнее время ничего не прочитано @@ -119,7 +119,7 @@ История Обновления Настройки - Ландшафтный режим + Альбомная Последняя прочитанная глава Слева направо Название или автор… @@ -137,7 +137,7 @@ Название Следующая глава не найдена Предыдущая глава не найдена - Пожалуйста, включите хотя бы один источник + Пожалуйста, включите хотя бы, один, действительный источник Не удалось обновить обложку Пожалуйста, добавьте мангу в свою библиотеку, перед тем как делать это Найдены новые главы @@ -147,13 +147,13 @@ Синхронизация отменена Статус обновления: %1$d/%2$d Заморожено - Выпускается + Не завершено Опции Страница скопирована в %1$s Пароль Картинка сохранена Планирую прочитать - Портретный режим + Портретная Помогает исправлять любые баги. Личные данные не передаются Обновлять прогресс глав после прочтения Информация @@ -195,13 +195,12 @@ Обновлять только незавершенную мангу Режим чтения по умолчанию Стартовая позиция увеличения - В процессе чтения + Читаю Удалить из библиотеки Справа налево - Горизонтальная - Вертикальная + Заблокированная альбомная + Заблокированная портретная Свободная - Заблокированная Сохраняю картинку По высоте Уместить в экран @@ -311,10 +310,10 @@ Поисковик Общий Библиотека - У вас нет категорий. Нажмите кнопку + чтобы создать категорию для организации вашей библиотеки. + У вас нет категорий. Нажмите кнопку \"+\" чтобы создать категорию для организации вашей библиотеки. Мигрировать - Дополнения - Информация о дополнении + Расширения + Информация о расширении Все Обновление Установить @@ -337,10 +336,10 @@ Без анимации Нормальная Быстрая - Заголовок + Название Добавлено в библиотеку Удалено из библиотеки - Скопированно в буфер обмена: + Скопировано в буфер обмена: \n\"%1$s\" Загрузить определенное количество Другое кол-во @@ -356,8 +355,8 @@ Начато Тип Автор - Закончено: - Текущий: + Закончена: + Текущая: Следующая: Предыдущая: Следующей главы нет @@ -401,7 +400,7 @@ Тёмный режим Выключено Включено - Системная + В соответствии с системной темой Тёмная тема По умолчанию Темно-синяя @@ -413,9 +412,10 @@ Всегда Никогда - После 1 минуты + После %1$s минуты После %1$s минут После %1$s минут + После %1$s минут Защита экрана Доступны обновления @@ -433,27 +433,26 @@ Глава %1$s Отобразить - 1 новая глава + %1$d новая глава %1$d новые главы %1$d новых глав %1$d новых глав - Для 1 тайтла - Для %d тайтлов - Для %d тайтлов - Для %d тайтлов + Для %d названия + Для %d названий + Для %d названий + Для %d названий - Примечания к предварительной сборке Проверка наличия новых глав Обновление библиотеки - Проверить страницу в WebView + Проверить вебстраницу в WebView Оптимизация батареи уже выключена Помогает с фоновым обновлением библиотеки и резевной копии Выключить оптимизацию батареи По умолчанию - Доступно обновление для расширения + Доступно обновление для %d расширения Доступны обновления для %d расширений Доступны обновления для %d расширений Доступны обновления для %d расширений @@ -477,7 +476,7 @@ Источники Чтение Пропускать фильтрованные главы - Инверсия + Выбрать наоборот Непрерывно вертикально Боковой отступ 25% @@ -513,7 +512,7 @@ %02d мин, %02d сек Включать только закрепленные источники - Осталось 1 + Осталось %1$s Осталось %1$s Осталось %1$s Осталось %1$s @@ -539,7 +538,7 @@ Односторонняя синхронизация для обновления прогресса глав в сервисах отслеживания. Настройте отслеживание отдельных записей манги при помощи их кнопок отслеживания. Значки непрочитанных - 1 неудачное обновление + %1$d неудачное обновление %1$d неудачных обновления %1$d неудачных обновлений %1$d неудачных обновлений @@ -572,7 +571,7 @@ Отключить Открыть в Настройках - 1 глава + %1$s глава %1$s главы %1$s глав %1$s глав @@ -585,11 +584,11 @@ Вертикально Горизонтально Нет - Инверсия нажатия + Нажатие наоборот Неизвестный статус Неизвестный автор Загрузить непрочитанные главы - Обновлено до v%1$s + Обновлено до в%1$s Что нового Невозможно загрузить главы из-за нехватки места на диске Искать \"%1$s\" глобально @@ -598,7 +597,7 @@ Тема Дата добавления - 1 трекер + %d трекер %d трекера %d трекеров %d трекеров @@ -607,24 +606,24 @@ Загрузка завершена Резервирование и восстановление Загрузки - Завершение + Завершено Прогресс Ошибки - Не выполнен вход в трекеры: + Сервисы отслеживания в которые не выполнен вход: Удалять главы с закладками Удаление глав Может содержать контент NSFW (18+) 18+ Это не предотвращает появление внутри приложения контента NSFW (18+) из неофициальных или потенциально неправильно помеченных расширений. - Отсутствует 1 глава + Отсутствует %d глава Отсутствуют %d главы Отсутствуют %d глав Отсутствуют %d глав Нет глав Обновлены настройки главы по умолчанию - По умолчанию + Установить по умолчанию Также применить для всей манги в моей библиотеке Вы уверены, что хотите сохранить эти настройки по умолчанию\? Настройки главы @@ -653,15 +652,15 @@ Приложение для выбора файлов не найдено Пожалуйста, войдите в MAL снова Учетные данные входа в MAL не найдены - Разметка навигации - Край + Схема навигации + Края Kindle-подобная L-образная По умолчанию - Логи вылетов + Логи о вылетах Дата окончания чтения Дата начала чтения - Логи вылета сохранены + Логи о вылетах сохранены Сохраняет логи ошибок в файл для отправки разработчикам Выгрузка логов о вылетах Безлимитная сеть @@ -697,4 +696,6 @@ Показать ошибки Эта версия Андроида больше не поддерживается Не удалось скопировать в буфер обмена + Альбомная + Портретная \ No newline at end of file diff --git a/app/src/main/res/values-sah/strings.xml b/app/src/main/res/values-sah/strings.xml index 3b2732621a..b0f5057324 100644 --- a/app/src/main/res/values-sah/strings.xml +++ b/app/src/main/res/values-sah/strings.xml @@ -198,7 +198,6 @@ Кыһыл Сытыары Туруору - Хатаммыт Босхо Эргичийии Тыл: %1$s @@ -355,7 +354,6 @@ Батарея тупсуруута араарыллынна Хаппаас куопуйаны оҥоруу уонна төнүҥнэрии Хаппаас куопуйа билэтин талыы - Инники биэрсиялар бэлиэтэ Туох саҥа баарый Тахсыбыт кэмэ Биэрсия diff --git a/app/src/main/res/values-sc/strings.xml b/app/src/main/res/values-sc/strings.xml index 3f90ffc18e..4a60d2525a 100644 --- a/app/src/main/res/values-sc/strings.xml +++ b/app/src/main/res/values-sc/strings.xml @@ -169,9 +169,8 @@ Lestra Rotatzione Lìbera - Blocada - Bloca in verticale - Bloca in orizontale + Blocadu in verticale + Blocadu in orizontale R G B @@ -447,7 +446,6 @@ Pro 1 tìtulu Pro %d tìtulos - Avisos de versiones de anteprima Tema craru Predefinidu Biaitu craru @@ -573,7 +571,7 @@ Agiornadu a v%1$s Ite b\'at de nou Iscàrriga sos capìtulos non lèghidos - Iscarrigamentu de sos capìtulos fallidu pro more de su pagu ispàtziu lìberu in su discu + Iscarrigamentu de sos capìtulos fallidu pro neghe de su pagu ispàtziu lìberu in su discu Chirca «%1$s» globalmente Modalidade de leghidura Limba @@ -675,4 +673,6 @@ Ammustra sos errores Còpia in punta de billete fallida Custa versione de Android no est prus suportada + Orizontale + Verticale \ No newline at end of file diff --git a/app/src/main/res/values-sk/strings.xml b/app/src/main/res/values-sk/strings.xml index 64f90a1e4f..da71843c7b 100644 --- a/app/src/main/res/values-sk/strings.xml +++ b/app/src/main/res/values-sk/strings.xml @@ -181,7 +181,6 @@ Rýchlo Rotácia Voľná - Zamknúť Iba na výšku Iba na šírku R diff --git a/app/src/main/res/values-sr/strings.xml b/app/src/main/res/values-sr/strings.xml index 6043cc3019..f64ff9f89f 100644 --- a/app/src/main/res/values-sr/strings.xml +++ b/app/src/main/res/values-sr/strings.xml @@ -168,7 +168,6 @@ Brzo Rotacija Slovodno - Zaključano Forsirano vertikalno Forsirano horizontalno R diff --git a/app/src/main/res/values-sv/strings.xml b/app/src/main/res/values-sv/strings.xml index cbe3fa2787..92cbe36acf 100644 --- a/app/src/main/res/values-sv/strings.xml +++ b/app/src/main/res/values-sv/strings.xml @@ -168,9 +168,8 @@ Snabb Rotering Upplåst - Lås - Tvinga stående - Tvinga liggande + Låst porträtt + Låst liggande R G B @@ -447,7 +446,6 @@ För 1 titel För %d titlar - Byggmeddelanden Ljust tema Standard Ljusblå @@ -675,4 +673,6 @@ Visa fel Denna Android-version stöds inte längre Kunde inte kopiera till urklipp + Liggande + Porträtt \ No newline at end of file diff --git a/app/src/main/res/values-th/strings.xml b/app/src/main/res/values-th/strings.xml index b07e46d6b6..a82421eba2 100644 --- a/app/src/main/res/values-th/strings.xml +++ b/app/src/main/res/values-th/strings.xml @@ -170,7 +170,6 @@ เร็ว การหมุน ว่าง - ล็อค บังคับแนวตั้ง บังคับแนวนอน R diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml index ce0310ccda..425af77105 100644 --- a/app/src/main/res/values-tr/strings.xml +++ b/app/src/main/res/values-tr/strings.xml @@ -168,9 +168,8 @@ Hızlı Döndürme Serbest - Kilitle - Dikeyi zorla - Yatayı zorla + Kilitli dikey + Kilitli yatay K Y M @@ -447,7 +446,6 @@ 1 başlık için %d başlık için - Derleme notlarını göster Aydınlık tema Öntanımlı Açık mavi @@ -675,4 +673,6 @@ Hataları göster Bu Android sürümü artık desteklenmiyor Panoya kopyalanamadı + Yatay + Dikey \ No newline at end of file diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml index 2c25465ef2..7148019571 100644 --- a/app/src/main/res/values-uk/strings.xml +++ b/app/src/main/res/values-uk/strings.xml @@ -168,9 +168,8 @@ Швидка Орієнтація Вільна - Заблокована - Вертикальна - Горизонтальна + Заблокована портретна + Заблокована альбомна R G B @@ -452,7 +451,6 @@ Для %d тайтлів Для %d тайтлів - Примітки білдів Світла тема За замовчуванням Блакитна @@ -693,4 +691,8 @@ Мангу в виключених категоріях не буде оновлено, навіть якщо вона також знаходиться у включених категоріях. Дата отримання Показати помилки + Ця версія Android більше не підтримується + Не вдалось скопіювати до буферу обміну + Альбомна + Портретна \ No newline at end of file diff --git a/app/src/main/res/values-v23/themes.xml b/app/src/main/res/values-v23/themes.xml deleted file mode 100644 index 49f3e5ea60..0000000000 --- a/app/src/main/res/values-v23/themes.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - diff --git a/app/src/main/res/values-vi/strings.xml b/app/src/main/res/values-vi/strings.xml index 34989cc04d..4846c3c63a 100644 --- a/app/src/main/res/values-vi/strings.xml +++ b/app/src/main/res/values-vi/strings.xml @@ -126,7 +126,6 @@ Giữa Xoay Tự do - Khóa Bắt buộc theo chiều dọc Bắt buộc theo chiều ngang R @@ -434,7 +433,6 @@ Đăng xuất Đăng xuất khỏi %1$s\? Địa chỉ email - Thông báo Tối ưu pin đã được tắt Giúp cập nhật và sao lưu thư viện nền Tắt tối ưu pin diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index 987ca65c5b..e205427e4f 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -84,8 +84,8 @@ 高级 关于 书架界面网格 - 纵向 - 横向 + 竖屏 + 横屏 预设 更新频率 手动 @@ -168,9 +168,8 @@ 快速 屏幕方向 自动 - 锁定 - 强制竖向 - 强制横向 + 锁定的竖屏 + 锁定的横屏 R G B @@ -446,7 +445,6 @@ 浅色主题 默认 浅蓝 - 预览版本公告 菜单 重新排序 最新 @@ -663,4 +661,6 @@ 显示错误 英勇不再支持此 Android 版本 未能复制到剪贴板 + 横屏 + 竖屏 \ No newline at end of file diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml index 8193ce133c..83f89634c5 100644 --- a/app/src/main/res/values-zh-rTW/strings.xml +++ b/app/src/main/res/values-zh-rTW/strings.xml @@ -125,7 +125,6 @@ 快速 螢幕方向 自動 - 鎖定 強制縱向 強制橫向 R @@ -402,7 +401,6 @@ 再按一次以離開 將套用至你書櫃中的漫畫 僅限下載內容 - 預覽版本公告 開放原始碼授權 網站 檢查更新 diff --git a/app/src/main/res/values/arrays.xml b/app/src/main/res/values/arrays.xml index 9479f94229..fd178ae5ab 100644 --- a/app/src/main/res/values/arrays.xml +++ b/app/src/main/res/values/arrays.xml @@ -61,6 +61,7 @@ + @string/default_rotation_type @string/rotation_free @string/rotation_portrait @string/rotation_landscape @@ -68,14 +69,6 @@ @string/rotation_force_landscape - - 1 - 2 - 3 - 4 - 5 - - @string/filter_mode_default @string/filter_mode_multiply diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 786ebbea83..c2d861a80c 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -291,7 +291,10 @@ Horizontal Vertical Both - Long tap dialog + Actions + Show on long tap + Saves pages into separate folders + Creates folders according to manga title Background color White Gray @@ -330,7 +333,9 @@ No animation Normal Fast - Rotation + Default rotation type + Default + Rotation type Free Portrait Landscape diff --git a/app/src/main/res/values/themes.xml b/app/src/main/res/values/themes.xml index 75ab61b32f..24e31ed5a4 100644 --- a/app/src/main/res/values/themes.xml +++ b/app/src/main/res/values/themes.xml @@ -91,7 +91,10 @@ -