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

View File

@ -2,9 +2,15 @@
I acknowledge that:
- I have updated to the latest version of the app (stable is v0.10.10)
- I have updated all extensions
- I have updated:
- 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
- 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**

View File

@ -9,9 +9,15 @@ labels: "bug"
I acknowledge that:
- I have updated to the latest version of the app (stable is v0.10.10)
- I have updated all extensions
- I have updated:
- 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
- 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**

View File

@ -9,9 +9,14 @@ labels: "feature"
I acknowledge that:
- I have updated to the latest version of the app (stable is v0.10.10)
- I have updated all extensions
- I have updated:
- 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
- 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**

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 }}
files: |
tachiyomi-${{ env.VERSION_TAG }}.apk
draft: ${{ github.event.inputs.dry-run != '' }}
draft: true
prerelease: false
env:
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 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
* 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

View File

@ -29,8 +29,8 @@ android {
minSdkVersion(AndroidConfig.minSdk)
targetSdkVersion(AndroidConfig.targetSdk)
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
versionCode = 57
versionName = "0.10.10"
versionCode = 58
versionName = "0.10.11"
buildConfigField("String", "COMMIT_COUNT", "\"${getCommitCount()}\"")
buildConfigField("String", "COMMIT_SHA", "\"${getGitSha()}\"")
@ -163,7 +163,7 @@ dependencies {
implementation("org.conscrypt:conscrypt-android:2.5.1")
// JSON
val kotlinSerializationVersion = "1.0.1"
val kotlinSerializationVersion = "1.1.0"
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:$kotlinSerializationVersion")
implementation("org.jetbrains.kotlinx:kotlinx-serialization-protobuf:$kotlinSerializationVersion")
implementation("com.google.code.gson:gson:2.8.6")
@ -174,7 +174,7 @@ dependencies {
// Disk
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")
// HTML parser
@ -260,12 +260,12 @@ dependencies {
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-android:$coroutinesVersion")
// 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 {

View File

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

View File

@ -2,17 +2,17 @@ package eu.kanade.tachiyomi.data.cache
import android.content.Context
import android.text.format.Formatter
import com.github.salomonbrys.kotson.fromJson
import com.google.gson.Gson
import com.jakewharton.disklrucache.DiskLruCache
import eu.kanade.tachiyomi.data.database.models.Chapter
import eu.kanade.tachiyomi.source.model.Page
import eu.kanade.tachiyomi.util.storage.DiskUtil
import eu.kanade.tachiyomi.util.storage.saveTo
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
import okhttp3.Response
import okio.buffer
import okio.sink
import rx.Observable
import uy.kohesive.injekt.injectLazy
import java.io.File
import java.io.IOException
@ -42,8 +42,7 @@ class ChapterCache(private val context: Context) {
const val PARAMETER_CACHE_SIZE = 100L * 1024 * 1024
}
/** Google Json class used for parsing JSON files. */
private val gson: Gson by injectLazy()
private val json: Json by injectLazy()
/** Cache class used for cache management. */
private val diskCache = DiskLruCache.open(
@ -56,7 +55,7 @@ class ChapterCache(private val context: Context) {
/**
* Returns directory of cache.
*/
val cacheDir: File
private val cacheDir: File
get() = diskCache.directory
/**
@ -71,43 +70,19 @@ class ChapterCache(private val context: Context) {
val readableSize: String
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.
*
* @param chapter the chapter.
* @return an observable of the list of pages.
* @return the list of pages.
*/
fun getPageListFromCache(chapter: Chapter): Observable<List<Page>> {
return Observable.fromCallable {
// Get the key for the chapter.
val key = DiskUtil.hashKeyForDisk(getKey(chapter))
fun getPageListFromCache(chapter: Chapter): List<Page> {
// Get the key for the chapter.
val key = DiskUtil.hashKeyForDisk(getKey(chapter))
// Convert JSON string to list of objects. Throws an exception if snapshot is null
diskCache.get(key).use {
gson.fromJson<List<Page>>(it.getString(0))
}
// Convert JSON string to list of objects. Throws an exception if snapshot is null
return diskCache.get(key).use {
json.decodeFromString(it.getString(0))
}
}
@ -119,7 +94,7 @@ class ChapterCache(private val context: Context) {
*/
fun putPageListToCache(chapter: Chapter, pages: List<Page>) {
// 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).
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 {
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) {
setLargeIcon(BitmapFactory.decodeResource(context.resources, R.mipmap.ic_launcher))
setAutoCancel(false)
setOngoing(true)
setOnlyAlertOnce(true)
}
}
@ -84,7 +83,6 @@ internal class DownloadNotifier(private val context: Context) {
*/
fun onProgressChange(download: Download) {
with(progressNotificationBuilder) {
// Check if first call.
if (!isDownloading) {
setSmallIcon(android.R.drawable.stat_sys_download)
clearActions()
@ -116,6 +114,7 @@ internal class DownloadNotifier(private val context: Context) {
}
setProgress(download.pages!!.size, download.downloadedImages, false)
setOngoing(true)
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))
setSmallIcon(R.drawable.ic_pause_24dp)
setProgress(0, 0, false)
setOngoing(false)
clearActions()
// Open download manager when clicked
setContentIntent(NotificationHandler.openDownloadManagerPendingActivity(context))

View File

@ -65,7 +65,7 @@ class DownloadProvider(private val context: Context) {
* @param source the source to query.
*/
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
import android.app.PendingIntent
import android.content.Context
import android.content.Intent
import androidx.core.app.NotificationCompat
import androidx.work.Constraints
import androidx.work.ExistingPeriodicWorkPolicy
import androidx.work.NetworkType
@ -11,52 +8,26 @@ import androidx.work.PeriodicWorkRequestBuilder
import androidx.work.WorkManager
import androidx.work.Worker
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.util.system.notificationManager
import kotlinx.coroutines.runBlocking
import java.util.concurrent.TimeUnit
class UpdaterJob(private val context: Context, workerParams: WorkerParameters) :
Worker(context, workerParams) {
override fun doWork(): Result {
return runBlocking {
try {
val result = GithubUpdateChecker().checkForUpdate()
override fun doWork() = runBlocking {
try {
val result = GithubUpdateChecker().checkForUpdate()
if (result is UpdateResult.NewUpdate<*>) {
val url = 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()
if (result is UpdateResult.NewUpdate<*>) {
UpdaterNotifier(context).promptUpdate(result.release.downloadLink)
}
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 {
private const val TAG = "UpdateChecker"

View File

@ -1,6 +1,8 @@
package eu.kanade.tachiyomi.data.updater
import android.app.PendingIntent
import android.content.Context
import android.content.Intent
import android.net.Uri
import androidx.core.app.NotificationCompat
import eu.kanade.tachiyomi.R
@ -28,6 +30,27 @@ internal class UpdaterNotifier(private val context: Context) {
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.
*
@ -63,19 +86,20 @@ internal class UpdaterNotifier(private val context: Context) {
* @param uri path location of apk.
*/
fun onDownloadFinished(uri: Uri) {
val installIntent = NotificationHandler.installApkPendingActivity(context, uri)
with(notificationBuilder) {
setContentText(context.getString(R.string.update_check_notification_download_complete))
setSmallIcon(android.R.drawable.stat_sys_download_done)
setOnlyAlertOnce(false)
setProgress(0, 0, false)
// Install action
setContentIntent(NotificationHandler.installApkPendingActivity(context, uri))
setContentIntent(installIntent)
clearActions()
addAction(
R.drawable.ic_system_update_alt_white_24dp,
context.getString(R.string.action_install),
NotificationHandler.installApkPendingActivity(context, uri)
installIntent
)
// Cancel action
addAction(
R.drawable.ic_close_24dp,
context.getString(R.string.action_cancel),
@ -96,13 +120,13 @@ internal class UpdaterNotifier(private val context: Context) {
setSmallIcon(android.R.drawable.stat_sys_warning)
setOnlyAlertOnce(false)
setProgress(0, 0, false)
// Retry action
clearActions()
addAction(
R.drawable.ic_refresh_24dp,
context.getString(R.string.action_retry),
UpdaterService.downloadApkPendingService(context, url)
)
// Cancel action
addAction(
R.drawable.ic_close_24dp,
context.getString(R.string.action_cancel),

View File

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

View File

@ -1,65 +1,39 @@
package eu.kanade.tachiyomi.ui.base.activity
import android.content.res.Configuration
import android.os.Build
import android.content.res.Configuration.UI_MODE_NIGHT_MASK
import android.content.res.Configuration.UI_MODE_NIGHT_YES
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
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 uy.kohesive.injekt.injectLazy
import eu.kanade.tachiyomi.data.preference.PreferenceValues as Values
abstract class BaseThemedActivity : AppCompatActivity() {
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?) {
setTheme(
when {
isDarkMode -> darkTheme
else -> lightTheme
val isDarkMode = when (preferences.themeMode().get()) {
ThemeMode.light -> false
ThemeMode.dark -> true
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)
}
}

View File

@ -19,7 +19,8 @@ import timber.log.Timber
abstract class BaseController<VB : ViewBinding>(bundle: Bundle? = null) :
RestoreViewOnCreateController(bundle) {
lateinit var binding: VB
protected lateinit var binding: VB
private set
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 {
return inflateView(inflater, container)
}
abstract fun createBinding(inflater: LayoutInflater): VB
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) {}

View File

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

View File

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

View File

@ -4,12 +4,12 @@ import android.annotation.SuppressLint
import android.view.View
import eu.davidea.flexibleadapter.FlexibleAdapter
import eu.davidea.viewholders.FlexibleViewHolder
import eu.kanade.tachiyomi.databinding.SourceMainControllerCardHeaderBinding
import eu.kanade.tachiyomi.databinding.SectionHeaderItemBinding
class ExtensionGroupHolder(view: View, adapter: FlexibleAdapter<*>) :
FlexibleViewHolder(view, adapter) {
private val binding = SourceMainControllerCardHeaderBinding.bind(view)
private val binding = SectionHeaderItemBinding.bind(view)
@SuppressLint("SetTextI18n")
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.
*/
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.MenuItem
import android.view.View
import android.view.ViewGroup
import androidx.appcompat.view.ContextThemeWrapper
import androidx.core.os.bundleOf
import androidx.preference.Preference
@ -65,15 +64,9 @@ class ExtensionDetailsController(bundle: Bundle? = null) :
setHasOptionsMenu(true)
}
override fun inflateView(inflater: LayoutInflater, container: ViewGroup): View {
override fun createBinding(inflater: LayoutInflater): ExtensionDetailControllerBinding {
val themedInflater = inflater.cloneInContext(getPreferenceThemeContext())
binding = ExtensionDetailControllerBinding.inflate(themedInflater)
binding.extensionPrefsRecycler.applyInsetter {
type(navigationBars = true) {
padding()
}
}
return binding.root
return ExtensionDetailControllerBinding.inflate(themedInflater)
}
override fun createPresenter(): ExtensionDetailsPresenter {
@ -88,6 +81,12 @@ class ExtensionDetailsController(bundle: Bundle? = null) :
override fun onViewCreated(view: View) {
super.onViewCreated(view)
binding.extensionPrefsRecycler.applyInsetter {
type(navigationBars = true) {
padding()
}
}
val extension = presenter.extension ?: return
val context = view.context

View File

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

View File

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

View File

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

View File

@ -5,7 +5,6 @@ import android.view.Menu
import android.view.MenuInflater
import android.view.MenuItem
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.LinearLayoutManager
import dev.chrisbanes.insetter.applyInsetter
import eu.davidea.flexibleadapter.FlexibleAdapter
@ -30,18 +29,16 @@ class MigrationSourcesController :
return MigrationSourcesPresenter()
}
override fun inflateView(inflater: LayoutInflater, container: ViewGroup): View {
binding = MigrationSourcesControllerBinding.inflate(inflater)
override fun createBinding(inflater: LayoutInflater) = MigrationSourcesControllerBinding.inflate(inflater)
override fun onViewCreated(view: View) {
super.onViewCreated(view)
binding.recycler.applyInsetter {
type(navigationBars = true) {
padding()
}
}
return binding.root
}
override fun onViewCreated(view: View) {
super.onViewCreated(view)
adapter = SourceAdapter(this)
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.viewholders.FlexibleViewHolder
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.databinding.SourceMainControllerCardHeaderBinding
import eu.kanade.tachiyomi.databinding.SectionHeaderItemBinding
/**
* Item that contains the selection header.
@ -18,7 +18,7 @@ class SelectionHeader : AbstractHeaderItem<SelectionHeader.Holder>() {
* Returns the layout resource of this item.
*/
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) {
private val binding = SourceMainControllerCardHeaderBinding.bind(view)
private val binding = SectionHeaderItemBinding.bind(view)
init {
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 eu.davidea.flexibleadapter.FlexibleAdapter
import eu.davidea.viewholders.FlexibleViewHolder
import eu.kanade.tachiyomi.databinding.SourceMainControllerCardHeaderBinding
import eu.kanade.tachiyomi.databinding.SectionHeaderItemBinding
import eu.kanade.tachiyomi.util.system.LocaleHelper
class LangHolder(view: View, adapter: FlexibleAdapter<*>) :
FlexibleViewHolder(view, adapter) {
private val binding = SourceMainControllerCardHeaderBinding.bind(view)
private val binding = SectionHeaderItemBinding.bind(view)
fun bind(item: LangItem) {
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.
*/
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.MenuItem
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.LinearLayoutManager
import com.afollestad.materialdialogs.MaterialDialog
import com.afollestad.materialdialogs.list.listItems
@ -67,25 +66,16 @@ class SourceController :
return SourcePresenter()
}
/**
* Initiate the view with [R.layout.source_main_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 = SourceMainControllerBinding.inflate(inflater)
override fun createBinding(inflater: LayoutInflater) = SourceMainControllerBinding.inflate(inflater)
override fun onViewCreated(view: View) {
super.onViewCreated(view)
binding.recycler.applyInsetter {
type(navigationBars = true) {
padding()
}
}
return binding.root
}
override fun onViewCreated(view: View) {
super.onViewCreated(view)
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.icon
import eu.kanade.tachiyomi.util.system.LocaleHelper
import eu.kanade.tachiyomi.util.system.getResourceColor
import eu.kanade.tachiyomi.util.view.setVectorCompat
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
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 {
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))
}
override fun inflateView(inflater: LayoutInflater, container: ViewGroup): View {
binding = SourceControllerBinding.inflate(inflater)
return binding.root
}
override fun createBinding(inflater: LayoutInflater) = SourceControllerBinding.inflate(inflater)
override fun onViewCreated(view: 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) {
router.popController(this)
} else {
nonSubmittedQuery = ""
searchWithQuery("")
}

View File

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

View File

@ -6,7 +6,6 @@ import android.view.Menu
import android.view.MenuInflater
import android.view.MenuItem
import android.view.View
import android.view.ViewGroup
import androidx.appcompat.widget.SearchView
import androidx.core.view.isVisible
import androidx.recyclerview.widget.LinearLayoutManager
@ -50,22 +49,7 @@ open class GlobalSearchController(
setHasOptionsMenu(true)
}
/**
* 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 createBinding(inflater: LayoutInflater) = GlobalSearchControllerBinding.inflate(inflater)
override fun getTitle(): String? {
return presenter.query
@ -142,6 +126,12 @@ open class GlobalSearchController(
override fun onViewCreated(view: View) {
super.onViewCreated(view)
binding.recycler.applyInsetter {
type(navigationBars = true) {
padding()
}
}
adapter = GlobalSearchAdapter(this)
// Create recycler and set adapter.

View File

@ -4,7 +4,6 @@ import android.view.LayoutInflater
import android.view.Menu
import android.view.MenuItem
import android.view.View
import android.view.ViewGroup
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.view.ActionMode
import androidx.recyclerview.widget.LinearLayoutManager
@ -68,21 +67,7 @@ class CategoryController :
return resources?.getString(R.string.action_edit_categories)
}
/**
* 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
}
override fun createBinding(inflater: LayoutInflater) = CategoriesControllerBinding.inflate(inflater)
/**
* Called after view inflation. Used to initialize the view.
@ -92,6 +77,12 @@ class CategoryController :
override fun onViewCreated(view: View) {
super.onViewCreated(view)
binding.recycler.applyInsetter {
type(navigationBars = true) {
padding()
}
}
adapter = CategoryAdapter(this@CategoryController)
binding.recycler.layoutManager = LinearLayoutManager(view.context)
binding.recycler.setHasFixedSize(true)

View File

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

View File

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

View File

@ -13,6 +13,7 @@ import androidx.core.view.ViewCompat
import androidx.core.view.WindowCompat
import androidx.core.view.WindowInsetsCompat
import androidx.core.view.isVisible
import androidx.core.view.marginTop
import androidx.core.view.updateLayoutParams
import androidx.lifecycle.lifecycleScope
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.util.lang.launchIO
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 kotlinx.coroutines.delay
import kotlinx.coroutines.flow.launchIn
@ -98,23 +101,27 @@ class MainActivity : BaseViewBindingActivity<MainActivityBinding>() {
padding(left = true, top = true, right = true)
}
}
binding.bottomNav.applyInsetter {
type(navigationBars = true) {
padding()
}
}
binding.rootFab.applyInsetter {
type(navigationBars = true) {
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 ->
if (insets.getInsets(WindowInsetsCompat.Type.navigationBars()).bottom > 0) {
// Keep scrim on light theme if windowLightNavigationBar is not available
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1 || isDarkMode) {
window.navigationBarColor = Color.TRANSPARENT
window.navigationBarColor = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q &&
!InternalResourceHelper.getBoolean(this, "config_navBarNeedsScrim", true)
) {
Color.TRANSPARENT
} else {
// Set navbar scrim 70% of navigationBarColor
getResourceColor(android.R.attr.navigationBarColor, 0.7F)
}
}
insets
@ -146,6 +153,9 @@ class MainActivity : BaseViewBindingActivity<MainActivityBinding>() {
val controller = router.getControllerWithTag(id.toString()) as? LibraryController
controller?.showSettingsSheet()
}
R.id.nav_updates -> {
router.pushController(DownloadController().withFadeTransaction())
}
}
}
true
@ -439,7 +449,7 @@ class MainActivity : BaseViewBindingActivity<MainActivityBinding>() {
fun fixViewToBottom(view: View) {
val listener = AppBarLayout.OnOffsetChangedListener { appBarLayout, verticalOffset ->
val maxAbsOffset = appBarLayout.measuredHeight - binding.tabs.measuredHeight
view.translationY = -maxAbsOffset - verticalOffset.toFloat()
view.translationY = -maxAbsOffset - verticalOffset.toFloat() + appBarLayout.marginTop
}
binding.appbar.addOnOffsetChangedListener(listener)
fixedViewsToBottom[view] = listener

View File

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

View File

@ -429,7 +429,11 @@ class MangaPresenter(
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()) {
true -> { c1, c2 -> c1.source_order.compareTo(c2.source_order) }
false -> { c1, c2 -> c2.source_order.compareTo(c1.source_order) }
@ -444,8 +448,6 @@ class MangaPresenter(
}
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.
*/
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
import android.text.SpannableString
import android.text.SpannableStringBuilder
import android.text.style.ForegroundColorSpan
import android.view.View
import androidx.core.text.buildSpannedString
import androidx.core.text.color
import androidx.core.view.isVisible
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.database.models.Manga
@ -59,8 +59,10 @@ class ChapterHolder(
descriptions.add(adapter.dateFormat.format(Date(chapter.date_upload)))
}
if (!chapter.read && chapter.last_page_read > 0) {
val lastPageRead = SpannableString(itemView.context.getString(R.string.chapter_progress, chapter.last_page_read + 1)).apply {
setSpan(ForegroundColorSpan(adapter.readColor), 0, length, SpannableString.SPAN_EXCLUSIVE_EXCLUSIVE)
val lastPageRead = buildSpannedString {
color(adapter.readColor) {
append(itemView.context.getString(R.string.chapter_progress, chapter.last_page_read + 1))
}
}
descriptions.add(lastPageRead)
}

View File

@ -1,7 +1,6 @@
package eu.kanade.tachiyomi.ui.more
import android.app.Dialog
import android.os.Build
import android.os.Bundle
import androidx.core.os.bundleOf
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.openInBrowser
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.toDateTimestampString
import eu.kanade.tachiyomi.util.preference.onClick
@ -138,6 +138,7 @@ class AboutController : SettingsController() {
.withAboutIconShown(false)
.withAboutVersionShown(false)
.withLicenseShown(true)
.withEdgeToEdge(true)
.start(activity!!)
}
}
@ -201,19 +202,10 @@ class AboutController : SettingsController() {
}
private fun copyDebugInfo() {
val deviceInfo =
"""
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()
activity?.copyToClipboard("Debug information", deviceInfo)
activity?.let {
val deviceInfo = CrashLogUtil(it).getDebugInfo()
activity?.copyToClipboard("Debug information", deviceInfo)
}
}
private fun getFormattedBuildTime(): String {

View File

@ -1,7 +1,11 @@
package eu.kanade.tachiyomi.ui.more
import android.content.Context
import android.os.Bundle
import android.util.AttributeSet
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.preference.Preference
import androidx.preference.PreferenceScreen
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.system.getResourceColor
import eu.kanade.tachiyomi.util.system.openInBrowser
import rx.Observable
import rx.Subscription
import rx.android.schedulers.AndroidSchedulers
import rx.subscriptions.CompositeSubscription
import uy.kohesive.injekt.injectLazy
import eu.kanade.tachiyomi.data.preference.PreferenceKeys as Keys
@ -39,6 +46,9 @@ class MoreController :
private var isDownloading: Boolean = false
private var downloadQueueSize: Int = 0
private var untilDestroySubscriptions = CompositeSubscription()
private set
override fun setupPreferenceScreen(screen: PreferenceScreen) = screen.apply {
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) {
// Handle running/paused status change
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) :
Preference(context, attrs) {

View File

@ -10,7 +10,6 @@ import android.graphics.Bitmap
import android.graphics.Color
import android.os.Build
import android.os.Bundle
import android.view.Gravity
import android.view.KeyEvent
import android.view.Menu
import android.view.MenuItem
@ -414,6 +413,11 @@ class ReaderActivity : BaseRxActivity<ReaderActivityBinding, ReaderPresenter>()
setOnClickListener {
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) {
val strings = resources.getStringArray(R.array.viewers_selector)
readingModeToast?.cancel()
readingModeToast = toast(strings[mode]) {
it.setGravity(Gravity.CENTER_VERTICAL or Gravity.CENTER_HORIZONTAL, 0, 0)
try {
val strings = resources.getStringArray(R.array.viewers_selector)
readingModeToast?.cancel()
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.
*/
override fun getPages(): Observable<List<ReaderPage>> {
return chapterCache
.getPageListFromCache(chapter.chapter)
return Observable.fromCallable { chapterCache.getPageListFromCache(chapter.chapter) }
.onErrorResumeNext { source.fetchPageList(chapter.chapter) }
.map { pages ->
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.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 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(

View File

@ -79,16 +79,7 @@ class WebtoonViewer(val activity: ReaderActivity, val isContinuous: Boolean = tr
recycler.addOnScrollListener(
object : RecyclerView.OnScrollListener() {
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
val position = 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)
}
}
onScrolled()
if (dy < 0) {
val firstIndex = layoutManager.findFirstVisibleItemPosition()
@ -243,11 +234,27 @@ class WebtoonViewer(val activity: ReaderActivity, val isContinuous: Boolean = tr
val position = adapter.items.indexOf(page)
if (position != -1) {
recycler.scrollToPosition(position)
if (layoutManager.findLastEndVisibleItemPosition() == -1) {
onScrolled(position)
}
} else {
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].
*/

View File

@ -8,13 +8,13 @@ import eu.davidea.flexibleadapter.items.AbstractHeaderItem
import eu.davidea.flexibleadapter.items.IFlexible
import eu.davidea.viewholders.FlexibleViewHolder
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.databinding.RecentSectionItemBinding
import eu.kanade.tachiyomi.databinding.SectionHeaderItemBinding
import java.util.Date
class DateSectionItem(val date: Date) : AbstractHeaderItem<DateSectionItem.DateSectionItemHolder>() {
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 {
@ -39,12 +39,12 @@ class DateSectionItem(val date: Date) : AbstractHeaderItem<DateSectionItem.DateS
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
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 {
setDisplayHeadersAtStartUp(true)
setStickyHeaders(true)
}
interface OnResumeClickListener {

View File

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

View File

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

View File

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

View File

@ -1,21 +1,19 @@
package eu.kanade.tachiyomi.ui.security
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import androidx.biometric.BiometricPrompt
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 uy.kohesive.injekt.injectLazy
import timber.log.Timber
import java.util.Date
import java.util.concurrent.Executors
/**
* Blank activity with a BiometricPrompt.
*/
class BiometricUnlockActivity : AppCompatActivity() {
class BiometricUnlockActivity : BaseThemedActivity() {
private val preferences: PreferencesHelper by injectLazy()
private val executor = Executors.newSingleThreadExecutor()
override fun onCreate(savedInstanceState: Bundle?) {
@ -27,6 +25,7 @@ class BiometricUnlockActivity : AppCompatActivity() {
object : BiometricPrompt.AuthenticationCallback() {
override fun onAuthenticationError(errorCode: Int, errString: CharSequence) {
super.onAuthenticationError(errorCode, errString)
Timber.e(errString.toString())
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.ui.base.controller.DialogController
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.intListPreference
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.system.powerManager
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 eu.kanade.tachiyomi.data.preference.PreferenceKeys as Keys
@ -172,27 +171,18 @@ class SettingsAdvancedController : SettingsController() {
private fun clearChapterCache() {
if (activity == null) return
val files = chapterCache.cacheDir.listFiles() ?: return
var deletedFiles = 0
Observable.defer { Observable.from(files) }
.doOnNext { file ->
if (chapterCache.removeFileFromCache(file.name)) {
deletedFiles++
launchIO {
try {
val deletedFiles = chapterCache.clear()
withUIContext {
activity?.toast(resources?.getString(R.string.cache_deleted, deletedFiles))
findPreference(CLEAR_CACHE_KEY)?.summary =
resources?.getString(R.string.used_cache, chapterCache.readableSize)
}
} 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() {

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.util.system.getResourceColor
import kotlinx.coroutines.MainScope
import rx.Observable
import rx.Subscription
import rx.subscriptions.CompositeSubscription
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
@ -35,19 +32,14 @@ abstract class SettingsController : PreferenceController() {
var preferenceKey: String? = null
val preferences: PreferencesHelper = Injekt.get()
val viewScope = MainScope()
var untilDestroySubscriptions = CompositeSubscription()
private set
private var themedContext: Context? = null
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup, savedInstanceState: Bundle?): View {
if (untilDestroySubscriptions.isUnsubscribed) {
untilDestroySubscriptions = CompositeSubscription()
}
val view = super.onCreateView(inflater, container, savedInstanceState)
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 {
@ -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) {
super.onDestroyView(view)
untilDestroySubscriptions.unsubscribe()
themedContext = null
}
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
setupPreferenceScreen(screen)
}
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) {
ValueAnimator
.ofObject(ArgbEvaluator(), Color.TRANSPARENT, view.context.getResourceColor(R.attr.rippleColor))
@ -111,7 +109,7 @@ abstract class SettingsController : PreferenceController() {
return preferenceScreen?.title?.toString()
}
fun setTitle() {
private fun setTitle() {
var parentController = parentController
while (parentController != null) {
if (parentController is BaseController<*> && parentController.getTitle() != null) {
@ -122,16 +120,4 @@ abstract class SettingsController : PreferenceController() {
(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.MenuItem
import android.view.View
import android.view.ViewGroup
import androidx.appcompat.widget.SearchView
import androidx.recyclerview.widget.LinearLayoutManager
import eu.kanade.tachiyomi.R
@ -33,17 +32,7 @@ class SettingsSearchController :
setHasOptionsMenu(true)
}
/**
* 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 createBinding(inflater: LayoutInflater) = SettingsSearchControllerBinding.inflate(inflater)
override fun getTitle(): String? {
return presenter.query

View File

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

View File

@ -2,15 +2,18 @@ package eu.kanade.tachiyomi.util
import android.content.Context
import android.net.Uri
import android.os.Build
import eu.kanade.tachiyomi.BuildConfig
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.notification.NotificationReceiver
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.system.createFileInCacheDir
import eu.kanade.tachiyomi.util.system.notificationBuilder
import eu.kanade.tachiyomi.util.system.notificationManager
import eu.kanade.tachiyomi.util.system.toast
import java.io.IOException
class CrashLogUtil(private val context: Context) {
@ -19,31 +22,44 @@ class CrashLogUtil(private val context: Context) {
}
fun dumpLogs() {
try {
val file = context.createFileInCacheDir("tachiyomi_crash_logs.txt")
Runtime.getRuntime().exec("logcat *:E -d -f ${file.absolutePath}")
launchIO {
try {
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))
} catch (e: IOException) {
context.toast("Failed to get logs")
showNotification(file.getUriCompat(context))
} catch (e: Throwable) {
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) {
context.notificationManager.cancel(Notifications.ID_CRASH_LOGS)
with(notificationBuilder) {
setContentTitle(context.getString(R.string.crash_log_saved))
// Clear old actions if they exist
clearActions()
addAction(
R.drawable.ic_folder_24dp,
context.getString(R.string.action_open_log),
NotificationReceiver.openErrorLogPendingActivity(context, uri)
)
addAction(
R.drawable.ic_share_24dp,
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.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
import android.content.Context
import android.os.Build
import androidx.biometric.BiometricManager
import androidx.biometric.BiometricManager.Authenticators
object BiometricUtil {
fun getSupportedAuthenticators(context: Context): Int {
if (isLegacySecured(context)) {
return Authenticators.BIOMETRIC_WEAK or Authenticators.DEVICE_CREDENTIAL
}
return listOf(
Authenticators.BIOMETRIC_STRONG,
Authenticators.BIOMETRIC_WEAK,
@ -17,10 +22,22 @@ object BiometricUtil {
}
fun isSupported(context: Context): Boolean {
return getSupportedAuthenticators(context) != 0
return isLegacySecured(context) || getSupportedAuthenticators(context) != 0
}
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
import android.app.ActivityManager
import android.app.KeyguardManager
import android.app.Notification
import android.app.NotificationManager
import android.content.BroadcastReceiver
@ -33,6 +34,7 @@ import androidx.core.net.toUri
import androidx.localbroadcastmanager.content.LocalBroadcastManager
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.util.lang.truncateCenter
import timber.log.Timber
import java.io.File
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) {
if (content.isBlank()) return
val clipboard = getSystemService<ClipboardManager>()!!
clipboard.setPrimaryClip(ClipData.newPlainText(label, content))
try {
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
get() = configuration.layoutDirection == View.LAYOUT_DIRECTION_LTR
/**
* Property to get the notification manager from the context.
*/
val Context.notificationManager: NotificationManager
get() = getSystemService()!!
/**
* Property to get the connectivity manager from the context.
*/
val Context.connectivityManager: ConnectivityManager
get() = getSystemService()!!
/**
* Property to get the power manager from the context.
*/
val Context.powerManager: PowerManager
get() = getSystemService()!!
val Context.keyguardManager: KeyguardManager
get() = getSystemService()!!
/**
* 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
import android.widget.ImageView
import androidx.annotation.AttrRes
import androidx.annotation.DrawableRes
import androidx.appcompat.content.res.AppCompatResources
import eu.kanade.tachiyomi.util.system.getResourceColor
/**
* Set a vector on a [ImageView].
*
* @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)
if (tint != null) {
vector?.mutate()
vector?.setTint(tint)
vector?.setTint(context.getResourceColor(tint))
}
setImageDrawable(vector)
}

View File

@ -12,7 +12,7 @@ import androidx.annotation.MenuRes
import androidx.appcompat.view.ActionMode
import androidx.core.view.isVisible
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.databinding.CommonActionToolbarBinding
import eu.kanade.tachiyomi.databinding.ActionToolbarBinding
/**
* 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) :
FrameLayout(context, attrs) {
private val binding: CommonActionToolbarBinding
init {
binding = CommonActionToolbarBinding.inflate(LayoutInflater.from(context), this, true)
}
private val binding = ActionToolbarBinding.inflate(LayoutInflater.from(context), this, true)
/**
* Remove menu items and remove listener.
*/
fun destroy() {
binding.commonActionMenu.menu.clear()
binding.commonActionMenu.setOnMenuItemClickListener(null)
binding.menu.menu.clear()
binding.menu.setOnMenuItemClickListener(null)
}
/**
* Gets a menu item if found.
*/
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) {
// Avoid re-inflating the menu
if (binding.commonActionMenu.menu.size() == 0) {
mode.menuInflater.inflate(menuRes, binding.commonActionMenu.menu)
binding.commonActionMenu.setOnMenuItemClickListener { listener(it) }
if (binding.menu.menu.size() == 0) {
mode.menuInflater.inflate(menuRes, binding.menu.menu)
binding.menu.setOnMenuItemClickListener { listener(it) }
}
binding.commonActionToolbar.isVisible = true
binding.actionToolbar.isVisible = true
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(
object : SimpleAnimationListener() {
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.util.AttributeSet
import android.view.Gravity
@ -7,15 +8,21 @@ import android.view.LayoutInflater
import android.view.MenuItem
import android.widget.FrameLayout
import androidx.annotation.ArrayRes
import androidx.appcompat.view.menu.MenuBuilder
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 eu.kanade.tachiyomi.R
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) {
private var entries = emptyList<String>()
private var selectedPosition = 0
private var popup: PopupMenu? = 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)
init {
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
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
binding.details.text = entries.firstOrNull().orEmpty()
@ -48,6 +64,14 @@ class SpinnerPreference @JvmOverloads constructor(context: Context, attrs: Attri
}
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()
}
@ -118,11 +142,19 @@ class SpinnerPreference @JvmOverloads constructor(context: Context, attrs: Attri
return pos
}
@SuppressLint("RestrictedApi")
fun createPopupMenu(onItemClick: (Int) -> Unit): PopupMenu {
val popup = PopupMenu(context, this, Gravity.END, R.attr.actionOverflowMenuStyle, 0)
entries.forEachIndexed { index, 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 ->
val pos = menuClicked(menuItem)
onItemClick(pos)

View File

@ -1,13 +1,10 @@
package eu.kanade.tachiyomi.widget.materialdialogs
import android.content.Context
import android.graphics.drawable.Drawable
import android.util.AttributeSet
import androidx.annotation.AttrRes
import androidx.appcompat.content.res.AppCompatResources
import androidx.appcompat.widget.AppCompatImageView
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) :
AppCompatImageView(context, attrs) {
@ -19,19 +16,11 @@ class QuadStateCheckBox @JvmOverloads constructor(context: Context, attrs: Attri
}
private fun updateDrawable() {
val drawable = when (state) {
State.UNCHECKED -> tintVector(context, R.drawable.ic_check_box_outline_blank_24dp, R.attr.colorControlNormal)
State.INDETERMINATE -> tintVector(context, R.drawable.ic_indeterminate_check_box_24dp)
State.CHECKED -> tintVector(context, R.drawable.ic_check_box_24dp)
State.INVERSED -> tintVector(context, R.drawable.ic_check_box_x_24dp)
}
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))
when (state) {
State.UNCHECKED -> setVectorCompat(R.drawable.ic_check_box_outline_blank_24dp, R.attr.colorControlNormal)
State.INDETERMINATE -> setVectorCompat(R.drawable.ic_indeterminate_check_box_24dp, R.attr.colorAccent)
State.CHECKED -> setVectorCompat(R.drawable.ic_check_box_24dp, R.attr.colorAccent)
State.INVERSED -> setVectorCompat(R.drawable.ic_check_box_x_24dp, R.attr.colorAccent)
}
}

View File

@ -8,7 +8,7 @@ import eu.kanade.tachiyomi.widget.ViewPagerAdapter
abstract class TabbedBottomSheetDialog(private val activity: Activity) : BaseBottomSheetDialog(activity) {
val binding: CommonTabbedSheetBinding = CommonTabbedSheetBinding.inflate(activity.layoutInflater)
val binding = CommonTabbedSheetBinding.inflate(activity.layoutInflater)
init {
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"
xmlns:app="http://schemas.android.com/apk/res-auto"
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_height="wrap_content"
android:clipToPadding="false"
@ -25,7 +25,7 @@
app:contentInsetStart="8dp">
<androidx.appcompat.widget.ActionMenuView
android:id="@+id/common_action_menu"
android:id="@+id/menu"
android:layout_width="match_parent"
android:layout_height="match_parent" />

View File

@ -17,7 +17,7 @@
android:clipToPadding="false"
android:paddingTop="8dp"
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>

View File

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

View File

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

View File

@ -10,14 +10,14 @@
android:layout_height="wrap_content"
android:orientation="vertical">
<eu.kanade.tachiyomi.ui.reader.setting.SpinnerPreference
<eu.kanade.tachiyomi.widget.MaterialSpinnerView
android:id="@+id/rotation_mode"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:entries="@array/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:layout_width="match_parent"
android:layout_height="wrap_content"

View File

@ -15,28 +15,28 @@
android:paddingEnd="16dp"
android:textAppearance="@style/TextAppearance.Medium.SubHeading" />
<eu.kanade.tachiyomi.ui.reader.setting.SpinnerPreference
<eu.kanade.tachiyomi.widget.MaterialSpinnerView
android:id="@+id/pager_nav"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:entries="@array/pager_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:layout_width="match_parent"
android:layout_height="wrap_content"
android:entries="@array/invert_tapping_mode"
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:layout_width="match_parent"
android:layout_height="wrap_content"
android:entries="@array/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:layout_width="match_parent"
android:layout_height="wrap_content"

View File

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

View File

@ -15,21 +15,21 @@
android:paddingEnd="16dp"
android:textAppearance="@style/TextAppearance.Medium.SubHeading" />
<eu.kanade.tachiyomi.ui.reader.setting.SpinnerPreference
<eu.kanade.tachiyomi.widget.MaterialSpinnerView
android:id="@+id/webtoon_nav"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:entries="@array/webtoon_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:layout_width="match_parent"
android:layout_height="wrap_content"
android:entries="@array/invert_tapping_mode"
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:layout_width="match_parent"
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"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
@ -15,19 +14,6 @@
android:paddingStart="?attr/listPreferredItemPaddingStart"
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
android:id="@+id/reset_btn"
style="@style/Theme.Widget.Button"

View File

@ -12,7 +12,7 @@
android:clipToPadding="false"
android:paddingTop="8dp"
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
android:id="@+id/fast_scroller"

View File

@ -630,7 +630,7 @@
<string name="action_disable_all">Zakázat 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_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_filter_tracked">Sledováno</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="action_display_show_tabs">Zobrazovat karty kategorií</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>

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

View File

@ -672,4 +672,7 @@
<string name="pref_download_new_categories_details">Τα Manga σε εξαιρούμενες κατηγορίες δεν θα ληφθούν ακόμα κι αν ανήκουν και σε κατηγορίες που περιλαμβάνονται.</string>
<string name="pref_category_auto_download">Αυτόματη λήψη</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>

View File

@ -4,15 +4,15 @@
<string name="label_recent_manga">Historio</string>
<string name="label_recent_updates">Ĝisdatigoj</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_more">Plu</string>
<string name="label_more">Pli</string>
<string name="name">Nomo</string>
<string name="information_no_recent">Neniuj ĝisdatigoj</string>
<string name="information_no_downloads">Neniu elŝuto</string>
<string name="label_help">Asistado</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_backup">Arĥivo</string>
<string name="website">Retejo</string>
@ -162,4 +162,114 @@
<string name="ext_nsfw_short">18+</string>
<string name="ext_language_info">Lingvo: %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>

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

View File

@ -254,7 +254,7 @@
<string name="all_lang">Lahat</string>
<string name="all">Lahat</string>
<plurals name="num_categories">
<item quantity="one">1 kategorya</item>
<item quantity="one">%d kategorya</item>
<item quantity="other">%d (na) kategorya</item>
</plurals>
<string name="default_category_summary">Palaging tanungin</string>
@ -526,7 +526,7 @@
<string name="username">Sagisag (username)</string>
<string name="login_title">Mag-login sa %1$s</string>
<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>
</plurals>
<string name="downloaded_only_summary">Isalâ ang Aklatan</string>
@ -672,4 +672,5 @@
<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="action_sort_chapter_fetch_date">Petsa kinuha</string>
<string name="action_show_errors">Ipakita ang mga error</string>
</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_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="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>

View File

@ -684,4 +684,5 @@
<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="action_sort_chapter_fetch_date">Datum preuzimanja</string>
<string name="action_show_errors">Prikaži greške</string>
</resources>

View File

@ -80,7 +80,7 @@
<string name="portrait">Tegak</string>
<string name="landscape">Menyamping</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_1hour">Tiap 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_weekly">Tiap minggu</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="charging">Sedang mengisi daya</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_darken">Burn / Gelapkan</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="migration_selection_prompt">Pilih sumber untuk migrasi dari</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="update_8hour">Setiap 8 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>

View File

@ -594,7 +594,7 @@
</plurals>
<string name="unknown_status">Stato 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="requires_app_restart">Richiesto riavvio dell\'app per applicare le modifiche</string>
<string name="label_network">Rete</string>
@ -702,4 +702,8 @@
<string name="nav_zone_right">Destra</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_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>

View File

@ -70,7 +70,7 @@
<string name="portrait">縦向き</string>
<string name="landscape">横向き</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_1hour">毎時間</string>
<string name="update_2hour">2時間ごと</string>
@ -81,7 +81,7 @@
<string name="update_48hour">2日ごと</string>
<string name="update_weekly">毎週</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="charging">充電中</string>
<string name="pref_update_only_non_completed">連載中のマンガのみ更新</string>
@ -369,7 +369,7 @@
<string name="filter_mode_default">既定</string>
<string name="filter_mode_overlay">オーバーレイ</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="migration_selection_prompt">移行元を選択</string>
<string name="action_webview_back">前へ</string>
@ -380,7 +380,7 @@
<string name="ext_obsolete">廃止済み</string>
<string name="obsolete_extension_message">この拡張機能は利用不可になりました。</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">ログアウト</string>
<string name="logout_success">ログアウトしました</string>
@ -402,7 +402,7 @@
<string name="theme_dark_amoled">AMOLEDブラック</string>
<string name="pref_manage_notifications">通知設定</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_always">常時</string>
<string name="lock_never">しない</string>
@ -650,6 +650,15 @@
<string name="nav_zone_left"></string>
<string name="nav_zone_next">次へ</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_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>

View File

@ -14,7 +14,7 @@
<string name="lock_never">ಎಂದಿಗೂ ಇಲ್ಲ</string>
<string name="lock_always">ಯಾವಾಗಲೂ</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_manage_notifications">ಸೂಚನೆಗಳನ್ನು ನಿರ್ವಹಿಸಿ</string>
<string name="pref_confirm_exit">ನಿರ್ಗಮನವನ್ನು ಖಚಿತಪಡಿಸಿ</string>
@ -281,8 +281,8 @@
<string name="pref_update_only_non_completed">ಚಾಲ್ತಿಯಿರುವ ಮಾಂಗಾವನ್ನು ಮಾತ್ರ ನವೀಕರಿಸಿ</string>
<string name="charging">ಚಾರ್ಜಿಂಗ್</string>
<string name="pref_library_update_restriction_summary">ಷರತ್ತುಗಳನ್ನು ಪೂರೈಸಿದಾಗ ಮಾತ್ರ ನವೀಕರಿಸಿ</string>
<string name="pref_library_update_restriction">ಗ್ರಂಥಾಲಯ ನವೀಕರಣ ನಿರ್ಬಂಧಗಳು</string>
<string name="pref_library_update_prioritization">ಗ್ರಂಥಾಲಯ ನವೀಕರಣ ಪಾಳಿ</string>
<string name="pref_library_update_restriction">ನವೀಕರಣ ನಿರ್ಬಂಧಗಳು</string>
<string name="pref_library_update_prioritization">ನವೀಕರಣ ಪಾಳಿ</string>
<string name="update_weekly">ವಾರಕ್ಕೊಮ್ಮೆ</string>
<string name="update_48hour">ಪ್ರತಿ 2 ದಿನಗಳಿಗೊಮ್ಮೆ</string>
<string name="update_24hour">ಪ್ರತಿದಿನ</string>
@ -292,7 +292,7 @@
<string name="update_2hour">ಪ್ರತಿ 2 ಗಂಟೆಗಳಿಗೊಮ್ಮೆ</string>
<string name="update_1hour">ಗಂಟೆ</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="default_columns">ಡೀಫಾಲ್ಟ್</string>
<string name="source_requires_login">ಈ ಮೂಲದ ಬಳಕೆಗೆ ಲಾಗಿನ್ ಆಗುವ ಅಗತ್ಯವಿದೆ</string>
@ -607,7 +607,7 @@
<string name="backup_restore_missing_trackers">ಟ್ರ್ಯಾಕರ್ ಗಳು ಲಾಗಿನ್ ಆಗಿಲ್ಲ:</string>
<string name="pref_remove_bookmarked_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="pref_hide_bottom_bar_on_scroll">ಸ್ಕ್ರಾಲ್‌ ಮಾಡಿದಾಗ ಕೆಳಗಿನ ಪಟ್ಟಿಯನ್ನು ಮರೆಮಾಡಿ</string>
<string name="action_search_settings">ಸಂಯೋಜನೆಗಳಲ್ಲಿ ಹುಡುಕಿ</string>
@ -622,7 +622,8 @@
<string name="pref_clear_history">ಇತಿಹಾಸವನ್ನು ತೆರವುಗೊಳಿಸಿ</string>
<string name="clear_history_confirmation">ನೀವು ಖಚಿತವಾಗಿರುವಿರಾ\? ಎಲ್ಲಾ ಇತಿಹಾಸವೂ ಕಳೆದುಹೋಗುತ್ತದೆ.</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_create_legacy_backup_summary">ತಚಿಯೋಮಿಯ ಹಳೆಯ ಆವೃತ್ತಿಗಳಲ್ಲಿ ಬಳಸಬಹುದು</string>
<string name="pref_create_legacy_backup">ಹಳೆಯ ಪ್ರಕಾರದ ಬ್ಯಾಕಪ್ ರಚಿಸಿ</string>
@ -635,8 +636,41 @@
<string name="update_4hour">ಪ್ರತಿ ೪ ಗಂಟೆಗೆ</string>
<string name="action_desc">ಮೊದಲು ಚಿಕ್ಕದು</string>
<string name="action_asc">ಮೊದಲ ಸಾಣ್ಣದ್ದು</string>
<string name="action_order_by_chapter_number">ಅಧ್ಯಾಯ ಸಂಖ್ಯಯಂತ</string>
<string name="action_order_by_upload_date">ದಿನಾಂಕ ದಂತೆ</string>
<string name="action_display_show_number_of_items">"ವಸ್ತುವಿನ ಂಖ್ಯ ತೋರಿಸಿ"</string>
<string name="action_sort_chapter_fetch_date">ಪಡೆಯುಲಾದ ಮಾಹಿತಿ</string>
<string name="action_order_by_chapter_number">ಅಧ್ಯಾಯ ಸಂಖ್ಯೆಯಿಂದ</string>
<string name="action_order_by_upload_date">ಅಪ್ಲೋಡ್ ದಿನಾಂಕ ದಂತೆ</string>
<string name="action_display_show_number_of_items">ವಸ್ತುಗಳ ಸಂಖ್ಯೆಯನ್ನು ತೋರಿಸಿ</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>

View File

@ -101,7 +101,7 @@
<string name="pref_auto_update_manga_sync">Kemas kini bab selepas dibaca</string>
<string name="pref_start_screen">Skrin permulaan</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_summary">Sentiasa tanya</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_read">Setelah membaca</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="second_to_last">Bab kedua terakhir</string>
<string name="third_to_last">Bab ketiga terakhir</string>
@ -212,7 +212,7 @@
<string name="local_source">Sumber lokal</string>
<string name="other_source">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="latest">Terkini</string>
<string name="browse">Semak imbas</string>
@ -232,7 +232,7 @@
<string name="chapter_downloading">Muat turun dalam progres</string>
<string name="chapter_downloading_progress">Memuat turun (%1$d/%2$d)</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="show_title">Tajuk sumber</string>
<string name="show_chapter_number">Nombor bab</string>
@ -244,7 +244,7 @@
<string name="download_5">5 bab seterusnya</string>
<string name="download_10">10 bab seterusnya</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="manga_tracking_tab">Penjejakan</string>
<string name="reading">Sedang baca</string>
@ -274,7 +274,7 @@
<string name="chapter_subtitle">Bab %1$s</string>
<string name="no_next_chapter">Bab seterusnya 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="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>
@ -319,7 +319,7 @@
<string name="ext_update">Kemaskini</string>
<string name="ext_install">Pasang</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_installed">Dipasang</string>
<string name="ext_trust">Dipercayai</string>
@ -391,9 +391,9 @@
<string name="logout_title">Log keluar daripada %1$s\?</string>
<string name="logout">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="want_to_read">Ingin baca</string>
<string name="want_to_read">Hendak di baca</string>
<string name="label_more">Lain-lain</string>
<string name="action_sort_latest_chapter">Bab terkini</string>
<string name="action_view_chapters">Buka bab</string>
@ -408,7 +408,7 @@
<string name="theme_dark_amoled">AMOLED</string>
<string name="pref_manage_notifications">Uruskan pemberitahuan</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_always">Selalu</string>
<string name="lock_never">Tidak</string>
@ -474,8 +474,8 @@
<string name="add_tracking">Tambah penjejakan</string>
<string name="manga_info_collapse">Tutup</string>
<string name="manga_info_expand">Buka</string>
<string name="in_library">Dalam pustaka</string>
<string name="add_to_library">Tambah ke pustaka</string>
<string name="in_library">Dalam Pustaka</string>
<string name="add_to_library">Tambah ke Pustaka</string>
<string name="pinned_sources">Disematkan</string>
<string name="licenses">Lesen perisian sumber terbuka</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_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="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>

View File

@ -122,8 +122,8 @@
<string name="pref_category_tracking">Sporing</string>
<string name="portrait">Stående</string>
<string name="landscape">Liggende</string>
<string name="pref_library_update_interval">Frekvens for oppdatering av bibliotek</string>
<string name="pref_library_update_restriction">Restriksjoner for oppdatering av bibliotek</string>
<string name="pref_library_update_interval">Oppdateringsfrekvens</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_update_only_non_completed">Kun oppdater pågående manga</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_lighten">Unngå / lysne</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="migration_selection_prompt">Velg en kilde å migrere fra</string>
<string name="action_webview_back">Tilbake</string>
@ -387,7 +387,7 @@
<string name="ext_obsolete">Foreldet</string>
<string name="obsolete_extension_message">Denne utvidelsen er ikke lenger tilgjengelig.</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">Logg ut</string>
<string name="logout_success">Du er utlogget</string>
@ -669,4 +669,8 @@
<string name="update_8hour">Hver 8 time</string>
<string name="update_4hour">Hver 4 time</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>

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="action_sort_chapter_fetch_date">Datum opgehaald</string>
<string name="none">Geen</string>
<string name="action_show_errors">Fouten weergeven</string>
</resources>

View File

@ -641,7 +641,7 @@
<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="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="action_desc">Decrescente</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_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="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>

View File

@ -59,7 +59,7 @@
<string name="portrait">Retrato</string>
<string name="landscape">Paisagem</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_1hour">Hora à hora</string>
<string name="update_2hour">A cada 2 horas</string>
@ -69,7 +69,7 @@
<string name="update_24hour">Diariamente</string>
<string name="update_48hour">A cada 2 dias</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="charging">A carregar</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_number">Por número de capítulo</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_10">Próximos 10 capítulos</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="filter_mode_lighten">Sub-exposição / Clarear</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="migration_selection_prompt">Selecione uma fonte da qual migrar</string>
<string name="action_webview_back">Voltar</string>
@ -417,7 +417,7 @@
<string name="ext_obsolete">Obsoleto</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_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">Terminar sessão</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="pref_manage_notifications">Gerir notificações</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_always">Sempre</string>
<string name="lock_never">Nunca</string>
@ -690,8 +690,19 @@
<string name="nav_zone_left">Esquerda</string>
<string name="nav_zone_next">Seguinte</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="update_8hour">A cada 8 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>

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