Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Updating how we get images #58

Merged
merged 4 commits into from
Oct 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion androidApp/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -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)

}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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()
}
Expand Down
31 changes: 31 additions & 0 deletions desktopApp/src/jvmMain/kotlin/Main.kt
Original file line number Diff line number Diff line change
@@ -1,28 +1,59 @@
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 =
DirectoryViewModel(UseCaseFactory.playlistRepository, Logger.withTag("DirectoryViewModel"))
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 {
Window(
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,
Expand Down
6 changes: 5 additions & 1 deletion gradle/libs.versions.toml
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
[versions]
accompanistPermissions = "0.36.0"
activity-compose = "1.9.2"
agp = "8.5.2"
appcompat = "1.7.0"
Expand All @@ -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"
Expand All @@ -33,19 +35,21 @@ 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" }
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" }
Expand Down
3 changes: 2 additions & 1 deletion shared/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -47,6 +49,14 @@ internal actual val platformModule: Module =
SMBJHandler
}
single<SqlDriver> { DriverFactory(context = get()).createDriver() }
single<NetworkHandler> {
SMBJHandler
}
single<SqlDriver> { DriverFactory(context = get()).createDriver() }
single<CacheInterface> {
val context: Context = get()
SharedFileCache(context.cacheDir.path)
}
}

@OptIn(KoinInternalApi::class)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -37,13 +35,12 @@ val commonModule =
module {

// Data
single<CacheInterface> { 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()) }
Expand Down Expand Up @@ -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()) }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand Down
Original file line number Diff line number Diff line change
@@ -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
}
}
Loading
Loading