Skip to content

Commit

Permalink
Merge pull request #46 from KevinSchildhorn/StartingPlaylists
Browse files Browse the repository at this point in the history
Starting playlists
  • Loading branch information
KevinSchildhorn authored Dec 20, 2023
2 parents 1ce3fcd + 2faea4c commit c48e2b4
Show file tree
Hide file tree
Showing 15 changed files with 134 additions and 56 deletions.
5 changes: 2 additions & 3 deletions shared/src/commonMain/kotlin/App.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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(),
Expand All @@ -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
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.kevinschildhorn.fotopresenter.data

val supportedImageTypes = listOf<String>(
"png",
"jpg",
"jpeg",
"bmp"
)
Original file line number Diff line number Diff line change
@@ -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<PlaylistImage> = emptyList(),
val items: List<PlaylistItems> = 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()}
"""
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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) {
Expand All @@ -51,29 +51,29 @@ 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) {
null
}
}

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(),
)
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
Expand All @@ -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
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package com.kevinschildhorn.fotopresenter.data.network

import com.kevinschildhorn.fotopresenter.data.supportedImageTypes

interface NetworkDirectoryDetails {
val fullPath: String
val id: Int
Expand All @@ -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(
Expand Down
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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 =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<ImageBitmap>
get() = this?.let {
State.SUCCESS(it)
} ?: State.ERROR("No Image Found")
}
Original file line number Diff line number Diff line change
@@ -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<ImageDirectory> = 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)
}
}
Original file line number Diff line number Diff line change
@@ -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 =
Expand All @@ -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)
}

Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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()
Expand All @@ -58,7 +70,7 @@ class SlideshowViewModel(
startImageTimer()
}

fun stopSlideshow(){
fun stopSlideshow() {
stopImageTimer()
clearPresentedImage()
setImageDirectories(emptyList())
Expand Down
Loading

0 comments on commit c48e2b4

Please sign in to comment.