diff --git a/shared/src/commonMain/kotlin/App.kt b/shared/src/commonMain/kotlin/App.kt index e2373b53..b0d30305 100644 --- a/shared/src/commonMain/kotlin/App.kt +++ b/shared/src/commonMain/kotlin/App.kt @@ -53,9 +53,8 @@ fun App( Screen.PLAYLIST -> { PlaylistScreen(playlistViewModel, overlaid = false) { - playlistViewModel.logger.i { "Starting playlist" } - playlistViewModel.logger.i { "$it" } - + slideshowViewModel.setSlideshowFromPlaylist(it) + currentScreen.value = Screen.SLIDESHOW } } } diff --git a/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/Koin.kt b/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/Koin.kt index 93f43fa1..0769a39f 100644 --- a/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/Koin.kt +++ b/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/Koin.kt @@ -19,6 +19,7 @@ import com.kevinschildhorn.fotopresenter.domain.connection.SaveCredentialsUseCas 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 com.kevinschildhorn.fotopresenter.ui.screens.directory.DirectoryViewModel import com.kevinschildhorn.fotopresenter.ui.screens.login.LoginViewModel import com.kevinschildhorn.fotopresenter.ui.screens.playlist.PlaylistViewModel @@ -49,8 +50,15 @@ val commonModule = factory { ChangeDirectoryUseCase(get(), baseLogger.withTag("ChangeDirectoryUseCase")) } factory { AutoConnectUseCase(get(), get(), baseLogger.withTag("AutoConnectUseCase")) } factory { SaveCredentialsUseCase(get(), baseLogger.withTag("SaveCredentialsUseCase")) } - factory { DisconnectFromServerUseCase(get(), get(), baseLogger.withTag("LogoutUseCase")) } - factory { RetrieveImageDirectoriesUseCase(baseLogger.withTag("LogoutUseCase")) } + factory { + DisconnectFromServerUseCase( + get(), + get(), + baseLogger.withTag("DisconnectFromServerUseCase") + ) + } + factory { RetrieveImageDirectoriesUseCase(baseLogger.withTag("RetrieveImageDirectoriesUseCase")) } + factory { RetrieveSlideshowFromPlaylistUseCase(baseLogger.withTag("RetrieveSlideshowFromPlaylistUseCase")) } factory { RetrieveDirectoryContentsUseCase( get(), @@ -62,9 +70,9 @@ val commonModule = // UI single { LoginViewModel(baseLogger.withTag("LoginViewModel"), get()) } - single { DirectoryViewModel(get(),baseLogger.withTag("DirectoryViewModel")) } + single { DirectoryViewModel(get(), baseLogger.withTag("DirectoryViewModel")) } single { SlideshowViewModel(baseLogger.withTag("SlideshowViewModel")) } - single { PlaylistViewModel(get(),baseLogger.withTag("PlaylistViewModel")) } + single { PlaylistViewModel(get(), baseLogger.withTag("PlaylistViewModel")) } } internal expect val platformModule: Module diff --git a/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/data/ImageUtils.kt b/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/data/ImageUtils.kt new file mode 100644 index 00000000..08b825a8 --- /dev/null +++ b/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/data/ImageUtils.kt @@ -0,0 +1,8 @@ +package com.kevinschildhorn.fotopresenter.data + +val supportedImageTypes = listOf( + "png", + "jpg", + "jpeg", + "bmp" +) \ No newline at end of file diff --git a/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/data/PlaylistDetails.kt b/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/data/PlaylistDetails.kt index e8cc83ce..dec2da8c 100644 --- a/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/data/PlaylistDetails.kt +++ b/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/data/PlaylistDetails.kt @@ -1,31 +1,20 @@ package com.kevinschildhorn.fotopresenter.data -import com.kevinschildhorn.fotopresenter.PlaylistImage +import com.kevinschildhorn.fotopresenter.PlaylistItems import com.kevinschildhorn.fotopresenter.data.network.DefaultNetworkDirectoryDetails +import com.kevinschildhorn.fotopresenter.extension.isImagePath data class PlaylistDetails( val id: Long, val name: String, - val images: List = emptyList(), + val items: List = emptyList(), ){ - val asImageSlideshowDetails:ImageSlideshowDetails - get() = ImageSlideshowDetails( - directories = images.map { - ImageDirectory( - DefaultNetworkDirectoryDetails( - id = it.directory_id.toInt(), - fullPath = it.directory_path - ) - ) - } - ) - override fun toString(): String { return """ Playlist Details: id: $id name: $name - images: ${images.count()} + images: ${items.count()} """ } } \ No newline at end of file diff --git a/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/data/datasources/PlaylistDataSource.kt b/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/data/datasources/PlaylistDataSource.kt index 1189485d..314919e7 100644 --- a/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/data/datasources/PlaylistDataSource.kt +++ b/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/data/datasources/PlaylistDataSource.kt @@ -4,7 +4,7 @@ import app.cash.sqldelight.db.SqlDriver import co.touchlab.kermit.Logger import com.kevinschildhorn.fotopresenter.Playlist import com.kevinschildhorn.fotopresenter.PlaylistDatabase -import com.kevinschildhorn.fotopresenter.PlaylistImage +import com.kevinschildhorn.fotopresenter.PlaylistItems import com.kevinschildhorn.fotopresenter.data.Directory import com.kevinschildhorn.fotopresenter.data.ImageDirectory import com.kevinschildhorn.fotopresenter.data.PlaylistDetails @@ -37,7 +37,7 @@ class PlaylistDataSource( return try { database.playlistQueries.selectAllPlaylists().executeAsList().map { val images = - database.imageDirectoryQueries.selectPlaylistImages(it.id).executeAsList() + database.playlistItemsQueries.selectPlaylistImages(it.id).executeAsList() PlaylistDetails(it.id,it.name, images) } } catch (e: Exception) { @@ -51,7 +51,7 @@ class PlaylistDataSource( val playList: Playlist = database.playlistQueries.selectPlaylistByName(name).executeAsOne() val images = - database.imageDirectoryQueries.selectPlaylistImages(playList.id).executeAsList() + database.playlistItemsQueries.selectPlaylistImages(playList.id).executeAsList() logger?.i { "Retrieved Playlist!" } PlaylistDetails(playList.id, playList.name, images) } catch (e: Exception) { @@ -59,9 +59,9 @@ class PlaylistDataSource( } } - fun insertPlaylistImage(playlistId: Long, directory: Directory): PlaylistImage? { + fun insertPlaylistImage(playlistId: Long, directory: Directory): PlaylistItems? { logger?.i { "Inserting Playlist Image ${directory.name}" } - database.imageDirectoryQueries.insertPlaylistImage( + database.playlistItemsQueries.insertPlaylistImage( playlist_id = playlistId, directory_path = directory.details.fullPath, directory_id = directory.id.toLong(), @@ -69,11 +69,11 @@ class PlaylistDataSource( return getPlaylistImage(playlistId, directory.details.fullPath) } - fun getPlaylistImage(playlistId: Long, directoryPath: String): PlaylistImage? { + fun getPlaylistImage(playlistId: Long, directoryPath: String): PlaylistItems? { return try { logger?.i { "Selecting Playlist Image $playlistId" } - val image: PlaylistImage = - database.imageDirectoryQueries.selectPlaylistImage(playlistId, directoryPath) + val image: PlaylistItems = + database.playlistItemsQueries.selectPlaylistImage(playlistId, directoryPath) .executeAsOne() logger?.i { "Selecting Playlist Image" } image @@ -86,7 +86,7 @@ class PlaylistDataSource( return try { val playlist = database.playlistQueries.selectPlaylistById(id).executeAsOne() database.playlistQueries.deletePlaylist(playlist.name) - database.imageDirectoryQueries.deletePlaylist(playlist.id) + database.playlistItemsQueries.deletePlaylist(playlist.id) true } catch (e: Exception) { false diff --git a/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/data/network/NetworkDirectoryDetails.kt b/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/data/network/NetworkDirectoryDetails.kt index 4c93da17..a2d40c47 100644 --- a/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/data/network/NetworkDirectoryDetails.kt +++ b/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/data/network/NetworkDirectoryDetails.kt @@ -1,5 +1,7 @@ package com.kevinschildhorn.fotopresenter.data.network +import com.kevinschildhorn.fotopresenter.data.supportedImageTypes + interface NetworkDirectoryDetails { val fullPath: String val id: Int @@ -16,11 +18,7 @@ interface NetworkDirectoryDetails { get() = this.fileExtension.isNullOrEmpty() val isAnImage: Boolean - get() = - fileExtension == "png" || - fileExtension == "jpg" || - fileExtension == "jpeg" || - fileExtension == "bmp" + get() = supportedImageTypes.contains(fileExtension) } class DefaultNetworkDirectoryDetails( diff --git a/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/data/repositories/PlaylistRepository.kt b/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/data/repositories/PlaylistRepository.kt index a503ba39..14e05a87 100644 --- a/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/data/repositories/PlaylistRepository.kt +++ b/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/data/repositories/PlaylistRepository.kt @@ -1,7 +1,7 @@ package com.kevinschildhorn.fotopresenter.data.repositories import com.kevinschildhorn.fotopresenter.Playlist -import com.kevinschildhorn.fotopresenter.PlaylistImage +import com.kevinschildhorn.fotopresenter.PlaylistItems import com.kevinschildhorn.fotopresenter.data.Directory import com.kevinschildhorn.fotopresenter.data.ImageDirectory import com.kevinschildhorn.fotopresenter.data.PlaylistDetails @@ -20,10 +20,10 @@ class PlaylistRepository( fun getPlaylistByName(name: String): PlaylistDetails? = playlistDataSource.getPlaylistByName(name) - fun insertPlaylistImage(playlistId: Long, directory: Directory): PlaylistImage? = + fun insertPlaylistImage(playlistId: Long, directory: Directory): PlaylistItems? = playlistDataSource.insertPlaylistImage(playlistId, directory) - fun getPlaylistImage(playlistId: Long, directoryPath: String): PlaylistImage? = + fun getPlaylistImage(playlistId: Long, directoryPath: String): PlaylistItems? = playlistDataSource.getPlaylistImage(playlistId, directoryPath) fun deletePlaylist(id: Long): Boolean = diff --git a/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/domain/image/RetrieveImageUseCase.kt b/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/domain/image/RetrieveImageUseCase.kt index f20206c5..9a3827af 100644 --- a/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/domain/image/RetrieveImageUseCase.kt +++ b/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/domain/image/RetrieveImageUseCase.kt @@ -26,15 +26,28 @@ class RetrieveImageUseCase( } logger.i { "Getting Image Bitmap from File ${directory.name}" } - val imageBitmap: ImageBitmap? = directory.image?.getImageBitmap(400) - callback( - imageBitmap?.let { - State.SUCCESS(it) - } ?: State.ERROR("No Image Found"), - ) + val smallImageBitmap = downloadAndStoreImage(directory, 64) + callback(smallImageBitmap.asState) + + val largeImageBitmap = downloadAndStoreImage(directory, 256) + callback(largeImageBitmap.asState) + } + + private fun downloadAndStoreImage( + directory: ImageDirectory, + bitmapSize: Int, + ): ImageBitmap? { + logger.i { "Getting Image Bitmap from File ${directory.name}" } + val imageBitmap: ImageBitmap? = directory.image?.getImageBitmap(bitmapSize) imageBitmap?.let { logger.i { "Caching new Image ${directory.name}" } imageCacheDataSource.saveImage(directory.details, it) } + return imageBitmap } + + private val ImageBitmap?.asState: State + get() = this?.let { + State.SUCCESS(it) + } ?: State.ERROR("No Image Found") } diff --git a/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/domain/image/RetrieveSlideshowFromPlaylistUseCase.kt b/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/domain/image/RetrieveSlideshowFromPlaylistUseCase.kt new file mode 100644 index 00000000..6f2c713e --- /dev/null +++ b/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/domain/image/RetrieveSlideshowFromPlaylistUseCase.kt @@ -0,0 +1,41 @@ +package com.kevinschildhorn.fotopresenter.domain.image + +import co.touchlab.kermit.Logger +import com.kevinschildhorn.fotopresenter.data.ImageDirectory +import com.kevinschildhorn.fotopresenter.data.ImageSlideshowDetails +import com.kevinschildhorn.fotopresenter.data.PlaylistDetails +import com.kevinschildhorn.fotopresenter.data.network.DefaultNetworkDirectoryDetails +import com.kevinschildhorn.fotopresenter.extension.isImagePath +import org.koin.core.component.KoinComponent +import org.koin.core.component.inject + +/** +Retrieving Slideshow Details From Playlist Details + **/ +class RetrieveSlideshowFromPlaylistUseCase( + private val logger: Logger, +) : KoinComponent { + suspend operator fun invoke( + playlistDetails: PlaylistDetails, + ): ImageSlideshowDetails { + logger.i { "Starting to get details from playlist ${playlistDetails.name}" } + val retrieveDirectoryUseCase: RetrieveImageDirectoriesUseCase by inject() + + val directories: List = playlistDetails.items.map { item -> + val directoryDetails = DefaultNetworkDirectoryDetails( + id = item.directory_id.toInt(), + fullPath = item.directory_path + ) + if (item.directory_path.isImagePath) { + listOf(ImageDirectory(directoryDetails)) + } else { + retrieveDirectoryUseCase( + directoryDetails = directoryDetails, + recursively = true, + ) + } + }.flatten() + + return ImageSlideshowDetails(directories) + } +} \ No newline at end of file diff --git a/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/extension/StringExtensions.kt b/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/extension/StringExtensions.kt index 1a52902a..4ca6a561 100644 --- a/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/extension/StringExtensions.kt +++ b/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/extension/StringExtensions.kt @@ -1,5 +1,7 @@ package com.kevinschildhorn.fotopresenter.extension +import com.kevinschildhorn.fotopresenter.data.supportedImageTypes + fun String.required(required: Boolean = true) = if (required) "$this*" else this fun String.addPath(directoryName: String): String = @@ -24,3 +26,11 @@ fun String.navigateBackToPathAtIndex(index: Int): String = .dropLast(1) } +val String.isImagePath: Boolean + get() { + if (!this.contains(".")) return false + + val extension = this.split(".").last() + return supportedImageTypes.contains(extension) + } + diff --git a/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/ui/screens/directory/DirectoryViewModel.kt b/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/ui/screens/directory/DirectoryViewModel.kt index 29c6c983..b8df0360 100644 --- a/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/ui/screens/directory/DirectoryViewModel.kt +++ b/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/ui/screens/directory/DirectoryViewModel.kt @@ -35,7 +35,7 @@ import org.koin.core.component.inject class DirectoryViewModel( private val playlistRepository: PlaylistRepository, - logger: Logger, + private val logger: Logger, ) : PlaylistViewModel(playlistRepository, logger), ImageViewModel by DefaultImageViewModel(logger), KoinComponent { diff --git a/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/ui/screens/playlist/PlaylistScreen.kt b/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/ui/screens/playlist/PlaylistScreen.kt index 7b704342..50776824 100644 --- a/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/ui/screens/playlist/PlaylistScreen.kt +++ b/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/ui/screens/playlist/PlaylistScreen.kt @@ -91,7 +91,7 @@ fun PlaylistScreen( PlaylistDialog.DETAILS -> { uiState.selectedPlaylist?.let { - TextListDialog(it.images.map { it.directory_path }) { + TextListDialog(it.items.map { it.directory_path }) { dialogOpen = PlaylistDialog.NONE } } diff --git a/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/ui/screens/playlist/PlaylistViewModel.kt b/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/ui/screens/playlist/PlaylistViewModel.kt index 7c9914e1..239a4c32 100644 --- a/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/ui/screens/playlist/PlaylistViewModel.kt +++ b/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/ui/screens/playlist/PlaylistViewModel.kt @@ -15,7 +15,7 @@ import org.koin.core.component.KoinComponent open class PlaylistViewModel( private val playlistRepository: PlaylistRepository, - val logger: Logger, + private val logger: Logger, ) : ViewModel(), KoinComponent { private val _uiState = MutableStateFlow(PlaylistScreenState()) diff --git a/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/ui/screens/slideshow/SlideshowViewModel.kt b/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/ui/screens/slideshow/SlideshowViewModel.kt index c62c24ee..e9bbd827 100644 --- a/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/ui/screens/slideshow/SlideshowViewModel.kt +++ b/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/ui/screens/slideshow/SlideshowViewModel.kt @@ -2,6 +2,8 @@ package com.kevinschildhorn.fotopresenter.ui.screens.slideshow import co.touchlab.kermit.Logger import com.kevinschildhorn.fotopresenter.data.ImageSlideshowDetails +import com.kevinschildhorn.fotopresenter.data.PlaylistDetails +import com.kevinschildhorn.fotopresenter.domain.image.RetrieveSlideshowFromPlaylistUseCase import com.kevinschildhorn.fotopresenter.ui.screens.common.DefaultImageViewModel import com.kevinschildhorn.fotopresenter.ui.screens.common.ImageViewModel import com.kevinschildhorn.fotopresenter.ui.shared.ViewModel @@ -15,6 +17,7 @@ import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch import org.koin.core.component.KoinComponent +import org.koin.core.component.inject import java.util.* import kotlin.concurrent.fixedRateTimer import kotlin.coroutines.EmptyCoroutineContext @@ -34,12 +37,21 @@ class SlideshowViewModel( setImageScope(viewModelScope) } + fun setSlideshowFromPlaylist(playlistDetails: PlaylistDetails) { + logger.i { "Starting playlist $playlistDetails" } + val useCase: RetrieveSlideshowFromPlaylistUseCase by inject() + viewModelScope.launch(Dispatchers.Default) { + val slideshow = useCase(playlistDetails) + setSlideshow(slideshow) + } + } + fun setSlideshow(details: ImageSlideshowDetails) { _uiState.update { it.copy(slideshowDetails = details) } setImageDirectories(details.directories) setSelectedImage(0) viewModelScope.launch(Dispatchers.Default) { - while(imageUiState.value.selectedImage == null) { + while (imageUiState.value.selectedImage == null) { delay(250) } startImageTimer() @@ -58,7 +70,7 @@ class SlideshowViewModel( startImageTimer() } - fun stopSlideshow(){ + fun stopSlideshow() { stopImageTimer() clearPresentedImage() setImageDirectories(emptyList()) diff --git a/shared/src/commonMain/sqldelight/com/kevinschildhorn/fotopresenter/ImageDirectory.sq b/shared/src/commonMain/sqldelight/com/kevinschildhorn/fotopresenter/PlaylistItems.sq similarity index 70% rename from shared/src/commonMain/sqldelight/com/kevinschildhorn/fotopresenter/ImageDirectory.sq rename to shared/src/commonMain/sqldelight/com/kevinschildhorn/fotopresenter/PlaylistItems.sq index 0f70792c..bdb57168 100644 --- a/shared/src/commonMain/sqldelight/com/kevinschildhorn/fotopresenter/ImageDirectory.sq +++ b/shared/src/commonMain/sqldelight/com/kevinschildhorn/fotopresenter/PlaylistItems.sq @@ -1,4 +1,4 @@ -CREATE TABLE PlaylistImage ( +CREATE TABLE PlaylistItems ( id INTEGER PRIMARY KEY NOT NULL, playlist_id INTEGER NOT NULL, directory_path TEXT NOT NULL, @@ -6,25 +6,25 @@ CREATE TABLE PlaylistImage ( ); insertPlaylistImage: -INSERT INTO PlaylistImage(playlist_id, directory_path, directory_id) +INSERT INTO PlaylistItems(playlist_id, directory_path, directory_id) VALUES (?, ?, ?); selectPlaylistImages: SELECT * -FROM PlaylistImage +FROM PlaylistItems WHERE playlist_id = ?; selectPlaylistImage: SELECT * -FROM PlaylistImage +FROM PlaylistItems WHERE playlist_id = ? AND directory_path = ?; deletePlaylistImage: DELETE -FROM PlaylistImage +FROM PlaylistItems WHERE directory_path = ?; deletePlaylist: DELETE -FROM PlaylistImage +FROM PlaylistItems WHERE playlist_id = ?; \ No newline at end of file