diff --git a/androidApp/build.gradle.kts b/androidApp/build.gradle.kts index 1d80406..e6475ce 100644 --- a/androidApp/build.gradle.kts +++ b/androidApp/build.gradle.kts @@ -19,7 +19,9 @@ kotlin { implementation(project(":shared")) implementation(libs.koin.android) implementation(libs.firebase.crashlytics) - implementation(libs.coil) + implementation(libs.accompanist.permissions) + implementation(libs.kermit) + } } } diff --git a/androidApp/src/androidMain/kotlin/com/kevinschildhorn/MainActivity.kt b/androidApp/src/androidMain/kotlin/com/kevinschildhorn/MainActivity.kt index 4e9ea81..2cd0d8e 100644 --- a/androidApp/src/androidMain/kotlin/com/kevinschildhorn/MainActivity.kt +++ b/androidApp/src/androidMain/kotlin/com/kevinschildhorn/MainActivity.kt @@ -1,16 +1,25 @@ package com.kevinschildhorn import MainView +import android.Manifest import android.os.Bundle import androidx.activity.compose.setContent +import androidx.activity.result.contract.ActivityResultContracts import androidx.appcompat.app.AppCompatActivity +import co.touchlab.kermit.Logger import coil3.ImageLoader import coil3.compose.setSingletonImageLoaderFactory -import com.kevinschildhorn.fotopresenter.data.datasources.image.CachedImageDataSource -import com.kevinschildhorn.fotopresenter.ui.SMBJFetcher -import com.kevinschildhorn.fotopresenter.data.network.NetworkHandler +import coil3.disk.DiskCache +import coil3.disk.directory +import coil3.memory.MemoryCache +import com.google.accompanist.permissions.ExperimentalPermissionsApi +import com.google.accompanist.permissions.rememberPermissionState +import com.kevinschildhorn.fotopresenter.baseLogger +import com.kevinschildhorn.fotopresenter.data.network.SMBJHandler import com.kevinschildhorn.fotopresenter.data.repositories.ImageRepository import com.kevinschildhorn.fotopresenter.startKoin +import com.kevinschildhorn.fotopresenter.ui.ByteArrayFetcher +import com.kevinschildhorn.fotopresenter.ui.SMBJFetcher import com.kevinschildhorn.fotopresenter.ui.screens.directory.DirectoryViewModel import com.kevinschildhorn.fotopresenter.ui.screens.login.LoginViewModel import com.kevinschildhorn.fotopresenter.ui.screens.playlist.PlaylistViewModel @@ -32,10 +41,23 @@ class MainActivity : AppCompatActivity(), KoinComponent { startKoin(this) setContent { + setSingletonImageLoaderFactory { context -> ImageLoader.Builder(context) .components { - add(SMBJFetcher.Factory(imageRepository)) + add(SMBJFetcher.Factory(imageRepository, baseLogger)) + add(ByteArrayFetcher.Factory(Logger.withTag("ByteArrayFetcher"))) + } + .memoryCache { + MemoryCache.Builder() + .maxSizePercent(context,0.25) + .build() + } + .diskCache { + DiskCache.Builder() + .directory(context.cacheDir.resolve("image_cache")) + .maxSizePercent(0.02) + .build() } .build() } diff --git a/desktopApp/src/jvmMain/kotlin/Main.kt b/desktopApp/src/jvmMain/kotlin/Main.kt index 44a8bfb..823b05c 100644 --- a/desktopApp/src/jvmMain/kotlin/Main.kt +++ b/desktopApp/src/jvmMain/kotlin/Main.kt @@ -1,14 +1,29 @@ +import KoinPurse.imageRepository import androidx.compose.ui.window.Window import androidx.compose.ui.window.application import co.touchlab.kermit.Logger +import coil3.ImageLoader import com.kevinschildhorn.fotopresenter.UseCaseFactory +import com.kevinschildhorn.fotopresenter.baseLogger +import com.kevinschildhorn.fotopresenter.ui.SMBJFetcher import com.kevinschildhorn.fotopresenter.ui.screens.directory.DirectoryViewModel import com.kevinschildhorn.fotopresenter.ui.screens.login.LoginViewModel import com.kevinschildhorn.fotopresenter.ui.screens.playlist.PlaylistViewModel import com.kevinschildhorn.fotopresenter.ui.screens.slideshow.SlideshowViewModel +import coil3.compose.setSingletonImageLoaderFactory +import com.kevinschildhorn.fotopresenter.data.datasources.image.CachedImageDataSource +import com.kevinschildhorn.fotopresenter.data.datasources.image.NetworkImageDataSource +import com.kevinschildhorn.fotopresenter.data.network.SMBJHandler +import com.kevinschildhorn.fotopresenter.data.repositories.ImageRepository +import com.kevinschildhorn.fotopresenter.ui.ByteArrayFetcher +import com.kevinschildhorn.fotopresenter.ui.shared.SharedFileCache object KoinPurse { + private val remoteImageDataSource: NetworkImageDataSource = NetworkImageDataSource(SMBJHandler) + private val localImageDataSource: CachedImageDataSource = + CachedImageDataSource(SharedFileCache("cache"), Logger.withTag("CachedImageDataSource")) + val loginViewModel = LoginViewModel(Logger.withTag("LoginViewModel"), UseCaseFactory.credentialsRepository) val directoryViewModel = @@ -16,6 +31,12 @@ object KoinPurse { val slideshowViewModel = SlideshowViewModel(Logger.withTag("SlideshowViewModel")) val playlistViewModel = PlaylistViewModel(UseCaseFactory.playlistRepository, Logger.withTag("PlaylistViewModel")) + val imageRepository = + ImageRepository( + remoteImageDataSource, + localImageDataSource, + Logger.withTag("ImageRepository") + ) } fun main() = application { @@ -23,6 +44,16 @@ fun main() = application { title = "FotoPresenter", onCloseRequest = ::exitApplication ) { + + setSingletonImageLoaderFactory { context -> + ImageLoader.Builder(context) + .components { + add(SMBJFetcher.Factory(imageRepository, Logger.withTag("SMBJFetcher"))) + add(ByteArrayFetcher.Factory(Logger.withTag("ByteArrayFetcher"))) + } + .build() + } + MainView( KoinPurse.loginViewModel, KoinPurse.directoryViewModel, diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 4380afa..d4ee932 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,4 +1,5 @@ [versions] +accompanistPermissions = "0.36.0" activity-compose = "1.9.2" agp = "8.5.2" appcompat = "1.7.0" @@ -7,6 +8,7 @@ cache4k = "0.12.0" coil = "3.0.0-rc01" core-ktx = "1.13.1" eva-icons = "1.1.0" +fileKache = "2.1.0" firebase-crashlytics = "19.2.0" kermit = "2.0.4" kermit-koin = "1.2.2" @@ -33,12 +35,13 @@ sqlite-driver = "2.0.1" turbine = "1.0.0" ## SDK Versions -minSdk = "26" +minSdk = "29" targetSdk = "34" compileSdk = "34" java = "21" [libraries] +accompanist-permissions = { module = "com.google.accompanist:accompanist-permissions", version.ref = "accompanistPermissions" } activity-compose = { module = "androidx.activity:activity-compose", version.ref = "activity-compose" } android-driver = { module = "app.cash.sqldelight:android-driver", version.ref = "sqlite-driver" } appcompat = { module = "androidx.appcompat:appcompat", version.ref = "appcompat" } @@ -46,6 +49,7 @@ atomik = { module = "io.github.kevinschildhorn:atomik", version.ref = "atomik" } cache4k = { module = "io.github.reactivecircus.cache4k:cache4k", version.ref = "cache4k" } core-ktx = { module = "androidx.core:core-ktx", version.ref = "core-ktx" } eva-icons = { module = "br.com.devsrsouza.compose.icons:eva-icons", version.ref = "eva-icons" } +file-kache = { module = "com.mayakapps.kache:file-kache", version.ref = "fileKache" } firebase-crashlytics = { module = "com.google.firebase:firebase-crashlytics", version.ref = "firebase-crashlytics" } kermit = { module = "co.touchlab:kermit", version.ref = "kermit" } kermit-koin = { module = "co.touchlab:kermit-koin", version.ref = "kermit-koin" } diff --git a/shared/build.gradle.kts b/shared/build.gradle.kts index b6280bc..c457cd9 100644 --- a/shared/build.gradle.kts +++ b/shared/build.gradle.kts @@ -53,7 +53,8 @@ kotlin { implementation(libs.multiplatform.settings) implementation(libs.kotlinx.datetime) implementation(libs.kim) - implementation(libs.coil) + api(libs.coil) + implementation(libs.file.kache) } } val commonTest by getting { diff --git a/shared/src/androidMain/kotlin/com/kevinschildhorn/fotopresenter/KoinAndroid.kt b/shared/src/androidMain/kotlin/com/kevinschildhorn/fotopresenter/KoinAndroid.kt index f79c403..181713f 100644 --- a/shared/src/androidMain/kotlin/com/kevinschildhorn/fotopresenter/KoinAndroid.kt +++ b/shared/src/androidMain/kotlin/com/kevinschildhorn/fotopresenter/KoinAndroid.kt @@ -8,7 +8,9 @@ import app.cash.sqldelight.db.SqlDriver import com.kevinschildhorn.fotopresenter.data.datasources.CredentialsDataSource import com.kevinschildhorn.fotopresenter.data.network.NetworkHandler import com.kevinschildhorn.fotopresenter.data.network.SMBJHandler +import com.kevinschildhorn.fotopresenter.ui.shared.CacheInterface import com.kevinschildhorn.fotopresenter.ui.shared.DriverFactory +import com.kevinschildhorn.fotopresenter.ui.shared.SharedFileCache import com.russhwolf.settings.Settings import com.russhwolf.settings.SharedPreferencesSettings import org.koin.core.KoinApplication @@ -47,6 +49,14 @@ internal actual val platformModule: Module = SMBJHandler } single { DriverFactory(context = get()).createDriver() } + single { + SMBJHandler + } + single { DriverFactory(context = get()).createDriver() } + single { + val context: Context = get() + SharedFileCache(context.cacheDir.path) + } } @OptIn(KoinInternalApi::class) diff --git a/shared/src/androidMain/kotlin/com/kevinschildhorn/fotopresenter/ui/shared/SharedImageAndroid.kt b/shared/src/androidMain/kotlin/com/kevinschildhorn/fotopresenter/ui/shared/SharedImageAndroid.kt index 17add46..c3fa9a6 100644 --- a/shared/src/androidMain/kotlin/com/kevinschildhorn/fotopresenter/ui/shared/SharedImageAndroid.kt +++ b/shared/src/androidMain/kotlin/com/kevinschildhorn/fotopresenter/ui/shared/SharedImageAndroid.kt @@ -6,9 +6,25 @@ import android.graphics.Bitmap import android.graphics.BitmapFactory import coil3.Image import coil3.asImage +import coil3.decode.DataSource +import coil3.fetch.FetchResult +import coil3.fetch.ImageFetchResult actual open class SharedImage actual constructor(actual val byteArray: ByteArray) { - actual fun getCoilImage(size: Int): Image? = getAndroidBitmap(byteArray, size)?.asImage() + actual fun getFetchResult(size: Int): FetchResult? { + val image: Image? = getAndroidBitmap(byteArray, size)?.asImage() + return if (image != null) { + ImageFetchResult( + image = image, + isSampled = true, + dataSource = DataSource.NETWORK, + ) + } else { + null + } + } + + private fun getCoilImage(size: Int): Image? = getAndroidBitmap(byteArray, size)?.asImage() private fun getAndroidBitmap( byteArray: ByteArray, diff --git a/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/Koin.kt b/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/Koin.kt index bbc09e7..826192e 100644 --- a/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/Koin.kt +++ b/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/Koin.kt @@ -26,8 +26,6 @@ import com.kevinschildhorn.fotopresenter.ui.screens.directory.DirectoryViewModel import com.kevinschildhorn.fotopresenter.ui.screens.login.LoginViewModel import com.kevinschildhorn.fotopresenter.ui.screens.playlist.PlaylistViewModel import com.kevinschildhorn.fotopresenter.ui.screens.slideshow.SlideshowViewModel -import com.kevinschildhorn.fotopresenter.ui.shared.CacheInterface -import com.kevinschildhorn.fotopresenter.ui.shared.SharedInMemoryCache import org.koin.core.module.Module import org.koin.dsl.module @@ -37,13 +35,12 @@ val commonModule = module { // Data - single { SharedInMemoryCache } single { NetworkImageDataSource(get()) } single { CredentialsDataSource(get()) } single { CredentialsRepository(get()) } single { DirectoryDataSource(get(), baseLogger.withTag("DirectoryDataSource")) } single { DirectoryRepository(get(), get()) } - single { CachedImageDataSource(get(), get(), baseLogger.withTag("ImageCacheDataSource")) } + single { CachedImageDataSource(get(), baseLogger.withTag("ImageCacheDataSource"), get()) } single { PlaylistFileDataSource(baseLogger.withTag("PlaylistDataSource"), get()) } single { PlaylistSQLDataSource(get(), baseLogger.withTag("PlaylistDataSource")) } single { PlaylistRepository(get(), get()) } @@ -75,7 +72,7 @@ val commonModule = baseLogger.withTag("RetrieveDirectoryContentsUseCase"), ) } - factory { RetrieveImageUseCase(get(), baseLogger.withTag("RetrieveImagesUseCase")) } + factory { RetrieveImageUseCase(get(), baseLogger.withTag("RetrieveImageUseCase")) } factory { SaveMetadataForPathUseCase(get()) } // UI single { LoginViewModel(baseLogger.withTag("LoginViewModel"), get()) } diff --git a/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/data/datasources/image/CachedImageDataSource.kt b/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/data/datasources/image/CachedImageDataSource.kt index ad51c56..ddb6791 100644 --- a/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/data/datasources/image/CachedImageDataSource.kt +++ b/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/data/datasources/image/CachedImageDataSource.kt @@ -6,35 +6,37 @@ import com.kevinschildhorn.fotopresenter.PlaylistDatabase import com.kevinschildhorn.fotopresenter.data.network.NetworkDirectoryDetails import com.kevinschildhorn.fotopresenter.ui.shared.CacheInterface import com.kevinschildhorn.fotopresenter.ui.shared.SharedImage -import com.kevinschildhorn.fotopresenter.Image as SQLImage class CachedImageDataSource( private val cache: CacheInterface, - driver: SqlDriver, private val logger: Logger, + driver: SqlDriver, ) { private val database = PlaylistDatabase(driver) - fun getImage(directory: NetworkDirectoryDetails): SharedImage? { + suspend fun getImage(directory: NetworkDirectoryDetails): SharedImage? { logger.i { "Getting Image from Cache ${directory.cacheId}" } return try { - val image: SQLImage = database.imageQueries.selectImageByName(directory.cacheId).executeAsOne() - SharedImage(image.image) + cache.getImage(directory.cacheId) + // val image = database.imageQueries.selectImageByName(directory.cacheId).executeAsOne() + // SharedImage(image.image) } catch (e: Exception) { logger.e(e) { "Image NOT found" } + logger.e { e.localizedMessage ?: "" } null } } - fun saveImage( + suspend fun saveImage( directory: NetworkDirectoryDetails, image: SharedImage, ) { logger.i { "Saving Image To Cache ${directory.cacheId}" } - database.imageQueries.insertImage( - directory.cacheId, - image.byteArray, - ) + cache.cacheImage(directory.cacheId, image) + // database.imageQueries.insertImage( + // directory.cacheId, + // image.byteArray, + // ) logger.i { "Image Saved" } // cache.cacheImage(directory.cacheId, image) TODO } diff --git a/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/data/repositories/ImageRepository.kt b/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/data/repositories/ImageRepository.kt index d8bb414..af52436 100644 --- a/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/data/repositories/ImageRepository.kt +++ b/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/data/repositories/ImageRepository.kt @@ -1,30 +1,42 @@ package com.kevinschildhorn.fotopresenter.data.repositories import co.touchlab.kermit.Logger -import coil3.Image +import coil3.fetch.FetchResult import com.kevinschildhorn.fotopresenter.data.datasources.image.CachedImageDataSource import com.kevinschildhorn.fotopresenter.data.datasources.image.NetworkImageDataSource import com.kevinschildhorn.fotopresenter.data.network.NetworkDirectoryDetails +import com.kevinschildhorn.fotopresenter.ui.shared.SharedImage class ImageRepository( private val remoteImageDataSource: NetworkImageDataSource, private val localImageDataSource: CachedImageDataSource, private val logger: Logger?, ) { - suspend fun getCoilImage( + suspend fun getFetchResult( directoryDetails: NetworkDirectoryDetails, size: Int, - ): Image? { - logger?.i { "Getting Image from Cache" } + ): FetchResult? { + val image = getImage(directoryDetails, size) + return image?.getFetchResult(size) + } + + private suspend fun getImage( + directoryDetails: NetworkDirectoryDetails, + size: Int, + ): SharedImage? { + logger?.i { "Getting Image from Cache: ${directoryDetails.name}" } val cachedImage = localImageDataSource.getImage(directoryDetails) - if (cachedImage != null) return cachedImage.getCoilImage(size) + if (cachedImage != null) { + logger?.i { "Cached image found from Cache: ${directoryDetails.name}" } + return cachedImage + } logger?.i { "No cached image found, getting image from directory" } val image = remoteImageDataSource.getImage(directoryDetails) if (image != null) { - logger?.i { "Storing image in cache" } + logger?.i { "Storing image in cache: ${directoryDetails.name}" } localImageDataSource.saveImage(directoryDetails, image) } - return image?.getCoilImage(size) + return image } } diff --git a/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/ui/ByteArrayFetcher.kt b/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/ui/ByteArrayFetcher.kt index 3f88d76..6ce5ce2 100644 --- a/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/ui/ByteArrayFetcher.kt +++ b/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/ui/ByteArrayFetcher.kt @@ -1,41 +1,37 @@ package com.kevinschildhorn.fotopresenter.ui +import co.touchlab.kermit.Logger import coil3.ImageLoader -import coil3.decode.DataSource import coil3.fetch.FetchResult import coil3.fetch.Fetcher -import coil3.fetch.ImageFetchResult import coil3.request.Options -import com.kevinschildhorn.fotopresenter.data.network.NetworkHandler import com.kevinschildhorn.fotopresenter.ui.shared.SharedImage import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext class ByteArrayFetcher( private val byteArray: ByteArray, - private val networkHandler: NetworkHandler, + private val logger: Logger, ) : Fetcher { override suspend fun fetch(): FetchResult? { return withContext(Dispatchers.IO) { val image = SharedImage(byteArray) - val coilImage = image.getCoilImage(64) - if (coilImage != null) { - ImageFetchResult( - image = coilImage, - isSampled = true, - dataSource = DataSource.NETWORK, - ) + val result = image.getFetchResult(64) + if (result != null) { + logger.i { "Image Got!" } + result } else { - throw Exception("Failed to fetch image from FTP") + logger.i { "No Image Fetched" } + null } } } - class Factory(private val networkHandler: NetworkHandler) : Fetcher.Factory { + class Factory(private val logger: Logger) : Fetcher.Factory { override fun create( data: ByteArray, options: Options, imageLoader: ImageLoader, - ): Fetcher = ByteArrayFetcher(data, networkHandler) + ): Fetcher = ByteArrayFetcher(data, logger) } } diff --git a/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/ui/shared/SharedCache.kt b/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/ui/shared/SharedCache.kt index 0e535d9..891ef2e 100644 --- a/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/ui/shared/SharedCache.kt +++ b/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/ui/shared/SharedCache.kt @@ -1,11 +1,14 @@ package com.kevinschildhorn.fotopresenter.ui.shared +import com.mayakapps.kache.FileKache +import com.mayakapps.kache.KacheStrategy import io.github.reactivecircus.cache4k.Cache +import java.io.File interface CacheInterface { - fun getImage(id: String): SharedImage? + suspend fun getImage(id: String): SharedImage? - fun cacheImage( + suspend fun cacheImage( id: String, image: SharedImage, ) @@ -14,12 +17,12 @@ interface CacheInterface { object SharedInMemoryCache : CacheInterface { private val imageCache = Cache.Builder().build() - override fun getImage(id: String): SharedImage? = + override suspend fun getImage(id: String): SharedImage? = imageCache.get(id)?.let { SharedImage(it) } - override fun cacheImage( + override suspend fun cacheImage( id: String, image: SharedImage, ) { @@ -27,17 +30,66 @@ object SharedInMemoryCache : CacheInterface { } } +class SharedFileCache(private val cacheLocation: String) : CacheInterface { + override suspend fun getImage(id: String): SharedImage? { + val cache = createCache() + + val test = cache?.get(id) + println(test) + val byteArray = cache?.get(id)?.toByteArray() + return if (byteArray != null) SharedImage(byteArray) else null + } + + override suspend fun cacheImage( + id: String, + image: SharedImage, + ) { + val cache = createCache() + try { + val imageData = + cache?.put(id) { path -> + val file = File(path) + try { + val stream = file.outputStream() + stream.write(image.byteArray) + stream.close() + true + } catch (e: Exception) { + println(e.localizedMessage) + false + } + } + println(imageData) + } finally { + cache?.close() + } + } + + private suspend fun createCache(): FileKache? { + try { + println("Creating Cache") + return FileKache(directory = cacheLocation, maxSize = 10 * 1024 * 1024) { + strategy = KacheStrategy.LRU + } + } catch (e: Exception) { + println("Error Creating Cache") + println(e.localizedMessage) + return null + } + } +} + class MockSharedCache : CacheInterface { private val contents = mutableMapOf() - override fun cacheImage( + override suspend fun cacheImage( id: String, image: SharedImage, ) { contents[id] = image.byteArray } - override fun getImage(id: String): SharedImage? = + override suspend fun getImage(id: String): SharedImage? = contents[id]?.let { SharedImage(it) } diff --git a/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/ui/shared/SharedImage.kt b/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/ui/shared/SharedImage.kt index 831d34d..c229a9b 100644 --- a/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/ui/shared/SharedImage.kt +++ b/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/ui/shared/SharedImage.kt @@ -1,11 +1,11 @@ package com.kevinschildhorn.fotopresenter.ui.shared -import coil3.Image +import coil3.fetch.FetchResult expect class SharedImage(byteArray: ByteArray) { val byteArray: ByteArray - fun getCoilImage(size: Int): Image? + fun getFetchResult(size: Int): FetchResult? } fun getScaledDimensions( diff --git a/shared/src/desktopMain/kotlin/com/kevinschildhorn/fotopresenter/ui/shared/SharedImageDesktop.kt b/shared/src/desktopMain/kotlin/com/kevinschildhorn/fotopresenter/ui/shared/SharedImageDesktop.kt index 29e5218..e492b0a 100644 --- a/shared/src/desktopMain/kotlin/com/kevinschildhorn/fotopresenter/ui/shared/SharedImageDesktop.kt +++ b/shared/src/desktopMain/kotlin/com/kevinschildhorn/fotopresenter/ui/shared/SharedImageDesktop.kt @@ -2,9 +2,27 @@ package com.kevinschildhorn.fotopresenter.ui.shared -import coil3.Image +import coil3.decode.DataSource +import coil3.decode.ImageSource +import coil3.fetch.FetchResult +import coil3.fetch.SourceFetchResult +import okio.FileSystem +import okio.buffer +import okio.source +import java.io.ByteArrayInputStream actual open class SharedImage actual constructor(actual val byteArray: ByteArray) { - // TODO - actual fun getCoilImage(size: Int): Image? = null + actual fun getFetchResult(size: Int): FetchResult? { + val source = ByteArrayInputStream(byteArray).source().buffer() + + return SourceFetchResult( + source = + ImageSource( + source = source, + fileSystem = FileSystem.SYSTEM, + ), + mimeType = null, + dataSource = DataSource.NETWORK, + ) + } } diff --git a/shared/src/jvmMain/kotlin/com/kevinschildhorn/fotopresenter/ui/SMBJFetcher.kt b/shared/src/jvmMain/kotlin/com/kevinschildhorn/fotopresenter/ui/SMBJFetcher.kt index 651ef07..e1caacb 100644 --- a/shared/src/jvmMain/kotlin/com/kevinschildhorn/fotopresenter/ui/SMBJFetcher.kt +++ b/shared/src/jvmMain/kotlin/com/kevinschildhorn/fotopresenter/ui/SMBJFetcher.kt @@ -1,10 +1,9 @@ package com.kevinschildhorn.fotopresenter.ui +import co.touchlab.kermit.Logger import coil3.ImageLoader -import coil3.decode.DataSource import coil3.fetch.FetchResult import coil3.fetch.Fetcher -import coil3.fetch.ImageFetchResult import coil3.request.Options import com.kevinschildhorn.fotopresenter.data.network.NetworkDirectoryDetails import com.kevinschildhorn.fotopresenter.data.repositories.ImageRepository @@ -14,28 +13,28 @@ import kotlinx.coroutines.withContext class SMBJFetcher( private val directoryDetails: NetworkDirectoryDetails, private val imageRepository: ImageRepository, + private val logger: Logger, ) : Fetcher { - override suspend fun fetch(): FetchResult? { - return withContext(Dispatchers.IO) { - val image = imageRepository.getCoilImage(directoryDetails, 64) + override suspend fun fetch(): FetchResult? = + withContext(Dispatchers.IO) { + logger.i { "Fetching image: ${directoryDetails.name}" } + + val image = imageRepository.getFetchResult(directoryDetails, 64) if (image != null) { - ImageFetchResult( - image = image, - isSampled = true, - dataSource = DataSource.NETWORK, - ) + logger.i { "Image Got! ${directoryDetails.name}" } + image } else { + logger.i { "No Image Fetched: ${directoryDetails.name}" } null } } - } - class Factory(private val imageRepository: ImageRepository) : + class Factory(private val imageRepository: ImageRepository, private val logger: Logger) : Fetcher.Factory { override fun create( data: NetworkDirectoryDetails, options: Options, imageLoader: ImageLoader, - ): Fetcher = SMBJFetcher(data, imageRepository) + ): Fetcher = SMBJFetcher(data, imageRepository, logger) } }