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
This commit is contained in:
AwkwardPeak7 2025-02-02 12:20:28 +05:00 committed by Draff
parent 86a24181b5
commit 4867bf18a0
No known key found for this signature in database
GPG Key ID: E8A89F3211677653
9 changed files with 126 additions and 112 deletions

View File

@ -12,5 +12,5 @@ dependencies {
implementation(libs.gradle.agp) implementation(libs.gradle.agp)
implementation(libs.gradle.kotlin) implementation(libs.gradle.kotlin)
implementation(libs.gradle.serialization) implementation(libs.gradle.serialization)
implementation(libs.gradle.kotlinter) implementation(libs.spotless.gradle)
} }

View File

@ -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")
)
}
}
}

View File

@ -2,6 +2,7 @@ plugins {
id("com.android.library") id("com.android.library")
kotlin("android") kotlin("android")
id("kotlinx-serialization") id("kotlinx-serialization")
id("keiyoushi.lint")
} }
android { android {
@ -16,6 +17,16 @@ android {
buildFeatures { buildFeatures {
androidResources = false androidResources = false
} }
compileOptions {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}
kotlinOptions {
jvmTarget = JavaVersion.VERSION_17.toString()
freeCompilerArgs += "-opt-in=kotlinx.serialization.ExperimentalSerializationApi"
}
} }
dependencies { dependencies {

View File

@ -2,7 +2,7 @@ plugins {
id("com.android.library") id("com.android.library")
kotlin("android") kotlin("android")
id("kotlinx-serialization") id("kotlinx-serialization")
id("org.jmailen.kotlinter") id("keiyoushi.lint")
} }
android { android {
@ -23,31 +23,23 @@ android {
} }
} }
kotlinOptions { compileOptions {
freeCompilerArgs += "-opt-in=kotlinx.serialization.ExperimentalSerializationApi" sourceCompatibility = JavaVersion.VERSION_17
} targetCompatibility = JavaVersion.VERSION_17
} }
kotlinter { kotlinOptions {
experimentalRules = true jvmTarget = JavaVersion.VERSION_17.toString()
disabledRules = arrayOf( freeCompilerArgs += "-opt-in=kotlinx.serialization.ExperimentalSerializationApi"
"experimental:argument-list-wrapping", // Doesn't play well with Android Studio }
"experimental:comment-wrapping",
)
} }
dependencies { dependencies {
compileOnly(versionCatalogs.named("libs").findBundle("common").get()) compileOnly(versionCatalogs.named("libs").findBundle("common").get())
} }
tasks { tasks.register("printDependentExtensions") {
preBuild { doLast {
dependsOn(lintKotlin) project.printDependentExtensions()
}
if (System.getenv("CI") != "true") {
lintKotlin {
dependsOn(formatKotlin)
}
} }
} }

View File

