You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

361 lines
10 KiB

package example.imageviewer.model
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.RememberObserver
import androidx.compose.runtime.mutableStateOf
import example.imageviewer.ResString
import example.imageviewer.core.FilterType
import example.imageviewer.model.filtration.FiltersManager
import example.imageviewer.utils.cacheImagePath
import example.imageviewer.utils.clearCache
import example.imageviewer.utils.isInternetAvailable
import example.imageviewer.view.showPopUpMessage
import java.awt.image.BufferedImage
import java.io.File
import java.util.concurrent.ExecutorService
import java.util.concurrent.Executors
import javax.swing.SwingUtilities.invokeLater
object ContentState : RememberObserver {
private lateinit var repository: ImageRepository
private lateinit var uriRepository: String
fun applyContent(uriRepository: String): ContentState {
if (this::uriRepository.isInitialized && this.uriRepository == uriRepository) {
return this
}
this.uriRepository = uriRepository
repository = ImageRepository(uriRepository)
isContentReady.value = false
initData()
return this
}
private val executor: ExecutorService by lazy { Executors.newFixedThreadPool(2) }
private val isAppReady = mutableStateOf(false)
fun isAppReady(): Boolean {
return isAppReady.value
}
private val isContentReady = mutableStateOf(false)
fun isContentReady(): Boolean {
return isContentReady.value
}
// drawable content
private val mainImageWrapper = MainImageWrapper
private val mainImage = mutableStateOf(BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB))
private val currentImageIndex = mutableStateOf(0)
private val miniatures = Miniatures()
fun getMiniatures(): List<Picture> {
return miniatures.getMiniatures()
}
fun getSelectedImage(): BufferedImage {
return mainImage.value
}
fun getSelectedImageName(): String {
return mainImageWrapper.getName()
}
// filters managing
private val appliedFilters = FiltersManager()
private val filterUIState: MutableMap<FilterType, MutableState<Boolean>> = LinkedHashMap()
private fun toggleFilterState(filter: FilterType) {
if (!filterUIState.containsKey(filter)) {
filterUIState[filter] = mutableStateOf(true)
} else {
val value = filterUIState[filter]!!.value
filterUIState[filter]!!.value = !value
}
}
fun toggleFilter(filter: FilterType) {
if (containsFilter(filter)) {
removeFilter(filter)
} else {
addFilter(filter)
}
toggleFilterState(filter)
var bitmap = mainImageWrapper.origin
if (bitmap != null) {
bitmap = appliedFilters.applyFilters(bitmap)
mainImageWrapper.setImage(bitmap)
mainImage.value = bitmap
}
}
private fun addFilter(filter: FilterType) {
appliedFilters.add(filter)
mainImageWrapper.addFilter(filter)
}
private fun removeFilter(filter: FilterType) {
appliedFilters.remove(filter)
mainImageWrapper.removeFilter(filter)
}
private fun containsFilter(type: FilterType): Boolean {
return appliedFilters.contains(type)
}
fun isFilterEnabled(type: FilterType): Boolean {
if (!filterUIState.containsKey(type)) {
filterUIState[type] = mutableStateOf(false)
}
return filterUIState[type]!!.value
}
private fun restoreFilters(): BufferedImage {
filterUIState.clear()
appliedFilters.clear()
return mainImageWrapper.restore()
}
fun restoreMainImage() {
mainImage.value = restoreFilters()
}
// application content initialization
private fun initData() {
if (isContentReady.value)
return
val directory = File(cacheImagePath)
if (!directory.exists()) {
directory.mkdir()
}
executor.execute {
try {
if (isInternetAvailable()) {
val imageList = repository.get()
if (imageList.isEmpty()) {
invokeLater {
showPopUpMessage(
ResString.repoInvalid
)
onContentReady()
}
return@execute
}
val pictureList = loadImages(cacheImagePath, imageList)
if (pictureList.isEmpty()) {
invokeLater {
showPopUpMessage(
ResString.repoEmpty
)
onContentReady()
}
} else {
val picture = loadFullImage(imageList[0])
invokeLater {
miniatures.setMiniatures(pictureList)
if (isMainImageEmpty()) {
wrapPictureIntoMainImage(picture)
} else {
appliedFilters.add(mainImageWrapper.getFilters())
currentImageIndex.value = mainImageWrapper.getId()
}
onContentReady()
}
}
} else {
invokeLater {
showPopUpMessage(
ResString.noInternet
)
onContentReady()
}
}
} catch (e: Exception) {
e.printStackTrace()
}
}
}
// preview/fullscreen image managing
fun isMainImageEmpty(): Boolean {
return mainImageWrapper.isEmpty()
}
fun fullscreen(picture: Picture) {
isContentReady.value = false
AppState.screenState(ScreenType.FullscreenImage)
setMainImage(picture)
}
fun setMainImage(picture: Picture) {
if (mainImageWrapper.getId() == picture.id) {
if (!isContentReady()) {
onContentReady()
}
return
}
executor.execute {
if (isInternetAvailable()) {
invokeLater {
val fullSizePicture = loadFullImage(picture.source)
fullSizePicture.id = picture.id
wrapPictureIntoMainImage(fullSizePicture)
onContentReady()
}
} else {
invokeLater {
showPopUpMessage(
"${ResString.noInternet}\n${ResString.loadImageUnavailable}"
)
wrapPictureIntoMainImage(picture)
onContentReady()
}
}
}
}
private fun onContentReady() {
isContentReady.value = true
isAppReady.value = true
}
private fun wrapPictureIntoMainImage(picture: Picture) {
mainImageWrapper.wrapPicture(picture)
mainImageWrapper.saveOrigin()
mainImage.value = picture.image
currentImageIndex.value = picture.id
}
fun swipeNext() {
if (currentImageIndex.value == miniatures.size() - 1) {
showPopUpMessage(ResString.lastImage)
return
}
restoreFilters()
setMainImage(miniatures.get(++currentImageIndex.value))
}
fun swipePrevious() {
if (currentImageIndex.value == 0) {
showPopUpMessage(ResString.firstImage)
return
}
restoreFilters()
setMainImage(miniatures.get(--currentImageIndex.value))
}
fun refresh() {
executor.execute {
if (isInternetAvailable()) {
invokeLater {
clearCache()
miniatures.clear()
isContentReady.value = false
initData()
}
} else {
invokeLater {
showPopUpMessage(
"${ResString.noInternet}\n${ResString.refreshUnavailable}"
)
}
}
}
}
override fun onRemembered() { }
override fun onAbandoned() { }
override fun onForgotten() {
executor.shutdown()
}
}
private object MainImageWrapper {
// origin image
var origin: BufferedImage? = null
private set
fun saveOrigin() {
origin = copy(picture.value.image)
}
fun restore(): BufferedImage {
if (origin != null) {
picture.value.image = copy(origin!!)
filtersSet.clear()
}
return copy(picture.value.image)
}
// picture adapter
private var picture = mutableStateOf(
Picture(image = BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB))
)
fun wrapPicture(picture: Picture) {
this.picture.value = picture
}
fun setImage(bitmap: BufferedImage) {
picture.value.image = bitmap
}
fun isEmpty(): Boolean {
return (picture.value.name == "")
}
fun getName(): String {
return picture.value.name
}
fun getImage(): BufferedImage {
return picture.value.image
}
fun getId(): Int {
return picture.value.id
}
// applied filters
private var filtersSet: MutableSet<FilterType> = LinkedHashSet()
fun addFilter(filter: FilterType) {
filtersSet.add(filter)
}
fun removeFilter(filter: FilterType) {
filtersSet.remove(filter)
}
fun getFilters(): Set<FilterType> {
return filtersSet
}
private fun copy(bitmap: BufferedImage) : BufferedImage {
var result = BufferedImage(bitmap.width, bitmap.height, bitmap.type)
val graphics = result.createGraphics()
graphics.drawImage(bitmap, 0, 0, result.width, result.height, null)
return result
}
}