mirror of
https://github.com/mihonapp/mihon.git
synced 2026-10-17 06:02:28 -07:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bc4fc765b2 | ||
|
|
253a560b20 | ||
|
|
eb3d2c6676 | ||
|
|
b1c6994977 | ||
|
|
2990762df9 | ||
|
|
8e6af6492b | ||
|
|
c4a606112a | ||
|
+6 |
230dc6fd05 | ||
|
|
e4b7002909 | ||
|
|
02c6427823 | ||
|
|
50b0ccb928 | ||
|
|
1712b9cdc5 | ||
|
|
b201ed163e | ||
|
|
3521ccfb4f | ||
|
|
b7cdfb1b30 | ||
|
|
4b1ae0526b | ||
|
|
378e734eed | ||
|
|
ed3b5305f1 | ||
|
|
cfc69d8d0b | ||
|
|
2a1859d7d7 | ||
|
|
8a2a2b1f36 | ||
|
|
af04276bed | ||
|
|
25d4bf5e2f | ||
|
|
d75db63258 | ||
|
|
52fc38e965 | ||
|
|
580863e891 | ||
|
|
85eb539c4d | ||
|
|
09ee38bb7a | ||
|
|
af4f17efc5 | ||
|
|
fa197a4ab2 | ||
|
|
c3243882b1 | ||
|
|
a938708e1f | ||
|
|
2e635ff2ce | ||
|
|
209805359e | ||
|
|
eb5b762515 | ||
|
|
7c5034275f | ||
|
|
d75a125b26 | ||
|
|
54d2c7b9e0 | ||
|
|
8b9d0a89c8 | ||
|
|
34afdabcfe | ||
|
|
bc32308327 | ||
|
|
c95269afd7 | ||
|
|
4af8f1eab0 | ||
|
+2 |
2366fbb4b3 | ||
|
|
e930f38608 |
@@ -30,7 +30,7 @@ body:
|
||||
required: true
|
||||
- label: I have written a short but informative title.
|
||||
required: true
|
||||
- label: I have updated the app to version **[0.19.6](https://github.com/mihonapp/mihon/releases/latest)**.
|
||||
- label: I have updated the app to version **[0.19.8](https://github.com/mihonapp/mihon/releases/latest)**.
|
||||
required: true
|
||||
- label: I will fill out all of the requested information in this form.
|
||||
required: true
|
||||
|
||||
@@ -52,7 +52,7 @@ body:
|
||||
label: Mihon version
|
||||
description: You can find your Mihon version in **More → About**.
|
||||
placeholder: |
|
||||
Example: "0.19.6"
|
||||
Example: "0.19.8"
|
||||
validations:
|
||||
required: true
|
||||
|
||||
@@ -95,7 +95,7 @@ body:
|
||||
required: true
|
||||
- label: I have gone through the [FAQ](https://mihon.app/docs/faq/general) and [troubleshooting guide](https://mihon.app/docs/guides/troubleshooting/).
|
||||
required: true
|
||||
- label: I have updated the app to version **[0.19.6](https://github.com/mihonapp/mihon/releases/latest)**.
|
||||
- label: I have updated the app to version **[0.19.8](https://github.com/mihonapp/mihon/releases/latest)**.
|
||||
required: true
|
||||
- label: I have filled out all of the requested information in this form, including specific version numbers.
|
||||
required: true
|
||||
|
||||
@@ -44,13 +44,13 @@ jobs:
|
||||
- name: Check code format
|
||||
run: ./gradlew spotlessCheck
|
||||
|
||||
- name: Build app
|
||||
run: ./gradlew assembleRelease -Pinclude-telemetry -Penable-updater
|
||||
|
||||
- name: Run unit tests
|
||||
id: unit_tests
|
||||
run: ./gradlew testReleaseUnitTest
|
||||
|
||||
- name: Verify SQLDelight migrations
|
||||
run: ./gradlew verifySqlDelightMigration
|
||||
|
||||
- name: Upload test report
|
||||
if: steps.unit_tests.outcome == 'failure'
|
||||
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
|
||||
@@ -58,6 +58,9 @@ jobs:
|
||||
name: test-report-${{ github.sha }}
|
||||
path: app/build/reports/tests/testReleaseUnitTest
|
||||
|
||||
- name: Build app
|
||||
run: ./gradlew assembleRelease -Pinclude-telemetry -Penable-updater
|
||||
|
||||
- name: Upload APK
|
||||
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
|
||||
with:
|
||||
|
||||
@@ -44,7 +44,7 @@ jobs:
|
||||
run: ./gradlew assembleRelease -Pinclude-telemetry -Penable-updater
|
||||
|
||||
- name: Sign APK
|
||||
uses: r0adkll/sign-android-release@f30bdd30588842ac76044ecdbd4b6d0e3e813478
|
||||
uses: r0adkll/sign-android-release@a391126403df92c6a0a3988a829dd47f79b89ed7
|
||||
with:
|
||||
releaseDirectory: app/build/outputs/apk/release
|
||||
signingKeyBase64: ${{ secrets.SIGNING_KEY }}
|
||||
@@ -100,7 +100,7 @@ jobs:
|
||||
run: ./gradlew assembleFoss -Penable-updater
|
||||
|
||||
- name: Sign APK
|
||||
uses: r0adkll/sign-android-release@f30bdd30588842ac76044ecdbd4b6d0e3e813478
|
||||
uses: r0adkll/sign-android-release@a391126403df92c6a0a3988a829dd47f79b89ed7
|
||||
with:
|
||||
releaseDirectory: app/build/outputs/apk/foss
|
||||
signingKeyBase64: ${{ secrets.SIGNING_KEY }}
|
||||
|
||||
+25
-1
@@ -12,6 +12,28 @@ The format is a modified version of [Keep a Changelog](https://keepachangelog.co
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
## [v0.19.8] - 2026-04-11
|
||||
### Added
|
||||
- Add dedicated "Support Us" screen ([@AntsyLich](https://github.com/AntsyLich)) ([#3200](https://github.com/mihonapp/mihon/pull/3200))
|
||||
- Add a one time popup asking for donation from long time users ([@AntsyLich](https://github.com/AntsyLich)) ([#3203](https://github.com/mihonapp/mihon/pull/3203))
|
||||
|
||||
### Changed
|
||||
- Don't prompt users to enable Google Play services if disabled or unavailable ([@leodyversemilla07](https://github.com/leodyversemilla07)) ([#3152](https://github.com/mihonapp/mihon/pull/3152))
|
||||
- Make "Support Us" more prominent in More screen ([@AntsyLich](https://github.com/AntsyLich)) ([#3196](https://github.com/mihonapp/mihon/pull/3196))
|
||||
|
||||
### Improved
|
||||
- Show informative error when trying to add unapproved titles to list on MAL ([@MajorTanya](https://github.com/MajorTanya)) ([#3155](https://github.com/mihonapp/mihon/pull/3155))
|
||||
- Show AniList publishing type based on country of origin ([@zweimach](https://github.com/zweimach)) ([#3100](https://github.com/mihonapp/mihon/pull/3100))
|
||||
|
||||
### Fixed
|
||||
- Fix app trying to split long strip when not needed ([@leodyversemilla07](https://github.com/leodyversemilla07)) ([#3121](https://github.com/mihonapp/mihon/pull/3121))
|
||||
|
||||
### Removed
|
||||
- Remove the empty "Updated to X.Y.Z" popup after app update ([@AntsyLich](https://github.com/AntsyLich)) ([#3203](https://github.com/mihonapp/mihon/pull/3203))
|
||||
|
||||
## [v0.19.7] - 2026-03-23
|
||||
Same as v0.19.6
|
||||
|
||||
## [v0.19.6] - 2026-03-23
|
||||
### Fixed
|
||||
- Fix app crashing when trying to add extension repo with existing signature ([@AntsyLich](https://github.com/AntsyLich)) ([`cb0e329`](https://github.com/mihonapp/mihon/commit/cb0e329))
|
||||
@@ -502,7 +524,9 @@ The format is a modified version of [Keep a Changelog](https://keepachangelog.co
|
||||
- Branding to Mihon ([@AntsyLich](https://github.com/AntsyLich))
|
||||
- Minimum supported Android version to 8 ([@AntsyLich](https://github.com/AntsyLich)) ([`dfb3091`](https://github.com/mihonapp/mihon/commit/dfb3091e380dda3e9bfb64bf5c9a685cf3a03d0e))
|
||||
|
||||
[unreleased]: https://github.com/mihonapp/mihon/compare/v0.19.6...main
|
||||
[unreleased]: https://github.com/mihonapp/mihon/compare/v0.19.8...main
|
||||
[v0.19.8]: https://github.com/mihonapp/mihon/compare/v0.19.7...v0.19.8
|
||||
[v0.19.7]: https://github.com/mihonapp/mihon/compare/v0.19.6...v0.19.7
|
||||
[v0.19.6]: https://github.com/mihonapp/mihon/compare/v0.19.5...v0.19.6
|
||||
[v0.19.5]: https://github.com/mihonapp/mihon/compare/v0.19.4...v0.19.5
|
||||
[v0.19.4]: https://github.com/mihonapp/mihon/compare/v0.19.3...v0.19.4
|
||||
|
||||
+75
-64
@@ -1,14 +1,16 @@
|
||||
import mihon.buildlogic.Config
|
||||
import mihon.buildlogic.getBuildTime
|
||||
import mihon.buildlogic.getCommitCount
|
||||
import mihon.buildlogic.getGitSha
|
||||
import mihon.gradle.Config
|
||||
import mihon.gradle.getBuildTime
|
||||
import mihon.gradle.getLatestCommitCount
|
||||
import mihon.gradle.getLatestCommitSha
|
||||
import mihon.gradle.tasks.ReplaceShortcutsPlaceholderTask
|
||||
|
||||
plugins {
|
||||
id("mihon.android.application")
|
||||
id("mihon.android.application.compose")
|
||||
id("com.github.zellius.shortcut-helper")
|
||||
kotlin("plugin.serialization")
|
||||
alias(mihonx.plugins.android.application)
|
||||
alias(mihonx.plugins.compose)
|
||||
alias(mihonx.plugins.spotless)
|
||||
|
||||
alias(libs.plugins.aboutLibraries)
|
||||
alias(libs.plugins.kotlin.serialization)
|
||||
}
|
||||
|
||||
if (Config.includeTelemetry) {
|
||||
@@ -18,20 +20,18 @@ if (Config.includeTelemetry) {
|
||||
}
|
||||
}
|
||||
|
||||
shortcutHelper.setFilePath("./shortcuts.xml")
|
||||
|
||||
android {
|
||||
namespace = "eu.kanade.tachiyomi"
|
||||
|
||||
defaultConfig {
|
||||
applicationId = "app.mihon"
|
||||
|
||||
versionCode = 19
|
||||
versionName = "0.19.5"
|
||||
versionCode = 21
|
||||
versionName = "0.19.8"
|
||||
|
||||
buildConfigField("String", "COMMIT_COUNT", "\"${getCommitCount()}\"")
|
||||
buildConfigField("String", "COMMIT_SHA", "\"${getGitSha()}\"")
|
||||
buildConfigField("String", "BUILD_TIME", "\"${getBuildTime(useLastCommitTime = false)}\"")
|
||||
buildConfigField("String", "COMMIT_COUNT", "\"${getLatestCommitCount()}\"")
|
||||
buildConfigField("String", "COMMIT_SHA", "\"${getLatestCommitSha()}\"")
|
||||
buildConfigField("String", "BUILD_TIME", "\"${getBuildTime(useLatestCommitTime = false)}\"")
|
||||
buildConfigField("boolean", "TELEMETRY_INCLUDED", "${Config.includeTelemetry}")
|
||||
buildConfigField("boolean", "UPDATER_ENABLED", "${Config.enableUpdater}")
|
||||
|
||||
@@ -41,7 +41,7 @@ android {
|
||||
buildTypes {
|
||||
val debug by getting {
|
||||
applicationIdSuffix = ".dev"
|
||||
versionNameSuffix = "-${getCommitCount()}"
|
||||
versionNameSuffix = "-${getLatestCommitCount()}"
|
||||
isPseudoLocalesEnabled = true
|
||||
}
|
||||
val release by getting {
|
||||
@@ -50,7 +50,7 @@ android {
|
||||
|
||||
proguardFiles("proguard-android-optimize.txt", "proguard-rules.pro")
|
||||
|
||||
buildConfigField("String", "BUILD_TIME", "\"${getBuildTime(useLastCommitTime = true)}\"")
|
||||
buildConfigField("String", "BUILD_TIME", "\"${getBuildTime(useLatestCommitTime = true)}\"")
|
||||
}
|
||||
|
||||
val commonMatchingFallbacks = listOf(release.name)
|
||||
@@ -72,7 +72,7 @@ android {
|
||||
|
||||
matchingFallbacks.addAll(commonMatchingFallbacks)
|
||||
|
||||
buildConfigField("String", "BUILD_TIME", "\"${getBuildTime(useLastCommitTime = false)}\"")
|
||||
buildConfigField("String", "BUILD_TIME", "\"${getBuildTime(useLatestCommitTime = false)}\"")
|
||||
}
|
||||
create("benchmark") {
|
||||
initWith(release)
|
||||
@@ -185,91 +185,89 @@ dependencies {
|
||||
implementation(projects.telemetry)
|
||||
|
||||
// Compose
|
||||
implementation(compose.activity)
|
||||
implementation(compose.foundation)
|
||||
implementation(compose.material3.core)
|
||||
implementation(compose.material.icons)
|
||||
implementation(compose.animation)
|
||||
implementation(compose.animation.graphics)
|
||||
debugImplementation(compose.ui.tooling)
|
||||
implementation(compose.ui.tooling.preview)
|
||||
implementation(compose.ui.util)
|
||||
implementation(libs.androidx.activity.compose)
|
||||
implementation(libs.androidx.compose.foundation)
|
||||
implementation(libs.androidx.compose.material3)
|
||||
implementation(libs.androidx.compose.materialIcons)
|
||||
implementation(libs.androidx.compose.animation)
|
||||
implementation(libs.androidx.compose.animationGraphics)
|
||||
debugImplementation(libs.androidx.compose.uiTooling)
|
||||
implementation(libs.androidx.compose.uiToolingPreview)
|
||||
implementation(libs.androidx.compose.uiUtil)
|
||||
|
||||
implementation(androidx.interpolator)
|
||||
implementation(libs.androidx.interpolator)
|
||||
|
||||
implementation(androidx.paging.runtime)
|
||||
implementation(androidx.paging.compose)
|
||||
implementation(libs.androidx.paging.runtime)
|
||||
implementation(libs.androidx.paging.compose)
|
||||
|
||||
implementation(androidx.sqlite.bundled)
|
||||
implementation(libs.androidx.sqlite.bundled)
|
||||
|
||||
implementation(kotlinx.reflect)
|
||||
implementation(kotlinx.immutables)
|
||||
implementation(libs.kotlin.reflect)
|
||||
implementation(libs.kotlinx.collections.immutable)
|
||||
|
||||
implementation(platform(kotlinx.coroutines.bom))
|
||||
implementation(kotlinx.bundles.coroutines)
|
||||
implementation(libs.bundles.kotlinx.coroutines)
|
||||
|
||||
// AndroidX libraries
|
||||
implementation(androidx.annotation)
|
||||
implementation(androidx.appcompat)
|
||||
implementation(androidx.biometricktx)
|
||||
implementation(androidx.constraintlayout)
|
||||
implementation(androidx.corektx)
|
||||
implementation(androidx.splashscreen)
|
||||
implementation(androidx.recyclerview)
|
||||
implementation(androidx.viewpager)
|
||||
implementation(androidx.profileinstaller)
|
||||
implementation(libs.androidx.annotation)
|
||||
implementation(libs.androidx.appCompat)
|
||||
implementation(libs.androidx.biometric)
|
||||
implementation(libs.androidx.constraintLayout)
|
||||
implementation(libs.androidx.core)
|
||||
implementation(libs.androidx.coreSplashScreen)
|
||||
implementation(libs.androidx.recyclerView)
|
||||
implementation(libs.androidx.viewPager)
|
||||
implementation(libs.androidx.profileInstaller)
|
||||
|
||||
implementation(androidx.bundles.lifecycle)
|
||||
implementation(libs.bundles.androidx.lifecycle)
|
||||
|
||||
// Job scheduling
|
||||
implementation(androidx.workmanager)
|
||||
implementation(libs.androidx.work)
|
||||
|
||||
// RxJava
|
||||
implementation(libs.rxjava)
|
||||
implementation(libs.rxJava)
|
||||
|
||||
// Networking
|
||||
implementation(libs.bundles.okhttp)
|
||||
implementation(libs.okio)
|
||||
implementation(libs.conscrypt.android) // TLS 1.3 support for Android < 10
|
||||
implementation(libs.conscrypt) // TLS 1.3 support for Android < 10
|
||||
|
||||
// Data serialization (JSON, protobuf, xml)
|
||||
implementation(kotlinx.bundles.serialization)
|
||||
implementation(libs.bundles.serialization)
|
||||
|
||||
// HTML parser
|
||||
implementation(libs.jsoup)
|
||||
|
||||
// Disk
|
||||
implementation(libs.disklrucache)
|
||||
implementation(libs.diskLruCache)
|
||||
implementation(libs.unifile)
|
||||
|
||||
// Preferences
|
||||
implementation(libs.preferencektx)
|
||||
implementation(libs.androidx.preference)
|
||||
|
||||
// Dependency injection
|
||||
implementation(libs.injekt)
|
||||
|
||||
// Image loading
|
||||
implementation(platform(libs.coil.bom))
|
||||
implementation(libs.bundles.coil)
|
||||
implementation(libs.subsamplingscaleimageview) {
|
||||
implementation(libs.subsamplingScaleImageView) {
|
||||
exclude(module = "image-decoder")
|
||||
}
|
||||
implementation(libs.image.decoder)
|
||||
|
||||
// UI libraries
|
||||
implementation(libs.material)
|
||||
implementation(libs.flexible.adapter.core)
|
||||
implementation(libs.photoview)
|
||||
implementation(libs.directionalviewpager) {
|
||||
implementation(libs.flexibleAdapter)
|
||||
implementation(libs.photoView)
|
||||
implementation(libs.directionalViewPager) {
|
||||
exclude(group = "androidx.viewpager", module = "viewpager")
|
||||
}
|
||||
implementation(libs.richeditor.compose)
|
||||
implementation(libs.composeRichEditor)
|
||||
implementation(libs.aboutLibraries.compose)
|
||||
implementation(libs.bundles.voyager)
|
||||
implementation(libs.compose.materialmotion)
|
||||
implementation(libs.composeMaterialMotion)
|
||||
implementation(libs.swipe)
|
||||
implementation(libs.compose.webview)
|
||||
implementation(libs.compose.grid)
|
||||
implementation(libs.composeWebview)
|
||||
implementation(libs.composeGrid)
|
||||
implementation(libs.reorderable)
|
||||
implementation(libs.bundles.markdown)
|
||||
implementation(libs.materialKolor)
|
||||
@@ -288,13 +286,26 @@ dependencies {
|
||||
testRuntimeOnly(libs.junit.platform.launcher)
|
||||
|
||||
// For detecting memory leaks; see https://square.github.io/leakcanary/
|
||||
// debugImplementation(libs.leakcanary.android)
|
||||
implementation(libs.leakcanary.plumber)
|
||||
// debugImplementation(libs.leakCanary.android)
|
||||
implementation(libs.leakCanary.plumber)
|
||||
|
||||
testImplementation(kotlinx.coroutines.test)
|
||||
testImplementation(libs.kotlinx.coroutines.test)
|
||||
}
|
||||
|
||||
androidComponents {
|
||||
onVariants { variant ->
|
||||
val resSource = variant.sources.res ?: return@onVariants
|
||||
|
||||
val variantName = variant.name.replaceFirstChar { it.uppercase() }
|
||||
val replaceShortcutsPlaceholderTask = tasks.register<ReplaceShortcutsPlaceholderTask>(
|
||||
"replace${variantName}ShortcutPlaceholder",
|
||||
) {
|
||||
applicationId.set(variant.applicationId)
|
||||
shortcutsFile.set(projectDir.resolve("src/main/shortcuts.xml"))
|
||||
}
|
||||
resSource.addGeneratedSourceDirectory(replaceShortcutsPlaceholderTask) { it.outputDir }
|
||||
}
|
||||
|
||||
onVariants(selector().withFlavor("default" to "standard")) {
|
||||
// Only excluding in standard flavor because this breaks
|
||||
// Layout Inspector's Compose tree
|
||||
@@ -304,6 +315,6 @@ androidComponents {
|
||||
|
||||
buildscript {
|
||||
dependencies {
|
||||
classpath(kotlinx.gradle)
|
||||
classpath(libs.kotlin.gradle)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,4 +46,9 @@ class BasePreferences(
|
||||
)
|
||||
|
||||
val installationId: Preference<String> = preferenceStore.getString(Preference.appStateKey("installation_id"), "")
|
||||
|
||||
val donationCampaignShown: Preference<Boolean> = preferenceStore.getBoolean(
|
||||
Preference.appStateKey("donation_campaign_shown"),
|
||||
false,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ fun NavigatorAdaptiveSheet(
|
||||
content = { sheetNavigator ->
|
||||
AdaptiveSheet(
|
||||
onDismissRequest = onDismissRequest,
|
||||
enableSwipeDismiss = enableSwipeDismiss(sheetNavigator),
|
||||
enableImplicitDismiss = enableSwipeDismiss(sheetNavigator),
|
||||
) {
|
||||
ScreenTransition(
|
||||
navigator = sheetNavigator,
|
||||
@@ -63,7 +63,7 @@ fun NavigatorAdaptiveSheet(
|
||||
fun AdaptiveSheet(
|
||||
onDismissRequest: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
enableSwipeDismiss: Boolean = true,
|
||||
enableImplicitDismiss: Boolean = true,
|
||||
content: @Composable () -> Unit,
|
||||
) {
|
||||
val isTabletUi = isTabletUi()
|
||||
@@ -74,7 +74,7 @@ fun AdaptiveSheet(
|
||||
) {
|
||||
AdaptiveSheetImpl(
|
||||
isTabletUi = isTabletUi,
|
||||
enableSwipeDismiss = enableSwipeDismiss,
|
||||
enableImplicitDismiss = enableImplicitDismiss,
|
||||
onDismissRequest = onDismissRequest,
|
||||
modifier = modifier,
|
||||
) {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package eu.kanade.presentation.more
|
||||
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
@@ -15,7 +16,9 @@ import androidx.compose.ui.unit.dp
|
||||
import eu.kanade.tachiyomi.R
|
||||
|
||||
@Composable
|
||||
fun LogoHeader() {
|
||||
fun LogoHeader(
|
||||
iconPadding: PaddingValues = PaddingValues(),
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
@@ -25,7 +28,7 @@ fun LogoHeader() {
|
||||
contentDescription = null,
|
||||
tint = MaterialTheme.colorScheme.onSurface,
|
||||
modifier = Modifier
|
||||
.padding(vertical = 56.dp)
|
||||
.padding(iconPadding)
|
||||
.size(64.dp),
|
||||
)
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
package eu.kanade.presentation.more
|
||||
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.automirrored.outlined.HelpOutline
|
||||
import androidx.compose.material.icons.automirrored.outlined.Label
|
||||
import androidx.compose.material.icons.outlined.AttachMoney
|
||||
import androidx.compose.material.icons.filled.VolunteerActivism
|
||||
import androidx.compose.material.icons.outlined.CloudOff
|
||||
import androidx.compose.material.icons.outlined.GetApp
|
||||
import androidx.compose.material.icons.outlined.Info
|
||||
@@ -13,10 +13,10 @@ import androidx.compose.material.icons.outlined.Settings
|
||||
import androidx.compose.material.icons.outlined.Storage
|
||||
import androidx.compose.material3.HorizontalDivider
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.platform.LocalUriHandler
|
||||
import androidx.compose.ui.res.vectorResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import eu.kanade.presentation.more.settings.widget.SwitchPreferenceWidget
|
||||
import eu.kanade.presentation.more.settings.widget.TextPreferenceWidget
|
||||
import eu.kanade.tachiyomi.R
|
||||
@@ -40,16 +40,17 @@ fun MoreScreen(
|
||||
onClickStats: () -> Unit,
|
||||
onClickDataAndStorage: () -> Unit,
|
||||
onClickSettings: () -> Unit,
|
||||
onClickSupport: () -> Unit,
|
||||
onClickAbout: () -> Unit,
|
||||
) {
|
||||
val uriHandler = LocalUriHandler.current
|
||||
|
||||
Scaffold { contentPadding ->
|
||||
ScrollbarLazyColumn(
|
||||
modifier = Modifier.padding(contentPadding),
|
||||
) {
|
||||
ScrollbarLazyColumn(contentPadding = contentPadding) {
|
||||
item {
|
||||
LogoHeader()
|
||||
LogoHeader(
|
||||
iconPadding = PaddingValues(vertical = 32.dp),
|
||||
)
|
||||
}
|
||||
item {
|
||||
SwitchPreferenceWidget(
|
||||
@@ -132,6 +133,13 @@ fun MoreScreen(
|
||||
onPreferenceClick = onClickSettings,
|
||||
)
|
||||
}
|
||||
item {
|
||||
TextPreferenceWidget(
|
||||
title = stringResource(MR.strings.label_support_us),
|
||||
icon = Icons.Default.VolunteerActivism,
|
||||
onPreferenceClick = onClickSupport,
|
||||
)
|
||||
}
|
||||
item {
|
||||
TextPreferenceWidget(
|
||||
title = stringResource(MR.strings.pref_category_about),
|
||||
@@ -146,13 +154,6 @@ fun MoreScreen(
|
||||
onPreferenceClick = { uriHandler.openUri(Constants.URL_HELP) },
|
||||
)
|
||||
}
|
||||
item {
|
||||
TextPreferenceWidget(
|
||||
title = stringResource(MR.strings.label_donate),
|
||||
icon = Icons.Outlined.AttachMoney,
|
||||
onPreferenceClick = { uriHandler.openUri(Constants.URL_DONATE) },
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package eu.kanade.presentation.more.settings.screen.about
|
||||
import android.content.Context
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
@@ -40,6 +41,7 @@ import eu.kanade.tachiyomi.util.system.toast
|
||||
import eu.kanade.tachiyomi.util.system.updaterEnabled
|
||||
import kotlinx.coroutines.launch
|
||||
import logcat.LogPriority
|
||||
import tachiyomi.core.common.Constants
|
||||
import tachiyomi.core.common.util.lang.withIOContext
|
||||
import tachiyomi.core.common.util.lang.withUIContext
|
||||
import tachiyomi.core.common.util.system.logcat
|
||||
@@ -85,7 +87,9 @@ object AboutScreen : Screen() {
|
||||
contentPadding = contentPadding,
|
||||
) {
|
||||
item {
|
||||
LogoHeader()
|
||||
LogoHeader(
|
||||
iconPadding = PaddingValues(vertical = 56.dp),
|
||||
)
|
||||
}
|
||||
|
||||
item {
|
||||
@@ -176,7 +180,7 @@ object AboutScreen : Screen() {
|
||||
LinkIcon(
|
||||
label = "Discord",
|
||||
icon = CustomIcons.Discord,
|
||||
url = "https://discord.gg/mihon",
|
||||
url = Constants.URL_DISCORD,
|
||||
)
|
||||
LinkIcon(
|
||||
label = "X",
|
||||
|
||||
@@ -153,7 +153,7 @@ class LibraryUpdateNotifier(
|
||||
Notifications.ID_LIBRARY_ERROR,
|
||||
Notifications.CHANNEL_LIBRARY_ERROR,
|
||||
) {
|
||||
setContentTitle(context.stringResource(MR.strings.notification_update_error, failed))
|
||||
setContentTitle(context.pluralStringResource(MR.plurals.notification_update_error, failed, failed))
|
||||
setContentText(context.stringResource(MR.strings.action_show_errors))
|
||||
setSmallIcon(R.drawable.ic_mihon)
|
||||
|
||||
|
||||
@@ -160,6 +160,7 @@ class AnilistApi(val client: OkHttpClient, interceptor: AnilistInterceptor) {
|
||||
|large
|
||||
|}
|
||||
|format
|
||||
|countryOfOrigin
|
||||
|status
|
||||
|chapters
|
||||
|description
|
||||
|
||||
@@ -14,13 +14,22 @@ data class ALSearchItem(
|
||||
val chapters: Long?,
|
||||
val averageScore: Int?,
|
||||
val staff: ALStaff,
|
||||
val countryOfOrigin: String = "",
|
||||
) {
|
||||
fun toALManga(): ALManga = ALManga(
|
||||
remoteId = id,
|
||||
title = title.userPreferred,
|
||||
imageUrl = coverImage.large,
|
||||
description = description,
|
||||
format = format.replace("_", "-"),
|
||||
format = if (format != "MANGA") {
|
||||
format.replace("_", "-")
|
||||
} else {
|
||||
when (countryOfOrigin) {
|
||||
"KR" -> "Manhwa"
|
||||
"CN", "TW" -> "Manhua"
|
||||
else -> "Manga"
|
||||
}
|
||||
},
|
||||
publishingStatus = status ?: "",
|
||||
startDateFuzzy = startDate.toEpochMilli(),
|
||||
totalChapters = chapters ?: 0,
|
||||
|
||||
@@ -12,7 +12,9 @@ import eu.kanade.tachiyomi.data.track.myanimelist.dto.MALSearchResult
|
||||
import eu.kanade.tachiyomi.data.track.myanimelist.dto.MALUser
|
||||
import eu.kanade.tachiyomi.network.DELETE
|
||||
import eu.kanade.tachiyomi.network.GET
|
||||
import eu.kanade.tachiyomi.network.HttpException
|
||||
import eu.kanade.tachiyomi.network.POST
|
||||
import eu.kanade.tachiyomi.network.await
|
||||
import eu.kanade.tachiyomi.network.awaitSuccess
|
||||
import eu.kanade.tachiyomi.network.parseAs
|
||||
import eu.kanade.tachiyomi.util.PkceUtil
|
||||
@@ -122,8 +124,23 @@ class MyAnimeListApi(
|
||||
.put(formBodyBuilder.build())
|
||||
.build()
|
||||
with(json) {
|
||||
authClient.newCall(request)
|
||||
.awaitSuccess()
|
||||
val response = authClient
|
||||
.newCall(request)
|
||||
.await()
|
||||
|
||||
if (!response.isSuccessful) {
|
||||
if (response.body.string().contains("invalid_content")) {
|
||||
// MAL returns unapproved titles in search but does not allow adding them to the list
|
||||
// returns 400 with this body: {"message":"Invalid content","error":"invalid_content"}
|
||||
// These unapproved titles cannot be filtered out in search and are also returned by the
|
||||
// endpoint we use for id prefix search
|
||||
throw MALTitleNotApproved()
|
||||
} else {
|
||||
throw HttpException(response.code)
|
||||
}
|
||||
}
|
||||
|
||||
response
|
||||
.parseAs<MALListItemStatus>()
|
||||
.let { parseMangaItem(it, track) }
|
||||
}
|
||||
|
||||
@@ -80,5 +80,6 @@ class MyAnimeListInterceptor(private val myanimelist: MyAnimeList) : Interceptor
|
||||
}
|
||||
}
|
||||
|
||||
class MALTitleNotApproved : IOException("MAL: This title can't be added because it is waiting for approval.")
|
||||
class MALTokenRefreshFailed : IOException("MAL: Failed to refresh account token")
|
||||
class MALTokenExpired : IOException("MAL: Login has expired")
|
||||
|
||||
@@ -1,15 +1,17 @@
|
||||
package eu.kanade.tachiyomi.extension.installer
|
||||
|
||||
import android.app.Service
|
||||
import android.content.BroadcastReceiver
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.IntentFilter
|
||||
import android.net.Uri
|
||||
import androidx.annotation.CallSuper
|
||||
import androidx.localbroadcastmanager.content.LocalBroadcastManager
|
||||
import eu.kanade.tachiyomi.extension.ExtensionManager
|
||||
import eu.kanade.tachiyomi.extension.model.InstallStep
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.cancel
|
||||
import kotlinx.coroutines.channels.Channel
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import kotlinx.coroutines.flow.receiveAsFlow
|
||||
import uy.kohesive.injekt.injectLazy
|
||||
import java.util.Collections
|
||||
import kotlin.concurrent.atomics.AtomicReference
|
||||
@@ -26,13 +28,6 @@ abstract class Installer(private val service: Service) {
|
||||
private var waitingInstall = AtomicReference<Entry?>(null)
|
||||
private val queue = Collections.synchronizedList(mutableListOf<Entry>())
|
||||
|
||||
private val cancelReceiver = object : BroadcastReceiver() {
|
||||
override fun onReceive(context: Context, intent: Intent) {
|
||||
val downloadId = intent.getLongExtra(EXTRA_DOWNLOAD_ID, -1).takeIf { it >= 0 } ?: return
|
||||
cancelQueue(downloadId)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Installer readiness. If false, queue check will not run.
|
||||
*
|
||||
@@ -114,7 +109,7 @@ abstract class Installer(private val service: Service) {
|
||||
*/
|
||||
@CallSuper
|
||||
open fun onDestroy() {
|
||||
LocalBroadcastManager.getInstance(service).unregisterReceiver(cancelReceiver)
|
||||
scope.cancel()
|
||||
queue.forEach { extensionManager.updateInstallStep(it.downloadId, InstallStep.Error) }
|
||||
queue.clear()
|
||||
waitingInstall.store(null)
|
||||
@@ -149,24 +144,24 @@ abstract class Installer(private val service: Service) {
|
||||
*/
|
||||
data class Entry(val downloadId: Long, val uri: Uri)
|
||||
|
||||
private val scope = CoroutineScope(Dispatchers.Main)
|
||||
|
||||
init {
|
||||
val filter = IntentFilter(ACTION_CANCEL_QUEUE)
|
||||
LocalBroadcastManager.getInstance(service).registerReceiver(cancelReceiver, filter)
|
||||
cancelChannel.receiveAsFlow()
|
||||
.onEach(::cancelQueue)
|
||||
.launchIn(scope)
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val ACTION_CANCEL_QUEUE = "Installer.action.CANCEL_QUEUE"
|
||||
private const val EXTRA_DOWNLOAD_ID = "Installer.extra.DOWNLOAD_ID"
|
||||
private val cancelChannel = Channel<Long>(Channel.UNLIMITED)
|
||||
|
||||
/**
|
||||
* Attempts to cancel the installation entry for the provided download ID.
|
||||
*
|
||||
* @param downloadId Download ID as known by [ExtensionManager]
|
||||
*/
|
||||
fun cancelInstallQueue(context: Context, downloadId: Long) {
|
||||
val intent = Intent(ACTION_CANCEL_QUEUE)
|
||||
intent.putExtra(EXTRA_DOWNLOAD_ID, downloadId)
|
||||
LocalBroadcastManager.getInstance(context).sendBroadcast(intent)
|
||||
suspend fun cancelInstallQueue(downloadId: Long) {
|
||||
cancelChannel.send(downloadId)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -140,8 +140,10 @@ internal class ExtensionInstaller(
|
||||
* Cancels extension install and remove from download manager and installer.
|
||||
*/
|
||||
fun cancelInstall(pkgName: String) {
|
||||
activeJobs.remove(pkgName)?.cancel()
|
||||
Installer.cancelInstallQueue(context, pkgName.hashCode().toLong())
|
||||
scope.launch {
|
||||
activeJobs.remove(pkgName)?.cancel()
|
||||
Installer.cancelInstallQueue(pkgName.hashCode().toLong())
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -5,6 +5,7 @@ import android.app.SearchManager
|
||||
import android.app.assist.AssistContent
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.pm.PackageManager
|
||||
import android.graphics.Color
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
@@ -14,21 +15,32 @@ import androidx.activity.SystemBarStyle
|
||||
import androidx.activity.enableEdgeToEdge
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.isSystemInDarkTheme
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.WindowInsets
|
||||
import androidx.compose.foundation.layout.WindowInsetsSides
|
||||
import androidx.compose.foundation.layout.consumeWindowInsets
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.navigationBars
|
||||
import androidx.compose.foundation.layout.only
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.windowInsetsBottomHeight
|
||||
import androidx.compose.foundation.layout.windowInsetsPadding
|
||||
import androidx.compose.material3.AlertDialog
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.automirrored.filled.OpenInNew
|
||||
import androidx.compose.material.icons.filled.VolunteerActivism
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.material3.HorizontalDivider
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.OutlinedButton
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextButton
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.collectAsState
|
||||
@@ -41,6 +53,8 @@ import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.alpha
|
||||
import androidx.compose.ui.graphics.luminance
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.LocalUriHandler
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.core.animation.doOnEnd
|
||||
import androidx.core.net.toUri
|
||||
import androidx.core.splashscreen.SplashScreen
|
||||
@@ -55,6 +69,7 @@ import cafe.adriel.voyager.navigator.NavigatorDisposeBehavior
|
||||
import cafe.adriel.voyager.navigator.currentOrThrow
|
||||
import eu.kanade.domain.base.BasePreferences
|
||||
import eu.kanade.domain.source.interactor.GetIncognitoState
|
||||
import eu.kanade.presentation.components.AdaptiveSheet
|
||||
import eu.kanade.presentation.components.AppStateBanners
|
||||
import eu.kanade.presentation.components.DownloadedOnlyBannerBackgroundColor
|
||||
import eu.kanade.presentation.components.IncognitoModeBannerBackgroundColor
|
||||
@@ -63,12 +78,10 @@ import eu.kanade.presentation.more.settings.screen.browse.ExtensionReposScreen
|
||||
import eu.kanade.presentation.more.settings.screen.data.RestoreBackupScreen
|
||||
import eu.kanade.presentation.util.AssistContentScreen
|
||||
import eu.kanade.presentation.util.DefaultNavigatorScreenTransition
|
||||
import eu.kanade.tachiyomi.BuildConfig
|
||||
import eu.kanade.tachiyomi.data.cache.ChapterCache
|
||||
import eu.kanade.tachiyomi.data.download.DownloadCache
|
||||
import eu.kanade.tachiyomi.data.notification.NotificationReceiver
|
||||
import eu.kanade.tachiyomi.data.updater.AppUpdateChecker
|
||||
import eu.kanade.tachiyomi.data.updater.RELEASE_URL
|
||||
import eu.kanade.tachiyomi.extension.api.ExtensionApi
|
||||
import eu.kanade.tachiyomi.ui.base.activity.BaseActivity
|
||||
import eu.kanade.tachiyomi.ui.browse.source.browse.BrowseSourceScreen
|
||||
@@ -80,7 +93,6 @@ import eu.kanade.tachiyomi.ui.more.NewUpdateScreen
|
||||
import eu.kanade.tachiyomi.ui.more.OnboardingScreen
|
||||
import eu.kanade.tachiyomi.util.system.dpToPx
|
||||
import eu.kanade.tachiyomi.util.system.isNavigationBarNeedsScrim
|
||||
import eu.kanade.tachiyomi.util.system.openInBrowser
|
||||
import eu.kanade.tachiyomi.util.system.updaterEnabled
|
||||
import eu.kanade.tachiyomi.util.view.setComposeContent
|
||||
import kotlinx.coroutines.channels.awaitClose
|
||||
@@ -93,6 +105,7 @@ import kotlinx.coroutines.flow.onEach
|
||||
import kotlinx.coroutines.launch
|
||||
import logcat.LogPriority
|
||||
import mihon.core.migration.Migrator
|
||||
import mihon.feature.support.SupportUsScreen
|
||||
import tachiyomi.core.common.Constants
|
||||
import tachiyomi.core.common.util.lang.launchIO
|
||||
import tachiyomi.core.common.util.system.logcat
|
||||
@@ -100,9 +113,14 @@ import tachiyomi.domain.library.service.LibraryPreferences
|
||||
import tachiyomi.domain.release.interactor.GetApplicationRelease
|
||||
import tachiyomi.i18n.MR
|
||||
import tachiyomi.presentation.core.components.material.Scaffold
|
||||
import tachiyomi.presentation.core.components.material.padding
|
||||
import tachiyomi.presentation.core.i18n.stringResource
|
||||
import tachiyomi.presentation.core.util.collectAsState
|
||||
import uy.kohesive.injekt.injectLazy
|
||||
import kotlin.time.Clock
|
||||
import kotlin.time.Duration.Companion.days
|
||||
import kotlin.time.Instant
|
||||
import kotlin.time.times
|
||||
|
||||
class MainActivity : BaseActivity() {
|
||||
|
||||
@@ -131,7 +149,7 @@ class MainActivity : BaseActivity() {
|
||||
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
val didMigration = Migrator.awaitAndRelease()
|
||||
Migrator.awaitAndRelease()
|
||||
|
||||
// Do not let the launcher create a new activity http://stackoverflow.com/questions/16283079
|
||||
if (!isTaskRoot) {
|
||||
@@ -240,24 +258,7 @@ class MainActivity : BaseActivity() {
|
||||
|
||||
CheckForUpdates()
|
||||
ShowOnboarding()
|
||||
}
|
||||
|
||||
var showChangelog by remember { mutableStateOf(didMigration && !BuildConfig.DEBUG) }
|
||||
if (showChangelog) {
|
||||
AlertDialog(
|
||||
onDismissRequest = { showChangelog = false },
|
||||
title = { Text(text = stringResource(MR.strings.updated_version, BuildConfig.VERSION_NAME)) },
|
||||
dismissButton = {
|
||||
TextButton(onClick = { openInBrowser(RELEASE_URL) }) {
|
||||
Text(text = stringResource(MR.strings.whats_new))
|
||||
}
|
||||
},
|
||||
confirmButton = {
|
||||
TextButton(onClick = { showChangelog = false }) {
|
||||
Text(text = stringResource(MR.strings.action_ok))
|
||||
}
|
||||
},
|
||||
)
|
||||
ShowDonationCampaign()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -343,6 +344,128 @@ class MainActivity : BaseActivity() {
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ShowDonationCampaign() {
|
||||
val navigator = LocalNavigator.currentOrThrow
|
||||
|
||||
var showCampaign by remember { mutableStateOf(false) }
|
||||
if (showCampaign) {
|
||||
val uriHandler = LocalUriHandler.current
|
||||
val dismissSupportMessage = {
|
||||
preferences.donationCampaignShown.set(true)
|
||||
@Suppress("AssignedValueIsNeverRead")
|
||||
showCampaign = false
|
||||
}
|
||||
AdaptiveSheet(
|
||||
onDismissRequest = dismissSupportMessage,
|
||||
enableImplicitDismiss = false,
|
||||
) {
|
||||
Column {
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.verticalScroll(rememberScrollState())
|
||||
.padding(16.dp)
|
||||
.weight(1f, fill = false)
|
||||
.fillMaxWidth(),
|
||||
verticalArrangement = Arrangement.spacedBy(8.dp),
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(MR.strings.donationCampaign_title),
|
||||
color = MaterialTheme.colorScheme.primary,
|
||||
style = MaterialTheme.typography.headlineSmall,
|
||||
)
|
||||
Text(
|
||||
text = stringResource(MR.strings.donationCampaign_paragraph1),
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
)
|
||||
Text(
|
||||
text = stringResource(MR.strings.donationCampaign_paragraph2),
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
)
|
||||
Text(
|
||||
text = stringResource(MR.strings.donationCampaign_paragraph3),
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
)
|
||||
}
|
||||
|
||||
HorizontalDivider()
|
||||
|
||||
Button(
|
||||
modifier = Modifier
|
||||
.padding(top = MaterialTheme.padding.small)
|
||||
.padding(horizontal = MaterialTheme.padding.medium)
|
||||
.fillMaxWidth(),
|
||||
onClick = {
|
||||
navigator.push(SupportUsScreen())
|
||||
dismissSupportMessage()
|
||||
},
|
||||
) {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(MaterialTheme.padding.small),
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.VolunteerActivism,
|
||||
contentDescription = null,
|
||||
)
|
||||
Text(
|
||||
text = stringResource(MR.strings.label_support_us),
|
||||
color = MaterialTheme.colorScheme.onPrimary,
|
||||
)
|
||||
}
|
||||
}
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.spacedBy(MaterialTheme.padding.small),
|
||||
modifier = Modifier
|
||||
.padding(bottom = MaterialTheme.padding.small)
|
||||
.padding(horizontal = MaterialTheme.padding.medium),
|
||||
) {
|
||||
OutlinedButton(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.weight(1f),
|
||||
onClick = { uriHandler.openUri(Constants.URL_DISCORD) },
|
||||
) {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(MaterialTheme.padding.small),
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(MR.strings.donationCampaign_contactPlatform),
|
||||
)
|
||||
Icon(
|
||||
imageVector = Icons.AutoMirrored.Default.OpenInNew,
|
||||
contentDescription = null,
|
||||
)
|
||||
}
|
||||
}
|
||||
OutlinedButton(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.weight(1f),
|
||||
onClick = dismissSupportMessage,
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(MR.strings.donationCampaign_dismiss),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LaunchedEffect(Unit) {
|
||||
try {
|
||||
val firstInstallTime = packageManager.getPackageInfo(packageName, 0).firstInstallTime
|
||||
val eligibleTime = Instant.fromEpochMilliseconds(firstInstallTime).plus(6 * 30.days)
|
||||
@Suppress("AssignedValueIsNeverRead")
|
||||
showCampaign = (Clock.System.now() >= eligibleTime && !preferences.donationCampaignShown.get())
|
||||
} catch (_: PackageManager.NameNotFoundException) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets custom splash screen exit animation on devices prior to Android 12.
|
||||
*
|
||||
|
||||
@@ -7,7 +7,6 @@ import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import cafe.adriel.voyager.core.model.ScreenModel
|
||||
import cafe.adriel.voyager.core.model.rememberScreenModel
|
||||
import cafe.adriel.voyager.core.model.screenModelScope
|
||||
@@ -31,6 +30,7 @@ import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import mihon.feature.support.SupportUsScreen
|
||||
import tachiyomi.core.common.util.lang.launchIO
|
||||
import tachiyomi.i18n.MR
|
||||
import tachiyomi.presentation.core.i18n.stringResource
|
||||
@@ -57,7 +57,6 @@ data object MoreTab : Tab {
|
||||
|
||||
@Composable
|
||||
override fun Content() {
|
||||
val context = LocalContext.current
|
||||
val navigator = LocalNavigator.currentOrThrow
|
||||
val screenModel = rememberScreenModel { MoreScreenModel() }
|
||||
val downloadQueueState by screenModel.downloadQueueState.collectAsState()
|
||||
@@ -72,6 +71,7 @@ data object MoreTab : Tab {
|
||||
onClickStats = { navigator.push(StatsScreen()) },
|
||||
onClickDataAndStorage = { navigator.push(SettingsScreen(SettingsScreen.Destination.DataAndStorage)) },
|
||||
onClickSettings = { navigator.push(SettingsScreen()) },
|
||||
onClickSupport = { navigator.push(SupportUsScreen()) },
|
||||
onClickAbout = { navigator.push(SettingsScreen(SettingsScreen.Destination.About)) },
|
||||
)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,143 @@
|
||||
package mihon.feature.support
|
||||
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.automirrored.filled.OpenInNew
|
||||
import androidx.compose.material3.Card
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.platform.LocalUriHandler
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.LayoutDirection
|
||||
import androidx.compose.ui.unit.dp
|
||||
import cafe.adriel.voyager.navigator.LocalNavigator
|
||||
import cafe.adriel.voyager.navigator.currentOrThrow
|
||||
import eu.kanade.presentation.components.AppBar
|
||||
import eu.kanade.presentation.more.settings.widget.TextPreferenceWidget
|
||||
import eu.kanade.presentation.util.Screen
|
||||
import tachiyomi.core.common.Constants
|
||||
import tachiyomi.i18n.MR
|
||||
import tachiyomi.presentation.core.components.material.padding
|
||||
import tachiyomi.presentation.core.i18n.stringResource
|
||||
import tachiyomi.presentation.core.icons.CustomIcons
|
||||
import tachiyomi.presentation.core.icons.Discord
|
||||
import tachiyomi.presentation.core.icons.OpenCollective
|
||||
import tachiyomi.presentation.core.icons.Patreon
|
||||
|
||||
class SupportUsScreen : Screen() {
|
||||
|
||||
@Composable
|
||||
override fun Content() {
|
||||
val navigator = LocalNavigator.currentOrThrow
|
||||
val uriHandler = LocalUriHandler.current
|
||||
|
||||
Scaffold(
|
||||
topBar = {
|
||||
AppBar(
|
||||
title = stringResource(MR.strings.label_support_us),
|
||||
navigateUp = navigator::pop,
|
||||
)
|
||||
},
|
||||
) { paddingValues ->
|
||||
Column(
|
||||
verticalArrangement = Arrangement.spacedBy(MaterialTheme.padding.small),
|
||||
modifier = Modifier.verticalScroll(rememberScrollState()).padding(
|
||||
remember(paddingValues) {
|
||||
object : PaddingValues {
|
||||
override fun calculateLeftPadding(layoutDirection: LayoutDirection): Dp {
|
||||
return paddingValues.calculateLeftPadding(layoutDirection) +
|
||||
MaterialTheme.padding.medium
|
||||
}
|
||||
|
||||
override fun calculateTopPadding(): Dp {
|
||||
return 0.dp
|
||||
}
|
||||
|
||||
override fun calculateRightPadding(layoutDirection: LayoutDirection): Dp {
|
||||
return paddingValues.calculateRightPadding(layoutDirection) +
|
||||
MaterialTheme.padding.medium
|
||||
}
|
||||
|
||||
override fun calculateBottomPadding(): Dp {
|
||||
return 0.dp
|
||||
}
|
||||
}
|
||||
},
|
||||
),
|
||||
) {
|
||||
Spacer(modifier = Modifier.height(paddingValues.calculateTopPadding()))
|
||||
|
||||
Text(
|
||||
text = stringResource(MR.strings.supportUsScreen_perks),
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
modifier = Modifier.padding(horizontal = MaterialTheme.padding.medium),
|
||||
)
|
||||
|
||||
SupportItem(
|
||||
icon = CustomIcons.Patreon,
|
||||
title = stringResource(MR.strings.supportUsScreen_donationPlatform_patreon),
|
||||
onClick = { uriHandler.openUri(Constants.URL_DONATE_PATREON) },
|
||||
)
|
||||
SupportItem(
|
||||
icon = CustomIcons.OpenCollective,
|
||||
title = stringResource(MR.strings.supportUsScreen_donationPlatform_opencollective),
|
||||
onClick = { uriHandler.openUri(Constants.URL_DONATE_OPENCOLLECTIVE) },
|
||||
)
|
||||
|
||||
Text(
|
||||
text = stringResource(MR.strings.supportUsScreen_currentlySupportedBy, 200),
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
modifier = Modifier.padding(horizontal = MaterialTheme.padding.medium),
|
||||
)
|
||||
|
||||
Text(
|
||||
text = stringResource(MR.strings.supportUsScreen_contactForDetailsMessage),
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
modifier = Modifier.padding(horizontal = MaterialTheme.padding.medium),
|
||||
)
|
||||
|
||||
SupportItem(
|
||||
icon = CustomIcons.Discord,
|
||||
title = stringResource(MR.strings.supportUsScreen_contactPlatform),
|
||||
onClick = { uriHandler.openUri(Constants.URL_DISCORD) },
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(paddingValues.calculateBottomPadding()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun SupportItem(
|
||||
icon: ImageVector,
|
||||
title: String,
|
||||
onClick: () -> Unit,
|
||||
) {
|
||||
Card {
|
||||
TextPreferenceWidget(
|
||||
title = title,
|
||||
icon = icon,
|
||||
widget = {
|
||||
Icon(
|
||||
imageVector = Icons.AutoMirrored.Filled.OpenInNew,
|
||||
contentDescription = null,
|
||||
)
|
||||
},
|
||||
onPreferenceClick = onClick,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8,7 +8,8 @@
|
||||
android:shortcutShortLabel="@string/label_library">
|
||||
<intent
|
||||
android:action="eu.kanade.tachiyomi.SHOW_LIBRARY"
|
||||
android:targetClass="eu.kanade.tachiyomi.ui.main.MainActivity" />
|
||||
android:targetClass="eu.kanade.tachiyomi.ui.main.MainActivity"
|
||||
android:targetPackage="${applicationId}" />
|
||||
</shortcut>
|
||||
<shortcut
|
||||
android:enabled="true"
|
||||
@@ -19,7 +20,8 @@
|
||||
android:shortcutShortLabel="@string/label_recent_updates">
|
||||
<intent
|
||||
android:action="eu.kanade.tachiyomi.SHOW_RECENTLY_UPDATED"
|
||||
android:targetClass="eu.kanade.tachiyomi.ui.main.MainActivity" />
|
||||
android:targetClass="eu.kanade.tachiyomi.ui.main.MainActivity"
|
||||
android:targetPackage="${applicationId}" />
|
||||
</shortcut>
|
||||
<shortcut
|
||||
android:enabled="true"
|
||||
@@ -30,7 +32,8 @@
|
||||
android:shortcutShortLabel="@string/label_recent_manga">
|
||||
<intent
|
||||
android:action="eu.kanade.tachiyomi.SHOW_RECENTLY_READ"
|
||||
android:targetClass="eu.kanade.tachiyomi.ui.main.MainActivity" />
|
||||
android:targetClass="eu.kanade.tachiyomi.ui.main.MainActivity"
|
||||
android:targetPackage="${applicationId}" />
|
||||
</shortcut>
|
||||
<shortcut
|
||||
android:enabled="true"
|
||||
@@ -41,6 +44,7 @@
|
||||
android:shortcutShortLabel="@string/browse">
|
||||
<intent
|
||||
android:action="eu.kanade.tachiyomi.SHOW_CATALOGUES"
|
||||
android:targetClass="eu.kanade.tachiyomi.ui.main.MainActivity" />
|
||||
android:targetClass="eu.kanade.tachiyomi.ui.main.MainActivity"
|
||||
android:targetPackage="${applicationId}" />
|
||||
</shortcut>
|
||||
</shortcuts>
|
||||
+15
-10
@@ -1,18 +1,23 @@
|
||||
buildscript {
|
||||
dependencies {
|
||||
classpath(libs.android.shortcut.gradle)
|
||||
}
|
||||
}
|
||||
|
||||
plugins {
|
||||
alias(kotlinx.plugins.serialization) apply false
|
||||
alias(libs.plugins.aboutLibraries) apply false
|
||||
alias(libs.plugins.android.application) apply false
|
||||
alias(libs.plugins.android.library) apply false
|
||||
alias(libs.plugins.android.test) apply false
|
||||
alias(libs.plugins.firebase.crashlytics) apply false
|
||||
alias(libs.plugins.google.services) apply false
|
||||
alias(libs.plugins.moko) apply false
|
||||
alias(libs.plugins.kotlin.compose.compiler) apply false
|
||||
alias(libs.plugins.kotlin.serialization) apply false
|
||||
alias(libs.plugins.moko.resources) apply false
|
||||
alias(libs.plugins.sqldelight) apply false
|
||||
|
||||
alias(mihonx.plugins.spotless)
|
||||
}
|
||||
|
||||
tasks.register<Delete>("clean") {
|
||||
delete(rootProject.layout.buildDirectory)
|
||||
val buildLogic: IncludedBuild = gradle.includedBuild("build-logic")
|
||||
tasks {
|
||||
listOf("clean", "spotlessApply", "spotlessCheck").forEach { task ->
|
||||
named(task) {
|
||||
dependsOn(buildLogic.task(":$task"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
plugins {
|
||||
`kotlin-dsl`
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation(androidx.gradle)
|
||||
implementation(kotlinx.gradle)
|
||||
implementation(kotlinx.compose.compiler.gradle)
|
||||
implementation(libs.spotless.gradle)
|
||||
implementation(gradleApi())
|
||||
|
||||
implementation(files(libs.javaClass.superclass.protectionDomain.codeSource.location))
|
||||
implementation(files(androidx.javaClass.superclass.protectionDomain.codeSource.location))
|
||||
implementation(files(compose.javaClass.superclass.protectionDomain.codeSource.location))
|
||||
implementation(files(kotlinx.javaClass.superclass.protectionDomain.codeSource.location))
|
||||
}
|
||||
|
||||
repositories {
|
||||
gradlePluginPortal()
|
||||
mavenCentral()
|
||||
google()
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
dependencyResolutionManagement {
|
||||
versionCatalogs {
|
||||
create("libs") {
|
||||
from(files("../gradle/libs.versions.toml"))
|
||||
}
|
||||
create("androidx") {
|
||||
from(files("../gradle/androidx.versions.toml"))
|
||||
}
|
||||
create("compose") {
|
||||
from(files("../gradle/compose.versions.toml"))
|
||||
}
|
||||
create("kotlinx") {
|
||||
from(files("../gradle/kotlinx.versions.toml"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
rootProject.name = "mihon-buildSrc"
|
||||
@@ -1,12 +0,0 @@
|
||||
import mihon.buildlogic.configureCompose
|
||||
|
||||
plugins {
|
||||
id("com.android.application")
|
||||
kotlin("android")
|
||||
|
||||
id("mihon.code.lint")
|
||||
}
|
||||
|
||||
android {
|
||||
configureCompose(this)
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
import mihon.buildlogic.AndroidConfig
|
||||
import mihon.buildlogic.configureAndroid
|
||||
import mihon.buildlogic.configureTest
|
||||
|
||||
plugins {
|
||||
id("com.android.application")
|
||||
kotlin("android")
|
||||
|
||||
id("mihon.code.lint")
|
||||
}
|
||||
|
||||
android {
|
||||
defaultConfig {
|
||||
targetSdk = AndroidConfig.TARGET_SDK
|
||||
}
|
||||
configureAndroid(this)
|
||||
configureTest()
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
import mihon.buildlogic.configureAndroid
|
||||
import mihon.buildlogic.configureTest
|
||||
|
||||
plugins {
|
||||
id("com.android.test")
|
||||
kotlin("android")
|
||||
|
||||
id("mihon.code.lint")
|
||||
}
|
||||
|
||||
android {
|
||||
configureAndroid(this)
|
||||
configureTest()
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
import org.gradle.accessors.dm.LibrariesForLibs
|
||||
|
||||
plugins {
|
||||
id("com.diffplug.spotless")
|
||||
}
|
||||
|
||||
val libs = the<LibrariesForLibs>()
|
||||
|
||||
val xmlFormatExclude = buildList(2) {
|
||||
add("**/build/**/*.xml")
|
||||
|
||||
projectDir
|
||||
.resolve("src/commonMain/moko-resources")
|
||||
.takeIf { it.isDirectory }
|
||||
?.let(::fileTree)
|
||||
?.matching { exclude("/base/**") }
|
||||
?.let(::add)
|
||||
}
|
||||
.toTypedArray()
|
||||
|
||||
spotless {
|
||||
kotlin {
|
||||
target("**/*.kt", "**/*.kts")
|
||||
targetExclude("**/build/**/*.kt")
|
||||
ktlint(libs.ktlint.core.get().version)
|
||||
trimTrailingWhitespace()
|
||||
endWithNewline()
|
||||
}
|
||||
format("xml") {
|
||||
target("**/*.xml")
|
||||
targetExclude(*xmlFormatExclude)
|
||||
trimTrailingWhitespace()
|
||||
endWithNewline()
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
import mihon.buildlogic.configureCompose
|
||||
|
||||
plugins {
|
||||
id("com.android.library")
|
||||
|
||||
id("mihon.code.lint")
|
||||
}
|
||||
|
||||
android {
|
||||
configureCompose(this)
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
import mihon.buildlogic.configureAndroid
|
||||
import mihon.buildlogic.configureTest
|
||||
|
||||
plugins {
|
||||
id("com.android.library")
|
||||
|
||||
id("mihon.code.lint")
|
||||
}
|
||||
|
||||
android {
|
||||
configureAndroid(this)
|
||||
configureTest()
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
package mihon.buildlogic
|
||||
|
||||
import org.gradle.api.JavaVersion as GradleJavaVersion
|
||||
import org.jetbrains.kotlin.gradle.dsl.JvmTarget as KotlinJvmTarget
|
||||
|
||||
object AndroidConfig {
|
||||
const val COMPILE_SDK = 36
|
||||
const val TARGET_SDK = 36
|
||||
const val MIN_SDK = 26
|
||||
|
||||
// https://youtrack.jetbrains.com/issue/KT-66995/JvmTarget-and-JavaVersion-compatibility-for-easier-JVM-version-setup
|
||||
val JavaVersion = GradleJavaVersion.VERSION_17
|
||||
val JvmTarget = KotlinJvmTarget.JVM_17
|
||||
}
|
||||
@@ -1,101 +0,0 @@
|
||||
package mihon.buildlogic
|
||||
|
||||
import com.android.build.api.dsl.CommonExtension
|
||||
import org.gradle.accessors.dm.LibrariesForAndroidx
|
||||
import org.gradle.accessors.dm.LibrariesForCompose
|
||||
import org.gradle.accessors.dm.LibrariesForKotlinx
|
||||
import org.gradle.accessors.dm.LibrariesForLibs
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.api.tasks.testing.Test
|
||||
import org.gradle.api.tasks.testing.logging.TestLogEvent
|
||||
import org.gradle.kotlin.dsl.configure
|
||||
import org.gradle.kotlin.dsl.dependencies
|
||||
import org.gradle.kotlin.dsl.provideDelegate
|
||||
import org.gradle.kotlin.dsl.the
|
||||
import org.gradle.kotlin.dsl.withType
|
||||
import org.jetbrains.kotlin.compose.compiler.gradle.ComposeCompilerGradlePluginExtension
|
||||
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
|
||||
import java.io.File
|
||||
|
||||
val Project.androidx get() = the<LibrariesForAndroidx>()
|
||||
val Project.compose get() = the<LibrariesForCompose>()
|
||||
val Project.kotlinx get() = the<LibrariesForKotlinx>()
|
||||
val Project.libs get() = the<LibrariesForLibs>()
|
||||
|
||||
internal fun Project.configureAndroid(commonExtension: CommonExtension<*, *, *, *, *, *>) {
|
||||
commonExtension.apply {
|
||||
compileSdk = AndroidConfig.COMPILE_SDK
|
||||
|
||||
defaultConfig {
|
||||
minSdk = AndroidConfig.MIN_SDK
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility = AndroidConfig.JavaVersion
|
||||
targetCompatibility = AndroidConfig.JavaVersion
|
||||
isCoreLibraryDesugaringEnabled = true
|
||||
}
|
||||
}
|
||||
|
||||
tasks.withType<KotlinCompile>().configureEach {
|
||||
compilerOptions {
|
||||
jvmTarget.set(AndroidConfig.JvmTarget)
|
||||
freeCompilerArgs.addAll(
|
||||
"-Xcontext-parameters",
|
||||
"-opt-in=kotlin.RequiresOptIn",
|
||||
)
|
||||
|
||||
// Treat all Kotlin warnings as errors (disabled by default)
|
||||
// Override by setting warningsAsErrors=true in your ~/.gradle/gradle.properties
|
||||
val warningsAsErrors: String? by project
|
||||
allWarningsAsErrors.set(warningsAsErrors.toBoolean())
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
"coreLibraryDesugaring"(libs.desugar)
|
||||
}
|
||||
}
|
||||
|
||||
internal fun Project.configureCompose(commonExtension: CommonExtension<*, *, *, *, *, *>) {
|
||||
pluginManager.apply(kotlinx.plugins.compose.compiler.get().pluginId)
|
||||
|
||||
commonExtension.apply {
|
||||
buildFeatures {
|
||||
compose = true
|
||||
}
|
||||
|
||||
dependencies {
|
||||
"implementation"(platform(compose.bom))
|
||||
}
|
||||
}
|
||||
|
||||
extensions.configure<ComposeCompilerGradlePluginExtension> {
|
||||
val enableMetrics = project.providers.gradleProperty("enableComposeCompilerMetrics").orNull.toBoolean()
|
||||
val enableReports = project.providers.gradleProperty("enableComposeCompilerReports").orNull.toBoolean()
|
||||
|
||||
val rootBuildDir = rootProject.layout.buildDirectory.asFile.get()
|
||||
val relativePath = projectDir.relativeTo(rootDir)
|
||||
|
||||
if (enableMetrics) {
|
||||
rootBuildDir.resolve("compose-metrics").resolve(relativePath).let(metricsDestination::set)
|
||||
}
|
||||
|
||||
if (enableReports) {
|
||||
rootBuildDir.resolve("compose-reports").resolve(relativePath).let(reportsDestination::set)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
internal fun Project.configureTest() {
|
||||
tasks.withType<Test> {
|
||||
useJUnitPlatform()
|
||||
testLogging {
|
||||
events(TestLogEvent.PASSED, TestLogEvent.SKIPPED, TestLogEvent.FAILED)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val Project.generatedBuildDir: File get() = project.layout.buildDirectory.asFile.get().resolve("generated/mihon")
|
||||
@@ -1,36 +0,0 @@
|
||||
package mihon.buildlogic.tasks
|
||||
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.api.Task
|
||||
import org.gradle.api.tasks.TaskProvider
|
||||
import java.io.File
|
||||
|
||||
private val emptyResourcesElement = "<resources>\\s*</resources>|<resources\\s*/>".toRegex()
|
||||
|
||||
fun Project.getLocalesConfigTask(outputResourceDir: File): TaskProvider<Task> {
|
||||
return tasks.register("generateLocalesConfig") {
|
||||
val locales = fileTree("$projectDir/src/commonMain/moko-resources/")
|
||||
.matching { include("**/strings.xml") }
|
||||
.filterNot { it.readText().contains(emptyResourcesElement) }
|
||||
.map {
|
||||
it.parentFile.name
|
||||
.replace("base", "en")
|
||||
.replace("-r", "-")
|
||||
.replace("+", "-")
|
||||
}
|
||||
.sorted()
|
||||
.joinToString("\n") { "| <locale android:name=\"$it\"/>" }
|
||||
|
||||
val content = """
|
||||
|<?xml version="1.0" encoding="utf-8"?>
|
||||
|<locale-config xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
$locales
|
||||
|</locale-config>
|
||||
""".trimMargin()
|
||||
|
||||
outputResourceDir.resolve("xml/locales_config.xml").apply {
|
||||
parentFile.mkdirs()
|
||||
writeText(content)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,20 +1,16 @@
|
||||
plugins {
|
||||
id("mihon.library")
|
||||
kotlin("android")
|
||||
kotlin("plugin.serialization")
|
||||
alias(mihonx.plugins.android.library)
|
||||
alias(mihonx.plugins.spotless)
|
||||
|
||||
alias(libs.plugins.kotlin.serialization)
|
||||
}
|
||||
|
||||
android {
|
||||
namespace = "tachiyomi.core.metadata"
|
||||
|
||||
defaultConfig {
|
||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||
consumerProguardFiles("consumer-rules.pro")
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation(projects.sourceApi)
|
||||
|
||||
implementation(kotlinx.bundles.serialization)
|
||||
implementation(libs.bundles.serialization)
|
||||
}
|
||||
|
||||
Vendored
-21
@@ -1,21 +0,0 @@
|
||||
# Add project specific ProGuard rules here.
|
||||
# You can control the set of applied configuration files using the
|
||||
# proguardFiles setting in build.gradle.
|
||||
#
|
||||
# For more details, see
|
||||
# http://developer.android.com/guide/developing/tools/proguard.html
|
||||
|
||||
# If your project uses WebView with JS, uncomment the following
|
||||
# and specify the fully qualified class name to the JavaScript interface
|
||||
# class:
|
||||
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
||||
# public *;
|
||||
#}
|
||||
|
||||
# Uncomment this to preserve the line number information for
|
||||
# debugging stack traces.
|
||||
#-keepattributes SourceFile,LineNumberTable
|
||||
|
||||
# If you keep the line number information, uncomment this to
|
||||
# hide the original source file name.
|
||||
#-renamesourcefileattribute SourceFile
|
||||
@@ -1,7 +1,8 @@
|
||||
plugins {
|
||||
id("mihon.library")
|
||||
kotlin("android")
|
||||
kotlin("plugin.serialization")
|
||||
alias(mihonx.plugins.android.library)
|
||||
alias(mihonx.plugins.spotless)
|
||||
|
||||
alias(libs.plugins.kotlin.serialization)
|
||||
}
|
||||
|
||||
android {
|
||||
@@ -10,6 +11,6 @@ android {
|
||||
|
||||
dependencies {
|
||||
implementation(libs.jsoup)
|
||||
implementation(libs.libarchive)
|
||||
implementation(libs.archive)
|
||||
implementation(libs.unifile)
|
||||
}
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
plugins {
|
||||
id("mihon.library")
|
||||
kotlin("android")
|
||||
kotlin("plugin.serialization")
|
||||
alias(mihonx.plugins.android.library)
|
||||
alias(mihonx.plugins.spotless)
|
||||
|
||||
alias(libs.plugins.kotlin.serialization)
|
||||
}
|
||||
|
||||
android {
|
||||
@@ -22,24 +23,24 @@ dependencies {
|
||||
|
||||
api(libs.logcat)
|
||||
|
||||
api(libs.rxjava)
|
||||
api(libs.rxJava)
|
||||
|
||||
api(libs.okhttp.core)
|
||||
api(libs.okhttp.logging)
|
||||
api(libs.okhttp.brotli)
|
||||
api(libs.okhttp.dnsoverhttps)
|
||||
api(libs.okhttp.dnsOverHttps)
|
||||
api(libs.okio)
|
||||
|
||||
implementation(libs.image.decoder)
|
||||
|
||||
implementation(libs.unifile)
|
||||
implementation(libs.libarchive)
|
||||
implementation(libs.archive)
|
||||
|
||||
api(kotlinx.coroutines.core)
|
||||
api(kotlinx.serialization.json)
|
||||
api(kotlinx.serialization.json.okio)
|
||||
api(libs.kotlinx.coroutines.core)
|
||||
api(libs.kotlinx.serialization.json)
|
||||
api(libs.kotlinx.serialization.jsonOkio)
|
||||
|
||||
api(libs.preferencektx)
|
||||
api(libs.androidx.preference)
|
||||
|
||||
implementation(libs.jsoup)
|
||||
|
||||
@@ -47,7 +48,7 @@ dependencies {
|
||||
implementation(libs.natural.comparator)
|
||||
|
||||
// JavaScript engine
|
||||
implementation(libs.bundles.js.engine)
|
||||
implementation(libs.quickJs)
|
||||
|
||||
testImplementation(libs.bundles.test)
|
||||
testRuntimeOnly(libs.junit.platform.launcher)
|
||||
|
||||
@@ -77,7 +77,7 @@ private suspend fun Call.await(callStack: Array<StackTraceElement>): Response {
|
||||
val callback =
|
||||
object : Callback {
|
||||
override fun onResponse(call: Call, response: Response) {
|
||||
continuation.resume(response) {
|
||||
continuation.resume(response) { _, _, _ ->
|
||||
response.body.close()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,9 @@ package tachiyomi.core.common
|
||||
object Constants {
|
||||
const val URL_HELP = "https://mihon.app/docs/guides/troubleshooting/"
|
||||
const val URL_HELP_UPCOMING = "https://mihon.app/docs/faq/updates/upcoming"
|
||||
const val URL_DONATE = "https://patreon.com/mihon/membership"
|
||||
const val URL_DONATE_PATREON = "https://patreon.com/mihon/membership"
|
||||
const val URL_DONATE_OPENCOLLECTIVE = "https://opencollective.com/mihon/contribute"
|
||||
const val URL_DISCORD = "https://discord.gg/mihon"
|
||||
|
||||
const val MANGA_EXTRA = "manga"
|
||||
|
||||
|
||||
@@ -205,7 +205,11 @@ object ImageUtil {
|
||||
*/
|
||||
private fun isTallImage(imageSource: BufferedSource): Boolean {
|
||||
val options = extractImageOptions(imageSource)
|
||||
return (options.outHeight / options.outWidth) > 3
|
||||
return TallImageSplitCalculator.shouldSplit(
|
||||
imageWidth = options.outWidth,
|
||||
imageHeight = options.outHeight,
|
||||
optimalImageHeight = optimalImageHeight,
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -226,7 +230,6 @@ object ImageUtil {
|
||||
val options = extractImageOptions(imageSource).apply {
|
||||
inJustDecodeBounds = false
|
||||
}
|
||||
|
||||
val splitDataList = options.splitData
|
||||
|
||||
return try {
|
||||
@@ -273,8 +276,7 @@ object ImageUtil {
|
||||
val imageHeight = outHeight
|
||||
val imageWidth = outWidth
|
||||
|
||||
// -1 so it doesn't try to split when imageHeight = optimalImageHeight
|
||||
val partCount = (imageHeight - 1) / optimalImageHeight + 1
|
||||
val partCount = TallImageSplitCalculator.calculatePartCount(imageHeight, optimalImageHeight)
|
||||
val optimalSplitHeight = imageHeight / partCount
|
||||
|
||||
logcat {
|
||||
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
package tachiyomi.core.common.util.system
|
||||
|
||||
internal object TallImageSplitCalculator {
|
||||
|
||||
fun calculatePartCount(imageHeight: Int, optimalImageHeight: Int): Int {
|
||||
require(imageHeight > 0) { "imageHeight must be positive" }
|
||||
require(optimalImageHeight > 0) { "optimalImageHeight must be positive" }
|
||||
// -1 so it doesn't try to split when imageHeight = optimalImageHeight
|
||||
return (imageHeight - 1) / optimalImageHeight + 1
|
||||
}
|
||||
|
||||
fun shouldSplit(imageWidth: Int, imageHeight: Int, optimalImageHeight: Int): Boolean {
|
||||
require(imageWidth > 0) { "imageWidth must be positive" }
|
||||
return imageHeight > imageWidth * 3 &&
|
||||
calculatePartCount(imageHeight, optimalImageHeight) > 1
|
||||
}
|
||||
}
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
package tachiyomi.core.common.util.system
|
||||
|
||||
import org.junit.jupiter.api.Assertions.assertEquals
|
||||
import org.junit.jupiter.api.Assertions.assertFalse
|
||||
import org.junit.jupiter.api.Assertions.assertTrue
|
||||
import org.junit.jupiter.api.Test
|
||||
|
||||
class TallImageSplitCalculatorTest {
|
||||
|
||||
@Test
|
||||
fun `does not split when aspect ratio is tall but computed split count is one`() {
|
||||
assertFalse(
|
||||
TallImageSplitCalculator.shouldSplit(
|
||||
imageWidth = 1024,
|
||||
imageHeight = 4385,
|
||||
optimalImageHeight = 4386,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `splits when aspect ratio is tall and computed split count is greater than one`() {
|
||||
assertTrue(
|
||||
TallImageSplitCalculator.shouldSplit(
|
||||
imageWidth = 1024,
|
||||
imageHeight = 4385,
|
||||
optimalImageHeight = 4384,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `calculate part count rounds boundary correctly`() {
|
||||
assertEquals(1, TallImageSplitCalculator.calculatePartCount(imageHeight = 4384, optimalImageHeight = 4384))
|
||||
assertEquals(2, TallImageSplitCalculator.calculatePartCount(imageHeight = 4385, optimalImageHeight = 4384))
|
||||
}
|
||||
}
|
||||
@@ -1,22 +1,19 @@
|
||||
plugins {
|
||||
id("mihon.library")
|
||||
kotlin("android")
|
||||
kotlin("plugin.serialization")
|
||||
alias(mihonx.plugins.android.library)
|
||||
alias(mihonx.plugins.spotless)
|
||||
|
||||
alias(libs.plugins.kotlin.serialization)
|
||||
alias(libs.plugins.sqldelight)
|
||||
}
|
||||
|
||||
android {
|
||||
namespace = "tachiyomi.data"
|
||||
|
||||
defaultConfig {
|
||||
consumerProguardFiles("consumer-rules.pro")
|
||||
}
|
||||
|
||||
sqldelight {
|
||||
databases {
|
||||
create("Database") {
|
||||
packageName.set("tachiyomi.data")
|
||||
dialect(libs.sqldelight.dialects.sql)
|
||||
dialect(libs.sqldelight.sqliteDialect338)
|
||||
schemaOutputDirectory.set(project.file("./src/main/sqldelight"))
|
||||
}
|
||||
}
|
||||
|
||||
Vendored
-21
@@ -1,21 +0,0 @@
|
||||
# Add project specific ProGuard rules here.
|
||||
# You can control the set of applied configuration files using the
|
||||
# proguardFiles setting in build.gradle.
|
||||
#
|
||||
# For more details, see
|
||||
# http://developer.android.com/guide/developing/tools/proguard.html
|
||||
|
||||
# If your project uses WebView with JS, uncomment the following
|
||||
# and specify the fully qualified class name to the JavaScript interface
|
||||
# class:
|
||||
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
||||
# public *;
|
||||
#}
|
||||
|
||||
# Uncomment this to preserve the line number information for
|
||||
# debugging stack traces.
|
||||
#-keepattributes SourceFile,LineNumberTable
|
||||
|
||||
# If you keep the line number information, uncomment this to
|
||||
# hide the original source file name.
|
||||
#-renamesourcefileattribute SourceFile
|
||||
@@ -18,7 +18,7 @@ SELECT
|
||||
mangas.cover_last_modified AS coverLastModified,
|
||||
chapters.date_upload AS dateUpload,
|
||||
chapters.date_fetch AS datefetch,
|
||||
excluded_scanlators.scanlator as excludedScanlator
|
||||
excluded_scanlators.scanlator AS excludedScanlator
|
||||
FROM mangas JOIN chapters
|
||||
ON mangas._id = chapters.manga_id
|
||||
LEFT JOIN excluded_scanlators
|
||||
|
||||
+9
-14
@@ -1,16 +1,12 @@
|
||||
plugins {
|
||||
id("mihon.library")
|
||||
kotlin("android")
|
||||
kotlin("plugin.serialization")
|
||||
alias(mihonx.plugins.android.library)
|
||||
alias(mihonx.plugins.spotless)
|
||||
|
||||
alias(libs.plugins.kotlin.serialization)
|
||||
}
|
||||
|
||||
android {
|
||||
namespace = "tachiyomi.domain"
|
||||
|
||||
defaultConfig {
|
||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||
consumerProguardFiles("consumer-rules.pro")
|
||||
}
|
||||
}
|
||||
|
||||
kotlin {
|
||||
@@ -23,17 +19,16 @@ dependencies {
|
||||
implementation(projects.sourceApi)
|
||||
implementation(projects.core.common)
|
||||
|
||||
implementation(platform(kotlinx.coroutines.bom))
|
||||
implementation(kotlinx.bundles.coroutines)
|
||||
implementation(kotlinx.bundles.serialization)
|
||||
implementation(libs.bundles.kotlinx.coroutines)
|
||||
implementation(libs.bundles.serialization)
|
||||
|
||||
implementation(libs.unifile)
|
||||
|
||||
api(libs.sqldelight.android.paging)
|
||||
api(libs.sqldelight.androidxPaging)
|
||||
|
||||
compileOnly(compose.runtime.annotation)
|
||||
compileOnly(libs.androidx.compose.runtimeAnnotation)
|
||||
|
||||
testImplementation(libs.bundles.test)
|
||||
testImplementation(kotlinx.coroutines.test)
|
||||
testImplementation(libs.kotlinx.coroutines.test)
|
||||
testRuntimeOnly(libs.junit.platform.launcher)
|
||||
}
|
||||
|
||||
Vendored
-21
@@ -1,21 +0,0 @@
|
||||
# Add project specific ProGuard rules here.
|
||||
# You can control the set of applied configuration files using the
|
||||
# proguardFiles setting in build.gradle.
|
||||
#
|
||||
# For more details, see
|
||||
# http://developer.android.com/guide/developing/tools/proguard.html
|
||||
|
||||
# If your project uses WebView with JS, uncomment the following
|
||||
# and specify the fully qualified class name to the JavaScript interface
|
||||
# class:
|
||||
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
||||
# public *;
|
||||
#}
|
||||
|
||||
# Uncomment this to preserve the line number information for
|
||||
# debugging stack traces.
|
||||
#-keepattributes SourceFile,LineNumberTable
|
||||
|
||||
# If you keep the line number information, uncomment this to
|
||||
# hide the original source file name.
|
||||
#-renamesourcefileattribute SourceFile
|
||||
@@ -4,6 +4,7 @@ android.useAndroidX=true
|
||||
kotlin.code.style=official
|
||||
kotlin.mpp.androidSourceSetLayoutVersion=2
|
||||
|
||||
# Configuration should be synced with [/gradle/build-logic/gradle.properties]
|
||||
org.gradle.caching=true
|
||||
org.gradle.configureondemand=true
|
||||
org.gradle.jvmargs=-Xmx4g -Dfile.encoding=UTF-8
|
||||
|
||||
@@ -1,40 +0,0 @@
|
||||
[versions]
|
||||
agp_version = "8.13.2"
|
||||
lifecycle_version = "2.10.0"
|
||||
paging_version = "3.4.2"
|
||||
interpolator_version = "1.0.0"
|
||||
sqlite = "2.6.2"
|
||||
|
||||
[libraries]
|
||||
gradle = { module = "com.android.tools.build:gradle", version.ref = "agp_version" }
|
||||
|
||||
annotation = "androidx.annotation:annotation:1.9.1"
|
||||
appcompat = "androidx.appcompat:appcompat:1.7.1"
|
||||
biometricktx = "androidx.biometric:biometric-ktx:1.2.0-alpha05"
|
||||
constraintlayout = "androidx.constraintlayout:constraintlayout:2.2.1"
|
||||
corektx = "androidx.core:core-ktx:1.18.0"
|
||||
splashscreen = "androidx.core:core-splashscreen:1.2.0"
|
||||
recyclerview = "androidx.recyclerview:recyclerview:1.4.0"
|
||||
viewpager = "androidx.viewpager:viewpager:1.1.0"
|
||||
profileinstaller = "androidx.profileinstaller:profileinstaller:1.4.1"
|
||||
|
||||
lifecycle-common = { module = "androidx.lifecycle:lifecycle-common", version.ref = "lifecycle_version" }
|
||||
lifecycle-process = { module = "androidx.lifecycle:lifecycle-process", version.ref = "lifecycle_version" }
|
||||
lifecycle-runtimektx = { module = "androidx.lifecycle:lifecycle-runtime-ktx", version.ref = "lifecycle_version" }
|
||||
|
||||
workmanager = "androidx.work:work-runtime:2.11.1"
|
||||
|
||||
paging-runtime = { module = "androidx.paging:paging-runtime", version.ref = "paging_version" }
|
||||
paging-compose = { module = "androidx.paging:paging-compose", version.ref = "paging_version" }
|
||||
|
||||
interpolator = { group = "androidx.interpolator", name = "interpolator", version.ref = "interpolator_version" }
|
||||
|
||||
benchmark-macro = "androidx.benchmark:benchmark-macro-junit4:1.4.1"
|
||||
test-ext = "androidx.test.ext:junit-ktx:1.3.0"
|
||||
test-espresso-core = "androidx.test.espresso:espresso-core:3.7.0"
|
||||
test-uiautomator = "androidx.test.uiautomator:uiautomator:2.3.0"
|
||||
|
||||
sqlite-bundled = { module = "androidx.sqlite:sqlite-bundled", version.ref = "sqlite" }
|
||||
|
||||
[bundles]
|
||||
lifecycle = ["lifecycle-common", "lifecycle-process", "lifecycle-runtimektx"]
|
||||
@@ -0,0 +1,75 @@
|
||||
plugins {
|
||||
alias(libs.plugins.kotlin.jvm)
|
||||
alias(libs.plugins.kotlin.samWithReceiver)
|
||||
alias(libs.plugins.spotless)
|
||||
`java-gradle-plugin`
|
||||
}
|
||||
|
||||
// Configuration should be synced with [/gradle/build-logic/src/main/kotlin/PluginSpotless.kt]
|
||||
val ktlintVersion = libs.ktlint.bom.get().version
|
||||
val editorConfigFile = rootProject.file("../../.editorconfig")
|
||||
spotless {
|
||||
kotlin {
|
||||
target("src/**/*.kt")
|
||||
ktlint(ktlintVersion).setEditorConfigPath(editorConfigFile)
|
||||
trimTrailingWhitespace()
|
||||
endWithNewline()
|
||||
}
|
||||
|
||||
kotlinGradle {
|
||||
target("*.kts")
|
||||
ktlint(ktlintVersion).setEditorConfigPath(editorConfigFile)
|
||||
trimTrailingWhitespace()
|
||||
endWithNewline()
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compileOnly(gradleKotlinDsl())
|
||||
compileOnly(libs.android.gradle)
|
||||
compileOnly(libs.kotlin.compose.compiler.gradle)
|
||||
compileOnly(libs.kotlin.gradle)
|
||||
implementation(libs.spotless.gradle)
|
||||
implementation(libs.tapmoc.gradle)
|
||||
|
||||
// These allow us to reference the dependency catalog inside our compiled plugins
|
||||
compileOnly(files(libs::class.java.superclass.protectionDomain.codeSource.location))
|
||||
compileOnly(files(mihonx::class.java.superclass.protectionDomain.codeSource.location))
|
||||
}
|
||||
|
||||
samWithReceiver {
|
||||
annotation("org.gradle.api.HasImplicitReceiver")
|
||||
}
|
||||
|
||||
gradlePlugin {
|
||||
plugins {
|
||||
register("android-application") {
|
||||
id = mihonx.plugins.android.application.get().pluginId
|
||||
implementationClass = "PluginAndroidApplication"
|
||||
}
|
||||
register("android-base") {
|
||||
id = mihonx.plugins.android.base.get().pluginId
|
||||
implementationClass = "PluginAndroidBase"
|
||||
}
|
||||
register("android-library") {
|
||||
id = mihonx.plugins.android.library.get().pluginId
|
||||
implementationClass = "PluginAndroidLibrary"
|
||||
}
|
||||
register("android-test") {
|
||||
id = mihonx.plugins.android.test.get().pluginId
|
||||
implementationClass = "PluginAndroidTest"
|
||||
}
|
||||
register("compose-android") {
|
||||
id = mihonx.plugins.compose.get().pluginId
|
||||
implementationClass = "PluginComposeAndroid"
|
||||
}
|
||||
register("kotlin-multiplatform") {
|
||||
id = mihonx.plugins.kotlin.multiplatform.get().pluginId
|
||||
implementationClass = "PluginKotlinMultiplatform"
|
||||
}
|
||||
register("spotless") {
|
||||
id = mihonx.plugins.spotless.get().pluginId
|
||||
implementationClass = "PluginSpotless"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
# Configuration should be synced with [/gradle.properties]
|
||||
#
|
||||
# Gradle properties are not passed to included builds
|
||||
# https://github.com/gradle/gradle/issues/2534
|
||||
org.gradle.caching=true
|
||||
org.gradle.configureondemand=true
|
||||
org.gradle.jvmargs=-Xmx4g -Dfile.encoding=UTF-8
|
||||
org.gradle.parallel=true
|
||||
@@ -0,0 +1,18 @@
|
||||
dependencyResolutionManagement {
|
||||
@Suppress("UnstableApiUsage")
|
||||
repositories {
|
||||
google()
|
||||
mavenCentral()
|
||||
gradlePluginPortal()
|
||||
}
|
||||
versionCatalogs {
|
||||
create("libs") {
|
||||
from(files("../libs.versions.toml"))
|
||||
}
|
||||
create("mihonx") {
|
||||
from(files("../mihon.versions.toml"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
rootProject.name = "build-logic"
|
||||
@@ -0,0 +1,16 @@
|
||||
import mihon.gradle.extensions.alias
|
||||
import mihon.gradle.extensions.libs
|
||||
import mihon.gradle.extensions.mihonx
|
||||
import mihon.gradle.extensions.plugins
|
||||
import org.gradle.api.Plugin
|
||||
import org.gradle.api.Project
|
||||
|
||||
@Suppress("UNUSED")
|
||||
class PluginAndroidApplication : Plugin<Project> {
|
||||
override fun apply(target: Project): Unit = with(target) {
|
||||
plugins {
|
||||
alias(libs.plugins.android.application)
|
||||
alias(mihonx.plugins.android.base)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
import mihon.gradle.configurations.configureAndroid
|
||||
import mihon.gradle.configurations.configureKotlin
|
||||
import mihon.gradle.extensions.alias
|
||||
import mihon.gradle.extensions.configureTest
|
||||
import mihon.gradle.extensions.libs
|
||||
import mihon.gradle.extensions.plugins
|
||||
import org.gradle.api.Plugin
|
||||
import org.gradle.api.Project
|
||||
|
||||
@Suppress("UNUSED")
|
||||
class PluginAndroidBase : Plugin<Project> {
|
||||
override fun apply(target: Project): Unit = with(target) {
|
||||
plugins {
|
||||
alias(libs.plugins.kotlin.android)
|
||||
}
|
||||
|
||||
configureKotlin()
|
||||
configureTest()
|
||||
configureAndroid()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
import mihon.gradle.extensions.alias
|
||||
import mihon.gradle.extensions.libs
|
||||
import mihon.gradle.extensions.mihonx
|
||||
import mihon.gradle.extensions.plugins
|
||||
import org.gradle.api.Plugin
|
||||
import org.gradle.api.Project
|
||||
|
||||
@Suppress("UNUSED")
|
||||
class PluginAndroidLibrary : Plugin<Project> {
|
||||
override fun apply(target: Project): Unit = with(target) {
|
||||
plugins {
|
||||
alias(libs.plugins.android.library)
|
||||
alias(mihonx.plugins.android.base)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
import mihon.gradle.extensions.alias
|
||||
import mihon.gradle.extensions.libs
|
||||
import mihon.gradle.extensions.mihonx
|
||||
import mihon.gradle.extensions.plugins
|
||||
import org.gradle.api.Plugin
|
||||
import org.gradle.api.Project
|
||||
|
||||
@Suppress("UNUSED")
|
||||
class PluginAndroidTest : Plugin<Project> {
|
||||
override fun apply(target: Project): Unit = with(target) {
|
||||
plugins {
|
||||
alias(libs.plugins.android.test)
|
||||
alias(mihonx.plugins.android.base)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
import mihon.gradle.extensions.alias
|
||||
import mihon.gradle.extensions.android
|
||||
import mihon.gradle.extensions.api
|
||||
import mihon.gradle.extensions.debugApi
|
||||
import mihon.gradle.extensions.implementation
|
||||
import mihon.gradle.extensions.libs
|
||||
import mihon.gradle.extensions.plugins
|
||||
import org.gradle.api.Plugin
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.kotlin.dsl.dependencies
|
||||
|
||||
@Suppress("UNUSED")
|
||||
class PluginComposeAndroid : Plugin<Project> {
|
||||
override fun apply(target: Project): Unit = with(target) {
|
||||
plugins {
|
||||
alias(libs.plugins.kotlin.compose.compiler)
|
||||
}
|
||||
|
||||
android {
|
||||
buildFeatures {
|
||||
compose = true
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation(platform(libs.androidx.compose.bom))
|
||||
|
||||
// Compose @Preview tooling
|
||||
api(libs.androidx.compose.uiToolingPreview)
|
||||
debugApi(libs.androidx.compose.uiTooling)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
import mihon.gradle.configurations.configureAndroid
|
||||
import mihon.gradle.configurations.configureKotlin
|
||||
import mihon.gradle.extensions.alias
|
||||
import mihon.gradle.extensions.configureTest
|
||||
import mihon.gradle.extensions.libs
|
||||
import mihon.gradle.extensions.plugins
|
||||
import org.gradle.api.Plugin
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.kotlin.dsl.configure
|
||||
import org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi
|
||||
import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension
|
||||
|
||||
@Suppress("UNUSED")
|
||||
class PluginKotlinMultiplatform : Plugin<Project> {
|
||||
override fun apply(target: Project): Unit = with(target) {
|
||||
plugins {
|
||||
alias(libs.plugins.android.library)
|
||||
alias(libs.plugins.kotlin.multiplatform)
|
||||
}
|
||||
|
||||
configureKotlin()
|
||||
configureTest()
|
||||
configureAndroid()
|
||||
|
||||
kotlin {
|
||||
@OptIn(ExperimentalKotlinGradlePluginApi::class)
|
||||
applyDefaultHierarchyTemplate()
|
||||
|
||||
androidTarget()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun Project.kotlin(block: KotlinMultiplatformExtension.() -> Unit) {
|
||||
extensions.configure(block)
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
import com.diffplug.gradle.spotless.SpotlessExtension
|
||||
import mihon.gradle.extensions.alias
|
||||
import mihon.gradle.extensions.libs
|
||||
import mihon.gradle.extensions.plugins
|
||||
import org.gradle.api.Plugin
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.kotlin.dsl.configure
|
||||
|
||||
@Suppress("UNUSED")
|
||||
class PluginSpotless : Plugin<Project> {
|
||||
override fun apply(target: Project): Unit = with(target) {
|
||||
plugins {
|
||||
alias(libs.plugins.spotless)
|
||||
}
|
||||
|
||||
// Configuration should be synced with [/gradle/build-logic/build.gradle.kts]
|
||||
val ktlintVersion = libs.ktlint.bom.get().version
|
||||
spotless {
|
||||
kotlin {
|
||||
target("src/**/*.kt")
|
||||
ktlint(ktlintVersion)
|
||||
trimTrailingWhitespace()
|
||||
endWithNewline()
|
||||
}
|
||||
|
||||
kotlinGradle {
|
||||
target("*.kts")
|
||||
ktlint(ktlintVersion)
|
||||
trimTrailingWhitespace()
|
||||
endWithNewline()
|
||||
}
|
||||
|
||||
format("xml") {
|
||||
target("src/**/*.xml")
|
||||
trimTrailingWhitespace()
|
||||
endWithNewline()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun Project.spotless(block: SpotlessExtension.() -> Unit) {
|
||||
extensions.configure(block)
|
||||
}
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package mihon.buildlogic
|
||||
package mihon.gradle
|
||||
|
||||
import org.gradle.api.Project
|
||||
|
||||
+10
-10
@@ -1,4 +1,4 @@
|
||||
package mihon.buildlogic
|
||||
package mihon.gradle
|
||||
|
||||
import org.gradle.api.Project
|
||||
import java.time.Instant
|
||||
@@ -8,33 +8,33 @@ import java.time.format.DateTimeFormatter
|
||||
|
||||
// Git is needed in your system PATH for these commands to work.
|
||||
// If it's not installed, you can return a random value as a workaround
|
||||
fun Project.getCommitCount(): String {
|
||||
return runCommand("git rev-list --count HEAD")
|
||||
fun Project.getLatestCommitCount(): String {
|
||||
return exec("git rev-list --count HEAD")
|
||||
// return "1"
|
||||
}
|
||||
|
||||
fun Project.getGitSha(): String {
|
||||
return runCommand("git rev-parse --short HEAD")
|
||||
fun Project.getLatestCommitSha(): String {
|
||||
return exec("git rev-parse --short HEAD")
|
||||
// return "1"
|
||||
}
|
||||
|
||||
private val BUILD_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss'Z'")
|
||||
|
||||
/**
|
||||
* @param useLastCommitTime If `true`, the build time is based on the timestamp of the last Git commit;
|
||||
* @param useLatestCommitTime If `true`, the build time is based on the timestamp of the last Git commit;
|
||||
* otherwise, the current time is used. Both are in UTC.
|
||||
* @return A formatted string representing the build time. The format used is defined by [BUILD_TIME_FORMATTER].
|
||||
*/
|
||||
fun Project.getBuildTime(useLastCommitTime: Boolean): String {
|
||||
return if (useLastCommitTime) {
|
||||
val epoch = runCommand("git log -1 --format=%ct").toLong()
|
||||
fun Project.getBuildTime(useLatestCommitTime: Boolean): String {
|
||||
return if (useLatestCommitTime) {
|
||||
val epoch = exec("git log -1 --format=%ct").toLong()
|
||||
Instant.ofEpochSecond(epoch).atOffset(ZoneOffset.UTC).format(BUILD_TIME_FORMATTER)
|
||||
} else {
|
||||
LocalDateTime.now(ZoneOffset.UTC).format(BUILD_TIME_FORMATTER)
|
||||
}
|
||||
}
|
||||
|
||||
private fun Project.runCommand(command: String): String {
|
||||
fun Project.exec(command: String): String {
|
||||
return providers.exec {
|
||||
commandLine = command.split(" ")
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package mihon.gradle.configurations
|
||||
|
||||
import com.android.build.api.dsl.ApplicationDefaultConfig
|
||||
import mihon.gradle.extensions.android
|
||||
import mihon.gradle.extensions.coreLibraryDesugaring
|
||||
import mihon.gradle.extensions.libs
|
||||
import mihon.gradle.extensions.mihonx
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.kotlin.dsl.dependencies
|
||||
|
||||
fun Project.configureAndroid() {
|
||||
android {
|
||||
defaultConfig {
|
||||
minSdk = mihonx.versions.android.sdk.min.get().toInt()
|
||||
if (this is ApplicationDefaultConfig) {
|
||||
targetSdk = mihonx.versions.android.sdk.target.get().toInt()
|
||||
}
|
||||
|
||||
ndkVersion = mihonx.versions.android.ndk.get()
|
||||
}
|
||||
|
||||
compileSdk = mihonx.versions.android.sdk.compile.get().toInt()
|
||||
|
||||
compileOptions {
|
||||
isCoreLibraryDesugaringEnabled = true
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
coreLibraryDesugaring(libs.android.desugar)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package mihon.gradle.configurations
|
||||
|
||||
import mihon.gradle.extensions.mihonx
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.kotlin.dsl.configure
|
||||
import org.jetbrains.kotlin.gradle.dsl.HasConfigurableKotlinCompilerOptions
|
||||
import org.jetbrains.kotlin.gradle.dsl.KotlinBaseExtension
|
||||
import org.jetbrains.kotlin.gradle.dsl.KotlinCommonCompilerOptions
|
||||
import tapmoc.configureJavaCompatibility
|
||||
|
||||
fun Project.configureKotlin() {
|
||||
configureJavaCompatibility(mihonx.versions.java.get().toInt())
|
||||
|
||||
kotlin {
|
||||
compilerOptions {
|
||||
freeCompilerArgs.add("-Xcontext-parameters")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun Project.kotlin(block: KotlinBaseExtension.() -> Unit) {
|
||||
extensions.configure(block)
|
||||
}
|
||||
|
||||
private fun KotlinBaseExtension.compilerOptions(block: KotlinCommonCompilerOptions.() -> Unit) {
|
||||
if (this is HasConfigurableKotlinCompilerOptions<*>) compilerOptions(block)
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package mihon.gradle.extensions
|
||||
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.api.artifacts.ExternalModuleDependencyBundle
|
||||
import org.gradle.api.artifacts.MinimalExternalModuleDependency
|
||||
import org.gradle.api.provider.Provider
|
||||
import org.gradle.kotlin.dsl.DependencyHandlerScope
|
||||
|
||||
fun DependencyHandlerScope.api(dependencyNotation: Provider<MinimalExternalModuleDependency>) {
|
||||
add("api", dependencyNotation)
|
||||
}
|
||||
|
||||
fun DependencyHandlerScope.coreLibraryDesugaring(dependencyNotation: Provider<MinimalExternalModuleDependency>) {
|
||||
add("coreLibraryDesugaring", dependencyNotation)
|
||||
}
|
||||
|
||||
fun DependencyHandlerScope.debugApi(dependencyNotation: Provider<MinimalExternalModuleDependency>) {
|
||||
add("debugApi", dependencyNotation)
|
||||
}
|
||||
|
||||
@JvmName("implementationBundle")
|
||||
fun DependencyHandlerScope.implementation(dependencyNotation: Provider<ExternalModuleDependencyBundle>) {
|
||||
add("implementation", dependencyNotation)
|
||||
}
|
||||
|
||||
fun DependencyHandlerScope.implementation(dependencyNotation: Provider<MinimalExternalModuleDependency>) {
|
||||
add("implementation", dependencyNotation)
|
||||
}
|
||||
|
||||
fun DependencyHandlerScope.implementation(dependencyNotation: Project) {
|
||||
add("implementation", dependencyNotation)
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package mihon.gradle.extensions
|
||||
|
||||
import org.gradle.api.plugins.PluginManager
|
||||
import org.gradle.api.provider.Provider
|
||||
import org.gradle.plugin.use.PluginDependency
|
||||
|
||||
fun PluginManager.alias(notation: Provider<PluginDependency>) {
|
||||
apply(notation.get().pluginId)
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
package mihon.gradle.extensions
|
||||
|
||||
import com.android.build.api.dsl.CommonExtension
|
||||
import com.android.build.gradle.BaseExtension
|
||||
import org.gradle.accessors.dm.LibrariesForLibs
|
||||
import org.gradle.accessors.dm.LibrariesForMihonx
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.api.plugins.PluginManager
|
||||
import org.gradle.api.tasks.testing.Test
|
||||
import org.gradle.api.tasks.testing.logging.TestLogEvent
|
||||
import org.gradle.kotlin.dsl.configure
|
||||
import org.gradle.kotlin.dsl.the
|
||||
import org.gradle.kotlin.dsl.withType
|
||||
|
||||
internal val Project.libs get() = the<LibrariesForLibs>()
|
||||
internal val Project.mihonx get() = the<LibrariesForMihonx>()
|
||||
|
||||
internal fun Project.plugins(block: PluginManager.() -> Unit) {
|
||||
pluginManager.apply(block)
|
||||
}
|
||||
|
||||
internal fun Project.android(block: CommonExtension<*, *, *, *, *, *>.() -> Unit) {
|
||||
extensions.configure<BaseExtension> {
|
||||
if (this is CommonExtension<*, *, *, *, *, *>) apply(block)
|
||||
}
|
||||
}
|
||||
|
||||
fun Project.configureTest() {
|
||||
tasks.withType<Test> {
|
||||
useJUnitPlatform()
|
||||
testLogging {
|
||||
events(TestLogEvent.PASSED, TestLogEvent.SKIPPED, TestLogEvent.FAILED)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
package mihon.gradle.tasks
|
||||
|
||||
import org.gradle.api.DefaultTask
|
||||
import org.gradle.api.file.DirectoryProperty
|
||||
import org.gradle.api.model.ObjectFactory
|
||||
import org.gradle.api.tasks.OutputDirectory
|
||||
import org.gradle.api.tasks.TaskAction
|
||||
import javax.inject.Inject
|
||||
|
||||
abstract class GenerateLocalesConfigTask : DefaultTask() {
|
||||
|
||||
@get:Inject
|
||||
abstract val objectFactory: ObjectFactory
|
||||
|
||||
@get:OutputDirectory
|
||||
abstract val outputDir: DirectoryProperty
|
||||
|
||||
@TaskAction
|
||||
fun action() {
|
||||
val locales = objectFactory.fileTree()
|
||||
.from("src/commonMain/moko-resources")
|
||||
.matching { include("**/strings.xml") }
|
||||
.asSequence()
|
||||
.filterNot { it.readText().contains(emptyResourcesElement) }
|
||||
.map {
|
||||
it.parentFile.name
|
||||
.replace("base", "en")
|
||||
.replace("-r", "-")
|
||||
.replace("+", "-")
|
||||
}
|
||||
.distinct()
|
||||
.sorted()
|
||||
.joinToString("\n") { "| <locale android:name=\"$it\"/>" }
|
||||
|
||||
val content = """
|
||||
|<?xml version="1.0" encoding="utf-8"?>
|
||||
|<locale-config xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
$locales
|
||||
|</locale-config>
|
||||
""".trimMargin()
|
||||
|
||||
outputDir.get().file("xml/locales_config.xml").asFile.apply {
|
||||
parentFile.mkdirs()
|
||||
writeText(content)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val emptyResourcesElement = "<resources>\\s*</resources>|<resources\\s*/>".toRegex()
|
||||
}
|
||||
}
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
package mihon.gradle.tasks
|
||||
|
||||
import org.gradle.api.DefaultTask
|
||||
import org.gradle.api.file.DirectoryProperty
|
||||
import org.gradle.api.file.RegularFileProperty
|
||||
import org.gradle.api.provider.Property
|
||||
import org.gradle.api.tasks.Input
|
||||
import org.gradle.api.tasks.InputFile
|
||||
import org.gradle.api.tasks.OutputDirectory
|
||||
import org.gradle.api.tasks.TaskAction
|
||||
|
||||
abstract class ReplaceShortcutsPlaceholderTask : DefaultTask() {
|
||||
|
||||
@get:Input
|
||||
abstract val applicationId: Property<String>
|
||||
|
||||
@get:InputFile
|
||||
abstract val shortcutsFile: RegularFileProperty
|
||||
|
||||
@get:OutputDirectory
|
||||
abstract val outputDir: DirectoryProperty
|
||||
|
||||
@TaskAction
|
||||
fun action() {
|
||||
val content = shortcutsFile.asFile.get()
|
||||
.readText()
|
||||
.replace($$"${applicationId}", applicationId.get())
|
||||
|
||||
outputDir.get().file("xml/shortcuts.xml").asFile.apply {
|
||||
parentFile.mkdirs()
|
||||
writeText(content)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
[versions]
|
||||
compose-bom = "2026.03.00"
|
||||
|
||||
[libraries]
|
||||
activity = "androidx.activity:activity-compose:1.13.0"
|
||||
bom = { group = "androidx.compose", name = "compose-bom", version.ref = "compose-bom" }
|
||||
foundation = { module = "androidx.compose.foundation:foundation" }
|
||||
animation = { module = "androidx.compose.animation:animation" }
|
||||
animation-graphics = { module = "androidx.compose.animation:animation-graphics" }
|
||||
runtime = { module = "androidx.compose.runtime:runtime" }
|
||||
runtime-annotation = { module = "androidx.compose.runtime:runtime-annotation" }
|
||||
ui-tooling = { module = "androidx.compose.ui:ui-tooling" }
|
||||
ui-tooling-preview = { module = "androidx.compose.ui:ui-tooling-preview" }
|
||||
ui-util = { module = "androidx.compose.ui:ui-util" }
|
||||
|
||||
material3-core = { module = "androidx.compose.material3:material3" }
|
||||
material-icons = { module = "androidx.compose.material:material-icons-extended" }
|
||||
|
||||
glance = "androidx.glance:glance-appwidget:1.1.1"
|
||||
@@ -1,33 +0,0 @@
|
||||
[versions]
|
||||
kotlin_version = "2.3.20"
|
||||
serialization_version = "1.10.0"
|
||||
xml_serialization_version = "0.91.3"
|
||||
|
||||
[libraries]
|
||||
reflect = { module = "org.jetbrains.kotlin:kotlin-reflect", version.ref = "kotlin_version" }
|
||||
gradle = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin", version.ref = "kotlin_version" }
|
||||
compose-compiler-gradle = { module = "org.jetbrains.kotlin:compose-compiler-gradle-plugin", version.ref = "kotlin_version" }
|
||||
|
||||
immutables = { module = "org.jetbrains.kotlinx:kotlinx-collections-immutable", version = "0.4.0" }
|
||||
|
||||
coroutines-bom = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-bom", version = "1.10.2" }
|
||||
coroutines-core = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core" }
|
||||
coroutines-android = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-android" }
|
||||
coroutines-guava = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-guava" }
|
||||
coroutines-test = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-test" }
|
||||
|
||||
serialization-json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "serialization_version" }
|
||||
serialization-json-okio = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json-okio", version.ref = "serialization_version" }
|
||||
serialization-protobuf = { module = "org.jetbrains.kotlinx:kotlinx-serialization-protobuf", version.ref = "serialization_version" }
|
||||
|
||||
serialization-xml-core = { module = "io.github.pdvrieze.xmlutil:core-android", version.ref = "xml_serialization_version" }
|
||||
serialization-xml = { module = "io.github.pdvrieze.xmlutil:serialization", version.ref = "xml_serialization_version" }
|
||||
|
||||
[bundles]
|
||||
coroutines = ["coroutines-core", "coroutines-android", "coroutines-guava"]
|
||||
serialization = ["serialization-json", "serialization-json-okio", "serialization-protobuf", "serialization-xml-core", "serialization-xml"]
|
||||
|
||||
[plugins]
|
||||
android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin_version" }
|
||||
compose-compiler = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin_version" }
|
||||
serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin_version" }
|
||||
+195
-104
@@ -1,122 +1,213 @@
|
||||
[versions]
|
||||
aboutlib_version = "13.2.1"
|
||||
leakcanary = "2.14"
|
||||
moko = "0.26.1"
|
||||
okhttp_version = "5.3.2"
|
||||
shizuku_version = "13.1.5"
|
||||
sqldelight = "2.3.2"
|
||||
voyager = "1.1.0-beta03"
|
||||
spotless = "8.4.0"
|
||||
ktlint-core = "1.8.0"
|
||||
firebase-bom = "34.11.0"
|
||||
markdown = "0.39.2"
|
||||
aboutLibraries = "14.0.1"
|
||||
android-desugar = "2.1.5"
|
||||
android-gradle = "8.13.2"
|
||||
androidx-activity-compose = "1.13.0"
|
||||
androidx-annotation = "1.10.0"
|
||||
androidx-appCompat = "1.7.1"
|
||||
androidx-benchmark = "1.4.1"
|
||||
androidx-biometric = "1.2.0-alpha05"
|
||||
androidx-compose-bom = "2026.04.00"
|
||||
androidx-constraintLayout = "2.2.1"
|
||||
androidx-core = "1.18.0"
|
||||
androidx-coreSplashScreen = "1.2.0"
|
||||
androidx-glance = "1.1.1"
|
||||
androidx-interpolator = "1.0.0"
|
||||
androidx-lifecycle = "2.10.0"
|
||||
androidx-paging = "3.4.2"
|
||||
androidx-preference = "1.2.1"
|
||||
androidx-profileInstaller = "1.4.1"
|
||||
androidx-recyclerView = "1.4.0"
|
||||
androidx-sqlite = "2.6.2"
|
||||
androidx-test-espresso = "3.7.0"
|
||||
androidx-test-junit = "1.3.0"
|
||||
androidx-test-uiautomator = "2.3.0"
|
||||
androidx-viewPager = "1.1.0"
|
||||
androidx-work = "2.11.2"
|
||||
archive = "1.1.6"
|
||||
coil = "3.4.0"
|
||||
composeGrid = "1.2.2"
|
||||
composeMaterialMotion = "2.0.1"
|
||||
composeRichEditor = "1.0.0-rc13"
|
||||
composeWebview = "0.33.6"
|
||||
conscrypt = "2.5.3"
|
||||
directionalViewPager = "1.0.0"
|
||||
diskLruCache = "2.0.2"
|
||||
firebase-bom = "34.12.0"
|
||||
firebase-crashlytics = "3.0.7"
|
||||
flexibleAdapter = "c8013533"
|
||||
google-services = "4.4.4"
|
||||
image-decoder = "41c059e540"
|
||||
injekt = "91edab2317"
|
||||
jsoup = "1.22.1"
|
||||
junit = "6.0.3"
|
||||
kotest-assertions = "6.1.11"
|
||||
kotlin-gradle = "2.3.20"
|
||||
kotlinx-collections-immutable = "0.4.0"
|
||||
kotlinx-coroutines = "1.10.2"
|
||||
kotlinx-serialization = "1.11.0"
|
||||
ktlint = "1.8.0"
|
||||
leakCanary = "2.14"
|
||||
logcat = "0.4"
|
||||
markdown = "0.39.2"
|
||||
material = "1.13.0"
|
||||
materialKolor = "5.0.0-alpha07"
|
||||
mockk = "1.14.9"
|
||||
moko-resources = "0.26.3"
|
||||
okhttp = "5.3.2"
|
||||
okio = "3.17.0"
|
||||
photoView = "2.3.0"
|
||||
quickJs = "547f5b1597"
|
||||
reorderable = "3.0.0"
|
||||
rxJava = "1.3.8"
|
||||
shizuku = "13.1.5"
|
||||
spotless = "8.4.0"
|
||||
sqldelight = "2.3.2"
|
||||
sqldelight-androidx-driver = "0.0.17"
|
||||
stringSimilarity = "0.1.0"
|
||||
subsamplingScaleImageView = "66e0db195d"
|
||||
swipe = "1.3.0"
|
||||
tapmoc = "0.4.1"
|
||||
unifile = "e0def6b3dc"
|
||||
voyager = "1.1.0-beta03"
|
||||
xmlutil = "0.91.3"
|
||||
|
||||
[libraries]
|
||||
desugar = "com.android.tools:desugar_jdk_libs:2.1.5"
|
||||
android-shortcut-gradle = "com.github.zellius:android-shortcut-gradle-plugin:0.1.2"
|
||||
|
||||
rxjava = "io.reactivex:rxjava:1.3.8"
|
||||
|
||||
okhttp-core = { module = "com.squareup.okhttp3:okhttp", version.ref = "okhttp_version" }
|
||||
okhttp-logging = { module = "com.squareup.okhttp3:logging-interceptor", version.ref = "okhttp_version" }
|
||||
okhttp-brotli = { module = "com.squareup.okhttp3:okhttp-brotli", version.ref = "okhttp_version" }
|
||||
okhttp-dnsoverhttps = { module = "com.squareup.okhttp3:okhttp-dnsoverhttps", version.ref = "okhttp_version" }
|
||||
okio = "com.squareup.okio:okio:3.17.0"
|
||||
|
||||
conscrypt-android = "org.conscrypt:conscrypt-android:2.5.3"
|
||||
|
||||
quickjs-android = { group = "com.github.zhanghai.quickjs-java", name = "quickjs-android", version = "547f5b1597" }
|
||||
|
||||
jsoup = "org.jsoup:jsoup:1.22.1"
|
||||
|
||||
disklrucache = "com.jakewharton:disklrucache:2.0.2"
|
||||
unifile = "com.github.tachiyomiorg:unifile:e0def6b3dc"
|
||||
libarchive = "me.zhanghai.android.libarchive:library:1.1.6"
|
||||
|
||||
preferencektx = "androidx.preference:preference-ktx:1.2.1"
|
||||
|
||||
injekt = "com.github.mihonapp:injekt:91edab2317"
|
||||
|
||||
coil-bom = { module = "io.coil-kt.coil3:coil-bom", version = "3.4.0" }
|
||||
coil-core = { module = "io.coil-kt.coil3:coil" }
|
||||
coil-gif = { module = "io.coil-kt.coil3:coil-gif" }
|
||||
coil-compose = { module = "io.coil-kt.coil3:coil-compose" }
|
||||
coil-network-okhttp = { module = "io.coil-kt.coil3:coil-network-okhttp" }
|
||||
|
||||
subsamplingscaleimageview = "com.github.tachiyomiorg:subsampling-scale-image-view:66e0db195d"
|
||||
image-decoder = "com.github.tachiyomiorg:image-decoder:41c059e540"
|
||||
|
||||
natural-comparator = "com.github.gpanther:java-nat-sort:natural-comparator-1.1"
|
||||
|
||||
richeditor-compose = "com.mohamedrejeb.richeditor:richeditor-compose:1.0.0-rc13"
|
||||
|
||||
material = "com.google.android.material:material:1.12.0"
|
||||
flexible-adapter-core = "com.github.arkon.FlexibleAdapter:flexible-adapter:c8013533"
|
||||
photoview = "com.github.chrisbanes:PhotoView:2.3.0"
|
||||
directionalviewpager = "com.github.tachiyomiorg:DirectionalViewPager:1.0.0"
|
||||
compose-materialmotion = "io.github.fornewid:material-motion-compose-core:2.0.1"
|
||||
compose-webview = "io.github.kevinnzou:compose-webview:0.33.6"
|
||||
compose-grid = "io.woong.compose.grid:grid:1.2.2"
|
||||
reorderable = { module = "sh.calvin.reorderable:reorderable", version = "3.0.0" }
|
||||
|
||||
swipe = "me.saket.swipe:swipe:1.3.0"
|
||||
|
||||
moko-core = { module = "dev.icerock.moko:resources", version.ref = "moko" }
|
||||
|
||||
logcat = "com.squareup.logcat:logcat:0.4"
|
||||
|
||||
firebase-bom = { module = "com.google.firebase:firebase-bom", version.ref = "firebase-bom" }
|
||||
aboutLibraries-compose = { module = "com.mikepenz:aboutlibraries-compose-m3", version.ref = "aboutLibraries" }
|
||||
android-desugar = { module = "com.android.tools:desugar_jdk_libs", version.ref = "android-desugar" }
|
||||
android-gradle = { module = "com.android.tools.build:gradle", version.ref = "android-gradle" }
|
||||
androidx-activity-compose = { module = "androidx.activity:activity-compose", version.ref = "androidx-activity-compose" }
|
||||
androidx-annotation = { module = "androidx.annotation:annotation", version.ref = "androidx-annotation" }
|
||||
androidx-appCompat = { module = "androidx.appcompat:appcompat", version.ref = "androidx-appCompat" }
|
||||
androidx-benchmark-macroJunit4 = { module = "androidx.benchmark:benchmark-macro-junit4", version.ref = "androidx-benchmark" }
|
||||
androidx-biometric = { module = "androidx.biometric:biometric-ktx", version.ref = "androidx-biometric" }
|
||||
androidx-compose-animation = { module = "androidx.compose.animation:animation" }
|
||||
androidx-compose-animationGraphics = { module = "androidx.compose.animation:animation-graphics" }
|
||||
androidx-compose-bom = { module = "androidx.compose:compose-bom-beta", version.ref = "androidx-compose-bom" }
|
||||
androidx-compose-foundation = { module = "androidx.compose.foundation:foundation" }
|
||||
androidx-compose-material3 = { module = "androidx.compose.material3:material3" }
|
||||
androidx-compose-materialIcons = { module = "androidx.compose.material:material-icons-extended" }
|
||||
androidx-compose-runtime = { module = "androidx.compose.runtime:runtime" }
|
||||
androidx-compose-runtimeAnnotation = { module = "androidx.compose.runtime:runtime-annotation" }
|
||||
androidx-compose-uiTooling = { module = "androidx.compose.ui:ui-tooling" }
|
||||
androidx-compose-uiToolingPreview = { module = "androidx.compose.ui:ui-tooling-preview" }
|
||||
androidx-compose-uiUtil = { module = "androidx.compose.ui:ui-util" }
|
||||
androidx-constraintLayout = { module = "androidx.constraintlayout:constraintlayout", version.ref = "androidx-constraintLayout" }
|
||||
androidx-core = { module = "androidx.core:core-ktx", version.ref = "androidx-core" }
|
||||
androidx-coreSplashScreen = { module = "androidx.core:core-splashscreen", version.ref = "androidx-coreSplashScreen" }
|
||||
androidx-glance-appWidget = { module = "androidx.glance:glance-appwidget", version.ref = "androidx-glance" }
|
||||
androidx-interpolator = { module = "androidx.interpolator:interpolator", version.ref = "androidx-interpolator" }
|
||||
androidx-lifecycle-common = { module = "androidx.lifecycle:lifecycle-common", version.ref = "androidx-lifecycle" }
|
||||
androidx-lifecycle-process = { module = "androidx.lifecycle:lifecycle-process", version.ref = "androidx-lifecycle" }
|
||||
androidx-lifecycle-runtime = { module = "androidx.lifecycle:lifecycle-runtime-ktx", version.ref = "androidx-lifecycle" }
|
||||
androidx-paging-compose = { module = "androidx.paging:paging-compose", version.ref = "androidx-paging" }
|
||||
androidx-paging-runtime = { module = "androidx.paging:paging-runtime", version.ref = "androidx-paging" }
|
||||
androidx-preference = { module = "androidx.preference:preference-ktx", version.ref = "androidx-preference" }
|
||||
androidx-profileInstaller = { module = "androidx.profileinstaller:profileinstaller", version.ref = "androidx-profileInstaller" }
|
||||
androidx-recyclerView = { module = "androidx.recyclerview:recyclerview", version.ref = "androidx-recyclerView" }
|
||||
androidx-sqlite-bundled = { module = "androidx.sqlite:sqlite-bundled", version.ref = "androidx-sqlite" }
|
||||
androidx-test-espresso-core = { module = "androidx.test.espresso:espresso-core", version.ref = "androidx-test-espresso" }
|
||||
androidx-test-junit = { module = "androidx.test.ext:junit-ktx", version.ref = "androidx-test-junit" }
|
||||
androidx-test-uiautomator = { module = "androidx.test.uiautomator:uiautomator", version.ref = "androidx-test-uiautomator" }
|
||||
androidx-viewPager = { module = "androidx.viewpager:viewpager", version.ref = "androidx-viewPager" }
|
||||
androidx-work = { module = "androidx.work:work-runtime", version.ref = "androidx-work" }
|
||||
archive = { module = "me.zhanghai.android.libarchive:library", version.ref = "archive" }
|
||||
coil-compose = { module = "io.coil-kt.coil3:coil-compose", version.ref = "coil" }
|
||||
coil-core = { module = "io.coil-kt.coil3:coil", version.ref = "coil" }
|
||||
coil-gif = { module = "io.coil-kt.coil3:coil-gif", version.ref = "coil" }
|
||||
coil-network-okhttp = { module = "io.coil-kt.coil3:coil-network-okhttp", version.ref = "coil" }
|
||||
composeGrid = { module = "io.woong.compose.grid:grid", version.ref = "composeGrid" }
|
||||
composeMaterialMotion = { module = "io.github.fornewid:material-motion-compose-core", version.ref = "composeMaterialMotion" }
|
||||
composeRichEditor = { module = "com.mohamedrejeb.richeditor:richeditor-compose", version.ref = "composeRichEditor" }
|
||||
composeWebview = { module = "io.github.kevinnzou:compose-webview", version.ref = "composeWebview" }
|
||||
conscrypt = { module = "org.conscrypt:conscrypt-android", version.ref = "conscrypt" }
|
||||
directionalViewPager = { module = "com.github.tachiyomiorg:DirectionalViewPager", version.ref = "directionalViewPager" }
|
||||
diskLruCache = { module = "com.jakewharton:disklrucache", version.ref = "diskLruCache" }
|
||||
firebase-analytics = { module = "com.google.firebase:firebase-analytics" }
|
||||
firebase-bom = { module = "com.google.firebase:firebase-bom", version.ref = "firebase-bom" }
|
||||
firebase-crashlytics = { module = "com.google.firebase:firebase-crashlytics" }
|
||||
|
||||
aboutLibraries-compose = { module = "com.mikepenz:aboutlibraries-compose-m3", version.ref = "aboutlib_version" }
|
||||
|
||||
shizuku-api = { module = "dev.rikka.shizuku:api", version.ref = "shizuku_version" }
|
||||
shizuku-provider = { module = "dev.rikka.shizuku:provider", version.ref = "shizuku_version" }
|
||||
|
||||
leakcanary-android = { module = "com.squareup.leakcanary:leakcanary-android", version.ref = "leakcanary" }
|
||||
leakcanary-plumber = { module = "com.squareup.leakcanary:plumber-android", version.ref = "leakcanary" }
|
||||
|
||||
sqldelight-androidx-driver = { module = "com.eygraber:sqldelight-androidx-driver", version = "0.0.17" }
|
||||
sqldelight-coroutines = { module = "app.cash.sqldelight:coroutines-extensions-jvm", version.ref = "sqldelight" }
|
||||
sqldelight-android-paging = { module = "app.cash.sqldelight:androidx-paging3-extensions", version.ref = "sqldelight" }
|
||||
sqldelight-dialects-sql = { module = "app.cash.sqldelight:sqlite-3-38-dialect", version.ref = "sqldelight" }
|
||||
|
||||
flexibleAdapter = { module = "com.github.arkon.FlexibleAdapter:flexible-adapter", version.ref = "flexibleAdapter" }
|
||||
image-decoder = { module = "com.github.tachiyomiorg:image-decoder", version.ref = "image-decoder" }
|
||||
injekt = { module = "com.github.mihonapp:injekt", version.ref = "injekt" }
|
||||
jsoup = { module = "org.jsoup:jsoup", version.ref = "jsoup" }
|
||||
junit-jupiter = { module = "org.junit.jupiter:junit-jupiter", version.ref = "junit" }
|
||||
junit-platform-launcher = { module = "org.junit.platform:junit-platform-launcher" }
|
||||
kotest-assertions = "io.kotest:kotest-assertions-core:6.1.7"
|
||||
mockk = "io.mockk:mockk:1.14.9"
|
||||
|
||||
voyager-navigator = { module = "cafe.adriel.voyager:voyager-navigator", version.ref = "voyager" }
|
||||
voyager-screenmodel = { module = "cafe.adriel.voyager:voyager-screenmodel", version.ref = "voyager" }
|
||||
voyager-tab-navigator = { module = "cafe.adriel.voyager:voyager-tab-navigator", version.ref = "voyager" }
|
||||
voyager-transitions = { module = "cafe.adriel.voyager:voyager-transitions", version.ref = "voyager" }
|
||||
|
||||
spotless-gradle = { group = "com.diffplug.spotless", name = "spotless-plugin-gradle", version.ref = "spotless" }
|
||||
ktlint-core = { module = "com.pinterest.ktlint:ktlint-cli", version.ref = "ktlint-core" }
|
||||
|
||||
markdown-core = { module = "com.mikepenz:multiplatform-markdown-renderer", version.ref = "markdown" }
|
||||
kotest-assertions = { module = "io.kotest:kotest-assertions-core", version.ref = "kotest-assertions" }
|
||||
kotlin-compose-compiler-gradle = { module = "org.jetbrains.kotlin:compose-compiler-gradle-plugin", version.ref = "kotlin-gradle" }
|
||||
kotlin-gradle = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin", version.ref = "kotlin-gradle" }
|
||||
kotlin-reflect = { module = "org.jetbrains.kotlin:kotlin-reflect", version.ref = "kotlin-gradle" }
|
||||
kotlinx-collections-immutable = { module = "org.jetbrains.kotlinx:kotlinx-collections-immutable", version.ref = "kotlinx-collections-immutable" }
|
||||
kotlinx-coroutines-android = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-android", version.ref = "kotlinx-coroutines" }
|
||||
kotlinx-coroutines-core = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "kotlinx-coroutines" }
|
||||
kotlinx-coroutines-guava = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-guava", version.ref = "kotlinx-coroutines" }
|
||||
kotlinx-coroutines-test = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-test", version.ref = "kotlinx-coroutines" }
|
||||
kotlinx-serialization-json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "kotlinx-serialization" }
|
||||
kotlinx-serialization-jsonOkio = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json-okio", version.ref = "kotlinx-serialization" }
|
||||
kotlinx-serialization-protobuf = { module = "org.jetbrains.kotlinx:kotlinx-serialization-protobuf", version.ref = "kotlinx-serialization" }
|
||||
ktlint-bom = { module = "com.pinterest.ktlint:ktlint-bom", version.ref = "ktlint" }
|
||||
leakCanary-core = { module = "com.squareup.leakcanary:leakcanary-android", version.ref = "leakCanary" }
|
||||
leakCanary-plumber = { module = "com.squareup.leakcanary:plumber-android", version.ref = "leakCanary" }
|
||||
logcat = { module = "com.squareup.logcat:logcat", version.ref = "logcat" }
|
||||
markdown-coil = { module = "com.mikepenz:multiplatform-markdown-renderer-coil3", version.ref = "markdown" }
|
||||
|
||||
stringSimilarity = { module = "com.aallam.similarity:string-similarity-kotlin", version = "0.1.0" }
|
||||
|
||||
markdown-core = { module = "com.mikepenz:multiplatform-markdown-renderer", version.ref = "markdown" }
|
||||
material = { module = "com.google.android.material:material", version.ref = "material" }
|
||||
materialKolor = { module = "com.materialkolor:material-kolor", version.ref = "materialKolor" }
|
||||
mockk = { module = "io.mockk:mockk", version.ref = "mockk" }
|
||||
moko-resources = { module = "dev.icerock.moko:resources", version.ref = "moko-resources" }
|
||||
natural-comparator = { module = "com.github.gpanther:java-nat-sort", version = "natural-comparator-1.1" }
|
||||
okhttp-brotli = { module = "com.squareup.okhttp3:okhttp-brotli", version.ref = "okhttp" }
|
||||
okhttp-core = { module = "com.squareup.okhttp3:okhttp", version.ref = "okhttp" }
|
||||
okhttp-dnsOverHttps = { module = "com.squareup.okhttp3:okhttp-dnsoverhttps", version.ref = "okhttp" }
|
||||
okhttp-logging = { module = "com.squareup.okhttp3:logging-interceptor", version.ref = "okhttp" }
|
||||
okio = { module = "com.squareup.okio:okio", version.ref = "okio" }
|
||||
photoView = { module = "com.github.chrisbanes:PhotoView", version.ref = "photoView" }
|
||||
quickJs = { module = "com.github.zhanghai.quickjs-java:quickjs-android", version.ref = "quickJs" }
|
||||
reorderable = { module = "sh.calvin.reorderable:reorderable", version.ref = "reorderable" }
|
||||
rxJava = { module = "io.reactivex:rxjava", version.ref = "rxJava" }
|
||||
shizuku-api = { module = "dev.rikka.shizuku:api", version.ref = "shizuku" }
|
||||
shizuku-provider = { module = "dev.rikka.shizuku:provider", version.ref = "shizuku" }
|
||||
spotless-gradle = { module = "com.diffplug.spotless:spotless-plugin-gradle", version.ref = "spotless" }
|
||||
sqldelight-androidxDriver = { module = "com.eygraber:sqldelight-androidx-driver", version.ref = "sqldelight-androidx-driver" }
|
||||
sqldelight-androidxPaging = { module = "app.cash.sqldelight:androidx-paging3-extensions", version.ref = "sqldelight" }
|
||||
sqldelight-coroutines = { module = "app.cash.sqldelight:coroutines-extensions-jvm", version.ref = "sqldelight" }
|
||||
sqldelight-sqliteDialect338 = { module = "app.cash.sqldelight:sqlite-3-38-dialect", version.ref = "sqldelight" }
|
||||
stringSimilarity = { module = "com.aallam.similarity:string-similarity-kotlin", version.ref = "stringSimilarity" }
|
||||
subsamplingScaleImageView = { module = "com.github.tachiyomiorg:subsampling-scale-image-view", version.ref = "subsamplingScaleImageView" }
|
||||
swipe = { module = "me.saket.swipe:swipe", version.ref = "swipe" }
|
||||
tapmoc-gradle = { module = "com.gradleup.tapmoc:tapmoc-gradle-plugin", version.ref = "tapmoc" }
|
||||
unifile = { module = "com.github.tachiyomiorg:unifile", version.ref = "unifile" }
|
||||
voyager-navigator = { module = "cafe.adriel.voyager:voyager-navigator", version.ref = "voyager" }
|
||||
voyager-screenModel = { module = "cafe.adriel.voyager:voyager-screenmodel", version.ref = "voyager" }
|
||||
voyager-tabNavigator = { module = "cafe.adriel.voyager:voyager-tab-navigator", version.ref = "voyager" }
|
||||
voyager-transitions = { module = "cafe.adriel.voyager:voyager-transitions", version.ref = "voyager" }
|
||||
xmlutil-core = { module = "io.github.pdvrieze.xmlutil:core-android", version.ref = "xmlutil" }
|
||||
xmlutil-serialization = { module = "io.github.pdvrieze.xmlutil:serialization", version.ref = "xmlutil" }
|
||||
|
||||
[plugins]
|
||||
google-services = { id = "com.google.gms.google-services", version = "4.4.4" }
|
||||
aboutLibraries = { id = "com.mikepenz.aboutlibraries.plugin.android", version.ref = "aboutlib_version" }
|
||||
aboutLibraries = { id = "com.mikepenz.aboutlibraries.plugin.android", version.ref = "aboutLibraries" }
|
||||
android-application = { id = "com.android.application", version.ref = "android-gradle" }
|
||||
android-library = { id = "com.android.library", version.ref = "android-gradle" }
|
||||
android-test = { id = "com.android.test", version.ref = "android-gradle" }
|
||||
firebase-crashlytics = { id = "com.google.firebase.crashlytics", version.ref = "firebase-crashlytics" }
|
||||
google-services = { id = "com.google.gms.google-services", version.ref = "google-services" }
|
||||
kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin-gradle" }
|
||||
kotlin-compose-compiler = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin-gradle" }
|
||||
kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin-gradle" }
|
||||
kotlin-multiplatform = { id = "org.jetbrains.kotlin.multiplatform", version.ref = "kotlin-gradle" }
|
||||
kotlin-samWithReceiver = { id = "org.jetbrains.kotlin.plugin.sam.with.receiver", version.ref = "kotlin-gradle" }
|
||||
kotlin-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin-gradle" }
|
||||
moko-resources = { id = "dev.icerock.mobile.multiplatform-resources", version.ref = "moko-resources" }
|
||||
spotless = { id = "com.diffplug.spotless", version.ref = "spotless" }
|
||||
sqldelight = { id = "app.cash.sqldelight", version.ref = "sqldelight" }
|
||||
moko = { id = "dev.icerock.mobile.multiplatform-resources", version.ref = "moko" }
|
||||
firebase-crashlytics = { id = "com.google.firebase.crashlytics", version = "3.0.6" }
|
||||
|
||||
[bundles]
|
||||
okhttp = ["okhttp-core", "okhttp-logging", "okhttp-brotli", "okhttp-dnsoverhttps"]
|
||||
js-engine = ["quickjs-android"]
|
||||
androidx-lifecycle = ["androidx-lifecycle-common", "androidx-lifecycle-process", "androidx-lifecycle-runtime"]
|
||||
coil = ["coil-core", "coil-gif", "coil-compose", "coil-network-okhttp"]
|
||||
shizuku = ["shizuku-api", "shizuku-provider"]
|
||||
sqldelight = ["sqldelight-androidx-driver", "sqldelight-coroutines", "sqldelight-android-paging"]
|
||||
voyager = ["voyager-navigator", "voyager-screenmodel", "voyager-tab-navigator", "voyager-transitions"]
|
||||
test = ["junit-jupiter", "kotest-assertions", "mockk"]
|
||||
kotlinx-coroutines = ["kotlinx-coroutines-core", "kotlinx-coroutines-android", "kotlinx-coroutines-guava"]
|
||||
markdown = ["markdown-core", "markdown-coil"]
|
||||
okhttp = ["okhttp-core", "okhttp-logging", "okhttp-brotli", "okhttp-dnsOverHttps"]
|
||||
serialization = ["kotlinx-serialization-json", "kotlinx-serialization-jsonOkio", "kotlinx-serialization-protobuf", "xmlutil-core", "xmlutil-serialization"]
|
||||
shizuku = ["shizuku-api", "shizuku-provider"]
|
||||
sqldelight = ["sqldelight-androidxDriver", "sqldelight-coroutines", "sqldelight-androidxPaging"]
|
||||
test = ["junit-jupiter", "kotest-assertions", "mockk"]
|
||||
voyager = ["voyager-navigator", "voyager-screenModel", "voyager-tabNavigator", "voyager-transitions"]
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
[versions]
|
||||
# Used in [/gradle/build-config/src/main/kotlin/PluginKotlinMultiplatform.kt]
|
||||
android-sdk-min = "26"
|
||||
android-sdk-compile = "36"
|
||||
android-sdk-target = "36"
|
||||
android-ndk = "29.0.14206865"
|
||||
java = "17"
|
||||
|
||||
[plugins]
|
||||
android-application = { id = "mihon.plugins.android.application" }
|
||||
android-base = { id = "mihon.plugins.android.base" }
|
||||
android-library = { id = "mihon.plugins.android.library" }
|
||||
android-test = { id = "mihon.plugins.android.test" }
|
||||
compose = { id = "mihon.plugins.compose.android" }
|
||||
kotlin-multiplatform = { id = "mihon.plugins.kotlin.multiplatform" }
|
||||
spotless = { id = "mihon.plugins.spotless" }
|
||||
Vendored
BIN
Binary file not shown.
+1
-1
@@ -1,6 +1,6 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.4-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-9.4.1-bin.zip
|
||||
networkTimeout=10000
|
||||
validateDistributionUrl=true
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#!/bin/sh
|
||||
|
||||
#
|
||||
# Copyright © 2015-2021 the original authors.
|
||||
# Copyright © 2015 the original authors.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
@@ -57,7 +57,7 @@
|
||||
# Darwin, MinGW, and NonStop.
|
||||
#
|
||||
# (3) This script is generated from the Groovy template
|
||||
# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
||||
# https://github.com/gradle/gradle/blob/2d6327017519d23b96af35865dc997fcb544fb40/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
||||
# within the Gradle project.
|
||||
#
|
||||
# You can find Gradle at https://github.com/gradle/gradle/.
|
||||
@@ -114,7 +114,6 @@ case "$( uname )" in #(
|
||||
NONSTOP* ) nonstop=true ;;
|
||||
esac
|
||||
|
||||
CLASSPATH="\\\"\\\""
|
||||
|
||||
|
||||
# Determine the Java command to use to start the JVM.
|
||||
@@ -172,7 +171,6 @@ fi
|
||||
# For Cygwin or MSYS, switch paths to Windows format before running java
|
||||
if "$cygwin" || "$msys" ; then
|
||||
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
|
||||
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
|
||||
|
||||
JAVACMD=$( cygpath --unix "$JAVACMD" )
|
||||
|
||||
@@ -212,7 +210,6 @@ DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||
|
||||
set -- \
|
||||
"-Dorg.gradle.appname=$APP_BASE_NAME" \
|
||||
-classpath "$CLASSPATH" \
|
||||
-jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \
|
||||
"$@"
|
||||
|
||||
|
||||
Vendored
+1
-2
@@ -70,11 +70,10 @@ goto fail
|
||||
:execute
|
||||
@rem Setup the command line
|
||||
|
||||
set CLASSPATH=
|
||||
|
||||
|
||||
@rem Execute Gradle
|
||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %*
|
||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %*
|
||||
|
||||
:end
|
||||
@rem End local scope for the variables with windows NT shell
|
||||
|
||||
+18
-35
@@ -1,24 +1,22 @@
|
||||
import mihon.buildlogic.generatedBuildDir
|
||||
import mihon.buildlogic.tasks.getLocalesConfigTask
|
||||
import mihon.gradle.tasks.GenerateLocalesConfigTask
|
||||
import org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi
|
||||
|
||||
plugins {
|
||||
id("mihon.library")
|
||||
kotlin("multiplatform")
|
||||
alias(libs.plugins.moko)
|
||||
alias(mihonx.plugins.kotlin.multiplatform)
|
||||
alias(mihonx.plugins.spotless)
|
||||
|
||||
alias(libs.plugins.moko.resources)
|
||||
}
|
||||
|
||||
kotlin {
|
||||
androidTarget()
|
||||
android {
|
||||
namespace = "tachiyomi.i18n"
|
||||
}
|
||||
|
||||
applyDefaultHierarchyTemplate()
|
||||
|
||||
sourceSets {
|
||||
commonMain {
|
||||
dependencies {
|
||||
api(libs.moko.core)
|
||||
}
|
||||
}
|
||||
@OptIn(ExperimentalKotlinGradlePluginApi::class)
|
||||
@Suppress("UnstableApiUsage")
|
||||
dependencies {
|
||||
api(libs.moko.resources)
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalKotlinGradlePluginApi::class)
|
||||
@@ -27,31 +25,16 @@ kotlin {
|
||||
}
|
||||
}
|
||||
|
||||
val generatedAndroidResourceDir = generatedBuildDir.resolve("android/res")
|
||||
androidComponents {
|
||||
onVariants { variant ->
|
||||
val resSource = variant.sources.res ?: return@onVariants
|
||||
|
||||
android {
|
||||
namespace = "tachiyomi.i18n"
|
||||
|
||||
sourceSets {
|
||||
val main by getting
|
||||
main.res.srcDirs(
|
||||
"src/commonMain/resources",
|
||||
generatedAndroidResourceDir,
|
||||
)
|
||||
}
|
||||
|
||||
lint {
|
||||
disable.addAll(listOf("MissingTranslation", "ExtraTranslation"))
|
||||
val variantName = variant.name.replaceFirstChar { it.uppercase() }
|
||||
val task = tasks.register<GenerateLocalesConfigTask>("generate${variantName}LocalesConfig")
|
||||
resSource.addGeneratedSourceDirectory(task) { it.outputDir }
|
||||
}
|
||||
}
|
||||
|
||||
multiplatformResources {
|
||||
resourcesPackage.set("tachiyomi.i18n")
|
||||
}
|
||||
|
||||
tasks {
|
||||
val localesConfigTask = project.getLocalesConfigTask(generatedAndroidResourceDir)
|
||||
preBuild {
|
||||
dependsOn(localesConfigTask)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -409,7 +409,6 @@
|
||||
<string name="unknown_author">مؤلّف غير معروف</string>
|
||||
<string name="no_pinned_sources">لا تمتلك أيّة مصادر مثبّتة</string>
|
||||
<string name="action_global_search_query">إبحث عن \"%1$s\" بشكلٍ شامل</string>
|
||||
<string name="updated_version">حُدِّث إلى الإصدار v%1$s</string>
|
||||
<string name="no_chapters_error">لم يُعثر على فصول</string>
|
||||
<string name="share_page_info">%1$s: %2$s, صفحة %3$d</string>
|
||||
<string name="chapter_settings_updated">إعدادات الفصل الإفتراضية المُحدثة</string>
|
||||
@@ -567,7 +566,6 @@
|
||||
<string name="error_saving_picture">خطأ أثناء حفظ الصورة</string>
|
||||
<string name="disabled_nav">معطل</string>
|
||||
<string name="rotation_reverse_portrait">رأسي بالعكس</string>
|
||||
<string name="notification_update_error">فشل %1$d تحديث أو تحديثات</string>
|
||||
<string name="learn_more">اضغط لمعرفة المزيد</string>
|
||||
<string name="action_move_to_top_all_for_series">نقل سلسلة الفصول للأعلى</string>
|
||||
<string name="empty_backup_error">لا توجد مدخلات في المكتبة لإجراء نسخة احتياطية</string>
|
||||
@@ -852,7 +850,6 @@
|
||||
<string name="possible_duplicates_title">التكرارات المحتملة</string>
|
||||
<string name="confirm_tracker_update">تحديث المتابعات إلى الفصل %d؟</string>
|
||||
<string name="pref_incognito_mode_extension_summary">إيقاف قراءة التاريخ مؤقتًا للتمديد</string>
|
||||
<string name="label_donate">تبرع</string>
|
||||
<string name="theme_catppuccin">كاتبوتشين</string>
|
||||
<string name="pref_display_images_description">تقديم الصور في أوصاف المانجا</string>
|
||||
<string name="pref_hide_missing_chapter_indicators">إخفاء مؤشرات الفصل المفقودة</string>
|
||||
|
||||
@@ -551,7 +551,6 @@
|
||||
<string name="licenses">খুলা উৎস লাইচেন্স</string>
|
||||
<string name="privacy_policy">গোপনীয়তা নীতি</string>
|
||||
<string name="check_for_updates">আপডেট চাওক</string>
|
||||
<string name="updated_version">v%1$s ত আপডেট কৰা হৈছে</string>
|
||||
<string name="pref_enable_acra">ক্রাশ ৰিপ\'ৰ্ট পঠিয়াব</string>
|
||||
<string name="pref_acra_summary">যিকোনো বাগ মেলাবলৈ সহায় কৰে। কোনো সংবেদনশীল তথ্য পঠিয়াব নোৱাৰি</string>
|
||||
<string name="fdroid_warning">F-Droid নিৰ্মাণসমূহৰ আনুষ্ঠানিক সমৰ্থন নাই।
|
||||
@@ -755,7 +754,6 @@
|
||||
<string name="notification_chapters_single">অধ্যায় %1$s</string>
|
||||
<string name="notification_chapters_single_and_more">অধ্যায় %1$s আৰু %2$d অধিক</string>
|
||||
<string name="notification_chapters_multiple">অধ্যায়সমূহ %1$s</string>
|
||||
<string name="notification_update_error">%1$d আপডেট(s) ব্যৰ্থ</string>
|
||||
<string name="learn_more">অধিক জানিবলৈ টেপ কৰক</string>
|
||||
<string name="notification_cover_update_failed">কভাৰ আপডেট কৰিবলৈ ব্যৰ্থ</string>
|
||||
<string name="notification_first_add_to_library">এইটো কৰিবলৈ আগত অনুগ্ৰহ কৰি ৰেকৰ্ডটো আপোনাৰ লাইব্রেৰীত যোগ কৰক</string>
|
||||
|
||||
@@ -109,4 +109,9 @@
|
||||
<item quantity="one">An entry was skipped</item>
|
||||
<item quantity="other">%1$d entries were skipped</item>
|
||||
</plurals>
|
||||
|
||||
<plurals name="notification_update_error">
|
||||
<item quantity="one">%1$d update failed</item>
|
||||
<item quantity="other">%1$d updates failed</item>
|
||||
</plurals>
|
||||
</resources>
|
||||
|
||||
@@ -36,7 +36,7 @@
|
||||
<string name="label_extensions">Extensions</string>
|
||||
<string name="label_extension_info">Extension info</string>
|
||||
<string name="label_help">Help</string>
|
||||
<string name="label_donate">Donate</string>
|
||||
<string name="label_support_us">Support Us</string>
|
||||
<string name="label_default">Default</string>
|
||||
<string name="label_warning">Warning</string>
|
||||
<string name="label_auto">Auto</string>
|
||||
@@ -648,7 +648,6 @@
|
||||
<string name="licenses">Open source licenses</string>
|
||||
<string name="privacy_policy">Privacy policy</string>
|
||||
<string name="check_for_updates">Check for updates</string>
|
||||
<string name="updated_version">Updated to v%1$s</string>
|
||||
|
||||
<!-- ACRA -->
|
||||
<string name="pref_enable_acra">Send crash reports</string>
|
||||
@@ -921,7 +920,6 @@
|
||||
<string name="notification_chapters_single">Chapter %1$s</string>
|
||||
<string name="notification_chapters_single_and_more">Chapter %1$s and %2$d more</string>
|
||||
<string name="notification_chapters_multiple">Chapters %1$s</string>
|
||||
<string name="notification_update_error">%1$d update(s) failed</string>
|
||||
<string name="learn_more">Tap to learn more</string>
|
||||
<string name="notification_cover_update_failed">Failed to update cover</string>
|
||||
<string name="notification_first_add_to_library">Please add the entry to your library before doing this</string>
|
||||
@@ -1048,4 +1046,18 @@
|
||||
<string name="migrationListScreen.migrateDialog.cancelLabel">Cancel</string>
|
||||
<string name="migrationListScreen.progressDialog.cancelLabel">Cancel</string>
|
||||
<string name="migrationListScreen.matchWithoutChapterToast">No chapters found, this entry cannot be used for migration</string>
|
||||
|
||||
<string name="supportUsScreen.perks">Patrons get access to exclusive channels, with more perks to come.</string>
|
||||
<string name="supportUsScreen.donationPlatform.patreon">Patreon (preferred)</string>
|
||||
<string name="supportUsScreen.donationPlatform.opencollective">OpenCollective</string>
|
||||
<string name="supportUsScreen.currentlySupportedBy">Mihon is currently backed by %d+ patrons. We want to thank each and everyone of you. Let's make Mihon even better together.</string>
|
||||
<string name="supportUsScreen.contactForDetailsMessage">For alternative options or any related questions, join our Discord server.</string>
|
||||
<string name="supportUsScreen.contactPlatform">Discord</string>
|
||||
|
||||
<string name="donationCampaign.title">Enjoying the app?</string>
|
||||
<string name="donationCampaign.paragraph1">Hi! I’m the main developer behind Mihon, and I’d like to ask you to consider supporting its development financially. With enough support, I’ll be able to work on Mihon full-time and focus on long-standing issues as well as highly requested features.</string>
|
||||
<string name="donationCampaign.paragraph2">You can donate through either Patreon (preferred) or OpenCollective. If you have questions or would like to support using alternative methods, feel free to reach out to me through Mihon's Discord server.</string>
|
||||
<string name="donationCampaign.paragraph3">Regardless, Mihon will continue to be maintained by me and the volunteers, and it will stay open source.</string>
|
||||
<string name="donationCampaign.contactPlatform">Discord</string>
|
||||
<string name="donationCampaign.dismiss">Dismiss</string>
|
||||
</resources>
|
||||
|
||||
@@ -7,10 +7,10 @@
|
||||
<item quantity="other">%d катэгорый</item>
|
||||
</plurals>
|
||||
<plurals name="lock_after_mins">
|
||||
<item quantity="one">Пасля %1$s хвіліны</item>
|
||||
<item quantity="few">Пасля %1$s хвілін</item>
|
||||
<item quantity="many">Пасля %1$s хвілін</item>
|
||||
<item quantity="other">Пасля %1$s хвілін</item>
|
||||
<item quantity="one">Праз %1$s хвіліну</item>
|
||||
<item quantity="few">Праз %1$s хвіліны</item>
|
||||
<item quantity="many">Праз %1$s хвілін</item>
|
||||
<item quantity="other">Праз %1$s хвілін</item>
|
||||
</plurals>
|
||||
<plurals name="relative_time">
|
||||
<item quantity="one">Учора</item>
|
||||
@@ -19,9 +19,117 @@
|
||||
<item quantity="other">%1$d дзён таму</item>
|
||||
</plurals>
|
||||
<plurals name="next_unread_chapters">
|
||||
<item quantity="one">Наступная непрачытаная глава</item>
|
||||
<item quantity="few">Наступныя %d непрачытаныя главы</item>
|
||||
<item quantity="many">Наступныя %d непрачытаных глав</item>
|
||||
<item quantity="other">Наступныя %d непрачытаных глав</item>
|
||||
<item quantity="one">Наступны непрачытаная раздзел</item>
|
||||
<item quantity="few">Наступныя %d непрачытаныя раздзелы</item>
|
||||
<item quantity="many">Наступныя %d непрачытаных раздзелаў</item>
|
||||
<item quantity="other">Наступныя %d непрачытаных раздзелаў</item>
|
||||
</plurals>
|
||||
<plurals name="upcoming_relative_time">
|
||||
<item quantity="one">Заўтра</item>
|
||||
<item quantity="few">Праз %1$d дні</item>
|
||||
<item quantity="many">Праз %1$d дзён</item>
|
||||
<item quantity="other">Праз %1$d дзён</item>
|
||||
</plurals>
|
||||
<plurals name="restore_completed_message">
|
||||
<item quantity="one">Выканана за %1$s з %2$s памылкай</item>
|
||||
<item quantity="few">Выканана за %1$s з %2$s памылкамі</item>
|
||||
<item quantity="many">Выканана за %1$s з %2$s памылкамі</item>
|
||||
<item quantity="other">Выканана за %1$s з %2$s памылкамі</item>
|
||||
</plurals>
|
||||
<plurals name="download_queue_summary">
|
||||
<item quantity="one">Застаўся %1$s</item>
|
||||
<item quantity="few">Засталося %1$s</item>
|
||||
<item quantity="many">Засталося %1$s</item>
|
||||
<item quantity="other">Засталося %1$s</item>
|
||||
</plurals>
|
||||
<plurals name="day">
|
||||
<item quantity="one">%d дзень</item>
|
||||
<item quantity="few">%d дні</item>
|
||||
<item quantity="many">%d дзён</item>
|
||||
<item quantity="other">%d дзён</item>
|
||||
</plurals>
|
||||
<plurals name="pref_pages">
|
||||
<item quantity="one">%1$s старонка</item>
|
||||
<item quantity="few">%1$s старонкі</item>
|
||||
<item quantity="many">%1$s старонак</item>
|
||||
<item quantity="other">%1$s старонак</item>
|
||||
</plurals>
|
||||
<plurals name="missing_chapters">
|
||||
<item quantity="one">Адсутнічае %1$s раздзел</item>
|
||||
<item quantity="few">Адсутнічаюць %1$s раздзелы</item>
|
||||
<item quantity="many">Адсутнічаюць %1$s раздзелаў</item>
|
||||
<item quantity="other">Адсутнічаюць %1$s раздзелаў</item>
|
||||
</plurals>
|
||||
<plurals name="manga_num_chapters">
|
||||
<item quantity="one">%1$s раздзел</item>
|
||||
<item quantity="few">%1$s раздзелы</item>
|
||||
<item quantity="many">%1$s раздзелаў</item>
|
||||
<item quantity="other">%1$s раздзелаў</item>
|
||||
</plurals>
|
||||
<plurals name="download_amount">
|
||||
<item quantity="one">Наступны раздзел</item>
|
||||
<item quantity="few">Наступныя %d раздзелы</item>
|
||||
<item quantity="many">Наступныя %d раздзелаў</item>
|
||||
<item quantity="other">Наступныя %d раздзелаў</item>
|
||||
</plurals>
|
||||
<plurals name="num_trackers">
|
||||
<item quantity="one">%d трэкер</item>
|
||||
<item quantity="few">%d трэкеры</item>
|
||||
<item quantity="many">%d трэкераў</item>
|
||||
<item quantity="other">%d трэкераў</item>
|
||||
</plurals>
|
||||
<plurals name="missing_chapters_warning">
|
||||
<item quantity="one">Прапускаецца %d раздзел: адсутнічае ў крыніцы або адфільтраваны</item>
|
||||
<item quantity="few">Прапускаюцца %d раздзелы: адсутнічаюць у крыніцы або адфільтраваныя</item>
|
||||
<item quantity="many">Прапускаецца %d раздзелаў: адсутнічаюць у крыніцы або адфільтраваныя</item>
|
||||
<item quantity="other">Прапускаецца %d раздзелаў: адсутнічаюць у крыніцы або адфільтраваныя</item>
|
||||
</plurals>
|
||||
<plurals name="notification_new_chapters_summary">
|
||||
<item quantity="one">Для %d твора</item>
|
||||
<item quantity="few">Для %d твораў</item>
|
||||
<item quantity="many">Для %d твораў</item>
|
||||
<item quantity="other">Для %d твораў</item>
|
||||
</plurals>
|
||||
<plurals name="notification_chapters_generic">
|
||||
<item quantity="one">%1$d новы раздзел</item>
|
||||
<item quantity="few">%1$d новыя раздзелы</item>
|
||||
<item quantity="many">%1$d новых раздзелаў</item>
|
||||
<item quantity="other">%1$d новых раздзелаў</item>
|
||||
</plurals>
|
||||
<plurals name="migrationListScreen.migrateDialog.skipText">
|
||||
<item quantity="one">%1$d твор быў прапушчаны</item>
|
||||
<item quantity="few">%1$d творы былі прапушчаны</item>
|
||||
<item quantity="many">%1$d твораў было прапушчана</item>
|
||||
<item quantity="other">%1$d твораў было прапушчана</item>
|
||||
</plurals>
|
||||
<plurals name="notification_chapters_multiple_and_more">
|
||||
<item quantity="one">Раздзелы %1$s і яшчэ адзін</item>
|
||||
<item quantity="few">Раздзелы %1$s і яшчэ %2$d</item>
|
||||
<item quantity="many">Раздзелы %1$s і яшчэ %2$d</item>
|
||||
<item quantity="other">Раздзелы %1$s і яшчэ %2$d</item>
|
||||
</plurals>
|
||||
<plurals name="update_check_notification_ext_updates">
|
||||
<item quantity="one">Даступна абнаўленне пашырэння</item>
|
||||
<item quantity="few">Даступна %d абнаўленні пашырэнняў</item>
|
||||
<item quantity="many">Даступна %d абнаўленняў пашырэнняў</item>
|
||||
<item quantity="other">Даступна %d абнаўленняў пашырэнняў</item>
|
||||
</plurals>
|
||||
<plurals name="num_repos">
|
||||
<item quantity="one">%d рэпазіторый</item>
|
||||
<item quantity="few">%d рэпазіторыі</item>
|
||||
<item quantity="many">%d рэпазіторыяў</item>
|
||||
<item quantity="other">%d рэпазіторыяў</item>
|
||||
</plurals>
|
||||
<plurals name="migrationListScreen.migrateDialog.migrateTitle">
|
||||
<item quantity="one">Перанесці %1$d твор?</item>
|
||||
<item quantity="few">Перанесці %1$d творы?</item>
|
||||
<item quantity="many">Перанесці %1$d твораў?</item>
|
||||
<item quantity="other">Перанесці %1$d твораў?</item>
|
||||
</plurals>
|
||||
<plurals name="migrationListScreen.migrateDialog.copyTitle">
|
||||
<item quantity="one">Скапіяваць %1$d твор?</item>
|
||||
<item quantity="few">Скапіяваць %1$d творы?</item>
|
||||
<item quantity="many">Скапіяваць %1$d твораў?</item>
|
||||
<item quantity="other">Скапіяваць %1$d твораў?</item>
|
||||
</plurals>
|
||||
</resources>
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -400,7 +400,6 @@
|
||||
<string name="unknown_author">Неизвестен автор</string>
|
||||
<string name="no_pinned_sources">Нямате отметнати източници</string>
|
||||
<string name="action_global_search_query">Търси „%1$s“ глобално</string>
|
||||
<string name="updated_version">Обновена до v%1$s</string>
|
||||
<string name="whats_new">Какво е новото</string>
|
||||
<string name="backup_restore_missing_trackers">Не сте влезли като потребител в:</string>
|
||||
<string name="pref_remove_bookmarked_chapters">Позволи изтриване на отметнати глави</string>
|
||||
@@ -618,7 +617,6 @@
|
||||
<string name="ext_info_version">Версия</string>
|
||||
<string name="on_hiatus">В хиатус</string>
|
||||
<string name="custom_cover">Корица по избор</string>
|
||||
<string name="notification_update_error">%1$d провалени обновление/я</string>
|
||||
<string name="action_search_hint">Търсене…</string>
|
||||
<string name="disabled_nav">Деактивирано</string>
|
||||
<string name="learn_more">Натисни, за да научиш повече</string>
|
||||
|
||||
@@ -410,7 +410,6 @@
|
||||
<string name="pref_incognito_mode_summary">পড়ার ইতিহাসে বিরতি দেয়</string>
|
||||
<string name="pref_incognito_mode">ছদ্মবেশী মোড</string>
|
||||
<string name="label_downloaded_only">শুধু ডাউনলোড</string>
|
||||
<string name="updated_version">v%1$s এ আপডেট হয়েছে</string>
|
||||
<string name="check_for_updates">সংস্করণ যাচাই</string>
|
||||
<string name="licenses">ওপেন সোর্স লাইসেন্স</string>
|
||||
<string name="whats_new">নতুন কি</string>
|
||||
@@ -551,7 +550,6 @@
|
||||
<string name="skipped_reason_completed">এড়িয়ে যাওয়া হয়েছে কারণ সিরিজটি সম্পূর্ণ</string>
|
||||
<string name="cancelled">বাতিল করা হয়েছে</string>
|
||||
<string name="on_hiatus">বিরতিতে রয়েছে</string>
|
||||
<string name="notification_update_error">%1$d আপডেট(গুলি) ব্যর্থ হয়েছে</string>
|
||||
<string name="publishing_finished">সম্পূর্ণ প্রকাশিত</string>
|
||||
<string name="action_sort_last_manga_update">সর্বশেষ হালনাগাদ চেক</string>
|
||||
<string name="delete_category_confirmation">আপনি কি “%s” ক্যাটাগরিটি মুছে ফেলতে চান?</string>
|
||||
@@ -687,7 +685,6 @@
|
||||
<string name="pref_flash_style_white_black">সাদা আর কালো</string>
|
||||
<string name="pref_hardware_bitmap_threshold_summary">যদি রিডার একটি ফাঁকা ছবি লোড করে, ধাপে ধাপে থ্রেশহোল্ড কমিয়ে দিন।\nসিলেক্টেড %s</string>
|
||||
<string name="pref_always_decode_long_strip_with_ssiv_summary">পারফরম্যান্স অ্যাফেক্ট করবে। ফাঁকা ইমেজ সমস্যা থ্রেশহোল্ড কমিয়ে ঠিক না হলে কেবল এনেবল করবে</string>
|
||||
<string name="label_donate">ডোনেট</string>
|
||||
<string name="pref_downloads_summary">অটোমেটিক ডাউনলোড, আগাম ডাউনলোড</string>
|
||||
<string name="pref_tracking_summary">ওয়ান-ওয়ে প্রগ্রেস সিঙ্ক, এনহ্যান্সড সিঙ্ক</string>
|
||||
<string name="pref_browse_summary">সোর্স, এক্সটেনশন, গ্লোবাল সার্চ</string>
|
||||
|
||||
@@ -386,7 +386,6 @@
|
||||
<string name="unknown_status">Estat desconegut</string>
|
||||
<string name="unknown_author">Autor desconegut</string>
|
||||
<string name="action_global_search_query">Cerca «%1$s» globalment</string>
|
||||
<string name="updated_version">S’ha actualitzat a la v%1$s</string>
|
||||
<string name="whats_new">Novetats</string>
|
||||
<string name="requires_app_restart">Cal reiniciar l’aplicació perquè tingui efecte</string>
|
||||
<string name="label_network">Xarxa</string>
|
||||
@@ -562,7 +561,6 @@
|
||||
<string name="skipped_reason_not_started">S’ha omès perquè no hi ha cap capítol llegit</string>
|
||||
<string name="skipped_reason_not_caught_up">S’ha omès perquè hi ha capítols no llegits</string>
|
||||
<string name="learn_more">Premeu per a obtenir més informació</string>
|
||||
<string name="notification_update_error">Han fallat %1$d actualitzacions</string>
|
||||
<string name="rotation_reverse_portrait">Vertical inversa</string>
|
||||
<string name="action_move_to_top_all_for_series">Mou la sèrie a dalt de tot</string>
|
||||
<string name="disabled_nav">Desactivat</string>
|
||||
@@ -833,7 +831,6 @@
|
||||
<string name="theme_monochrome">Monocrom</string>
|
||||
<string name="library_list">Llista de la biblioteca</string>
|
||||
<string name="action_toggle_private_on">Segueix de manera privada</string>
|
||||
<string name="label_donate">Dóna</string>
|
||||
<string name="action_display_unread_badge">Capítols no llegits</string>
|
||||
<string name="action_notes">Notes</string>
|
||||
<string name="action_edit_notes">Edita les notes</string>
|
||||
|
||||
@@ -419,7 +419,6 @@
|
||||
<string name="pref_incognito_mode">Incognito mode</string>
|
||||
<string name="version">Bersyon</string>
|
||||
<string name="check_for_updates">Susiha ang mga update</string>
|
||||
<string name="updated_version">Mga update sa v%1$s</string>
|
||||
<string name="pref_enable_acra">Ipadala ang mga taho sa pagkahagsa</string>
|
||||
<string name="logout_success">Naka-log out ka na karon</string>
|
||||
<string name="password">Password</string>
|
||||
|
||||
@@ -332,7 +332,6 @@
|
||||
<string name="pref_incognito_mode_summary">Pozastaví historii čtení</string>
|
||||
<string name="pref_incognito_mode">Anonymní režim</string>
|
||||
<string name="label_downloaded_only">Pouze staženo</string>
|
||||
<string name="updated_version">Aktualizováno na verzi v%1$s</string>
|
||||
<string name="check_for_updates">Zkontrolovat aktualizace</string>
|
||||
<string name="licenses">Open Source licence</string>
|
||||
<string name="whats_new">Co je nového</string>
|
||||
@@ -558,7 +557,6 @@
|
||||
<string name="action_show_manga">Zobrazit položku</string>
|
||||
<string name="action_display_cover_only_grid">Mřížka jen s přebaly</string>
|
||||
<string name="pref_update_only_started">Přeskočit nerozečtené položky</string>
|
||||
<string name="notification_update_error">%1$d aktualizace(í) selhalo</string>
|
||||
<string name="on_hiatus">Má pauzu</string>
|
||||
<string name="skipped_reason_not_caught_up">Přeskočeno, protože obsahuje nepřečtené kapitoly</string>
|
||||
<string name="action_move_to_top_all_for_series">Přesunout sérii na začátek</string>
|
||||
@@ -860,7 +858,6 @@
|
||||
<string name="storage_failed_to_create_directory">Nepodařilo se vytvořit adresář: %s</string>
|
||||
<string name="clear_database_text">Chystáte se odstranit položky z databáze</string>
|
||||
<string name="clear_db_exclude_read">Ponechat položky s přečtenými kapitolami</string>
|
||||
<string name="label_donate">Přispěj</string>
|
||||
<string name="theme_catppuccin">Catppuccin</string>
|
||||
<string name="pref_hide_missing_chapter_indicators">Skrýt indikátory chybějících kapitol</string>
|
||||
<string name="pref_disallow_non_ascii_filenames">Nepovolovat soubory s non-ASCII znaky</string>
|
||||
@@ -868,7 +865,6 @@
|
||||
<string name="pref_download_concurrent_sources">Souběžné stahování zdrojů</string>
|
||||
<string name="pref_download_concurrent_pages">Souběžné stahování stránek</string>
|
||||
<string name="action_webview_close_tab">Zavřít okno</string>
|
||||
<string name="calculating">Vypočítávání…</string>
|
||||
<string name="pref_download_concurrent_pages_summary">Souběžně stahované stránky na zdroj</string>
|
||||
<string name="migrationConfigScreen.selectAllLabel">Vybrat vše</string>
|
||||
<string name="migrationConfigScreen.selectNoneLabel">Vybrat žádné</string>
|
||||
|
||||
@@ -391,7 +391,6 @@
|
||||
<string name="pref_restore_backup_summ">Вулавӑша йантӑлавран тавӑр</string>
|
||||
<string name="pref_restore_backup">Йантӑлав тавӑр</string>
|
||||
<string name="pref_create_backup">Йантӑлав ту</string>
|
||||
<string name="updated_version">v%1$s верссиччен ҫӗнетнӗ</string>
|
||||
<string name="whats_new">Мӗн ҫӗнни</string>
|
||||
<string name="pref_category_theme">Темӗ</string>
|
||||
<string name="action_sort_date_added">Хушни вӑхӑчӗпе</string>
|
||||
|
||||
@@ -88,4 +88,8 @@
|
||||
<item quantity="one">Ein Eintrag wurde übersprungen</item>
|
||||
<item quantity="other">%1$d Einträge wurden übersprungen</item>
|
||||
</plurals>
|
||||
<plurals name="notification_update_error">
|
||||
<item quantity="one">%1$d Aktualisierung ist fehlgeschlagen</item>
|
||||
<item quantity="other">%1$d Aktualisierungen sind fehlgeschlagen</item>
|
||||
</plurals>
|
||||
</resources>
|
||||
|
||||
@@ -393,7 +393,6 @@
|
||||
<string name="tapping_inverted_none">–</string>
|
||||
<string name="unknown_status">Unbekannter Status</string>
|
||||
<string name="unknown_author">Unbekannter Autor</string>
|
||||
<string name="updated_version">Auf v%1$s aktualisiert</string>
|
||||
<string name="whats_new">Was ist neu</string>
|
||||
<string name="download_insufficient_space">Herunterladen von Kapiteln aufgrund von zu wenig Speicherplatz nicht möglich</string>
|
||||
<string name="action_global_search_query">Überall nach „%1$s“ suchen</string>
|
||||
@@ -564,7 +563,6 @@
|
||||
<string name="skipped_reason_completed">Übersprungen, da die Serie abgeschlossen ist</string>
|
||||
<string name="skipped_reason_not_caught_up">Übersprungen, weil es ungelesene Kapitel gibt</string>
|
||||
<string name="skipped_reason_not_started">Übersprungen, weil keine Kapitel gelesen wurden</string>
|
||||
<string name="notification_update_error">%1$d Aktualisierung(en) fehlgeschlagen</string>
|
||||
<string name="learn_more">Antippen, um mehr zu erfahren</string>
|
||||
<string name="rotation_reverse_portrait">Umgekehrtes Hochformat</string>
|
||||
<string name="action_move_to_top_all_for_series">Serie nach oben verschieben</string>
|
||||
@@ -901,7 +899,6 @@
|
||||
<string name="migrationListScreen.migrateDialog.cancelLabel">Abbrechen</string>
|
||||
<string name="migrationListScreen.progressDialog.cancelLabel">Abbrechen</string>
|
||||
<string name="migrationListScreen.matchWithoutChapterToast">Keine Kapitel gefunden, dieser Eintrag konnte nicht für eine Migration verwendet werden</string>
|
||||
<string name="label_donate">Spenden</string>
|
||||
<string name="pref_display_images_description">Bilder in Mangabeschreibungen anzeigen</string>
|
||||
<string name="pref_disallow_non_ascii_filenames">Nicht-ASCII-Dateinamen nicht zulassen</string>
|
||||
<string name="pref_disallow_non_ascii_filenames_details">Versichert Kompatibilität mit bestimmten Speichermedien, die Unicode nicht unterstützen. Ist diese Option aktiviert, müssen Quellen- und Manga-Ordner manuell umbenannt werden, indem Nicht-ASCII-Zeichen durch ihre UTF-8-Hexadezimaldarstellungen in Kleinbuchstaben ersetzt werden. Kapiteldateien müssen nicht umbenannt werden.</string>
|
||||
@@ -910,6 +907,18 @@
|
||||
<string name="pref_download_concurrent_pages_summary">Pro Quelle gleichzeitig heruntergeladene Seiten</string>
|
||||
<string name="action_webview_close_tab">Tab schließen</string>
|
||||
<string name="action_filter_excluded_scanlators">Ausgeschlossene Scanlationgruppen herausfiltern</string>
|
||||
<string name="calculating">Wird berechnet…</string>
|
||||
<string name="download_bookmarked">Mit Lesezeichen versehen</string>
|
||||
<string name="label_support_us">Unterstütze Uns</string>
|
||||
<string name="supportUsScreen.perks">Unterstützende erhalten Zugang zu exklusiven Kanälen. Weitere Vorteile folgen.</string>
|
||||
<string name="supportUsScreen.donationPlatform.patreon">Patreon (bevorzugt)</string>
|
||||
<string name="supportUsScreen.donationPlatform.opencollective">OpenCollective</string>
|
||||
<string name="supportUsScreen.currentlySupportedBy">Mihon wird aktuell von %d+ Personen unterstützt. Wir bedanken uns bei euch allen. Lasst uns Mihon gemeinsam noch besser machen.</string>
|
||||
<string name="supportUsScreen.contactForDetailsMessage">Trete unserem Discord-Server für alternative Optionen oder bei weiteren Fragen bei.</string>
|
||||
<string name="supportUsScreen.contactPlatform">Discord</string>
|
||||
<string name="donationCampaign.title">Gefällt dir die App?</string>
|
||||
<string name="donationCampaign.paragraph1">Hi! Ich bin der Hauptentwickler von Mihon und möchte darum bitten, die Entwicklung finanziell zu unterstützen. Mit genügend Unterstützung werde ich in der Lage sein, in Vollzeit an Mihon zu arbeiten und sowohl langjährige Probleme zu beheben als auch häufig gewünschte Features zu implementieren.</string>
|
||||
<string name="donationCampaign.paragraph2">Du kannst entweder mittels Patreon (bevorzugt) oder OpenCollective spenden. Wenn du Fragen hast oder auf anderem Wege die Entwicklung unterstützen möchstest, dann kannst du mich gerne über Mihons Discord-Server erreichen.</string>
|
||||
<string name="donationCampaign.paragraph3">Unabhängig davon wird Mihon weiterhin von mir und anderen Freiwilligen entwickelt werden und bleibt Open-Source-Software.</string>
|
||||
<string name="donationCampaign.contactPlatform">Discord</string>
|
||||
<string name="donationCampaign.dismiss">Verwerfen</string>
|
||||
</resources>
|
||||
|
||||
@@ -88,4 +88,8 @@
|
||||
<item quantity="one">Μια καταχώρηση παραλείφθηκε</item>
|
||||
<item quantity="other">%1$d καταχωρήσεις παραλείφθηκαν</item>
|
||||
</plurals>
|
||||
<plurals name="notification_update_error">
|
||||
<item quantity="one">%1$d ενημέρωση απέτυχε</item>
|
||||
<item quantity="other">%1$d ενημερώσεις απέτυχαν</item>
|
||||
</plurals>
|
||||
</resources>
|
||||
|
||||
@@ -393,7 +393,6 @@
|
||||
<string name="pref_read_with_tapping_inverted">Αντιστροφή ζωνών πατήματος</string>
|
||||
<string name="unknown_status">Άγνωστη κατάσταση</string>
|
||||
<string name="unknown_author">Άγνωστος συγγραφέας</string>
|
||||
<string name="updated_version">Ενημερώθηκε σε v%1$s</string>
|
||||
<string name="whats_new">Τι νέο υπάρχει</string>
|
||||
<string name="download_insufficient_space">Δεν ήταν δυνατή η λήψη κεφαλαίων λόγω χαμηλού αποθηκευτικού χώρου</string>
|
||||
<string name="action_global_search_query">Αναζήτηση του \"%1$s\" καθολικά</string>
|
||||
@@ -564,7 +563,6 @@
|
||||
<string name="skipped_reason_completed">Παραλείφθηκε επειδή η σειρά ολοκληρώθηκε</string>
|
||||
<string name="skipped_reason_not_caught_up">Παραβλέφθηκε επειδή υπάρχουν μη αναγνωσμένα κεφάλαια</string>
|
||||
<string name="skipped_reason_not_started">Παραλείπεται επειδή δεν έχουν διαβαστεί κεφάλαια</string>
|
||||
<string name="notification_update_error">%1$d ενημέρωση(ες) απέτυχε(-αν)</string>
|
||||
<string name="learn_more">Πατήστε για να μάθετε περισσότερα</string>
|
||||
<string name="rotation_reverse_portrait">Αντίστροφο πορτρέτο</string>
|
||||
<string name="action_move_to_top_all_for_series">Μετακίνηση σειράς προς τα πάνω</string>
|
||||
@@ -901,7 +899,6 @@
|
||||
<string name="clear_db_exclude_read">Κρατήστε καταχωρήσεις με αναγνωσμένα κεφάλαια</string>
|
||||
<string name="storage_failed_to_create_download_directory">Αποτυχία δημιουργίας καταλόγου λήψης</string>
|
||||
<string name="storage_failed_to_create_directory">Αποτυχία δημιουργίας καταλόγου: %s</string>
|
||||
<string name="label_donate">Δωρεά</string>
|
||||
<string name="pref_display_images_description">Αναπαράσταση εικόνων σε περιγραφές manga</string>
|
||||
<string name="pref_disallow_non_ascii_filenames">Απαγόρευση μη ASCII ονομάτων αρχείων</string>
|
||||
<string name="pref_disallow_non_ascii_filenames_details">Εξασφαλίζει συμβατότητα με ορισμένα μέσα αποθήκευσης που δεν υποστηρίζουν Unicode. Όταν αυτή η επιλογή είναι ενεργοποιημένη, θα πρέπει να μετονομάσετε χειροκίνητα τους φακέλους πηγής και manga, αντικαθιστώντας τους χαρακτήρες που δεν είναι ASCII με τις μικρές κεφαλαίες δεκαεξαδικές αναπαραστάσεις UTF-8. Τα αρχεία κεφαλαίων δε χρειάζεται να μετονομάζονται.</string>
|
||||
@@ -909,7 +906,7 @@
|
||||
<string name="pref_download_concurrent_sources">Ταυτόχρονες λήψεις πηγών</string>
|
||||
<string name="pref_download_concurrent_pages">Ταυτόχρονες λήψεις σελίδων</string>
|
||||
<string name="pref_download_concurrent_pages_summary">Σελίδες που λήφθηκαν ταυτόχρονα ανά πηγή</string>
|
||||
<string name="calculating">Υπολογισμός…</string>
|
||||
<string name="download_bookmarked">Προστέθηκε στους σελιδοδείκτες</string>
|
||||
<string name="action_filter_excluded_scanlators">Φιλτράρισμα εξαιρούμενων scanlator</string>
|
||||
<string name="label_support_us">Υποστηρίξτε μας</string>
|
||||
</resources>
|
||||
|
||||
@@ -297,7 +297,6 @@
|
||||
<string name="login">Ensaluti</string>
|
||||
<string name="login_title">Ensaluti al %1$s</string>
|
||||
<string name="label_downloaded_only">Nur elŝutitaj</string>
|
||||
<string name="updated_version">Ĝisdatigita al v%1$s</string>
|
||||
<string name="licenses">Malfermitkodaj permesiloj</string>
|
||||
<string name="whats_new">Kio novas</string>
|
||||
<string name="version">Versio</string>
|
||||
@@ -815,7 +814,6 @@
|
||||
<string name="download_queue_size_warning">Averto: grandaj amasaj elŝutoj povas igi fontojn fariĝi pli malrapidaj kaj/aŭ bloki %s. Tuŝetu por ekscii pli.</string>
|
||||
<string name="notification_updating_progress">Ĝisdatigado de biblioteko… (%s)</string>
|
||||
<string name="notification_size_warning">Grandaj ĝisdatigoj povas damaĝi fontojn kaj konduki al pli malrapidaj ĝisdatigoj kaj pliigan baterian uzadon. Tuŝetu por ekscii pli.</string>
|
||||
<string name="notification_update_error">%1$d ĝisdatigo(j) fiaskis</string>
|
||||
<string name="learn_more">Tuŝetu por ekscii pli</string>
|
||||
<string name="notification_first_add_to_library">Bonvolu aldoni la elementon al via biblioteko antaŭ ol fari tion</string>
|
||||
<string name="library_errors_help">Por helpo pri kiel ripari erarojn de bibliotekaj ĝisdatigoj, vidu %1$s</string>
|
||||
@@ -889,7 +887,6 @@
|
||||
<string name="migrationListScreen.migrateDialog.cancelLabel">Nuligi</string>
|
||||
<string name="migrationListScreen.progressDialog.cancelLabel">Nuligi</string>
|
||||
<string name="migrationListScreen.matchWithoutChapterToast">Neniu ĉapitro trovita, ĉi tiu elemento ne povas esti uzota por migri</string>
|
||||
<string name="label_donate">Donacu</string>
|
||||
<string name="theme_catppuccin">Katpuĉino</string>
|
||||
<string name="pref_display_images_description">Montri bildojn en mangaaj priskriboj</string>
|
||||
<string name="pref_hide_missing_chapter_indicators">Kaŝi mankantajn ĉapitrajn indikilojn</string>
|
||||
@@ -901,6 +898,5 @@
|
||||
<string name="pref_download_concurrent_pages">Kunrulaj elŝutoj de paĝoj</string>
|
||||
<string name="pref_download_concurrent_pages_summary">Paĝoj elŝutitaj sammomente por fonto</string>
|
||||
<string name="action_filter_excluded_scanlators">Filtri ekskluzivitajn skantradukistojn</string>
|
||||
<string name="calculating">Kalkulado…</string>
|
||||
<string name="download_bookmarked">Legosignita</string>
|
||||
</resources>
|
||||
|
||||
@@ -110,4 +110,9 @@
|
||||
<item quantity="many">¿Migrar estos %1$d elementos?</item>
|
||||
<item quantity="other">¿Migrar estos %1$d elementos?</item>
|
||||
</plurals>
|
||||
<plurals name="notification_update_error">
|
||||
<item quantity="one">No se ha podido actualizar %1$d elemento</item>
|
||||
<item quantity="many">No se han podido actualizar %1$d elementos</item>
|
||||
<item quantity="other">No se han podido actualizar %1$d elementos</item>
|
||||
</plurals>
|
||||
</resources>
|
||||
|
||||
@@ -391,7 +391,6 @@
|
||||
<string name="tapping_inverted_none">No invertir</string>
|
||||
<string name="pref_read_with_tapping_inverted">Invertir zonas de toque</string>
|
||||
<string name="unknown_author">Autor desconocido</string>
|
||||
<string name="updated_version">Actualizado a v%1$s</string>
|
||||
<string name="whats_new">Novedades</string>
|
||||
<string name="download_insufficient_space">No se pudo descargar ningún capítulo, queda muy poco espacio</string>
|
||||
<string name="action_global_search_query">Buscar por «%1$s» en todas las fuentes</string>
|
||||
@@ -563,7 +562,6 @@
|
||||
<string name="skipped_reason_not_caught_up">Omitido porque todavía hay capítulos sin leer</string>
|
||||
<string name="skipped_reason_not_started">Omitido porque no hay capítulos leídos</string>
|
||||
<string name="learn_more">Ver más detalles</string>
|
||||
<string name="notification_update_error">Actualizaciones fallidas: %1$d</string>
|
||||
<string name="rotation_reverse_portrait">En vertical, al revés</string>
|
||||
<string name="action_move_to_top_all_for_series">Mover al primer puesto</string>
|
||||
<string name="disabled_nav">Desactivado</string>
|
||||
@@ -894,7 +892,6 @@
|
||||
<string name="migrationListScreenTitleWithProgress">Migrando (%1$d/%2$d)</string>
|
||||
<string name="migrationListScreen.migrateNowActionLabel">Migrar ahora</string>
|
||||
<string name="pref_hide_missing_chapter_indicators">Ocultar los indicadores de capítulos que falten</string>
|
||||
<string name="label_donate">Donar</string>
|
||||
<string name="pref_display_images_description">Ver imágenes en las descripciones de manga</string>
|
||||
<string name="pref_disallow_non_ascii_filenames">Prohibir los nombres de archivo que no sean ASCII</string>
|
||||
<string name="pref_disallow_non_ascii_filenames_details">Permite guardar tus datos en ciertos tipos de almacenamiento que no admitan Unicode. Al activarlo tendrás que renombrar las carpetas de tus fuentes y manga, pasándolas a representaciones UTF-8 en hexadecimal en minúscula. No tienes que hacer lo mismo con las carpetas de capítulos.</string>
|
||||
@@ -903,6 +900,18 @@
|
||||
<string name="pref_download_concurrent_pages_summary">Cantidad de páginas a descargar simultáneamente desde cada fuente remota</string>
|
||||
<string name="action_webview_close_tab">Cerrar pestaña</string>
|
||||
<string name="action_filter_excluded_scanlators">Ocultar equipos de traducción excluidos</string>
|
||||
<string name="calculating">Calculando…</string>
|
||||
<string name="download_bookmarked">Favoritos</string>
|
||||
<string name="label_support_us">Apóyanos</string>
|
||||
<string name="supportUsScreen.perks">Los patrocinadores obtienen acceso a canales exclusivos, con más beneficios por venir.</string>
|
||||
<string name="supportUsScreen.donationPlatform.patreon">Patreon (preferido)</string>
|
||||
<string name="supportUsScreen.donationPlatform.opencollective">OpenCollective</string>
|
||||
<string name="supportUsScreen.currentlySupportedBy">Mihon cuenta actualmente con el apoyo de %d+ patrocinadores. Queremos agradecerles a cada uno de ustedes. Hagamos Mihon aún mejor juntos.</string>
|
||||
<string name="supportUsScreen.contactForDetailsMessage">Para opciones alternativas o cualquier pregunta relacionada, únete a nuestro servidor de Discord.</string>
|
||||
<string name="supportUsScreen.contactPlatform">Discord</string>
|
||||
<string name="donationCampaign.title">¿Disfrutando la aplicación?</string>
|
||||
<string name="donationCampaign.paragraph1">¡Hola! Soy el desarrollador principal de Mihon, y me gustaría pedirte que consideres apoyar su desarrollo económicamente. Con suficiente apoyo, podré trabajar en Mihon a tiempo completo y centrarme en problemas antiguos, así como en funciones muy solicitadas.</string>
|
||||
<string name="donationCampaign.paragraph2">Puedes donar a través de Patreon (preferido) o OpenCollective. Si tienes preguntas o prefieres apoyar usando otros métodos, no dudes en contactarme a través del servidor de Discord de Mihon.</string>
|
||||
<string name="donationCampaign.paragraph3">De todas formas, Mihon seguirá siendo mantenido por mí y los voluntarios, y se mantendrá como código abierto.</string>
|
||||
<string name="donationCampaign.dismiss">Descartar</string>
|
||||
<string name="donationCampaign.contactPlatform">Discord</string>
|
||||
</resources>
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user