Skip to content

Commit

Permalink
Updating how the app downloads images
Browse files Browse the repository at this point in the history
  • Loading branch information
KevinSchildhorn committed Feb 9, 2024
1 parent 900cb94 commit e084533
Show file tree
Hide file tree
Showing 4 changed files with 69 additions and 94 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,18 @@ import androidx.compose.ui.graphics.asImageBitmap
import com.hierynomus.smbj.share.File

actual fun getBitmapFromFile(file: File, size: Int): ImageBitmap? {

val options = BitmapFactory.Options()
options.inSampleSize = 2
BitmapFactory.decodeStream(file.inputStream, null, options)?.let {
val dimensions = getScaledDimensions(it.width, it.height, size)
return Bitmap.createScaledBitmap(it, dimensions.first, dimensions.second, false)
.asImageBitmap()
}
return null
options.inJustDecodeBounds = true
BitmapFactory.decodeStream(file.inputStream, null, options)

val height: Int = options.outHeight
val width: Int = options.outWidth
val dimensions = getScaledDimensions(width, height, size)
val heightRatio: Int = Math.round(height.toFloat() / dimensions.second.toFloat())
val widthRatio: Int = Math.round(width.toFloat() / dimensions.first.toFloat())
options.inSampleSize = if (heightRatio < widthRatio) heightRatio else widthRatio

options.inJustDecodeBounds = false
return BitmapFactory.decodeStream(file.inputStream, null, options)?.asImageBitmap()
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,73 +14,24 @@ class RetrieveImageUseCase(
private val logger: Logger,
) {

private val IMAGE_SIZE_SMALL = 64
private val IMAGE_SIZE_MEDIUM = 256
private val IMAGE_SIZE_LARGE = 512
private val IMAGE_SIZE_EXTRA_LARGE = 1024

suspend operator fun invoke(
directory: ImageDirectory,
callback: suspend (State<ImageBitmap>) -> Unit,
) {
imageSize: Int,
): ImageBitmap? {
val imageName = "\"${directory.details.fullPath}\""
logger.i { "Starting to get Image $imageName" }
callback(State.LOADING)

var workingWidth: Int = 0
var updatedImage: Boolean = false

/*
imageCacheDataSource.getImage(directory.details)?.let {
logger.i { "$imageName found in cache, using that" }
callback(State.SUCCESS(it))
workingWidth = it.width
}
logger.i { "Getting Image Bitmap from File $imageName" }
if (workingWidth <= IMAGE_SIZE_SMALL) {
logger.i { "Getting Small Bitmap for $imageName" }

val smallImageBitmap = downloadAndStoreImage(directory, IMAGE_SIZE_SMALL)
callback(smallImageBitmap.asState)
workingWidth = IMAGE_SIZE_SMALL
updatedImage = true
}
if (workingWidth <= IMAGE_SIZE_MEDIUM) {
logger.i { "Getting Medium Image" }
val mediumImageBitmap = downloadAndStoreImage(directory, IMAGE_SIZE_MEDIUM)
callback(mediumImageBitmap.asState)
workingWidth = IMAGE_SIZE_MEDIUM
updatedImage = true
}
if (workingWidth <= IMAGE_SIZE_LARGE) {
logger.i { "Getting Large Image" }
val mediumImageBitmap = downloadAndStoreImage(directory, IMAGE_SIZE_LARGE)
callback(mediumImageBitmap.asState)
workingWidth = IMAGE_SIZE_LARGE
updatedImage = true
}
if (workingWidth <= IMAGE_SIZE_EXTRA_LARGE || !updatedImage) {
logger.i { "Getting Extra Large Image" }
val mediumImageBitmap = downloadAndStoreImage(directory, IMAGE_SIZE_EXTRA_LARGE)
callback(mediumImageBitmap.asState)
workingWidth = IMAGE_SIZE_EXTRA_LARGE
}
}
return it
}*/

private fun downloadAndStoreImage(
directory: ImageDirectory,
bitmapSize: Int,
): ImageBitmap? {
logger.i { "Getting Image Bitmap from File ${directory.name}" }
val imageBitmap: ImageBitmap? = directory.image?.getImageBitmap(bitmapSize)
val imageBitmap: ImageBitmap? = directory.image?.getImageBitmap(imageSize)
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
@@ -1,14 +1,16 @@
package com.kevinschildhorn.fotopresenter.ui.screens.directory

import co.touchlab.kermit.Logger
import com.kevinschildhorn.fotopresenter.UseCaseFactory
import com.kevinschildhorn.fotopresenter.data.Directory
import com.kevinschildhorn.fotopresenter.data.DirectoryContents
import com.kevinschildhorn.fotopresenter.data.ImageDirectory
import com.kevinschildhorn.fotopresenter.data.ImageSlideshowDetails
import com.kevinschildhorn.fotopresenter.data.PlaylistDetails
import com.kevinschildhorn.fotopresenter.data.State
import com.kevinschildhorn.fotopresenter.UseCaseFactory
import com.kevinschildhorn.fotopresenter.data.network.NetworkHandlerException
import com.kevinschildhorn.fotopresenter.data.repositories.PlaylistRepository
import com.kevinschildhorn.fotopresenter.domain.image.RetrieveImageUseCase
import com.kevinschildhorn.fotopresenter.extension.addPath
import com.kevinschildhorn.fotopresenter.extension.navigateBackToPathAtIndex
import com.kevinschildhorn.fotopresenter.ui.SortingType
Expand All @@ -17,13 +19,16 @@ import com.kevinschildhorn.fotopresenter.ui.screens.common.ActionSheetContext
import com.kevinschildhorn.fotopresenter.ui.screens.common.DefaultImageViewModel
import com.kevinschildhorn.fotopresenter.ui.screens.common.ImageViewModel
import com.kevinschildhorn.fotopresenter.ui.screens.playlist.PlaylistViewModel
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.cancelChildren
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
import kotlinx.coroutines.plus
import kotlinx.datetime.Clock
import org.koin.core.component.KoinComponent

class DirectoryViewModel(
Expand All @@ -33,13 +38,16 @@ class DirectoryViewModel(
ImageViewModel by DefaultImageViewModel(logger),
KoinComponent {

private val slideshowScope: CoroutineScope = viewModelScope + Dispatchers.IO
private val imageScope: CoroutineScope = viewModelScope + Dispatchers.IO

private val _uiState = MutableStateFlow(DirectoryScreenState())
val uiState: StateFlow<DirectoryScreenState> = _uiState.asStateFlow()

private val _directoryContentsState = MutableStateFlow(DirectoryContents())

// Indexes of all Downloaded images
private val downloadedImageSet: MutableSet<Int> = mutableSetOf()
private val jobs: MutableList<Job> = mutableListOf<Job>()

private val currentPath: String
get() = uiState.value.currentPath
Expand All @@ -48,7 +56,7 @@ class DirectoryViewModel(
get() = uiState.value.selectedDirectory?.actionSheetContexts ?: emptyList()

init {
setImageScope(viewModelScope)
setImageScope(viewModelScope + Dispatchers.Default)
}

fun refreshScreen() {
Expand Down Expand Up @@ -84,13 +92,12 @@ class DirectoryViewModel(
logger.d { "Finding Folder" }
_directoryContentsState.value.folders.find { it.id == id }?.let {
logger.d { "Folder found, starting to retrieve images" }
val job = viewModelScope.launch(Dispatchers.Default) {
slideshowScope.launch {
val retrieveImagesUseCase = UseCaseFactory.retrieveImageDirectoriesUseCase
val images = retrieveImagesUseCase(it.details)
logger.v { "Retrieved images, copying them to state" }
_uiState.update { it.copy(slideshowDetails = ImageSlideshowDetails(images)) }
}
jobs.add(job)
}
} ?: run {
logger.w { "No Directory Selected!" }
Expand Down Expand Up @@ -136,7 +143,7 @@ class DirectoryViewModel(
logger.i { "Changing directory to path $path" }

cancelJobs()
viewModelScope.launch(Dispatchers.Default) {
slideshowScope.launch(Dispatchers.Default) {
val changeDirectoryUseCase = UseCaseFactory.changeDirectoryUseCase
try {
logger.i { "Getting New Path" }
Expand Down Expand Up @@ -171,7 +178,7 @@ class DirectoryViewModel(
private fun updateDirectories() {
logger.i { "Updating Directories" }
_uiState.update { it.copy(state = UiState.LOADING) }
val job = viewModelScope.launch(Dispatchers.Default) {
viewModelScope.launch(Dispatchers.Default) {
val retrieveDirectoryUseCase = UseCaseFactory.retrieveDirectoryContentsUseCase

logger.i { "Getting Directory Contents" }
Expand All @@ -183,34 +190,43 @@ class DirectoryViewModel(
logger.i { "Current State ${uiState.value.state}" }
updatePhotos()
}
jobs.add(job)
}

private fun updatePhotos() {
val count = imageUiState.value.imageDirectories.count()
downloadedImageSet.clear()

_uiState.update { it.copy(totalImageCount = count, currentImageCount = 0) }

logger.i { "Updating Photos" }
imageUiState.value.imageDirectories.forEachIndexed { index, imageDirectory ->
val job = viewModelScope.launch(Dispatchers.Default) {
val retrieveImagesUseCase = UseCaseFactory.retrieveImageUseCase

retrieveImagesUseCase(imageDirectory) { newState ->

downloadedImageSet.add(index)
_uiState.update {
it.copyImageState(
imageDirectory.id,
state = newState,
).copy(
currentImageCount = downloadedImageSet.size
)
imageScope.launch {
val startTime = Clock.System.now().toEpochMilliseconds()
logger.i { "Updating Photos" }
val retrieveImagesUseCase: RetrieveImageUseCase = UseCaseFactory.retrieveImageUseCase
val imageDirectories: List<ImageDirectory> = imageUiState.value.imageDirectories
imageDirectories.forEachIndexed { index, imageDirectory ->
launch {
retrieveImagesUseCase(
imageDirectory,
imageSize = 512, // TODO: Change
)?.let { newImage ->
logger.i { "Downloaded Image at index $index" }
downloadedImageSet.add(index)

_uiState.update {
it.copyImageState(
imageDirectory.id,
state = State.SUCCESS(newImage),
).copy(
currentImageCount = downloadedImageSet.size
)
}

if (_uiState.value.currentImageCount == _uiState.value.totalImageCount) {
val endTime = Clock.System.now().toEpochMilliseconds()
val difference: Float = (endTime.toFloat() - startTime.toFloat()) / 1000
logger.i { "Downloading all images took $difference seconds" }
}
}
}
}
jobs.add(job)
}
}

Expand Down Expand Up @@ -279,10 +295,8 @@ class DirectoryViewModel(
private fun cancelJobs() {
logger.d { "Cancelling Jobs!" }
cancelImageJobs()
jobs.forEach {
it.cancel()
}
jobs.clear()
slideshowScope.coroutineContext.cancelChildren()
imageScope.coroutineContext.cancelChildren()
logger.v { "Finished Cancelling Jobs!" }

}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.kevinschildhorn.fotopresenter.data.network

import co.touchlab.kermit.Logger
import com.hierynomus.msdtyp.AccessMask
import com.hierynomus.msfscc.FileAttributes
import com.hierynomus.mssmb2.SMB2CreateDisposition
Expand All @@ -25,7 +26,8 @@ object SMBJHandler : NetworkHandler {
private var connection: Connection? = null
private var session: Session? = null
private var share: DiskShare? = null
private val metaDataName: String = "FotoMetaData.json"
private const val metaDataName: String = "FotoMetaData.json"
private val logger = Logger.withTag("SMBJHandler")

private val accessMask: Set<AccessMask> =
setOf(
Expand All @@ -52,11 +54,13 @@ object SMBJHandler : NetworkHandler {
val session: Session? = connection?.authenticate(context)
share = session?.connectShare(credentials.sharedFolder) as? DiskShare
if (share == null) {
logger.e { "Failed To Connect, shared was null" }
disconnect()
return false
}
}
} catch (e: Exception) {
logger.e(e) { "Failed To Connect" }
disconnect()
return false
}
Expand Down

0 comments on commit e084533

Please sign in to comment.