From 0dfba6086b3811c408f348e827c8a7d115c970a3 Mon Sep 17 00:00:00 2001 From: Sebastian Aigner Date: Fri, 17 Feb 2023 14:38:50 +0100 Subject: [PATCH] Visual refresh for experimental Image Viewer (#2748) * Design changes; move to material3 * Use animations to move between different images * More design changes, rounded corners and animations * Introduce square gallery view, start with granularizing state management * Introduce square gallery view, start with granularizing state management * Make PreviewImage not depend on the whole gallery state * Move in initialization logic from composition into launched effect * Highlight currently selected image * Hoist state for FullscreenImage TopAppBar Move from Custom Implementation to Material App Bar, use color scheme from main page Extract hardcoded colors to ImageViewerColors * Provide floating action buttons with nicer colors * Provide keyboard events via SharedFlow (remove passing around MutableState in the composable hierarchy as it may potentially violate UDF) Commonize IOScope initialization * Provide German translation in shared R-strings * Move from immutable data classes to Compose-aware State Holders. * Fix gradlew formatting issue? * Regenerate gradle wrapper after Android Studio autoformatting debacle * Resolve rememberCoroutineScope issue * Provide mock name for remaining picture in repo * Restore TEAM_ID in project.pbxproj * Use emptyFlow as default to simplify nullability handling for external events * Remove extraneous newline and unnecessary print statement * Provide German translation in XML format Consistently rename title to "My Memories" * Remove commented-out code, cleanup rendundant modifiers Make Title Bar use callbacks instead of accessing ViewModel directly Add toggle & icon for list and grid view --- .../kotlin/example/imageviewer/Main.kt | 5 +- .../gradle/wrapper/gradle-wrapper.jar | Bin 60756 -> 61574 bytes .../gradle/wrapper/gradle-wrapper.properties | 3 +- experimental/examples/imageviewer/gradlew | 12 +- experimental/examples/imageviewer/gradlew.bat | 1 + .../iosApp/iosApp.xcodeproj/project.pbxproj | 6 +- .../imageviewer/shared/build.gradle.kts | 2 + .../imageviewer/utils/IOScope.android.kt | 7 + .../imageviewer/view/ImageViewer.android.kt | 25 +- .../imageviewer/view/ScalableImage.android.kt | 3 +- .../src/androidMain/res/values-de/strings.xml | 17 ++ .../src/androidMain/res/values-ru/strings.xml | 16 -- .../src/androidMain/res/values/strings.xml | 3 +- .../example/imageviewer/ImageViewer.common.kt | 77 +++-- .../example/imageviewer/model/GalleryState.kt | 77 +++++ .../example/imageviewer/model/Picture.kt | 21 +- .../imageviewer/model/ScalableState.kt | 61 ++-- .../kotlin/example/imageviewer/model/State.kt | 85 ------ .../example/imageviewer/style/Palette.kt | 27 +- .../example/imageviewer/utils/IOScope.kt | 5 + .../imageviewer/view/FullscreenImage.kt | 137 ++++----- .../example/imageviewer/view/LoadingScreen.kt | 11 +- .../example/imageviewer/view/MainScreen.kt | 237 +++++++++++++--- .../example/imageviewer/view/Miniature.kt | 57 ++-- .../imageviewer/view/PreviewImage.common.kt | 90 ++++-- .../imageviewer/view/ScalableImage.common.kt | 5 +- .../kotlin/example/imageviewer/view/Toast.kt | 3 +- .../src/commonMain/resources/list_view.png | Bin 0 -> 5541 bytes .../kotlin/example/imageviewer/R.kt | 32 +-- .../imageviewer/utils/IOScope.desktop.kt | 5 + .../imageviewer/view/ImageViewer.desktop.kt | 117 ++++---- .../imageviewer/view/ScalaImage.desktop.kt | 3 +- .../example/imageviewer/ImageViewer.ios.kt | 17 +- .../example/imageviewer/utils/IOScope.ios.kt | 6 + .../example/imageviewer/view/addUserInput.kt | 3 +- .../gradle/wrapper/gradle-wrapper.jar | Bin 59203 -> 60756 bytes experimental/examples/visual-effects/gradlew | 263 +++++++++++------- .../examples/visual-effects/gradlew.bat | 14 +- 38 files changed, 908 insertions(+), 545 deletions(-) create mode 100644 experimental/examples/imageviewer/shared/src/androidMain/kotlin/example/imageviewer/utils/IOScope.android.kt create mode 100755 experimental/examples/imageviewer/shared/src/androidMain/res/values-de/strings.xml delete mode 100755 experimental/examples/imageviewer/shared/src/androidMain/res/values-ru/strings.xml create mode 100644 experimental/examples/imageviewer/shared/src/commonMain/kotlin/example/imageviewer/model/GalleryState.kt delete mode 100644 experimental/examples/imageviewer/shared/src/commonMain/kotlin/example/imageviewer/model/State.kt create mode 100644 experimental/examples/imageviewer/shared/src/commonMain/kotlin/example/imageviewer/utils/IOScope.kt create mode 100644 experimental/examples/imageviewer/shared/src/commonMain/resources/list_view.png create mode 100644 experimental/examples/imageviewer/shared/src/desktopMain/kotlin/example/imageviewer/utils/IOScope.desktop.kt create mode 100644 experimental/examples/imageviewer/shared/src/iosMain/kotlin/example/imageviewer/utils/IOScope.ios.kt diff --git a/experimental/examples/imageviewer/desktopApp/src/jvmMain/kotlin/example/imageviewer/Main.kt b/experimental/examples/imageviewer/desktopApp/src/jvmMain/kotlin/example/imageviewer/Main.kt index 96fe7556df..36fdf3914f 100644 --- a/experimental/examples/imageviewer/desktopApp/src/jvmMain/kotlin/example/imageviewer/Main.kt +++ b/experimental/examples/imageviewer/desktopApp/src/jvmMain/kotlin/example/imageviewer/Main.kt @@ -1,11 +1,8 @@ package example.imageviewer -import androidx.compose.material.MaterialTheme import androidx.compose.ui.window.application import example.imageviewer.view.ImageViewerDesktop fun main() = application { - MaterialTheme { - ImageViewerDesktop() - } + ImageViewerDesktop() } diff --git a/experimental/examples/imageviewer/gradle/wrapper/gradle-wrapper.jar b/experimental/examples/imageviewer/gradle/wrapper/gradle-wrapper.jar index 249e5832f090a2944b7473328c07c9755baa3196..943f0cbfa754578e88a3dae77fce6e3dea56edbf 100644 GIT binary patch delta 36524 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%0DGK?7GHTj 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 VwlMN}`%er%aGR6olj~j${vQ;P=LY}) diff --git a/experimental/examples/imageviewer/gradle/wrapper/gradle-wrapper.properties b/experimental/examples/imageviewer/gradle/wrapper/gradle-wrapper.properties index ae04661ee7..f398c33c4b 100755 --- a/experimental/examples/imageviewer/gradle/wrapper/gradle-wrapper.properties +++ b/experimental/examples/imageviewer/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.5.1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.6-bin.zip +networkTimeout=10000 zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/experimental/examples/imageviewer/gradlew b/experimental/examples/imageviewer/gradlew index a69d9cb6c2..65dcd68d65 100755 --- a/experimental/examples/imageviewer/gradlew +++ b/experimental/examples/imageviewer/gradlew @@ -55,7 +55,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -80,10 +80,10 @@ do esac done -APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit - -APP_NAME="Gradle" +# This is normally unused +# shellcheck disable=SC2034 APP_BASE_NAME=${0##*/} +APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' @@ -143,12 +143,16 @@ fi if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then case $MAX_FD in #( max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC3045 MAX_FD=$( ulimit -H -n ) || warn "Could not query maximum file descriptor limit" esac case $MAX_FD in #( '' | soft) :;; #( *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC3045 ulimit -n "$MAX_FD" || warn "Could not set maximum file descriptor limit to $MAX_FD" esac diff --git a/experimental/examples/imageviewer/gradlew.bat b/experimental/examples/imageviewer/gradlew.bat index f127cfd49d..93e3f59f13 100755 --- a/experimental/examples/imageviewer/gradlew.bat +++ b/experimental/examples/imageviewer/gradlew.bat @@ -26,6 +26,7 @@ if "%OS%"=="Windows_NT" setlocal set DIRNAME=%~dp0 if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% diff --git a/experimental/examples/imageviewer/iosApp/iosApp.xcodeproj/project.pbxproj b/experimental/examples/imageviewer/iosApp/iosApp.xcodeproj/project.pbxproj index 2d52838b5e..db611b6470 100644 --- a/experimental/examples/imageviewer/iosApp/iosApp.xcodeproj/project.pbxproj +++ b/experimental/examples/imageviewer/iosApp/iosApp.xcodeproj/project.pbxproj @@ -3,7 +3,7 @@ archiveVersion = 1; classes = { }; - objectVersion = 50; + objectVersion = 51; objects = { /* Begin PBXBuildFile section */ @@ -340,7 +340,7 @@ "@executable_path/Frameworks", ); PRODUCT_BUNDLE_IDENTIFIER = "org.jetbrains.Imageviewer${TEAM_ID}"; - PRODUCT_NAME = "Imageviewer"; + PRODUCT_NAME = Imageviewer; PROVISIONING_PROFILE_SPECIFIER = ""; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; @@ -364,7 +364,7 @@ "@executable_path/Frameworks", ); PRODUCT_BUNDLE_IDENTIFIER = "org.jetbrains.Imageviewer${TEAM_ID}"; - PRODUCT_NAME = "Imageviewer"; + PRODUCT_NAME = Imageviewer; PROVISIONING_PROFILE_SPECIFIER = ""; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; diff --git a/experimental/examples/imageviewer/shared/build.gradle.kts b/experimental/examples/imageviewer/shared/build.gradle.kts index 9d7a377757..d3960e4aa8 100755 --- a/experimental/examples/imageviewer/shared/build.gradle.kts +++ b/experimental/examples/imageviewer/shared/build.gradle.kts @@ -36,6 +36,8 @@ kotlin { implementation(compose.material) implementation("org.jetbrains.compose.components:components-resources:1.3.0-beta04-dev879") implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.4.1") + @OptIn(org.jetbrains.compose.ExperimentalComposeLibrary::class) + implementation(compose.material3) } } val androidMain by getting { diff --git a/experimental/examples/imageviewer/shared/src/androidMain/kotlin/example/imageviewer/utils/IOScope.android.kt b/experimental/examples/imageviewer/shared/src/androidMain/kotlin/example/imageviewer/utils/IOScope.android.kt new file mode 100644 index 0000000000..100d513e91 --- /dev/null +++ b/experimental/examples/imageviewer/shared/src/androidMain/kotlin/example/imageviewer/utils/IOScope.android.kt @@ -0,0 +1,7 @@ +package example.imageviewer.utils + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.rememberCoroutineScope +import kotlinx.coroutines.Dispatchers + +actual val ioDispatcher = Dispatchers.IO \ No newline at end of file diff --git a/experimental/examples/imageviewer/shared/src/androidMain/kotlin/example/imageviewer/view/ImageViewer.android.kt b/experimental/examples/imageviewer/shared/src/androidMain/kotlin/example/imageviewer/view/ImageViewer.android.kt index 366e5427ef..9bfadaaf5d 100755 --- a/experimental/examples/imageviewer/shared/src/androidMain/kotlin/example/imageviewer/view/ImageViewer.android.kt +++ b/experimental/examples/imageviewer/shared/src/androidMain/kotlin/example/imageviewer/view/ImageViewer.android.kt @@ -3,16 +3,18 @@ package example.imageviewer.view import android.content.Context import android.widget.Toast import androidx.compose.runtime.Composable -import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.graphics.ImageBitmap import androidx.compose.ui.platform.LocalContext -import example.imageviewer.* +import example.imageviewer.Dependencies +import example.imageviewer.ImageViewerCommon +import example.imageviewer.Localization +import example.imageviewer.Notification +import example.imageviewer.PopupNotification import example.imageviewer.core.BitmapFilter import example.imageviewer.core.FilterType import example.imageviewer.model.ContentRepository -import example.imageviewer.model.State import example.imageviewer.model.adapter import example.imageviewer.model.createNetworkRepository import example.imageviewer.model.filtration.BlurFilter @@ -20,21 +22,20 @@ import example.imageviewer.model.filtration.GrayScaleFilter import example.imageviewer.model.filtration.PixelFilter import example.imageviewer.shared.R import example.imageviewer.style.ImageViewerTheme -import io.ktor.client.* -import io.ktor.client.engine.okhttp.* +import example.imageviewer.toImageBitmap +import example.imageviewer.utils.ioDispatcher +import io.ktor.client.HttpClient +import io.ktor.client.engine.okhttp.OkHttp import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.GlobalScope -import kotlinx.coroutines.launch @Composable fun ImageViewerAndroid() { val context: Context = LocalContext.current - val ioScope = rememberCoroutineScope { Dispatchers.IO } + val ioScope = rememberCoroutineScope { ioDispatcher } val dependencies = remember(context, ioScope) { getDependencies(context, ioScope) } - val state = remember { mutableStateOf(State()) } ImageViewerTheme { - ImageViewerCommon(state, dependencies) + ImageViewerCommon(dependencies) } } @@ -70,9 +71,7 @@ private fun getDependencies(context: Context, ioScope: CoroutineScope) = object override val notification: Notification = object : PopupNotification(localization) { override fun showPopUpMessage(text: String) { - GlobalScope.launch(Dispatchers.Main) { - Toast.makeText(context, text, Toast.LENGTH_SHORT).show() - } + Toast.makeText(context, text, Toast.LENGTH_SHORT).show() } } } diff --git a/experimental/examples/imageviewer/shared/src/androidMain/kotlin/example/imageviewer/view/ScalableImage.android.kt b/experimental/examples/imageviewer/shared/src/androidMain/kotlin/example/imageviewer/view/ScalableImage.android.kt index fa88d1804b..775b837077 100644 --- a/experimental/examples/imageviewer/shared/src/androidMain/kotlin/example/imageviewer/view/ScalableImage.android.kt +++ b/experimental/examples/imageviewer/shared/src/androidMain/kotlin/example/imageviewer/view/ScalableImage.android.kt @@ -1,8 +1,7 @@ package example.imageviewer.view -import androidx.compose.runtime.MutableState import androidx.compose.ui.Modifier import example.imageviewer.model.ScalableState -actual fun Modifier.addUserInput(state: MutableState) = +actual fun Modifier.addUserInput(state: ScalableState) = addTouchUserInput(state) diff --git a/experimental/examples/imageviewer/shared/src/androidMain/res/values-de/strings.xml b/experimental/examples/imageviewer/shared/src/androidMain/res/values-de/strings.xml new file mode 100755 index 0000000000..134f6a332a --- /dev/null +++ b/experimental/examples/imageviewer/shared/src/androidMain/res/values-de/strings.xml @@ -0,0 +1,17 @@ + + + ImageViewer + Bilder werden geladen... + Bildverzeichnis ist leer. + Kein Internetzugriff. + Bildverzeichnis beschädigt oder leer. + Kann Bilder nicht aktualisieren. + Kann volles Bild nicht laden. + Dies ist das letzte Bild. + Dies ist das erste Bild. + Bild: + Abmessungen: + Pixel. + Zurück + Aktualisieren + diff --git a/experimental/examples/imageviewer/shared/src/androidMain/res/values-ru/strings.xml b/experimental/examples/imageviewer/shared/src/androidMain/res/values-ru/strings.xml deleted file mode 100755 index adb38dc804..0000000000 --- a/experimental/examples/imageviewer/shared/src/androidMain/res/values-ru/strings.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - ImageViewer - Загружаем изображения... - Репозиторий пуст. - Нет доступа в интернет. - Список изображений в репозитории пуст или имеет неверный формат. - Невозможно обновить изображения. - Невозможно загузить полное изображение. - Это последнее изображение. - Это первое изображение. - Изображение: - Размеры: - пикселей. - назад - \ No newline at end of file diff --git a/experimental/examples/imageviewer/shared/src/androidMain/res/values/strings.xml b/experimental/examples/imageviewer/shared/src/androidMain/res/values/strings.xml index cbde2d5a37..2f055b36cb 100755 --- a/experimental/examples/imageviewer/shared/src/androidMain/res/values/strings.xml +++ b/experimental/examples/imageviewer/shared/src/androidMain/res/values/strings.xml @@ -1,5 +1,5 @@ - ImageViewer + My Memories Loading images... Repository is empty. No internet access. @@ -12,4 +12,5 @@ Size: pixels. back + Refresh \ No newline at end of file diff --git a/experimental/examples/imageviewer/shared/src/commonMain/kotlin/example/imageviewer/ImageViewer.common.kt b/experimental/examples/imageviewer/shared/src/commonMain/kotlin/example/imageviewer/ImageViewer.common.kt index 292836ead9..07c45ce256 100644 --- a/experimental/examples/imageviewer/shared/src/commonMain/kotlin/example/imageviewer/ImageViewer.common.kt +++ b/experimental/examples/imageviewer/shared/src/commonMain/kotlin/example/imageviewer/ImageViewer.common.kt @@ -1,35 +1,72 @@ package example.imageviewer +import androidx.compose.animation.AnimatedVisibility +import androidx.compose.animation.fadeIn +import androidx.compose.animation.fadeOut +import androidx.compose.animation.slideInHorizontally +import androidx.compose.animation.slideOutHorizontally import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.material.Surface +import androidx.compose.material3.Surface import androidx.compose.runtime.Composable -import androidx.compose.runtime.MutableState +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.remember import androidx.compose.ui.Modifier -import example.imageviewer.model.* +import example.imageviewer.model.GalleryScreenState +import example.imageviewer.model.ScreenState +import example.imageviewer.model.bigUrl import example.imageviewer.view.FullscreenImage import example.imageviewer.view.MainScreen +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.emptyFlow + +enum class ExternalImageViewerEvent { + Foward, + Back +} @Composable -internal fun ImageViewerCommon(state: MutableState, dependencies: Dependencies) { - state.refresh(dependencies) +internal fun ImageViewerCommon( + dependencies: Dependencies, + externalEvents: Flow = emptyFlow() +) { + val galleryScreenState = remember { GalleryScreenState() } - Surface(modifier = Modifier.fillMaxSize()) { - when (state.value.screen) { - ScreenState.Miniatures -> { - MainScreen(state, dependencies) + LaunchedEffect(Unit) { + galleryScreenState.refresh(dependencies) + } + LaunchedEffect(Unit) { + externalEvents.collect { + when (it) { + ExternalImageViewerEvent.Foward -> galleryScreenState.nextImage() + ExternalImageViewerEvent.Back -> galleryScreenState.previousImage() } + } + } - ScreenState.FullScreen -> { - FullscreenImage( - picture = state.value.picture, - getImage = { dependencies.imageRepository.loadContent(it.bigUrl) }, - getFilter = { dependencies.getFilter(it) }, - localization = dependencies.localization, - back = { state.value = state.value.copy(screen = ScreenState.Miniatures) }, - nextImage = { state.nextImage() }, - previousImage = { state.previousImage() }, - ) - } + Surface(modifier = Modifier.fillMaxSize()) { + AnimatedVisibility( + galleryScreenState.screen == ScreenState.Miniatures, + enter = fadeIn(), + exit = fadeOut() + ) { + MainScreen(galleryScreenState, dependencies) + } + + AnimatedVisibility( + galleryScreenState.screen == ScreenState.FullScreen, + enter = slideInHorizontally { -it }, + exit = slideOutHorizontally { -it }) { + FullscreenImage( + picture = galleryScreenState.picture, + getImage = { dependencies.imageRepository.loadContent(it.bigUrl) }, + getFilter = { dependencies.getFilter(it) }, + localization = dependencies.localization, + back = { + galleryScreenState.screen = ScreenState.Miniatures + }, + nextImage = { galleryScreenState.nextImage() }, + previousImage = { galleryScreenState.previousImage() }, + ) } } } diff --git a/experimental/examples/imageviewer/shared/src/commonMain/kotlin/example/imageviewer/model/GalleryState.kt b/experimental/examples/imageviewer/shared/src/commonMain/kotlin/example/imageviewer/model/GalleryState.kt new file mode 100644 index 0000000000..aba589c4c6 --- /dev/null +++ b/experimental/examples/imageviewer/shared/src/commonMain/kotlin/example/imageviewer/model/GalleryState.kt @@ -0,0 +1,77 @@ +package example.imageviewer.model + +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateListOf +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.setValue +import androidx.compose.ui.graphics.ImageBitmap +import example.imageviewer.Dependencies +import io.ktor.client.request.get +import io.ktor.client.statement.bodyAsText +import kotlinx.coroutines.CancellationException +import kotlinx.coroutines.async +import kotlinx.coroutines.awaitAll +import kotlinx.coroutines.launch +import kotlinx.serialization.builtins.ListSerializer + +data class PictureWithThumbnail(val picture: Picture, val thumbnail: ImageBitmap) + +class GalleryScreenState { + var currentPictureIndex by mutableStateOf(0) + val picturesWithThumbnail = mutableStateListOf() + var screen by mutableStateOf(ScreenState.Miniatures) + val isContentReady get() = picturesWithThumbnail.isNotEmpty() + + val picture get(): Picture? = picturesWithThumbnail.getOrNull(currentPictureIndex)?.picture + + fun nextImage() { + currentPictureIndex = (currentPictureIndex + 1).mod(picturesWithThumbnail.lastIndex) + } + + fun previousImage() { + currentPictureIndex = (currentPictureIndex - 1).mod(picturesWithThumbnail.lastIndex) + } + + fun selectPicture(picture: Picture) { + currentPictureIndex = picturesWithThumbnail.indexOfFirst { it.picture == picture } + } + + fun toFullscreen(idx: Int = currentPictureIndex) { + currentPictureIndex = idx + screen = ScreenState.FullScreen + } + + fun refresh(dependencies: Dependencies) { + dependencies.ioScope.launch { + try { + val pictures = dependencies.json.decodeFromString( + ListSerializer(Picture.serializer()), + dependencies.httpClient.get(PICTURES_DATA_URL).bodyAsText() + ) + val miniatures = pictures + .map { picture -> + async { + picture to dependencies.imageRepository.loadContent(picture.smallUrl) + } + } + .awaitAll() + .map { (pic, bit) -> PictureWithThumbnail(pic, bit) } + + picturesWithThumbnail.clear() + picturesWithThumbnail.addAll(miniatures) + } catch (e: CancellationException) { + println("Rethrowing CancellationException with original cause") + // https://kotlinlang.org/docs/exception-handling.html#exceptions-aggregation + throw e + } catch (e: Exception) { + e.printStackTrace() + dependencies.notification.notifyNoInternet() + } + } + } +} + +sealed interface ScreenState { + object Miniatures : ScreenState + object FullScreen : ScreenState +} \ No newline at end of file diff --git a/experimental/examples/imageviewer/shared/src/commonMain/kotlin/example/imageviewer/model/Picture.kt b/experimental/examples/imageviewer/shared/src/commonMain/kotlin/example/imageviewer/model/Picture.kt index 7463f87381..134a5acb70 100644 --- a/experimental/examples/imageviewer/shared/src/commonMain/kotlin/example/imageviewer/model/Picture.kt +++ b/experimental/examples/imageviewer/shared/src/commonMain/kotlin/example/imageviewer/model/Picture.kt @@ -6,6 +6,25 @@ import kotlinx.serialization.Serializable data class Picture(val big: String, val small: String) fun getNameURL(url: String): String = url.substring(url.lastIndexOf('/') + 1, url.length) -val Picture.name get() = getNameURL(big) +val Picture.name: String get() { + val realName = getNameURL(big) + return mockNames.getOrElse(realName) { realName } +} val Picture.bigUrl get() = "$BASE_URL/$big" val Picture.smallUrl get() = "$BASE_URL/$small" + +val mockNames = mapOf( + "1.jpg" to "Gondolas", + "2.jpg" to "Winter Pier", + "3.jpg" to "Kitties outside", + "4.jpg" to "Heap of trees", + "5.jpg" to "Resilient Cacti", + "6.jpg" to "Swirls", + "7.jpg" to "Gradient Descent", + "8.jpg" to "Sleepy in Seattle", + "9.jpg" to "Lightful infrastructure", + "10.jpg" to "Compose Pathway", + "11.jpg" to "Rotary", + "12.jpg" to "Towering", + "13.jpg" to "Vasa" +) \ No newline at end of file diff --git a/experimental/examples/imageviewer/shared/src/commonMain/kotlin/example/imageviewer/model/ScalableState.kt b/experimental/examples/imageviewer/shared/src/commonMain/kotlin/example/imageviewer/model/ScalableState.kt index 6c9bf68752..96584a20c8 100644 --- a/experimental/examples/imageviewer/shared/src/commonMain/kotlin/example/imageviewer/model/ScalableState.kt +++ b/experimental/examples/imageviewer/shared/src/commonMain/kotlin/example/imageviewer/model/ScalableState.kt @@ -1,17 +1,18 @@ package example.imageviewer.model -import androidx.compose.runtime.MutableState +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.setValue import androidx.compose.ui.geometry.Offset import androidx.compose.ui.unit.IntOffset import androidx.compose.ui.unit.IntRect import androidx.compose.ui.unit.IntSize -data class ScalableState( - val imageSize: IntSize, - val boxSize: IntSize = IntSize(1, 1), - val offset: IntOffset = IntOffset.Zero, - val scale: Float = 1f -) +class ScalableState(val imageSize: IntSize) { + var boxSize by mutableStateOf(IntSize(1, 1)) + var offset by mutableStateOf(IntOffset.Zero) + var scale by mutableStateOf(1f) +} val ScalableState.visiblePart get() : IntRect { @@ -32,46 +33,46 @@ val ScalableState.visiblePart return IntRect(offset = offset, size = size) } -fun MutableState.changeBoxSize(size: IntSize) = modifyState { - copy(boxSize = size) - .updateOffsetLimits() +fun ScalableState.changeBoxSize(size: IntSize) { + boxSize = size + updateOffsetLimits() } -fun MutableState.setScale(scale: Float) = modifyState { - copy(scale = scale) - .updateOffsetLimits() +fun ScalableState.setScale(scale: Float) { + this.scale = scale } -fun MutableState.addScale(diff: Float) = modifyState { - if (scale + diff > MAX_SCALE) { - copy(scale = MAX_SCALE) +fun ScalableState.addScale(diff: Float) { + scale = if (scale + diff > MAX_SCALE) { + MAX_SCALE } else if (scale + diff < MIN_SCALE) { - copy(scale = MIN_SCALE) + MIN_SCALE } else { - copy(scale = scale + diff) - }.updateOffsetLimits() + scale + diff + } + updateOffsetLimits() } -fun MutableState.addDragAmount(diff: Offset) = modifyState { - copy(offset = offset - IntOffset((diff.x + 1).toInt(), (diff.y + 1).toInt())) - .updateOffsetLimits() +fun ScalableState.addDragAmount(diff: Offset) { + offset -= IntOffset((diff.x + 1).toInt(), (diff.y + 1).toInt()) + updateOffsetLimits() } -private fun ScalableState.updateOffsetLimits(): ScalableState { - var result = this +private fun ScalableState.updateOffsetLimits() { if (offset.x + visiblePart.width > imageSize.width) { - result = result.changeOffset(x = imageSize.width - visiblePart.width) + changeOffset(x = imageSize.width - visiblePart.width) } if (offset.y + visiblePart.height > imageSize.height) { - result = result.changeOffset(y = imageSize.height - visiblePart.height) + changeOffset(y = imageSize.height - visiblePart.height) } if (offset.x < 0) { - result = result.changeOffset(x = 0) + changeOffset(x = 0) } if (offset.y < 0) { - result = result.changeOffset(y = 0) + changeOffset(y = 0) } - return result } -private fun ScalableState.changeOffset(x: Int = offset.x, y: Int = offset.y) = copy(offset = IntOffset(x, y)) +private fun ScalableState.changeOffset(x: Int = offset.x, y: Int = offset.y) { + offset = IntOffset(x, y) +} diff --git a/experimental/examples/imageviewer/shared/src/commonMain/kotlin/example/imageviewer/model/State.kt b/experimental/examples/imageviewer/shared/src/commonMain/kotlin/example/imageviewer/model/State.kt deleted file mode 100644 index 593b830976..0000000000 --- a/experimental/examples/imageviewer/shared/src/commonMain/kotlin/example/imageviewer/model/State.kt +++ /dev/null @@ -1,85 +0,0 @@ -package example.imageviewer.model - -import androidx.compose.runtime.MutableState -import androidx.compose.ui.graphics.ImageBitmap -import example.imageviewer.Dependencies -import io.ktor.client.request.* -import io.ktor.client.statement.* -import kotlinx.coroutines.CancellationException -import kotlinx.coroutines.async -import kotlinx.coroutines.awaitAll -import kotlinx.coroutines.launch -import kotlinx.serialization.builtins.ListSerializer - -data class State( - val currentImageIndex: Int = 0, - val miniatures: Map = emptyMap(), - val pictures: List = emptyList(), - val screen: ScreenState = ScreenState.Miniatures -) - -sealed interface ScreenState { - object Miniatures : ScreenState - object FullScreen : ScreenState -} - -val State.isContentReady get() = pictures.isNotEmpty() -val State.picture get():Picture? = pictures.getOrNull(currentImageIndex) - -fun MutableState.modifyState(modification: T.() -> T) { - value = value.modification() -} - -fun MutableState.nextImage() = modifyState { - var newIndex = currentImageIndex + 1 - if (newIndex > pictures.lastIndex) { - newIndex = 0 - } - copy(currentImageIndex = newIndex) -} - -fun MutableState.previousImage() = modifyState { - var newIndex = currentImageIndex - 1 - if (newIndex < 0) { - newIndex = pictures.lastIndex - } - copy(currentImageIndex = newIndex) -} - -fun MutableState.refresh(dependencies: Dependencies) { - dependencies.ioScope.launch { - try { - val pictures = dependencies.json.decodeFromString( - ListSerializer(Picture.serializer()), - dependencies.httpClient.get(PICTURES_DATA_URL).bodyAsText() - ) - val miniatures = pictures.map { picture -> - async { - picture to dependencies.imageRepository.loadContent(picture.smallUrl) - } - }.awaitAll().toMap() - - modifyState { - copy(pictures = pictures, miniatures = miniatures) - } - } catch (e: CancellationException) { - println("Rethrowing CancellationException with original cause") - // https://kotlinlang.org/docs/exception-handling.html#exceptions-aggregation - throw e - } catch (e: Exception) { - e.printStackTrace() - dependencies.notification.notifyNoInternet() - } - } -} - -fun MutableState.setSelectedIndex(index: Int) = modifyState { - copy(currentImageIndex = index) -} - -fun MutableState.toFullscreen(index: Int = value.currentImageIndex) = modifyState { - copy( - currentImageIndex = index, - screen = ScreenState.FullScreen - ) -} diff --git a/experimental/examples/imageviewer/shared/src/commonMain/kotlin/example/imageviewer/style/Palette.kt b/experimental/examples/imageviewer/shared/src/commonMain/kotlin/example/imageviewer/style/Palette.kt index b63fafac57..6e8e568402 100755 --- a/experimental/examples/imageviewer/shared/src/commonMain/kotlin/example/imageviewer/style/Palette.kt +++ b/experimental/examples/imageviewer/shared/src/commonMain/kotlin/example/imageviewer/style/Palette.kt @@ -1,8 +1,9 @@ package example.imageviewer.style import androidx.compose.foundation.isSystemInDarkTheme -import androidx.compose.material.MaterialTheme +import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable +import androidx.compose.ui.graphics.Brush import androidx.compose.ui.graphics.Color object ImageviewerColors { @@ -18,6 +19,18 @@ object ImageviewerColors { val TranslucentWhite = Color(255, 255, 255, 20) val Transparent = Color.Transparent + val KotlinGradient0 = Color(0xFF7F52FF) + val KotlinGradient50 = Color(0xFFC811E2) + val KotlinGradient100 = Color(0xFFE54857) + + val kotlinHorizontalGradientBrush = Brush.horizontalGradient( + colors = listOf( + KotlinGradient0, + KotlinGradient50, + KotlinGradient100 + ) + ) + fun buttonBackground(isHover: Boolean) = if (isHover) TranslucentBlack else Transparent } @@ -25,15 +38,9 @@ object ImageviewerColors { internal fun ImageViewerTheme(content: @Composable () -> Unit) { isSystemInDarkTheme() // todo check and change colors MaterialTheme( - colors = MaterialTheme.colors.copy( - primary = ImageviewerColors.Foreground, - secondary = ImageviewerColors.LightGray, - background = ImageviewerColors.DarkGray, - surface = ImageviewerColors.Gray, - onPrimary = ImageviewerColors.Foreground, - onSecondary = Color.Black, - onBackground = ImageviewerColors.Foreground, - onSurface = ImageviewerColors.Foreground + colorScheme = MaterialTheme.colorScheme.copy( + background = Color(0xFF1B1B1B), + onBackground = Color(0xFFFFFFFF) ) ) { content() diff --git a/experimental/examples/imageviewer/shared/src/commonMain/kotlin/example/imageviewer/utils/IOScope.kt b/experimental/examples/imageviewer/shared/src/commonMain/kotlin/example/imageviewer/utils/IOScope.kt new file mode 100644 index 0000000000..1e31a01e01 --- /dev/null +++ b/experimental/examples/imageviewer/shared/src/commonMain/kotlin/example/imageviewer/utils/IOScope.kt @@ -0,0 +1,5 @@ +package example.imageviewer.utils + +import kotlinx.coroutines.CoroutineDispatcher + +expect val ioDispatcher: CoroutineDispatcher \ No newline at end of file diff --git a/experimental/examples/imageviewer/shared/src/commonMain/kotlin/example/imageviewer/view/FullscreenImage.kt b/experimental/examples/imageviewer/shared/src/commonMain/kotlin/example/imageviewer/view/FullscreenImage.kt index 4675c84a21..0f8efcb53d 100644 --- a/experimental/examples/imageviewer/shared/src/commonMain/kotlin/example/imageviewer/view/FullscreenImage.kt +++ b/experimental/examples/imageviewer/shared/src/commonMain/kotlin/example/imageviewer/view/FullscreenImage.kt @@ -5,15 +5,14 @@ import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.interaction.collectIsHoveredAsState import androidx.compose.foundation.layout.* import androidx.compose.foundation.shape.CircleShape -import androidx.compose.material.* import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.KeyboardArrowLeft import androidx.compose.material.icons.filled.KeyboardArrowRight +import androidx.compose.material3.* import androidx.compose.runtime.* import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip -import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.ImageBitmap import androidx.compose.ui.graphics.painter.BitmapPainter import androidx.compose.ui.input.key.* @@ -41,7 +40,8 @@ internal fun FullscreenImage( nextImage: () -> Unit, previousImage: () -> Unit, ) { - val filtersState = remember { mutableStateOf(emptySet()) } + val availableFilters = FilterType.values().toList() + var selectedFilters by remember { mutableStateOf(emptySet()) } val originalImageState = remember(picture) { mutableStateOf(null) } LaunchedEffect(picture) { @@ -51,11 +51,10 @@ internal fun FullscreenImage( } val originalImage = originalImageState.value - val filters = filtersState.value - val imageWithFilter = remember(originalImage, filters) { + val imageWithFilter = remember(originalImage, selectedFilters) { if (originalImage != null) { var result: ImageBitmap = originalImage - for (filter in filters.map { getFilter(it) }) { + for (filter in selectedFilters.map { getFilter(it) }) { result = filter.apply(result) } result @@ -63,17 +62,28 @@ internal fun FullscreenImage( null } } - - Box(Modifier.fillMaxSize().background(color = MaterialTheme.colors.background)) { + Box(Modifier.fillMaxSize().background(color = MaterialTheme.colorScheme.background)) { Column { - Toolbar(picture?.name ?: "", filtersState, localization, back) + FullscreenImageBar( + localization, + picture?.name, + back, + availableFilters, + selectedFilters, + onSelectFilter = { + if (it !in selectedFilters) { + selectedFilters += it + } else { + selectedFilters -= it + } + }) if (imageWithFilter != null) { val imageSize = IntSize(imageWithFilter.width, imageWithFilter.height) - val scalableState = remember(imageSize) { mutableStateOf(ScalableState(imageSize)) } - val visiblePartOfImage: IntRect = scalableState.value.visiblePart + val scalableState = remember(imageSize) { ScalableState(imageSize) } + val visiblePartOfImage: IntRect = scalableState.visiblePart Slider( modifier = Modifier.fillMaxWidth(), - value = scalableState.value.scale, + value = scalableState.scale, valueRange = MIN_SCALE..MAX_SCALE, onValueChange = { scalableState.setScale(it) }, ) @@ -99,84 +109,75 @@ internal fun FullscreenImage( } } - FloatingActionButton(modifier = Modifier.align(Alignment.BottomStart).padding(10.dp), onClick = previousImage) { + FloatingActionButton( + modifier = Modifier.align(Alignment.BottomStart).padding(10.dp), + containerColor = ImageviewerColors.KotlinGradient0, + onClick = previousImage + ) { Icon( imageVector = Icons.Filled.KeyboardArrowLeft, contentDescription = "Previous", - tint = MaterialTheme.colors.primary + tint = MaterialTheme.colorScheme.onBackground ) } - FloatingActionButton(modifier = Modifier.align(Alignment.BottomEnd).padding(10.dp), onClick = nextImage) { + FloatingActionButton( + modifier = Modifier.align(Alignment.BottomEnd).padding(10.dp), + containerColor = ImageviewerColors.KotlinGradient0, + onClick = nextImage + ) { Icon( imageVector = Icons.Filled.KeyboardArrowRight, contentDescription = "Next", - tint = MaterialTheme.colors.primary + tint = MaterialTheme.colorScheme.onBackground ) } } } -@OptIn(ExperimentalResourceApi::class) +@OptIn(ExperimentalMaterial3Api::class, ExperimentalResourceApi::class) @Composable -private fun Toolbar( - title: String, - filtersState: MutableState>, +private fun FullscreenImageBar( localization: Localization, - back: () -> Unit + pictureName: String?, + onBack: () -> Unit, + filters: List, + selectedFilters: Set, + onSelectFilter: (FilterType) -> Unit ) { - val backButtonInteractionSource = remember { MutableInteractionSource() } - val backButtonHover by backButtonInteractionSource.collectIsHoveredAsState() - Surface( - modifier = Modifier.height(44.dp) - ) { - Row(modifier = Modifier.padding(end = 30.dp)) { - Surface( - color = Color.Transparent, - modifier = Modifier.padding(start = 20.dp).align(Alignment.CenterVertically), - shape = CircleShape - ) { - Tooltip(localization.back) { - Image( - resource("back.png").rememberImageBitmap().orEmpty(), - contentDescription = null, - modifier = Modifier.size(38.dp) - .hoverable(backButtonInteractionSource) - .background(color = ImageviewerColors.buttonBackground(backButtonHover)) - .clickable { back() } - ) - } + TopAppBar( + modifier = Modifier.background(brush = ImageviewerColors.kotlinHorizontalGradientBrush), + colors = TopAppBarDefaults.smallTopAppBarColors( + containerColor = ImageviewerColors.Transparent, + titleContentColor = MaterialTheme.colorScheme.onBackground + ), + title = { + Text("${localization.picture} ${pictureName ?: "Unknown"}") + }, + navigationIcon = { + Tooltip(localization.back) { + Image( + resource("back.png").rememberImageBitmap().orEmpty(), + contentDescription = null, + modifier = Modifier.size(38.dp) + .clip(CircleShape) + .clickable { onBack() } + ) } - Text( - title, - maxLines = 1, - modifier = Modifier.padding(start = 30.dp).weight(1f) - .align(Alignment.CenterVertically), - style = MaterialTheme.typography.body1 - ) - - Surface( - color = Color(255, 255, 255, 40), - modifier = Modifier.size(154.dp, 38.dp) - .align(Alignment.CenterVertically), - shape = CircleShape - ) { - Row(Modifier.horizontalScroll(rememberScrollState())) { - for (type in FilterType.values()) { - FilterButton(filtersState.value.contains(type), type, onClick = { - filtersState.value = if (filtersState.value.contains(type)) { - filtersState.value - type - } else { - filtersState.value + type - } - }) - } - } + }, + actions = { + for (type in filters) { + FilterButton(active = type in selectedFilters, + type, + onClick = { + onSelectFilter(type) + }) } } - } + ) } + @Composable private fun FilterButton( active: Boolean, diff --git a/experimental/examples/imageviewer/shared/src/commonMain/kotlin/example/imageviewer/view/LoadingScreen.kt b/experimental/examples/imageviewer/shared/src/commonMain/kotlin/example/imageviewer/view/LoadingScreen.kt index f97df4b357..b8af7c22e9 100644 --- a/experimental/examples/imageviewer/shared/src/commonMain/kotlin/example/imageviewer/view/LoadingScreen.kt +++ b/experimental/examples/imageviewer/shared/src/commonMain/kotlin/example/imageviewer/view/LoadingScreen.kt @@ -3,10 +3,7 @@ package example.imageviewer.view import androidx.compose.foundation.background import androidx.compose.foundation.layout.* import androidx.compose.foundation.shape.CircleShape -import androidx.compose.material.CircularProgressIndicator -import androidx.compose.material.MaterialTheme -import androidx.compose.material.Surface -import androidx.compose.material.Text +import androidx.compose.material3.* import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier @@ -15,10 +12,10 @@ import androidx.compose.ui.unit.dp @Composable internal fun LoadingScreen(text: String = "") { Box( - modifier = Modifier.fillMaxSize().background(color = MaterialTheme.colors.background) + modifier = Modifier.fillMaxSize().background(color = MaterialTheme.colorScheme.background) ) { Box(modifier = Modifier.align(Alignment.Center)) { - Surface(elevation = 4.dp, shape = CircleShape) { + Surface(/*elevation = 4.dp, */shape = CircleShape) { CircularProgressIndicator( modifier = Modifier.size(50.dp).padding(3.dp, 3.dp, 4.dp, 4.dp) ) @@ -27,7 +24,7 @@ internal fun LoadingScreen(text: String = "") { Text( text = text, modifier = Modifier.align(Alignment.Center).offset(0.dp, 70.dp), - style = MaterialTheme.typography.body1 + style = MaterialTheme.typography.bodyMedium ) } } diff --git a/experimental/examples/imageviewer/shared/src/commonMain/kotlin/example/imageviewer/view/MainScreen.kt b/experimental/examples/imageviewer/shared/src/commonMain/kotlin/example/imageviewer/view/MainScreen.kt index 97763df099..86deae0d47 100755 --- a/experimental/examples/imageviewer/shared/src/commonMain/kotlin/example/imageviewer/view/MainScreen.kt +++ b/experimental/examples/imageviewer/shared/src/commonMain/kotlin/example/imageviewer/view/MainScreen.kt @@ -1,72 +1,229 @@ package example.imageviewer.view -import androidx.compose.foundation.* -import androidx.compose.foundation.layout.* +import androidx.compose.foundation.BorderStroke +import androidx.compose.foundation.Image +import androidx.compose.foundation.background +import androidx.compose.foundation.border +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.aspectRatio +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.lazy.grid.GridCells +import androidx.compose.foundation.lazy.grid.LazyVerticalGrid +import androidx.compose.foundation.lazy.grid.itemsIndexed import androidx.compose.foundation.shape.CircleShape -import androidx.compose.material.* -import androidx.compose.runtime.* +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Surface +import androidx.compose.material3.Text +import androidx.compose.material3.TopAppBar +import androidx.compose.material3.TopAppBarDefaults +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.ImageBitmap +import androidx.compose.ui.layout.ContentScale +import androidx.compose.ui.text.font.FontStyle +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp import example.imageviewer.Dependencies -import example.imageviewer.model.* -import example.imageviewer.model.State -import example.imageviewer.style.* +import example.imageviewer.model.GalleryScreenState +import example.imageviewer.model.Picture +import example.imageviewer.model.PictureWithThumbnail +import example.imageviewer.model.bigUrl +import example.imageviewer.style.ImageviewerColors +import example.imageviewer.style.ImageviewerColors.kotlinHorizontalGradientBrush import org.jetbrains.compose.resources.ExperimentalResourceApi import org.jetbrains.compose.resources.orEmpty import org.jetbrains.compose.resources.rememberImageBitmap import org.jetbrains.compose.resources.resource @Composable -internal fun MainScreen(state: MutableState, dependencies: Dependencies) { - Column { - TopContent(state, dependencies) - ScrollableColumn( - modifier = Modifier.fillMaxSize() - ) { - for (i in state.value.pictures.indices) { - val picture = state.value.pictures[i] - Miniature( - picture = picture, - image = state.value.miniatures[picture], - onClickSelect = { - state.setSelectedIndex(i) - }, - onClickFullScreen = { - state.toFullscreen(i) - }, - onClickInfo = { - dependencies.notification.notifyImageData(picture) - }, - ) - Spacer(modifier = Modifier.height(4.dp)) - } +internal fun GalleryHeader() { + Row( + horizontalArrangement = Arrangement.Center, + modifier = Modifier.padding(10.dp).fillMaxWidth() + ) { + Text( + "My Gallery", + fontSize = 25.sp, + color = MaterialTheme.colorScheme.onBackground, + fontStyle = FontStyle.Italic + ) + } +} + +enum class GalleryStyle { + SQUARES, + LIST +} + +fun GalleryStyle.toggled(): GalleryStyle { + return if (this == GalleryStyle.SQUARES) GalleryStyle.LIST else GalleryStyle.SQUARES +} + +@Composable +internal fun MainScreen(galleryScreenState: GalleryScreenState, dependencies: Dependencies) { + var galleryStyle by remember { mutableStateOf(GalleryStyle.SQUARES) } + Column(modifier = Modifier.background(MaterialTheme.colorScheme.background)) { + TitleBar( + onRefresh = { galleryScreenState.refresh(dependencies) }, + onToggle = { galleryStyle = galleryStyle.toggled() }, + dependencies + ) + if (needShowPreview()) { + PreviewImage( + getImage = { dependencies.imageRepository.loadContent(it.bigUrl) }, + picture = galleryScreenState.picture, onClick = { + galleryScreenState.toFullscreen() + }) + } + when (galleryStyle) { + GalleryStyle.SQUARES -> SquaresGalleryView( + galleryScreenState.picturesWithThumbnail, + galleryScreenState.picturesWithThumbnail.getOrNull(galleryScreenState.currentPictureIndex), + onSelect = { galleryScreenState.selectPicture(it) } + ) + + GalleryStyle.LIST -> ListGalleryView( + galleryScreenState.picturesWithThumbnail, + dependencies, + onSelect = { galleryScreenState.selectPicture(it) }, + onFullScreen = { galleryScreenState.toFullscreen(it) } + ) } } - if (!state.value.isContentReady) { + if (!galleryScreenState.isContentReady) { LoadingScreen(dependencies.localization.loading) } } @Composable -private fun TopContent(state: MutableState, dependencies: Dependencies) { - TitleBar(state, dependencies) - if (needShowPreview()) { - PreviewImage(state = state, getImage = { dependencies.imageRepository.loadContent(it.bigUrl) }) +private fun SquaresGalleryView( + images: List, + selectedImage: PictureWithThumbnail?, + onSelect: (Picture) -> Unit +) { + LazyVerticalGrid(columns = GridCells.Adaptive(minSize = 128.dp)) { + item { + MakeNewMemoryMiniature() + } + itemsIndexed(images) { idx, image -> + val isSelected = image == selectedImage + val (picture, bitmap) = image + SquareMiniature(bitmap, onClick = { onSelect(picture) }, isHighlighted = isSelected) + } } } -@OptIn(ExperimentalResourceApi::class) @Composable -private fun TitleBar(state: MutableState, dependencies: Dependencies) { +private fun MakeNewMemoryMiniature() { + Box( + Modifier.aspectRatio(1.0f) + .clickable { + // TODO: Open Camera! + }, contentAlignment = Alignment.Center + ) { + Text( + "+", + modifier = Modifier.fillMaxWidth(), + color = MaterialTheme.colorScheme.onBackground, + textAlign = TextAlign.Center, + fontSize = 50.sp + ) + } +} + +@Composable +private fun SquareMiniature(image: ImageBitmap, isHighlighted: Boolean, onClick: () -> Unit) { + Image( + bitmap = image, + contentDescription = null, + modifier = Modifier.aspectRatio(1.0f).clickable { onClick() }.then( + if (isHighlighted) { + Modifier.border(BorderStroke(5.dp, Color.White)) + } else Modifier + ), + contentScale = ContentScale.Crop + ) +} + +@Composable +private fun ListGalleryView( + pictures: List, + dependencies: Dependencies, + onSelect: (Picture) -> Unit, + onFullScreen: (Int) -> Unit +) { + GalleryHeader() + Spacer(modifier = Modifier.height(10.dp)) + ScrollableColumn( + modifier = Modifier.fillMaxSize() + ) { + for ((idx, picWithThumb) in pictures.withIndex()) { + val (picture, miniature) = picWithThumb + Miniature( + picture = picture, + image = miniature, + onClickSelect = { + onSelect(picture) + }, + onClickFullScreen = { + onFullScreen(idx) + }, + onClickInfo = { + dependencies.notification.notifyImageData(picture) + }, + ) + Spacer(modifier = Modifier.height(10.dp)) + } + } +} + +@OptIn(ExperimentalResourceApi::class, ExperimentalMaterial3Api::class) +@Composable +private fun TitleBar(onRefresh: () -> Unit, onToggle: () -> Unit, dependencies: Dependencies) { TopAppBar( - backgroundColor = MaterialTheme.colors.surface, + modifier = Modifier.background(brush = kotlinHorizontalGradientBrush), + colors = TopAppBarDefaults.smallTopAppBarColors( + containerColor = ImageviewerColors.Transparent, + titleContentColor = MaterialTheme.colorScheme.onBackground + ), title = { Row(Modifier.height(50.dp)) { Text( dependencies.localization.appName, - modifier = Modifier.weight(1f).align(Alignment.CenterVertically) + modifier = Modifier.weight(1f).align(Alignment.CenterVertically), + fontWeight = FontWeight.Bold ) + Surface( + color = ImageviewerColors.Transparent, + modifier = Modifier.padding(end = 20.dp).align(Alignment.CenterVertically), + shape = CircleShape + ) { + Image( + bitmap = resource("list_view.png").rememberImageBitmap().orEmpty(), + contentDescription = null, + modifier = Modifier.size(35.dp).clickable { + onToggle() + } + ) + } Surface( color = ImageviewerColors.Transparent, modifier = Modifier.padding(end = 20.dp).align(Alignment.CenterVertically), @@ -76,7 +233,7 @@ private fun TitleBar(state: MutableState, dependencies: Dependencies) { bitmap = resource("refresh.png").rememberImageBitmap().orEmpty(), contentDescription = null, modifier = Modifier.size(35.dp).clickable { - state.refresh(dependencies) + onRefresh() } ) } diff --git a/experimental/examples/imageviewer/shared/src/commonMain/kotlin/example/imageviewer/view/Miniature.kt b/experimental/examples/imageviewer/shared/src/commonMain/kotlin/example/imageviewer/view/Miniature.kt index 1e725cd8b0..a756581e48 100644 --- a/experimental/examples/imageviewer/shared/src/commonMain/kotlin/example/imageviewer/view/Miniature.kt +++ b/experimental/examples/imageviewer/shared/src/commonMain/kotlin/example/imageviewer/view/Miniature.kt @@ -1,24 +1,38 @@ package example.imageviewer.view +import androidx.compose.foundation.BorderStroke import androidx.compose.foundation.Image +import androidx.compose.foundation.border import androidx.compose.foundation.clickable -import androidx.compose.foundation.layout.* -import androidx.compose.material.* -import androidx.compose.runtime.* +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.Card +import androidx.compose.material3.CardDefaults +import androidx.compose.material3.CircularProgressIndicator +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.ImageBitmap -import androidx.compose.ui.graphics.RectangleShape import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.unit.dp -import example.imageviewer.model.* -import example.imageviewer.style.* +import example.imageviewer.model.Picture +import example.imageviewer.model.name import org.jetbrains.compose.resources.ExperimentalResourceApi import org.jetbrains.compose.resources.orEmpty import org.jetbrains.compose.resources.rememberImageBitmap import org.jetbrains.compose.resources.resource -@OptIn(ExperimentalResourceApi::class) +@OptIn(ExperimentalResourceApi::class, ExperimentalMaterial3Api::class) @Composable internal fun Miniature( picture: Picture, @@ -28,24 +42,28 @@ internal fun Miniature( onClickInfo: () -> Unit, ) { Card( - backgroundColor = ImageviewerColors.MiniatureColor, modifier = Modifier.padding(start = 10.dp, end = 10.dp).height(70.dp) - .fillMaxWidth() - .clickable { - onClickSelect() - }, - shape = RectangleShape, - elevation = 2.dp + .fillMaxWidth(), + onClick = { onClickSelect() }, + shape = RoundedCornerShape(200.dp), + border = BorderStroke(1.dp, Color.White), + colors = CardDefaults.cardColors( + containerColor = MaterialTheme.colorScheme.background, + contentColor = MaterialTheme.colorScheme.onBackground + ) + ) { Row(modifier = Modifier.padding(end = 30.dp)) { val modifier = Modifier.height(70.dp) - .width(90.dp) - .padding(start = 1.dp, top = 1.dp, end = 1.dp, bottom = 1.dp) + .width(70.dp) if (image != null) { Image( image, contentDescription = null, - modifier = modifier.clickable { onClickFullScreen() }, + modifier = modifier + .clip(CircleShape) + .border(BorderStroke(1.dp, Color.White), CircleShape) + .clickable { onClickFullScreen() }, contentScale = ContentScale.Crop ) } else { @@ -53,8 +71,9 @@ internal fun Miniature( } Text( text = picture.name, - modifier = Modifier.weight(1f).align(Alignment.CenterVertically).padding(start = 16.dp), - style = MaterialTheme.typography.body1 + modifier = Modifier.weight(1f).align(Alignment.CenterVertically) + .padding(start = 16.dp), + style = MaterialTheme.typography.titleLarge ) Image( diff --git a/experimental/examples/imageviewer/shared/src/commonMain/kotlin/example/imageviewer/view/PreviewImage.common.kt b/experimental/examples/imageviewer/shared/src/commonMain/kotlin/example/imageviewer/view/PreviewImage.common.kt index 85ebb73cc0..02c1453619 100644 --- a/experimental/examples/imageviewer/shared/src/commonMain/kotlin/example/imageviewer/view/PreviewImage.common.kt +++ b/experimental/examples/imageviewer/shared/src/commonMain/kotlin/example/imageviewer/view/PreviewImage.common.kt @@ -1,51 +1,81 @@ package example.imageviewer.view +import androidx.compose.animation.AnimatedContent +import androidx.compose.animation.ExperimentalAnimationApi +import androidx.compose.animation.slideInVertically +import androidx.compose.animation.slideOutVertically +import androidx.compose.animation.with import androidx.compose.foundation.Image +import androidx.compose.foundation.background import androidx.compose.foundation.clickable -import androidx.compose.foundation.layout.* -import androidx.compose.material.* -import androidx.compose.runtime.* +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.Card +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.ImageBitmap -import androidx.compose.ui.graphics.RectangleShape import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.unit.dp -import example.imageviewer.model.* -import example.imageviewer.model.State -import example.imageviewer.style.* +import example.imageviewer.model.Picture +import example.imageviewer.style.ImageviewerColors.kotlinHorizontalGradientBrush import org.jetbrains.compose.resources.ExperimentalResourceApi -import org.jetbrains.compose.resources.orEmpty -import org.jetbrains.compose.resources.rememberImageBitmap -import org.jetbrains.compose.resources.resource -@OptIn(ExperimentalResourceApi::class) +@OptIn(ExperimentalResourceApi::class, ExperimentalAnimationApi::class) @Composable -internal fun PreviewImage(state: MutableState, getImage: suspend (Picture) -> ImageBitmap) { - val pictures = state.value.pictures - val index = state.value.currentImageIndex - val imageState = remember(pictures, index) { mutableStateOf(null) } - LaunchedEffect(pictures, index) { - val picture = pictures.getOrNull(index) +internal fun PreviewImage( + picture: Picture?, + onClick: () -> Unit, + getImage: suspend (Picture) -> ImageBitmap +) { + var image by remember(picture) { mutableStateOf(null) } + LaunchedEffect(picture) { if (picture != null) { - imageState.value = getImage(picture) + image = getImage(picture) } } - val image = imageState.value + Spacer( + modifier = Modifier.height(5.dp).fillMaxWidth() + .background(brush = kotlinHorizontalGradientBrush) + ) Card( - backgroundColor = MaterialTheme.colors.background, modifier = Modifier.height(200.dp) - .clickable { state.toFullscreen() }, - shape = RectangleShape, - elevation = 1.dp + .background(brush = kotlinHorizontalGradientBrush) + .padding(10.dp) + .clickable { onClick() }, + shape = RoundedCornerShape(10.dp, 10.dp, 10.dp, 10.dp), ) { - Image( - bitmap = image ?: resource("empty.png").rememberImageBitmap().orEmpty(), - contentDescription = null, - modifier = Modifier - .fillMaxWidth().padding(start = 1.dp, top = 1.dp, end = 1.dp, bottom = 5.dp), - contentScale = ContentScale.Fit - ) + AnimatedContent( + targetState = image, + transitionSpec = { + slideInVertically(initialOffsetY = { it }) with slideOutVertically(targetOffsetY = { -it }) + } + ) { imageBitmap -> + if (imageBitmap != null) { + Image( + bitmap = imageBitmap, + contentDescription = null, + modifier = Modifier + .fillMaxSize() + , + contentScale = ContentScale.Crop + ) + } else { + Spacer( + modifier = Modifier.fillMaxSize() + .background(brush = kotlinHorizontalGradientBrush) + ) + } + } } } diff --git a/experimental/examples/imageviewer/shared/src/commonMain/kotlin/example/imageviewer/view/ScalableImage.common.kt b/experimental/examples/imageviewer/shared/src/commonMain/kotlin/example/imageviewer/view/ScalableImage.common.kt index c21a4d0339..88e2844a03 100644 --- a/experimental/examples/imageviewer/shared/src/commonMain/kotlin/example/imageviewer/view/ScalableImage.common.kt +++ b/experimental/examples/imageviewer/shared/src/commonMain/kotlin/example/imageviewer/view/ScalableImage.common.kt @@ -2,7 +2,6 @@ package example.imageviewer.view import androidx.compose.foundation.gestures.detectTapGestures import androidx.compose.foundation.gestures.detectTransformGestures -import androidx.compose.runtime.MutableState import androidx.compose.ui.Modifier import androidx.compose.ui.input.pointer.pointerInput import example.imageviewer.model.ScalableState @@ -10,9 +9,9 @@ import example.imageviewer.model.addDragAmount import example.imageviewer.model.addScale import example.imageviewer.model.setScale -expect fun Modifier.addUserInput(state: MutableState): Modifier +expect fun Modifier.addUserInput(state: ScalableState): Modifier -fun Modifier.addTouchUserInput(state: MutableState): Modifier = +fun Modifier.addTouchUserInput(state: ScalableState): Modifier = pointerInput(Unit) { detectTransformGestures { _, pan, zoom, _ -> state.addDragAmount(pan) diff --git a/experimental/examples/imageviewer/shared/src/commonMain/kotlin/example/imageviewer/view/Toast.kt b/experimental/examples/imageviewer/shared/src/commonMain/kotlin/example/imageviewer/view/Toast.kt index 6db9603f25..6a0ca3e85f 100755 --- a/experimental/examples/imageviewer/shared/src/commonMain/kotlin/example/imageviewer/view/Toast.kt +++ b/experimental/examples/imageviewer/shared/src/commonMain/kotlin/example/imageviewer/view/Toast.kt @@ -5,8 +5,7 @@ import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.material.Surface -import androidx.compose.material.Text +import androidx.compose.material3.* import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.MutableState diff --git a/experimental/examples/imageviewer/shared/src/commonMain/resources/list_view.png b/experimental/examples/imageviewer/shared/src/commonMain/resources/list_view.png new file mode 100644 index 0000000000000000000000000000000000000000..077ee0d0d6980ce3053e0954e95bdf0255c4ccee GIT binary patch literal 5541 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 + replay = 1, + onBufferOverflow = BufferOverflow.DROP_LATEST + ) + val events = _events.asSharedFlow() + + fun produceEvent(event: ExternalImageViewerEvent) { + _events.tryEmit(event) + } +} + @OptIn(ExperimentalComposeUiApi::class) @Composable fun ApplicationScope.ImageViewerDesktop() { val toastState = remember { mutableStateOf(ToastState.Hidden) } - val state = remember { mutableStateOf(State()) } - val ioScope: CoroutineScope = rememberCoroutineScope { Dispatchers.IO } + val ioScope: CoroutineScope = rememberCoroutineScope { ioDispatcher } val dependencies = remember(ioScope) { getDependencies(ioScope, toastState) } + val externalNavigationEventBus = remember { ExternalNavigationEventBus() } Window( onCloseRequest = ::exitApplication, @@ -47,11 +61,17 @@ fun ApplicationScope.ImageViewerDesktop() { size = getPreferredWindowSize(800, 1000) ), icon = painterResource("ic_imageviewer_round.png"), + // https://github.com/JetBrains/compose-jb/issues/2741 onKeyEvent = { if (it.type == KeyEventType.KeyUp) { when (it.key) { - Key.DirectionLeft -> state.previousImage() - Key.DirectionRight -> state.nextImage() + Key.DirectionLeft -> externalNavigationEventBus.produceEvent( + ExternalImageViewerEvent.Back + ) + + Key.DirectionRight -> externalNavigationEventBus.produceEvent( + ExternalImageViewerEvent.Foward + ) } } false @@ -62,8 +82,8 @@ fun ApplicationScope.ImageViewerDesktop() { modifier = Modifier.fillMaxSize() ) { ImageViewerCommon( - state = state, - dependencies = dependencies + dependencies = dependencies, + externalEvents = externalNavigationEventBus.events ) Toast(toastState) } @@ -71,50 +91,51 @@ fun ApplicationScope.ImageViewerDesktop() { } } -private fun getDependencies(ioScope: CoroutineScope, toastState: MutableState) = object : Dependencies { - override val ioScope: CoroutineScope = ioScope - override fun getFilter(type: FilterType): BitmapFilter = when (type) { - FilterType.GrayScale -> GrayScaleFilter() - FilterType.Pixel -> PixelFilter() - FilterType.Blur -> BlurFilter() - } +private fun getDependencies(ioScope: CoroutineScope, toastState: MutableState) = + object : Dependencies { + override val ioScope: CoroutineScope = ioScope + override fun getFilter(type: FilterType): BitmapFilter = when (type) { + FilterType.GrayScale -> GrayScaleFilter() + FilterType.Pixel -> PixelFilter() + FilterType.Blur -> BlurFilter() + } - override val localization: Localization = object : Localization { - override val back: String get() = ResString.back - override val appName: String get() = ResString.appName - override val loading: String get() = ResString.loading - override val repoInvalid: String get() = ResString.repoInvalid - override val repoEmpty: String get() = ResString.repoEmpty - override val noInternet: String get() = ResString.noInternet - override val loadImageUnavailable: String get() = ResString.loadImageUnavailable - override val lastImage: String get() = ResString.lastImage - override val firstImage: String get() = ResString.firstImage - override val picture: String get() = ResString.picture - override val size: String get() = ResString.size - override val pixels: String get() = ResString.pixels - override val refreshUnavailable: String get() = ResString.refreshUnavailable - } + override val localization: Localization = object : Localization { + override val back: String get() = ResString.back + override val appName: String get() = ResString.appName + override val loading: String get() = ResString.loading + override val repoInvalid: String get() = ResString.repoInvalid + override val repoEmpty: String get() = ResString.repoEmpty + override val noInternet: String get() = ResString.noInternet + override val loadImageUnavailable: String get() = ResString.loadImageUnavailable + override val lastImage: String get() = ResString.lastImage + override val firstImage: String get() = ResString.firstImage + override val picture: String get() = ResString.picture + override val size: String get() = ResString.size + override val pixels: String get() = ResString.pixels + override val refreshUnavailable: String get() = ResString.refreshUnavailable + } - override val httpClient: HttpClient = HttpClient(CIO) + override val httpClient: HttpClient = HttpClient(CIO) - val userHome: String? = System.getProperty("user.home") - override val imageRepository: ContentRepository = - createNetworkRepository(httpClient) - .run { - if (userHome != null) { - decorateWithDiskCache( - ioScope, - File(userHome).resolve("Pictures").resolve("imageviewer") - ) - } else { - this + val userHome: String? = System.getProperty("user.home") + override val imageRepository: ContentRepository = + createNetworkRepository(httpClient) + .run { + if (userHome != null) { + decorateWithDiskCache( + ioScope, + File(userHome).resolve("Pictures").resolve("imageviewer") + ) + } else { + this + } } - } - .adapter { it.toImageBitmap() } + .adapter { it.toImageBitmap() } - override val notification: Notification = object : PopupNotification(localization) { - override fun showPopUpMessage(text: String) { - toastState.value = ToastState.Shown(text) + override val notification: Notification = object : PopupNotification(localization) { + override fun showPopUpMessage(text: String) { + toastState.value = ToastState.Shown(text) + } } } -} diff --git a/experimental/examples/imageviewer/shared/src/desktopMain/kotlin/example/imageviewer/view/ScalaImage.desktop.kt b/experimental/examples/imageviewer/shared/src/desktopMain/kotlin/example/imageviewer/view/ScalaImage.desktop.kt index 23f214db17..b13adf4a34 100644 --- a/experimental/examples/imageviewer/shared/src/desktopMain/kotlin/example/imageviewer/view/ScalaImage.desktop.kt +++ b/experimental/examples/imageviewer/shared/src/desktopMain/kotlin/example/imageviewer/view/ScalaImage.desktop.kt @@ -1,7 +1,6 @@ package example.imageviewer.view import androidx.compose.foundation.gestures.detectDragGestures -import androidx.compose.runtime.MutableState import androidx.compose.ui.Modifier import androidx.compose.ui.geometry.Offset import androidx.compose.ui.input.pointer.PointerEventType @@ -10,7 +9,7 @@ import example.imageviewer.model.ScalableState import example.imageviewer.model.addDragAmount import example.imageviewer.model.addScale -actual fun Modifier.addUserInput(state: MutableState): Modifier = +actual fun Modifier.addUserInput(state: ScalableState): Modifier = pointerInput(Unit) { detectDragGestures { change, dragAmount: Offset -> state.addDragAmount(dragAmount) diff --git a/experimental/examples/imageviewer/shared/src/iosMain/kotlin/example/imageviewer/ImageViewer.ios.kt b/experimental/examples/imageviewer/shared/src/iosMain/kotlin/example/imageviewer/ImageViewer.ios.kt index 2f0eb17ab2..fd3a012438 100755 --- a/experimental/examples/imageviewer/shared/src/iosMain/kotlin/example/imageviewer/ImageViewer.ios.kt +++ b/experimental/examples/imageviewer/shared/src/iosMain/kotlin/example/imageviewer/ImageViewer.ios.kt @@ -2,31 +2,33 @@ package example.imageviewer import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.material.Surface -import androidx.compose.runtime.* +import androidx.compose.runtime.Composable +import androidx.compose.runtime.MutableState +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.ImageBitmap import example.imageviewer.core.BitmapFilter import example.imageviewer.core.FilterType import example.imageviewer.model.ContentRepository -import example.imageviewer.model.State import example.imageviewer.model.adapter import example.imageviewer.model.createNetworkRepository import example.imageviewer.model.filtration.BlurFilter import example.imageviewer.model.filtration.GrayScaleFilter import example.imageviewer.model.filtration.PixelFilter import example.imageviewer.style.ImageViewerTheme +import example.imageviewer.utils.ioDispatcher import example.imageviewer.view.Toast import example.imageviewer.view.ToastState -import io.ktor.client.* -import io.ktor.client.engine.darwin.* +import io.ktor.client.HttpClient +import io.ktor.client.engine.darwin.Darwin import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers @Composable internal fun ImageViewerIos() { val toastState = remember { mutableStateOf(ToastState.Hidden) } - val state = remember { mutableStateOf(State()) } - val ioScope: CoroutineScope = rememberCoroutineScope { Dispatchers.Default } + val ioScope: CoroutineScope = rememberCoroutineScope { ioDispatcher } val dependencies = remember(ioScope) { getDependencies(ioScope, toastState) } ImageViewerTheme { @@ -34,7 +36,6 @@ internal fun ImageViewerIos() { modifier = Modifier.fillMaxSize() ) { ImageViewerCommon( - state = state, dependencies = dependencies ) Toast(toastState) diff --git a/experimental/examples/imageviewer/shared/src/iosMain/kotlin/example/imageviewer/utils/IOScope.ios.kt b/experimental/examples/imageviewer/shared/src/iosMain/kotlin/example/imageviewer/utils/IOScope.ios.kt new file mode 100644 index 0000000000..27a2a034df --- /dev/null +++ b/experimental/examples/imageviewer/shared/src/iosMain/kotlin/example/imageviewer/utils/IOScope.ios.kt @@ -0,0 +1,6 @@ +package example.imageviewer.utils + +import kotlinx.coroutines.Dispatchers + +// https://github.com/Kotlin/kotlinx.coroutines/issues/3205 +actual val ioDispatcher = Dispatchers.Default \ No newline at end of file diff --git a/experimental/examples/imageviewer/shared/src/iosMain/kotlin/example/imageviewer/view/addUserInput.kt b/experimental/examples/imageviewer/shared/src/iosMain/kotlin/example/imageviewer/view/addUserInput.kt index 67889370aa..221df4bd14 100644 --- a/experimental/examples/imageviewer/shared/src/iosMain/kotlin/example/imageviewer/view/addUserInput.kt +++ b/experimental/examples/imageviewer/shared/src/iosMain/kotlin/example/imageviewer/view/addUserInput.kt @@ -1,8 +1,7 @@ package example.imageviewer.view -import androidx.compose.runtime.MutableState import androidx.compose.ui.Modifier import example.imageviewer.model.ScalableState -actual fun Modifier.addUserInput(state: MutableState): Modifier = +actual fun Modifier.addUserInput(state: ScalableState): Modifier = addTouchUserInput(state) diff --git a/experimental/examples/visual-effects/gradle/wrapper/gradle-wrapper.jar b/experimental/examples/visual-effects/gradle/wrapper/gradle-wrapper.jar index e708b1c023ec8b20f512888fe07c5bd3ff77bb8f..249e5832f090a2944b7473328c07c9755baa3196 100644 GIT binary patch delta 21931 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 delta 20228 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 \(.*\)$'` - if expr "$link" : '/.*' > /dev/null; then - PRG="$link" - else - PRG=`dirname "$PRG"`"/$link" - fi +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac done -SAVED="`pwd`" -cd "`dirname \"$PRG\"`/" >/dev/null -APP_HOME="`pwd -P`" -cd "$SAVED" >/dev/null + +APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit APP_NAME="Gradle" -APP_BASE_NAME=`basename "$0"` +APP_BASE_NAME=${0##*/} # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' # Use the maximum available, or set MAX_FD != -1 to use that value. -MAX_FD="maximum" +MAX_FD=maximum warn () { echo "$*" -} +} >&2 die () { echo echo "$*" echo exit 1 -} +} >&2 # OS specific support (must be 'true' or 'false'). cygwin=false msys=false darwin=false nonstop=false -case "`uname`" in - CYGWIN* ) - cygwin=true - ;; - Darwin* ) - darwin=true - ;; - MINGW* ) - msys=true - ;; - NONSTOP* ) - nonstop=true - ;; +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; esac CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar @@ -87,9 +121,9 @@ CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar if [ -n "$JAVA_HOME" ] ; then if [ -x "$JAVA_HOME/jre/sh/java" ] ; then # IBM's JDK on AIX uses strange locations for the executables - JAVACMD="$JAVA_HOME/jre/sh/java" + JAVACMD=$JAVA_HOME/jre/sh/java else - JAVACMD="$JAVA_HOME/bin/java" + JAVACMD=$JAVA_HOME/bin/java fi if [ ! -x "$JAVACMD" ] ; then die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME @@ -98,7 +132,7 @@ Please set the JAVA_HOME variable in your environment to match the location of your Java installation." fi else - JAVACMD="java" + JAVACMD=java which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. Please set the JAVA_HOME variable in your environment to match the @@ -106,80 +140,101 @@ location of your Java installation." fi # Increase the maximum file descriptors if we can. -if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then - MAX_FD_LIMIT=`ulimit -H -n` - if [ $? -eq 0 ] ; then - if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then - MAX_FD="$MAX_FD_LIMIT" - fi - ulimit -n $MAX_FD - if [ $? -ne 0 ] ; then - warn "Could not set maximum file descriptor limit: $MAX_FD" - fi - else - warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" - fi +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac fi -# For Darwin, add options to specify how the application appears in the dock -if $darwin; then - GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" -fi +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. # For Cygwin or MSYS, switch paths to Windows format before running java -if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then - APP_HOME=`cygpath --path --mixed "$APP_HOME"` - CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` - - JAVACMD=`cygpath --unix "$JAVACMD"` - - # We build the pattern for arguments to be converted via cygpath - ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` - SEP="" - for dir in $ROOTDIRSRAW ; do - ROOTDIRS="$ROOTDIRS$SEP$dir" - SEP="|" - done - OURCYGPATTERN="(^($ROOTDIRS))" - # Add a user-defined pattern to the cygpath arguments - if [ "$GRADLE_CYGPATTERN" != "" ] ; then - OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" - fi +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + # Now convert the arguments - kludge to limit ourselves to /bin/sh - i=0 - for arg in "$@" ; do - CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` - CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option - - if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition - eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` - else - eval `echo args$i`="\"$arg\"" + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) fi - i=`expr $i + 1` + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg done - case $i in - 0) set -- ;; - 1) set -- "$args0" ;; - 2) set -- "$args0" "$args1" ;; - 3) set -- "$args0" "$args1" "$args2" ;; - 4) set -- "$args0" "$args1" "$args2" "$args3" ;; - 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; - 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; - 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; - 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; - 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; - esac fi -# Escape application args -save () { - for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done - echo " " -} -APP_ARGS=`save "$@"` +# Collect all arguments for the java command; +# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of +# shell script including quotes and variable substitutions, so put them in +# double quotes to make sure that they get re-expanded; and +# * put everything else in single quotes, so that it's not re-expanded. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# -# Collect all arguments for the java command, following the shell quoting and substitution rules -eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' exec "$JAVACMD" "$@" diff --git a/experimental/examples/visual-effects/gradlew.bat b/experimental/examples/visual-effects/gradlew.bat index 107acd32c4..f127cfd49d 100644 --- a/experimental/examples/visual-effects/gradlew.bat +++ b/experimental/examples/visual-effects/gradlew.bat @@ -14,7 +14,7 @@ @rem limitations under the License. @rem -@if "%DEBUG%" == "" @echo off +@if "%DEBUG%"=="" @echo off @rem ########################################################################## @rem @rem Gradle startup script for Windows @@ -25,7 +25,7 @@ if "%OS%"=="Windows_NT" setlocal set DIRNAME=%~dp0 -if "%DIRNAME%" == "" set DIRNAME=. +if "%DIRNAME%"=="" set DIRNAME=. set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% @@ -40,7 +40,7 @@ if defined JAVA_HOME goto findJavaFromJavaHome set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto execute +if %ERRORLEVEL% equ 0 goto execute echo. echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. @@ -75,13 +75,15 @@ set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar :end @rem End local scope for the variables with windows NT shell -if "%ERRORLEVEL%"=="0" goto mainEnd +if %ERRORLEVEL% equ 0 goto mainEnd :fail rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of rem the _cmd.exe /c_ return code! -if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 -exit /b 1 +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% :mainEnd if "%OS%"=="Windows_NT" endlocal