Skip to content

Commit

Permalink
Revert "Replace custom image loader with image loader dependency (#99)"
Browse files Browse the repository at this point in the history
This reverts commit d3e2ccb.

While image-loader library is great, it was causing OOM when doing dynamic theming. For some reason I also noticed lot of "blinks" when switching from one image to another in featured image section. So, reverting back to my custom image loader
  • Loading branch information
msasikanth committed Sep 16, 2023
1 parent 12fc0c0 commit 96ec0a3
Show file tree
Hide file tree
Showing 13 changed files with 381 additions and 105 deletions.
4 changes: 2 additions & 2 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ androidx_test_rules = "1.5.0"
androidx_work = "2.8.1"
androidx_datastore = "1.1.0-alpha05"
androidx_browser = "1.6.0"
coil = "2.4.0"
spotless = "6.21.0"
ktfmt = "0.44"
kotlininject = "0.6.3"
Expand All @@ -41,7 +42,6 @@ lyricist = "1.4.2"
atomicfu = "0.22.0"
okio = "3.5.0"
paging = "3.2.0-alpha05-0.2.3"
imageloader = "1.6.7"

[libraries]
compose_runtime = { module = "org.jetbrains.compose.runtime:runtime", version.ref = "compose" }
Expand Down Expand Up @@ -80,6 +80,7 @@ androidx_work = { module = "androidx.work:work-runtime-ktx", version.ref = "andr
androidx_datastore_okio = { module = "androidx.datastore:datastore-core-okio", version.ref = "androidx_datastore" }
androidx_datastore_preferences = { module = "androidx.datastore:datastore-preferences-core", version.ref = "androidx_datastore" }
androidx_browser = { module = "androidx.browser:browser", version.ref = "androidx_browser" }
coil_compose = { module = "io.coil-kt:coil-compose", version.ref = "coil" }
kotlininject-compiler = { module = 'me.tatarka.inject:kotlin-inject-compiler-ksp', version.ref = 'kotlininject' }
kotlininject-runtime = { module = 'me.tatarka.inject:kotlin-inject-runtime', version.ref = 'kotlininject' }
material_color_utilities = { module = "dev.sasikanth:material-color-utilities", version.ref = "material_color_utilities" }
Expand All @@ -93,7 +94,6 @@ lyricist-processor = { module = "cafe.adriel.lyricist:lyricist-processor", versi
okio = { module = "com.squareup.okio:okio", version.ref = "okio" }
paging-common = { module = "app.cash.paging:paging-common", version.ref = "paging" }
paging-compose = { module = "app.cash.paging:paging-compose-common", version.ref = "paging" }
imageloader = { module = "io.github.qdsfdhvh:image-loader", version.ref = "imageloader" }

[plugins]
android_application = { id = "com.android.application", version.ref = "android_gradle_plugin" }
Expand Down
2 changes: 1 addition & 1 deletion shared/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,6 @@ kotlin {
api(libs.okio)
implementation(libs.paging.common)
implementation(libs.paging.compose)
api(libs.imageloader)
}
}
val commonTest by getting {
Expand All @@ -126,6 +125,7 @@ kotlin {
api(libs.androidx.core)
implementation(libs.ktor.client.okhttp)
implementation(libs.sqldelight.driver.android)
implementation(libs.coil.compose)
api(libs.sqliteAndroid)
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/*
* Copyright 2023 Sasikanth Miriyampalli
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package dev.sasikanth.rss.reader.components

import androidx.compose.foundation.layout.Box
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.layout.ContentScale

@Composable
actual fun AsyncImage(
url: String,
contentDescription: String?,
contentScale: ContentScale,
modifier: Modifier,
) {
Box(modifier) {
coil.compose.AsyncImage(
modifier = Modifier.matchParentSize(),
model = url,
contentDescription = contentDescription,
contentScale = ContentScale.Crop
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/*
* Copyright 2023 Sasikanth Miriyampalli
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package dev.sasikanth.rss.reader.components

import android.content.Context
import androidx.compose.ui.graphics.ImageBitmap
import androidx.compose.ui.graphics.asImageBitmap
import androidx.core.graphics.drawable.toBitmap
import coil.imageLoader
import coil.request.ImageRequest
import coil.request.SuccessResult
import coil.size.Scale
import dev.sasikanth.rss.reader.di.scopes.AppScope
import me.tatarka.inject.annotations.Inject

@Inject
@AppScope
class AndroidImageLoader(private val context: Context) : ImageLoader {

override suspend fun getImage(url: String, size: Int?): ImageBitmap? {
val requestBuilder =
ImageRequest.Builder(context)
.data(url)
.scale(Scale.FILL)
.allowHardware(false)
.memoryCacheKey("$url.dynamic_colors")

if (size != null) {
requestBuilder.size(size)
}

return when (val result = context.imageLoader.execute(requestBuilder.build())) {
is SuccessResult -> result.drawable.toBitmap().asImageBitmap()
else -> null
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,30 +15,11 @@
*/
package dev.sasikanth.rss.reader.di

import android.content.Context
import com.seiko.imageloader.ImageLoader
import com.seiko.imageloader.component.setupDefaultComponents
import com.seiko.imageloader.defaultImageResultMemoryCache
import com.seiko.imageloader.option.androidContext
import dev.sasikanth.rss.reader.di.scopes.AppScope
import dev.sasikanth.rss.reader.components.AndroidImageLoader
import dev.sasikanth.rss.reader.components.ImageLoader
import me.tatarka.inject.annotations.Provides
import okio.Path.Companion.toOkioPath

actual interface ImageLoaderComponent {

@Provides
@AppScope
fun providesImageLoader(context: Context): ImageLoader {
return ImageLoader {
options { androidContext(context) }
components { setupDefaultComponents() }
interceptor {
defaultImageResultMemoryCache()
diskCacheConfig {
directory(context.cacheDir.resolve("image_cache").toOkioPath())
maxSizeBytes(512L * 1024 * 1024) // 512MB
}
}
}
}
@Provides fun AndroidImageLoader.bind(): ImageLoader = this
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,12 @@ import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import com.arkivanov.decompose.extensions.compose.jetbrains.stack.Children
import com.arkivanov.decompose.extensions.compose.jetbrains.stack.animation.StackAnimation
import com.arkivanov.decompose.extensions.compose.jetbrains.stack.animation.plus
import com.arkivanov.essenty.backhandler.BackHandler
import com.seiko.imageloader.ImageLoader
import com.seiko.imageloader.LocalImageLoader
import dev.sasikanth.rss.reader.bookmarks.ui.BookmarksScreen
import dev.sasikanth.rss.reader.components.DynamicContentTheme
import dev.sasikanth.rss.reader.components.ImageLoader
import dev.sasikanth.rss.reader.components.LocalImageLoader
import dev.sasikanth.rss.reader.components.rememberDynamicColorState
import dev.sasikanth.rss.reader.home.ui.HomeScreen
import dev.sasikanth.rss.reader.repository.BrowserType
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,39 +15,14 @@
*/
package dev.sasikanth.rss.reader.components

import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.Box
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Size
import androidx.compose.ui.layout.ContentScale
import com.seiko.imageloader.model.ImageRequest
import com.seiko.imageloader.option.SizeResolver
import com.seiko.imageloader.rememberImagePainter

@Composable
fun AsyncImage(
expect fun AsyncImage(
url: String,
contentDescription: String?,
contentScale: ContentScale = ContentScale.Crop,
contentScale: ContentScale = ContentScale.Fit,
modifier: Modifier = Modifier,
size: Size = Size(1024f, 1024f)
) {
val request =
remember(url) {
ImageRequest {
data(url)
size(SizeResolver(size))
}
}
val painter = rememberImagePainter(request)
Box(modifier) {
Image(
modifier = Modifier.matchParentSize(),
painter = painter,
contentDescription = contentDescription,
contentScale = contentScale,
)
}
}
)
Original file line number Diff line number Diff line change
Expand Up @@ -26,15 +26,8 @@ import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.geometry.Size
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.ImageBitmap
import com.seiko.imageloader.ImageLoader
import com.seiko.imageloader.LocalImageLoader
import com.seiko.imageloader.asImageBitmap
import com.seiko.imageloader.model.ImageRequest
import com.seiko.imageloader.model.ImageResult
import com.seiko.imageloader.option.SizeResolver
import dev.sasikanth.material.color.utilities.dynamiccolor.DynamicColor
import dev.sasikanth.material.color.utilities.dynamiccolor.MaterialDynamicColors
import dev.sasikanth.material.color.utilities.dynamiccolor.ToneDeltaConstraint
Expand All @@ -46,8 +39,6 @@ import dev.sasikanth.material.color.utilities.scheme.SchemeContent
import dev.sasikanth.material.color.utilities.score.Score
import dev.sasikanth.rss.reader.ui.AppTheme
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.filterIsInstance
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.withContext

private const val TINTED_BACKGROUND = "tinted_background"
Expand Down Expand Up @@ -288,15 +279,9 @@ class DynamicColorState(
return cached
}

val imageRequest = ImageRequest {
data(url)
size(SizeResolver(Size(128f, 128f)))
}
val imageResult =
imageLoader?.async(imageRequest)?.filterIsInstance<ImageResult.Bitmap>()?.first()

return if (imageResult != null) {
extractColorsFromImage(imageResult.bitmap.asImageBitmap())
val image = imageLoader?.getImage(url, size = 128)
return if (image != null) {
extractColorsFromImage(image)
.let { colorsMap ->
return@let if (colorsMap.isNotEmpty()) {
DynamicColors(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/*
* Copyright 2023 Sasikanth Miriyampalli
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package dev.sasikanth.rss.reader.components

import androidx.compose.runtime.staticCompositionLocalOf
import androidx.compose.ui.graphics.ImageBitmap

interface ImageLoader {
suspend fun getImage(url: String, size: Int?): ImageBitmap?
}

val LocalImageLoader = staticCompositionLocalOf<ImageLoader?> { null }
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
*/
package dev.sasikanth.rss.reader.di

import dev.sasikanth.rss.reader.components.ImageLoader
import dev.sasikanth.rss.reader.di.scopes.AppScope
import dev.sasikanth.rss.reader.initializers.Initializer
import dev.sasikanth.rss.reader.network.NetworkComponent
Expand All @@ -26,6 +27,8 @@ import me.tatarka.inject.annotations.Provides
abstract class SharedApplicationComponent :
DataComponent, ImageLoaderComponent, SentryComponent, NetworkComponent {

abstract val imageLoader: ImageLoader

abstract val initializers: Set<Initializer>

@Provides @AppScope fun DefaultDispatchersProvider.bind(): DispatchersProvider = this
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/*
* Copyright 2023 Sasikanth Miriyampalli
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package dev.sasikanth.rss.reader.components

import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.Box
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.layout.ContentScale
import io.ktor.client.request.get
import org.jetbrains.skia.Image

@Composable
actual fun AsyncImage(
url: String,
contentDescription: String?,
contentScale: ContentScale,
modifier: Modifier,
) {
Box(modifier) {
val imageState by rememberImageLoaderState(url)

when (imageState) {
is ImageLoaderState.Loaded -> {
Image(
modifier = Modifier.matchParentSize(),
bitmap = (imageState as ImageLoaderState.Loaded).image,
contentDescription = contentDescription,
contentScale = contentScale
)
}
else -> {
// TODO: Handle other cases instead of just showing blank space?
}
}
}
}
Loading

0 comments on commit 96ec0a3

Please sign in to comment.