diff --git a/buildSrc/build.gradle.kts b/buildSrc/build.gradle.kts
index deebe960b..6a112583f 100644
--- a/buildSrc/build.gradle.kts
+++ b/buildSrc/build.gradle.kts
@@ -12,5 +12,5 @@ dependencies {
implementation(libs.gradle.agp)
implementation(libs.gradle.kotlin)
implementation(libs.gradle.serialization)
- implementation(libs.gradle.kotlinter)
+ implementation(libs.spotless.gradle)
}
diff --git a/buildSrc/src/main/kotlin/keiyoushi.lint.gradle.kts b/buildSrc/src/main/kotlin/keiyoushi.lint.gradle.kts
new file mode 100644
index 000000000..65b486e49
--- /dev/null
+++ b/buildSrc/src/main/kotlin/keiyoushi.lint.gradle.kts
@@ -0,0 +1,50 @@
+plugins {
+ id("com.diffplug.spotless")
+}
+
+spotless {
+ ratchetFrom = "105f615b339e681a630f21dc0d363b8ca1cb17d5"
+
+ kotlin {
+ target("**/*.kt", "**/*.kts")
+ targetExclude("**/build/**/*.kt")
+ ktlint()
+ .editorConfigOverride(mapOf(
+ "ktlint_standard_discouraged-comment-location" to "disabled",
+ "ktlint_function_signature_body_expression_wrapping" to "default",
+ "ktlint_standard_no-empty-first-line-in-class-body" to "disable",
+ "ktlint_standard_chain-method-continuation" to "disable"
+ ))
+ trimTrailingWhitespace()
+ endWithNewline()
+ }
+
+ format("gradle") {
+ target("**/*.gradle")
+ trimTrailingWhitespace()
+ endWithNewline()
+ }
+
+ format("xml") {
+ target("**/*.xml")
+ targetExclude("**/build/**/*.xml")
+ trimTrailingWhitespace()
+ endWithNewline()
+ }
+}
+
+tasks {
+ named("preBuild") {
+ dependsOn(
+ tasks.getByName("spotlessCheck")
+ )
+ }
+
+ if (System.getenv("CI") != "true") {
+ named("spotlessCheck") {
+ dependsOn(
+ tasks.getByName("spotlessApply")
+ )
+ }
+ }
+}
diff --git a/buildSrc/src/main/kotlin/lib-android.gradle.kts b/buildSrc/src/main/kotlin/lib-android.gradle.kts
index ee9eb1c22..0e1c83130 100644
--- a/buildSrc/src/main/kotlin/lib-android.gradle.kts
+++ b/buildSrc/src/main/kotlin/lib-android.gradle.kts
@@ -2,6 +2,7 @@ plugins {
id("com.android.library")
kotlin("android")
id("kotlinx-serialization")
+ id("keiyoushi.lint")
}
android {
@@ -16,6 +17,16 @@ android {
buildFeatures {
androidResources = false
}
+
+ compileOptions {
+ sourceCompatibility = JavaVersion.VERSION_17
+ targetCompatibility = JavaVersion.VERSION_17
+ }
+
+ kotlinOptions {
+ jvmTarget = JavaVersion.VERSION_17.toString()
+ freeCompilerArgs += "-opt-in=kotlinx.serialization.ExperimentalSerializationApi"
+ }
}
dependencies {
diff --git a/buildSrc/src/main/kotlin/lib-multisrc.gradle.kts b/buildSrc/src/main/kotlin/lib-multisrc.gradle.kts
index 29e52f46f..3ed2f14b2 100644
--- a/buildSrc/src/main/kotlin/lib-multisrc.gradle.kts
+++ b/buildSrc/src/main/kotlin/lib-multisrc.gradle.kts
@@ -2,7 +2,7 @@ plugins {
id("com.android.library")
kotlin("android")
id("kotlinx-serialization")
- id("org.jmailen.kotlinter")
+ id("keiyoushi.lint")
}
android {
@@ -23,31 +23,23 @@ android {
}
}
+ compileOptions {
+ sourceCompatibility = JavaVersion.VERSION_17
+ targetCompatibility = JavaVersion.VERSION_17
+ }
+
kotlinOptions {
+ jvmTarget = JavaVersion.VERSION_17.toString()
freeCompilerArgs += "-opt-in=kotlinx.serialization.ExperimentalSerializationApi"
}
}
-kotlinter {
- experimentalRules = true
- disabledRules = arrayOf(
- "experimental:argument-list-wrapping", // Doesn't play well with Android Studio
- "experimental:comment-wrapping",
- )
-}
-
dependencies {
compileOnly(versionCatalogs.named("libs").findBundle("common").get())
}
-tasks {
- preBuild {
- dependsOn(lintKotlin)
- }
-
- if (System.getenv("CI") != "true") {
- lintKotlin {
- dependsOn(formatKotlin)
- }
+tasks.register("printDependentExtensions") {
+ doLast {
+ project.printDependentExtensions()
}
}
diff --git a/common.gradle b/common.gradle
index f916f509a..787dcf07b 100644
--- a/common.gradle
+++ b/common.gradle
@@ -1,7 +1,7 @@
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlinx-serialization'
-apply plugin: 'org.jmailen.kotlinter'
+apply plugin: 'keiyoushi.lint'
assert !ext.has("pkgNameSuffix")
assert !ext.has("libVersion")
@@ -18,6 +18,17 @@ android {
sourceSets {
main {
manifest.srcFile "AndroidManifest.xml"
+ if (!manifest.srcFile.exists()) {
+ def buildDir = layout.buildDirectory.get().getAsFile().path
+ mkdir(buildDir)
+ File tempFile = new File(buildDir, "tempAndroidManifest.xml")
+ if (!tempFile.exists()) {
+ tempFile.withWriter {
+ it.write('\n\n')
+ }
+ }
+ manifest.srcFile(tempFile.path)
+ }
java.srcDirs = ['src']
res.srcDirs = ['res']
assets.srcDirs = ['assets']
@@ -81,22 +92,14 @@ android {
}
compileOptions {
- sourceCompatibility = JavaVersion.VERSION_1_8
- targetCompatibility = JavaVersion.VERSION_1_8
+ sourceCompatibility = JavaVersion.VERSION_17
+ targetCompatibility = JavaVersion.VERSION_17
}
kotlinOptions {
- jvmTarget = JavaVersion.VERSION_1_8.toString()
+ jvmTarget = JavaVersion.VERSION_17.toString()
freeCompilerArgs += "-opt-in=kotlinx.serialization.ExperimentalSerializationApi"
}
-
- kotlinter {
- experimentalRules = true
- disabledRules = [
- "experimental:argument-list-wrapping", // Doesn't play well with Android Studio
- "experimental:comment-wrapping",
- ]
- }
}
dependencies {
@@ -104,23 +107,3 @@ dependencies {
implementation(project(":core"))
compileOnly(libs.bundles.common)
}
-
-tasks.register("writeManifestFile") {
- doLast {
- def manifest = android.sourceSets.getByName("main").manifest
- if (!manifest.srcFile.exists()) {
- File tempFile = layout.buildDirectory.get().file("tempAndroidManifest.xml").getAsFile()
- if (!tempFile.exists()) {
- tempFile.withWriter {
- it.write('\n\n')
- }
- }
- manifest.srcFile(tempFile.path)
- }
- }
-}
-
-preBuild.dependsOn(writeManifestFile, lintKotlin)
-if (System.getenv("CI") != "true") {
- lintKotlin.dependsOn(formatKotlin)
-}
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index e5127d7fd..aeea7d93c 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -1,13 +1,14 @@
[versions]
-kotlin_version = "1.7.21"
-coroutines_version = "1.6.4"
-serialization_version = "1.4.0"
+kotlin_version = "2.1.0"
+coroutines_version = "1.10.1"
+serialization_version = "1.8.0"
[libraries]
-gradle-agp = { module = "com.android.tools.build:gradle", version = "8.6.1" }
+gradle-agp = { module = "com.android.tools.build:gradle", version = "8.8.0" }
gradle-kotlin = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin", version.ref = "kotlin_version" }
gradle-serialization = { module = "org.jetbrains.kotlin:kotlin-serialization", version.ref = "kotlin_version" }
-gradle-kotlinter = { module = "org.jmailen.gradle:kotlinter-gradle", version = "3.13.0" }
+
+spotless-gradle = { group = "com.diffplug.spotless", name = "spotless-plugin-gradle", version = "7.0.2" }
tachiyomi-lib = { module = "com.github.tachiyomiorg:extensions-lib", version = "1.4.2" }
@@ -20,8 +21,8 @@ coroutines-android = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-androi
injekt-core = { module = "com.github.null2264.injekt:injekt-core", version = "4135455a2a" }
rxjava = { module = "io.reactivex:rxjava", version = "1.3.8" }
-jsoup = { module = "org.jsoup:jsoup", version = "1.15.1" }
-okhttp = { module = "com.squareup.okhttp3:okhttp", version = "5.0.0-alpha.11" }
+jsoup = { module = "org.jsoup:jsoup", version = "1.18.3" }
+okhttp = { module = "com.squareup.okhttp3:okhttp", version = "5.0.0-alpha.14" }
quickjs = { module = "app.cash.quickjs:quickjs-android", version = "0.9.2" }
[bundles]
diff --git a/src/en/mangamo/build.gradle b/src/en/mangamo/build.gradle
index b5dd1e62a..647332f2f 100644
--- a/src/en/mangamo/build.gradle
+++ b/src/en/mangamo/build.gradle
@@ -1,7 +1,7 @@
ext {
extName = 'Mangamo'
extClass = '.Mangamo'
- extVersionCode = 1
+ extVersionCode = 2
isNsfw = false
}
diff --git a/src/en/mangamo/src/eu/kanade/tachiyomi/extension/en/mangamo/MangamoAuth.kt b/src/en/mangamo/src/eu/kanade/tachiyomi/extension/en/mangamo/MangamoAuth.kt
index 95036d8a5..f6d3d939b 100644
--- a/src/en/mangamo/src/eu/kanade/tachiyomi/extension/en/mangamo/MangamoAuth.kt
+++ b/src/en/mangamo/src/eu/kanade/tachiyomi/extension/en/mangamo/MangamoAuth.kt
@@ -9,7 +9,6 @@ import eu.kanade.tachiyomi.network.POST
import okhttp3.Headers
import okhttp3.OkHttpClient
import okhttp3.RequestBody.Companion.toRequestBody
-import okhttp3.internal.EMPTY_HEADERS
class MangamoAuth(
private val helper: MangamoHelper,
@@ -53,8 +52,7 @@ class MangamoAuth(
val googleIdentityResponse = client.newCall(
POST(
"https://identitytoolkit.googleapis.com/v1/accounts:signInWithCustomToken?key=${MangamoConstants.FIREBASE_API_KEY}",
- EMPTY_HEADERS,
- "{\"token\":\"$customToken\",\"returnSecureToken\":true}".toRequestBody(),
+ body = "{\"token\":\"$customToken\",\"returnSecureToken\":true}".toRequestBody(),
),
).execute()
@@ -97,8 +95,7 @@ class MangamoAuth(
val googleIdentityResponse = client.newCall(
POST(
"https://identitytoolkit.googleapis.com/v1/accounts:signUp?key=${MangamoConstants.FIREBASE_API_KEY}",
- EMPTY_HEADERS,
- "{\"returnSecureToken\":true}".toRequestBody(),
+ body = "{\"returnSecureToken\":true}".toRequestBody(),
),
).execute()
diff --git a/src/pt/taiyo/src/eu/kanade/tachiyomi/extension/pt/taiyo/Taiyo.kt b/src/pt/taiyo/src/eu/kanade/tachiyomi/extension/pt/taiyo/Taiyo.kt
index 211acbf09..0b3d89b5c 100644
--- a/src/pt/taiyo/src/eu/kanade/tachiyomi/extension/pt/taiyo/Taiyo.kt
+++ b/src/pt/taiyo/src/eu/kanade/tachiyomi/extension/pt/taiyo/Taiyo.kt
@@ -32,8 +32,6 @@ import okhttp3.MediaType.Companion.toMediaType
import okhttp3.Request
import okhttp3.RequestBody.Companion.toRequestBody
import okhttp3.Response
-import okhttp3.internal.http.HTTP_FORBIDDEN
-import okhttp3.internal.http.HTTP_UNAUTHORIZED
import org.jsoup.nodes.Document
import org.jsoup.nodes.Element
import org.jsoup.select.Elements
@@ -41,6 +39,8 @@ import rx.Observable
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import uy.kohesive.injekt.injectLazy
+import java.net.HttpURLConnection.HTTP_FORBIDDEN
+import java.net.HttpURLConnection.HTTP_UNAUTHORIZED
import java.text.SimpleDateFormat
import java.util.Locale
@@ -85,15 +85,13 @@ class Taiyo : ParsedHttpSource() {
// =============================== Search ===============================
- override fun fetchSearchManga(page: Int, query: String, filters: FilterList): Observable {
- return if (query.startsWith(PREFIX_SEARCH)) { // URL intent handler
- val id = query.removePrefix(PREFIX_SEARCH)
- client.newCall(GET("$baseUrl/media/$id"))
- .asObservableSuccess()
- .map(::searchMangaByIdParse)
- } else {
- super.fetchSearchManga(page, query, filters)
- }
+ override fun fetchSearchManga(page: Int, query: String, filters: FilterList): Observable = if (query.startsWith(PREFIX_SEARCH)) { // URL intent handler
+ val id = query.removePrefix(PREFIX_SEARCH)
+ client.newCall(GET("$baseUrl/media/$id"))
+ .asObservableSuccess()
+ .map(::searchMangaByIdParse)
+ } else {
+ super.fetchSearchManga(page, query, filters)
}
private fun searchMangaByIdParse(response: Response): MangasPage {
@@ -146,17 +144,11 @@ class Taiyo : ParsedHttpSource() {
return MangasPage(mangas, mangas.isNotEmpty())
}
- override fun searchMangaSelector(): String {
- throw UnsupportedOperationException()
- }
+ override fun searchMangaSelector(): String = throw UnsupportedOperationException()
- override fun searchMangaFromElement(element: Element): SManga {
- throw UnsupportedOperationException()
- }
+ override fun searchMangaFromElement(element: Element): SManga = throw UnsupportedOperationException()
- override fun searchMangaNextPageSelector(): String? {
- throw UnsupportedOperationException()
- }
+ override fun searchMangaNextPageSelector(): String? = throw UnsupportedOperationException()
// =========================== Manga Details ============================
override fun mangaDetailsParse(document: Document) = SManga.create().apply {
@@ -235,13 +227,9 @@ class Taiyo : ParsedHttpSource() {
return Observable.just(chapters.sortedByDescending { it.chapter_number })
}
- override fun chapterListSelector(): String {
- throw UnsupportedOperationException()
- }
+ override fun chapterListSelector(): String = throw UnsupportedOperationException()
- override fun chapterFromElement(element: Element): SChapter {
- throw UnsupportedOperationException()
- }
+ override fun chapterFromElement(element: Element): SChapter = throw UnsupportedOperationException()
// =============================== Pages ================================
override fun pageListParse(document: Document): List {
@@ -256,9 +244,7 @@ class Taiyo : ParsedHttpSource() {
}
}
- override fun imageUrlParse(document: Document): String {
- throw UnsupportedOperationException()
- }
+ override fun imageUrlParse(document: Document): String = throw UnsupportedOperationException()
// ============================= Utilities ==============================
private fun Element.getImageUrl() = absUrl("srcset").substringBefore(" ")
@@ -267,23 +253,19 @@ class Taiyo : ParsedHttpSource() {
json.decodeFromStream(it.body.byteStream())
}
- private fun String.toDate(): Long {
- return runCatching { DATE_FORMATTER.parse(this)?.time }
- .getOrNull() ?: 0L
- }
+ private fun String.toDate(): Long = runCatching { DATE_FORMATTER.parse(this)?.time }
+ .getOrNull() ?: 0L
private inline fun Document.parseJsonFromDocument(
itemName: String = "media",
crossinline transformer: String.() -> String,
- ): T? {
- return runCatching {
- val script = selectFirst("script:containsData($itemName\\\\\":):containsData(\\\"6:\\[)")!!.data()
- val obj = script.substringAfter(",{\\\"$itemName\\\":")
- .run(transformer)
- .replace("\\", "")
- json.decodeFromString(obj)
- }.onFailure { it.printStackTrace() }.getOrNull()
- }
+ ): T? = runCatching {
+ val script = selectFirst("script:containsData($itemName\\\\\":):containsData(\\\"6:\\[)")!!.data()
+ val obj = script.substringAfter(",{\\\"$itemName\\\":")
+ .run(transformer)
+ .replace("\\", "")
+ json.decodeFromString(obj)
+ }.onFailure { it.printStackTrace() }.getOrNull()
// ============================= Authorization ========================
@@ -304,12 +286,10 @@ class Taiyo : ParsedHttpSource() {
return chain.proceed(req)
}
- private fun getToken(): String {
- return fetchBearerToken().also {
- preferences.edit()
- .putString(BEARER_TOKEN_PREF, it)
- .apply()
- }
+ private fun getToken(): String = fetchBearerToken().also {
+ preferences.edit()
+ .putString(BEARER_TOKEN_PREF, it)
+ .apply()
}
private fun fetchBearerToken(): String {