Skip to content

Commit

Permalink
Merge pull request #61 from KevinSchildhorn/ks/FixingPreview
Browse files Browse the repository at this point in the history
Ks/fixing preview
  • Loading branch information
KevinSchildhorn authored Dec 13, 2024
2 parents 9f37120 + a2c1ade commit 3590457
Show file tree
Hide file tree
Showing 17 changed files with 141 additions and 62 deletions.
1 change: 1 addition & 0 deletions androidApp/src/androidMain/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:largeHeap="true"
android:theme="@style/Theme.AppCompat.Light.NoActionBar">
<activity
android:exported="true"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import coil3.memory.MemoryCache
import com.kevinschildhorn.fotopresenter.data.repositories.ImageRepository
import com.kevinschildhorn.fotopresenter.extension.logLargeTitle
import com.kevinschildhorn.fotopresenter.startKoin
import com.kevinschildhorn.fotopresenter.ui.ByteArrayFetcher
import com.kevinschildhorn.fotopresenter.ui.SharedImageFetcher
import com.kevinschildhorn.fotopresenter.ui.SMBJFetcher
import com.kevinschildhorn.fotopresenter.ui.screens.directory.DirectoryViewModel
import com.kevinschildhorn.fotopresenter.ui.screens.login.LoginViewModel
Expand Down Expand Up @@ -43,7 +43,7 @@ class MainActivity : AppCompatActivity(), KoinComponent {
ImageLoader.Builder(context)
.components {
add(SMBJFetcher.Factory(imageRepository, Logger))
add(ByteArrayFetcher.Factory(Logger))
add(SharedImageFetcher.Factory(Logger))
}
.memoryCache {
MemoryCache.Builder()
Expand Down
4 changes: 2 additions & 2 deletions desktopApp/src/jvmMain/kotlin/Main.kt
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import com.kevinschildhorn.fotopresenter.data.datasources.image.CachedImageDataS
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.SharedImageFetcher
import com.kevinschildhorn.fotopresenter.ui.shared.SharedFileCache


Expand Down Expand Up @@ -49,7 +49,7 @@ fun main() = application {
ImageLoader.Builder(context)
.components {
add(SMBJFetcher.Factory(imageRepository, baseLogger))
add(ByteArrayFetcher.Factory(baseLogger))
add(SharedImageFetcher.Factory(baseLogger))
}
.build()
}
Expand Down
2 changes: 2 additions & 0 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ appcompat = "1.7.0"
atomik = "0.0.6"
cache4k = "0.12.0"
coil = "3.0.4"
composeShimmer = "1.3.1"
core-ktx = "1.15.0"
eva-icons = "1.1.0"
fileKache = "2.1.0"
Expand Down Expand Up @@ -48,6 +49,7 @@ android-driver = { module = "app.cash.sqldelight:android-driver", version.ref =
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" }
compose-shimmer = { module = "com.valentinilk.shimmer:compose-shimmer", version.ref = "composeShimmer" }
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" }
Expand Down
1 change: 1 addition & 0 deletions shared/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ kotlin {
implementation(libs.kim)
api(libs.coil)
implementation(libs.file.kache)
implementation(libs.compose.shimmer)
}
}
val commonTest by getting {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,16 @@ import androidx.security.crypto.EncryptedSharedPreferences
import androidx.security.crypto.MasterKey
import app.cash.sqldelight.db.SqlDriver
import co.touchlab.kermit.Logger
import co.touchlab.kermit.koin.getLoggerWithTag
import co.touchlab.kermit.koin.kermitLoggerModule
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.extension.LoggerTagSuffix
import com.kevinschildhorn.fotopresenter.ui.shared.CacheInterface
import com.kevinschildhorn.fotopresenter.ui.shared.DriverFactory
import com.kevinschildhorn.fotopresenter.ui.shared.SharedFileCache
import com.kevinschildhorn.fotopresenter.ui.shared.SharedInMemoryCache
import com.russhwolf.settings.Settings
import com.russhwolf.settings.SharedPreferencesSettings
import org.koin.core.KoinApplication
Expand Down Expand Up @@ -53,6 +58,11 @@ internal actual val platformModule: Module =
SMBJHandler
}
single<SqlDriver> { DriverFactory(context = get()).createDriver() }
/*
single<CacheInterface> {
val context: Context = get()
SharedFileCache(context.filesDir.path, getLoggerWithTag("SharedFileCache$LoggerTagSuffix"))
}*/
}

@OptIn(KoinInternalApi::class)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,42 +4,40 @@ package com.kevinschildhorn.fotopresenter.ui.shared

import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.graphics.Matrix
import co.touchlab.kermit.Logger
import coil3.asImage
import coil3.decode.DataSource
import coil3.fetch.FetchResult
import coil3.fetch.ImageFetchResult
import com.ashampoo.kim.Kim
import com.ashampoo.kim.common.convertToPhotoMetadata
import com.ashampoo.kim.model.TiffOrientation

actual open class SharedImage actual constructor(actual val byteArray: ByteArray) {
actual fun getFetchResult(size: Int, logger: Logger?): FetchResult? {
if(byteArray.isEmpty()){
if (byteArray.isEmpty()) {
logger?.e { "Byte Array is Empty!" }
return null
}

logger?.d { "Getting Android Bitmap from Byte Array" }
val bitmap = getAndroidBitmap(byteArray, size, logger)
if (bitmap == null) logger?.e { "Could not generate Bitmap" }
logger?.v { "Getting Image from Bitmap" }
val image = bitmap?.asImage()
return if (image != null) {
logger?.v { "Returning result" }
ImageFetchResult(
image = image,
isSampled = true,
dataSource = DataSource.NETWORK,
)
} else {
logger?.e { "Image NOT Found" }
null
}
val image = bitmap.asImage()
logger?.v { "Returning result" }
return ImageFetchResult(
image = image,
isSampled = true,
dataSource = DataSource.NETWORK,
)
}

private fun getAndroidBitmap(
byteArray: ByteArray,
size: Int,
logger: Logger?,
): Bitmap? {
): Bitmap {
logger?.v { "Getting Android Bitmap" }
val options = BitmapFactory.Options()
options.inJustDecodeBounds = true
Expand All @@ -59,6 +57,34 @@ actual open class SharedImage actual constructor(actual val byteArray: ByteArray

options.inJustDecodeBounds = false
logger?.v { "Decoding Byte Array (Size: ${byteArray.size})" }
return BitmapFactory.decodeByteArray(byteArray, 0, byteArray.size, options)
val bitmap = BitmapFactory.decodeByteArray(byteArray, 0, byteArray.size, options)

val imageRotation = getImageRotationOffset(byteArray) ?: return bitmap
val matrix = Matrix()
matrix.postRotate(imageRotation)
val rotatedBitmap = Bitmap.createBitmap(
bitmap,
0,
0,
bitmap.width,
bitmap.height,
matrix,
true
)
return rotatedBitmap
}

private fun getImageRotationOffset(byteArray: ByteArray): Float? {
val photoMetadata = Kim.readMetadata(byteArray)?.convertToPhotoMetadata()
if (photoMetadata?.orientation != null) {
val orientation = photoMetadata.orientation!!
return when (orientation) {
TiffOrientation.ROTATE_LEFT -> -90f
TiffOrientation.ROTATE_RIGHT -> 90f
TiffOrientation.UPSIDE_DOWN -> 180f
else -> null
}
}
return null
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -47,11 +47,7 @@ val commonModule =
single { PlaylistRepository(get(), get(), getLoggerWithTag("PlaylistRepository$LoggerTagSuffix")) }
factory { ImageMetadataDataSource(getLoggerWithTag("ImageMetadataDataSource$LoggerTagSuffix"), get()) }
single { ImageRepository(get(), get(), getLoggerWithTag("ImageRepository$LoggerTagSuffix")) }
single<CacheInterface> {
//val context: Context = get()
//SharedFileCache(context.filesDir.path, getLoggerWithTag("SharedFileCache$LoggerTagSuffix"))
SharedInMemoryCache
}
single<CacheInterface> { SharedInMemoryCache }

// Domain
factory { ConnectToServerUseCase(get(), getLoggerWithTag("ConnectToServerUseCase$LoggerTagSuffix")) }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,12 @@ class ImageRepository(
directoryDetails: NetworkDirectoryDetails,
size: Int,
): FetchResult? {
val image = getImage(directoryDetails, size)
val image = getImage(directoryDetails)
if (image == null) logger?.e { "Shared Image was not retrieved from directory :${directoryDetails.fullPath}" }
return image?.getFetchResult(size, logger)
}

private suspend fun getImage(
directoryDetails: NetworkDirectoryDetails,
size: Int,
): SharedImage? {
private suspend fun getImage(directoryDetails: NetworkDirectoryDetails): SharedImage? {
logger?.d { "Getting Image: ${directoryDetails.name}" }
val cachedImage = localImageDataSource.getImage(directoryDetails)
if (cachedImage != null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,13 @@ import com.kevinschildhorn.fotopresenter.ui.shared.SharedImage
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext

class ByteArrayFetcher(
private val byteArray: ByteArray,
class SharedImageFetcher(
private val sharedImage: SharedImage,
private val logger: Logger,
) : Fetcher {
override suspend fun fetch(): FetchResult? {
return withContext(Dispatchers.IO) {
val image = SharedImage(byteArray)
val result = image.getFetchResult(64, logger)
val result = sharedImage.getFetchResult(1024, logger)
if (result != null) {
logger.i { "Image Got!" }
result
Expand All @@ -28,11 +27,11 @@ class ByteArrayFetcher(
}
}

class Factory(private val logger: Logger) : Fetcher.Factory<ByteArray> {
class Factory(private val logger: Logger) : Fetcher.Factory<SharedImage> {
override fun create(
data: ByteArray,
data: SharedImage,
options: Options,
imageLoader: ImageLoader,
): Fetcher = ByteArrayFetcher(data, logger.withTag("ByteArrayFetcher$LoggerTagSuffix"))
): Fetcher = SharedImageFetcher(data, logger.withTag("ByteArrayFetcher$LoggerTagSuffix"))
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import androidx.compose.ui.unit.dp
val fotoColors =
Colors(
primary = Color(0xFFFFA500),
primaryVariant = Color(0xFFFFA500),
primaryVariant = Color(0x44FFA500),
secondary = Color(0xFFFFD383),
secondaryVariant = Color(0xFFFFD383),
background = Color(0xFFFFFFFF),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package com.kevinschildhorn.fotopresenter.ui.composables

import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.layout.ContentScale
import coil3.compose.AsyncImage
import com.kevinschildhorn.fotopresenter.Res
import com.kevinschildhorn.fotopresenter.error
import com.valentinilk.shimmer.LocalShimmerTheme
import com.valentinilk.shimmer.defaultShimmerTheme
import com.valentinilk.shimmer.shimmer
import org.jetbrains.compose.resources.painterResource

@Composable
fun LoadingAsyncImage(
model: Any?,
contentDescription: String?,
contentScale: ContentScale,
modifier: Modifier = Modifier
) {
var loading by remember { mutableStateOf(true) }
AsyncImage(
model = model,
contentDescription = contentDescription,
contentScale = contentScale,
modifier = modifier,
onLoading = { loading = true },
onSuccess = { loading = false },
onError = { loading = false },
error = painterResource(Res.drawable.error),
)
if (loading) {
val shimmerTheme = defaultShimmerTheme.copy(rotation = 35f)
CompositionLocalProvider(LocalShimmerTheme provides shimmerTheme) {
Box(
modifier = Modifier.fillMaxSize().shimmer(),
contentAlignment = Alignment.Center
) {
Box(
modifier = Modifier.fillMaxSize()
.background(MaterialTheme.colors.primaryVariant)
)
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ class DefaultImageViewModel(private val logger: Logger? = null) : ImageViewModel
}

private fun updateSelectedImage() {
logger?.i { "Updating Selected Index" }
logger?.d { "Updating Selected Index" }
with(imageUiState.value) {
selectedImageDirectory?.let {
logger?.d { "Image Directory found, showing photo" }
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.kevinschildhorn.fotopresenter.ui.screens.common.composables

import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
Expand All @@ -10,14 +11,21 @@ import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.unit.dp
import coil3.compose.AsyncImage
import com.kevinschildhorn.fotopresenter.Res
import com.kevinschildhorn.fotopresenter.error
import com.kevinschildhorn.fotopresenter.photo_camera
import com.kevinschildhorn.fotopresenter.ui.atoms.Padding
import com.kevinschildhorn.fotopresenter.ui.atoms.fotoColors
import com.kevinschildhorn.fotopresenter.ui.composables.LoadingAsyncImage
import com.kevinschildhorn.fotopresenter.ui.shared.SharedImage
import compose.icons.EvaIcons
import compose.icons.evaicons.Fill
import compose.icons.evaicons.fill.ArrowLeft
import compose.icons.evaicons.fill.ArrowRight
import org.jetbrains.compose.resources.painterResource

@Composable
fun ImagePreviewOverlay(
Expand All @@ -36,10 +44,10 @@ fun ImagePreviewOverlay(
modifier = Modifier.fillMaxSize(),
verticalArrangement = Arrangement.SpaceBetween,
) {
// imageState.onSuccess {
AsyncImage(
LoadingAsyncImage(
model = image,
contentDescription = null,
contentScale = ContentScale.Fit,
modifier =
Modifier
.fillMaxWidth()
Expand All @@ -64,14 +72,5 @@ fun ImagePreviewOverlay(
)
}
}
/*
}.onLoading {
CircularProgressIndicator(
modifier = Modifier.width(75.dp).align(Alignment.Center),
color = FotoColors.primary,
)
}.onError {
Text("Error")
}*/
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ class DirectoryViewModel(
imageId?.let {
index = _uiState.value.getImageIndexFromId(it)
}
logger.i { "Set Image with Index: $index" }
logger.d { "Set Image with Index: $index" }
setSelectedImage(index)
}

Expand Down
Loading

0 comments on commit 3590457

Please sign in to comment.