mirror of
https://github.com/mihonapp/mihon.git
synced 2025-07-28 02:15:52 +02:00
Compare commits
20 Commits
dbc6dac180
...
624d65d9fd
Author | SHA1 | Date | |
---|---|---|---|
624d65d9fd | |||
86805651f1 | |||
d736bec003 | |||
348b23a9fd | |||
121b2ec829 | |||
1dd130df9e | |||
e17d87f357 | |||
de75561402 | |||
58085336a5 | |||
89ea0a271b | |||
e3f33e24f5 | |||
9fd1419142 | |||
cb06898430 | |||
39407407f2 | |||
a024218410 | |||
26815c7356 | |||
e0deeb8008 | |||
38d6ab80ce | |||
78e66fd8d3 | |||
26aa126ecb |
6
.github/ISSUE_TEMPLATE/config.yml
vendored
6
.github/ISSUE_TEMPLATE/config.yml
vendored
@ -1,11 +1,5 @@
|
||||
blank_issues_enabled: false
|
||||
contact_links:
|
||||
- name: ⚠️ Extension/source issue
|
||||
url: https://github.com/tachiyomiorg/extensions/issues/new/choose
|
||||
about: Issues and requests for official extensions and sources should be opened in the extensions repository instead
|
||||
- name: 📦 Mihon extensions
|
||||
url: https://mihon.app/extensions/
|
||||
about: List of all available extensions with download links
|
||||
- name: 🖥️ Mihon website
|
||||
url: https://mihon.app/
|
||||
about: Guides, troubleshooting, and answers to common questions
|
||||
|
BIN
.github/assets/logo.png
vendored
Normal file
BIN
.github/assets/logo.png
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 7.5 KiB |
BIN
.github/readme-images/app-icon.png
vendored
BIN
.github/readme-images/app-icon.png
vendored
Binary file not shown.
Before Width: | Height: | Size: 736 B |
136
README.md
136
README.md
@ -1,89 +1,113 @@
|
||||
| Build | Stable | Weekly Beta | Support Server |
|
||||
|-------|--------|-------------|----------------|
|
||||
| [](https://github.com/mihonapp/mihon/actions/workflows/build_push.yml) | [](https://github.com/mihonapp/mihon/releases) | [](https://github.com/mihonapp/mihon-preview/releases) | [](https://discord.gg/mihon) |
|
||||
<div align="center">
|
||||
|
||||
# Mihon
|
||||
Mihon is a free and open source manga reader for Android 8.0 and above.
|
||||
<a href="https://mihon.app">
|
||||
<img src="./.github/assets/logo.png" alt="Mihon logo" title="Mihon logo" width="80"/>
|
||||
</a>
|
||||
|
||||
# Mihon [App](#)
|
||||
|
||||
### Full-featured reader
|
||||
Discover and read manga, webtoons, comics, and more – easier than ever on your Android device.
|
||||
|
||||
[](https://discord.gg/mihon)
|
||||
[](https://github.com/mihonapp/mihon/releases)
|
||||
|
||||
[](https://github.com/mihonapp/mihon/actions/workflows/build_push.yml)
|
||||
[](/LICENSE)
|
||||
[](https://hosted.weblate.org/engage/mihon/)
|
||||
|
||||
## Download
|
||||
|
||||
[](https://github.com/mihonapp/mihon/releases)
|
||||
[](https://github.com/mihonapp/mihon-preview/releases)
|
||||
|
||||
*Requires Android 8.0 or higher.*
|
||||
|
||||
## Features
|
||||
|
||||
Features include:
|
||||
* Online reading from a variety of sources
|
||||
* Local reading of downloaded content
|
||||
<div align="left">
|
||||
|
||||
* Local reading of content.
|
||||
* A configurable reader with multiple viewers, reading directions and other settings.
|
||||
* Tracker support: [MyAnimeList](https://myanimelist.net/), [AniList](https://anilist.co/), [Kitsu](https://kitsu.io/), [MangaUpdates](https://mangaupdates.com), [Shikimori](https://shikimori.one), and [Bangumi](https://bgm.tv/) support
|
||||
* Categories to organize your library
|
||||
* Light and dark themes
|
||||
* Schedule updating your library for new chapters
|
||||
* Create backups locally to read offline or to your desired cloud service
|
||||
* Tracker support: [MyAnimeList](https://myanimelist.net/), [AniList](https://anilist.co/), [Kitsu](https://kitsu.io/), [MangaUpdates](https://mangaupdates.com), [Shikimori](https://shikimori.one), and [Bangumi](https://bgm.tv/) support.
|
||||
* Categories to organize your library.
|
||||
* Light and dark themes.
|
||||
* Schedule updating your library for new chapters.
|
||||
* Create backups locally to read offline or to your desired cloud service.
|
||||
* Plus much more...
|
||||
|
||||
## Download
|
||||
Get the app from our [releases page](https://github.com/mihonapp/mihon/releases).
|
||||
</div>
|
||||
|
||||
If you want to try new features before they get to the stable release, you can download the beta version [here](https://github.com/mihonapp/mihon-preview/releases).
|
||||
## Contributing
|
||||
|
||||
## Issues, Feature Requests and Contributing
|
||||
[Code of conduct](./CODE_OF_CONDUCT.md) · [Contributing guide](./CONTRIBUTING.md)
|
||||
|
||||
Please make sure to read the full guidelines. Your issue may be closed without warning if you do not.
|
||||
Pull requests are welcome. For major changes, please open an issue first to discuss what you would like to change.
|
||||
|
||||
<details><summary>Issues</summary>
|
||||
If you got any questions, [join our Discord server](https://discord.gg/mihon).
|
||||
|
||||
1. **Before reporting a new issue, take a look at the [FAQ](https://mihon.app/docs/faq/general), the [changelog](https://mihon.app/changelogs/) and the already opened [issues](https://github.com/mihonapp/mihon/issues).**
|
||||
2. If you are unsure, ask here: [](https://discord.gg/mihon)
|
||||
<details align="center"><summary>Issues</summary><div align="left">
|
||||
|
||||
</details>
|
||||
Before reporting a new issue, take a look at the [FAQ](https://mihon.app/docs/faq/general), the [changelog](https://mihon.app/changelogs/) and the already opened [issues](https://github.com/mihonapp/mihon/issues).
|
||||
|
||||
<details><summary>Bugs</summary>
|
||||
</div></details>
|
||||
|
||||
* Include version (More → About → Version)
|
||||
* If not latest, try updating, it may have already been solved
|
||||
* Beta version is equal to the number of commits as seen on the main page
|
||||
* Include steps to reproduce (if not obvious from description)
|
||||
* Include screenshot (if needed)
|
||||
* If it could be device-dependent, try reproducing on another device (if possible)
|
||||
<details align="center"><summary>Bugs</summary><div align="left">
|
||||
|
||||
* Include version (**More → About → Version**).
|
||||
* If not latest, try updating, it may have already been solved.
|
||||
* Beta version is equal to the number of commits as seen on the main page.
|
||||
* Include steps to reproduce (if not obvious from description).
|
||||
* Include screenshot (if needed).
|
||||
* If it could be device-dependent, try reproducing on another device (if possible).
|
||||
* Don't group unrelated requests into one issue
|
||||
- **DO:** [#24](https://git.mihon.dev/tachiyomi/tachiyomi/issues/24), [#71](https://git.mihon.dev/tachiyomi/tachiyomi/issues/71)
|
||||
- **DON'T:** [#75](https://git.mihon.dev/tachiyomi/tachiyomi/issues/75)
|
||||
|
||||
</details>
|
||||
</div></details>
|
||||
|
||||
<details><summary>Feature Requests</summary>
|
||||
<details align="center"><summary>Feature requests</summary><div align="left">
|
||||
|
||||
* Write a detailed issue, explaining what it should do or how. Avoid writing just "like X app does"
|
||||
* Write a detailed issue, explaining what it should do or how.
|
||||
* Avoid writing just "like X app does";
|
||||
* Include screenshot (if needed)
|
||||
* Source requests are not accepted.
|
||||
|
||||
Source requests are not accepted.
|
||||
</details>
|
||||
</div></details>
|
||||
|
||||
<details><summary>Contributing</summary>
|
||||
### Repositories
|
||||
|
||||
See [CONTRIBUTING.md](./CONTRIBUTING.md).
|
||||
</details>
|
||||
[](https://github.com/mihonapp/website/)
|
||||
|
||||
<details><summary>Code of Conduct</summary>
|
||||
### Credits
|
||||
|
||||
See [CODE_OF_CONDUCT.md](./CODE_OF_CONDUCT.md).
|
||||
</details>
|
||||
Thank you to all the people who have contributed!
|
||||
|
||||
## FAQ
|
||||
<a href="https://github.com/mihonapp/mihon/graphs/contributors">
|
||||
<img src="https://contrib.rocks/image?repo=mihonapp/mihon" alt="Mihon app contributors" title="Mihon app contributors" width="800"/>
|
||||
</a>
|
||||
|
||||
[See our website.](https://mihon.app/)
|
||||
You can also reach out to us on [Discord](https://discord.gg/mihon).
|
||||
### Disclaimer
|
||||
|
||||
## License
|
||||
The developer(s) of this application does not have any affiliation with the content providers available, and this application hosts zero content.
|
||||
|
||||
Copyright 2015 Javier Tomás
|
||||
### License
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
```
|
||||
Copyright © 2015 Javier Tomás
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
## Disclaimer
|
||||
Modifications Copyright © 2024 The Mihon Open Source Project
|
||||
```
|
||||
|
||||
The developer of this application does not have any affiliation with the content providers available.
|
||||
</div>
|
@ -139,15 +139,15 @@ android {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation(project(":i18n"))
|
||||
implementation(project(":core"))
|
||||
implementation(project(":core-metadata"))
|
||||
implementation(project(":source-api"))
|
||||
implementation(project(":source-local"))
|
||||
implementation(project(":data"))
|
||||
implementation(project(":domain"))
|
||||
implementation(project(":presentation-core"))
|
||||
implementation(project(":presentation-widget"))
|
||||
implementation(projects.i18n)
|
||||
implementation(projects.core)
|
||||
implementation(projects.coreMetadata)
|
||||
implementation(projects.sourceApi)
|
||||
implementation(projects.sourceLocal)
|
||||
implementation(projects.data)
|
||||
implementation(projects.domain)
|
||||
implementation(projects.presentationCore)
|
||||
implementation(projects.presentationWidget)
|
||||
|
||||
// Compose
|
||||
implementation(platform(compose.bom))
|
||||
|
@ -12,5 +12,12 @@
|
||||
<path
|
||||
android:pathData="M0,0h432v432h-432z"
|
||||
android:fillColor="#2E3943"/>
|
||||
<path
|
||||
android:pathData="M322.13,215.5C322.13,272.66 274.64,319 216.07,319C157.49,319 110,272.66 110,215.5C110,158.34 157.49,112 216.07,112C274.64,112 322.13,158.34 322.13,215.5Z"
|
||||
android:fillColor="#F2FAFF"/>
|
||||
<path
|
||||
android:pathData="M216.07,299.59C263.66,299.59 302.24,261.94 302.24,215.5C302.24,169.06 263.66,131.41 216.07,131.41C168.47,131.41 129.89,169.06 129.89,215.5C129.89,261.94 168.47,299.59 216.07,299.59ZM216.07,319C274.64,319 322.13,272.66 322.13,215.5C322.13,158.34 274.64,112 216.07,112C157.49,112 110,158.34 110,215.5C110,272.66 157.49,319 216.07,319Z"
|
||||
android:fillColor="#7EBBED"
|
||||
android:fillType="evenOdd"/>
|
||||
</group>
|
||||
</vector>
|
||||
|
@ -4,13 +4,6 @@
|
||||
android:viewportWidth="432"
|
||||
android:viewportHeight="432">
|
||||
<path
|
||||
android:pathData="M337,216C337,282.83 282.83,337 216,337C149.17,337 95,282.83 95,216C95,149.17 149.17,95 216,95C282.83,95 337,149.17 337,216Z"
|
||||
android:fillColor="#F2FAFF"/>
|
||||
<path
|
||||
android:pathData="M216,314.31C270.3,314.31 314.31,270.3 314.31,216C314.31,161.7 270.3,117.69 216,117.69C161.7,117.69 117.69,161.7 117.69,216C117.69,270.3 161.7,314.31 216,314.31ZM216,337C282.83,337 337,282.83 337,216C337,149.17 282.83,95 216,95C149.17,95 95,149.17 95,216C95,282.83 149.17,337 216,337Z"
|
||||
android:fillColor="#7EBBED"
|
||||
android:fillType="evenOdd"/>
|
||||
<path
|
||||
android:pathData="M163.71,174.79L162.66,150.56C165.82,151.16 169.58,151.31 178.16,151.31C188.55,151.31 202.24,150.71 209.32,149.81C212.32,149.51 213.53,149.05 215.49,148L231.74,161.85C230.23,163.95 229.78,164.86 228.13,169.07C226.77,172.38 220.6,191.49 218.2,199.62C229.33,201.88 235.5,203.53 243.93,206.99C244.98,199.62 245.13,195.71 245.13,182.31C245.13,178.85 244.98,176.9 244.53,173.74L270.72,174.64C269.96,178.25 269.82,179.76 269.66,185.62C269.06,199.77 268.46,206.54 266.95,216.78C277.34,222.04 277.34,222.04 282.61,224.9C285.32,226.41 285.92,226.71 287.72,227.31L278.99,255.45C274.78,251.69 268.91,247.63 260.33,242.81C252.81,260.72 240.32,273.82 221.35,284.2C215.03,275.77 210.22,270.65 202.54,264.63C213.68,259.37 218.8,256.06 224.67,250.49C230.38,244.92 234.15,239.5 237.76,231.38C228.13,227.01 221.96,225.05 211.27,222.8C205.1,241.16 200.13,252.9 195.77,259.97C189.9,269.45 181.93,274.42 172.74,274.42C165.67,274.42 158.45,271.26 153.18,265.84C147.16,259.67 144,251.09 144,241.16C144,226.41 151.07,213.62 163.41,205.64C171.39,200.52 179.82,198.27 193.21,197.51C195.92,188.63 198.18,180.96 200.29,172.38C193.66,172.98 185.39,173.43 175.3,173.88C169.88,174.04 168.08,174.19 163.71,174.79ZM186.59,220.54C179.52,221.74 175.3,224 171.54,228.82C168.68,232.13 167.33,236.04 167.33,240.25C167.33,244.92 169.58,248.38 172.44,248.38C175.9,248.38 179.82,240.55 186.59,220.54Z"
|
||||
android:pathData="M182.03,188.7L181.33,172.69C183.42,173.09 185.91,173.19 191.57,173.19C198.44,173.19 207.49,172.79 212.16,172.19C214.15,171.99 214.95,171.7 216.24,171L226.98,180.15C225.98,181.54 225.68,182.14 224.59,184.92C223.7,187.11 219.62,199.74 218.03,205.11C225.39,206.6 229.46,207.7 235.03,209.98C235.73,205.11 235.83,202.52 235.83,193.67C235.83,191.39 235.73,190.09 235.43,188.01L252.74,188.6C252.24,190.99 252.14,191.98 252.04,195.86C251.64,205.21 251.24,209.68 250.25,216.45C257.11,219.93 257.11,219.93 260.59,221.82C262.38,222.81 262.78,223.01 263.97,223.41L258.2,242.01C255.42,239.52 251.54,236.83 245.87,233.65C240.9,245.49 232.65,254.14 220.12,261C215.94,255.43 212.76,252.05 207.68,248.07C215.04,244.59 218.43,242.4 222.3,238.72C226.08,235.04 228.57,231.46 230.96,226.09C224.59,223.21 220.51,221.92 213.45,220.43C209.38,232.56 206.09,240.32 203.21,244.99C199.33,251.25 194.06,254.54 187.99,254.54C183.32,254.54 178.55,252.45 175.07,248.87C171.09,244.79 169,239.12 169,232.56C169,222.81 173.67,214.36 181.83,209.09C187.1,205.71 192.67,204.21 201.52,203.72C203.31,197.85 204.8,192.78 206.19,187.11C201.82,187.51 196.35,187.81 189.68,188.1C186.1,188.2 184.91,188.3 182.03,188.7ZM197.14,218.93C192.47,219.73 189.68,221.22 187.2,224.4C185.31,226.59 184.41,229.18 184.41,231.96C184.41,235.04 185.91,237.33 187.8,237.33C190.08,237.33 192.67,232.16 197.14,218.93Z"
|
||||
android:fillColor="#031019"/>
|
||||
</vector>
|
||||
|
@ -163,7 +163,7 @@ class SyncChaptersWithSource(
|
||||
var updatedToAdd = newChapters.map { toAddItem ->
|
||||
var chapter = toAddItem.copy(dateFetch = nowMillis + itemCount--)
|
||||
|
||||
if (chapter.isRecognizedNumber.not() || chapter.chapterNumber !in deletedChapterNumbers) return@map chapter
|
||||
if (!chapter.isRecognizedNumber || chapter.chapterNumber !in deletedChapterNumbers) return@map chapter
|
||||
|
||||
chapter = chapter.copy(
|
||||
read = chapter.chapterNumber in deletedReadChapterNumbers,
|
||||
|
@ -23,7 +23,7 @@ class GetExtensionSources(
|
||||
ExtensionSourceItem(
|
||||
source = source,
|
||||
enabled = source.isEnabled(),
|
||||
labelAsName = isMultiSource && isMultiLangSingleSource.not(),
|
||||
labelAsName = isMultiSource && !isMultiLangSingleSource,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -22,9 +22,9 @@ class GetExtensionsByType(
|
||||
extensionManager.availableExtensionsFlow,
|
||||
) { _activeLanguages, _installed, _untrusted, _available ->
|
||||
val (updates, installed) = _installed
|
||||
.filter { (showNsfwSources || it.isNsfw.not()) }
|
||||
.filter { (showNsfwSources || !it.isNsfw) }
|
||||
.sortedWith(
|
||||
compareBy<Extension.Installed> { it.isObsolete.not() }
|
||||
compareBy<Extension.Installed> { !it.isObsolete }
|
||||
.thenBy(String.CASE_INSENSITIVE_ORDER) { it.name },
|
||||
)
|
||||
.partition { it.hasUpdate }
|
||||
@ -36,7 +36,7 @@ class GetExtensionsByType(
|
||||
.filter { extension ->
|
||||
_installed.none { it.pkgName == extension.pkgName } &&
|
||||
_untrusted.none { it.pkgName == extension.pkgName } &&
|
||||
(showNsfwSources || extension.isNsfw.not())
|
||||
(showNsfwSources || !extension.isNsfw)
|
||||
}
|
||||
.flatMap { ext ->
|
||||
if (ext.sources.isEmpty()) {
|
||||
|
@ -34,15 +34,15 @@ class GetSourcesWithFavoriteCount(
|
||||
when (sorting) {
|
||||
SetMigrateSorting.Mode.ALPHABETICAL -> {
|
||||
when {
|
||||
a.first.isStub && b.first.isStub.not() -> -1
|
||||
b.first.isStub && a.first.isStub.not() -> 1
|
||||
a.first.isStub && !b.first.isStub -> -1
|
||||
b.first.isStub && !a.first.isStub -> 1
|
||||
else -> a.first.name.lowercase().compareToWithCollator(b.first.name.lowercase())
|
||||
}
|
||||
}
|
||||
SetMigrateSorting.Mode.TOTAL -> {
|
||||
when {
|
||||
a.first.isStub && b.first.isStub.not() -> -1
|
||||
b.first.isStub && a.first.isStub.not() -> 1
|
||||
a.first.isStub && !b.first.isStub -> -1
|
||||
b.first.isStub && !a.first.isStub -> 1
|
||||
else -> a.second.compareTo(b.second)
|
||||
}
|
||||
}
|
||||
|
@ -30,9 +30,9 @@ class RefreshTracks(
|
||||
.map { (track, service) ->
|
||||
async {
|
||||
return@async try {
|
||||
val updatedTrack = service!!.refresh(track.toDbTrack())
|
||||
insertTrack.await(updatedTrack.toDomainTrack()!!)
|
||||
syncChapterProgressWithTrack.await(mangaId, track, service)
|
||||
val updatedTrack = service!!.refresh(track.toDbTrack()).toDomainTrack()!!
|
||||
insertTrack.await(updatedTrack)
|
||||
syncChapterProgressWithTrack.await(mangaId, updatedTrack, service)
|
||||
null
|
||||
} catch (e: Throwable) {
|
||||
service to e
|
||||
|
@ -29,7 +29,7 @@ fun Track.toDbTrack(): DbTrack = DbTrack.create(trackerId).also {
|
||||
}
|
||||
|
||||
fun DbTrack.toDomainTrack(idRequired: Boolean = true): Track? {
|
||||
val trackId = id ?: if (idRequired.not()) -1 else return null
|
||||
val trackId = id ?: if (!idRequired) -1 else return null
|
||||
return Track(
|
||||
id = trackId,
|
||||
mangaId = manga_id,
|
||||
|
@ -203,7 +203,13 @@ private fun ExtensionContent(
|
||||
items(
|
||||
items = items,
|
||||
contentType = { "item" },
|
||||
key = { "extension-${it.hashCode()}" },
|
||||
key = { item ->
|
||||
when (item.extension) {
|
||||
is Extension.Untrusted -> "extension-untrusted-${item.hashCode()}"
|
||||
is Extension.Installed -> "extension-installed-${item.hashCode()}"
|
||||
is Extension.Available -> "extension-available-${item.hashCode()}"
|
||||
}
|
||||
},
|
||||
) { item ->
|
||||
ExtensionItem(
|
||||
modifier = Modifier.animateItemPlacement(),
|
||||
|
@ -7,8 +7,11 @@ import android.net.Uri
|
||||
import androidx.activity.compose.ManagedActivityResultLauncher
|
||||
import androidx.activity.compose.rememberLauncherForActivityResult
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.compose.foundation.layout.IntrinsicSize
|
||||
import androidx.compose.foundation.layout.RowScope
|
||||
import androidx.compose.foundation.layout.fillMaxHeight
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.automirrored.outlined.HelpOutline
|
||||
@ -203,9 +206,11 @@ object SettingsDataScreen : SearchableSettings {
|
||||
MultiChoiceSegmentedButtonRow(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(intrinsicSize = IntrinsicSize.Min)
|
||||
.padding(horizontal = PrefsHorizontalPadding),
|
||||
) {
|
||||
SegmentedButton(
|
||||
modifier = Modifier.fillMaxHeight(),
|
||||
checked = false,
|
||||
onCheckedChange = { navigator.push(CreateBackupScreen()) },
|
||||
shape = SegmentedButtonDefaults.itemShape(0, 2),
|
||||
@ -213,6 +218,7 @@ object SettingsDataScreen : SearchableSettings {
|
||||
Text(stringResource(MR.strings.pref_create_backup))
|
||||
}
|
||||
SegmentedButton(
|
||||
modifier = Modifier.fillMaxHeight(),
|
||||
checked = false,
|
||||
onCheckedChange = {
|
||||
if (!BackupRestoreJob.isRunning(context)) {
|
||||
@ -394,12 +400,19 @@ object SettingsDataScreen : SearchableSettings {
|
||||
when (result) {
|
||||
GoogleDriveSyncService.DeleteSyncDataStatus.NOT_INITIALIZED -> context.toast(
|
||||
MR.strings.google_drive_not_signed_in,
|
||||
duration = 5000,
|
||||
)
|
||||
GoogleDriveSyncService.DeleteSyncDataStatus.NO_FILES -> context.toast(
|
||||
MR.strings.google_drive_sync_data_not_found,
|
||||
duration = 5000,
|
||||
)
|
||||
GoogleDriveSyncService.DeleteSyncDataStatus.SUCCESS -> context.toast(
|
||||
MR.strings.google_drive_sync_data_purged,
|
||||
duration = 5000,
|
||||
)
|
||||
GoogleDriveSyncService.DeleteSyncDataStatus.ERROR -> context.toast(
|
||||
MR.strings.google_drive_sync_data_purge_error,
|
||||
duration = 10000,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -11,6 +11,7 @@ import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlin.time.Duration.Companion.seconds
|
||||
|
||||
@Stable
|
||||
class DisplayRefreshHost {
|
||||
@ -30,15 +31,15 @@ fun DisplayRefreshHost(
|
||||
val currentDisplayRefresh = hostState.currentDisplayRefresh
|
||||
LaunchedEffect(currentDisplayRefresh) {
|
||||
if (currentDisplayRefresh) {
|
||||
delay(1500)
|
||||
delay(1.5.seconds)
|
||||
hostState.currentDisplayRefresh = false
|
||||
}
|
||||
}
|
||||
|
||||
if (currentDisplayRefresh) {
|
||||
Canvas(
|
||||
modifier = modifier.fillMaxSize(),
|
||||
) {
|
||||
Canvas(
|
||||
modifier = modifier.fillMaxSize(),
|
||||
) {
|
||||
if (currentDisplayRefresh) {
|
||||
drawRect(Color.Black)
|
||||
}
|
||||
}
|
||||
|
@ -30,6 +30,10 @@ class MangaBackupCreator(
|
||||
// Entry for this manga
|
||||
val mangaObject = manga.toBackupManga()
|
||||
|
||||
mangaObject.excludedScanlators = handler.awaitList {
|
||||
excluded_scanlatorsQueries.getExcludedScanlatorsByMangaId(manga.id)
|
||||
}
|
||||
|
||||
if (options.chapters) {
|
||||
// Backup all the chapters
|
||||
handler.awaitList {
|
||||
|
@ -38,6 +38,7 @@ data class BackupManga(
|
||||
@ProtoNumber(105) var updateStrategy: UpdateStrategy = UpdateStrategy.ALWAYS_UPDATE,
|
||||
@ProtoNumber(106) var lastModifiedAt: Long = 0,
|
||||
@ProtoNumber(107) var favoriteModifiedAt: Long? = null,
|
||||
@ProtoNumber(108) var excludedScanlators: List<String> = emptyList(),
|
||||
) {
|
||||
fun getMangaImpl(): Manga {
|
||||
return Manga.create().copy(
|
||||
|
@ -74,6 +74,7 @@ class MangaRestorer(
|
||||
backupCategories = backupCategories,
|
||||
history = backupManga.history + backupManga.brokenHistory.map { it.toBackupHistory() },
|
||||
tracks = backupManga.tracking,
|
||||
excludedScanlators = backupManga.excludedScanlators,
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -265,11 +266,13 @@ class MangaRestorer(
|
||||
backupCategories: List<BackupCategory>,
|
||||
history: List<BackupHistory>,
|
||||
tracks: List<BackupTracking>,
|
||||
excludedScanlators: List<String>,
|
||||
): Manga {
|
||||
restoreCategories(manga, categories, backupCategories)
|
||||
restoreChapters(manga, chapters)
|
||||
restoreTracking(manga, tracks)
|
||||
restoreHistory(history)
|
||||
restoreExcludedScanlators(manga, excludedScanlators)
|
||||
updateManga.awaitUpdateFetchInterval(manga, now, currentFetchWindow)
|
||||
return manga
|
||||
}
|
||||
@ -402,4 +405,25 @@ class MangaRestorer(
|
||||
}
|
||||
|
||||
private fun Track.forComparison() = this.copy(id = 0L, mangaId = 0L)
|
||||
|
||||
/**
|
||||
* Restores the excluded scanlators for the manga.
|
||||
*
|
||||
* @param manga the manga whose excluded scanlators have to be restored.
|
||||
* @param excludedScanlators the excluded scanlators to restore.
|
||||
*/
|
||||
private suspend fun restoreExcludedScanlators(manga: Manga, excludedScanlators: List<String>) {
|
||||
if (excludedScanlators.isEmpty()) return
|
||||
val existingExcludedScanlators = handler.awaitList {
|
||||
excluded_scanlatorsQueries.getExcludedScanlatorsByMangaId(manga.id)
|
||||
}
|
||||
val toInsert = excludedScanlators.filter { it !in existingExcludedScanlators }
|
||||
if (toInsert.isNotEmpty()) {
|
||||
handler.await {
|
||||
toInsert.forEach {
|
||||
excluded_scanlatorsQueries.insert(manga.id, it)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -315,13 +315,13 @@ class DownloadManager(
|
||||
val capitalizationChanged = oldFolder.name.equals(newName, ignoreCase = true)
|
||||
if (capitalizationChanged) {
|
||||
val tempName = newName + Downloader.TMP_DIR_SUFFIX
|
||||
if (oldFolder.renameTo(tempName).not()) {
|
||||
if (!oldFolder.renameTo(tempName)) {
|
||||
logcat(LogPriority.ERROR) { "Failed to rename source download folder: ${oldFolder.name}" }
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if (oldFolder.renameTo(newName).not()) {
|
||||
if (!oldFolder.renameTo(newName)) {
|
||||
logcat(LogPriority.ERROR) { "Failed to rename source download folder: ${oldFolder.name}" }
|
||||
}
|
||||
}
|
||||
|
@ -130,7 +130,7 @@ class DownloadProvider(
|
||||
val newChapterName = sanitizeChapterName(chapterName)
|
||||
return DiskUtil.buildValidFilename(
|
||||
when {
|
||||
chapterScanlator.isNullOrBlank().not() -> "${chapterScanlator}_$newChapterName"
|
||||
!chapterScanlator.isNullOrBlank() -> "${chapterScanlator}_$newChapterName"
|
||||
else -> newChapterName
|
||||
},
|
||||
)
|
||||
|
@ -56,18 +56,22 @@ class GoogleDriveSyncService(context: Context, json: Json, syncPreferences: Sync
|
||||
NOT_INITIALIZED,
|
||||
NO_FILES,
|
||||
SUCCESS,
|
||||
ERROR,
|
||||
}
|
||||
|
||||
private val remoteFileName = "tachiyomi_sync_data.gz"
|
||||
private val appName = context.stringResource(MR.strings.app_name)
|
||||
|
||||
private val lockFileName = "tachiyomi_sync.lock"
|
||||
private val remoteFileName = "${appName}_sync_data.gz"
|
||||
|
||||
private val lockFileName = "${appName}_sync.lock"
|
||||
|
||||
private val googleDriveService = GoogleDriveService(context)
|
||||
|
||||
override suspend fun beforeSync() {
|
||||
try {
|
||||
googleDriveService.refreshToken()
|
||||
val drive = googleDriveService.driveService ?: throw Exception("Google Drive service not initialized")
|
||||
val drive = googleDriveService.driveService
|
||||
?: throw Exception(context.stringResource(MR.strings.google_drive_not_signed_in))
|
||||
|
||||
var backoff = 1000L
|
||||
|
||||
@ -105,6 +109,7 @@ class GoogleDriveSyncService(context: Context, json: Json, syncPreferences: Sync
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
logcat(LogPriority.ERROR) { "Error in GoogleDrive beforeSync: ${e.message}" }
|
||||
throw Exception(context.stringResource(MR.strings.error_before_sync_gdrive) + ": ${e.message}")
|
||||
}
|
||||
}
|
||||
|
||||
@ -234,6 +239,7 @@ class GoogleDriveSyncService(context: Context, json: Json, syncPreferences: Sync
|
||||
Log.d("GoogleDrive", "Created lock file with ID: ${file.id}")
|
||||
} catch (e: Exception) {
|
||||
Log.e("GoogleDrive", "Error creating lock file: ${e.message}")
|
||||
throw Exception(e.message)
|
||||
}
|
||||
}
|
||||
|
||||
@ -282,19 +288,24 @@ class GoogleDriveSyncService(context: Context, json: Json, syncPreferences: Sync
|
||||
googleDriveService.refreshToken()
|
||||
|
||||
return withContext(Dispatchers.IO) {
|
||||
val appDataFileList = getAppDataFileList(drive)
|
||||
try {
|
||||
val appDataFileList = getAppDataFileList(drive)
|
||||
|
||||
if (appDataFileList.isEmpty()) {
|
||||
logcat(LogPriority.DEBUG) { "No sync data file found in appData folder of Google Drive" }
|
||||
DeleteSyncDataStatus.NO_FILES
|
||||
} else {
|
||||
for (file in appDataFileList) {
|
||||
drive.files().delete(file.id).execute()
|
||||
logcat(
|
||||
LogPriority.DEBUG,
|
||||
) { "Deleted sync data file in appData folder of Google Drive with file ID: ${file.id}" }
|
||||
if (appDataFileList.isEmpty()) {
|
||||
logcat(LogPriority.DEBUG) { "No sync data file found in appData folder of Google Drive" }
|
||||
DeleteSyncDataStatus.NO_FILES
|
||||
} else {
|
||||
for (file in appDataFileList) {
|
||||
drive.files().delete(file.id).execute()
|
||||
logcat(
|
||||
LogPriority.DEBUG,
|
||||
) { "Deleted sync data file in appData folder of Google Drive with file ID: ${file.id}" }
|
||||
}
|
||||
DeleteSyncDataStatus.SUCCESS
|
||||
}
|
||||
DeleteSyncDataStatus.SUCCESS
|
||||
} catch (e: Exception) {
|
||||
logcat(LogPriority.ERROR) { "Error occurred while interacting with Google Drive: ${e.message}" }
|
||||
DeleteSyncDataStatus.ERROR
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -441,7 +452,7 @@ class GoogleDriveService(private val context: Context) {
|
||||
NetHttpTransport(),
|
||||
jsonFactory,
|
||||
credential,
|
||||
).setApplicationName("Tachiyomi")
|
||||
).setApplicationName(context.stringResource(MR.strings.app_name))
|
||||
.build()
|
||||
}
|
||||
|
||||
|
@ -185,7 +185,7 @@ class Anilist(id: Long) : BaseTracker(id, "AniList"), DeletableTracker {
|
||||
|
||||
if (track.status != COMPLETED) {
|
||||
val isRereading = track.status == REREADING
|
||||
track.status = if (isRereading.not() && hasReadChapters) READING else track.status
|
||||
track.status = if (!isRereading && hasReadChapters) READING else track.status
|
||||
}
|
||||
|
||||
update(track)
|
||||
|
@ -1,5 +1,6 @@
|
||||
package eu.kanade.tachiyomi.data.track.anilist
|
||||
|
||||
import eu.kanade.tachiyomi.BuildConfig
|
||||
import okhttp3.Interceptor
|
||||
import okhttp3.Response
|
||||
import java.io.IOException
|
||||
@ -40,6 +41,7 @@ class AnilistInterceptor(val anilist: Anilist, private var token: String?) : Int
|
||||
// Add the authorization header to the original request.
|
||||
val authRequest = originalRequest.newBuilder()
|
||||
.addHeader("Authorization", "Bearer ${oauth!!.access_token}")
|
||||
.header("User-Agent", "Mihon v${BuildConfig.VERSION_NAME} (${BuildConfig.APPLICATION_ID})")
|
||||
.build()
|
||||
|
||||
return chain.proceed(authRequest)
|
||||
|
@ -182,8 +182,8 @@ class BangumiApi(
|
||||
)
|
||||
|
||||
companion object {
|
||||
private const val clientId = "bgm10555cda0762e80ca"
|
||||
private const val clientSecret = "8fff394a8627b4c388cbf349ec865775"
|
||||
private const val clientId = "bgm291665acbd06a4c28"
|
||||
private const val clientSecret = "43e5ce36b207de16e5d3cfd3e79118db"
|
||||
|
||||
private const val apiUrl = "https://api.bgm.tv"
|
||||
private const val oauthUrl = "https://bgm.tv/oauth/access_token"
|
||||
|
@ -1,5 +1,6 @@
|
||||
package eu.kanade.tachiyomi.data.track.bangumi
|
||||
|
||||
import eu.kanade.tachiyomi.BuildConfig
|
||||
import kotlinx.serialization.json.Json
|
||||
import okhttp3.FormBody
|
||||
import okhttp3.Interceptor
|
||||
@ -29,22 +30,23 @@ class BangumiInterceptor(private val bangumi: Bangumi) : Interceptor {
|
||||
}
|
||||
}
|
||||
|
||||
val authRequest = if (originalRequest.method == "GET") {
|
||||
originalRequest.newBuilder()
|
||||
.header("User-Agent", "Tachiyomi")
|
||||
.url(
|
||||
originalRequest.url.newBuilder()
|
||||
.addQueryParameter("access_token", currAuth.access_token).build(),
|
||||
)
|
||||
.build()
|
||||
} else {
|
||||
originalRequest.newBuilder()
|
||||
.post(addToken(currAuth.access_token, originalRequest.body as FormBody))
|
||||
.header("User-Agent", "Tachiyomi")
|
||||
.build()
|
||||
}
|
||||
|
||||
return chain.proceed(authRequest)
|
||||
return originalRequest.newBuilder()
|
||||
.header(
|
||||
"User-Agent",
|
||||
"antsylich/Mihon/v${BuildConfig.VERSION_NAME} (Android) (http://github.com/mihonapp/mihon)",
|
||||
)
|
||||
.apply {
|
||||
if (originalRequest.method == "GET") {
|
||||
val newUrl = originalRequest.url.newBuilder()
|
||||
.addQueryParameter("access_token", currAuth.access_token)
|
||||
.build()
|
||||
url(newUrl)
|
||||
} else {
|
||||
post(addToken(currAuth.access_token, originalRequest.body as FormBody))
|
||||
}
|
||||
}
|
||||
.build()
|
||||
.let(chain::proceed)
|
||||
}
|
||||
|
||||
fun newAuth(oauth: OAuth?) {
|
||||
|
@ -18,7 +18,7 @@ class KavitaInterceptor(private val kavita: Kavita) : Interceptor {
|
||||
// Add the authorization header to the original request.
|
||||
val authRequest = originalRequest.newBuilder()
|
||||
.addHeader("Authorization", "Bearer $jwtToken")
|
||||
.header("User-Agent", "Tachiyomi Kavita v${BuildConfig.VERSION_NAME}")
|
||||
.header("User-Agent", "Mihon v${BuildConfig.VERSION_NAME} (${BuildConfig.APPLICATION_ID})")
|
||||
.build()
|
||||
|
||||
return chain.proceed(authRequest)
|
||||
|
@ -1,5 +1,6 @@
|
||||
package eu.kanade.tachiyomi.data.track.kitsu
|
||||
|
||||
import eu.kanade.tachiyomi.BuildConfig
|
||||
import kotlinx.serialization.json.Json
|
||||
import okhttp3.Interceptor
|
||||
import okhttp3.Response
|
||||
@ -34,6 +35,7 @@ class KitsuInterceptor(private val kitsu: Kitsu) : Interceptor {
|
||||
// Add the authorization header to the original request.
|
||||
val authRequest = originalRequest.newBuilder()
|
||||
.addHeader("Authorization", "Bearer ${oauth!!.access_token}")
|
||||
.header("User-Agent", "Mihon v${BuildConfig.VERSION_NAME} (${BuildConfig.APPLICATION_ID})")
|
||||
.header("Accept", "application/vnd.api+json")
|
||||
.header("Content-Type", "application/vnd.api+json")
|
||||
.build()
|
||||
|
@ -1,5 +1,6 @@
|
||||
package eu.kanade.tachiyomi.data.track.komga
|
||||
|
||||
import eu.kanade.tachiyomi.BuildConfig
|
||||
import eu.kanade.tachiyomi.data.database.models.Track
|
||||
import eu.kanade.tachiyomi.data.track.model.TrackSearch
|
||||
import eu.kanade.tachiyomi.network.GET
|
||||
@ -8,6 +9,7 @@ import eu.kanade.tachiyomi.network.parseAs
|
||||
import kotlinx.serialization.encodeToString
|
||||
import kotlinx.serialization.json.Json
|
||||
import logcat.LogPriority
|
||||
import okhttp3.Headers
|
||||
import okhttp3.MediaType.Companion.toMediaType
|
||||
import okhttp3.OkHttpClient
|
||||
import okhttp3.Request
|
||||
@ -23,6 +25,12 @@ class KomgaApi(
|
||||
private val client: OkHttpClient,
|
||||
) {
|
||||
|
||||
private val headers: Headers by lazy {
|
||||
Headers.Builder()
|
||||
.add("User-Agent", "Mihon v${BuildConfig.VERSION_NAME} (${BuildConfig.APPLICATION_ID})")
|
||||
.build()
|
||||
}
|
||||
|
||||
private val json: Json by injectLazy()
|
||||
|
||||
suspend fun getTrackSearch(url: String): TrackSearch =
|
||||
@ -30,12 +38,12 @@ class KomgaApi(
|
||||
try {
|
||||
val track = with(json) {
|
||||
if (url.contains(READLIST_API)) {
|
||||
client.newCall(GET(url))
|
||||
client.newCall(GET(url, headers))
|
||||
.awaitSuccess()
|
||||
.parseAs<ReadListDto>()
|
||||
.toTrack()
|
||||
} else {
|
||||
client.newCall(GET(url))
|
||||
client.newCall(GET(url, headers))
|
||||
.awaitSuccess()
|
||||
.parseAs<SeriesDto>()
|
||||
.toTrack()
|
||||
@ -43,7 +51,9 @@ class KomgaApi(
|
||||
}
|
||||
|
||||
val progress = client
|
||||
.newCall(GET("${url.replace("/api/v1/series/", "/api/v2/series/")}/read-progress/tachiyomi"))
|
||||
.newCall(
|
||||
GET("${url.replace("/api/v1/series/", "/api/v2/series/")}/read-progress/tachiyomi", headers),
|
||||
)
|
||||
.awaitSuccess().let {
|
||||
with(json) {
|
||||
if (url.contains("/api/v1/series/")) {
|
||||
@ -80,6 +90,7 @@ class KomgaApi(
|
||||
client.newCall(
|
||||
Request.Builder()
|
||||
.url("${track.tracking_url.replace("/api/v1/series/", "/api/v2/series/")}/read-progress/tachiyomi")
|
||||
.headers(headers)
|
||||
.put(payload.toRequestBody("application/json".toMediaType()))
|
||||
.build(),
|
||||
)
|
||||
|
@ -104,7 +104,7 @@ class MyAnimeList(id: Long) : BaseTracker(id, "MyAnimeList"), DeletableTracker {
|
||||
|
||||
if (track.status != COMPLETED) {
|
||||
val isRereading = track.status == REREADING
|
||||
track.status = if (isRereading.not() && hasReadChapters) READING else track.status
|
||||
track.status = if (!isRereading && hasReadChapters) READING else track.status
|
||||
}
|
||||
|
||||
update(track)
|
||||
|
@ -1,5 +1,6 @@
|
||||
package eu.kanade.tachiyomi.data.track.myanimelist
|
||||
|
||||
import eu.kanade.tachiyomi.BuildConfig
|
||||
import eu.kanade.tachiyomi.network.parseAs
|
||||
import kotlinx.serialization.json.Json
|
||||
import okhttp3.Interceptor
|
||||
@ -34,6 +35,7 @@ class MyAnimeListInterceptor(private val myanimelist: MyAnimeList, private var t
|
||||
// Add the authorization header to the original request
|
||||
val authRequest = originalRequest.newBuilder()
|
||||
.addHeader("Authorization", "Bearer ${oauth!!.access_token}")
|
||||
.header("User-Agent", "Mihon v${BuildConfig.VERSION_NAME} (${BuildConfig.APPLICATION_ID})")
|
||||
.build()
|
||||
|
||||
val response = chain.proceed(authRequest)
|
||||
@ -50,6 +52,7 @@ class MyAnimeListInterceptor(private val myanimelist: MyAnimeList, private var t
|
||||
|
||||
val newRequest = originalRequest.newBuilder()
|
||||
.addHeader("Authorization", "Bearer ${newToken.access_token}")
|
||||
.header("User-Agent", "Mihon v${BuildConfig.VERSION_NAME} (${BuildConfig.APPLICATION_ID})")
|
||||
.build()
|
||||
|
||||
return chain.proceed(newRequest)
|
||||
|
@ -72,7 +72,7 @@ class Shikimori(id: Long) : BaseTracker(id, "Shikimori"), DeletableTracker {
|
||||
|
||||
if (track.status != COMPLETED) {
|
||||
val isRereading = track.status == REREADING
|
||||
track.status = if (isRereading.not() && hasReadChapters) READING else track.status
|
||||
track.status = if (!isRereading && hasReadChapters) READING else track.status
|
||||
}
|
||||
|
||||
update(track)
|
||||
|
@ -192,8 +192,8 @@ class ShikimoriApi(
|
||||
)
|
||||
|
||||
companion object {
|
||||
private const val clientId = "1aaf4cf232372708e98b5abc813d795b539c5a916dbbfe9ac61bf02a360832cc"
|
||||
private const val clientSecret = "229942c742dd4cde803125d17d64501d91c0b12e14cb1e5120184d77d67024c0"
|
||||
private const val clientId = "PB9dq8DzI405s7wdtwTdirYqHiyVMh--djnP7lBUqSA"
|
||||
private const val clientSecret = "NajpZcOBKB9sJtgNcejf8OB9jBN1OYYoo-k4h2WWZus"
|
||||
|
||||
private const val baseUrl = "https://shikimori.one"
|
||||
private const val apiUrl = "$baseUrl/api"
|
||||
|
@ -1,5 +1,6 @@
|
||||
package eu.kanade.tachiyomi.data.track.shikimori
|
||||
|
||||
import eu.kanade.tachiyomi.BuildConfig
|
||||
import kotlinx.serialization.json.Json
|
||||
import okhttp3.Interceptor
|
||||
import okhttp3.Response
|
||||
@ -33,7 +34,7 @@ class ShikimoriInterceptor(private val shikimori: Shikimori) : Interceptor {
|
||||
// Add the authorization header to the original request.
|
||||
val authRequest = originalRequest.newBuilder()
|
||||
.addHeader("Authorization", "Bearer ${oauth!!.access_token}")
|
||||
.header("User-Agent", "Tachiyomi")
|
||||
.header("User-Agent", "Mihon v${BuildConfig.VERSION_NAME} (${BuildConfig.APPLICATION_ID})")
|
||||
.build()
|
||||
|
||||
return chain.proceed(authRequest)
|
||||
|
@ -338,6 +338,10 @@ class ExtensionManager(
|
||||
}
|
||||
|
||||
override fun onExtensionUntrusted(extension: Extension.Untrusted) {
|
||||
val installedExtension = _installedExtensionsFlow.value
|
||||
.find { it.pkgName == extension.pkgName }
|
||||
?: return
|
||||
_installedExtensionsFlow.value -= installedExtension
|
||||
_untrustedExtensionsFlow.value += extension
|
||||
}
|
||||
|
||||
|
@ -70,8 +70,7 @@ internal class ExtensionInstallReceiver(private val listener: Listener) :
|
||||
launchNow {
|
||||
when (val result = getExtensionFromIntent(context, intent)) {
|
||||
is LoadResult.Success -> listener.onExtensionUpdated(result.extension)
|
||||
// Not needed as a package can't be upgraded if the signature is different
|
||||
// is LoadResult.Untrusted -> {}
|
||||
is LoadResult.Untrusted -> listener.onExtensionUntrusted(result.extension)
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
|
@ -90,7 +90,7 @@ abstract class PagerViewer(val activity: ReaderActivity) : Viewer {
|
||||
pager.addOnPageChangeListener(
|
||||
object : ViewPager.SimpleOnPageChangeListener() {
|
||||
override fun onPageSelected(position: Int) {
|
||||
if (activity.isScrollingThroughPages.not()) {
|
||||
if (!activity.isScrollingThroughPages) {
|
||||
activity.hideMenu()
|
||||
}
|
||||
onPageChange(position)
|
||||
|
@ -9,5 +9,12 @@
|
||||
<path
|
||||
android:pathData="M0,0h432v432h-432z"
|
||||
android:fillColor="#FAFAFA"/>
|
||||
<path
|
||||
android:pathData="M322.13,215.5C322.13,272.66 274.64,319 216.07,319C157.49,319 110,272.66 110,215.5C110,158.34 157.49,112 216.07,112C274.64,112 322.13,158.34 322.13,215.5Z"
|
||||
android:fillColor="#F2FAFF"/>
|
||||
<path
|
||||
android:pathData="M216.07,299.59C263.66,299.59 302.24,261.94 302.24,215.5C302.24,169.06 263.66,131.41 216.07,131.41C168.47,131.41 129.89,169.06 129.89,215.5C129.89,261.94 168.47,299.59 216.07,299.59ZM216.07,319C274.64,319 322.13,272.66 322.13,215.5C322.13,158.34 274.64,112 216.07,112C157.49,112 110,158.34 110,215.5C110,272.66 157.49,319 216.07,319Z"
|
||||
android:fillColor="#0058A0"
|
||||
android:fillType="evenOdd"/>
|
||||
</group>
|
||||
</vector>
|
||||
|
@ -4,13 +4,6 @@
|
||||
android:viewportWidth="432"
|
||||
android:viewportHeight="432">
|
||||
<path
|
||||
android:pathData="M337,216C337,282.83 282.83,337 216,337C149.17,337 95,282.83 95,216C95,149.17 149.17,95 216,95C282.83,95 337,149.17 337,216Z"
|
||||
android:fillColor="#F2FAFF"/>
|
||||
<path
|
||||
android:pathData="M216,314.31C270.3,314.31 314.31,270.3 314.31,216C314.31,161.7 270.3,117.69 216,117.69C161.7,117.69 117.69,161.7 117.69,216C117.69,270.3 161.7,314.31 216,314.31ZM216,337C282.83,337 337,282.83 337,216C337,149.17 282.83,95 216,95C149.17,95 95,149.17 95,216C95,282.83 149.17,337 216,337Z"
|
||||
android:fillColor="#0058A0"
|
||||
android:fillType="evenOdd"/>
|
||||
<path
|
||||
android:pathData="M163.71,174.79L162.66,150.56C165.82,151.16 169.58,151.31 178.16,151.31C188.55,151.31 202.24,150.71 209.32,149.81C212.32,149.51 213.53,149.05 215.49,148L231.74,161.85C230.23,163.95 229.78,164.86 228.13,169.07C226.77,172.38 220.6,191.49 218.2,199.62C229.33,201.88 235.5,203.53 243.93,206.99C244.98,199.62 245.13,195.71 245.13,182.31C245.13,178.85 244.98,176.9 244.53,173.74L270.72,174.64C269.96,178.25 269.82,179.76 269.66,185.62C269.06,199.77 268.46,206.54 266.95,216.78C277.34,222.04 277.34,222.04 282.61,224.9C285.32,226.41 285.92,226.71 287.72,227.31L278.99,255.45C274.78,251.69 268.91,247.63 260.33,242.81C252.81,260.72 240.32,273.82 221.35,284.2C215.03,275.77 210.22,270.65 202.54,264.63C213.68,259.37 218.8,256.06 224.67,250.49C230.38,244.92 234.15,239.5 237.76,231.38C228.13,227.01 221.96,225.05 211.27,222.8C205.1,241.16 200.13,252.9 195.77,259.97C189.9,269.45 181.93,274.42 172.74,274.42C165.67,274.42 158.45,271.26 153.18,265.84C147.16,259.67 144,251.09 144,241.16C144,226.41 151.07,213.62 163.41,205.64C171.39,200.52 179.82,198.27 193.21,197.51C195.92,188.63 198.18,180.96 200.29,172.38C193.66,172.98 185.39,173.43 175.3,173.88C169.88,174.04 168.08,174.19 163.71,174.79ZM186.59,220.54C179.52,221.74 175.3,224 171.54,228.82C168.68,232.13 167.33,236.04 167.33,240.25C167.33,244.92 169.58,248.38 172.44,248.38C175.9,248.38 179.82,240.55 186.59,220.54Z"
|
||||
android:pathData="M182.03,188.7L181.33,172.69C183.42,173.09 185.91,173.19 191.57,173.19C198.44,173.19 207.49,172.79 212.16,172.19C214.15,171.99 214.95,171.7 216.24,171L226.98,180.15C225.98,181.54 225.68,182.14 224.59,184.92C223.7,187.11 219.62,199.74 218.03,205.11C225.39,206.6 229.46,207.7 235.03,209.98C235.73,205.11 235.83,202.52 235.83,193.67C235.83,191.39 235.73,190.09 235.43,188.01L252.74,188.6C252.24,190.99 252.14,191.98 252.04,195.86C251.64,205.21 251.24,209.68 250.25,216.45C257.11,219.93 257.11,219.93 260.59,221.82C262.38,222.81 262.78,223.01 263.97,223.41L258.2,242.01C255.42,239.52 251.54,236.83 245.87,233.65C240.9,245.49 232.65,254.14 220.12,261C215.94,255.43 212.76,252.05 207.68,248.07C215.04,244.59 218.43,242.4 222.3,238.72C226.08,235.04 228.57,231.46 230.96,226.09C224.59,223.21 220.51,221.92 213.45,220.43C209.38,232.56 206.09,240.32 203.21,244.99C199.33,251.25 194.06,254.54 187.99,254.54C183.32,254.54 178.55,252.45 175.07,248.87C171.09,244.79 169,239.12 169,232.56C169,222.81 173.67,214.36 181.83,209.09C187.1,205.71 192.67,204.21 201.52,203.72C203.31,197.85 204.8,192.78 206.19,187.11C201.82,187.51 196.35,187.81 189.68,188.1C186.1,188.2 184.91,188.3 182.03,188.7ZM197.14,218.93C192.47,219.73 189.68,221.22 187.2,224.4C185.31,226.59 184.41,229.18 184.41,231.96C184.41,235.04 185.91,237.33 187.8,237.33C190.08,237.33 192.67,232.16 197.14,218.93Z"
|
||||
android:fillColor="#031019"/>
|
||||
</vector>
|
||||
|
@ -14,7 +14,7 @@ android {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation(project(":source-api"))
|
||||
implementation(projects.sourceApi)
|
||||
|
||||
implementation(kotlinx.bundles.serialization)
|
||||
}
|
||||
|
@ -17,7 +17,7 @@ android {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation(project(":i18n"))
|
||||
implementation(projects.i18n)
|
||||
|
||||
api(libs.logcat)
|
||||
|
||||
|
@ -89,7 +89,7 @@ internal class RateLimitInterceptor(
|
||||
while (requestQueue.size >= permits) { // queue is full, remove expired entries
|
||||
val periodStart = SystemClock.elapsedRealtime() - rateLimitMillis
|
||||
var hasRemovedExpired = false
|
||||
while (requestQueue.isEmpty().not() && requestQueue.first <= periodStart) {
|
||||
while (!requestQueue.isEmpty() && requestQueue.first <= periodStart) {
|
||||
requestQueue.removeFirst()
|
||||
hasRemovedExpired = true
|
||||
}
|
||||
|
@ -24,9 +24,9 @@ android {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation(project(":source-api"))
|
||||
implementation(project(":domain"))
|
||||
implementation(project(":core"))
|
||||
implementation(projects.sourceApi)
|
||||
implementation(projects.domain)
|
||||
implementation(projects.core)
|
||||
|
||||
api(libs.bundles.sqldelight)
|
||||
}
|
||||
|
4
data/src/main/sqldelight/tachiyomi/migrations/1.sqm
Normal file
4
data/src/main/sqldelight/tachiyomi/migrations/1.sqm
Normal file
@ -0,0 +1,4 @@
|
||||
-- MangaUpdates score fixing --
|
||||
UPDATE manga_sync
|
||||
SET score = max(score, 0)
|
||||
WHERE sync_id = 7;
|
@ -14,8 +14,8 @@ android {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation(project(":source-api"))
|
||||
implementation(project(":core"))
|
||||
implementation(projects.sourceApi)
|
||||
implementation(projects.core)
|
||||
|
||||
implementation(platform(kotlinx.coroutines.bom))
|
||||
implementation(kotlinx.bundles.coroutines)
|
||||
|
@ -20,7 +20,7 @@ class GetApplicationRelease(
|
||||
val now = Instant.now()
|
||||
|
||||
// Limit checks to once every 3 days at most
|
||||
if (arguments.forceCheck.not() && now.isBefore(
|
||||
if (!arguments.forceCheck && now.isBefore(
|
||||
Instant.ofEpochMilli(lastChecked.get()).plus(3, ChronoUnit.DAYS),
|
||||
)
|
||||
) {
|
||||
|
@ -22,7 +22,7 @@ class StubSource(
|
||||
throw SourceNotInstalledException()
|
||||
|
||||
override fun toString(): String =
|
||||
if (isInvalid.not()) "$name (${lang.uppercase()})" else id.toString()
|
||||
if (!isInvalid) "$name (${lang.uppercase()})" else id.toString()
|
||||
|
||||
companion object {
|
||||
fun from(source: Source): StubSource {
|
||||
|
@ -577,11 +577,13 @@
|
||||
<string name="pref_google_drive_purge_sync_data">Clear Sync Data from Google Drive</string>
|
||||
<string name="google_drive_sync_data_purged">Sync data purged from Google Drive</string>
|
||||
<string name="google_drive_sync_data_not_found">No sync data found in Google Drive</string>
|
||||
<string name="google_drive_sync_data_purge_error">Error purging sync data from Google Drive, Try to sign in again.</string>
|
||||
<string name="google_drive_login_success">Logged in to Google Drive</string>
|
||||
<string name="google_drive_login_failed">Failed to log in to Google Drive: %s</string>
|
||||
<string name="google_drive_not_signed_in">Not signed in to Google Drive</string>
|
||||
<string name="error_uploading_sync_data">Error uploading sync data to Google Drive</string>
|
||||
<string name="error_deleting_google_drive_lock_file">Error Deleting Google Drive Lock File</string>
|
||||
<string name="error_before_sync_gdrive">Error before sync: %s</string>
|
||||
<string name="pref_purge_confirmation_title">Purge confirmation</string>
|
||||
<string name="pref_purge_confirmation_message">Purging sync data will delete all your sync data from Google Drive. Are you sure you want to continue?</string>
|
||||
<string name="pref_sync_options">Create sync triggers</string>
|
||||
|
@ -38,7 +38,7 @@
|
||||
</plurals>
|
||||
<plurals name="num_trackers">
|
||||
<item quantity="one">%d tracker</item>
|
||||
<item quantity="other">%d mga tracker</item>
|
||||
<item quantity="other">%d na tracker</item>
|
||||
</plurals>
|
||||
<plurals name="missing_chapters_warning">
|
||||
<item quantity="one">Nilaktawan ang %d na kabanata, maaaring ito ay wala sa source o na-filter ang mga ito</item>
|
||||
@ -46,11 +46,11 @@
|
||||
</plurals>
|
||||
<plurals name="relative_time">
|
||||
<item quantity="one">Kahapon</item>
|
||||
<item quantity="other">%1$d araw na ang makalipas</item>
|
||||
<item quantity="other">%1$d araw na ang nakakalipas</item>
|
||||
</plurals>
|
||||
<plurals name="next_unread_chapters">
|
||||
<item quantity="one">Susunod na hindi pa nababasa na kabanata</item>
|
||||
<item quantity="other">Susunod na %d di pa nababasa na kabanata</item>
|
||||
<item quantity="one">Susunod na hindi pa nababasang kabanata</item>
|
||||
<item quantity="other">Susunod na %d hindi pa nababasang kabanata</item>
|
||||
</plurals>
|
||||
<plurals name="download_amount">
|
||||
<item quantity="one">Sunod na kabanata</item>
|
||||
@ -58,14 +58,14 @@
|
||||
</plurals>
|
||||
<plurals name="missing_chapters">
|
||||
<item quantity="one">Nawawalang %1$s na kabanata</item>
|
||||
<item quantity="other">Nawawalang %1$s mga kabanata</item>
|
||||
<item quantity="other">Nawawalang %1$s na mga kabanata</item>
|
||||
</plurals>
|
||||
<plurals name="day">
|
||||
<item quantity="one">1 araw</item>
|
||||
<item quantity="other">%d (mga) araw</item>
|
||||
<item quantity="other">%d (na) araw</item>
|
||||
</plurals>
|
||||
<plurals name="num_repos">
|
||||
<item quantity="one">%d na repo</item>
|
||||
<item quantity="other">%d na mga repo</item>
|
||||
</plurals>
|
||||
</resources>
|
||||
</resources>
|
||||
|
@ -22,7 +22,7 @@
|
||||
<string name="action_disable">Isara</string>
|
||||
<string name="action_display_show_tabs">Ipakita ang mga tab ng kategorya</string>
|
||||
<string name="action_display_download_badge">Bilang ng kabanatang na-download</string>
|
||||
<string name="action_display_comfortable_grid">Maalwan na grid</string>
|
||||
<string name="action_display_comfortable_grid">Kumportableng grid</string>
|
||||
<string name="action_display_list">Listahan</string>
|
||||
<string name="action_display_grid">Siksik na grid</string>
|
||||
<string name="action_display">Pagpapakita</string>
|
||||
@ -114,7 +114,7 @@
|
||||
<string name="second_to_last">Pangalawa sa huling nabasa</string>
|
||||
<string name="last_read_chapter">Huling nabasang kabanata</string>
|
||||
<string name="disabled">Sarado</string>
|
||||
<string name="pref_remove_after_marked_as_read">Pagkamarkahang nabasa na</string>
|
||||
<string name="pref_remove_after_marked_as_read">Markahang nabasa na</string>
|
||||
<string name="pref_remove_after_read">Pagkatapos basahin, awtomatikong burahin</string>
|
||||
<string name="pref_webtoon_side_padding">Kapal ng gilid</string>
|
||||
<string name="pref_category_reading">Pagbabasa</string>
|
||||
@ -243,7 +243,7 @@
|
||||
<string name="channel_ext_updates">Mga update sa extension</string>
|
||||
<string name="tapping_inverted_none">Wala</string>
|
||||
<string name="channel_new_chapters">Mga update sa kabanata</string>
|
||||
<string name="channel_common">Komon</string>
|
||||
<string name="channel_common">Pangkaraniwan</string>
|
||||
<string name="download_notifier_download_paused">Nakahinto ang mga pag-download</string>
|
||||
<string name="download_notifier_no_network">Walang koneksyon sa Internet</string>
|
||||
<string name="download_notifier_text_only_wifi">Walang koneksyon sa Wifi</string>
|
||||
@ -291,7 +291,7 @@
|
||||
<string name="decode_image_error">Di mai-load ang larawan</string>
|
||||
<string name="no_next_chapter">Di makita ang susunod na kabanata</string>
|
||||
<string name="chapter_progress">Pahina: %1$d</string>
|
||||
<string name="cover_updated">Napalitan na ang cover</string>
|
||||
<string name="cover_updated">Napalitan ang cover</string>
|
||||
<string name="set_as_cover">Gawin itong cover</string>
|
||||
<string name="custom_filter">Pinili kong filter</string>
|
||||
<string name="picture_saved">Na-save na ang larawan</string>
|
||||
@ -556,7 +556,7 @@
|
||||
<string name="library_errors_help">Para sa tulong sa pag-aayos ng mga error sa pag-update ng aklatan, tingnan ang %1$s</string>
|
||||
<string name="pref_update_only_completely_read">Laktawan ang mga entry na hindi pa nababasang kabanata</string>
|
||||
<string name="save_chapter_as_cbz">I-save bilang CBZ archive</string>
|
||||
<string name="publishing_finished">Tapos na\'ng mailathala</string>
|
||||
<string name="publishing_finished">Tapos na ang paglalathala</string>
|
||||
<string name="on_hiatus">Naka-hiatus</string>
|
||||
<string name="cancelled">Kinansela</string>
|
||||
<string name="action_show_manga">Ipakita ang entry</string>
|
||||
@ -639,9 +639,9 @@
|
||||
<string name="pref_library_summary">Mga kategorya, panlahatang update, pag-swipe ng kabanata</string>
|
||||
<string name="pref_browse_summary">Mga source, extension, panlahatang paghanap</string>
|
||||
<string name="crash_screen_description">Nagkaroon ng hindi inaasahang error ang %s. Iminumungkahi naming ibahagi mo ang mga crash log sa aming support channel sa Discord.</string>
|
||||
<string name="crash_screen_title">Ay!</string>
|
||||
<string name="crash_screen_title">Ay Naku!</string>
|
||||
<string name="crash_screen_restart_application">Buksan muli ang app</string>
|
||||
<string name="invalid_location">Invalid na lugar: %s</string>
|
||||
<string name="invalid_location">Imbalidong lugar: %s</string>
|
||||
<string name="unknown_title">Di alam na pamagat</string>
|
||||
<string name="error_user_agent_string_invalid">Di-wastong string ng user agent</string>
|
||||
<string name="updates_last_update_info_just_now">Ngayon lang</string>
|
||||
@ -693,7 +693,7 @@
|
||||
<string name="pref_page_rotate">I-rotate ang malalawak na pahina upang magkasya</string>
|
||||
<string name="pref_page_rotate_invert">I-flip ang oryentasyon ng mga pinaikot na malalawak na pahina</string>
|
||||
<string name="overlay_header">Nakapatong (Overlay)</string>
|
||||
<string name="split_tall_images">Hatiin ang mga matatangkad na larawan</string>
|
||||
<string name="split_tall_images">Hatiin ang mga matataas na larawan</string>
|
||||
<string name="pref_debug_info">Impormasyon sa pag-debug</string>
|
||||
<string name="pref_chapter_swipe_start">Mag-swipe ng pakaliwang pagkilos</string>
|
||||
<string name="pref_chapter_swipe">Mag swipe ng kabanata</string>
|
||||
@ -798,4 +798,4 @@
|
||||
<string name="manga_interval_expected_update_soon">Malapit na</string>
|
||||
<string name="ext_revoke_trust">Bawiin ang mga pinagkakatiwalaang hindi kilalang extension</string>
|
||||
<string name="action_open_repo">Open source na repo</string>
|
||||
</resources>
|
||||
</resources>
|
||||
|
@ -412,7 +412,7 @@
|
||||
<string name="pref_category_delete_chapters">Suppression des chapitres</string>
|
||||
<string name="ext_nsfw_warning">Les sources de cette extension peuvent contenir du contenu NSFW (18+)</string>
|
||||
<string name="ext_nsfw_short">18+</string>
|
||||
<string name="parental_controls_info">Ceci n\'empêche pas les extensions non officielles ou potentiellement mal signalées de diffuser du contenu +18 dans l\'application.</string>
|
||||
<string name="parental_controls_info">Ceci n\'empêche pas les extensions de diffuser du contenu +18 dans l\'application.</string>
|
||||
<string name="no_chapters_error">Aucun chapitre trouvé</string>
|
||||
<string name="confirm_set_chapter_settings">Appliquer ce paramétrage par défaut \?</string>
|
||||
<string name="chapter_settings">Paramètres du chapitre</string>
|
||||
@ -534,7 +534,7 @@
|
||||
<string name="ext_installer_shizuku_unavailable_dialog">Installez et démarrez Shizuku pour utiliser Shizuku comme installateur d\'extensions.</string>
|
||||
<string name="ext_installer_shizuku_stopped">Shizuku n\'est pas en cours d\'exécution</string>
|
||||
<string name="ext_installer_legacy">Legacy</string>
|
||||
<string name="ext_installer_pref">installeur</string>
|
||||
<string name="ext_installer_pref">Installeur</string>
|
||||
<string name="ext_install_service_notif">Installation de l\'extension…</string>
|
||||
<string name="action_sort_count">Entrées totales</string>
|
||||
<string name="pref_verbose_logging">Rapports détaillés</string>
|
||||
@ -798,4 +798,4 @@
|
||||
<string name="create_backup_file_error">Impossible de créer un fichier de sauvegarde</string>
|
||||
<string name="last_auto_backup_info">Dernière sauvegarde automatique : %s</string>
|
||||
<string name="source_settings">Paramètres sources</string>
|
||||
</resources>
|
||||
</resources>
|
||||
|
@ -64,4 +64,8 @@
|
||||
<item quantity="one">Nästa kapitel</item>
|
||||
<item quantity="other">Nästa %d kapitel</item>
|
||||
</plurals>
|
||||
<plurals name="num_repos">
|
||||
<item quantity="one">%d förråd</item>
|
||||
<item quantity="other">%d flera förråd</item>
|
||||
</plurals>
|
||||
</resources>
|
@ -544,7 +544,7 @@
|
||||
<string name="pref_verbose_logging">Ayrıntılı günlük kaydı</string>
|
||||
<string name="pref_verbose_logging_summary">Ayrıntılı günlükleri sistem günlüğüne yaz (uygulama performansını düşürür)</string>
|
||||
<string name="connected_to_wifi">Yalnızca kablosuz ağda</string>
|
||||
<string name="download_queue_size_warning">Uyarı: Büyük toplu indirmeler kaynakların yavaşlamasına ve/veya Mihon\'yi engellemesine neden olabilir. Daha çok öğrenmek için dokunun.</string>
|
||||
<string name="download_queue_size_warning">Uyarı: Büyük toplu indirmeler kaynakların yavaşlamasına ve/veya Mihon\'u engellemesine neden olabilir. Daha çok öğrenmek için dokunun.</string>
|
||||
<string name="update_72hour">3 günde bir</string>
|
||||
<string name="ext_update_all">Tümünü güncelle</string>
|
||||
<string name="channel_app_updates">Uygulama güncellemeleri</string>
|
||||
@ -755,11 +755,11 @@
|
||||
<string name="action_bar_up_description">Yukarı git</string>
|
||||
<string name="onboarding_storage_action_select">Klasör seç</string>
|
||||
<string name="pref_onboarding_guide">Başlangıç rehberi</string>
|
||||
<string name="onboarding_guides_new_user">%s\'de yeni misiniz? Başlangıç rehberine göz atmanızı tavsiye ederiz.</string>
|
||||
<string name="onboarding_guides_new_user">%s\'da yeni misiniz? Başlangıç rehberine göz atmanızı tavsiye ederiz.</string>
|
||||
<string name="onboarding_action_finish">Başlayın</string>
|
||||
<string name="onboarding_storage_selection_required">Bir klasör seçilmelidir</string>
|
||||
<string name="onboarding_heading">Hoş geldiniz!</string>
|
||||
<string name="onboarding_guides_returning_user">%s\'yi yeniden mi kuruyorsunuz?</string>
|
||||
<string name="onboarding_guides_returning_user">%s\'u yeniden mi kuruyorsunuz?</string>
|
||||
<string name="onboarding_action_skip">Atla</string>
|
||||
<string name="onboarding_action_next">Sonraki</string>
|
||||
<string name="onboarding_description">Önce bazı şeyleri ayarlayalım. Bunları daha sonra ayarlardan da değiştirebilirsiniz.</string>
|
||||
@ -785,7 +785,7 @@
|
||||
<string name="onboarding_storage_help_action">Depolama kılavuzu</string>
|
||||
<string name="action_add_repo">Depo ekle</string>
|
||||
<string name="label_add_repo_input">Depo URL\'si</string>
|
||||
<string name="action_add_repo_message">Mihon\'ye ek depolar ekleyin. Bu, \"index.min.json\" ile biten bir URL olmalıdır.</string>
|
||||
<string name="action_add_repo_message">Mihon\'a ek depolar ekleyin. Bu, \"index.min.json\" ile biten bir URL olmalıdır.</string>
|
||||
<string name="error_repo_exists">Bu depo zaten var!</string>
|
||||
<string name="action_delete_repo">Depoyu sil</string>
|
||||
<string name="invalid_repo_name">Geçersiz depo URL\'si</string>
|
||||
@ -798,4 +798,4 @@
|
||||
<string name="ext_revoke_trust">Güvenilen bilinmeyen uzantıları iptal et</string>
|
||||
<string name="manga_interval_expected_update_soon">Yakında</string>
|
||||
<string name="action_open_repo">Açık kaynaklı depo</string>
|
||||
</resources>
|
||||
</resources>
|
||||
|
@ -7,7 +7,7 @@
|
||||
<item quantity="other">%d 個擴充套件可更新</item>
|
||||
</plurals>
|
||||
<plurals name="download_queue_summary">
|
||||
<item quantity="other">剩餘 %1$s 本</item>
|
||||
<item quantity="other">剩餘 %1$s 項</item>
|
||||
</plurals>
|
||||
<plurals name="manga_num_chapters">
|
||||
<item quantity="other">共 %1$s 章</item>
|
||||
|
@ -594,11 +594,11 @@
|
||||
<string name="ext_info_version">版本</string>
|
||||
<string name="ext_info_language">語言</string>
|
||||
<string name="ext_info_age_rating">分級</string>
|
||||
<string name="unfinished_list">未成清單</string>
|
||||
<string name="on_hold_list">擱置清單</string>
|
||||
<string name="complete_list">閱畢清單</string>
|
||||
<string name="wish_list">願望清單</string>
|
||||
<string name="reading_list">閱讀清單</string>
|
||||
<string name="unfinished_list">已拋棄</string>
|
||||
<string name="on_hold_list">擱置中</string>
|
||||
<string name="complete_list">已完結</string>
|
||||
<string name="wish_list">準備讀</string>
|
||||
<string name="reading_list">閱讀中</string>
|
||||
<string name="network_not_metered">僅透過非計量付費網路</string>
|
||||
<string name="cant_open_last_read_chapter">無法開啟上次閱讀章節</string>
|
||||
<string name="custom_cover">自訂封面</string>
|
||||
@ -784,7 +784,7 @@
|
||||
<string name="manga_interval_custom_amount">自訂更新頻率:</string>
|
||||
<string name="label_add_repo_input">儲存庫網址</string>
|
||||
<string name="error_repo_exists">已有該儲存庫!</string>
|
||||
<string name="pref_library_update_smart_update">智慧型更新</string>
|
||||
<string name="pref_library_update_smart_update">智慧更新</string>
|
||||
<string name="invalid_repo_name">無效的儲存庫網址</string>
|
||||
<string name="action_add_repo_message">將額外的擴充套件儲存庫新增至 Mihon。此處應填入一個結尾為「index.min.json」的網址。</string>
|
||||
<string name="delete_repo_confirmation">確定要刪除「%s」儲存庫嗎?</string>
|
||||
|
@ -21,8 +21,8 @@ android {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
api(project(":core"))
|
||||
api(project(":i18n"))
|
||||
api(projects.core)
|
||||
api(projects.i18n)
|
||||
|
||||
// Compose
|
||||
implementation(platform(compose.bom))
|
||||
|
@ -21,10 +21,10 @@ android {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation(project(":core"))
|
||||
implementation(project(":domain"))
|
||||
implementation(project(":presentation-core"))
|
||||
api(project(":i18n"))
|
||||
implementation(projects.core)
|
||||
implementation(projects.domain)
|
||||
implementation(projects.presentationCore)
|
||||
api(projects.i18n)
|
||||
|
||||
implementation(compose.glance)
|
||||
lintChecks(compose.lintchecks)
|
||||
|
@ -35,6 +35,8 @@ dependencyResolutionManagement {
|
||||
}
|
||||
}
|
||||
|
||||
enableFeaturePreview("TYPESAFE_PROJECT_ACCESSORS")
|
||||
|
||||
rootProject.name = "Mihon"
|
||||
include(":app")
|
||||
include(":i18n")
|
||||
|
@ -17,7 +17,7 @@ kotlin {
|
||||
}
|
||||
val androidMain by getting {
|
||||
dependencies {
|
||||
implementation(project(":core"))
|
||||
implementation(projects.core)
|
||||
api(libs.preferencektx)
|
||||
|
||||
// Workaround for https://youtrack.jetbrains.com/issue/KT-57605
|
||||
|
@ -8,8 +8,8 @@ kotlin {
|
||||
sourceSets {
|
||||
val commonMain by getting {
|
||||
dependencies {
|
||||
implementation(project(":source-api"))
|
||||
api(project(":i18n"))
|
||||
implementation(projects.sourceApi)
|
||||
api(projects.i18n)
|
||||
|
||||
implementation(libs.unifile)
|
||||
implementation(libs.junrar)
|
||||
@ -17,11 +17,11 @@ kotlin {
|
||||
}
|
||||
val androidMain by getting {
|
||||
dependencies {
|
||||
implementation(project(":core"))
|
||||
implementation(project(":core-metadata"))
|
||||
implementation(projects.core)
|
||||
implementation(projects.coreMetadata)
|
||||
|
||||
// Move ChapterRecognition to separate module?
|
||||
implementation(project(":domain"))
|
||||
implementation(projects.domain)
|
||||
|
||||
implementation(kotlinx.bundles.serialization)
|
||||
}
|
||||
|
Reference in New Issue
Block a user