@ -1,7 +1,7 @@
apply plugin: 'com.android.application' apply plugin: 'com.android.application'
apply plugin: 'kotlin-android' apply plugin: 'kotlin-android'
apply plugin: 'kotlinx-serialization' apply plugin: 'kotlinx-serialization'
apply plugin: 'org.jmailen.kotlinter' apply plugin: 'keiyoushi.lint'
assert !ext.has("pkgNameSuffix") assert !ext.has("pkgNameSuffix")
assert !ext.has("libVersion") assert !ext.has("libVersion")
@ -18,6 +18,17 @@ android {
sourceSets { sourceSets {
main { main {
manifest.srcFile "AndroidManifest.xml" 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('<?xml version="1.0" encoding="utf-8"?>\n<manifest />\n')
}
}
manifest.srcFile(tempFile.path)
}
java.srcDirs = ['src'] java.srcDirs = ['src']
res.srcDirs = ['res'] res.srcDirs = ['res']
assets.srcDirs = ['assets'] assets.srcDirs = ['assets']
@ -81,22 +92,14 @@ android {
} }
compileOptions { compileOptions {
sourceCompatibility = JavaVersion.VERSION_1_8 sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_1_8 targetCompatibility = JavaVersion.VERSION_17
} }
kotlinOptions { kotlinOptions {
jvmTarget = JavaVersion.VERSION_1_8.toString() jvmTarget = JavaVersion.VERSION_17.toString()
freeCompilerArgs += "-opt-in=kotlinx.serialization.ExperimentalSerializationApi" 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 { dependencies {
@ -104,23 +107,3 @@ dependencies {
implementation(project(":core")) implementation(project(":core"))
compileOnly(libs.bundles.common) 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('<?xml version="1.0" encoding="utf-8"?>\n<manifest />\n')
}
}
manifest.srcFile(tempFile.path)
}
}
}
preBuild.dependsOn(writeManifestFile, lintKotlin)
if (System.getenv("CI") != "true") {
lintKotlin.dependsOn(formatKotlin)
}

View File

@ -1,13 +1,14 @@
[versions] [versions]
kotlin_version = "1.7.21" kotlin_version = "2.1.0"
coroutines_version = "1.6.4" coroutines_version = "1.10.1"
serialization_version = "1.4.0" serialization_version = "1.8.0"
[libraries] [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-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-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" } 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" } injekt-core = { module = "com.github.null2264.injekt:injekt-core", version = "4135455a2a" }
rxjava = { module = "io.reactivex:rxjava", version = "1.3.8" } rxjava = { module = "io.reactivex:rxjava", version = "1.3.8" }
jsoup = { module = "org.jsoup:jsoup", version = "1.15.1" } jsoup = { module = "org.jsoup:jsoup", version = "1.18.3" }
okhttp = { module = "com.squareup.okhttp3:okhttp", version = "5.0.0-alpha.11" } okhttp = { module = "com.squareup.okhttp3:okhttp", version = "5.0.0-alpha.14" }
quickjs = { module = "app.cash.quickjs:quickjs-android", version = "0.9.2" } quickjs = { module = "app.cash.quickjs:quickjs-android", version = "0.9.2" }
[bundles] [bundles]

View File

@ -1,7 +1,7 @@
ext { ext {
extName = 'Mangamo' extName = 'Mangamo'
extClass = '.Mangamo' extClass = '.Mangamo'
extVersionCode = 1 extVersionCode = 2
isNsfw = false isNsfw = false
} }

View File

@ -9,7 +9,6 @@ import eu.kanade.tachiyomi.network.POST
import okhttp3.Headers import okhttp3.Headers
import okhttp3.OkHttpClient import okhttp3.OkHttpClient
import okhttp3.RequestBody.Companion.toRequestBody import okhttp3.RequestBody.Companion.toRequestBody
import okhttp3.internal.EMPTY_HEADERS
class MangamoAuth( class MangamoAuth(
private val helper: MangamoHelper, private val helper: MangamoHelper,
@ -53,8 +52,7 @@ class MangamoAuth(
val googleIdentityResponse = client.newCall( val googleIdentityResponse = client.newCall(
POST( POST(
"https://identitytoolkit.googleapis.com/v1/accounts:signInWithCustomToken?key=${MangamoConstants.FIREBASE_API_KEY}", "https://identitytoolkit.googleapis.com/v1/accounts:signInWithCustomToken?key=${MangamoConstants.FIREBASE_API_KEY}",
EMPTY_HEADERS, body = "{\"token\":\"$customToken\",\"returnSecureToken\":true}".toRequestBody(),
"{\"token\":\"$customToken\",\"returnSecureToken\":true}".toRequestBody(),
), ),
).execute() ).execute()
@ -97,8 +95,7 @@ class MangamoAuth(
val googleIdentityResponse = client.newCall( val googleIdentityResponse = client.newCall(
POST( POST(
"https://identitytoolkit.googleapis.com/v1/accounts:signUp?key=${MangamoConstants.FIREBASE_API_KEY}", "https://identitytoolkit.googleapis.com/v1/accounts:signUp?key=${MangamoConstants.FIREBASE_API_KEY}",
EMPTY_HEADERS, body = "{\"returnSecureToken\":true}".toRequestBody(),
"{\"returnSecureToken\":true}".toRequestBody(),
), ),
).execute() ).execute()

View File

@ -32,8 +32,6 @@ import okhttp3.MediaType.Companion.toMediaType
import okhttp3.Request import okhttp3.Request
import okhttp3.RequestBody.Companion.toRequestBody import okhttp3.RequestBody.Companion.toRequestBody
import okhttp3.Response import okhttp3.Response
import okhttp3.internal.http.HTTP_FORBIDDEN
import okhttp3.internal.http.HTTP_UNAUTHORIZED
import org.jsoup.nodes.Document import org.jsoup.nodes.Document
import org.jsoup.nodes.Element import org.jsoup.nodes.Element
import org.jsoup.select.Elements import org.jsoup.select.Elements
@ -41,6 +39,8 @@ import rx.Observable
import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get import uy.kohesive.injekt.api.get
import uy.kohesive.injekt.injectLazy import uy.kohesive.injekt.injectLazy
import java.net.HttpURLConnection.HTTP_FORBIDDEN
import java.net.HttpURLConnection.HTTP_UNAUTHORIZED
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
import java.util.Locale import java.util.Locale
@ -85,8 +85,7 @@ class Taiyo : ParsedHttpSource() {
// =============================== Search =============================== // =============================== Search ===============================
override fun fetchSearchManga(page: Int, query: String, filters: FilterList): Observable<MangasPage> { override fun fetchSearchManga(page: Int, query: String, filters: FilterList): Observable<MangasPage> = if (query.startsWith(PREFIX_SEARCH)) { // URL intent handler
return if (query.startsWith(PREFIX_SEARCH)) { // URL intent handler
val id = query.removePrefix(PREFIX_SEARCH) val id = query.removePrefix(PREFIX_SEARCH)
client.newCall(GET("$baseUrl/media/$id")) client.newCall(GET("$baseUrl/media/$id"))
.asObservableSuccess() .asObservableSuccess()
@ -94,7 +93,6 @@ class Taiyo : ParsedHttpSource() {
} else { } else {
super.fetchSearchManga(page, query, filters) super.fetchSearchManga(page, query, filters)
} }
}
private fun searchMangaByIdParse(response: Response): MangasPage { private fun searchMangaByIdParse(response: Response): MangasPage {
val details = mangaDetailsParse(response.asJsoup()) val details = mangaDetailsParse(response.asJsoup())
@ -146,17 +144,11 @@ class Taiyo : ParsedHttpSource() {
return MangasPage(mangas, mangas.isNotEmpty()) return MangasPage(mangas, mangas.isNotEmpty())
} }
override fun searchMangaSelector(): String { override fun searchMangaSelector(): String = throw UnsupportedOperationException()
throw UnsupportedOperationException()
}
override fun searchMangaFromElement(element: Element): SManga { override fun searchMangaFromElement(element: Element): SManga = throw UnsupportedOperationException()
throw UnsupportedOperationException()
}
override fun searchMangaNextPageSelector(): String? { override fun searchMangaNextPageSelector(): String? = throw UnsupportedOperationException()
throw UnsupportedOperationException()
}
// =========================== Manga Details ============================ // =========================== Manga Details ============================
override fun mangaDetailsParse(document: Document) = SManga.create().apply { override fun mangaDetailsParse(document: Document) = SManga.create().apply {
@ -235,13 +227,9 @@ class Taiyo : ParsedHttpSource() {
return Observable.just(chapters.sortedByDescending { it.chapter_number }) return Observable.just(chapters.sortedByDescending { it.chapter_number })
} }
override fun chapterListSelector(): String { override fun chapterListSelector(): String = throw UnsupportedOperationException()
throw UnsupportedOperationException()
}
override fun chapterFromElement(element: Element): SChapter { override fun chapterFromElement(element: Element): SChapter = throw UnsupportedOperationException()
throw UnsupportedOperationException()
}
// =============================== Pages ================================ // =============================== Pages ================================
override fun pageListParse(document: Document): List<Page> { override fun pageListParse(document: Document): List<Page> {
@ -256,9 +244,7 @@ class Taiyo : ParsedHttpSource() {
} }
} }
override fun imageUrlParse(document: Document): String { override fun imageUrlParse(document: Document): String = throw UnsupportedOperationException()
throw UnsupportedOperationException()
}
// ============================= Utilities ============================== // ============================= Utilities ==============================
private fun Element.getImageUrl() = absUrl("srcset").substringBefore(" ") private fun Element.getImageUrl() = absUrl("srcset").substringBefore(" ")
@ -267,23 +253,19 @@ class Taiyo : ParsedHttpSource() {
json.decodeFromStream(it.body.byteStream()) json.decodeFromStream(it.body.byteStream())
} }
private fun String.toDate(): Long { private fun String.toDate(): Long = runCatching { DATE_FORMATTER.parse(this)?.time }
return runCatching { DATE_FORMATTER.parse(this)?.time }
.getOrNull() ?: 0L .getOrNull() ?: 0L
}
private inline fun <reified T> Document.parseJsonFromDocument( private inline fun <reified T> Document.parseJsonFromDocument(
itemName: String = "media", itemName: String = "media",
crossinline transformer: String.() -> String, crossinline transformer: String.() -> String,
): T? { ): T? = runCatching {
return runCatching {
val script = selectFirst("script:containsData($itemName\\\\\":):containsData(\\\"6:\\[)")!!.data() val script = selectFirst("script:containsData($itemName\\\\\":):containsData(\\\"6:\\[)")!!.data()
val obj = script.substringAfter(",{\\\"$itemName\\\":") val obj = script.substringAfter(",{\\\"$itemName\\\":")
.run(transformer) .run(transformer)
.replace("\\", "") .replace("\\", "")
json.decodeFromString<T>(obj) json.decodeFromString<T>(obj)
}.onFailure { it.printStackTrace() }.getOrNull() }.onFailure { it.printStackTrace() }.getOrNull()
}
// ============================= Authorization ======================== // ============================= Authorization ========================
@ -304,13 +286,11 @@ class Taiyo : ParsedHttpSource() {
return chain.proceed(req) return chain.proceed(req)
} }
private fun getToken(): String { private fun getToken(): String = fetchBearerToken().also {
return fetchBearerToken().also {
preferences.edit() preferences.edit()
.putString(BEARER_TOKEN_PREF, it) .putString(BEARER_TOKEN_PREF, it)
.apply() .apply()
} }
}
private fun fetchBearerToken(): String { private fun fetchBearerToken(): String {
val scripts = client.newCall(GET(baseUrl, headers)) val scripts = client.newCall(GET(baseUrl, headers))