Compare commits

...

56 Commits

Author SHA1 Message Date
0eb8d7d081 Release 0.10.11 2021-04-19 10:52:52 -04:00
554f890ae3 Weblate translations (#4812)
Co-authored-by: Ajeje Brazorf <lmelonimamo@yahoo.it>
Co-authored-by: Alessandro Jean <alessandrojean@gmail.com>
Co-authored-by: Allan Nordhøy <epost@anotheragency.no>
Co-authored-by: Ava <Sasu.ruotsalainen@live.fi>
Co-authored-by: Christian Elbrianno <christian.elbrianno41@gmail.com>
Co-authored-by: DarKCroX <darkcrox.2020@outlook.com>
Co-authored-by: Eric <spice2wolf@gmail.com>
Co-authored-by: Hautzii <am.03012002@gmail.com>
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: Huang Zhiyi <hzy980512@126.com>
Co-authored-by: J. Lavoie <j.lavoie@net-c.ca>
Co-authored-by: Jakub Fabijan <animatorzPolski@gmail.com>
Co-authored-by: Jozef Hollý <j2.00ghz@gmail.com>
Co-authored-by: Kurocon <weblate@kurocon.nl>
Co-authored-by: Lyfja <yassinelaoud@gmail.com>
Co-authored-by: Manuel Tassi <manueltassi91@gmail.com>
Co-authored-by: Marco Santos <enum.scima@gmail.com>
Co-authored-by: Matteo Gaeta <matteo.gaeta.1998@gmail.com>
Co-authored-by: Matyáš Caras <contact@hernikplays.cz>
Co-authored-by: Milo Ivir <mail@milotype.de>
Co-authored-by: OfficialBispo <diogobispo10@gmail.com>
Co-authored-by: Oğuz Ersen <oguzersen@protonmail.com>
Co-authored-by: Paulo Pinho <kebrus@gmail.com>
Co-authored-by: Pitpe11 <giorgos2550@gmail.com>
Co-authored-by: Reza Almanda <rezaalmanda27@gmail.com>
Co-authored-by: Rostyslav <info@ubilling.net.ua>
Co-authored-by: Shashank Pujari <shashankppujari@gmail.com>
Co-authored-by: Shjosan <shjosan@kakmix.co>
Co-authored-by: Zulkifli <zulhaha1@gmail.com>
Co-authored-by: f0roots <f0rootss@gmail.com>
Co-authored-by: monolifed <monolifed@protonmail.com>
Co-authored-by: Роман <Rozhenkov69@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/cs/
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/de/
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/el/
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/eo/
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/fi/
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/fil/
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/fr/
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/hr/
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/id/
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/it/
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/ja/
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/kn/
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/ms/
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/nb_NO/
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/nl/
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/pt/
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/pt_BR/
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/ro/
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/ru/
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/sc/
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/sv/
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/tr/
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/uk/
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/zh_Hans/
Translation: Tachiyomi/Tachiyomi 0.x

Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: Ajeje Brazorf <lmelonimamo@yahoo.it>
Co-authored-by: Alessandro Jean <alessandrojean@gmail.com>
Co-authored-by: Allan Nordhøy <epost@anotheragency.no>
Co-authored-by: Ava <Sasu.ruotsalainen@live.fi>
Co-authored-by: Christian Elbrianno <christian.elbrianno41@gmail.com>
Co-authored-by: DarKCroX <darkcrox.2020@outlook.com>
Co-authored-by: Eric <spice2wolf@gmail.com>
Co-authored-by: Hautzii <am.03012002@gmail.com>
Co-authored-by: Huang Zhiyi <hzy980512@126.com>
Co-authored-by: J. Lavoie <j.lavoie@net-c.ca>
Co-authored-by: Jakub Fabijan <animatorzPolski@gmail.com>
Co-authored-by: Kurocon <weblate@kurocon.nl>
Co-authored-by: Lyfja <yassinelaoud@gmail.com>
Co-authored-by: Manuel Tassi <manueltassi91@gmail.com>
Co-authored-by: Marco Santos <enum.scima@gmail.com>
Co-authored-by: Matteo Gaeta <matteo.gaeta.1998@gmail.com>
Co-authored-by: Matyáš Caras <contact@hernikplays.cz>
Co-authored-by: Milo Ivir <mail@milotype.de>
Co-authored-by: OfficialBispo <diogobispo10@gmail.com>
Co-authored-by: Oğuz Ersen <oguzersen@protonmail.com>
Co-authored-by: Paulo Pinho <kebrus@gmail.com>
Co-authored-by: Pitpe11 <giorgos2550@gmail.com>
Co-authored-by: Reza Almanda <rezaalmanda27@gmail.com>
Co-authored-by: Rostyslav <info@ubilling.net.ua>
Co-authored-by: Shashank Pujari <shashankppujari@gmail.com>
Co-authored-by: Shjosan <shjosan@kakmix.co>
Co-authored-by: Zulkifli <zulhaha1@gmail.com>
Co-authored-by: f0roots <f0rootss@gmail.com>
Co-authored-by: monolifed <monolifed@protonmail.com>
Co-authored-by: Роман <Rozhenkov69@gmail.com>
2021-04-19 10:33:06 -04:00
dd1743698f Theme BiometricUnlockActivity to avoid flashing light theme 2021-04-19 10:24:57 -04:00
b092e98ac9 Include extension loading errors in error logs 2021-04-19 10:18:32 -04:00
9ee6262aed Fix activity leak 2021-04-19 10:18:32 -04:00
24a2d86f41 Fix status bar icon colors in webview activity (#4903)
* Fixed status bar icon colors in webview activity

* Changed theme to Theme.Base

* Changed app theme to Theme.Base

* Update themes.xml

Co-authored-by: arkon <arkon@users.noreply.github.com>
2021-04-19 10:16:25 -04:00
b5c5c66336 [SKIP CI] Update FUNDING.yml (#4907) 2021-04-19 09:31:34 -04:00
7654feb6a8 Fix chapter read status not being migrated (fixes #4892) 2021-04-18 13:07:53 -04:00
a598ac3993 Update LeakCanary 2021-04-18 12:55:17 -04:00
cab919d74c Clean up controller viewbinding creation
Based on https://github.com/Jays2Kings/tachiyomiJ2K/blob/master/app/src/main/java/eu/kanade/tachiyomi/ui/base/controller/BaseController.kt
2021-04-18 12:54:51 -04:00
60a929b92c Fix source SearchView stuck open until query submitted (#4897)
closes #4850
2021-04-18 11:32:22 -04:00
356b7c346a Clean up ChapterCache (remove Gson, Rx usage) 2021-04-18 11:30:16 -04:00
ad57fde1c5 Themes cleanup (#4894) 2021-04-18 11:29:56 -04:00
17f7dea21b Update KotlinX dependencies 2021-04-17 19:19:08 -04:00
b40af7c3c6 Minor cleanup 2021-04-17 19:05:35 -04:00
9065362fde Move reading mode toast to default bottom position
Toasts don't block user interaction, so it's probably fine.
2021-04-17 18:52:52 -04:00
d264b03ca1 [SKIP CI] Update README banner image (#4887)
* Delete screens.png

* Add files via upload
2021-04-17 18:49:47 -04:00
ad9bad3d17 Adjust ActionToolbar positioning
Have I ever mentioned that I hate insets?
2021-04-17 13:07:25 -04:00
dfd858034f Avoid duplicate actions in update notifications 2021-04-17 12:58:14 -04:00
58ad8fa8c0 Add clipboard error string
I pulled a Jay and forgot to stage something.
2021-04-17 12:33:04 -04:00
38610d8a24 Avoid crash when users copying to clipboard fails because they have apps that are listening to their clipboards but also denied permissions
See https://commonsware.com/blog/2013/08/08/developer-psa-please-fix-your-clipboard-handling.html
2021-04-17 12:29:22 -04:00
27cec697bf Avoid rare crash in WebViewActivity 2021-04-17 12:22:58 -04:00
024f9a8c76 [SKIP CI] Add string for EOL update check message 2021-04-17 11:54:17 -04:00
f7cc36f2f0 Follow chapter sort setting for start/resume FAB (closes #1716) 2021-04-17 11:38:08 -04:00
ef5148ebb4 Double tap Updates to go to Download Queue (closes #4884) 2021-04-17 11:13:09 -04:00
6dbc0a6fd5 Use DSL for creating chapter description spanned string 2021-04-17 11:06:30 -04:00
fba3f9d501 Follow chapter sort setting when downloading next n chapters (closes #4725) 2021-04-17 10:51:38 -04:00
d9f8137362 Update issue templates 2021-04-16 23:16:46 -04:00
28416489b2 Adjust MoreController bottom padding for navbar 2021-04-16 23:10:38 -04:00
54a23ddd1f Long press reader settings icon to open color filter tab
Partially addresses #4867
2021-04-16 23:06:24 -04:00
3287ca9cf2 Add checkmark beside selected popup menu item
Based on what's in J2K. Also renamed to MaterialSpinnerView to match what's there.

Co-authored-by: Jays2Kings <Jays2Kings@users.noreply.github.com>
2021-04-16 22:39:19 -04:00
a59e134862 Case insensitive source directory search 2021-04-16 22:27:00 -04:00
1f8c5b0120 Adjust ActionToolbar positioning 2021-04-16 22:26:41 -04:00
c7f839ea4a Minor cleanup 2021-04-15 10:09:16 -04:00
d981245723 Remove toolbar snapping 2021-04-15 10:05:47 -04:00
1f729f1cb3 Add navigation bar scrim (#4845)
* Revert "Add navigation bar scrim (closes #4836)"

This reverts commit 2a69d1b0

* Add navigation bar scrim
2021-04-15 09:55:39 -04:00
b4577d6676 Avoid crash when unknown reading mode is used 2021-04-14 18:03:48 -04:00
544adb9940 Handle reader toolbar subtitle getting cut off when text is too big (closes #4843) 2021-04-14 08:59:23 -04:00
1875c4a752 Include chapter fetch date when migrating
Based on ee4f3e6586

Co-authored-by: Jays2Kings <Jays2Kings@users.noreply.github.com>
2021-04-14 08:57:00 -04:00
5f0493f1e5 Fix webtoon mode not calling OnPageSelected in some cases (in upstream too)
This fix isn't 100% tested, but like 80%.

@arkon if you're reading this, this issue is happening up stream too. I can make a issue for it in the repo but haven't checked if it happens there:

Steps:
Get Cubari source, search "cubari:imgur/3iOqiIy" change to continuous vertical, crop borders. Then back out and open the chapter again. onPageSelected isn't called because recycler position is -1. Regardless of the 4 pages you should be on

also fyi just a slight scroll fixes this issue but still

(cherry picked from commit 88fd6e5c9897d4a528f93dd02cfa2a4c644a799d)
2021-04-14 08:49:48 -04:00
c749e50bec Edge-to-edge in licenses activity 2021-04-13 22:48:54 -04:00
a4e5e3ece5 Use accent color for edge effect 2021-04-13 22:48:39 -04:00
2a69d1b051 Add navigation bar scrim (closes #4836) 2021-04-13 18:23:06 -04:00
126e1e2d9d Allow weaker unlock methods in Android 6 - 10 (fixes #4833) 2021-04-13 15:02:57 -04:00
0586e1d3ad Include debug info in dumped crash logs 2021-04-13 09:06:41 -04:00
07cb1c237e Allow dismissing download progress notification when paused (closes #4832) 2021-04-13 08:53:46 -04:00
f4f1efe5fa Disallow forced dark mode, such as MIUI's 2021-04-13 08:51:08 -04:00
37fdf4d434 Fix toolbar elevation in History and Updates 2021-04-12 18:43:22 -04:00
99b46096a4 Fully expand source filter sheet on show (closes #4455) 2021-04-12 17:30:44 -04:00
12e90ae35e Use same non-sticky heading style as Browse for Updates/History (closes #4822) 2021-04-12 17:11:47 -04:00
023311a874 Start download when tapping update notification (closes #4825) 2021-04-12 13:43:46 -04:00
155a4dd463 Fix ActionToolbar bottom offset 2021-04-12 12:42:07 -04:00
15bed1ac4c Offset appbar using margin instead (maybe fixes #4819) 2021-04-12 09:01:11 -04:00
27f55f8098 Fix LibraryUpdateServiceTest so ./gradlew ... doesn't crash (#4821) 2021-04-12 08:35:00 -04:00
00598879e2 Insets fix for migration manga list 2021-04-11 22:57:54 -04:00
df274a0a78 Always create releases as draft 2021-04-11 18:40:54 -04:00
114 changed files with 990 additions and 709 deletions

1
.github/FUNDING.yml vendored
View File

@ -1,2 +1 @@
github: inorichi
ko_fi: inorichi ko_fi: inorichi

View File

@ -2,9 +2,15 @@
I acknowledge that: I acknowledge that:
- I have updated to the latest version of the app (stable is v0.10.10) - I have updated:
- I have updated all extensions - To the latest version of the app (stable is v0.10.10)
- 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 - 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
- I will fill out the title and the information in this template
Note that the issue will be automatically closed if you do not fill out the title or requested information.
**DELETE THIS SECTION IF YOU HAVE READ AND ACKNOWLEDGED IT** **DELETE THIS SECTION IF YOU HAVE READ AND ACKNOWLEDGED IT**

View File

@ -9,9 +9,15 @@ labels: "bug"
I acknowledge that: I acknowledge that:
- I have updated to the latest version of the app (stable is v0.10.10) - I have updated:
- I have updated all extensions - To the latest version of the app (stable is v0.10.10)
- 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 - 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
- I will fill out the title and the information in this template
Note that the issue will be automatically closed if you do not fill out the title or requested information.
**DELETE THIS SECTION IF YOU HAVE READ AND ACKNOWLEDGED IT** **DELETE THIS SECTION IF YOU HAVE READ AND ACKNOWLEDGED IT**

View File

@ -9,9 +9,14 @@ labels: "feature"
I acknowledge that: I acknowledge that:
- I have updated to the latest version of the app (stable is v0.10.10) - I have updated:
- I have updated all extensions - To the latest version of the app (stable is v0.10.10)
- All extensions
- If this is an issue with an extension, that I should be opening an issue in https://github.com/tachiyomiorg/tachiyomi-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
- I will fill out the title and the information in this template
Note that the issue will be automatically closed if you do not fill out the title or requested information.
**DELETE THIS SECTION IF YOU HAVE READ AND ACKNOWLEDGED IT** **DELETE THIS SECTION IF YOU HAVE READ AND ACKNOWLEDGED IT**

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 MiB

After

Width:  |  Height:  |  Size: 454 KiB

View File

@ -88,7 +88,7 @@ jobs:
MD5: ${{ env.APK_MD5 }} MD5: ${{ env.APK_MD5 }}
files: | files: |
tachiyomi-${{ env.VERSION_TAG }}.apk tachiyomi-${{ env.VERSION_TAG }}.apk
draft: ${{ github.event.inputs.dry-run != '' }} draft: true
prerelease: false prerelease: false
env: env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

View File

@ -11,7 +11,7 @@ Tachiyomi is a free and open source manga reader for Android 5.0 and above.
## Features ## Features
Features include: Features include:
* Online reading from sources such as MangaDex, MangaSee, Mangakakalot, [and more](https://github.com/tachiyomiorg/tachiyomi-extensions) * Online reading from [a variety of sources](https://github.com/tachiyomiorg/tachiyomi-extensions)
* Local reading of downloaded manga * Local reading of downloaded manga
* A configurable reader with multiple viewers, reading directions and other settings. * A configurable reader with multiple viewers, reading directions and other settings.
* [MyAnimeList](https://myanimelist.net/), [AniList](https://anilist.co/), [Kitsu](https://kitsu.io/), [Shikimori](https://shikimori.one), and [Bangumi](https://bgm.tv/) support * [MyAnimeList](https://myanimelist.net/), [AniList](https://anilist.co/), [Kitsu](https://kitsu.io/), [Shikimori](https://shikimori.one), and [Bangumi](https://bgm.tv/) support

View File

@ -29,8 +29,8 @@ android {
minSdkVersion(AndroidConfig.minSdk) minSdkVersion(AndroidConfig.minSdk)
targetSdkVersion(AndroidConfig.targetSdk) targetSdkVersion(AndroidConfig.targetSdk)
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
versionCode = 57 versionCode = 58
versionName = "0.10.10" versionName = "0.10.11"
buildConfigField("String", "COMMIT_COUNT", "\"${getCommitCount()}\"") buildConfigField("String", "COMMIT_COUNT", "\"${getCommitCount()}\"")
buildConfigField("String", "COMMIT_SHA", "\"${getGitSha()}\"") buildConfigField("String", "COMMIT_SHA", "\"${getGitSha()}\"")
@ -163,7 +163,7 @@ dependencies {
implementation("org.conscrypt:conscrypt-android:2.5.1") implementation("org.conscrypt:conscrypt-android:2.5.1")
// JSON // JSON
val kotlinSerializationVersion = "1.0.1" val kotlinSerializationVersion = "1.1.0"
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:$kotlinSerializationVersion") implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:$kotlinSerializationVersion")
implementation("org.jetbrains.kotlinx:kotlinx-serialization-protobuf:$kotlinSerializationVersion") implementation("org.jetbrains.kotlinx:kotlinx-serialization-protobuf:$kotlinSerializationVersion")
implementation("com.google.code.gson:gson:2.8.6") implementation("com.google.code.gson:gson:2.8.6")
@ -174,7 +174,7 @@ dependencies {
// Disk // Disk
implementation("com.jakewharton:disklrucache:2.0.2") implementation("com.jakewharton:disklrucache:2.0.2")
implementation("com.github.tachiyomiorg:unifile:e9e3a40") implementation("com.github.tachiyomiorg:unifile:17bec43")
implementation("com.github.junrar:junrar:7.4.0") implementation("com.github.junrar:junrar:7.4.0")
// HTML parser // HTML parser
@ -260,12 +260,12 @@ dependencies {
implementation(kotlin("reflect", version = BuildPluginsVersion.KOTLIN)) implementation(kotlin("reflect", version = BuildPluginsVersion.KOTLIN))
val coroutinesVersion = "1.4.2" val coroutinesVersion = "1.4.3"
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutinesVersion") implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutinesVersion")
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutinesVersion") implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutinesVersion")
// For detecting memory leaks; see https://square.github.io/leakcanary/ // For detecting memory leaks; see https://square.github.io/leakcanary/
// debugImplementation("com.squareup.leakcanary:leakcanary-android:2.6") // debugImplementation("com.squareup.leakcanary:leakcanary-android:2.7")
} }
tasks { tasks {

View File

@ -32,7 +32,7 @@
android:largeHeap="true" android:largeHeap="true"
android:requestLegacyExternalStorage="true" android:requestLegacyExternalStorage="true"
android:roundIcon="@mipmap/ic_launcher_round" android:roundIcon="@mipmap/ic_launcher_round"
android:theme="@style/Theme.Tachiyomi.Light" android:theme="@style/Theme.Base"
android:networkSecurityConfig="@xml/network_security_config"> android:networkSecurityConfig="@xml/network_security_config">
<activity <activity
android:name=".ui.main.MainActivity" android:name=".ui.main.MainActivity"
@ -84,7 +84,7 @@
</activity> </activity>
<activity <activity
android:name=".ui.security.BiometricUnlockActivity" android:name=".ui.security.BiometricUnlockActivity"
android:theme="@style/Theme.Splash" /> android:theme="@style/Theme.Base" />
<activity <activity
android:name=".ui.webview.WebViewActivity" android:name=".ui.webview.WebViewActivity"
android:configChanges="uiMode|orientation|screenSize" /> android:configChanges="uiMode|orientation|screenSize" />

View File

@ -2,17 +2,17 @@ package eu.kanade.tachiyomi.data.cache
import android.content.Context import android.content.Context
import android.text.format.Formatter import android.text.format.Formatter
import com.github.salomonbrys.kotson.fromJson
import com.google.gson.Gson
import com.jakewharton.disklrucache.DiskLruCache import com.jakewharton.disklrucache.DiskLruCache
import eu.kanade.tachiyomi.data.database.models.Chapter import eu.kanade.tachiyomi.data.database.models.Chapter
import eu.kanade.tachiyomi.source.model.Page import eu.kanade.tachiyomi.source.model.Page
import eu.kanade.tachiyomi.util.storage.DiskUtil import eu.kanade.tachiyomi.util.storage.DiskUtil
import eu.kanade.tachiyomi.util.storage.saveTo import eu.kanade.tachiyomi.util.storage.saveTo
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
import okhttp3.Response import okhttp3.Response
import okio.buffer import okio.buffer
import okio.sink import okio.sink
import rx.Observable
import uy.kohesive.injekt.injectLazy import uy.kohesive.injekt.injectLazy
import java.io.File import java.io.File
import java.io.IOException import java.io.IOException
@ -42,8 +42,7 @@ class ChapterCache(private val context: Context) {
const val PARAMETER_CACHE_SIZE = 100L * 1024 * 1024 const val PARAMETER_CACHE_SIZE = 100L * 1024 * 1024
} }
/** Google Json class used for parsing JSON files. */ private val json: Json by injectLazy()
private val gson: Gson by injectLazy()
/** Cache class used for cache management. */ /** Cache class used for cache management. */
private val diskCache = DiskLruCache.open( private val diskCache = DiskLruCache.open(
@ -56,7 +55,7 @@ class ChapterCache(private val context: Context) {
/** /**
* Returns directory of cache. * Returns directory of cache.
*/ */
val cacheDir: File private val cacheDir: File
get() = diskCache.directory get() = diskCache.directory
/** /**
@ -71,43 +70,19 @@ class ChapterCache(private val context: Context) {
val readableSize: String val readableSize: String
get() = Formatter.formatFileSize(context, realSize) get() = Formatter.formatFileSize(context, realSize)
/**
* Remove file from cache.
*
* @param file name of file "md5.0".
* @return status of deletion for the file.
*/
fun removeFileFromCache(file: String): Boolean {
// Make sure we don't delete the journal file (keeps track of cache).
if (file == "journal" || file.startsWith("journal.")) {
return false
}
return try {
// Remove the extension from the file to get the key of the cache
val key = file.substringBeforeLast(".")
// Remove file from cache.
diskCache.remove(key)
} catch (e: Exception) {
false
}
}
/** /**
* Get page list from cache. * Get page list from cache.
* *
* @param chapter the chapter. * @param chapter the chapter.
* @return an observable of the list of pages. * @return the list of pages.
*/ */
fun getPageListFromCache(chapter: Chapter): Observable<List<Page>> { fun getPageListFromCache(chapter: Chapter): List<Page> {
return Observable.fromCallable { // Get the key for the chapter.
// Get the key for the chapter. val key = DiskUtil.hashKeyForDisk(getKey(chapter))
val key = DiskUtil.hashKeyForDisk(getKey(chapter))
// Convert JSON string to list of objects. Throws an exception if snapshot is null // Convert JSON string to list of objects. Throws an exception if snapshot is null
diskCache.get(key).use { return diskCache.get(key).use {
gson.fromJson<List<Page>>(it.getString(0)) json.decodeFromString(it.getString(0))
}
} }
} }
@ -119,7 +94,7 @@ class ChapterCache(private val context: Context) {
*/ */
fun putPageListToCache(chapter: Chapter, pages: List<Page>) { fun putPageListToCache(chapter: Chapter, pages: List<Page>) {
// Convert list of pages to json string. // Convert list of pages to json string.
val cachedValue = gson.toJson(pages) val cachedValue = json.encodeToString(pages)
// Initialize the editor (edits the values for an entry). // Initialize the editor (edits the values for an entry).
var editor: DiskLruCache.Editor? = null var editor: DiskLruCache.Editor? = null
@ -199,6 +174,38 @@ class ChapterCache(private val context: Context) {
} }
} }
fun clear(): Int {
var deletedFiles = 0
cacheDir.listFiles()?.forEach {
if (removeFileFromCache(it.name)) {
deletedFiles++
}
}
return deletedFiles
}
/**
* Remove file from cache.
*
* @param file name of file "md5.0".
* @return status of deletion for the file.
*/
private fun removeFileFromCache(file: String): Boolean {
// Make sure we don't delete the journal file (keeps track of cache).
if (file == "journal" || file.startsWith("journal.")) {
return false
}
return try {
// Remove the extension from the file to get the key of the cache
val key = file.substringBeforeLast(".")
// Remove file from cache.
diskCache.remove(key)
} catch (e: Exception) {
false
}
}
private fun getKey(chapter: Chapter): String { private fun getKey(chapter: Chapter): String {
return "${chapter.manga_id}${chapter.url}" return "${chapter.manga_id}${chapter.url}"
} }

View File

@ -28,7 +28,6 @@ internal class DownloadNotifier(private val context: Context) {
context.notificationBuilder(Notifications.CHANNEL_DOWNLOADER_PROGRESS) { context.notificationBuilder(Notifications.CHANNEL_DOWNLOADER_PROGRESS) {
setLargeIcon(BitmapFactory.decodeResource(context.resources, R.mipmap.ic_launcher)) setLargeIcon(BitmapFactory.decodeResource(context.resources, R.mipmap.ic_launcher))
setAutoCancel(false) setAutoCancel(false)
setOngoing(true)
setOnlyAlertOnce(true) setOnlyAlertOnce(true)
} }
} }
@ -84,7 +83,6 @@ internal class DownloadNotifier(private val context: Context) {
*/ */
fun onProgressChange(download: Download) { fun onProgressChange(download: Download) {
with(progressNotificationBuilder) { with(progressNotificationBuilder) {
// Check if first call.
if (!isDownloading) { if (!isDownloading) {
setSmallIcon(android.R.drawable.stat_sys_download) setSmallIcon(android.R.drawable.stat_sys_download)
clearActions() clearActions()
@ -116,6 +114,7 @@ internal class DownloadNotifier(private val context: Context) {
} }
setProgress(download.pages!!.size, download.downloadedImages, false) setProgress(download.pages!!.size, download.downloadedImages, false)
setOngoing(true)
show(Notifications.ID_DOWNLOAD_CHAPTER_PROGRESS) show(Notifications.ID_DOWNLOAD_CHAPTER_PROGRESS)
} }
@ -130,6 +129,7 @@ internal class DownloadNotifier(private val context: Context) {
setContentText(context.getString(R.string.download_notifier_download_paused)) setContentText(context.getString(R.string.download_notifier_download_paused))
setSmallIcon(R.drawable.ic_pause_24dp) setSmallIcon(R.drawable.ic_pause_24dp)
setProgress(0, 0, false) setProgress(0, 0, false)
setOngoing(false)
clearActions() clearActions()
// Open download manager when clicked // Open download manager when clicked
setContentIntent(NotificationHandler.openDownloadManagerPendingActivity(context)) setContentIntent(NotificationHandler.openDownloadManagerPendingActivity(context))

View File

@ -65,7 +65,7 @@ class DownloadProvider(private val context: Context) {
* @param source the source to query. * @param source the source to query.
*/ */
fun findSourceDir(source: Source): UniFile? { fun findSourceDir(source: Source): UniFile? {
return downloadsDir.findFile(getSourceDirName(source)) return downloadsDir.findFile(getSourceDirName(source), true)
} }
/** /**

View File

@ -1,9 +1,6 @@
package eu.kanade.tachiyomi.data.updater package eu.kanade.tachiyomi.data.updater
import android.app.PendingIntent
import android.content.Context import android.content.Context
import android.content.Intent
import androidx.core.app.NotificationCompat
import androidx.work.Constraints import androidx.work.Constraints
import androidx.work.ExistingPeriodicWorkPolicy import androidx.work.ExistingPeriodicWorkPolicy
import androidx.work.NetworkType import androidx.work.NetworkType
@ -11,52 +8,26 @@ import androidx.work.PeriodicWorkRequestBuilder
import androidx.work.WorkManager import androidx.work.WorkManager
import androidx.work.Worker import androidx.work.Worker
import androidx.work.WorkerParameters import androidx.work.WorkerParameters
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.notification.Notifications
import eu.kanade.tachiyomi.data.updater.github.GithubUpdateChecker import eu.kanade.tachiyomi.data.updater.github.GithubUpdateChecker
import eu.kanade.tachiyomi.util.system.notificationManager
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
class UpdaterJob(private val context: Context, workerParams: WorkerParameters) : class UpdaterJob(private val context: Context, workerParams: WorkerParameters) :
Worker(context, workerParams) { Worker(context, workerParams) {
override fun doWork(): Result { override fun doWork() = runBlocking {
return runBlocking { try {
try { val result = GithubUpdateChecker().checkForUpdate()
val result = GithubUpdateChecker().checkForUpdate()
if (result is UpdateResult.NewUpdate<*>) { if (result is UpdateResult.NewUpdate<*>) {
val url = result.release.downloadLink UpdaterNotifier(context).promptUpdate(result.release.downloadLink)
val intent = Intent(context, UpdaterService::class.java).apply {
putExtra(UpdaterService.EXTRA_DOWNLOAD_URL, url)
}
NotificationCompat.Builder(context, Notifications.CHANNEL_COMMON).update {
setContentTitle(context.getString(R.string.app_name))
setContentText(context.getString(R.string.update_check_notification_update_available))
setSmallIcon(android.R.drawable.stat_sys_download_done)
// Download action
addAction(
android.R.drawable.stat_sys_download_done,
context.getString(R.string.action_download),
PendingIntent.getService(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT)
)
}
}
Result.success()
} catch (e: Exception) {
Result.failure()
} }
Result.success()
} catch (e: Exception) {
Result.failure()
} }
} }
fun NotificationCompat.Builder.update(block: NotificationCompat.Builder.() -> Unit) {
block()
context.notificationManager.notify(Notifications.ID_UPDATER, build())
}
companion object { companion object {
private const val TAG = "UpdateChecker" private const val TAG = "UpdateChecker"

View File

@ -1,6 +1,8 @@
package eu.kanade.tachiyomi.data.updater package eu.kanade.tachiyomi.data.updater
import android.app.PendingIntent
import android.content.Context import android.content.Context
import android.content.Intent
import android.net.Uri import android.net.Uri
import androidx.core.app.NotificationCompat import androidx.core.app.NotificationCompat
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
@ -28,6 +30,27 @@ internal class UpdaterNotifier(private val context: Context) {
context.notificationManager.notify(id, build()) context.notificationManager.notify(id, build())
} }
fun promptUpdate(url: String) {
val intent = Intent(context, UpdaterService::class.java).apply {
putExtra(UpdaterService.EXTRA_DOWNLOAD_URL, url)
}
val pendingIntent = PendingIntent.getService(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT)
with(notificationBuilder) {
setContentTitle(context.getString(R.string.app_name))
setContentText(context.getString(R.string.update_check_notification_update_available))
setSmallIcon(android.R.drawable.stat_sys_download_done)
setContentIntent(pendingIntent)
clearActions()
addAction(
android.R.drawable.stat_sys_download_done,
context.getString(R.string.action_download),
pendingIntent
)
}
notificationBuilder.show()
}
/** /**
* Call when apk download starts. * Call when apk download starts.
* *
@ -63,19 +86,20 @@ internal class UpdaterNotifier(private val context: Context) {
* @param uri path location of apk. * @param uri path location of apk.
*/ */
fun onDownloadFinished(uri: Uri) { fun onDownloadFinished(uri: Uri) {
val installIntent = NotificationHandler.installApkPendingActivity(context, uri)
with(notificationBuilder) { with(notificationBuilder) {
setContentText(context.getString(R.string.update_check_notification_download_complete)) setContentText(context.getString(R.string.update_check_notification_download_complete))
setSmallIcon(android.R.drawable.stat_sys_download_done) setSmallIcon(android.R.drawable.stat_sys_download_done)
setOnlyAlertOnce(false) setOnlyAlertOnce(false)
setProgress(0, 0, false) setProgress(0, 0, false)
// Install action setContentIntent(installIntent)
setContentIntent(NotificationHandler.installApkPendingActivity(context, uri))
clearActions()
addAction( addAction(
R.drawable.ic_system_update_alt_white_24dp, R.drawable.ic_system_update_alt_white_24dp,
context.getString(R.string.action_install), context.getString(R.string.action_install),
NotificationHandler.installApkPendingActivity(context, uri) installIntent
) )
// Cancel action
addAction( addAction(
R.drawable.ic_close_24dp, R.drawable.ic_close_24dp,
context.getString(R.string.action_cancel), context.getString(R.string.action_cancel),
@ -96,13 +120,13 @@ internal class UpdaterNotifier(private val context: Context) {
setSmallIcon(android.R.drawable.stat_sys_warning) setSmallIcon(android.R.drawable.stat_sys_warning)
setOnlyAlertOnce(false) setOnlyAlertOnce(false)
setProgress(0, 0, false) setProgress(0, 0, false)
// Retry action
clearActions()
addAction( addAction(
R.drawable.ic_refresh_24dp, R.drawable.ic_refresh_24dp,
context.getString(R.string.action_retry), context.getString(R.string.action_retry),
UpdaterService.downloadApkPendingService(context, url) UpdaterService.downloadApkPendingService(context, url)
) )
// Cancel action
addAction( addAction(
R.drawable.ic_close_24dp, R.drawable.ic_close_24dp,
context.getString(R.string.action_cancel), context.getString(R.string.action_cancel),

View File

@ -163,7 +163,7 @@ internal object ExtensionLoader {
else -> throw Exception("Unknown source class type! ${obj.javaClass}") else -> throw Exception("Unknown source class type! ${obj.javaClass}")
} }
} catch (e: Throwable) { } catch (e: Throwable) {
Timber.w(e, "Extension load error: $extName ($it)") Timber.e(e, "Extension load error: $extName ($it)")
return LoadResult.Error(e) return LoadResult.Error(e)
} }
} }

View File

@ -1,65 +1,39 @@
package eu.kanade.tachiyomi.ui.base.activity package eu.kanade.tachiyomi.ui.base.activity
import android.content.res.Configuration import android.content.res.Configuration.UI_MODE_NIGHT_MASK
import android.os.Build import android.content.res.Configuration.UI_MODE_NIGHT_YES
import android.os.Bundle import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.preference.PreferenceValues.DarkThemeVariant
import eu.kanade.tachiyomi.data.preference.PreferenceValues.LightThemeVariant
import eu.kanade.tachiyomi.data.preference.PreferenceValues.ThemeMode
import eu.kanade.tachiyomi.data.preference.PreferencesHelper import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import uy.kohesive.injekt.injectLazy import uy.kohesive.injekt.injectLazy
import eu.kanade.tachiyomi.data.preference.PreferenceValues as Values
abstract class BaseThemedActivity : AppCompatActivity() { abstract class BaseThemedActivity : AppCompatActivity() {
val preferences: PreferencesHelper by injectLazy() val preferences: PreferencesHelper by injectLazy()
val isDarkMode: Boolean by lazy {
val themeMode = preferences.themeMode().get()
(themeMode == Values.ThemeMode.dark) ||
(
themeMode == Values.ThemeMode.system &&
(resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK == Configuration.UI_MODE_NIGHT_YES)
)
}
private val lightTheme: Int by lazy {
when (preferences.themeLight().get()) {
Values.LightThemeVariant.blue -> R.style.Theme_Tachiyomi_LightBlue
else -> {
when {
// Light status + navigation bar
Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1 -> {
R.style.Theme_Tachiyomi_Light_Api27
}
// Light status bar + fallback gray navigation bar
Build.VERSION.SDK_INT >= Build.VERSION_CODES.M -> {
R.style.Theme_Tachiyomi_Light_Api23
}
// Fallback gray status + navigation bar
else -> {
R.style.Theme_Tachiyomi_Light
}
}
}
}
}
private val darkTheme: Int by lazy {
when (preferences.themeDark().get()) {
Values.DarkThemeVariant.blue -> R.style.Theme_Tachiyomi_DarkBlue
Values.DarkThemeVariant.amoled -> R.style.Theme_Tachiyomi_Amoled
else -> R.style.Theme_Tachiyomi_Dark
}
}
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
setTheme( val isDarkMode = when (preferences.themeMode().get()) {
when { ThemeMode.light -> false
isDarkMode -> darkTheme ThemeMode.dark -> true
else -> lightTheme ThemeMode.system -> resources.configuration.uiMode and UI_MODE_NIGHT_MASK == UI_MODE_NIGHT_YES
}
val themeId = if (isDarkMode) {
when (preferences.themeDark().get()) {
DarkThemeVariant.default -> R.style.Theme_Tachiyomi_Dark
DarkThemeVariant.blue -> R.style.Theme_Tachiyomi_Dark_Blue
DarkThemeVariant.amoled -> R.style.Theme_Tachiyomi_Dark_Amoled
} }
) } else {
when (preferences.themeLight().get()) {
LightThemeVariant.default -> R.style.Theme_Tachiyomi_Light
LightThemeVariant.blue -> R.style.Theme_Tachiyomi_Light_Blue
}
}
setTheme(themeId)
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
} }
} }

View File

@ -19,7 +19,8 @@ import timber.log.Timber
abstract class BaseController<VB : ViewBinding>(bundle: Bundle? = null) : abstract class BaseController<VB : ViewBinding>(bundle: Bundle? = null) :
RestoreViewOnCreateController(bundle) { RestoreViewOnCreateController(bundle) {
lateinit var binding: VB protected lateinit var binding: VB
private set
lateinit var viewScope: CoroutineScope lateinit var viewScope: CoroutineScope
@ -51,11 +52,12 @@ abstract class BaseController<VB : ViewBinding>(bundle: Bundle? = null) :
) )
} }
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup, savedViewState: Bundle?): View { abstract fun createBinding(inflater: LayoutInflater): VB
return inflateView(inflater, container)
}
abstract fun inflateView(inflater: LayoutInflater, container: ViewGroup): View override fun onCreateView(inflater: LayoutInflater, container: ViewGroup, savedViewState: Bundle?): View {
binding = createBinding(inflater)
return binding.root
}
open fun onViewCreated(view: View) {} open fun onViewCreated(view: View) {}

View File

@ -3,7 +3,6 @@ package eu.kanade.tachiyomi.ui.browse
import android.os.Bundle import android.os.Bundle
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup
import androidx.core.os.bundleOf import androidx.core.os.bundleOf
import com.bluelinelabs.conductor.Controller import com.bluelinelabs.conductor.Controller
import com.bluelinelabs.conductor.ControllerChangeHandler import com.bluelinelabs.conductor.ControllerChangeHandler
@ -50,10 +49,7 @@ class BrowseController :
return resources!!.getString(R.string.browse) return resources!!.getString(R.string.browse)
} }
override fun inflateView(inflater: LayoutInflater, container: ViewGroup): View { override fun createBinding(inflater: LayoutInflater) = PagerControllerBinding.inflate(inflater)
binding = PagerControllerBinding.inflate(inflater)
return binding.root
}
override fun onViewCreated(view: View) { override fun onViewCreated(view: View) {
super.onViewCreated(view) super.onViewCreated(view)

View File

@ -5,7 +5,6 @@ import android.view.Menu
import android.view.MenuInflater import android.view.MenuInflater
import android.view.MenuItem import android.view.MenuItem
import android.view.View import android.view.View
import android.view.ViewGroup
import androidx.appcompat.widget.SearchView import androidx.appcompat.widget.SearchView
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import com.bluelinelabs.conductor.ControllerChangeHandler import com.bluelinelabs.conductor.ControllerChangeHandler
@ -57,18 +56,16 @@ open class ExtensionController :
return ExtensionPresenter() return ExtensionPresenter()
} }
override fun inflateView(inflater: LayoutInflater, container: ViewGroup): View { override fun createBinding(inflater: LayoutInflater) = ExtensionControllerBinding.inflate(inflater)
binding = ExtensionControllerBinding.inflate(inflater)
override fun onViewCreated(view: View) {
super.onViewCreated(view)
binding.recycler.applyInsetter { binding.recycler.applyInsetter {
type(navigationBars = true) { type(navigationBars = true) {
padding() padding()
} }
} }
return binding.root
}
override fun onViewCreated(view: View) {
super.onViewCreated(view)
binding.swipeRefresh.isRefreshing = true binding.swipeRefresh.isRefreshing = true
binding.swipeRefresh.refreshes() binding.swipeRefresh.refreshes()

View File

@ -4,12 +4,12 @@ import android.annotation.SuppressLint
import android.view.View import android.view.View
import eu.davidea.flexibleadapter.FlexibleAdapter import eu.davidea.flexibleadapter.FlexibleAdapter
import eu.davidea.viewholders.FlexibleViewHolder import eu.davidea.viewholders.FlexibleViewHolder
import eu.kanade.tachiyomi.databinding.SourceMainControllerCardHeaderBinding import eu.kanade.tachiyomi.databinding.SectionHeaderItemBinding
class ExtensionGroupHolder(view: View, adapter: FlexibleAdapter<*>) : class ExtensionGroupHolder(view: View, adapter: FlexibleAdapter<*>) :
FlexibleViewHolder(view, adapter) { FlexibleViewHolder(view, adapter) {
private val binding = SourceMainControllerCardHeaderBinding.bind(view) private val binding = SectionHeaderItemBinding.bind(view)
@SuppressLint("SetTextI18n") @SuppressLint("SetTextI18n")
fun bind(item: ExtensionGroupItem) { fun bind(item: ExtensionGroupItem) {

View File

@ -19,7 +19,7 @@ data class ExtensionGroupItem(val name: String, val size: Int, val showSize: Boo
* Returns the layout resource of this item. * Returns the layout resource of this item.
*/ */
override fun getLayoutRes(): Int { override fun getLayoutRes(): Int {
return R.layout.source_main_controller_card_header return R.layout.section_header_item
} }
/** /**

View File

@ -12,7 +12,6 @@ import android.view.Menu
import android.view.MenuInflater import android.view.MenuInflater
import android.view.MenuItem import android.view.MenuItem
import android.view.View import android.view.View
import android.view.ViewGroup
import androidx.appcompat.view.ContextThemeWrapper import androidx.appcompat.view.ContextThemeWrapper
import androidx.core.os.bundleOf import androidx.core.os.bundleOf
import androidx.preference.Preference import androidx.preference.Preference
@ -65,15 +64,9 @@ class ExtensionDetailsController(bundle: Bundle? = null) :
setHasOptionsMenu(true) setHasOptionsMenu(true)
} }
override fun inflateView(inflater: LayoutInflater, container: ViewGroup): View { override fun createBinding(inflater: LayoutInflater): ExtensionDetailControllerBinding {
val themedInflater = inflater.cloneInContext(getPreferenceThemeContext()) val themedInflater = inflater.cloneInContext(getPreferenceThemeContext())
binding = ExtensionDetailControllerBinding.inflate(themedInflater) return ExtensionDetailControllerBinding.inflate(themedInflater)
binding.extensionPrefsRecycler.applyInsetter {
type(navigationBars = true) {
padding()
}
}
return binding.root
} }
override fun createPresenter(): ExtensionDetailsPresenter { override fun createPresenter(): ExtensionDetailsPresenter {
@ -88,6 +81,12 @@ class ExtensionDetailsController(bundle: Bundle? = null) :
override fun onViewCreated(view: View) { override fun onViewCreated(view: View) {
super.onViewCreated(view) super.onViewCreated(view)
binding.extensionPrefsRecycler.applyInsetter {
type(navigationBars = true) {
padding()
}
}
val extension = presenter.extension ?: return val extension = presenter.extension ?: return
val context = view.context val context = view.context

View File

@ -6,7 +6,6 @@ import android.os.Bundle
import android.util.TypedValue import android.util.TypedValue
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup
import androidx.appcompat.view.ContextThemeWrapper import androidx.appcompat.view.ContextThemeWrapper
import androidx.core.os.bundleOf import androidx.core.os.bundleOf
import androidx.preference.DialogPreference import androidx.preference.DialogPreference
@ -45,10 +44,9 @@ class SourcePreferencesController(bundle: Bundle? = null) :
bundleOf(SOURCE_ID to sourceId) bundleOf(SOURCE_ID to sourceId)
) )
override fun inflateView(inflater: LayoutInflater, container: ViewGroup): View { override fun createBinding(inflater: LayoutInflater): SourcePreferencesControllerBinding {
val themedInflater = inflater.cloneInContext(getPreferenceThemeContext()) val themedInflater = inflater.cloneInContext(getPreferenceThemeContext())
binding = SourcePreferencesControllerBinding.inflate(themedInflater) return SourcePreferencesControllerBinding.inflate(themedInflater)
return binding.root
} }
override fun createPresenter(): SourcePreferencesPresenter { override fun createPresenter(): SourcePreferencesPresenter {

View File

@ -3,9 +3,9 @@ package eu.kanade.tachiyomi.ui.browse.migration.manga
import android.os.Bundle import android.os.Bundle
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup
import androidx.core.os.bundleOf import androidx.core.os.bundleOf
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import dev.chrisbanes.insetter.applyInsetter
import eu.davidea.flexibleadapter.FlexibleAdapter import eu.davidea.flexibleadapter.FlexibleAdapter
import eu.kanade.tachiyomi.databinding.MigrationMangaControllerBinding import eu.kanade.tachiyomi.databinding.MigrationMangaControllerBinding
import eu.kanade.tachiyomi.ui.base.controller.NucleusController import eu.kanade.tachiyomi.ui.base.controller.NucleusController
@ -44,14 +44,17 @@ class MigrationMangaController :
return MigrationMangaPresenter(sourceId) return MigrationMangaPresenter(sourceId)
} }
override fun inflateView(inflater: LayoutInflater, container: ViewGroup): View { override fun createBinding(inflater: LayoutInflater) = MigrationMangaControllerBinding.inflate(inflater)
binding = MigrationMangaControllerBinding.inflate(inflater)
return binding.root
}
override fun onViewCreated(view: View) { override fun onViewCreated(view: View) {
super.onViewCreated(view) super.onViewCreated(view)
binding.recycler.applyInsetter {
type(navigationBars = true) {
padding()
}
}
adapter = MigrationMangaAdapter(this) adapter = MigrationMangaAdapter(this)
binding.recycler.layoutManager = LinearLayoutManager(view.context) binding.recycler.layoutManager = LinearLayoutManager(view.context)
binding.recycler.adapter = adapter binding.recycler.adapter = adapter

View File

@ -104,24 +104,22 @@ class SearchPresenter(
val prevMangaChapters = db.getChapters(prevManga).executeAsBlocking() val prevMangaChapters = db.getChapters(prevManga).executeAsBlocking()
val maxChapterRead = prevMangaChapters val maxChapterRead = prevMangaChapters
.filter { it.read } .filter { it.read }
.maxByOrNull { it.chapter_number }?.chapter_number .maxOfOrNull { it.chapter_number } ?: 0f
val bookmarkedChapters = prevMangaChapters val dbChapters = db.getChapters(manga).executeAsBlocking()
.filter { it.bookmark && it.isRecognizedNumber } for (chapter in dbChapters) {
.map { it.chapter_number } if (chapter.isRecognizedNumber) {
if (maxChapterRead != null) { val prevChapter = prevMangaChapters
val dbChapters = db.getChapters(manga).executeAsBlocking() .find { it.isRecognizedNumber && it.chapter_number == chapter.chapter_number }
for (chapter in dbChapters) { if (prevChapter != null) {
if (chapter.isRecognizedNumber) { chapter.date_fetch = prevChapter.date_fetch
if (chapter.chapter_number <= maxChapterRead) { chapter.bookmark = prevChapter.bookmark
chapter.read = true }
} if (chapter.chapter_number <= maxChapterRead) {
if (chapter.chapter_number in bookmarkedChapters) { chapter.read = true
chapter.bookmark = true
}
} }
} }
db.insertChapters(dbChapters).executeAsBlocking()
} }
db.insertChapters(dbChapters).executeAsBlocking()
} }
// Update categories // Update categories

View File

@ -5,7 +5,6 @@ import android.view.Menu
import android.view.MenuInflater import android.view.MenuInflater
import android.view.MenuItem import android.view.MenuItem
import android.view.View import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import dev.chrisbanes.insetter.applyInsetter import dev.chrisbanes.insetter.applyInsetter
import eu.davidea.flexibleadapter.FlexibleAdapter import eu.davidea.flexibleadapter.FlexibleAdapter
@ -30,18 +29,16 @@ class MigrationSourcesController :
return MigrationSourcesPresenter() return MigrationSourcesPresenter()
} }
override fun inflateView(inflater: LayoutInflater, container: ViewGroup): View { override fun createBinding(inflater: LayoutInflater) = MigrationSourcesControllerBinding.inflate(inflater)
binding = MigrationSourcesControllerBinding.inflate(inflater)
override fun onViewCreated(view: View) {
super.onViewCreated(view)
binding.recycler.applyInsetter { binding.recycler.applyInsetter {
type(navigationBars = true) { type(navigationBars = true) {
padding() padding()
} }
} }
return binding.root
}
override fun onViewCreated(view: View) {
super.onViewCreated(view)
adapter = SourceAdapter(this) adapter = SourceAdapter(this)
binding.recycler.layoutManager = LinearLayoutManager(view.context) binding.recycler.layoutManager = LinearLayoutManager(view.context)

View File

@ -7,7 +7,7 @@ import eu.davidea.flexibleadapter.items.AbstractHeaderItem
import eu.davidea.flexibleadapter.items.IFlexible import eu.davidea.flexibleadapter.items.IFlexible
import eu.davidea.viewholders.FlexibleViewHolder import eu.davidea.viewholders.FlexibleViewHolder
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.databinding.SourceMainControllerCardHeaderBinding import eu.kanade.tachiyomi.databinding.SectionHeaderItemBinding
/** /**
* Item that contains the selection header. * Item that contains the selection header.
@ -18,7 +18,7 @@ class SelectionHeader : AbstractHeaderItem<SelectionHeader.Holder>() {
* Returns the layout resource of this item. * Returns the layout resource of this item.
*/ */
override fun getLayoutRes(): Int { override fun getLayoutRes(): Int {
return R.layout.source_main_controller_card_header return R.layout.section_header_item
} }
/** /**
@ -45,7 +45,7 @@ class SelectionHeader : AbstractHeaderItem<SelectionHeader.Holder>() {
class Holder(view: View, adapter: FlexibleAdapter<*>) : FlexibleViewHolder(view, adapter) { class Holder(view: View, adapter: FlexibleAdapter<*>) : FlexibleViewHolder(view, adapter) {
private val binding = SourceMainControllerCardHeaderBinding.bind(view) private val binding = SectionHeaderItemBinding.bind(view)
init { init {
binding.title.text = view.context.getString(R.string.migration_selection_prompt) binding.title.text = view.context.getString(R.string.migration_selection_prompt)

View File

@ -3,13 +3,13 @@ package eu.kanade.tachiyomi.ui.browse.source
import android.view.View import android.view.View
import eu.davidea.flexibleadapter.FlexibleAdapter import eu.davidea.flexibleadapter.FlexibleAdapter
import eu.davidea.viewholders.FlexibleViewHolder import eu.davidea.viewholders.FlexibleViewHolder
import eu.kanade.tachiyomi.databinding.SourceMainControllerCardHeaderBinding import eu.kanade.tachiyomi.databinding.SectionHeaderItemBinding
import eu.kanade.tachiyomi.util.system.LocaleHelper import eu.kanade.tachiyomi.util.system.LocaleHelper
class LangHolder(view: View, adapter: FlexibleAdapter<*>) : class LangHolder(view: View, adapter: FlexibleAdapter<*>) :
FlexibleViewHolder(view, adapter) { FlexibleViewHolder(view, adapter) {
private val binding = SourceMainControllerCardHeaderBinding.bind(view) private val binding = SectionHeaderItemBinding.bind(view)
fun bind(item: LangItem) { fun bind(item: LangItem) {
binding.title.text = LocaleHelper.getSourceDisplayName(item.code, itemView.context) binding.title.text = LocaleHelper.getSourceDisplayName(item.code, itemView.context)

View File

@ -18,7 +18,7 @@ data class LangItem(val code: String) : AbstractHeaderItem<LangHolder>() {
* Returns the layout resource of this item. * Returns the layout resource of this item.
*/ */
override fun getLayoutRes(): Int { override fun getLayoutRes(): Int {
return R.layout.source_main_controller_card_header return R.layout.section_header_item
} }
/** /**

View File

@ -8,7 +8,6 @@ import android.view.Menu
import android.view.MenuInflater import android.view.MenuInflater
import android.view.MenuItem import android.view.MenuItem
import android.view.View import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import com.afollestad.materialdialogs.MaterialDialog import com.afollestad.materialdialogs.MaterialDialog
import com.afollestad.materialdialogs.list.listItems import com.afollestad.materialdialogs.list.listItems
@ -67,25 +66,16 @@ class SourceController :
return SourcePresenter() return SourcePresenter()
} }
/** override fun createBinding(inflater: LayoutInflater) = SourceMainControllerBinding.inflate(inflater)
* Initiate the view with [R.layout.source_main_controller].
* override fun onViewCreated(view: View) {
* @param inflater used to load the layout xml. super.onViewCreated(view)
* @param container containing parent views.
* @return inflated view.
*/
override fun inflateView(inflater: LayoutInflater, container: ViewGroup): View {
binding = SourceMainControllerBinding.inflate(inflater)
binding.recycler.applyInsetter { binding.recycler.applyInsetter {
type(navigationBars = true) { type(navigationBars = true) {
padding() padding()
} }
} }
return binding.root
}
override fun onViewCreated(view: View) {
super.onViewCreated(view)
adapter = SourceAdapter(this) adapter = SourceAdapter(this)

View File

@ -8,7 +8,6 @@ import eu.kanade.tachiyomi.databinding.SourceMainControllerCardItemBinding
import eu.kanade.tachiyomi.source.LocalSource import eu.kanade.tachiyomi.source.LocalSource
import eu.kanade.tachiyomi.source.icon import eu.kanade.tachiyomi.source.icon
import eu.kanade.tachiyomi.util.system.LocaleHelper import eu.kanade.tachiyomi.util.system.LocaleHelper
import eu.kanade.tachiyomi.util.system.getResourceColor
import eu.kanade.tachiyomi.util.view.setVectorCompat import eu.kanade.tachiyomi.util.view.setVectorCompat
class SourceHolder(private val view: View, val adapter: SourceAdapter) : class SourceHolder(private val view: View, val adapter: SourceAdapter) :
@ -46,9 +45,9 @@ class SourceHolder(private val view: View, val adapter: SourceAdapter) :
binding.pin.isVisible = true binding.pin.isVisible = true
if (item.isPinned) { if (item.isPinned) {
binding.pin.setVectorCompat(R.drawable.ic_push_pin_24dp, view.context.getResourceColor(R.attr.colorAccent)) binding.pin.setVectorCompat(R.drawable.ic_push_pin_24dp, R.attr.colorAccent)
} else { } else {
binding.pin.setVectorCompat(R.drawable.ic_push_pin_outline_24dp, view.context.getResourceColor(android.R.attr.textColorHint)) binding.pin.setVectorCompat(R.drawable.ic_push_pin_outline_24dp, android.R.attr.textColorHint)
} }
} }
} }

View File

@ -124,10 +124,7 @@ open class BrowseSourceController(bundle: Bundle) :
return BrowseSourcePresenter(args.getLong(SOURCE_ID_KEY), args.getString(SEARCH_QUERY_KEY)) return BrowseSourcePresenter(args.getLong(SOURCE_ID_KEY), args.getString(SEARCH_QUERY_KEY))
} }
override fun inflateView(inflater: LayoutInflater, container: ViewGroup): View { override fun createBinding(inflater: LayoutInflater) = SourceControllerBinding.inflate(inflater)
binding = SourceControllerBinding.inflate(inflater)
return binding.root
}
override fun onViewCreated(view: View) { override fun onViewCreated(view: View) {
super.onViewCreated(view) super.onViewCreated(view)
@ -269,6 +266,7 @@ open class BrowseSourceController(bundle: Bundle) :
if (router.backstackSize >= 2 && router.backstack[router.backstackSize - 2].controller() is GlobalSearchController) { if (router.backstackSize >= 2 && router.backstack[router.backstackSize - 2].controller() is GlobalSearchController) {
router.popController(this) router.popController(this)
} else { } else {
nonSubmittedQuery = ""
searchWithQuery("") searchWithQuery("")
} }

View File

@ -5,6 +5,7 @@ import android.content.Context
import android.util.AttributeSet import android.util.AttributeSet
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.ViewGroup import android.view.ViewGroup
import com.google.android.material.bottomsheet.BottomSheetBehavior
import eu.davidea.flexibleadapter.FlexibleAdapter import eu.davidea.flexibleadapter.FlexibleAdapter
import eu.davidea.flexibleadapter.items.IFlexible import eu.davidea.flexibleadapter.items.IFlexible
import eu.kanade.tachiyomi.databinding.SourceFilterSheetBinding import eu.kanade.tachiyomi.databinding.SourceFilterSheetBinding
@ -17,10 +18,10 @@ class SourceFilterSheet(
onResetClicked: () -> Unit onResetClicked: () -> Unit
) : BaseBottomSheetDialog(activity) { ) : BaseBottomSheetDialog(activity) {
private var filterNavView: FilterNavigationView private var filterNavView: FilterNavigationView = FilterNavigationView(activity)
private val sheetBehavior: BottomSheetBehavior<*>
init { init {
filterNavView = FilterNavigationView(activity)
filterNavView.onFilterClicked = { filterNavView.onFilterClicked = {
onFilterClicked() onFilterClicked()
this.dismiss() this.dismiss()
@ -28,13 +29,23 @@ class SourceFilterSheet(
filterNavView.onResetClicked = onResetClicked filterNavView.onResetClicked = onResetClicked
setContentView(filterNavView) setContentView(filterNavView)
sheetBehavior = BottomSheetBehavior.from(filterNavView.parent as ViewGroup)
}
override fun show() {
super.show()
sheetBehavior.state = BottomSheetBehavior.STATE_EXPANDED
} }
fun setFilters(items: List<IFlexible<*>>) { fun setFilters(items: List<IFlexible<*>>) {
filterNavView.adapter.updateDataSet(items) filterNavView.adapter.updateDataSet(items)
} }
class FilterNavigationView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null) : class FilterNavigationView @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null
) :
SimpleNavigationView(context, attrs) { SimpleNavigationView(context, attrs) {
var onFilterClicked = {} var onFilterClicked = {}
@ -42,9 +53,12 @@ class SourceFilterSheet(
val adapter: FlexibleAdapter<IFlexible<*>> = FlexibleAdapter<IFlexible<*>>(null) val adapter: FlexibleAdapter<IFlexible<*>> = FlexibleAdapter<IFlexible<*>>(null)
.setDisplayHeadersAtStartUp(true) .setDisplayHeadersAtStartUp(true)
.setStickyHeaders(true)
private val binding = SourceFilterSheetBinding.inflate(LayoutInflater.from(context), null, false) private val binding = SourceFilterSheetBinding.inflate(
LayoutInflater.from(context),
null,
false
)
init { init {
recycler.adapter = adapter recycler.adapter = adapter

View File

@ -6,7 +6,6 @@ import android.view.Menu
import android.view.MenuInflater import android.view.MenuInflater
import android.view.MenuItem import android.view.MenuItem
import android.view.View import android.view.View
import android.view.ViewGroup
import androidx.appcompat.widget.SearchView import androidx.appcompat.widget.SearchView
import androidx.core.view.isVisible import androidx.core.view.isVisible
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
@ -50,22 +49,7 @@ open class GlobalSearchController(
setHasOptionsMenu(true) setHasOptionsMenu(true)
} }
/** override fun createBinding(inflater: LayoutInflater) = GlobalSearchControllerBinding.inflate(inflater)
* Initiate the view with [R.layout.global_search_controller].
*
* @param inflater used to load the layout xml.
* @param container containing parent views.
* @return inflated view
*/
override fun inflateView(inflater: LayoutInflater, container: ViewGroup): View {
binding = GlobalSearchControllerBinding.inflate(inflater)
binding.recycler.applyInsetter {
type(navigationBars = true) {
padding()
}
}
return binding.root
}
override fun getTitle(): String? { override fun getTitle(): String? {
return presenter.query return presenter.query
@ -142,6 +126,12 @@ open class GlobalSearchController(
override fun onViewCreated(view: View) { override fun onViewCreated(view: View) {
super.onViewCreated(view) super.onViewCreated(view)
binding.recycler.applyInsetter {
type(navigationBars = true) {
padding()
}
}
adapter = GlobalSearchAdapter(this) adapter = GlobalSearchAdapter(this)
// Create recycler and set adapter. // Create recycler and set adapter.

View File

@ -4,7 +4,6 @@ import android.view.LayoutInflater
import android.view.Menu import android.view.Menu
import android.view.MenuItem import android.view.MenuItem
import android.view.View import android.view.View
import android.view.ViewGroup
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.view.ActionMode import androidx.appcompat.view.ActionMode
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
@ -68,21 +67,7 @@ class CategoryController :
return resources?.getString(R.string.action_edit_categories) return resources?.getString(R.string.action_edit_categories)
} }
/** override fun createBinding(inflater: LayoutInflater) = CategoriesControllerBinding.inflate(inflater)
* Returns the view of this controller.
*
* @param inflater The layout inflater to create the view from XML.
* @param container The parent view for this one.
*/
override fun inflateView(inflater: LayoutInflater, container: ViewGroup): View {
binding = CategoriesControllerBinding.inflate(inflater)
binding.recycler.applyInsetter {
type(navigationBars = true) {
padding()
}
}
return binding.root
}
/** /**
* Called after view inflation. Used to initialize the view. * Called after view inflation. Used to initialize the view.
@ -92,6 +77,12 @@ class CategoryController :
override fun onViewCreated(view: View) { override fun onViewCreated(view: View) {
super.onViewCreated(view) super.onViewCreated(view)
binding.recycler.applyInsetter {
type(navigationBars = true) {
padding()
}
}
adapter = CategoryAdapter(this@CategoryController) adapter = CategoryAdapter(this@CategoryController)
binding.recycler.layoutManager = LinearLayoutManager(view.context) binding.recycler.layoutManager = LinearLayoutManager(view.context)
binding.recycler.setHasFixedSize(true) binding.recycler.setHasFixedSize(true)

View File

@ -5,7 +5,6 @@ import android.view.Menu
import android.view.MenuInflater import android.view.MenuInflater
import android.view.MenuItem import android.view.MenuItem
import android.view.View import android.view.View
import android.view.ViewGroup
import androidx.core.view.isVisible import androidx.core.view.isVisible
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
@ -55,15 +54,7 @@ class DownloadController :
setHasOptionsMenu(true) setHasOptionsMenu(true)
} }
override fun inflateView(inflater: LayoutInflater, container: ViewGroup): View { override fun createBinding(inflater: LayoutInflater) = DownloadControllerBinding.inflate(inflater)
binding = DownloadControllerBinding.inflate(inflater)
binding.recycler.applyInsetter {
type(navigationBars = true) {
padding()
}
}
return binding.root
}
override fun createPresenter(): DownloadPresenter { override fun createPresenter(): DownloadPresenter {
return DownloadPresenter() return DownloadPresenter()
@ -76,6 +67,12 @@ class DownloadController :
override fun onViewCreated(view: View) { override fun onViewCreated(view: View) {
super.onViewCreated(view) super.onViewCreated(view)
binding.recycler.applyInsetter {
type(navigationBars = true) {
padding()
}
}
// Check if download queue is empty and update information accordingly. // Check if download queue is empty and update information accordingly.
setInformationView() setInformationView()

View File

@ -7,7 +7,6 @@ import android.view.Menu
import android.view.MenuInflater import android.view.MenuInflater
import android.view.MenuItem import android.view.MenuItem
import android.view.View import android.view.View
import android.view.ViewGroup
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.view.ActionMode import androidx.appcompat.view.ActionMode
import androidx.core.graphics.drawable.DrawableCompat import androidx.core.graphics.drawable.DrawableCompat
@ -18,6 +17,7 @@ import com.google.android.material.tabs.TabLayout
import com.jakewharton.rxrelay.BehaviorRelay import com.jakewharton.rxrelay.BehaviorRelay
import com.jakewharton.rxrelay.PublishRelay import com.jakewharton.rxrelay.PublishRelay
import com.tfcporciuncula.flow.Preference import com.tfcporciuncula.flow.Preference
import dev.chrisbanes.insetter.applyInsetter
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.database.models.Category import eu.kanade.tachiyomi.data.database.models.Category
import eu.kanade.tachiyomi.data.database.models.Manga import eu.kanade.tachiyomi.data.database.models.Manga
@ -163,14 +163,17 @@ class LibraryController(
return LibraryPresenter() return LibraryPresenter()
} }
override fun inflateView(inflater: LayoutInflater, container: ViewGroup): View { override fun createBinding(inflater: LayoutInflater) = LibraryControllerBinding.inflate(inflater)
binding = LibraryControllerBinding.inflate(inflater)
return binding.root
}
override fun onViewCreated(view: View) { override fun onViewCreated(view: View) {
super.onViewCreated(view) super.onViewCreated(view)
binding.actionToolbar.applyInsetter {
type(navigationBars = true) {
margin(bottom = true)
}
}
adapter = LibraryAdapter(this) adapter = LibraryAdapter(this)
binding.libraryPager.adapter = adapter binding.libraryPager.adapter = adapter
binding.libraryPager.pageSelections() binding.libraryPager.pageSelections()

View File

@ -13,6 +13,7 @@ import androidx.core.view.ViewCompat
import androidx.core.view.WindowCompat import androidx.core.view.WindowCompat
import androidx.core.view.WindowInsetsCompat import androidx.core.view.WindowInsetsCompat
import androidx.core.view.isVisible import androidx.core.view.isVisible
import androidx.core.view.marginTop
import androidx.core.view.updateLayoutParams import androidx.core.view.updateLayoutParams
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import androidx.preference.PreferenceDialogController import androidx.preference.PreferenceDialogController
@ -49,6 +50,8 @@ import eu.kanade.tachiyomi.ui.recent.history.HistoryController
import eu.kanade.tachiyomi.ui.recent.updates.UpdatesController import eu.kanade.tachiyomi.ui.recent.updates.UpdatesController
import eu.kanade.tachiyomi.util.lang.launchIO import eu.kanade.tachiyomi.util.lang.launchIO
import eu.kanade.tachiyomi.util.lang.launchUI import eu.kanade.tachiyomi.util.lang.launchUI
import eu.kanade.tachiyomi.util.system.InternalResourceHelper
import eu.kanade.tachiyomi.util.system.getResourceColor
import eu.kanade.tachiyomi.util.system.toast import eu.kanade.tachiyomi.util.system.toast
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.launchIn
@ -98,23 +101,27 @@ class MainActivity : BaseViewBindingActivity<MainActivityBinding>() {
padding(left = true, top = true, right = true) padding(left = true, top = true, right = true)
} }
} }
binding.bottomNav.applyInsetter {
type(navigationBars = true) {
padding()
}
}
binding.rootFab.applyInsetter { binding.rootFab.applyInsetter {
type(navigationBars = true) { type(navigationBars = true) {
margin() margin()
} }
} }
binding.bottomNav.applyInsetter {
type(navigationBars = true) {
padding()
}
}
// Make sure navigation bar is on bottom when making it transparent // Make sure navigation bar is on bottom before we modify it
ViewCompat.setOnApplyWindowInsetsListener(binding.root) { _, insets -> ViewCompat.setOnApplyWindowInsetsListener(binding.root) { _, insets ->
if (insets.getInsets(WindowInsetsCompat.Type.navigationBars()).bottom > 0) { if (insets.getInsets(WindowInsetsCompat.Type.navigationBars()).bottom > 0) {
// Keep scrim on light theme if windowLightNavigationBar is not available window.navigationBarColor = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q &&
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1 || isDarkMode) { !InternalResourceHelper.getBoolean(this, "config_navBarNeedsScrim", true)
window.navigationBarColor = Color.TRANSPARENT ) {
Color.TRANSPARENT
} else {
// Set navbar scrim 70% of navigationBarColor
getResourceColor(android.R.attr.navigationBarColor, 0.7F)
} }
} }
insets insets
@ -146,6 +153,9 @@ class MainActivity : BaseViewBindingActivity<MainActivityBinding>() {
val controller = router.getControllerWithTag(id.toString()) as? LibraryController val controller = router.getControllerWithTag(id.toString()) as? LibraryController
controller?.showSettingsSheet() controller?.showSettingsSheet()
} }
R.id.nav_updates -> {
router.pushController(DownloadController().withFadeTransaction())
}
} }
} }
true true
@ -439,7 +449,7 @@ class MainActivity : BaseViewBindingActivity<MainActivityBinding>() {
fun fixViewToBottom(view: View) { fun fixViewToBottom(view: View) {
val listener = AppBarLayout.OnOffsetChangedListener { appBarLayout, verticalOffset -> val listener = AppBarLayout.OnOffsetChangedListener { appBarLayout, verticalOffset ->
val maxAbsOffset = appBarLayout.measuredHeight - binding.tabs.measuredHeight val maxAbsOffset = appBarLayout.measuredHeight - binding.tabs.measuredHeight
view.translationY = -maxAbsOffset - verticalOffset.toFloat() view.translationY = -maxAbsOffset - verticalOffset.toFloat() + appBarLayout.marginTop
} }
binding.appbar.addOnOffsetChangedListener(listener) binding.appbar.addOnOffsetChangedListener(listener)
fixedViewsToBottom[view] = listener fixedViewsToBottom[view] = listener

View File

@ -11,7 +11,6 @@ import android.view.Menu
import android.view.MenuInflater import android.view.MenuInflater
import android.view.MenuItem import android.view.MenuItem
import android.view.View import android.view.View
import android.view.ViewGroup
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.view.ActionMode import androidx.appcompat.view.ActionMode
import androidx.core.graphics.blue import androidx.core.graphics.blue
@ -199,18 +198,21 @@ class MangaController :
) )
} }
override fun inflateView(inflater: LayoutInflater, container: ViewGroup): View { override fun createBinding(inflater: LayoutInflater) = MangaControllerBinding.inflate(inflater)
binding = MangaControllerBinding.inflate(inflater)
override fun onViewCreated(view: View) {
super.onViewCreated(view)
binding.recycler.applyInsetter { binding.recycler.applyInsetter {
type(navigationBars = true) { type(navigationBars = true) {
padding() padding()
} }
} }
return binding.root binding.actionToolbar.applyInsetter {
} type(navigationBars = true) {
margin(bottom = true)
override fun onViewCreated(view: View) { }
super.onViewCreated(view) }
if (manga == null || source == null) return if (manga == null || source == null) return
@ -850,7 +852,6 @@ class MangaController :
binding.actionToolbar.findItem(R.id.action_mark_as_unread)?.isVisible = chapters.all { it.chapter.read } binding.actionToolbar.findItem(R.id.action_mark_as_unread)?.isVisible = chapters.all { it.chapter.read }
// Hide FAB to avoid interfering with the bottom action toolbar // Hide FAB to avoid interfering with the bottom action toolbar
// actionFab?.hide()
actionFab?.isVisible = false actionFab?.isVisible = false
} }
return false return false
@ -882,10 +883,6 @@ class MangaController :
chaptersAdapter?.clearSelection() chaptersAdapter?.clearSelection()
selectedChapters.clear() selectedChapters.clear()
actionMode = null actionMode = null
// TODO: there seems to be a bug in MaterialComponents where the [ExtendedFloatingActionButton]
// fails to show up properly
// actionFab?.show()
actionFab?.isVisible = true actionFab?.isVisible = true
} }
@ -1008,9 +1005,10 @@ class MangaController :
// OVERFLOW MENU DIALOGS // OVERFLOW MENU DIALOGS
private fun getUnreadChaptersSorted() = presenter.chapters private fun getUnreadChaptersSorted() = presenter.chapters
.sortedWith(presenter.getChapterSort())
.filter { !it.read && it.status == Download.State.NOT_DOWNLOADED } .filter { !it.read && it.status == Download.State.NOT_DOWNLOADED }
.distinctBy { it.name } .distinctBy { it.name }
.sortedByDescending { it.source_order } .reversed()
private fun downloadChapters(choice: Int) { private fun downloadChapters(choice: Int) {
val chaptersToDownload = when (choice) { val chaptersToDownload = when (choice) {

View File

@ -429,7 +429,11 @@ class MangaPresenter(
observable = observable.filter { !it.bookmark } observable = observable.filter { !it.bookmark }
} }
val sortFunction: (Chapter, Chapter) -> Int = when (manga.sorting) { return observable.toSortedList(getChapterSort())
}
fun getChapterSort(): (Chapter, Chapter) -> Int {
return when (manga.sorting) {
Manga.SORTING_SOURCE -> when (sortDescending()) { Manga.SORTING_SOURCE -> when (sortDescending()) {
true -> { c1, c2 -> c1.source_order.compareTo(c2.source_order) } true -> { c1, c2 -> c1.source_order.compareTo(c2.source_order) }
false -> { c1, c2 -> c2.source_order.compareTo(c1.source_order) } false -> { c1, c2 -> c2.source_order.compareTo(c1.source_order) }
@ -444,8 +448,6 @@ class MangaPresenter(
} }
else -> throw NotImplementedError("Unimplemented sorting method") else -> throw NotImplementedError("Unimplemented sorting method")
} }
return observable.toSortedList(sortFunction)
} }
/** /**
@ -472,7 +474,7 @@ class MangaPresenter(
* Returns the next unread chapter or null if everything is read. * Returns the next unread chapter or null if everything is read.
*/ */
fun getNextUnreadChapter(): ChapterItem? { fun getNextUnreadChapter(): ChapterItem? {
return chapters.sortedByDescending { it.source_order }.find { !it.read } return chapters.sortedWith(getChapterSort()).findLast { !it.read }
} }
/** /**

View File

@ -1,9 +1,9 @@
package eu.kanade.tachiyomi.ui.manga.chapter package eu.kanade.tachiyomi.ui.manga.chapter
import android.text.SpannableString
import android.text.SpannableStringBuilder import android.text.SpannableStringBuilder
import android.text.style.ForegroundColorSpan
import android.view.View import android.view.View
import androidx.core.text.buildSpannedString
import androidx.core.text.color
import androidx.core.view.isVisible import androidx.core.view.isVisible
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.database.models.Manga import eu.kanade.tachiyomi.data.database.models.Manga
@ -59,8 +59,10 @@ class ChapterHolder(
descriptions.add(adapter.dateFormat.format(Date(chapter.date_upload))) descriptions.add(adapter.dateFormat.format(Date(chapter.date_upload)))
} }
if (!chapter.read && chapter.last_page_read > 0) { if (!chapter.read && chapter.last_page_read > 0) {
val lastPageRead = SpannableString(itemView.context.getString(R.string.chapter_progress, chapter.last_page_read + 1)).apply { val lastPageRead = buildSpannedString {
setSpan(ForegroundColorSpan(adapter.readColor), 0, length, SpannableString.SPAN_EXCLUSIVE_EXCLUSIVE) color(adapter.readColor) {
append(itemView.context.getString(R.string.chapter_progress, chapter.last_page_read + 1))
}
} }
descriptions.add(lastPageRead) descriptions.add(lastPageRead)
} }

View File

@ -1,7 +1,6 @@
package eu.kanade.tachiyomi.ui.more package eu.kanade.tachiyomi.ui.more
import android.app.Dialog import android.app.Dialog
import android.os.Build
import android.os.Bundle import android.os.Bundle
import androidx.core.os.bundleOf import androidx.core.os.bundleOf
import androidx.preference.PreferenceScreen import androidx.preference.PreferenceScreen
@ -15,6 +14,7 @@ import eu.kanade.tachiyomi.data.updater.github.GithubUpdateChecker
import eu.kanade.tachiyomi.ui.base.controller.DialogController import eu.kanade.tachiyomi.ui.base.controller.DialogController
import eu.kanade.tachiyomi.ui.base.controller.openInBrowser import eu.kanade.tachiyomi.ui.base.controller.openInBrowser
import eu.kanade.tachiyomi.ui.setting.SettingsController import eu.kanade.tachiyomi.ui.setting.SettingsController
import eu.kanade.tachiyomi.util.CrashLogUtil
import eu.kanade.tachiyomi.util.lang.launchNow import eu.kanade.tachiyomi.util.lang.launchNow
import eu.kanade.tachiyomi.util.lang.toDateTimestampString import eu.kanade.tachiyomi.util.lang.toDateTimestampString
import eu.kanade.tachiyomi.util.preference.onClick import eu.kanade.tachiyomi.util.preference.onClick
@ -138,6 +138,7 @@ class AboutController : SettingsController() {
.withAboutIconShown(false) .withAboutIconShown(false)
.withAboutVersionShown(false) .withAboutVersionShown(false)
.withLicenseShown(true) .withLicenseShown(true)
.withEdgeToEdge(true)
.start(activity!!) .start(activity!!)
} }
} }
@ -201,19 +202,10 @@ class AboutController : SettingsController() {
} }
private fun copyDebugInfo() { private fun copyDebugInfo() {
val deviceInfo = activity?.let {
""" val deviceInfo = CrashLogUtil(it).getDebugInfo()
App version: ${BuildConfig.VERSION_NAME} (${BuildConfig.FLAVOR}, ${BuildConfig.COMMIT_SHA}, ${BuildConfig.VERSION_CODE}) activity?.copyToClipboard("Debug information", deviceInfo)
Android version: ${Build.VERSION.RELEASE} (SDK ${Build.VERSION.SDK_INT}) }
Android build ID: ${Build.DISPLAY}
Device brand: ${Build.BRAND}
Device manufacturer: ${Build.MANUFACTURER}
Device name: ${Build.DEVICE}
Device model: ${Build.MODEL}
Device product name: ${Build.PRODUCT}
""".trimIndent()
activity?.copyToClipboard("Debug information", deviceInfo)
} }
private fun getFormattedBuildTime(): String { private fun getFormattedBuildTime(): String {

View File

@ -1,7 +1,11 @@
package eu.kanade.tachiyomi.ui.more package eu.kanade.tachiyomi.ui.more
import android.content.Context import android.content.Context
import android.os.Bundle
import android.util.AttributeSet import android.util.AttributeSet
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.preference.Preference import androidx.preference.Preference
import androidx.preference.PreferenceScreen import androidx.preference.PreferenceScreen
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
@ -26,7 +30,10 @@ import eu.kanade.tachiyomi.util.preference.switchPreference
import eu.kanade.tachiyomi.util.preference.titleRes import eu.kanade.tachiyomi.util.preference.titleRes
import eu.kanade.tachiyomi.util.system.getResourceColor import eu.kanade.tachiyomi.util.system.getResourceColor
import eu.kanade.tachiyomi.util.system.openInBrowser import eu.kanade.tachiyomi.util.system.openInBrowser
import rx.Observable
import rx.Subscription
import rx.android.schedulers.AndroidSchedulers import rx.android.schedulers.AndroidSchedulers
import rx.subscriptions.CompositeSubscription
import uy.kohesive.injekt.injectLazy import uy.kohesive.injekt.injectLazy
import eu.kanade.tachiyomi.data.preference.PreferenceKeys as Keys import eu.kanade.tachiyomi.data.preference.PreferenceKeys as Keys
@ -39,6 +46,9 @@ class MoreController :
private var isDownloading: Boolean = false private var isDownloading: Boolean = false
private var downloadQueueSize: Int = 0 private var downloadQueueSize: Int = 0
private var untilDestroySubscriptions = CompositeSubscription()
private set
override fun setupPreferenceScreen(screen: PreferenceScreen) = screen.apply { override fun setupPreferenceScreen(screen: PreferenceScreen) = screen.apply {
titleRes = R.string.label_more titleRes = R.string.label_more
@ -115,6 +125,19 @@ class MoreController :
} }
} }
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup, savedInstanceState: Bundle?): View {
if (untilDestroySubscriptions.isUnsubscribed) {
untilDestroySubscriptions = CompositeSubscription()
}
return super.onCreateView(inflater, container, savedInstanceState)
}
override fun onDestroyView(view: View) {
super.onDestroyView(view)
untilDestroySubscriptions.unsubscribe()
}
private fun initDownloadQueueSummary(preference: Preference) { private fun initDownloadQueueSummary(preference: Preference) {
// Handle running/paused status change // Handle running/paused status change
DownloadService.runningRelay DownloadService.runningRelay
@ -141,6 +164,10 @@ class MoreController :
} }
} }
private fun <T> Observable<T>.subscribeUntilDestroy(onNext: (T) -> Unit): Subscription {
return subscribe(onNext).also { untilDestroySubscriptions.add(it) }
}
private class MoreHeaderPreference @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null) : private class MoreHeaderPreference @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null) :
Preference(context, attrs) { Preference(context, attrs) {

View File

@ -10,7 +10,6 @@ import android.graphics.Bitmap
import android.graphics.Color import android.graphics.Color
import android.os.Build import android.os.Build
import android.os.Bundle import android.os.Bundle
import android.view.Gravity
import android.view.KeyEvent import android.view.KeyEvent
import android.view.Menu import android.view.Menu
import android.view.MenuItem import android.view.MenuItem
@ -414,6 +413,11 @@ class ReaderActivity : BaseRxActivity<ReaderActivityBinding, ReaderPresenter>()
setOnClickListener { setOnClickListener {
ReaderSettingsSheet(this@ReaderActivity).show() ReaderSettingsSheet(this@ReaderActivity).show()
} }
setOnLongClickListener {
ReaderSettingsSheet(this@ReaderActivity, showColorFilterSettings = true).show()
true
}
} }
} }
@ -554,10 +558,12 @@ class ReaderActivity : BaseRxActivity<ReaderActivityBinding, ReaderPresenter>()
} }
private fun showReadingModeToast(mode: Int) { private fun showReadingModeToast(mode: Int) {
val strings = resources.getStringArray(R.array.viewers_selector) try {
readingModeToast?.cancel() val strings = resources.getStringArray(R.array.viewers_selector)
readingModeToast = toast(strings[mode]) { readingModeToast?.cancel()
it.setGravity(Gravity.CENTER_VERTICAL or Gravity.CENTER_HORIZONTAL, 0, 0) readingModeToast = toast(strings[mode])
} catch (e: ArrayIndexOutOfBoundsException) {
Timber.e("Unknown reading mode: $mode")
} }
} }

View File

@ -85,8 +85,7 @@ class HttpPageLoader(
* the local cache, otherwise fallbacks to network. * the local cache, otherwise fallbacks to network.
*/ */
override fun getPages(): Observable<List<ReaderPage>> { override fun getPages(): Observable<List<ReaderPage>> {
return chapterCache return Observable.fromCallable { chapterCache.getPageListFromCache(chapter.chapter) }
.getPageListFromCache(chapter.chapter)
.onErrorResumeNext { source.fetchPageList(chapter.chapter) } .onErrorResumeNext { source.fetchPageList(chapter.chapter) }
.map { pages -> .map { pages ->
pages.mapIndexed { index, page -> pages.mapIndexed { index, page ->

View File

@ -8,7 +8,10 @@ import eu.kanade.tachiyomi.ui.reader.ReaderActivity
import eu.kanade.tachiyomi.widget.SimpleTabSelectedListener import eu.kanade.tachiyomi.widget.SimpleTabSelectedListener
import eu.kanade.tachiyomi.widget.sheet.TabbedBottomSheetDialog import eu.kanade.tachiyomi.widget.sheet.TabbedBottomSheetDialog
class ReaderSettingsSheet(private val activity: ReaderActivity) : TabbedBottomSheetDialog(activity) { class ReaderSettingsSheet(
private val activity: ReaderActivity,
showColorFilterSettings: Boolean = false,
) : TabbedBottomSheetDialog(activity) {
private val readingModeSettings = ReaderReadingModeSettings(activity) private val readingModeSettings = ReaderReadingModeSettings(activity)
private val generalSettings = ReaderGeneralSettings(activity) private val generalSettings = ReaderGeneralSettings(activity)
@ -40,6 +43,10 @@ class ReaderSettingsSheet(private val activity: ReaderActivity) : TabbedBottomSh
} }
} }
}) })
if (showColorFilterSettings) {
binding.tabs.getTabAt(filterTabIndex)?.select()
}
} }
override fun getTabViews() = listOf( override fun getTabViews() = listOf(

View File

@ -79,16 +79,7 @@ class WebtoonViewer(val activity: ReaderActivity, val isContinuous: Boolean = tr
recycler.addOnScrollListener( recycler.addOnScrollListener(
object : RecyclerView.OnScrollListener() { object : RecyclerView.OnScrollListener() {
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) { override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
val position = layoutManager.findLastEndVisibleItemPosition() onScrolled()
val item = adapter.items.getOrNull(position)
val allowPreload = checkAllowPreload(item as? ReaderPage)
if (item != null && currentPage != item) {
currentPage = item
when (item) {
is ReaderPage -> onPageSelected(item, allowPreload)
is ChapterTransition -> onTransitionSelected(item)
}
}
if (dy < 0) { if (dy < 0) {
val firstIndex = layoutManager.findFirstVisibleItemPosition() val firstIndex = layoutManager.findFirstVisibleItemPosition()
@ -243,11 +234,27 @@ class WebtoonViewer(val activity: ReaderActivity, val isContinuous: Boolean = tr
val position = adapter.items.indexOf(page) val position = adapter.items.indexOf(page)
if (position != -1) { if (position != -1) {
recycler.scrollToPosition(position) recycler.scrollToPosition(position)
if (layoutManager.findLastEndVisibleItemPosition() == -1) {
onScrolled(position)
}
} else { } else {
Timber.d("Page $page not found in adapter") Timber.d("Page $page not found in adapter")
} }
} }
fun onScrolled(pos: Int? = null) {
val position = pos ?: layoutManager.findLastEndVisibleItemPosition()
val item = adapter.items.getOrNull(position)
val allowPreload = checkAllowPreload(item as? ReaderPage)
if (item != null && currentPage != item) {
currentPage = item
when (item) {
is ReaderPage -> onPageSelected(item, allowPreload)
is ChapterTransition -> onTransitionSelected(item)
}
}
}
/** /**
* Scrolls up by [scrollDistance]. * Scrolls up by [scrollDistance].
*/ */

View File

@ -8,13 +8,13 @@ import eu.davidea.flexibleadapter.items.AbstractHeaderItem
import eu.davidea.flexibleadapter.items.IFlexible import eu.davidea.flexibleadapter.items.IFlexible
import eu.davidea.viewholders.FlexibleViewHolder import eu.davidea.viewholders.FlexibleViewHolder
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.databinding.RecentSectionItemBinding import eu.kanade.tachiyomi.databinding.SectionHeaderItemBinding
import java.util.Date import java.util.Date
class DateSectionItem(val date: Date) : AbstractHeaderItem<DateSectionItem.DateSectionItemHolder>() { class DateSectionItem(val date: Date) : AbstractHeaderItem<DateSectionItem.DateSectionItemHolder>() {
override fun getLayoutRes(): Int { override fun getLayoutRes(): Int {
return R.layout.recent_section_item return R.layout.section_header_item
} }
override fun createViewHolder(view: View, adapter: FlexibleAdapter<IFlexible<RecyclerView.ViewHolder>>): DateSectionItemHolder { override fun createViewHolder(view: View, adapter: FlexibleAdapter<IFlexible<RecyclerView.ViewHolder>>): DateSectionItemHolder {
@ -39,12 +39,12 @@ class DateSectionItem(val date: Date) : AbstractHeaderItem<DateSectionItem.DateS
inner class DateSectionItemHolder(view: View, adapter: FlexibleAdapter<*>) : FlexibleViewHolder(view, adapter, true) { inner class DateSectionItemHolder(view: View, adapter: FlexibleAdapter<*>) : FlexibleViewHolder(view, adapter, true) {
private val binding = RecentSectionItemBinding.bind(view) private val binding = SectionHeaderItemBinding.bind(view)
private val now = Date().time private val now = Date().time
fun bind(item: DateSectionItem) { fun bind(item: DateSectionItem) {
binding.sectionText.text = DateUtils.getRelativeTimeSpanString(item.date.time, now, DateUtils.DAY_IN_MILLIS) binding.title.text = DateUtils.getRelativeTimeSpanString(item.date.time, now, DateUtils.DAY_IN_MILLIS)
} }
} }
} }

View File

@ -35,7 +35,6 @@ class HistoryAdapter(controller: HistoryController) :
init { init {
setDisplayHeadersAtStartUp(true) setDisplayHeadersAtStartUp(true)
setStickyHeaders(true)
} }
interface OnResumeClickListener { interface OnResumeClickListener {

View File

@ -7,7 +7,6 @@ import android.view.Menu
import android.view.MenuInflater import android.view.MenuInflater
import android.view.MenuItem import android.view.MenuItem
import android.view.View import android.view.View
import android.view.ViewGroup
import androidx.appcompat.widget.SearchView import androidx.appcompat.widget.SearchView
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import com.afollestad.materialdialogs.MaterialDialog import com.afollestad.materialdialogs.MaterialDialog
@ -20,7 +19,6 @@ import eu.kanade.tachiyomi.data.database.models.History
import eu.kanade.tachiyomi.data.database.models.Manga import eu.kanade.tachiyomi.data.database.models.Manga
import eu.kanade.tachiyomi.databinding.HistoryControllerBinding import eu.kanade.tachiyomi.databinding.HistoryControllerBinding
import eu.kanade.tachiyomi.ui.base.controller.DialogController import eu.kanade.tachiyomi.ui.base.controller.DialogController
import eu.kanade.tachiyomi.ui.base.controller.NoToolbarElevationController
import eu.kanade.tachiyomi.ui.base.controller.NucleusController import eu.kanade.tachiyomi.ui.base.controller.NucleusController
import eu.kanade.tachiyomi.ui.base.controller.RootController import eu.kanade.tachiyomi.ui.base.controller.RootController
import eu.kanade.tachiyomi.ui.base.controller.withFadeTransaction import eu.kanade.tachiyomi.ui.base.controller.withFadeTransaction
@ -36,13 +34,10 @@ import uy.kohesive.injekt.injectLazy
/** /**
* Fragment that shows recently read manga. * Fragment that shows recently read manga.
* Uses [R.layout.history_controller].
* UI related actions should be called from here.
*/ */
class HistoryController : class HistoryController :
NucleusController<HistoryControllerBinding, HistoryPresenter>(), NucleusController<HistoryControllerBinding, HistoryPresenter>(),
RootController, RootController,
NoToolbarElevationController,
FlexibleAdapter.OnUpdateListener, FlexibleAdapter.OnUpdateListener,
FlexibleAdapter.EndlessScrollListener, FlexibleAdapter.EndlessScrollListener,
HistoryAdapter.OnRemoveClickListener, HistoryAdapter.OnRemoveClickListener,
@ -76,18 +71,16 @@ class HistoryController :
return HistoryPresenter() return HistoryPresenter()
} }
override fun inflateView(inflater: LayoutInflater, container: ViewGroup): View { override fun createBinding(inflater: LayoutInflater) = HistoryControllerBinding.inflate(inflater)
binding = HistoryControllerBinding.inflate(inflater)
override fun onViewCreated(view: View) {
super.onViewCreated(view)
binding.recycler.applyInsetter { binding.recycler.applyInsetter {
type(navigationBars = true) { type(navigationBars = true) {
padding() padding()
} }
} }
return binding.root
}
override fun onViewCreated(view: View) {
super.onViewCreated(view)
// Initialize adapter // Initialize adapter
binding.recycler.layoutManager = LinearLayoutManager(view.context) binding.recycler.layoutManager = LinearLayoutManager(view.context)

View File

@ -18,7 +18,6 @@ class UpdatesAdapter(
init { init {
setDisplayHeadersAtStartUp(true) setDisplayHeadersAtStartUp(true)
setStickyHeaders(true)
} }
interface OnCoverClickListener { interface OnCoverClickListener {

View File

@ -5,7 +5,6 @@ import android.view.Menu
import android.view.MenuInflater import android.view.MenuInflater
import android.view.MenuItem import android.view.MenuItem
import android.view.View import android.view.View
import android.view.ViewGroup
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.view.ActionMode import androidx.appcompat.view.ActionMode
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
@ -19,7 +18,6 @@ import eu.kanade.tachiyomi.data.download.model.Download
import eu.kanade.tachiyomi.data.library.LibraryUpdateService import eu.kanade.tachiyomi.data.library.LibraryUpdateService
import eu.kanade.tachiyomi.data.notification.Notifications import eu.kanade.tachiyomi.data.notification.Notifications
import eu.kanade.tachiyomi.databinding.UpdatesControllerBinding import eu.kanade.tachiyomi.databinding.UpdatesControllerBinding
import eu.kanade.tachiyomi.ui.base.controller.NoToolbarElevationController
import eu.kanade.tachiyomi.ui.base.controller.NucleusController import eu.kanade.tachiyomi.ui.base.controller.NucleusController
import eu.kanade.tachiyomi.ui.base.controller.RootController import eu.kanade.tachiyomi.ui.base.controller.RootController
import eu.kanade.tachiyomi.ui.base.controller.withFadeTransaction import eu.kanade.tachiyomi.ui.base.controller.withFadeTransaction
@ -37,13 +35,10 @@ import timber.log.Timber
/** /**
* Fragment that shows recent chapters. * Fragment that shows recent chapters.
* Uses [R.layout.updates_controller].
* UI related actions should be called from here.
*/ */
class UpdatesController : class UpdatesController :
NucleusController<UpdatesControllerBinding, UpdatesPresenter>(), NucleusController<UpdatesControllerBinding, UpdatesPresenter>(),
RootController, RootController,
NoToolbarElevationController,
ActionMode.Callback, ActionMode.Callback,
FlexibleAdapter.OnItemClickListener, FlexibleAdapter.OnItemClickListener,
FlexibleAdapter.OnItemLongClickListener, FlexibleAdapter.OnItemLongClickListener,
@ -75,18 +70,21 @@ class UpdatesController :
return UpdatesPresenter() return UpdatesPresenter()
} }
override fun inflateView(inflater: LayoutInflater, container: ViewGroup): View { override fun createBinding(inflater: LayoutInflater) = UpdatesControllerBinding.inflate(inflater)
binding = UpdatesControllerBinding.inflate(inflater)
override fun onViewCreated(view: View) {
super.onViewCreated(view)
binding.recycler.applyInsetter { binding.recycler.applyInsetter {
type(navigationBars = true) { type(navigationBars = true) {
padding() padding()
} }
} }
return binding.root binding.actionToolbar.applyInsetter {
} type(navigationBars = true) {
margin(bottom = true)
}
}
override fun onViewCreated(view: View) {
super.onViewCreated(view)
view.context.notificationManager.cancel(Notifications.ID_NEW_CHAPTERS) view.context.notificationManager.cancel(Notifications.ID_NEW_CHAPTERS)
// Init RecyclerView and adapter // Init RecyclerView and adapter

View File

@ -1,21 +1,19 @@
package eu.kanade.tachiyomi.ui.security package eu.kanade.tachiyomi.ui.security
import android.os.Bundle import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import androidx.biometric.BiometricPrompt import androidx.biometric.BiometricPrompt
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.preference.PreferencesHelper import eu.kanade.tachiyomi.ui.base.activity.BaseThemedActivity
import eu.kanade.tachiyomi.util.system.BiometricUtil import eu.kanade.tachiyomi.util.system.BiometricUtil
import uy.kohesive.injekt.injectLazy import timber.log.Timber
import java.util.Date import java.util.Date
import java.util.concurrent.Executors import java.util.concurrent.Executors
/** /**
* Blank activity with a BiometricPrompt. * Blank activity with a BiometricPrompt.
*/ */
class BiometricUnlockActivity : AppCompatActivity() { class BiometricUnlockActivity : BaseThemedActivity() {
private val preferences: PreferencesHelper by injectLazy()
private val executor = Executors.newSingleThreadExecutor() private val executor = Executors.newSingleThreadExecutor()
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
@ -27,6 +25,7 @@ class BiometricUnlockActivity : AppCompatActivity() {
object : BiometricPrompt.AuthenticationCallback() { object : BiometricPrompt.AuthenticationCallback() {
override fun onAuthenticationError(errorCode: Int, errString: CharSequence) { override fun onAuthenticationError(errorCode: Int, errString: CharSequence) {
super.onAuthenticationError(errorCode, errString) super.onAuthenticationError(errorCode, errString)
Timber.e(errString.toString())
finishAffinity() finishAffinity()
} }

View File

@ -21,6 +21,8 @@ import eu.kanade.tachiyomi.network.PREF_DOH_CLOUDFLARE
import eu.kanade.tachiyomi.network.PREF_DOH_GOOGLE import eu.kanade.tachiyomi.network.PREF_DOH_GOOGLE
import eu.kanade.tachiyomi.ui.base.controller.DialogController import eu.kanade.tachiyomi.ui.base.controller.DialogController
import eu.kanade.tachiyomi.util.CrashLogUtil import eu.kanade.tachiyomi.util.CrashLogUtil
import eu.kanade.tachiyomi.util.lang.launchIO
import eu.kanade.tachiyomi.util.lang.withUIContext
import eu.kanade.tachiyomi.util.preference.defaultValue import eu.kanade.tachiyomi.util.preference.defaultValue
import eu.kanade.tachiyomi.util.preference.intListPreference import eu.kanade.tachiyomi.util.preference.intListPreference
import eu.kanade.tachiyomi.util.preference.onChange import eu.kanade.tachiyomi.util.preference.onChange
@ -32,9 +34,6 @@ import eu.kanade.tachiyomi.util.preference.switchPreference
import eu.kanade.tachiyomi.util.preference.titleRes import eu.kanade.tachiyomi.util.preference.titleRes
import eu.kanade.tachiyomi.util.system.powerManager import eu.kanade.tachiyomi.util.system.powerManager
import eu.kanade.tachiyomi.util.system.toast import eu.kanade.tachiyomi.util.system.toast
import rx.Observable
import rx.android.schedulers.AndroidSchedulers
import rx.schedulers.Schedulers
import uy.kohesive.injekt.injectLazy import uy.kohesive.injekt.injectLazy
import eu.kanade.tachiyomi.data.preference.PreferenceKeys as Keys import eu.kanade.tachiyomi.data.preference.PreferenceKeys as Keys
@ -172,27 +171,18 @@ class SettingsAdvancedController : SettingsController() {
private fun clearChapterCache() { private fun clearChapterCache() {
if (activity == null) return if (activity == null) return
val files = chapterCache.cacheDir.listFiles() ?: return launchIO {
try {
var deletedFiles = 0 val deletedFiles = chapterCache.clear()
withUIContext {
Observable.defer { Observable.from(files) } activity?.toast(resources?.getString(R.string.cache_deleted, deletedFiles))
.doOnNext { file -> findPreference(CLEAR_CACHE_KEY)?.summary =
if (chapterCache.removeFileFromCache(file.name)) { resources?.getString(R.string.used_cache, chapterCache.readableSize)
deletedFiles++
} }
} catch (e: Throwable) {
withUIContext { activity?.toast(R.string.cache_delete_error) }
} }
.subscribeOn(Schedulers.io()) }
.observeOn(AndroidSchedulers.mainThread())
.doOnError {
activity?.toast(R.string.cache_delete_error)
}
.doOnCompleted {
activity?.toast(resources?.getString(R.string.cache_deleted, deletedFiles))
findPreference(CLEAR_CACHE_KEY)?.summary =
resources?.getString(R.string.used_cache, chapterCache.readableSize)
}
.subscribe()
} }
class ClearDatabaseDialogController : DialogController() { class ClearDatabaseDialogController : DialogController() {

View File

@ -24,9 +24,6 @@ import eu.kanade.tachiyomi.ui.base.controller.BaseController
import eu.kanade.tachiyomi.ui.base.controller.RootController import eu.kanade.tachiyomi.ui.base.controller.RootController
import eu.kanade.tachiyomi.util.system.getResourceColor import eu.kanade.tachiyomi.util.system.getResourceColor
import kotlinx.coroutines.MainScope import kotlinx.coroutines.MainScope
import rx.Observable
import rx.Subscription
import rx.subscriptions.CompositeSubscription
import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get import uy.kohesive.injekt.api.get
@ -35,19 +32,14 @@ abstract class SettingsController : PreferenceController() {
var preferenceKey: String? = null var preferenceKey: String? = null
val preferences: PreferencesHelper = Injekt.get() val preferences: PreferencesHelper = Injekt.get()
val viewScope = MainScope() val viewScope = MainScope()
private var themedContext: Context? = null
var untilDestroySubscriptions = CompositeSubscription()
private set
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup, savedInstanceState: Bundle?): View { override fun onCreateView(inflater: LayoutInflater, container: ViewGroup, savedInstanceState: Bundle?): View {
if (untilDestroySubscriptions.isUnsubscribed) {
untilDestroySubscriptions = CompositeSubscription()
}
val view = super.onCreateView(inflater, container, savedInstanceState) val view = super.onCreateView(inflater, container, savedInstanceState)
if (this is RootController) { if (this is RootController) {
view.updatePadding(bottom = view.context.resources.getDimensionPixelSize(R.dimen.action_toolbar_list_padding)) listView.clipToPadding = false
listView.updatePadding(bottom = view.context.resources.getDimensionPixelSize(R.dimen.action_toolbar_list_padding))
} }
listView.applyInsetter { listView.applyInsetter {
@ -77,25 +69,31 @@ abstract class SettingsController : PreferenceController() {
} }
} }
override fun onChangeStarted(handler: ControllerChangeHandler, type: ControllerChangeType) {
if (type.isEnter) {
setTitle()
}
setHasOptionsMenu(type.isEnter)
super.onChangeStarted(handler, type)
}
override fun onDestroyView(view: View) { override fun onDestroyView(view: View) {
super.onDestroyView(view) super.onDestroyView(view)
untilDestroySubscriptions.unsubscribe() themedContext = null
} }
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
val screen = preferenceManager.createPreferenceScreen(getThemedContext()) val tv = TypedValue()
activity!!.theme.resolveAttribute(R.attr.preferenceTheme, tv, true)
themedContext = ContextThemeWrapper(activity, tv.resourceId)
val screen = preferenceManager.createPreferenceScreen(themedContext)
preferenceScreen = screen preferenceScreen = screen
setupPreferenceScreen(screen) setupPreferenceScreen(screen)
} }
abstract fun setupPreferenceScreen(screen: PreferenceScreen): PreferenceScreen abstract fun setupPreferenceScreen(screen: PreferenceScreen): PreferenceScreen
private fun getThemedContext(): Context {
val tv = TypedValue()
activity!!.theme.resolveAttribute(R.attr.preferenceTheme, tv, true)
return ContextThemeWrapper(activity, tv.resourceId)
}
private fun animatePreferenceHighlight(view: View) { private fun animatePreferenceHighlight(view: View) {
ValueAnimator ValueAnimator
.ofObject(ArgbEvaluator(), Color.TRANSPARENT, view.context.getResourceColor(R.attr.rippleColor)) .ofObject(ArgbEvaluator(), Color.TRANSPARENT, view.context.getResourceColor(R.attr.rippleColor))
@ -111,7 +109,7 @@ abstract class SettingsController : PreferenceController() {
return preferenceScreen?.title?.toString() return preferenceScreen?.title?.toString()
} }
fun setTitle() { private fun setTitle() {
var parentController = parentController var parentController = parentController
while (parentController != null) { while (parentController != null) {
if (parentController is BaseController<*> && parentController.getTitle() != null) { if (parentController is BaseController<*> && parentController.getTitle() != null) {
@ -122,16 +120,4 @@ abstract class SettingsController : PreferenceController() {
(activity as? AppCompatActivity)?.supportActionBar?.title = getTitle() (activity as? AppCompatActivity)?.supportActionBar?.title = getTitle()
} }
override fun onChangeStarted(handler: ControllerChangeHandler, type: ControllerChangeType) {
if (type.isEnter) {
setTitle()
}
setHasOptionsMenu(type.isEnter)
super.onChangeStarted(handler, type)
}
fun <T> Observable<T>.subscribeUntilDestroy(onNext: (T) -> Unit): Subscription {
return subscribe(onNext).also { untilDestroySubscriptions.add(it) }
}
} }

View File

@ -6,7 +6,6 @@ import android.view.Menu
import android.view.MenuInflater import android.view.MenuInflater
import android.view.MenuItem import android.view.MenuItem
import android.view.View import android.view.View
import android.view.ViewGroup
import androidx.appcompat.widget.SearchView import androidx.appcompat.widget.SearchView
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
@ -33,17 +32,7 @@ class SettingsSearchController :
setHasOptionsMenu(true) setHasOptionsMenu(true)
} }
/** override fun createBinding(inflater: LayoutInflater) = SettingsSearchControllerBinding.inflate(inflater)
* Initiate the view with [R.layout.settings_search_controller].
*
* @param inflater used to load the layout xml.
* @param container containing parent views.
* @return inflated view
*/
override fun inflateView(inflater: LayoutInflater, container: ViewGroup): View {
binding = SettingsSearchControllerBinding.inflate(inflater)
return binding.root
}
override fun getTitle(): String? { override fun getTitle(): String? {
return presenter.query return presenter.query

View File

@ -139,8 +139,10 @@ class WebViewActivity : BaseViewBindingActivity<WebviewActivityBinding>() {
} }
override fun onDestroy() { override fun onDestroy() {
binding.webview?.destroy()
super.onDestroy() super.onDestroy()
// Binding sometimes isn't actually instantiated yet somehow
binding?.webview?.destroy()
} }
override fun onCreateOptionsMenu(menu: Menu): Boolean { override fun onCreateOptionsMenu(menu: Menu): Boolean {

View File

@ -2,15 +2,18 @@ package eu.kanade.tachiyomi.util
import android.content.Context import android.content.Context
import android.net.Uri import android.net.Uri
import android.os.Build
import eu.kanade.tachiyomi.BuildConfig
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.notification.NotificationReceiver import eu.kanade.tachiyomi.data.notification.NotificationReceiver
import eu.kanade.tachiyomi.data.notification.Notifications import eu.kanade.tachiyomi.data.notification.Notifications
import eu.kanade.tachiyomi.util.lang.launchIO
import eu.kanade.tachiyomi.util.lang.withUIContext
import eu.kanade.tachiyomi.util.storage.getUriCompat import eu.kanade.tachiyomi.util.storage.getUriCompat
import eu.kanade.tachiyomi.util.system.createFileInCacheDir import eu.kanade.tachiyomi.util.system.createFileInCacheDir
import eu.kanade.tachiyomi.util.system.notificationBuilder import eu.kanade.tachiyomi.util.system.notificationBuilder
import eu.kanade.tachiyomi.util.system.notificationManager import eu.kanade.tachiyomi.util.system.notificationManager
import eu.kanade.tachiyomi.util.system.toast import eu.kanade.tachiyomi.util.system.toast
import java.io.IOException
class CrashLogUtil(private val context: Context) { class CrashLogUtil(private val context: Context) {
@ -19,31 +22,44 @@ class CrashLogUtil(private val context: Context) {
} }
fun dumpLogs() { fun dumpLogs() {
try { launchIO {
val file = context.createFileInCacheDir("tachiyomi_crash_logs.txt") try {
Runtime.getRuntime().exec("logcat *:E -d -f ${file.absolutePath}") val file = context.createFileInCacheDir("tachiyomi_crash_logs.txt")
Runtime.getRuntime().exec("logcat *:E -d -f ${file.absolutePath}").waitFor()
file.appendText(getDebugInfo())
showNotification(file.getUriCompat(context)) showNotification(file.getUriCompat(context))
} catch (e: IOException) { } catch (e: Throwable) {
context.toast("Failed to get logs") withUIContext { context.toast("Failed to get logs") }
}
} }
} }
fun getDebugInfo(): String {
return """
App version: ${BuildConfig.VERSION_NAME} (${BuildConfig.FLAVOR}, ${BuildConfig.COMMIT_SHA}, ${BuildConfig.VERSION_CODE})
Android version: ${Build.VERSION.RELEASE} (SDK ${Build.VERSION.SDK_INT})
Android build ID: ${Build.DISPLAY}
Device brand: ${Build.BRAND}
Device manufacturer: ${Build.MANUFACTURER}
Device name: ${Build.DEVICE}
Device model: ${Build.MODEL}
Device product name: ${Build.PRODUCT}
""".trimIndent()
}
private fun showNotification(uri: Uri) { private fun showNotification(uri: Uri) {
context.notificationManager.cancel(Notifications.ID_CRASH_LOGS) context.notificationManager.cancel(Notifications.ID_CRASH_LOGS)
with(notificationBuilder) { with(notificationBuilder) {
setContentTitle(context.getString(R.string.crash_log_saved)) setContentTitle(context.getString(R.string.crash_log_saved))
// Clear old actions if they exist
clearActions() clearActions()
addAction( addAction(
R.drawable.ic_folder_24dp, R.drawable.ic_folder_24dp,
context.getString(R.string.action_open_log), context.getString(R.string.action_open_log),
NotificationReceiver.openErrorLogPendingActivity(context, uri) NotificationReceiver.openErrorLogPendingActivity(context, uri)
) )
addAction( addAction(
R.drawable.ic_share_24dp, R.drawable.ic_share_24dp,
context.getString(R.string.action_share), context.getString(R.string.action_share),

View File

@ -157,3 +157,5 @@ private fun shouldUpdateDbChapter(dbChapter: Chapter, sourceChapter: SChapter):
dbChapter.date_upload != sourceChapter.date_upload || dbChapter.date_upload != sourceChapter.date_upload ||
dbChapter.chapter_number != sourceChapter.chapter_number dbChapter.chapter_number != sourceChapter.chapter_number
} }
class NoChaptersException : Exception()

View File

@ -1,3 +0,0 @@
package eu.kanade.tachiyomi.util.chapter
class NoChaptersException : Exception()

View File

@ -1,12 +1,17 @@
package eu.kanade.tachiyomi.util.system package eu.kanade.tachiyomi.util.system
import android.content.Context import android.content.Context
import android.os.Build
import androidx.biometric.BiometricManager import androidx.biometric.BiometricManager
import androidx.biometric.BiometricManager.Authenticators import androidx.biometric.BiometricManager.Authenticators
object BiometricUtil { object BiometricUtil {
fun getSupportedAuthenticators(context: Context): Int { fun getSupportedAuthenticators(context: Context): Int {
if (isLegacySecured(context)) {
return Authenticators.BIOMETRIC_WEAK or Authenticators.DEVICE_CREDENTIAL
}
return listOf( return listOf(
Authenticators.BIOMETRIC_STRONG, Authenticators.BIOMETRIC_STRONG,
Authenticators.BIOMETRIC_WEAK, Authenticators.BIOMETRIC_WEAK,
@ -17,10 +22,22 @@ object BiometricUtil {
} }
fun isSupported(context: Context): Boolean { fun isSupported(context: Context): Boolean {
return getSupportedAuthenticators(context) != 0 return isLegacySecured(context) || getSupportedAuthenticators(context) != 0
} }
fun isDeviceCredentialAllowed(context: Context): Boolean { fun isDeviceCredentialAllowed(context: Context): Boolean {
return getSupportedAuthenticators(context) and Authenticators.DEVICE_CREDENTIAL != 0 return isLegacySecured(context) || (getSupportedAuthenticators(context) and Authenticators.DEVICE_CREDENTIAL != 0)
}
/**
* 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 (context.keyguardManager.isDeviceSecure) {
return true
}
}
return false
} }
} }

View File

@ -1,6 +1,7 @@
package eu.kanade.tachiyomi.util.system package eu.kanade.tachiyomi.util.system
import android.app.ActivityManager import android.app.ActivityManager
import android.app.KeyguardManager
import android.app.Notification import android.app.Notification
import android.app.NotificationManager import android.app.NotificationManager
import android.content.BroadcastReceiver import android.content.BroadcastReceiver
@ -33,6 +34,7 @@ import androidx.core.net.toUri
import androidx.localbroadcastmanager.content.LocalBroadcastManager import androidx.localbroadcastmanager.content.LocalBroadcastManager
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.util.lang.truncateCenter import eu.kanade.tachiyomi.util.lang.truncateCenter
import timber.log.Timber
import java.io.File import java.io.File
import kotlin.math.roundToInt import kotlin.math.roundToInt
@ -68,10 +70,15 @@ fun Context.toast(text: String?, duration: Int = Toast.LENGTH_SHORT, block: (Toa
fun Context.copyToClipboard(label: String, content: String) { fun Context.copyToClipboard(label: String, content: String) {
if (content.isBlank()) return if (content.isBlank()) return
val clipboard = getSystemService<ClipboardManager>()!! try {
clipboard.setPrimaryClip(ClipData.newPlainText(label, content)) val clipboard = getSystemService<ClipboardManager>()!!
clipboard.setPrimaryClip(ClipData.newPlainText(label, content))
toast(getString(R.string.copied_to_clipboard, content.truncateCenter(50))) toast(getString(R.string.copied_to_clipboard, content.truncateCenter(50)))
} catch (e: Throwable) {
Timber.e(e)
toast(R.string.clipboard_copy_error)
}
} }
/** /**
@ -153,24 +160,18 @@ val Float.dpToPxEnd: Float
val Resources.isLTR val Resources.isLTR
get() = configuration.layoutDirection == View.LAYOUT_DIRECTION_LTR get() = configuration.layoutDirection == View.LAYOUT_DIRECTION_LTR
/**
* Property to get the notification manager from the context.
*/
val Context.notificationManager: NotificationManager val Context.notificationManager: NotificationManager
get() = getSystemService()!! get() = getSystemService()!!
/**
* Property to get the connectivity manager from the context.
*/
val Context.connectivityManager: ConnectivityManager val Context.connectivityManager: ConnectivityManager
get() = getSystemService()!! get() = getSystemService()!!
/**
* Property to get the power manager from the context.
*/
val Context.powerManager: PowerManager val Context.powerManager: PowerManager
get() = getSystemService()!! get() = getSystemService()!!
val Context.keyguardManager: KeyguardManager
get() = getSystemService()!!
/** /**
* Convenience method to acquire a partial wake lock. * Convenience method to acquire a partial wake lock.
*/ */

View File

@ -0,0 +1,26 @@
package eu.kanade.tachiyomi.util.system
import android.content.Context
import android.content.res.Resources
object InternalResourceHelper {
fun getBoolean(context: Context, resName: String, defaultValue: Boolean): Boolean {
val id = getResourceId(resName, "bool")
return if (id != 0) {
context.createPackageContext("android", 0).resources.getBoolean(id)
} else {
defaultValue
}
}
/**
* Get resource id from system resources
* @param resName resource name to get
* @param type resource type of [resName] to get
* @return 0 if not available
*/
private fun getResourceId(resName: String, type: String): Int {
return Resources.getSystem().getIdentifier(resName, type, "android")
}
}

View File

@ -1,19 +1,21 @@
package eu.kanade.tachiyomi.util.view package eu.kanade.tachiyomi.util.view
import android.widget.ImageView import android.widget.ImageView
import androidx.annotation.AttrRes
import androidx.annotation.DrawableRes import androidx.annotation.DrawableRes
import androidx.appcompat.content.res.AppCompatResources import androidx.appcompat.content.res.AppCompatResources
import eu.kanade.tachiyomi.util.system.getResourceColor
/** /**
* Set a vector on a [ImageView]. * Set a vector on a [ImageView].
* *
* @param drawable id of drawable resource * @param drawable id of drawable resource
*/ */
fun ImageView.setVectorCompat(@DrawableRes drawable: Int, tint: Int? = null) { fun ImageView.setVectorCompat(@DrawableRes drawable: Int, @AttrRes tint: Int? = null) {
val vector = AppCompatResources.getDrawable(context, drawable) val vector = AppCompatResources.getDrawable(context, drawable)
if (tint != null) { if (tint != null) {
vector?.mutate() vector?.mutate()
vector?.setTint(tint) vector?.setTint(context.getResourceColor(tint))
} }
setImageDrawable(vector) setImageDrawable(vector)
} }

View File

@ -12,7 +12,7 @@ import androidx.annotation.MenuRes
import androidx.appcompat.view.ActionMode import androidx.appcompat.view.ActionMode
import androidx.core.view.isVisible import androidx.core.view.isVisible
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.databinding.CommonActionToolbarBinding import eu.kanade.tachiyomi.databinding.ActionToolbarBinding
/** /**
* A toolbar holding only menu items. * A toolbar holding only menu items.
@ -20,25 +20,21 @@ import eu.kanade.tachiyomi.databinding.CommonActionToolbarBinding
class ActionToolbar @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null) : class ActionToolbar @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null) :
FrameLayout(context, attrs) { FrameLayout(context, attrs) {
private val binding: CommonActionToolbarBinding private val binding = ActionToolbarBinding.inflate(LayoutInflater.from(context), this, true)
init {
binding = CommonActionToolbarBinding.inflate(LayoutInflater.from(context), this, true)
}
/** /**
* Remove menu items and remove listener. * Remove menu items and remove listener.
*/ */
fun destroy() { fun destroy() {
binding.commonActionMenu.menu.clear() binding.menu.menu.clear()
binding.commonActionMenu.setOnMenuItemClickListener(null) binding.menu.setOnMenuItemClickListener(null)
} }
/** /**
* Gets a menu item if found. * Gets a menu item if found.
*/ */
fun findItem(@IdRes itemId: Int): MenuItem? { fun findItem(@IdRes itemId: Int): MenuItem? {
return binding.commonActionMenu.menu.findItem(itemId) return binding.menu.menu.findItem(itemId)
} }
/** /**
@ -46,14 +42,14 @@ class ActionToolbar @JvmOverloads constructor(context: Context, attrs: Attribute
*/ */
fun show(mode: ActionMode, @MenuRes menuRes: Int, listener: (item: MenuItem?) -> Boolean) { fun show(mode: ActionMode, @MenuRes menuRes: Int, listener: (item: MenuItem?) -> Boolean) {
// Avoid re-inflating the menu // Avoid re-inflating the menu
if (binding.commonActionMenu.menu.size() == 0) { if (binding.menu.menu.size() == 0) {
mode.menuInflater.inflate(menuRes, binding.commonActionMenu.menu) mode.menuInflater.inflate(menuRes, binding.menu.menu)
binding.commonActionMenu.setOnMenuItemClickListener { listener(it) } binding.menu.setOnMenuItemClickListener { listener(it) }
} }
binding.commonActionToolbar.isVisible = true binding.actionToolbar.isVisible = true
val bottomAnimation = AnimationUtils.loadAnimation(context, R.anim.enter_from_bottom) val bottomAnimation = AnimationUtils.loadAnimation(context, R.anim.enter_from_bottom)
binding.commonActionToolbar.startAnimation(bottomAnimation) binding.actionToolbar.startAnimation(bottomAnimation)
} }
/** /**
@ -64,10 +60,10 @@ class ActionToolbar @JvmOverloads constructor(context: Context, attrs: Attribute
bottomAnimation.setAnimationListener( bottomAnimation.setAnimationListener(
object : SimpleAnimationListener() { object : SimpleAnimationListener() {
override fun onAnimationEnd(animation: Animation) { override fun onAnimationEnd(animation: Animation) {
binding.commonActionToolbar.isVisible = false binding.actionToolbar.isVisible = false
} }
} }
) )
binding.commonActionToolbar.startAnimation(bottomAnimation) binding.actionToolbar.startAnimation(bottomAnimation)
} }
} }

View File

@ -1,5 +1,6 @@
package eu.kanade.tachiyomi.ui.reader.setting package eu.kanade.tachiyomi.widget
import android.annotation.SuppressLint
import android.content.Context import android.content.Context
import android.util.AttributeSet import android.util.AttributeSet
import android.view.Gravity import android.view.Gravity
@ -7,15 +8,21 @@ import android.view.LayoutInflater
import android.view.MenuItem import android.view.MenuItem
import android.widget.FrameLayout import android.widget.FrameLayout
import androidx.annotation.ArrayRes import androidx.annotation.ArrayRes
import androidx.appcompat.view.menu.MenuBuilder
import androidx.appcompat.widget.PopupMenu import androidx.appcompat.widget.PopupMenu
import androidx.core.content.ContextCompat
import androidx.core.view.forEach
import androidx.core.view.get
import com.tfcporciuncula.flow.Preference import com.tfcporciuncula.flow.Preference
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.databinding.SpinnerPreferenceBinding import eu.kanade.tachiyomi.databinding.SpinnerPreferenceBinding
import eu.kanade.tachiyomi.util.system.getResourceColor
class SpinnerPreference @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null) : class MaterialSpinnerView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null) :
FrameLayout(context, attrs) { FrameLayout(context, attrs) {
private var entries = emptyList<String>() private var entries = emptyList<String>()
private var selectedPosition = 0
private var popup: PopupMenu? = null private var popup: PopupMenu? = null
var onItemSelectedListener: ((Int) -> Unit)? = null var onItemSelectedListener: ((Int) -> Unit)? = null
@ -30,17 +37,26 @@ class SpinnerPreference @JvmOverloads constructor(context: Context, attrs: Attri
} }
} }
private val emptyIcon by lazy {
ContextCompat.getDrawable(context, R.drawable.ic_blank_24dp)
}
private val checkmarkIcon by lazy {
ContextCompat.getDrawable(context, R.drawable.ic_check_24dp)?.mutate()?.apply {
setTint(context.getResourceColor(android.R.attr.textColorPrimary))
}
}
private val binding = SpinnerPreferenceBinding.inflate(LayoutInflater.from(context), this, false) private val binding = SpinnerPreferenceBinding.inflate(LayoutInflater.from(context), this, false)
init { init {
addView(binding.root) addView(binding.root)
val attr = context.obtainStyledAttributes(attrs, R.styleable.SpinnerPreference) val attr = context.obtainStyledAttributes(attrs, R.styleable.MaterialSpinnerView)
val title = attr.getString(R.styleable.SpinnerPreference_title).orEmpty() val title = attr.getString(R.styleable.MaterialSpinnerView_title).orEmpty()
binding.title.text = title binding.title.text = title
val entries = (attr.getTextArray(R.styleable.SpinnerPreference_android_entries) ?: emptyArray()).map { it.toString() } val entries = (attr.getTextArray(R.styleable.MaterialSpinnerView_android_entries) ?: emptyArray()).map { it.toString() }
this.entries = entries this.entries = entries
binding.details.text = entries.firstOrNull().orEmpty() binding.details.text = entries.firstOrNull().orEmpty()
@ -48,6 +64,14 @@ class SpinnerPreference @JvmOverloads constructor(context: Context, attrs: Attri
} }
fun setSelection(selection: Int) { fun setSelection(selection: Int) {
popup?.menu?.get(selectedPosition)?.let {
it.icon = emptyIcon
it.title = entries[selectedPosition]
}
selectedPosition = selection
popup?.menu?.get(selectedPosition)?.let {
it.icon = checkmarkIcon
}
binding.details.text = entries.getOrNull(selection).orEmpty() binding.details.text = entries.getOrNull(selection).orEmpty()
} }
@ -118,11 +142,19 @@ class SpinnerPreference @JvmOverloads constructor(context: Context, attrs: Attri
return pos return pos
} }
@SuppressLint("RestrictedApi")
fun createPopupMenu(onItemClick: (Int) -> Unit): PopupMenu { fun createPopupMenu(onItemClick: (Int) -> Unit): PopupMenu {
val popup = PopupMenu(context, this, Gravity.END, R.attr.actionOverflowMenuStyle, 0) val popup = PopupMenu(context, this, Gravity.END, R.attr.actionOverflowMenuStyle, 0)
entries.forEachIndexed { index, entry -> entries.forEachIndexed { index, entry ->
popup.menu.add(0, index, 0, entry) popup.menu.add(0, index, 0, entry)
} }
(popup.menu as? MenuBuilder)?.setOptionalIconsVisible(true)
popup.menu.forEach {
it.icon = emptyIcon
}
popup.menu.getItem(selectedPosition)?.let {
it.icon = checkmarkIcon
}
popup.setOnMenuItemClickListener { menuItem -> popup.setOnMenuItemClickListener { menuItem ->
val pos = menuClicked(menuItem) val pos = menuClicked(menuItem)
onItemClick(pos) onItemClick(pos)

View File

@ -1,13 +1,10 @@
package eu.kanade.tachiyomi.widget.materialdialogs package eu.kanade.tachiyomi.widget.materialdialogs
import android.content.Context import android.content.Context
import android.graphics.drawable.Drawable
import android.util.AttributeSet import android.util.AttributeSet
import androidx.annotation.AttrRes
import androidx.appcompat.content.res.AppCompatResources
import androidx.appcompat.widget.AppCompatImageView import androidx.appcompat.widget.AppCompatImageView
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.util.system.getResourceColor import eu.kanade.tachiyomi.util.view.setVectorCompat
class QuadStateCheckBox @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null) : class QuadStateCheckBox @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null) :
AppCompatImageView(context, attrs) { AppCompatImageView(context, attrs) {
@ -19,19 +16,11 @@ class QuadStateCheckBox @JvmOverloads constructor(context: Context, attrs: Attri
} }
private fun updateDrawable() { private fun updateDrawable() {
val drawable = when (state) { when (state) {
State.UNCHECKED -> tintVector(context, R.drawable.ic_check_box_outline_blank_24dp, R.attr.colorControlNormal) State.UNCHECKED -> setVectorCompat(R.drawable.ic_check_box_outline_blank_24dp, R.attr.colorControlNormal)
State.INDETERMINATE -> tintVector(context, R.drawable.ic_indeterminate_check_box_24dp) State.INDETERMINATE -> setVectorCompat(R.drawable.ic_indeterminate_check_box_24dp, R.attr.colorAccent)
State.CHECKED -> tintVector(context, R.drawable.ic_check_box_24dp) State.CHECKED -> setVectorCompat(R.drawable.ic_check_box_24dp, R.attr.colorAccent)
State.INVERSED -> tintVector(context, R.drawable.ic_check_box_x_24dp) State.INVERSED -> setVectorCompat(R.drawable.ic_check_box_x_24dp, R.attr.colorAccent)
}
setImageDrawable(drawable)
}
private fun tintVector(context: Context, resId: Int, @AttrRes colorAttrRes: Int = R.attr.colorAccent): Drawable {
return AppCompatResources.getDrawable(context, resId)!!.apply {
setTint(context.getResourceColor(colorAttrRes))
} }
} }

View File

@ -8,7 +8,7 @@ import eu.kanade.tachiyomi.widget.ViewPagerAdapter
abstract class TabbedBottomSheetDialog(private val activity: Activity) : BaseBottomSheetDialog(activity) { abstract class TabbedBottomSheetDialog(private val activity: Activity) : BaseBottomSheetDialog(activity) {
val binding: CommonTabbedSheetBinding = CommonTabbedSheetBinding.inflate(activity.layoutInflater) val binding = CommonTabbedSheetBinding.inflate(activity.layoutInflater)
init { init {
val adapter = LibrarySettingsSheetAdapter() val adapter = LibrarySettingsSheetAdapter()

View File

@ -0,0 +1,7 @@
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<size
android:width="24dp"
android:height="24dp" />
<solid android:color="@android:color/transparent" />
</shape>

View File

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="@android:color/black"
android:pathData="M9,16.17L4.83,12l-1.42,1.41L9,19 21,7l-1.41,-1.41z" />
</vector>

View File

@ -1,9 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="@android:color/black"
android:pathData="M22.15,13.85H1.85A1.86,1.86,0,0,1,0,12H0a1.86,1.86,0,0,1,1.85-1.85H22.15A1.86,1.86,0,0,1,24,12h0A1.86,1.86,0,0,1,22.15,13.85Z" />
</vector>

View File

@ -2,7 +2,7 @@
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/common_action_toolbar" android:id="@+id/action_toolbar"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:clipToPadding="false" android:clipToPadding="false"
@ -25,7 +25,7 @@
app:contentInsetStart="8dp"> app:contentInsetStart="8dp">
<androidx.appcompat.widget.ActionMenuView <androidx.appcompat.widget.ActionMenuView
android:id="@+id/common_action_menu" android:id="@+id/menu"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" /> android:layout_height="match_parent" />

View File

@ -17,7 +17,7 @@
android:clipToPadding="false" android:clipToPadding="false"
android:paddingTop="8dp" android:paddingTop="8dp"
android:paddingBottom="@dimen/action_toolbar_list_padding" android:paddingBottom="@dimen/action_toolbar_list_padding"
tools:listitem="@layout/source_main_controller_card_header" /> tools:listitem="@layout/section_header_item" />
</eu.kanade.tachiyomi.widget.ThemedSwipeRefreshLayout> </eu.kanade.tachiyomi.widget.ThemedSwipeRefreshLayout>

View File

@ -19,7 +19,7 @@
android:layout_height="?attr/actionBarSize" android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary" android:background="?attr/colorPrimary"
android:theme="?attr/actionBarTheme" android:theme="?attr/actionBarTheme"
app:layout_scrollFlags="scroll|enterAlways|snap" /> app:layout_scrollFlags="scroll|enterAlways" />
<com.google.android.material.tabs.TabLayout <com.google.android.material.tabs.TabLayout
android:id="@+id/tabs" android:id="@+id/tabs"

View File

@ -60,7 +60,8 @@
<com.google.android.material.appbar.MaterialToolbar <com.google.android.material.appbar.MaterialToolbar
android:id="@+id/toolbar" android:id="@+id/toolbar"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize" android:layout_height="wrap_content"
android:minHeight="?attr/actionBarSize"
android:background="?attr/colorPrimary" /> android:background="?attr/colorPrimary" />
<LinearLayout <LinearLayout

View File

@ -10,14 +10,14 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:orientation="vertical"> android:orientation="vertical">
<eu.kanade.tachiyomi.ui.reader.setting.SpinnerPreference <eu.kanade.tachiyomi.widget.MaterialSpinnerView
android:id="@+id/rotation_mode" android:id="@+id/rotation_mode"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:entries="@array/rotation_type" android:entries="@array/rotation_type"
app:title="@string/pref_rotation_type" /> app:title="@string/pref_rotation_type" />
<eu.kanade.tachiyomi.ui.reader.setting.SpinnerPreference <eu.kanade.tachiyomi.widget.MaterialSpinnerView
android:id="@+id/background_color" android:id="@+id/background_color"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"

View File

@ -15,28 +15,28 @@
android:paddingEnd="16dp" android:paddingEnd="16dp"
android:textAppearance="@style/TextAppearance.Medium.SubHeading" /> android:textAppearance="@style/TextAppearance.Medium.SubHeading" />
<eu.kanade.tachiyomi.ui.reader.setting.SpinnerPreference <eu.kanade.tachiyomi.widget.MaterialSpinnerView
android:id="@+id/pager_nav" android:id="@+id/pager_nav"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:entries="@array/pager_nav" android:entries="@array/pager_nav"
app:title="@string/pref_viewer_nav" /> app:title="@string/pref_viewer_nav" />
<eu.kanade.tachiyomi.ui.reader.setting.SpinnerPreference <eu.kanade.tachiyomi.widget.MaterialSpinnerView
android:id="@+id/tapping_inverted" android:id="@+id/tapping_inverted"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:entries="@array/invert_tapping_mode" android:entries="@array/invert_tapping_mode"
app:title="@string/pref_read_with_tapping_inverted" /> app:title="@string/pref_read_with_tapping_inverted" />
<eu.kanade.tachiyomi.ui.reader.setting.SpinnerPreference <eu.kanade.tachiyomi.widget.MaterialSpinnerView
android:id="@+id/scale_type" android:id="@+id/scale_type"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:entries="@array/image_scale_type" android:entries="@array/image_scale_type"
app:title="@string/pref_image_scale_type" /> app:title="@string/pref_image_scale_type" />
<eu.kanade.tachiyomi.ui.reader.setting.SpinnerPreference <eu.kanade.tachiyomi.widget.MaterialSpinnerView
android:id="@+id/zoom_start" android:id="@+id/zoom_start"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"

View File

@ -10,7 +10,7 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:orientation="vertical"> android:orientation="vertical">
<eu.kanade.tachiyomi.ui.reader.setting.SpinnerPreference <eu.kanade.tachiyomi.widget.MaterialSpinnerView
android:id="@+id/viewer" android:id="@+id/viewer"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"

View File

@ -15,21 +15,21 @@
android:paddingEnd="16dp" android:paddingEnd="16dp"
android:textAppearance="@style/TextAppearance.Medium.SubHeading" /> android:textAppearance="@style/TextAppearance.Medium.SubHeading" />
<eu.kanade.tachiyomi.ui.reader.setting.SpinnerPreference <eu.kanade.tachiyomi.widget.MaterialSpinnerView
android:id="@+id/webtoon_nav" android:id="@+id/webtoon_nav"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:entries="@array/webtoon_nav" android:entries="@array/webtoon_nav"
app:title="@string/pref_viewer_nav" /> app:title="@string/pref_viewer_nav" />
<eu.kanade.tachiyomi.ui.reader.setting.SpinnerPreference <eu.kanade.tachiyomi.widget.MaterialSpinnerView
android:id="@+id/tapping_inverted" android:id="@+id/tapping_inverted"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:entries="@array/invert_tapping_mode" android:entries="@array/invert_tapping_mode"
app:title="@string/pref_read_with_tapping_inverted" /> app:title="@string/pref_read_with_tapping_inverted" />
<eu.kanade.tachiyomi.ui.reader.setting.SpinnerPreference <eu.kanade.tachiyomi.widget.MaterialSpinnerView
android:id="@+id/webtoon_side_padding" android:id="@+id/webtoon_side_padding"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"

View File

@ -1,21 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="32dp"
android:background="?attr/colorPrimary"
android:elevation="4dp"
android:gravity="center_vertical"
android:paddingStart="?android:attr/listPreferredItemPaddingStart"
android:paddingEnd="?android:attr/listPreferredItemPaddingEnd">
<TextView
android:id="@+id/section_text"
style="@style/TextAppearance.Regular.SubHeading"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:maxLines="1"
android:textColor="?attr/colorOnPrimary"
android:textStyle="bold" />
</FrameLayout>

View File

@ -1,7 +1,6 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:orientation="vertical"> android:orientation="vertical">
@ -15,19 +14,6 @@
android:paddingStart="?attr/listPreferredItemPaddingStart" android:paddingStart="?attr/listPreferredItemPaddingStart"
android:paddingEnd="?attr/listPreferredItemPaddingEnd"> android:paddingEnd="?attr/listPreferredItemPaddingEnd">
<ImageView
android:id="@+id/pull_up_bar"
android:layout_width="wrap_content"
android:layout_height="24dp"
android:alpha="0.5"
android:scaleType="fitCenter"
android:src="@drawable/ic_drag_pill_24dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:tint="?attr/colorOnSurface"
tools:ignore="ContentDescription" />
<Button <Button
android:id="@+id/reset_btn" android:id="@+id/reset_btn"
style="@style/Theme.Widget.Button" style="@style/Theme.Widget.Button"

View File

@ -12,7 +12,7 @@
android:clipToPadding="false" android:clipToPadding="false"
android:paddingTop="8dp" android:paddingTop="8dp"
android:paddingBottom="@dimen/action_toolbar_list_padding" android:paddingBottom="@dimen/action_toolbar_list_padding"
tools:listitem="@layout/source_main_controller_card_header" /> tools:listitem="@layout/section_header_item" />
<eu.kanade.tachiyomi.widget.MaterialFastScroll <eu.kanade.tachiyomi.widget.MaterialFastScroll
android:id="@+id/fast_scroller" android:id="@+id/fast_scroller"

View File

@ -630,7 +630,7 @@
<string name="action_disable_all">Zakázat vše</string> <string name="action_disable_all">Zakázat vše</string>
<string name="action_enable_all">Povolit vše</string> <string name="action_enable_all">Povolit vše</string>
<string name="action_search_settings">Nastavení vyhledávání</string> <string name="action_search_settings">Nastavení vyhledávání</string>
<string name="action_sort_date_added">Data přidání</string> <string name="action_sort_date_added">Datum přidání</string>
<string name="action_sort_last_checked">Naposledy zkontrolováno</string> <string name="action_sort_last_checked">Naposledy zkontrolováno</string>
<string name="action_filter_tracked">Sledováno</string> <string name="action_filter_tracked">Sledováno</string>
<string name="confirm_exit">Opětovným stisknutím tlačítka aplikaci opustíte</string> <string name="confirm_exit">Opětovným stisknutím tlačítka aplikaci opustíte</string>
@ -668,4 +668,15 @@
<string name="pref_category_display">Zobrazení</string> <string name="pref_category_display">Zobrazení</string>
<string name="action_display_show_tabs">Zobrazovat karty kategorií</string> <string name="action_display_show_tabs">Zobrazovat karty kategorií</string>
<string name="action_display_unread_badge">Odznáček u nepřečtených</string> <string name="action_display_unread_badge">Odznáček u nepřečtených</string>
<string name="update_check_eol">Tato verze systému Android již není podporována</string>
<string name="clipboard_copy_error">Kopírování do schránky se nezdařilo</string>
<string name="tracker_not_logged_in">Nejsi přihlášen/a: %1$s</string>
<string name="vertical_plus_viewer">Průběžné vertikální</string>
<string name="edge_nav">Okraj</string>
<string name="pref_read_with_tapping_inverted">Převrátit klepnutí</string>
<string name="pref_dual_page_split">Rozdělení na dvě stránky</string>
<string name="pref_show_navigation_mode_summary">Ukázat oblast klepnutí když je čtečka otevřená</string>
<string name="theme_system">Systém sledování</string>
<string name="action_show_errors">Zobrazit chyby</string>
<string name="action_sort_chapter_fetch_date">Datum načtení</string>
</resources> </resources>

View File

@ -672,4 +672,7 @@
<string name="pref_download_new_categories_details">Manga in ausgeschlossenen Kategorien werden nicht heruntergeladen, auch wenn sie in eingeschlossenen Kategorien vorhanden sind.</string> <string name="pref_download_new_categories_details">Manga in ausgeschlossenen Kategorien werden nicht heruntergeladen, auch wenn sie in eingeschlossenen Kategorien vorhanden sind.</string>
<string name="pref_category_auto_download">Automatisches Herunterladen</string> <string name="pref_category_auto_download">Automatisches Herunterladen</string>
<string name="pref_library_update_categories_details">Manga in ausgeschlossenen Kategorien werden nicht aktualisiert, auch wenn sie in eingeschlossenen Kategorien vorhanden sind.</string> <string name="pref_library_update_categories_details">Manga in ausgeschlossenen Kategorien werden nicht aktualisiert, auch wenn sie in eingeschlossenen Kategorien vorhanden sind.</string>
<string name="action_show_errors">Fehler anzeigen</string>
<string name="update_check_eol">Diese Android-Version wird nicht mehr unterstützt</string>
<string name="clipboard_copy_error">Kopieren in die Zwischenablage fehlgeschlagen</string>
</resources> </resources>

View File

@ -672,4 +672,7 @@
<string name="pref_download_new_categories_details">Τα Manga σε εξαιρούμενες κατηγορίες δεν θα ληφθούν ακόμα κι αν ανήκουν και σε κατηγορίες που περιλαμβάνονται.</string> <string name="pref_download_new_categories_details">Τα Manga σε εξαιρούμενες κατηγορίες δεν θα ληφθούν ακόμα κι αν ανήκουν και σε κατηγορίες που περιλαμβάνονται.</string>
<string name="pref_category_auto_download">Αυτόματη λήψη</string> <string name="pref_category_auto_download">Αυτόματη λήψη</string>
<string name="pref_library_update_categories_details">Τα manga στις αποκλεισμένες κατηγορίες δεν θα ενημερώνονται ακόμη και αν βρίσκονται επίσης στις συμπεριλαμβανόμενες κατηγορίες.</string> <string name="pref_library_update_categories_details">Τα manga στις αποκλεισμένες κατηγορίες δεν θα ενημερώνονται ακόμη και αν βρίσκονται επίσης στις συμπεριλαμβανόμενες κατηγορίες.</string>
<string name="action_show_errors">Εμφάνιση σφαλμάτων</string>
<string name="update_check_eol">Αυτή η έκδοση Android δεν υποστηρίζεται πλέον</string>
<string name="clipboard_copy_error">Απέτυχε η αντιγραφή στο πρόχειρο</string>
</resources> </resources>

View File

@ -4,15 +4,15 @@
<string name="label_recent_manga">Historio</string> <string name="label_recent_manga">Historio</string>
<string name="label_recent_updates">Ĝisdatigoj</string> <string name="label_recent_updates">Ĝisdatigoj</string>
<string name="label_library">Biblioteko</string> <string name="label_library">Biblioteko</string>
<string name="label_download_queue">Elŝutos</string> <string name="label_download_queue">Elŝutoj</string>
<string name="label_settings">Agordoj</string> <string name="label_settings">Agordoj</string>
<string name="label_more">Plu</string> <string name="label_more">Pli</string>
<string name="name">Nomo</string> <string name="name">Nomo</string>
<string name="information_no_recent">Neniuj ĝisdatigoj</string> <string name="information_no_recent">Neniuj ĝisdatigoj</string>
<string name="information_no_downloads">Neniu elŝuto</string> <string name="information_no_downloads">Neniu elŝuto</string>
<string name="label_help">Asistado</string> <string name="label_help">Asistado</string>
<string name="label_extension_info">Konektprogramaro Informaĵo</string> <string name="label_extension_info">Konektprogramaro Informaĵo</string>
<string name="label_extensions">Konektoprogramaro</string> <string name="label_extensions">Aldonaĵoj</string>
<string name="label_migration">Migri</string> <string name="label_migration">Migri</string>
<string name="label_backup">Arĥivo</string> <string name="label_backup">Arĥivo</string>
<string name="website">Retejo</string> <string name="website">Retejo</string>
@ -162,4 +162,114 @@
<string name="ext_nsfw_short">18+</string> <string name="ext_nsfw_short">18+</string>
<string name="ext_language_info">Lingvo: %1$s</string> <string name="ext_language_info">Lingvo: %1$s</string>
<string name="ext_version_info">Versio: %1$s</string> <string name="ext_version_info">Versio: %1$s</string>
<string name="nav_zone_next">Sekv\'</string>
<string name="nav_zone_prev">Antaŭ\'</string>
<string name="browse">Foliumi</string>
<string name="manga_chapters_tab">Ĉapitroj</string>
<plurals name="manga_num_chapters">
<item quantity="one">1 ĉapitro</item>
<item quantity="other">%1$s ĉapitroj</item>
</plurals>
<string name="downloaded_chapters">Elŝutitaj ĉapitroj</string>
<string name="in_library">En biblioteko</string>
<string name="library_search_hint">Titolo aŭ aŭtoro…</string>
<string name="manga_added_library">Aldonita al biblioteko</string>
<string name="add_to_library">Aldoni al biblioteko</string>
<string name="custom_dir">Propra dosierujo</string>
<string name="pref_remove_after_read">Post legi ilin</string>
<string name="pref_download_only_over_wifi">Elŝuti nur per Vifio</string>
<string name="webtoon_side_padding_25">25%</string>
<string name="webtoon_side_padding_20">20%</string>
<string name="webtoon_side_padding_15">15%</string>
<string name="webtoon_side_padding_10">10%</string>
<string name="webtoon_side_padding_0">Neniu</string>
<string name="pref_category_reading">Legada</string>
<string name="pref_category_reading_mode">Legada reĝimo</string>
<string name="color_filter_a_value">Vid</string>
<string name="color_filter_b_value">Blu</string>
<string name="color_filter_g_value">Verd</string>
<string name="color_filter_r_value">Ruĝ</string>
<string name="rotation_lock">Ŝlosita</string>
<string name="rotation_free">Libera</string>
<string name="pref_rotation_type">Orientiĝo</string>
<string name="double_tap_anim_speed_fast">Rapida</string>
<string name="double_tap_anim_speed_normal">Normala</string>
<string name="zoom_start_automatic">Aŭtomate</string>
<string name="scale_type_smart_fit">Ŝaĝa adapto</string>
<string name="scale_type_fit_height">Adapti al alto</string>
<string name="scale_type_fit_width">Adapti al larĝo</string>
<string name="vertical_plus_viewer">Seninterrompe vertikale</string>
<string name="vertical_viewer">Vertikale</string>
<string name="nav_zone_right">Dekstra</string>
<string name="nav_zone_left">Maldekstra</string>
<string name="right_and_left_nav">Dekstra kaj Maldekstra</string>
<string name="l_nav">En formo kiel \"L\"</string>
<string name="default_nav">Defaŭlte</string>
<string name="default_viewer">Defaŭlte</string>
<string name="tapping_inverted_both">Ambaŭ</string>
<string name="tapping_inverted_vertical">Vertikala</string>
<string name="tapping_inverted_horizontal">Horizontala</string>
<string name="tapping_inverted_none">Nenio</string>
<string name="pref_reader_navigation">Navigo</string>
<string name="pref_skip_read_chapters">Preterpasi ĉapitrojn markitajn kiel legitaj</string>
<string name="pref_keep_screen_on">Ne malŝalti ekranon</string>
<string name="filter_mode_screen">Ekranado</string>
<string name="filter_mode_multiply">Obligado</string>
<string name="filter_mode_overlay">Plustavolo</string>
<string name="filter_mode_default">Defaŭlta</string>
<string name="pref_custom_brightness">Adaptita lumeco</string>
<string name="pref_crop_borders">Stuci borderojn</string>
<string name="pref_true_color">32-bitaj koloroj</string>
<string name="pref_show_reading_mode">Legada reĝimo</string>
<string name="pref_show_page_number">Montri numero de paĝo</string>
<string name="pref_lock_orientation">Ŝlosi orientiĝon</string>
<string name="pref_fullscreen">Plenekrano</string>
<string name="obsolete_extension_message">Ĉi tiu aldonaĵo ne estas plu disponebla.</string>
<string name="all_lang">Ĉiuj</string>
<string name="all">Ĉio</string>
<string name="pref_library_update_prioritization">Ĝisdatiga ordigo</string>
<string name="update_48hour">Po unu fojon 2 tage</string>
<string name="update_12hour">Po unu fojon 12 hore</string>
<string name="update_8hour">Po unu fojon 8 hore</string>
<string name="update_6hour">Po unu fojon 6 hore</string>
<string name="update_4hour">Po unu fojon 4 hore</string>
<string name="update_3hour">Po unu fojon 3 hore</string>
<string name="update_2hour">Po unu fojon 2 hore</string>
<string name="update_1hour">Po unu fojon hore</string>
<string name="update_never">Mana</string>
<string name="pref_library_update_interval">Ofteco de ĝisdatigoj</string>
<string name="pref_category_library_update">Ĝisdatigoj</string>
<string name="default_columns">Defaŭlte</string>
<string name="landscape">Horizontale</string>
<string name="portrait">Vertikale</string>
<string name="pref_category_display">Montrado</string>
<string name="pref_show_nsfw_extension">Montri en aldonaĵlisto</string>
<string name="pref_show_nsfw_source">Montri en fontlisto</string>
<string name="pref_category_nsfw_content">MPL/NSFW (18+) fontoj</string>
<string name="pref_confirm_exit">Konfirmi eliron</string>
<string name="theme_dark_amoled">AMOLED-a nigra</string>
<string name="pref_category_locale">Lingvo</string>
<string name="pref_category_general">Ĝenerala</string>
<string name="app_not_available">Apo maldisponebla</string>
<string name="action_restore">Restaŭro</string>
<string name="action_show_errors">Montri erarojn</string>
<string name="action_reset">Restartigi</string>
<string name="action_sort_descending">Malkreskante</string>
<string name="action_display_show_number_of_items">Montri nombro de elementoj</string>
<string name="action_display_show_tabs">Montri kategoriajn langetojn</string>
<string name="action_display_unread_badge">Nelegitajsignoj</string>
<string name="action_display_download_badge">Elŝutosignoj</string>
<string name="action_display_grid">Kompakta krado</string>
<string name="action_display">Montrado</string>
<string name="action_display_mode">Montrada reĝimo</string>
<string name="action_open_in_web_view">Malfermi per WebView</string>
<string name="action_move">Movi</string>
<string name="action_resume">Daŭrigi</string>
<string name="action_pause">Paŭzigi</string>
<string name="action_next_unread">Sekva nelegita</string>
<string name="action_sort_down">Ordigi malkreskante</string>
<string name="action_sort_up">Ordigi kreskante</string>
<string name="action_edit_cover">Redakti kovrilon</string>
<string name="action_move_category">Aldoni al kategorioj</string>
<string name="action_global_search">Serĉi ĉie</string>
</resources> </resources>

View File

@ -672,4 +672,7 @@
<string name="pref_download_new_categories_details">Poissuljettuihin kategorioihin kuuluvia mangoja ei ladata, vaikka ne olisivat myös sisällytetyissä kategorioissa.</string> <string name="pref_download_new_categories_details">Poissuljettuihin kategorioihin kuuluvia mangoja ei ladata, vaikka ne olisivat myös sisällytetyissä kategorioissa.</string>
<string name="pref_category_auto_download">Automaattinen lataus</string> <string name="pref_category_auto_download">Automaattinen lataus</string>
<string name="pref_library_update_categories_details">Poissuljettuihin kategorioihin sisältyvää mangaa ei päivitetä, vaikka ne olisivat myös sisällytetyissä kategorioissa.</string> <string name="pref_library_update_categories_details">Poissuljettuihin kategorioihin sisältyvää mangaa ei päivitetä, vaikka ne olisivat myös sisällytetyissä kategorioissa.</string>
<string name="action_show_errors">Näytä virheet</string>
<string name="update_check_eol">Tätä Android-versiota ei enää tueta</string>
<string name="clipboard_copy_error">Kopiointi leikepöydälle epäonnistui</string>
</resources> </resources>

View File

@ -254,7 +254,7 @@
<string name="all_lang">Lahat</string> <string name="all_lang">Lahat</string>
<string name="all">Lahat</string> <string name="all">Lahat</string>
<plurals name="num_categories"> <plurals name="num_categories">
<item quantity="one">1 kategorya</item> <item quantity="one">%d kategorya</item>
<item quantity="other">%d (na) kategorya</item> <item quantity="other">%d (na) kategorya</item>
</plurals> </plurals>
<string name="default_category_summary">Palaging tanungin</string> <string name="default_category_summary">Palaging tanungin</string>
@ -526,7 +526,7 @@
<string name="username">Sagisag (username)</string> <string name="username">Sagisag (username)</string>
<string name="login_title">Mag-login sa %1$s</string> <string name="login_title">Mag-login sa %1$s</string>
<plurals name="download_queue_summary"> <plurals name="download_queue_summary">
<item quantity="one">Isa na lang</item> <item quantity="one">1 na lang</item>
<item quantity="other">%1$s na lang</item> <item quantity="other">%1$s na lang</item>
</plurals> </plurals>
<string name="downloaded_only_summary">Isalâ ang Aklatan</string> <string name="downloaded_only_summary">Isalâ ang Aklatan</string>
@ -672,4 +672,5 @@
<string name="none">Wala</string> <string name="none">Wala</string>
<string name="pref_library_update_categories_details">Di ia-update ang mga manga na nasa di kasamang kategorya kahit na nasa kasamang kategorya ang mga ito.</string> <string name="pref_library_update_categories_details">Di ia-update ang mga manga na nasa di kasamang kategorya kahit na nasa kasamang kategorya ang mga ito.</string>
<string name="action_sort_chapter_fetch_date">Petsa kinuha</string> <string name="action_sort_chapter_fetch_date">Petsa kinuha</string>
<string name="action_show_errors">Ipakita ang mga error</string>
</resources> </resources>

View File

@ -706,4 +706,7 @@
<string name="pref_library_update_categories_details">Les mangas dans les catégories exclus ne seront pas mises a jour meme s\'ils sont aussi dans les catégories inlcus.</string> <string name="pref_library_update_categories_details">Les mangas dans les catégories exclus ne seront pas mises a jour meme s\'ils sont aussi dans les catégories inlcus.</string>
<string name="pref_download_new_categories_details">Les mangas dans les catégories exclus ne seront pas mise a jour meme s\'ils sont aussi dans les catégories inclus.</string> <string name="pref_download_new_categories_details">Les mangas dans les catégories exclus ne seront pas mise a jour meme s\'ils sont aussi dans les catégories inclus.</string>
<string name="pref_category_auto_download">Téléchargement Automatique</string> <string name="pref_category_auto_download">Téléchargement Automatique</string>
<string name="action_show_errors">Afficher les erreurs</string>
<string name="update_check_eol">Cette version d\'Android n\'est plus supportée</string>
<string name="clipboard_copy_error">Échec de la copie dans le presse-papiers</string>
</resources> </resources>

View File

@ -684,4 +684,5 @@
<string name="include">Uključi: %s</string> <string name="include">Uključi: %s</string>
<string name="pref_library_update_categories_details">Manga u isključenim kategorijama neće se ažurirati čak niti ako se također nalaze u uključenim kategorijama.</string> <string name="pref_library_update_categories_details">Manga u isključenim kategorijama neće se ažurirati čak niti ako se također nalaze u uključenim kategorijama.</string>
<string name="action_sort_chapter_fetch_date">Datum preuzimanja</string> <string name="action_sort_chapter_fetch_date">Datum preuzimanja</string>
<string name="action_show_errors">Prikaži greške</string>
</resources> </resources>

View File

@ -80,7 +80,7 @@
<string name="portrait">Tegak</string> <string name="portrait">Tegak</string>
<string name="landscape">Menyamping</string> <string name="landscape">Menyamping</string>
<string name="default_columns">Asali</string> <string name="default_columns">Asali</string>
<string name="pref_library_update_interval">Frekuensi pembaruan perpustakaan</string> <string name="pref_library_update_interval">Frekuensi pembaruan</string>
<string name="update_never">Manual</string> <string name="update_never">Manual</string>
<string name="update_1hour">Tiap jam</string> <string name="update_1hour">Tiap jam</string>
<string name="update_2hour">Tiap 2 jam</string> <string name="update_2hour">Tiap 2 jam</string>
@ -91,7 +91,7 @@
<string name="update_48hour">Tiap 2 hari</string> <string name="update_48hour">Tiap 2 hari</string>
<string name="update_weekly">Tiap minggu</string> <string name="update_weekly">Tiap minggu</string>
<string name="all">Semua</string> <string name="all">Semua</string>
<string name="pref_library_update_restriction">Pembatasan pembaruan perpustakaan</string> <string name="pref_library_update_restriction">Pembatasan pembaruan</string>
<string name="pref_library_update_restriction_summary">Perbaharui hanya ketika kondisi terpenuhi</string> <string name="pref_library_update_restriction_summary">Perbaharui hanya ketika kondisi terpenuhi</string>
<string name="charging">Sedang mengisi daya</string> <string name="charging">Sedang mengisi daya</string>
<string name="pref_update_only_non_completed">Perbarui manga yang masih belum tamat saja</string> <string name="pref_update_only_non_completed">Perbarui manga yang masih belum tamat saja</string>
@ -376,7 +376,7 @@
<string name="filter_mode_lighten">Dodge / Cerahkan</string> <string name="filter_mode_lighten">Dodge / Cerahkan</string>
<string name="filter_mode_darken">Burn / Gelapkan</string> <string name="filter_mode_darken">Burn / Gelapkan</string>
<string name="label_help">Bantuan</string> <string name="label_help">Bantuan</string>
<string name="pref_library_update_prioritization">Urutan perbarui perpustakaan</string> <string name="pref_library_update_prioritization">Urutan pembaruan</string>
<string name="no_results_found">Hasil tidak ditemukan</string> <string name="no_results_found">Hasil tidak ditemukan</string>
<string name="migration_selection_prompt">Pilih sumber untuk migrasi dari</string> <string name="migration_selection_prompt">Pilih sumber untuk migrasi dari</string>
<string name="action_webview_back">Kembali</string> <string name="action_webview_back">Kembali</string>
@ -639,4 +639,7 @@
<string name="action_display_show_number_of_items">Tampilkan jumlah item</string> <string name="action_display_show_number_of_items">Tampilkan jumlah item</string>
<string name="update_8hour">Setiap 8 jam</string> <string name="update_8hour">Setiap 8 jam</string>
<string name="update_4hour">Setiap 4 jam</string> <string name="update_4hour">Setiap 4 jam</string>
<string name="none">Kosong</string>
<string name="action_show_errors">Tampilkan error</string>
<string name="action_sort_chapter_fetch_date">Tanggal diambil</string>
</resources> </resources>

View File

@ -594,7 +594,7 @@
</plurals> </plurals>
<string name="unknown_status">Stato sconosciuto</string> <string name="unknown_status">Stato sconosciuto</string>
<string name="unknown_author">Autore sconosciuto</string> <string name="unknown_author">Autore sconosciuto</string>
<string name="updated_version">Aggiornato verso v%1$s</string> <string name="updated_version">Aggiornato a v%1$s</string>
<string name="whats_new">Le novità</string> <string name="whats_new">Le novità</string>
<string name="requires_app_restart">Richiesto riavvio dell\'app per applicare le modifiche</string> <string name="requires_app_restart">Richiesto riavvio dell\'app per applicare le modifiche</string>
<string name="label_network">Rete</string> <string name="label_network">Rete</string>
@ -702,4 +702,8 @@
<string name="nav_zone_right">Destra</string> <string name="nav_zone_right">Destra</string>
<string name="nav_zone_left">Sinistra</string> <string name="nav_zone_left">Sinistra</string>
<string name="pref_library_update_categories_details">Manga che si trovano in categorie escluse non saranno aggiornati anche se si trovano in categorie incluse.</string> <string name="pref_library_update_categories_details">Manga che si trovano in categorie escluse non saranno aggiornati anche se si trovano in categorie incluse.</string>
<string name="pref_dns_over_https">DNS via HTTPS</string>
<string name="pref_show_navigation_mode">Mostra schema di navigazione</string>
<string name="pref_show_navigation_mode_summary">Mostra zone di tocco quando il lettore viene aperto</string>
<string name="action_show_errors">Mostra errori</string>
</resources> </resources>

View File

@ -70,7 +70,7 @@
<string name="portrait">縦向き</string> <string name="portrait">縦向き</string>
<string name="landscape">横向き</string> <string name="landscape">横向き</string>
<string name="default_columns">デフォルト</string> <string name="default_columns">デフォルト</string>
<string name="pref_library_update_interval">ライブラリ更新頻度</string> <string name="pref_library_update_interval">更新頻度</string>
<string name="update_never">マニュアル</string> <string name="update_never">マニュアル</string>
<string name="update_1hour">毎時間</string> <string name="update_1hour">毎時間</string>
<string name="update_2hour">2時間ごと</string> <string name="update_2hour">2時間ごと</string>
@ -81,7 +81,7 @@
<string name="update_48hour">2日ごと</string> <string name="update_48hour">2日ごと</string>
<string name="update_weekly">毎週</string> <string name="update_weekly">毎週</string>
<string name="all">すべて</string> <string name="all">すべて</string>
<string name="pref_library_update_restriction">ライブラリ更新制限</string> <string name="pref_library_update_restriction">更新制限</string>
<string name="pref_library_update_restriction_summary">条件が満たされた場合にのみ更新する</string> <string name="pref_library_update_restriction_summary">条件が満たされた場合にのみ更新する</string>
<string name="charging">充電中</string> <string name="charging">充電中</string>
<string name="pref_update_only_non_completed">連載中のマンガのみ更新</string> <string name="pref_update_only_non_completed">連載中のマンガのみ更新</string>
@ -369,7 +369,7 @@
<string name="filter_mode_default">既定</string> <string name="filter_mode_default">既定</string>
<string name="filter_mode_overlay">オーバーレイ</string> <string name="filter_mode_overlay">オーバーレイ</string>
<string name="filter_mode_screen">スクリーン</string> <string name="filter_mode_screen">スクリーン</string>
<string name="pref_library_update_prioritization">ライブラリ更新の順</string> <string name="pref_library_update_prioritization">更新の順</string>
<string name="no_results_found">結果が見つかりませんでした</string> <string name="no_results_found">結果が見つかりませんでした</string>
<string name="migration_selection_prompt">移行元を選択</string> <string name="migration_selection_prompt">移行元を選択</string>
<string name="action_webview_back">前へ</string> <string name="action_webview_back">前へ</string>
@ -380,7 +380,7 @@
<string name="ext_obsolete">廃止済み</string> <string name="ext_obsolete">廃止済み</string>
<string name="obsolete_extension_message">この拡張機能は利用不可になりました。</string> <string name="obsolete_extension_message">この拡張機能は利用不可になりました。</string>
<string name="pref_date_format">日付形式</string> <string name="pref_date_format">日付形式</string>
<string name="pref_category_library_update">更新</string> <string name="pref_category_library_update">グローバルアップデート</string>
<string name="logout_title">%1$sからログアウトしますか</string> <string name="logout_title">%1$sからログアウトしますか</string>
<string name="logout">ログアウト</string> <string name="logout">ログアウト</string>
<string name="logout_success">ログアウトしました</string> <string name="logout_success">ログアウトしました</string>
@ -402,7 +402,7 @@
<string name="theme_dark_amoled">AMOLEDブラック</string> <string name="theme_dark_amoled">AMOLEDブラック</string>
<string name="pref_manage_notifications">通知設定</string> <string name="pref_manage_notifications">通知設定</string>
<string name="pref_category_security">セキュリティ</string> <string name="pref_category_security">セキュリティ</string>
<string name="lock_with_biometrics">生体認証</string> <string name="lock_with_biometrics">アンロックを必要とする</string>
<string name="lock_when_idle">タイムアウトロック</string> <string name="lock_when_idle">タイムアウトロック</string>
<string name="lock_always">常時</string> <string name="lock_always">常時</string>
<string name="lock_never">しない</string> <string name="lock_never">しない</string>
@ -650,6 +650,15 @@
<string name="nav_zone_left"></string> <string name="nav_zone_left"></string>
<string name="nav_zone_next">次へ</string> <string name="nav_zone_next">次へ</string>
<string name="nav_zone_prev">前へ</string> <string name="nav_zone_prev">前へ</string>
<string name="pref_show_navigation_mode_summary">ビューアが立ち上がるとタップゾーンをしばらく表示します</string> <string name="pref_show_navigation_mode_summary">ビューアが立ち上がるとタップゾーンを表示します</string>
<string name="pref_show_navigation_mode">ナビゲーションレイアウトオーバーレイを表示</string> <string name="pref_show_navigation_mode">ナビゲーションレイアウトオーバーレイを表示</string>
<string name="pref_dns_over_https">DNS over HTTPS</string>
<string name="pref_download_new_categories_details">含まれているカテゴリーに入っていても、除外対象カテゴリーにあるマンガは更新されません。</string>
<string name="pref_category_auto_download">自動ダウンロード</string>
<string name="exclude">下記を除外:%s</string>
<string name="include">下記を含む:%s</string>
<string name="none">なし</string>
<string name="pref_library_update_categories_details">含まれているカテゴリーに入っていても、除外対象カテゴリーにあるマンガは更新されません。</string>
<string name="action_show_errors">エラーを表示</string>
<string name="action_sort_chapter_fetch_date">日付を取得しました</string>
</resources> </resources>

View File

@ -14,7 +14,7 @@
<string name="lock_never">ಎಂದಿಗೂ ಇಲ್ಲ</string> <string name="lock_never">ಎಂದಿಗೂ ಇಲ್ಲ</string>
<string name="lock_always">ಯಾವಾಗಲೂ</string> <string name="lock_always">ಯಾವಾಗಲೂ</string>
<string name="lock_when_idle">ನಿಷ್ಕ್ರಿಯವಾಗಿದ್ದಾಗ ಲಾಕ್ ಮಾಡಿ</string> <string name="lock_when_idle">ನಿಷ್ಕ್ರಿಯವಾಗಿದ್ದಾಗ ಲಾಕ್ ಮಾಡಿ</string>
<string name="lock_with_biometrics">"ಬೆರಳಚ್ಚ ಬದ್ರತೆ"</string> <string name="lock_with_biometrics">ಅನ್ ಲಾಕ್ ಅಗತ್ಯವಿದೆ</string>
<string name="pref_category_security">ಭದ್ರತೆ</string> <string name="pref_category_security">ಭದ್ರತೆ</string>
<string name="pref_manage_notifications">ಸೂಚನೆಗಳನ್ನು ನಿರ್ವಹಿಸಿ</string> <string name="pref_manage_notifications">ಸೂಚನೆಗಳನ್ನು ನಿರ್ವಹಿಸಿ</string>
<string name="pref_confirm_exit">ನಿರ್ಗಮನವನ್ನು ಖಚಿತಪಡಿಸಿ</string> <string name="pref_confirm_exit">ನಿರ್ಗಮನವನ್ನು ಖಚಿತಪಡಿಸಿ</string>
@ -281,8 +281,8 @@
<string name="pref_update_only_non_completed">ಚಾಲ್ತಿಯಿರುವ ಮಾಂಗಾವನ್ನು ಮಾತ್ರ ನವೀಕರಿಸಿ</string> <string name="pref_update_only_non_completed">ಚಾಲ್ತಿಯಿರುವ ಮಾಂಗಾವನ್ನು ಮಾತ್ರ ನವೀಕರಿಸಿ</string>
<string name="charging">ಚಾರ್ಜಿಂಗ್</string> <string name="charging">ಚಾರ್ಜಿಂಗ್</string>
<string name="pref_library_update_restriction_summary">ಷರತ್ತುಗಳನ್ನು ಪೂರೈಸಿದಾಗ ಮಾತ್ರ ನವೀಕರಿಸಿ</string> <string name="pref_library_update_restriction_summary">ಷರತ್ತುಗಳನ್ನು ಪೂರೈಸಿದಾಗ ಮಾತ್ರ ನವೀಕರಿಸಿ</string>
<string name="pref_library_update_restriction">ಗ್ರಂಥಾಲಯ ನವೀಕರಣ ನಿರ್ಬಂಧಗಳು</string> <string name="pref_library_update_restriction">ನವೀಕರಣ ನಿರ್ಬಂಧಗಳು</string>
<string name="pref_library_update_prioritization">ಗ್ರಂಥಾಲಯ ನವೀಕರಣ ಪಾಳಿ</string> <string name="pref_library_update_prioritization">ನವೀಕರಣ ಪಾಳಿ</string>
<string name="update_weekly">ವಾರಕ್ಕೊಮ್ಮೆ</string> <string name="update_weekly">ವಾರಕ್ಕೊಮ್ಮೆ</string>
<string name="update_48hour">ಪ್ರತಿ 2 ದಿನಗಳಿಗೊಮ್ಮೆ</string> <string name="update_48hour">ಪ್ರತಿ 2 ದಿನಗಳಿಗೊಮ್ಮೆ</string>
<string name="update_24hour">ಪ್ರತಿದಿನ</string> <string name="update_24hour">ಪ್ರತಿದಿನ</string>
@ -292,7 +292,7 @@
<string name="update_2hour">ಪ್ರತಿ 2 ಗಂಟೆಗಳಿಗೊಮ್ಮೆ</string> <string name="update_2hour">ಪ್ರತಿ 2 ಗಂಟೆಗಳಿಗೊಮ್ಮೆ</string>
<string name="update_1hour">ಗಂಟೆ</string> <string name="update_1hour">ಗಂಟೆ</string>
<string name="update_never">ಸ್ವಂತ ಮಾಡು</string> <string name="update_never">ಸ್ವಂತ ಮಾಡು</string>
<string name="pref_library_update_interval">ಗ್ರಂಥಾಲಯ ನವೀಕರಣ ಆವರ್ತನ</string> <string name="pref_library_update_interval">ನವೀಕರಣ ಆವರ್ತನ</string>
<string name="pref_category_library_update">ನವೀಕರಣಗಳು</string> <string name="pref_category_library_update">ನವೀಕರಣಗಳು</string>
<string name="default_columns">ಡೀಫಾಲ್ಟ್</string> <string name="default_columns">ಡೀಫಾಲ್ಟ್</string>
<string name="source_requires_login">ಈ ಮೂಲದ ಬಳಕೆಗೆ ಲಾಗಿನ್ ಆಗುವ ಅಗತ್ಯವಿದೆ</string> <string name="source_requires_login">ಈ ಮೂಲದ ಬಳಕೆಗೆ ಲಾಗಿನ್ ಆಗುವ ಅಗತ್ಯವಿದೆ</string>
@ -607,7 +607,7 @@
<string name="backup_restore_missing_trackers">ಟ್ರ್ಯಾಕರ್ ಗಳು ಲಾಗಿನ್ ಆಗಿಲ್ಲ:</string> <string name="backup_restore_missing_trackers">ಟ್ರ್ಯಾಕರ್ ಗಳು ಲಾಗಿನ್ ಆಗಿಲ್ಲ:</string>
<string name="pref_remove_bookmarked_chapters">ಬುಕ್ ಮಾರ್ಕ್ ಮಾಡಿದ ಅಧ್ಯಾಯಗಳನ್ನು ಅಳಿಸಿ</string> <string name="pref_remove_bookmarked_chapters">ಬುಕ್ ಮಾರ್ಕ್ ಮಾಡಿದ ಅಧ್ಯಾಯಗಳನ್ನು ಅಳಿಸಿ</string>
<string name="pref_category_delete_chapters">ಅಧ್ಯಾಯಗಳನ್ನು ಅಳಿಸಿ</string> <string name="pref_category_delete_chapters">ಅಧ್ಯಾಯಗಳನ್ನು ಅಳಿಸಿ</string>
<string name="ext_nsfw_warning">18+ ವಿಷಯವನ್ನು ಹೊಂದಿರಬಹುದು</string> <string name="ext_nsfw_warning">NSFW (18+) ವಿಷಯವನ್ನು ಹೊಂದಿರಬಹುದು</string>
<string name="ext_nsfw_short">18+</string> <string name="ext_nsfw_short">18+</string>
<string name="pref_hide_bottom_bar_on_scroll">ಸ್ಕ್ರಾಲ್‌ ಮಾಡಿದಾಗ ಕೆಳಗಿನ ಪಟ್ಟಿಯನ್ನು ಮರೆಮಾಡಿ</string> <string name="pref_hide_bottom_bar_on_scroll">ಸ್ಕ್ರಾಲ್‌ ಮಾಡಿದಾಗ ಕೆಳಗಿನ ಪಟ್ಟಿಯನ್ನು ಮರೆಮಾಡಿ</string>
<string name="action_search_settings">ಸಂಯೋಜನೆಗಳಲ್ಲಿ ಹುಡುಕಿ</string> <string name="action_search_settings">ಸಂಯೋಜನೆಗಳಲ್ಲಿ ಹುಡುಕಿ</string>
@ -622,7 +622,8 @@
<string name="pref_clear_history">ಇತಿಹಾಸವನ್ನು ತೆರವುಗೊಳಿಸಿ</string> <string name="pref_clear_history">ಇತಿಹಾಸವನ್ನು ತೆರವುಗೊಳಿಸಿ</string>
<string name="clear_history_confirmation">ನೀವು ಖಚಿತವಾಗಿರುವಿರಾ\? ಎಲ್ಲಾ ಇತಿಹಾಸವೂ ಕಳೆದುಹೋಗುತ್ತದೆ.</string> <string name="clear_history_confirmation">ನೀವು ಖಚಿತವಾಗಿರುವಿರಾ\? ಎಲ್ಲಾ ಇತಿಹಾಸವೂ ಕಳೆದುಹೋಗುತ್ತದೆ.</string>
<string name="clear_history_completed">ಇತಿಹಾಸವನ್ನು ಅಳಿಸಲಾಗಿದೆ</string> <string name="clear_history_completed">ಇತಿಹಾಸವನ್ನು ಅಳಿಸಲಾಗಿದೆ</string>
<string name="invalid_backup_file_type">ಅಮಾನ್ಯ ಬ್ಯಾಕಪ್ ಫೈಲ್:%1$s</string> <string name="invalid_backup_file_type">ಅಮಾನ್ಯ ಬ್ಯಾಕಪ್ ಫೈಲ್ ಪ್ರಕಾರ:%1$s
\nಫೈಲ್ ಪ್ರಕಾರ .proto.gz ಅಥವಾ .json ನೊಂದಿಗೆ ಕೊನೆಗೊಳ್ಳಬೇಕು.</string>
<string name="pref_backup_auto_create_legacy">ಹಳೆಯ ಪ್ರಕಾರದ ಬ್ಯಾಕಪ್ ಅನ್ನು ಸಹ ರಚಿಸಿ</string> <string name="pref_backup_auto_create_legacy">ಹಳೆಯ ಪ್ರಕಾರದ ಬ್ಯಾಕಪ್ ಅನ್ನು ಸಹ ರಚಿಸಿ</string>
<string name="pref_create_legacy_backup_summary">ತಚಿಯೋಮಿಯ ಹಳೆಯ ಆವೃತ್ತಿಗಳಲ್ಲಿ ಬಳಸಬಹುದು</string> <string name="pref_create_legacy_backup_summary">ತಚಿಯೋಮಿಯ ಹಳೆಯ ಆವೃತ್ತಿಗಳಲ್ಲಿ ಬಳಸಬಹುದು</string>
<string name="pref_create_legacy_backup">ಹಳೆಯ ಪ್ರಕಾರದ ಬ್ಯಾಕಪ್ ರಚಿಸಿ</string> <string name="pref_create_legacy_backup">ಹಳೆಯ ಪ್ರಕಾರದ ಬ್ಯಾಕಪ್ ರಚಿಸಿ</string>
@ -635,8 +636,41 @@
<string name="update_4hour">ಪ್ರತಿ ೪ ಗಂಟೆಗೆ</string> <string name="update_4hour">ಪ್ರತಿ ೪ ಗಂಟೆಗೆ</string>
<string name="action_desc">ಮೊದಲು ಚಿಕ್ಕದು</string> <string name="action_desc">ಮೊದಲು ಚಿಕ್ಕದು</string>
<string name="action_asc">ಮೊದಲ ಸಾಣ್ಣದ್ದು</string> <string name="action_asc">ಮೊದಲ ಸಾಣ್ಣದ್ದು</string>
<string name="action_order_by_chapter_number">ಅಧ್ಯಾಯ ಸಂಖ್ಯಯಂತ</string> <string name="action_order_by_chapter_number">ಅಧ್ಯಾಯ ಸಂಖ್ಯೆಯಿಂದ</string>
<string name="action_order_by_upload_date">ದಿನಾಂಕ ದಂತೆ</string> <string name="action_order_by_upload_date">ಅಪ್ಲೋಡ್ ದಿನಾಂಕ ದಂತೆ</string>
<string name="action_display_show_number_of_items">"ವಸ್ತುವಿನ ಂಖ್ಯ ತೋರಿಸಿ"</string> <string name="action_display_show_number_of_items">ವಸ್ತುಗಳ ಸಂಖ್ಯೆಯನ್ನು ತೋರಿಸಿ</string>
<string name="action_sort_chapter_fetch_date">ಪಡೆಯುಲಾದ ಮಾಹಿತಿ</string> <string name="action_sort_chapter_fetch_date">ಸಿಕ್ಕ ಮಾಹಿತಿ</string>
<string name="channel_crash_logs">ಕ್ರ್ಯಾಶ್ ಲಾಗ್ ಗಳು</string>
<string name="track_finished_reading_date">ಓದಿ ಮುಗಿಸಿದ ದಿನಾಂಕ</string>
<string name="track_started_reading_date">ಓದಲು ಪ್ರಾರಂಭಿಸಿದ ದಿನಾಂಕ</string>
<string name="crash_log_saved">ಕ್ರ್ಯಾಶ್ ಲಾಗ್‌ಗಳನ್ನು ಉಳಿಸಲಾಗಿದೆ</string>
<string name="pref_dump_crash_logs_summary">ಡೆವಲಪರ್‌ಗಳೊಂದಿಗೆ ಹಂಚಿಕೊಳ್ಳಲು ದೋಷದೆ ಲಾಗ್‌ಗಳನ್ನು ಫೈಲ್‌ಗೆ ಸೇರಿಸಿ</string>
<string name="pref_dump_crash_logs">ಕ್ರ್ಯಾಶ್ ಲಾಗ್‌ಗಳನ್ನು ಡಂಪ್ ಮಾಡಿ</string>
<string name="pref_dns_over_https">HTTPS ಮೇಲೆ DNS ಬಳಸಿ</string>
<string name="backup_restore_content_full">ಬ್ಯಾಕಪ್ ಫೈಲ್‌ನಿಂದ ಡೇಟಾವನ್ನು ಮರುಸ್ಥಾಪಿಸಲಾಗುತ್ತದೆ.
\n
\nಕಾಣೆಯಾದ ಯಾವುದೇ ವಿಸ್ತರಣೆಗಳನ್ನು ನೀವು ಪುನಃ ಸ್ಥಾಪಿಸಬೇಕಾಗುತ್ತದೆ ಮತ್ತು ಅವುಗಳನ್ನು ಬಳಸಲು ಟ್ರ್ಯಾಕಿಂಗ್ ಸೇವೆಗಳಿಗೆ ಲಾಗ್ ಇನ್ ಮಾಡಬೇಕಾಗುತ್ತದೆ.</string>
<string name="pref_download_new_categories_details">ಹೊರಗಿಡಲಾದ ವಿಭಾಗಗಳಲ್ಲಿ ಮಾಂಗಾವನ್ನು ಸೇರಿಸಿದ ವಿಭಾಗಗಳಲ್ಲಿದ್ದರೂ ಅವುಗಳನ್ನು ಡೌನ್‌ಲೋಡ್ ಮಾಡಲಾಗುವುದಿಲ್ಲ.</string>
<string name="pref_category_auto_download">ಸ್ವಯಂ ಡೌನ್‌ಲೋಡ್</string>
<string name="pref_viewer_nav">ನ್ಯಾವಿಗೇಶನ್ ಲೇಔಟ್</string>
<string name="nav_zone_right">ಬಲಕ್ಕೆ</string>
<string name="nav_zone_left">ಎಡಕ್ಕೆ</string>
<string name="nav_zone_next">ಮುಂದಿನ</string>
<string name="nav_zone_prev">ಹಿಂದಿನ</string>
<string name="right_and_left_nav">ಬಲ ಮತ್ತು ಎಡ</string>
<string name="edge_nav">ಅಂಚು</string>
<string name="kindlish_nav">ಕಿಂಡಲ್-ಇಶ್</string>
<string name="l_nav">L ಆಕಾರದ</string>
<string name="default_nav">ಪೂರ್ವನಿಯೋಜಿತ</string>
<string name="pref_dual_page_invert_summary">ಡ್ಯುಯಲ್ ಪೇಜ್ ಸ್ಪ್ಲಿಟ್ ನ ಪ್ಲೇಸ್ ಮೆಂಟ್ ಓದುವ ದಿಕ್ಕಿಗೆ ಹೊಂದಿಕೆಯಾಗದಿದ್ದರೆ</string>
<string name="pref_dual_page_invert">ಡ್ಯುಯಲ್ ಪೇಜ್ ಸ್ಪ್ಲಿಟ್ ಪ್ಲೇಸ್‌ಮೆಂಟ್ ಅನ್ನು ತಿರುಗಿಸಿ</string>
<string name="pref_dual_page_split">ಡ್ಯುಯಲ್ ಪುಟ ವಿಭಜನೆ</string>
<string name="pref_show_navigation_mode_summary">ರೀಡರ್ ತೆರೆದಾಗ ಟ್ಯಾಪ್ ವಲಯಗಳನ್ನು ತೋರಿಸಿ</string>
<string name="pref_show_navigation_mode">ನ್ಯಾವಿಗೇಶನ್ ಲೇಔಟ್ ಓವರ್ ಲೇ ತೋರಿಸಿ</string>
<string name="exclude">ಹೊರಗಿಡಿ: %s</string>
<string name="include">ಸೇರಿಸಿ: %s</string>
<string name="none">ಯಾವುದು ಅಲ್ಲ</string>
<string name="pref_library_update_categories_details">ಹೊರಗಿಡಲಾದ ವರ್ಗಗಳಲ್ಲಿನ ಮಾಂಗಾ ಸೇರಿಸಿದ ವಿಭಾಗಗಳಲ್ಲಿದ್ದರೂ ನವೀಕರಿಸಲಾಗುವುದಿಲ್ಲ.</string>
<string name="network_unmetered">ಅಳತೆಯಿಲ್ಲದ ನೆಟ್‌ವರ್ಕ್</string>
<string name="action_show_errors">ದೋಷಗಳನ್ನು ತೋರಿಸು</string>
</resources> </resources>

View File

@ -101,7 +101,7 @@
<string name="pref_auto_update_manga_sync">Kemas kini bab selepas dibaca</string> <string name="pref_auto_update_manga_sync">Kemas kini bab selepas dibaca</string>
<string name="pref_start_screen">Skrin permulaan</string> <string name="pref_start_screen">Skrin permulaan</string>
<string name="pref_language">Bahasa</string> <string name="pref_language">Bahasa</string>
<string name="system_default">Lalai</string> <string name="system_default">Sistem asal</string>
<string name="default_category">Kategori lalai</string> <string name="default_category">Kategori lalai</string>
<string name="default_category_summary">Sentiasa tanya</string> <string name="default_category_summary">Sentiasa tanya</string>
<string name="pref_fullscreen">Skrin penuh</string> <string name="pref_fullscreen">Skrin penuh</string>
@ -152,7 +152,7 @@
<string name="pref_remove_after_marked_as_read">Selepas ditandakan sebagai dibaca secara manual</string> <string name="pref_remove_after_marked_as_read">Selepas ditandakan sebagai dibaca secara manual</string>
<string name="pref_remove_after_read">Setelah membaca</string> <string name="pref_remove_after_read">Setelah membaca</string>
<string name="custom_dir">Lokasi tersuai</string> <string name="custom_dir">Lokasi tersuai</string>
<string name="disabled">Dinyahaktifkan</string> <string name="disabled">Di nyahkan</string>
<string name="last_read_chapter">Bab terakhir dibaca</string> <string name="last_read_chapter">Bab terakhir dibaca</string>
<string name="second_to_last">Bab kedua terakhir</string> <string name="second_to_last">Bab kedua terakhir</string>
<string name="third_to_last">Bab ketiga terakhir</string> <string name="third_to_last">Bab ketiga terakhir</string>
@ -212,7 +212,7 @@
<string name="local_source">Sumber lokal</string> <string name="local_source">Sumber lokal</string>
<string name="other_source">Lain</string> <string name="other_source">Lain</string>
<string name="invalid_combination">Lalai tidak boleh dipilih bersama kategori lain</string> <string name="invalid_combination">Lalai tidak boleh dipilih bersama kategori lain</string>
<string name="added_to_library">Manga ini telah ditambahkan ke pustaka anda</string> <string name="added_to_library">Ditambah ke pustaka</string>
<string name="action_global_search_hint">Carian keseluruhan…</string> <string name="action_global_search_hint">Carian keseluruhan…</string>
<string name="latest">Terkini</string> <string name="latest">Terkini</string>
<string name="browse">Semak imbas</string> <string name="browse">Semak imbas</string>
@ -232,7 +232,7 @@
<string name="chapter_downloading">Muat turun dalam progres</string> <string name="chapter_downloading">Muat turun dalam progres</string>
<string name="chapter_downloading_progress">Memuat turun (%1$d/%2$d)</string> <string name="chapter_downloading_progress">Memuat turun (%1$d/%2$d)</string>
<string name="chapter_error">Ralat</string> <string name="chapter_error">Ralat</string>
<string name="chapter_paused">Dihenti sebentar</string> <string name="chapter_paused">Ditangguh</string>
<string name="fetch_chapters_error">Tidak berhasil mendapatkan bab</string> <string name="fetch_chapters_error">Tidak berhasil mendapatkan bab</string>
<string name="show_title">Tajuk sumber</string> <string name="show_title">Tajuk sumber</string>
<string name="show_chapter_number">Nombor bab</string> <string name="show_chapter_number">Nombor bab</string>
@ -244,7 +244,7 @@
<string name="download_5">5 bab seterusnya</string> <string name="download_5">5 bab seterusnya</string>
<string name="download_10">10 bab seterusnya</string> <string name="download_10">10 bab seterusnya</string>
<string name="download_all">Semua</string> <string name="download_all">Semua</string>
<string name="download_unread">Belum dibaca</string> <string name="download_unread">Muat turun yang belum di baca</string>
<string name="confirm_delete_chapters">Adakah anda pasti ingin memadamkan bab yang dipilih\?</string> <string name="confirm_delete_chapters">Adakah anda pasti ingin memadamkan bab yang dipilih\?</string>
<string name="manga_tracking_tab">Penjejakan</string> <string name="manga_tracking_tab">Penjejakan</string>
<string name="reading">Sedang baca</string> <string name="reading">Sedang baca</string>
@ -274,7 +274,7 @@
<string name="chapter_subtitle">Bab %1$s</string> <string name="chapter_subtitle">Bab %1$s</string>
<string name="no_next_chapter">Bab seterusnya tidak dijumpai</string> <string name="no_next_chapter">Bab seterusnya tidak dijumpai</string>
<string name="no_previous_chapter">Bab sebelumnya tidak dijumpai</string> <string name="no_previous_chapter">Bab sebelumnya tidak dijumpai</string>
<string name="decode_image_error">Imej tidak dapat dimuatkan</string> <string name="decode_image_error">Imej tidak dapat di muatkan</string>
<string name="confirm_set_image_as_cover">Guna imej ini sebagai muka hadapan\?</string> <string name="confirm_set_image_as_cover">Guna imej ini sebagai muka hadapan\?</string>
<string name="download_queue_error">Memuat turun bab tidak berjaya. Anda boleh mencuba lagi di bahagian muat turun</string> <string name="download_queue_error">Memuat turun bab tidak berjaya. Anda boleh mencuba lagi di bahagian muat turun</string>
<string name="notification_update_progress">Progres kemas kini: %1$d/%2$d</string> <string name="notification_update_progress">Progres kemas kini: %1$d/%2$d</string>
@ -319,7 +319,7 @@
<string name="ext_update">Kemaskini</string> <string name="ext_update">Kemaskini</string>
<string name="ext_install">Pasang</string> <string name="ext_install">Pasang</string>
<string name="ext_pending">Masih menunggu</string> <string name="ext_pending">Masih menunggu</string>
<string name="ext_downloading">Muat turun dalam progres</string> <string name="ext_downloading">Menuat turun</string>
<string name="ext_installing">Memasang</string> <string name="ext_installing">Memasang</string>
<string name="ext_installed">Dipasang</string> <string name="ext_installed">Dipasang</string>
<string name="ext_trust">Dipercayai</string> <string name="ext_trust">Dipercayai</string>
@ -391,9 +391,9 @@
<string name="logout_title">Log keluar daripada %1$s\?</string> <string name="logout_title">Log keluar daripada %1$s\?</string>
<string name="logout">Log keluar</string> <string name="logout">Log keluar</string>
<string name="logout_success">Anda telah log keluar</string> <string name="logout_success">Anda telah log keluar</string>
<string name="currently_reading">Sedang baca</string> <string name="currently_reading">Sedang di baca</string>
<string name="paused">Ditangguh</string> <string name="paused">Ditangguh</string>
<string name="want_to_read">Ingin baca</string> <string name="want_to_read">Hendak di baca</string>
<string name="label_more">Lain-lain</string> <string name="label_more">Lain-lain</string>
<string name="action_sort_latest_chapter">Bab terkini</string> <string name="action_sort_latest_chapter">Bab terkini</string>
<string name="action_view_chapters">Buka bab</string> <string name="action_view_chapters">Buka bab</string>
@ -408,7 +408,7 @@
<string name="theme_dark_amoled">AMOLED</string> <string name="theme_dark_amoled">AMOLED</string>
<string name="pref_manage_notifications">Uruskan pemberitahuan</string> <string name="pref_manage_notifications">Uruskan pemberitahuan</string>
<string name="pref_category_security">Keselamatan</string> <string name="pref_category_security">Keselamatan</string>
<string name="lock_with_biometrics">Memerlukan buka kunci</string> <string name="lock_with_biometrics">Kunci dengan biometrik</string>
<string name="lock_when_idle">Kunci apabila terbiar</string> <string name="lock_when_idle">Kunci apabila terbiar</string>
<string name="lock_always">Selalu</string> <string name="lock_always">Selalu</string>
<string name="lock_never">Tidak</string> <string name="lock_never">Tidak</string>
@ -474,8 +474,8 @@
<string name="add_tracking">Tambah penjejakan</string> <string name="add_tracking">Tambah penjejakan</string>
<string name="manga_info_collapse">Tutup</string> <string name="manga_info_collapse">Tutup</string>
<string name="manga_info_expand">Buka</string> <string name="manga_info_expand">Buka</string>
<string name="in_library">Dalam pustaka</string> <string name="in_library">Dalam Pustaka</string>
<string name="add_to_library">Tambah ke pustaka</string> <string name="add_to_library">Tambah ke Pustaka</string>
<string name="pinned_sources">Disematkan</string> <string name="pinned_sources">Disematkan</string>
<string name="licenses">Lesen perisian sumber terbuka</string> <string name="licenses">Lesen perisian sumber terbuka</string>
<string name="website">Laman web</string> <string name="website">Laman web</string>
@ -660,4 +660,7 @@
<string name="pref_library_update_categories_details">Manga di dalam kategori berkecuali tidak akan dikemaskini walaupun ianya ada di dalam kategori hanya.</string> <string name="pref_library_update_categories_details">Manga di dalam kategori berkecuali tidak akan dikemaskini walaupun ianya ada di dalam kategori hanya.</string>
<string name="pref_download_new_categories_details">Manga di dalam kategori berkecuali tidak akan dimuat turun walaupun ianya ada di dalam kategori hanya.</string> <string name="pref_download_new_categories_details">Manga di dalam kategori berkecuali tidak akan dimuat turun walaupun ianya ada di dalam kategori hanya.</string>
<string name="pref_category_auto_download">Muat turun automatik</string> <string name="pref_category_auto_download">Muat turun automatik</string>
<string name="action_show_errors">Tunjuk ralat</string>
<string name="update_check_eol">Versi Android ini tidak lagi disokong</string>
<string name="clipboard_copy_error">Gagal menyalin ke papan keratan</string>
</resources> </resources>

View File

@ -122,8 +122,8 @@
<string name="pref_category_tracking">Sporing</string> <string name="pref_category_tracking">Sporing</string>
<string name="portrait">Stående</string> <string name="portrait">Stående</string>
<string name="landscape">Liggende</string> <string name="landscape">Liggende</string>
<string name="pref_library_update_interval">Frekvens for oppdatering av bibliotek</string> <string name="pref_library_update_interval">Oppdateringsfrekvens</string>
<string name="pref_library_update_restriction">Restriksjoner for oppdatering av bibliotek</string> <string name="pref_library_update_restriction">Oppdateringsrestriksjoner</string>
<string name="pref_library_update_restriction_summary">Kun oppdater når disse vilkårene oppfylles</string> <string name="pref_library_update_restriction_summary">Kun oppdater når disse vilkårene oppfylles</string>
<string name="pref_update_only_non_completed">Kun oppdater pågående manga</string> <string name="pref_update_only_non_completed">Kun oppdater pågående manga</string>
<string name="pref_auto_update_manga_sync">Oppdater kapittelfremdrift etter lesing</string> <string name="pref_auto_update_manga_sync">Oppdater kapittelfremdrift etter lesing</string>
@ -376,7 +376,7 @@
<string name="filter_mode_multiply">Multiplisere</string> <string name="filter_mode_multiply">Multiplisere</string>
<string name="filter_mode_lighten">Unngå / lysne</string> <string name="filter_mode_lighten">Unngå / lysne</string>
<string name="filter_mode_darken">Brenn / mørkere</string> <string name="filter_mode_darken">Brenn / mørkere</string>
<string name="pref_library_update_prioritization">Rekkefølge for biblioteksoppdatering</string> <string name="pref_library_update_prioritization">Oppdateringsrekkefølge</string>
<string name="no_results_found">Resultatløst</string> <string name="no_results_found">Resultatløst</string>
<string name="migration_selection_prompt">Velg en kilde å migrere fra</string> <string name="migration_selection_prompt">Velg en kilde å migrere fra</string>
<string name="action_webview_back">Tilbake</string> <string name="action_webview_back">Tilbake</string>
@ -387,7 +387,7 @@
<string name="ext_obsolete">Foreldet</string> <string name="ext_obsolete">Foreldet</string>
<string name="obsolete_extension_message">Denne utvidelsen er ikke lenger tilgjengelig.</string> <string name="obsolete_extension_message">Denne utvidelsen er ikke lenger tilgjengelig.</string>
<string name="pref_date_format">Datoformat</string> <string name="pref_date_format">Datoformat</string>
<string name="pref_category_library_update">Oppdateringer</string> <string name="pref_category_library_update">Global oppdatering</string>
<string name="logout_title">Logg ut fra %1$s\?</string> <string name="logout_title">Logg ut fra %1$s\?</string>
<string name="logout">Logg ut</string> <string name="logout">Logg ut</string>
<string name="logout_success">Du er utlogget</string> <string name="logout_success">Du er utlogget</string>
@ -669,4 +669,8 @@
<string name="update_8hour">Hver 8 time</string> <string name="update_8hour">Hver 8 time</string>
<string name="update_4hour">Hver 4 time</string> <string name="update_4hour">Hver 4 time</string>
<string name="action_sort_chapter_fetch_date">Dato hentet</string> <string name="action_sort_chapter_fetch_date">Dato hentet</string>
<string name="pref_download_new_categories_details">Manga i utelukkede kategorier vil ikke bli nedlastet selv om de også er i inkluderte kategorier.</string>
<string name="pref_category_auto_download">Last ned automatisk</string>
<string name="pref_library_update_categories_details">Manga i utelukkede kategorier vil ikke bli oppdatert selv om de også er i inkluderte kategorier.</string>
<string name="action_show_errors">Vis feil</string>
</resources> </resources>

View File

@ -672,4 +672,5 @@
<string name="pref_library_update_categories_details">Manga in uitgesloten categorieën worden niet bijgewerkt, zelfs niet als ze onder opgenomen categorieën vallen.</string> <string name="pref_library_update_categories_details">Manga in uitgesloten categorieën worden niet bijgewerkt, zelfs niet als ze onder opgenomen categorieën vallen.</string>
<string name="action_sort_chapter_fetch_date">Datum opgehaald</string> <string name="action_sort_chapter_fetch_date">Datum opgehaald</string>
<string name="none">Geen</string> <string name="none">Geen</string>
<string name="action_show_errors">Fouten weergeven</string>
</resources> </resources>

View File

@ -641,7 +641,7 @@
<string name="track_started_reading_date">Data de início da leitura</string> <string name="track_started_reading_date">Data de início da leitura</string>
<string name="crash_log_saved">Registros de travamento salvos</string> <string name="crash_log_saved">Registros de travamento salvos</string>
<string name="pref_dump_crash_logs_summary">Salva os registros de erro em um arquivo para o compartilhamento com os desenvolvedores</string> <string name="pref_dump_crash_logs_summary">Salva os registros de erro em um arquivo para o compartilhamento com os desenvolvedores</string>
<string name="pref_dump_crash_logs">Limpar os registros de travamentos</string> <string name="pref_dump_crash_logs">Exportar os registros de travamentos</string>
<string name="network_unmetered">Rede ilimitada</string> <string name="network_unmetered">Rede ilimitada</string>
<string name="action_desc">Decrescente</string> <string name="action_desc">Decrescente</string>
<string name="action_asc">Crescente</string> <string name="action_asc">Crescente</string>
@ -672,4 +672,7 @@
<string name="pref_library_update_categories_details">Os mangás nas categorias excluídas não serão atualizados mesmo que eles também estejam nas categorias incluídas.</string> <string name="pref_library_update_categories_details">Os mangás nas categorias excluídas não serão atualizados mesmo que eles também estejam nas categorias incluídas.</string>
<string name="pref_download_new_categories_details">Os mangás nas categorias excluídas não serão disponibilizados offline mesmo que eles também estejam nas categorias incluídas.</string> <string name="pref_download_new_categories_details">Os mangás nas categorias excluídas não serão disponibilizados offline mesmo que eles também estejam nas categorias incluídas.</string>
<string name="pref_category_auto_download">Disponibilizar offline automaticamente</string> <string name="pref_category_auto_download">Disponibilizar offline automaticamente</string>
<string name="action_show_errors">Mostrar erros</string>
<string name="update_check_eol">Esta versão do Android não é mais suportada</string>
<string name="clipboard_copy_error">Erro ao copiar para a área de transferência</string>
</resources> </resources>

View File

@ -59,7 +59,7 @@
<string name="portrait">Retrato</string> <string name="portrait">Retrato</string>
<string name="landscape">Paisagem</string> <string name="landscape">Paisagem</string>
<string name="default_columns">Padrão</string> <string name="default_columns">Padrão</string>
<string name="pref_library_update_interval">Frequência de atualização da biblioteca</string> <string name="pref_library_update_interval">Frequência de atualização</string>
<string name="update_never">Manual</string> <string name="update_never">Manual</string>
<string name="update_1hour">Hora à hora</string> <string name="update_1hour">Hora à hora</string>
<string name="update_2hour">A cada 2 horas</string> <string name="update_2hour">A cada 2 horas</string>
@ -69,7 +69,7 @@
<string name="update_24hour">Diariamente</string> <string name="update_24hour">Diariamente</string>
<string name="update_48hour">A cada 2 dias</string> <string name="update_48hour">A cada 2 dias</string>
<string name="all">Tudo</string> <string name="all">Tudo</string>
<string name="pref_library_update_restriction">Restrições sobre a atualização da biblioteca</string> <string name="pref_library_update_restriction">Restrições sobre a atualização</string>
<string name="pref_library_update_restriction_summary">Atualizar apenas quando se cumprem as condições</string> <string name="pref_library_update_restriction_summary">Atualizar apenas quando se cumprem as condições</string>
<string name="charging">A carregar</string> <string name="charging">A carregar</string>
<string name="pref_update_only_non_completed">Atualizar apenas mangás a decorrer</string> <string name="pref_update_only_non_completed">Atualizar apenas mangás a decorrer</string>
@ -181,7 +181,7 @@
<string name="sort_by_source">Por fonte</string> <string name="sort_by_source">Por fonte</string>
<string name="sort_by_number">Por número de capítulo</string> <string name="sort_by_number">Por número de capítulo</string>
<string name="manga_download">Transferir</string> <string name="manga_download">Transferir</string>
<string name="download_1">Próximo capítulo</string> <string name="download_1">Capítulo seguinte</string>
<string name="download_5">Próximos 5 capítulos</string> <string name="download_5">Próximos 5 capítulos</string>
<string name="download_10">Próximos 10 capítulos</string> <string name="download_10">Próximos 10 capítulos</string>
<string name="download_all">Tudo</string> <string name="download_all">Tudo</string>
@ -406,7 +406,7 @@
<string name="pref_color_filter_mode">Modo de mistura do filtro de cores</string> <string name="pref_color_filter_mode">Modo de mistura do filtro de cores</string>
<string name="filter_mode_lighten">Sub-exposição / Clarear</string> <string name="filter_mode_lighten">Sub-exposição / Clarear</string>
<string name="label_help">Ajuda</string> <string name="label_help">Ajuda</string>
<string name="pref_library_update_prioritization">Ordem de atualização da biblioteca</string> <string name="pref_library_update_prioritization">Ordem de atualização</string>
<string name="no_results_found">Nenhum resultado encontrado</string> <string name="no_results_found">Nenhum resultado encontrado</string>
<string name="migration_selection_prompt">Selecione uma fonte da qual migrar</string> <string name="migration_selection_prompt">Selecione uma fonte da qual migrar</string>
<string name="action_webview_back">Voltar</string> <string name="action_webview_back">Voltar</string>
@ -417,7 +417,7 @@
<string name="ext_obsolete">Obsoleto</string> <string name="ext_obsolete">Obsoleto</string>
<string name="obsolete_extension_message">Esta extensão já não está disponível.</string> <string name="obsolete_extension_message">Esta extensão já não está disponível.</string>
<string name="pref_date_format">Formato da data</string> <string name="pref_date_format">Formato da data</string>
<string name="pref_category_library_update">Atualizações</string> <string name="pref_category_library_update">Atualização global</string>
<string name="logout_title">Terminar sessão em %1$s\?</string> <string name="logout_title">Terminar sessão em %1$s\?</string>
<string name="logout">Terminar sessão</string> <string name="logout">Terminar sessão</string>
<string name="logout_success">Sua sessão está agora encerrada</string> <string name="logout_success">Sua sessão está agora encerrada</string>
@ -438,7 +438,7 @@
<string name="theme_dark_amoled">Preto AMOLED</string> <string name="theme_dark_amoled">Preto AMOLED</string>
<string name="pref_manage_notifications">Gerir notificações</string> <string name="pref_manage_notifications">Gerir notificações</string>
<string name="pref_category_security">Segurança</string> <string name="pref_category_security">Segurança</string>
<string name="lock_with_biometrics">Bloqueio com biometria</string> <string name="lock_with_biometrics">Requerer desbloqueio</string>
<string name="lock_when_idle">Bloquear automaticamente</string> <string name="lock_when_idle">Bloquear automaticamente</string>
<string name="lock_always">Sempre</string> <string name="lock_always">Sempre</string>
<string name="lock_never">Nunca</string> <string name="lock_never">Nunca</string>
@ -690,8 +690,19 @@
<string name="nav_zone_left">Esquerda</string> <string name="nav_zone_left">Esquerda</string>
<string name="nav_zone_next">Seguinte</string> <string name="nav_zone_next">Seguinte</string>
<string name="nav_zone_prev">Anterior</string> <string name="nav_zone_prev">Anterior</string>
<string name="pref_show_navigation_mode_summary">Brevemente mostrar zonas de toque quando o leitor é aberto</string> <string name="pref_show_navigation_mode_summary">Mostrar zonas de toque quando o leitor é aberto</string>
<string name="pref_show_navigation_mode">Mostrar sobreposição da disposição de navegação</string> <string name="pref_show_navigation_mode">Mostrar sobreposição da disposição de navegação</string>
<string name="update_8hour">A cada 8 horas</string> <string name="update_8hour">A cada 8 horas</string>
<string name="update_4hour">A cada 4 horas</string> <string name="update_4hour">A cada 4 horas</string>
<string name="none">Nenhum</string>
<string name="pref_dns_over_https">DNS por HTTPS</string>
<string name="pref_category_auto_download">Transferir automaticamente</string>
<string name="update_check_eol">Esta versão do Android não é mais suportada</string>
<string name="clipboard_copy_error">Falha ao copiar para a área de transferência</string>
<string name="pref_download_new_categories_details">Mangá nas categorias excluídas não será transferida mesmo que também esteja em categorias incluídas.</string>
<string name="exclude">Excluir: %s</string>
<string name="include">Incluir: %s</string>
<string name="pref_library_update_categories_details">Mangá em categorias excluídas não será atualizada mesmo que também estejam nas categorias incluídas.</string>
<string name="action_show_errors">Mostrar erros</string>
<string name="action_sort_chapter_fetch_date">Data de procura</string>
</resources> </resources>

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