diff --git a/.github/workflows/Check.yml b/.github/workflows/Check.yml index c866b49e..123d555a 100644 --- a/.github/workflows/Check.yml +++ b/.github/workflows/Check.yml @@ -24,7 +24,7 @@ jobs: uses: actions/checkout@v4 - name: Validate Gradle Wrapper - uses: gradle/wrapper-validation-action@v1 + uses: gradle/wrapper-validation-action@v2 - name: Copy CI gradle.properties run: mkdir -p ~/.gradle ; cp .github/ci-gradle.properties ~/.gradle/gradle.properties @@ -46,7 +46,7 @@ jobs: uses: actions/checkout@v4 - name: Validate Gradle Wrapper - uses: gradle/wrapper-validation-action@v1 + uses: gradle/wrapper-validation-action@v2 - name: Copy CI gradle.properties run: mkdir -p ~/.gradle ; cp .github/ci-gradle.properties ~/.gradle/gradle.properties @@ -68,7 +68,7 @@ jobs: uses: actions/checkout@v4 - name: Validate Gradle Wrapper - uses: gradle/wrapper-validation-action@v1 + uses: gradle/wrapper-validation-action@v2 - name: Copy CI gradle.properties run: mkdir -p ~/.gradle ; cp .github/ci-gradle.properties ~/.gradle/gradle.properties @@ -96,7 +96,7 @@ jobs: uses: actions/checkout@v4 - name: Validate Gradle Wrapper - uses: gradle/wrapper-validation-action@v1 + uses: gradle/wrapper-validation-action@v2 - name: Copy CI gradle.properties run: mkdir -p ~/.gradle ; cp .github/ci-gradle.properties ~/.gradle/gradle.properties @@ -135,7 +135,7 @@ jobs: uses: actions/checkout@v4 - name: Validate Gradle Wrapper - uses: gradle/wrapper-validation-action@v1 + uses: gradle/wrapper-validation-action@v2 - name: Copy CI gradle.properties run: mkdir -p ~/.gradle ; cp .github/ci-gradle.properties ~/.gradle/gradle.properties diff --git a/.github/workflows/CompareScreenshot.yml b/.github/workflows/CompareScreenshot.yml index e9b6dc45..06e0ab7a 100644 --- a/.github/workflows/CompareScreenshot.yml +++ b/.github/workflows/CompareScreenshot.yml @@ -30,7 +30,7 @@ jobs: java-version: 17 - name: Setup Gradle - uses: gradle/gradle-build-action@982da8e78c05368c70dac0351bb82647a9e9a5d2 # v2.11.1 + uses: gradle/gradle-build-action@3b1b3b9a2104c2b47fbae53f3938079c00c9bb87 # v3.0.0 with: gradle-version: wrapper diff --git a/.github/workflows/CompareScreenshotComment.yml b/.github/workflows/CompareScreenshotComment.yml index e0b073ca..1a80988d 100644 --- a/.github/workflows/CompareScreenshotComment.yml +++ b/.github/workflows/CompareScreenshotComment.yml @@ -114,7 +114,7 @@ jobs: done echo "${delimiter}" >> "$GITHUB_OUTPUT" - name: Find Comment - uses: peter-evans/find-comment@v2 + uses: peter-evans/find-comment@v3 id: fc if: steps.generate-diff-reports.outputs.reports != '' with: @@ -123,7 +123,7 @@ jobs: body-includes: Snapshot diff report - name: Add or update comment on PR - uses: peter-evans/create-or-update-comment@v3 + uses: peter-evans/create-or-update-comment@v4 if: steps.generate-diff-reports.outputs.reports != '' with: comment-id: ${{ steps.fc.outputs.comment-id }} diff --git a/.github/workflows/DocsDeploy.yml b/.github/workflows/DocsDeploy.yml index 86a96b16..b293388d 100644 --- a/.github/workflows/DocsDeploy.yml +++ b/.github/workflows/DocsDeploy.yml @@ -13,7 +13,7 @@ jobs: - uses: actions/checkout@v4 - name: Validate Gradle Wrapper - uses: gradle/wrapper-validation-action@v1 + uses: gradle/wrapper-validation-action@v2 - name: Copy CI gradle.properties run: mkdir -p ~/.gradle ; cp .github/ci-gradle.properties ~/.gradle/gradle.properties @@ -25,7 +25,7 @@ jobs: java-version: 17 - name: Setup Gradle - uses: gradle/gradle-build-action@v2 + uses: gradle/gradle-build-action@v3 with: gradle-home-cache-cleanup: true cache-read-only: ${{ github.ref != 'refs/heads/main' }} diff --git a/.github/workflows/DocsTest.yml b/.github/workflows/DocsTest.yml index 8a2f518a..61710032 100644 --- a/.github/workflows/DocsTest.yml +++ b/.github/workflows/DocsTest.yml @@ -13,7 +13,7 @@ jobs: - uses: actions/checkout@v4 - name: Validate Gradle Wrapper - uses: gradle/wrapper-validation-action@v1 + uses: gradle/wrapper-validation-action@v2 - name: Copy CI gradle.properties run: mkdir -p ~/.gradle ; cp .github/ci-gradle.properties ~/.gradle/gradle.properties @@ -25,7 +25,7 @@ jobs: java-version: 17 - name: Setup Gradle - uses: gradle/gradle-build-action@v2 + uses: gradle/gradle-build-action@v3 with: gradle-home-cache-cleanup: true cache-read-only: ${{ github.ref != 'refs/heads/main' }} diff --git a/.github/workflows/Publish.yml b/.github/workflows/Publish.yml index a07600a5..0f2e27ca 100644 --- a/.github/workflows/Publish.yml +++ b/.github/workflows/Publish.yml @@ -19,7 +19,7 @@ jobs: uses: actions/checkout@v4 - name: Validate Gradle Wrapper - uses: gradle/wrapper-validation-action@v1 + uses: gradle/wrapper-validation-action@v2 - name: Set up JDK 17 uses: actions/setup-java@v3 @@ -28,7 +28,7 @@ jobs: java-version: 17 - name: Setup Gradle - uses: gradle/gradle-build-action@v2 + uses: gradle/gradle-build-action@v3 - name: Publish to MavenCentral run: ./gradlew publishAllPublicationsToMavenCentral --no-build-cache diff --git a/.github/workflows/StoreScreenshot.yml b/.github/workflows/StoreScreenshot.yml index db8fa9f0..0b22a5cd 100644 --- a/.github/workflows/StoreScreenshot.yml +++ b/.github/workflows/StoreScreenshot.yml @@ -33,7 +33,7 @@ jobs: # Better than caching and/or extensions of actions/setup-java - name: Setup Gradle - uses: gradle/gradle-build-action@982da8e78c05368c70dac0351bb82647a9e9a5d2 # v2.11.1 + uses: gradle/gradle-build-action@3b1b3b9a2104c2b47fbae53f3938079c00c9bb87 # v3.0.0 with: gradle-version: wrapper diff --git a/README.md b/README.md index 2e973098..b2a4ff5c 100644 --- a/README.md +++ b/README.md @@ -17,17 +17,17 @@ kotlin { sourceSets { val commonMain by getting { dependencies { -+ api("io.github.qdsfdhvh:image-loader:1.7.3") ++ api("io.github.qdsfdhvh:image-loader:1.7.4") // optional - Moko Resources Decoder -+ api("io.github.qdsfdhvh:image-loader-extension-moko-resources:1.7.3") ++ api("io.github.qdsfdhvh:image-loader-extension-moko-resources:1.7.4") // optional - Blur Interceptor (only support bitmap) -+ api("io.github.qdsfdhvh:image-loader-extension-blur:1.7.3") ++ api("io.github.qdsfdhvh:image-loader-extension-blur:1.7.4") } } val jvmMain by getting { dependencies { // optional - ImageIO Decoder -+ api("io.github.qdsfdhvh:image-loader-extension-imageio:1.7.3") ++ api("io.github.qdsfdhvh:image-loader-extension-imageio:1.7.4") } } } diff --git a/app/common/build.gradle.kts b/app/common/build.gradle.kts index bf574850..935c044c 100644 --- a/app/common/build.gradle.kts +++ b/app/common/build.gradle.kts @@ -26,17 +26,19 @@ kotlin { implementation(libs.kotlinx.serialization.json) implementation(libs.ktor.client.logging) + implementation(libs.ktor.client.content.negotiation) + implementation(libs.ktor.serialization.kotlinx.json) implementation(libs.kermit) } } androidMain { dependencies { - implementation(libs.ktor.client.cio) + implementation(libs.ktor.client.okhttp) } } desktopMain { dependencies { - implementation(libs.ktor.client.cio) + implementation(libs.ktor.client.okhttp) } } appleMain { diff --git a/app/common/src/androidMain/kotlin/com/seiko/imageloader/demo/util/HttpEngine.kt b/app/common/src/androidMain/kotlin/com/seiko/imageloader/demo/util/HttpEngine.kt index c87c6da2..3abbd1e2 100644 --- a/app/common/src/androidMain/kotlin/com/seiko/imageloader/demo/util/HttpEngine.kt +++ b/app/common/src/androidMain/kotlin/com/seiko/imageloader/demo/util/HttpEngine.kt @@ -1,7 +1,7 @@ package com.seiko.imageloader.demo.util import io.ktor.client.engine.HttpClientEngine -import io.ktor.client.engine.cio.CIO +import io.ktor.client.engine.okhttp.OkHttp actual val httpEngine: HttpClientEngine - get() = CIO.create() + get() = OkHttp.create() diff --git a/app/common/src/commonMain/kotlin/com/seiko/imageloader/demo/App.kt b/app/common/src/commonMain/kotlin/com/seiko/imageloader/demo/App.kt index 7bf64397..7ec5e7dc 100644 --- a/app/common/src/commonMain/kotlin/com/seiko/imageloader/demo/App.kt +++ b/app/common/src/commonMain/kotlin/com/seiko/imageloader/demo/App.kt @@ -27,6 +27,7 @@ import com.seiko.imageloader.demo.scene.NetworkImagesScene import com.seiko.imageloader.demo.scene.OtherImagesScene import com.seiko.imageloader.demo.scene.PokemonScene import com.seiko.imageloader.demo.scene.SvgImagesScene +import com.seiko.imageloader.demo.scene.WanAndroidScene @Composable fun App(modifier: Modifier = Modifier) { @@ -48,6 +49,7 @@ fun App(modifier: Modifier = Modifier) { Route.LocalResource -> LocalResourceScene(::onBack) Route.Other -> OtherImagesScene(::onBack) Route.FilePicker -> FilePickerScene(::onBack) + Route.WanAndroid -> WanAndroidScene(::onBack) } } } @@ -94,4 +96,5 @@ private enum class Route { LocalResource, Other, FilePicker, + WanAndroid, } diff --git a/app/common/src/commonMain/kotlin/com/seiko/imageloader/demo/scene/WanAndroidScene.kt b/app/common/src/commonMain/kotlin/com/seiko/imageloader/demo/scene/WanAndroidScene.kt new file mode 100644 index 00000000..345c294e --- /dev/null +++ b/app/common/src/commonMain/kotlin/com/seiko/imageloader/demo/scene/WanAndroidScene.kt @@ -0,0 +1,156 @@ +package com.seiko.imageloader.demo.scene + +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.widthIn +import androidx.compose.foundation.lazy.grid.GridCells +import androidx.compose.foundation.lazy.grid.LazyVerticalGrid +import androidx.compose.material.ScrollableTabRow +import androidx.compose.material.Tab +import androidx.compose.material.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableIntStateOf +import androidx.compose.runtime.produceState +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp +import com.seiko.imageloader.demo.MR +import com.seiko.imageloader.demo.util.JSON +import com.seiko.imageloader.demo.util.httpEngine +import com.seiko.imageloader.ui.AutoSizeImage +import io.ktor.client.HttpClient +import io.ktor.client.call.body +import io.ktor.client.plugins.contentnegotiation.ContentNegotiation +import io.ktor.client.plugins.defaultRequest +import io.ktor.client.request.get +import io.ktor.client.statement.HttpResponse +import io.ktor.serialization.kotlinx.json.json +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable +import org.jetbrains.compose.resources.ExperimentalResourceApi +import org.jetbrains.compose.resources.painterResource + +@OptIn(ExperimentalResourceApi::class) +@Composable +fun WanAndroidScene( + onBack: () -> Unit, +) { + BackScene( + onBack = onBack, + title = { Text("WanAndroid") }, + ) { innerPadding -> + val client by remember { + lazy { + HttpClient(httpEngine) { + install(ContentNegotiation) { + json(JSON) + } + defaultRequest { + url("https://www.wanandroid.com/") + } + } + } + } + Column(Modifier.padding(innerPadding).fillMaxSize()) { + val tabs by produceState(emptyList()) { + value = runCatching { + client.get("project/tree/json") + .wanBody>() + .take(5) + }.getOrElse { + it.printStackTrace() + emptyList() + } + } + if (tabs.isEmpty()) return@Column + + var selectedTabIndex by remember { mutableIntStateOf(0) } + ScrollableTabRow( + selectedTabIndex = selectedTabIndex, + modifier = Modifier.fillMaxWidth(), + tabs = { + tabs.forEachIndexed { index, tag -> + Tab( + selected = index == selectedTabIndex, + onClick = { selectedTabIndex = index }, + modifier = Modifier.widthIn(min = 100.dp), + ) { + Text(tag.name) + } + } + }, + ) + + val projects by produceState(emptyList(), key1 = tabs, key2 = selectedTabIndex) { + value = runCatching { + client.get("project/list/1/json?cid=${tabs[selectedTabIndex].id}") + .wanBody().projects + }.getOrElse { + it.printStackTrace() + emptyList() + } + } + + LazyVerticalGrid( + GridCells.Fixed(3), + Modifier.weight(1f).fillMaxWidth(), + ) { + items( + projects.size, + key = { projects[it].id }, + ) { + Column { + AutoSizeImage( + projects[it].envelopePic, + contentDescription = projects[it].title, + modifier = Modifier.size(200.dp), + placeholderPainter = { painterResource(MR.images.cat) }, + errorPainter = { painterResource(MR.images.cat) }, + ) + Text(projects[it].title) + } + } + } + } + } +} + +private suspend inline fun HttpResponse.wanBody(): T { + val response = body>() + if (response.errorCode == 0) { + return response.data + } else { + error(response.errorMsg) + } +} + +@Serializable +private data class WanResponse( + val data: T, + val errorCode: Int, + val errorMsg: String, +) + +@Serializable +private data class WanProjectTabs( + val id: Int, + val name: String, +) + +@Serializable +private data class WanProjectPage( + val curPage: Int, + @SerialName("datas") val projects: List, +) + +@Serializable +private data class WanProject( + val id: Int, + val title: String, + val envelopePic: String, +) diff --git a/app/common/src/desktopMain/kotlin/com/seiko/imageloader/demo/util/HttpEngine.kt b/app/common/src/desktopMain/kotlin/com/seiko/imageloader/demo/util/HttpEngine.kt index c87c6da2..3abbd1e2 100644 --- a/app/common/src/desktopMain/kotlin/com/seiko/imageloader/demo/util/HttpEngine.kt +++ b/app/common/src/desktopMain/kotlin/com/seiko/imageloader/demo/util/HttpEngine.kt @@ -1,7 +1,7 @@ package com.seiko.imageloader.demo.util import io.ktor.client.engine.HttpClientEngine -import io.ktor.client.engine.cio.CIO +import io.ktor.client.engine.okhttp.OkHttp actual val httpEngine: HttpClientEngine - get() = CIO.create() + get() = OkHttp.create() diff --git a/app/intellij-plugin/build.gradle.kts b/app/intellij-plugin/build.gradle.kts index 5ba03242..e6187884 100644 --- a/app/intellij-plugin/build.gradle.kts +++ b/app/intellij-plugin/build.gradle.kts @@ -1,5 +1,5 @@ plugins { - id("org.jetbrains.intellij") version "1.16.1" + id("org.jetbrains.intellij") version "1.17.1" java id("app.kotlin.jvm") id("app.compose.multiplatform") diff --git a/build.gradle.kts b/build.gradle.kts index 67b27be0..1a952b26 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -91,7 +91,7 @@ object ProjectVersion { private const val monir = "7" // backwards compatible bug fixes - private const val path = "3" + private const val path = "4" const val version = "$major.$monir.$path" } diff --git a/docs/docs/setup.mdx b/docs/docs/setup.mdx index 8ce1fd27..3cfd7d38 100644 --- a/docs/docs/setup.mdx +++ b/docs/docs/setup.mdx @@ -15,17 +15,17 @@ kotlin { sourceSets { val commonMain by getting { dependencies { -+ api("io.github.qdsfdhvh:image-loader:1.7.3") ++ api("io.github.qdsfdhvh:image-loader:1.7.4") // optional - Moko Resources Decoder -+ api("io.github.qdsfdhvh:image-loader-extension-moko-resources:1.7.3") ++ api("io.github.qdsfdhvh:image-loader-extension-moko-resources:1.7.4") // optional - Blur Interceptor (only support bitmap) -+ api("io.github.qdsfdhvh:image-loader-extension-blur:1.7.3") ++ api("io.github.qdsfdhvh:image-loader-extension-blur:1.7.4") } } val jvmMain by getting { dependencies { // optional - ImageIO Decoder -+ api("io.github.qdsfdhvh:image-loader-extension-imageio:1.7.3") ++ api("io.github.qdsfdhvh:image-loader-extension-imageio:1.7.4") } } } @@ -38,7 +38,7 @@ Copy the following snippets if you are using [gradle version catalog](https://do ```xml title="libs.versions.toml" [versions] -image-loader = "1.7.3" +image-loader = "1.7.4" [libraries] image-loader = { module = "io.github.qdsfdhvh:image-loader", version.ref = "image-loader" } diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 7b9c1c34..be6ec332 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -5,27 +5,27 @@ kotlin = "1.9.21" kotlinx-coroutines = "1.8.0-RC2" kotlinx-serialization = "1.6.2" androidx-core-ktx = "1.12.0" -androidx-collection = "1.3.0" +androidx-collection = "1.4.0" androidx-appcompat = "1.6.1" androidx-exifinterface = "1.3.7" androidx-activity-compose = "1.8.2" -androidx-lifecycle-runtime-ktx = "2.6.2" -spotless = "6.23.3" +androidx-lifecycle-runtime-ktx = "2.7.0" +spotless = "6.25.0" ktlint = "0.50.0" publish = "0.27.0" dokka = "1.9.10" -ktor = "2.3.7" +ktor = "2.3.8" okio = "3.7.0" -uri-kmp = "0.0.15" -kermit = "2.0.2" +uri-kmp = "0.0.16" +kermit = "2.0.3" androidsvg = "1.4" -benchmark = "1.2.2" +benchmark = "1.2.3" junit = "4.13.2" androidx-test-junit = "1.1.5" androidx-test-espresso = "3.5.1" androidx-test-uiautomator = "2.2.0" profileinstaller = "1.3.1" -roborazzi = "1.8.0-rc-1" +roborazzi = "1.9.0" robolectric = "4.11.1" poko = "0.15.2" turbine = "1.0.0" @@ -81,8 +81,10 @@ roborazzi-compose-desktop = { module = "io.github.takahirom.roborazzi:roborazzi- okio = { module = "com.squareup.okio:okio", version.ref = "okio" } okio-fakefilesystem = { module = "com.squareup.okio:okio-fakefilesystem", version.ref = "okio" } +ktor-serialization-kotlinx-json = { module = "io.ktor:ktor-serialization-kotlinx-json", version.ref = "ktor" } ktor-client-core = { module = "io.ktor:ktor-client-core", version.ref = "ktor" } ktor-client-logging = { module = "io.ktor:ktor-client-logging", version.ref = "ktor" } +ktor-client-content-negotiation = { module = "io.ktor:ktor-client-content-negotiation", version.ref = "ktor" } ktor-client-okhttp = { module = "io.ktor:ktor-client-okhttp", version.ref = "ktor" } ktor-client-darwin = { module = "io.ktor:ktor-client-darwin", version.ref = "ktor" } ktor-client-cio = { module = "io.ktor:ktor-client-cio", version.ref = "ktor" } diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 1af9e093..a80b22ce 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/image-loader-singleton/src/commonMain/kotlin/com/seiko/imageloader/ui/AutoSizeImage.kt b/image-loader-singleton/src/commonMain/kotlin/com/seiko/imageloader/ui/AutoSizeImage.kt index c3adfec8..5df61336 100644 --- a/image-loader-singleton/src/commonMain/kotlin/com/seiko/imageloader/ui/AutoSizeImage.kt +++ b/image-loader-singleton/src/commonMain/kotlin/com/seiko/imageloader/ui/AutoSizeImage.kt @@ -177,7 +177,7 @@ private class AutoSizeImageNode( private var cachedSize: Size = Size.Unspecified - private var drawPainter: Painter? = null + private var drawPainter: Painter? = placeholderPainter private var drawPainterPositionAndSize: CachedPositionAndSize? = null private var hasFixedSize: Boolean = false @@ -202,11 +202,13 @@ private class AutoSizeImageNode( override fun onDetach() { super.onDetach() // if this node is reset from pool, not need to reset size - if (!isReset) { + if (isReset) { + updatePainter(placeholderPainter) + } else { hasFixedSize = false cachedSize = Size.Unspecified + updatePainter(null) } - updatePainter(null) } fun update( @@ -273,11 +275,10 @@ private class AutoSizeImageNode( drawPainterPositionAndSize = null - if (hasFixedSize) { - invalidateDraw() - } else { + if (!hasFixedSize) { invalidateMeasurement() } + invalidateDraw() } private fun checkPainterPlay() { diff --git a/settings.gradle.kts b/settings.gradle.kts index 95cf2ff2..c50f9aba 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -20,7 +20,7 @@ dependencyResolutionManagement { } plugins { - id("com.gradle.enterprise") version "3.16.1" + id("com.gradle.enterprise") version "3.16.2" } gradleEnterprise {