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

Fixing desktop version #47

Merged
merged 11 commits into from
Feb 8, 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
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -37,3 +37,5 @@ ios/FotoPresenter.xcworkspace/xcuserdata/kevinschildhorn.xcuserdatad/UserInterfa
ios/FotoPresenter.xcworkspace/xcuserdata/kevinschildhorn.xcuserdatad/UserInterfaceState.xcuserstate
common/src/commonMain/kotlin/me/kevinschildhorn/common/network/ftps/TestingLoginInfo.kt
androidApp/build/kotlin/compileDebugKotlinAndroid/cacheable/caches-jvm/lookups/lookups.tab_i.len

desktopApp/build
4 changes: 4 additions & 0 deletions desktopApp/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ kotlin {
val jvmMain by getting {
dependencies {
implementation(compose.desktop.currentOs)
implementation("io.insert-koin:koin-core:3.4.0")
implementation("co.touchlab:kermit:1.2.2")
implementation("com.russhwolf:multiplatform-settings:1.0.0")

implementation(project(":shared"))
}
}
Expand Down
29 changes: 26 additions & 3 deletions desktopApp/src/jvmMain/kotlin/Main.kt
Original file line number Diff line number Diff line change
@@ -1,9 +1,32 @@
import androidx.compose.material.Text
import androidx.compose.ui.window.Window
import androidx.compose.ui.window.application
import com.kevinschildhorn.fotopresenter.UseCaseFactory
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


object KoinPurse {
val loginViewModel =
LoginViewModel(UseCaseFactory.baseLogger, UseCaseFactory.credentialsRepository)
val directoryViewModel =
DirectoryViewModel(UseCaseFactory.playlistRepository, UseCaseFactory.baseLogger)
val slideshowViewModel = SlideshowViewModel(UseCaseFactory.baseLogger)
val playlistViewModel =
PlaylistViewModel(UseCaseFactory.playlistRepository, UseCaseFactory.baseLogger)
}

fun main() = application {
Window(onCloseRequest = ::exitApplication) {
Text("")
Window(
title = "FotoPresenter",
onCloseRequest = ::exitApplication
) {
MainView(
KoinPurse.loginViewModel,
KoinPurse.directoryViewModel,
KoinPurse.slideshowViewModel,
KoinPurse.playlistViewModel,
)
}
}
4 changes: 2 additions & 2 deletions gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@ kotlin.mpp.androidSourceSetLayoutVersion=2
android.useAndroidX=true
android.compileSdk=34
android.targetSdk=34
android.minSdk=24
android.minSdk=22

#Versions
kotlin.version=1.9.21
agp.version=8.1.4
compose.version=1.5.11
compose.version=1.5.11
4 changes: 2 additions & 2 deletions shared/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ kotlin {
implementation("co.touchlab:kermit:1.2.2")
implementation("co.touchlab:kermit-koin:1.2.2")
implementation("com.russhwolf:multiplatform-settings:1.0.0")

implementation("org.jetbrains.kotlinx:kotlinx-datetime:0.5.0")
api("dev.icerock.moko:resources:0.23.0")
api("dev.icerock.moko:resources-compose:0.23.0") // for compose multiplatform
}
Expand All @@ -60,7 +60,7 @@ kotlin {
dependsOn(commonMain)
resources.srcDir("src/commonMain/resources")
dependencies {
implementation("com.hierynomus:smbj:0.13.0")
implementation("com.hierynomus:smbj:0.11.5")
implementation(compose.uiTooling)
implementation("app.cash.sqldelight:sqlite-driver:2.0.1")
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package com.kevinschildhorn.fotopresenter

import com.kevinschildhorn.fotopresenter.domain.RetrieveDirectoryContentsUseCase
import com.kevinschildhorn.fotopresenter.domain.connection.AutoConnectUseCase
import com.kevinschildhorn.fotopresenter.domain.connection.ConnectToServerUseCase
import com.kevinschildhorn.fotopresenter.domain.connection.DisconnectFromServerUseCase
import com.kevinschildhorn.fotopresenter.domain.connection.SaveCredentialsUseCase
import com.kevinschildhorn.fotopresenter.domain.directory.ChangeDirectoryUseCase
import com.kevinschildhorn.fotopresenter.domain.image.RetrieveImageDirectoriesUseCase
import com.kevinschildhorn.fotopresenter.domain.image.RetrieveImageUseCase
import com.kevinschildhorn.fotopresenter.domain.image.RetrieveSlideshowFromPlaylistUseCase
import org.koin.core.component.KoinComponent
import org.koin.core.component.inject

actual object UseCaseFactory : KoinComponent {

actual val connectToServerUseCase: ConnectToServerUseCase
get() {
val useCase: ConnectToServerUseCase by inject()
return useCase
}
actual val changeDirectoryUseCase: ChangeDirectoryUseCase
get() {
val useCase: ChangeDirectoryUseCase by inject()
return useCase
}
actual val autoConnectUseCase: AutoConnectUseCase
get() {
val useCase: AutoConnectUseCase by inject()
return useCase
}
actual val saveCredentialsUseCase: SaveCredentialsUseCase
get() {
val useCase: SaveCredentialsUseCase by inject()
return useCase
}
actual val disconnectFromServerUseCase: DisconnectFromServerUseCase
get() {
val useCase: DisconnectFromServerUseCase by inject()
return useCase
}
actual val retrieveImageDirectoriesUseCase: RetrieveImageDirectoriesUseCase
get() {
val useCase: RetrieveImageDirectoriesUseCase by inject()
return useCase
}
actual val retrieveSlideshowFromPlaylistUseCase: RetrieveSlideshowFromPlaylistUseCase
get() {
val useCase: RetrieveSlideshowFromPlaylistUseCase by inject()
return useCase
}
actual val retrieveDirectoryContentsUseCase: RetrieveDirectoryContentsUseCase
get() {
val useCase: RetrieveDirectoryContentsUseCase by inject()
return useCase
}
actual val retrieveImageUseCase: RetrieveImageUseCase
get() {
val useCase: RetrieveImageUseCase by inject()
return useCase
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import com.kevinschildhorn.fotopresenter.ui.screens.common.ActionSheetContext
import com.kevinschildhorn.fotopresenter.ui.screens.common.composables.ActionSheet
import com.kevinschildhorn.fotopresenter.ui.screens.common.composables.ButtonState
import com.kevinschildhorn.fotopresenter.ui.screens.common.composables.ConfirmationDialog
import com.kevinschildhorn.fotopresenter.ui.screens.common.composables.FilterDialog
import com.kevinschildhorn.fotopresenter.ui.screens.common.composables.PrimaryTextButton
import com.kevinschildhorn.fotopresenter.ui.screens.playlist.composables.TextEntryDialog

Expand Down Expand Up @@ -64,3 +65,17 @@ fun TextConfirmationDialogPreview() {
)
}

@Preview
@Composable
fun FilterDialogPreview() {
FilterDialog(
"Hello",
{

},
{

},
)
}

Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ import android.graphics.BitmapFactory
import androidx.compose.ui.graphics.ImageBitmap
import androidx.compose.ui.graphics.asAndroidBitmap
import androidx.compose.ui.graphics.asImageBitmap
import java.nio.ByteBuffer
import java.io.ByteArrayOutputStream


actual object SharedImageConverter {
actual fun convertBytes(byteArray: ByteArray): ImageBitmap {
Expand All @@ -14,16 +15,20 @@ actual object SharedImageConverter {
}

actual fun convertImage(imageBitmap: ImageBitmap): ByteArray {
return imageBitmap.asAndroidBitmap().convertToByteArray()
val bitmap = imageBitmap.asAndroidBitmap()
val stream = ByteArrayOutputStream()
bitmap.compress(Bitmap.CompressFormat.PNG, 100, stream)
val byteArray = stream.toByteArray()
return byteArray
}

private fun Bitmap.convertToByteArray(): ByteArray {
val size = this.byteCount
val buffer = ByteBuffer.allocate(size)
val bytes = ByteArray(size)
this.copyPixelsToBuffer(buffer)
buffer.rewind()
buffer.get(bytes)
return bytes
val stream = ByteArrayOutputStream()
this.compress(Bitmap.CompressFormat.PNG, 100, stream)
val byteArray = stream.toByteArray()
if (this != null && !this.isRecycled()) {
this.recycle();
}
return byteArray
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,19 +29,20 @@ import com.kevinschildhorn.fotopresenter.ui.shared.SharedCache
import org.koin.core.module.Module
import org.koin.dsl.module

val baseLogger = Logger(LoggerConfig.default)

val commonModule =
module {
val baseLogger = Logger(LoggerConfig.default)

// Data
single<CacheInterface> { SharedCache }
single { CredentialsDataSource(get()) }
single { CredentialsRepository(get()) }
single { DirectoryDataSource(get()) }
single { DirectoryDataSource(get(), baseLogger.withTag("DirectoryDataSource")) }
single { DirectoryRepository(get()) }
single { ImageRemoteDataSource(get()) }
single { ImageRepository(get()) }
single { ImageCacheDataSource(get()) }
single { ImageCacheDataSource(get(), get(), baseLogger.withTag("ImageCacheDataSource")) }
single { PlaylistDataSource(get(), baseLogger.withTag("PlaylistDataSource")) }
single { PlaylistRepository(get()) }

Expand All @@ -58,7 +59,12 @@ val commonModule =
)
}
factory { RetrieveImageDirectoriesUseCase(baseLogger.withTag("RetrieveImageDirectoriesUseCase")) }
factory { RetrieveSlideshowFromPlaylistUseCase(baseLogger.withTag("RetrieveSlideshowFromPlaylistUseCase")) }
factory {
RetrieveSlideshowFromPlaylistUseCase(
baseLogger.withTag("RetrieveSlideshowFromPlaylistUseCase"),
get()
)
}
factory {
RetrieveDirectoryContentsUseCase(
get(),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package com.kevinschildhorn.fotopresenter

import com.kevinschildhorn.fotopresenter.domain.RetrieveDirectoryContentsUseCase
import com.kevinschildhorn.fotopresenter.domain.connection.AutoConnectUseCase
import com.kevinschildhorn.fotopresenter.domain.connection.ConnectToServerUseCase
import com.kevinschildhorn.fotopresenter.domain.connection.DisconnectFromServerUseCase
import com.kevinschildhorn.fotopresenter.domain.connection.SaveCredentialsUseCase
import com.kevinschildhorn.fotopresenter.domain.directory.ChangeDirectoryUseCase
import com.kevinschildhorn.fotopresenter.domain.image.RetrieveImageDirectoriesUseCase
import com.kevinschildhorn.fotopresenter.domain.image.RetrieveImageUseCase
import com.kevinschildhorn.fotopresenter.domain.image.RetrieveSlideshowFromPlaylistUseCase

expect object UseCaseFactory {
val connectToServerUseCase: ConnectToServerUseCase
val changeDirectoryUseCase: ChangeDirectoryUseCase
val autoConnectUseCase: AutoConnectUseCase
val saveCredentialsUseCase: SaveCredentialsUseCase
val disconnectFromServerUseCase: DisconnectFromServerUseCase
val retrieveImageDirectoriesUseCase: RetrieveImageDirectoriesUseCase
val retrieveSlideshowFromPlaylistUseCase: RetrieveSlideshowFromPlaylistUseCase
val retrieveDirectoryContentsUseCase: RetrieveDirectoryContentsUseCase
val retrieveImageUseCase: RetrieveImageUseCase
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.kevinschildhorn.fotopresenter.data

import com.kevinschildhorn.fotopresenter.data.network.NetworkDirectoryDetails
import com.kevinschildhorn.fotopresenter.ui.SortingType
import com.kevinschildhorn.fotopresenter.ui.shared.SharedImage

interface Directory {
Expand Down Expand Up @@ -38,6 +39,13 @@ data class DirectoryContents(
val allDirectories: List<Directory>
get() = folders + images

fun sorted(sortingType: SortingType): DirectoryContents {
return DirectoryContents(
folders = folders.sorted(sortingType) as List<FolderDirectory>,
images = images.sorted(sortingType) as List<ImageDirectory>,
)
}

override fun toString(): String {
return """
DirectoryContents:
Expand All @@ -48,3 +56,11 @@ data class DirectoryContents(
"""
}
}

fun List<Directory>.sorted(sortingType: SortingType): List<Directory> =
when (sortingType) {
SortingType.NAME_ASC -> this.sortedBy { it.name }
SortingType.NAME_DESC -> this.sortedByDescending { it.name }
SortingType.TIME_ASC -> this.sortedBy { it.details.dateMillis }
SortingType.TIME_DESC -> this.sortedByDescending { it.details.dateMillis }
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.kevinschildhorn.fotopresenter.data.datasources

import co.touchlab.kermit.Logger
import com.kevinschildhorn.fotopresenter.data.network.NetworkDirectoryDetails
import com.kevinschildhorn.fotopresenter.data.network.NetworkHandler
import com.kevinschildhorn.fotopresenter.data.network.NetworkHandlerError
Expand All @@ -9,11 +10,21 @@ import kotlin.coroutines.cancellation.CancellationException
/**
Fetches Directory info from a NAS
**/
class DirectoryDataSource(private val networkHandler: NetworkHandler) {
class DirectoryDataSource(
private val networkHandler: NetworkHandler,
private val logger: Logger,
) {
@Throws(NetworkHandlerException::class, CancellationException::class)
suspend fun changeDirectory(directoryName: String): String {
logger.i { "Changing directory to $directoryName" }
logger.i { "Is network Connected? ${networkHandler.isConnected}" }
if (!networkHandler.isConnected) throw NetworkHandlerException(NetworkHandlerError.NOT_CONNECTED)

logger.i { "Does the directory exist?" }
//val exists = networkHandler.folderExists(directoryName)
//logger.i { "Does the directory exist? $exists" }

logger.i { "Opening the directory..." }
networkHandler.openDirectory(directoryName)?.let { return it }
throw NetworkHandlerException(NetworkHandlerError.DIRECTORY_NOT_FOUND)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,46 @@
package com.kevinschildhorn.fotopresenter.data.datasources

import androidx.compose.ui.graphics.ImageBitmap
import app.cash.sqldelight.db.SqlDriver
import co.touchlab.kermit.Logger
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.SharedImageConverter

class ImageCacheDataSource(private val cache: CacheInterface) {
fun getImage(directory: NetworkDirectoryDetails): ImageBitmap? = cache.getImage(directory.cacheId)
class ImageCacheDataSource(
private val cache: CacheInterface,
driver: SqlDriver,
private val logger: Logger
) {
private val database = PlaylistDatabase(driver)


fun getImage(directory: NetworkDirectoryDetails): ImageBitmap? {
logger.i { "Getting Image from Cache ${directory.cacheId}" }
return try {
val image = database.imageQueries.selectImageByName(directory.cacheId).executeAsOne()
SharedImageConverter.convertBytes(image.image)
} catch (e: Exception) {
logger.e(e) { "Image NOT found" }

null
}
//return cache.getImage(directory.cacheId)
}

fun saveImage(
directory: NetworkDirectoryDetails,
bitmap: ImageBitmap,
) {
cache.cacheImage(directory.cacheId, bitmap)
logger.i { "Saving Image To Cache ${directory.cacheId}" }
database.imageQueries.insertImage(
directory.cacheId,
SharedImageConverter.convertImage(bitmap)
)
logger.i { "Image Saved" }

//DiscCache.storeFile(directory.cacheId, bitmap)
}

private val NetworkDirectoryDetails.cacheId: String
Expand Down
Loading
Loading