From 4867bf18a05aebc52f3d06cf89140637ae7a64a9 Mon Sep 17 00:00:00 2001 From: AwkwardPeak7 <48650614+AwkwardPeak7@users.noreply.github.com> Date: Sun, 2 Feb 2025 12:20:28 +0500 Subject: [PATCH] Update to Kotlin 2.1.0 and add spotless linter (#7447) * update kotlin & deps * add spotless * fetch depth * lint... * adjust rules * Revert "lint..." This reverts commit cc2f99fb218726d90045c5104ab9592a179cb6b6. * lint * rm generated file --- buildSrc/build.gradle.kts | 2 +- .../src/main/kotlin/keiyoushi.lint.gradle.kts | 50 ++++++++++++ .../src/main/kotlin/lib-android.gradle.kts | 11 +++ .../src/main/kotlin/lib-multisrc.gradle.kts | 28 +++---- common.gradle | 47 ++++-------- gradle/libs.versions.toml | 15 ++-- src/en/mangamo/build.gradle | 2 +- .../extension/en/mangamo/MangamoAuth.kt | 7 +- .../tachiyomi/extension/pt/taiyo/Taiyo.kt | 76 +++++++------------ 9 files changed, 126 insertions(+), 112 deletions(-) create mode 100644 buildSrc/src/main/kotlin/keiyoushi.lint.gradle.kts 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 {