diff --git a/examples/imageviewer/.gitignore b/examples/imageviewer/.gitignore
new file mode 100644
index 0000000000..3603efad10
--- /dev/null
+++ b/examples/imageviewer/.gitignore
@@ -0,0 +1,3 @@
+.DS_Store
+.gradle
+build
\ No newline at end of file
diff --git a/examples/imageviewer/.idea/codeStyles/Project.xml b/examples/imageviewer/.idea/codeStyles/Project.xml
new file mode 100755
index 0000000000..3cc336b934
--- /dev/null
+++ b/examples/imageviewer/.idea/codeStyles/Project.xml
@@ -0,0 +1,138 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ xmlns:android
+
+ ^$
+
+
+
+
+
+
+
+
+ xmlns:.*
+
+ ^$
+
+
+ BY_NAME
+
+
+
+
+
+
+ .*:id
+
+ http://schemas.android.com/apk/res/android
+
+
+
+
+
+
+
+
+ .*:name
+
+ http://schemas.android.com/apk/res/android
+
+
+
+
+
+
+
+
+ name
+
+ ^$
+
+
+
+
+
+
+
+
+ style
+
+ ^$
+
+
+
+
+
+
+
+
+ .*
+
+ ^$
+
+
+ BY_NAME
+
+
+
+
+
+
+ .*
+
+ http://schemas.android.com/apk/res/android
+
+
+ ANDROID_ATTRIBUTE_ORDER
+
+
+
+
+
+
+ .*
+
+ .*
+
+
+ BY_NAME
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/examples/imageviewer/.idea/codeStyles/codeStyleConfig.xml b/examples/imageviewer/.idea/codeStyles/codeStyleConfig.xml
new file mode 100755
index 0000000000..79ee123c2b
--- /dev/null
+++ b/examples/imageviewer/.idea/codeStyles/codeStyleConfig.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/examples/imageviewer/.idea/vcs.xml b/examples/imageviewer/.idea/vcs.xml
new file mode 100755
index 0000000000..35eb1ddfbb
--- /dev/null
+++ b/examples/imageviewer/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/examples/imageviewer/.run/desktop.run.xml b/examples/imageviewer/.run/desktop.run.xml
new file mode 100755
index 0000000000..d9335c1be5
--- /dev/null
+++ b/examples/imageviewer/.run/desktop.run.xml
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
+
+
+
+
+ true
+
+
+
\ No newline at end of file
diff --git a/examples/imageviewer/README.md b/examples/imageviewer/README.md
new file mode 100755
index 0000000000..f5c6e45ccf
--- /dev/null
+++ b/examples/imageviewer/README.md
@@ -0,0 +1 @@
+An example of image gallery for remote server image viewing, based on Jetpack Compose UI library (desktop).
diff --git a/examples/imageviewer/android/build.gradle.kts b/examples/imageviewer/android/build.gradle.kts
new file mode 100755
index 0000000000..f0a7bb5417
--- /dev/null
+++ b/examples/imageviewer/android/build.gradle.kts
@@ -0,0 +1,25 @@
+plugins {
+ id("com.android.application")
+ kotlin("android")
+ id("org.jetbrains.compose")
+}
+
+android {
+ compileSdkVersion(30)
+
+ defaultConfig {
+ minSdkVersion(21)
+ targetSdkVersion(30)
+ versionCode = 1
+ versionName = "1.0"
+ }
+
+ compileOptions {
+ sourceCompatibility = JavaVersion.VERSION_1_8
+ targetCompatibility = JavaVersion.VERSION_1_8
+ }
+}
+
+dependencies {
+ implementation(project(":common"))
+}
\ No newline at end of file
diff --git a/examples/imageviewer/android/src/main/AndroidManifest.xml b/examples/imageviewer/android/src/main/AndroidManifest.xml
new file mode 100755
index 0000000000..48862a9d07
--- /dev/null
+++ b/examples/imageviewer/android/src/main/AndroidManifest.xml
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/examples/imageviewer/android/src/main/java/imageviewer/MainActivity.kt b/examples/imageviewer/android/src/main/java/imageviewer/MainActivity.kt
new file mode 100755
index 0000000000..e71065eb70
--- /dev/null
+++ b/examples/imageviewer/android/src/main/java/imageviewer/MainActivity.kt
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package example.imageviewer
+
+import android.os.Bundle
+import androidx.appcompat.app.AppCompatActivity
+import androidx.compose.ui.platform.setContent
+import example.imageviewer.view.BuildAppUI
+import example.imageviewer.model.ContentState
+import example.imageviewer.model.ImageRepository
+
+class MainActivity : AppCompatActivity() {
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+
+ val content = ContentState.applyContent(
+ this@MainActivity,
+ "https://spvessel.com/iv/images/fetching.list"
+ )
+
+ setContent {
+ BuildAppUI(content)
+ }
+ }
+}
\ No newline at end of file
diff --git a/examples/imageviewer/build.gradle.kts b/examples/imageviewer/build.gradle.kts
new file mode 100755
index 0000000000..caeaa20b92
--- /dev/null
+++ b/examples/imageviewer/build.gradle.kts
@@ -0,0 +1,7 @@
+allprojects {
+ repositories {
+ google()
+ jcenter()
+ maven("https://packages.jetbrains.team/maven/p/ui/dev")
+ }
+}
\ No newline at end of file
diff --git a/examples/imageviewer/common/build.gradle.kts b/examples/imageviewer/common/build.gradle.kts
new file mode 100755
index 0000000000..d67363227f
--- /dev/null
+++ b/examples/imageviewer/common/build.gradle.kts
@@ -0,0 +1,56 @@
+import org.jetbrains.compose.compose
+
+plugins {
+ id("com.android.library")
+ kotlin("multiplatform")
+ id("org.jetbrains.compose")
+}
+
+kotlin {
+ android()
+ jvm("desktop")
+
+ sourceSets {
+ named("commonMain") {
+ dependencies {
+ api(compose.runtime)
+ api(compose.foundation)
+ api(compose.material)
+ }
+ }
+ named("androidMain") {
+ dependencies {
+ api("androidx.appcompat:appcompat:1.1.0")
+ api("androidx.core:core-ktx:1.3.1")
+ }
+ }
+ named("desktopMain") {
+ dependencies {
+ api(compose.desktop.common)
+ }
+ }
+ }
+}
+
+android {
+ compileSdkVersion(30)
+
+ defaultConfig {
+ minSdkVersion(21)
+ targetSdkVersion(30)
+ versionCode = 1
+ versionName = "1.0"
+ }
+
+ compileOptions {
+ sourceCompatibility = JavaVersion.VERSION_1_8
+ targetCompatibility = JavaVersion.VERSION_1_8
+ }
+
+ sourceSets {
+ named("main") {
+ manifest.srcFile("src/androidMain/AndroidManifest.xml")
+ res.srcDirs("src/androidMain/res")
+ }
+ }
+}
diff --git a/examples/imageviewer/common/src/androidMain/AndroidManifest.xml b/examples/imageviewer/common/src/androidMain/AndroidManifest.xml
new file mode 100755
index 0000000000..69c7572d0f
--- /dev/null
+++ b/examples/imageviewer/common/src/androidMain/AndroidManifest.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/examples/imageviewer/common/src/androidMain/kotlin/example/imageviewer/core/BitmapFilter.kt b/examples/imageviewer/common/src/androidMain/kotlin/example/imageviewer/core/BitmapFilter.kt
new file mode 100755
index 0000000000..1e14522811
--- /dev/null
+++ b/examples/imageviewer/common/src/androidMain/kotlin/example/imageviewer/core/BitmapFilter.kt
@@ -0,0 +1,7 @@
+package example.imageviewer.core
+
+import android.graphics.Bitmap
+
+interface BitmapFilter {
+ fun apply(bitmap: Bitmap) : Bitmap
+}
\ No newline at end of file
diff --git a/examples/imageviewer/common/src/androidMain/kotlin/example/imageviewer/model/ContentState.kt b/examples/imageviewer/common/src/androidMain/kotlin/example/imageviewer/model/ContentState.kt
new file mode 100755
index 0000000000..9a26915889
--- /dev/null
+++ b/examples/imageviewer/common/src/androidMain/kotlin/example/imageviewer/model/ContentState.kt
@@ -0,0 +1,375 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package example.imageviewer.model
+
+import android.content.Context
+import android.graphics.*
+import android.os.Handler
+import android.os.Looper
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.MutableState
+import androidx.compose.runtime.mutableStateOf
+import example.imageviewer.core.FilterType
+import example.imageviewer.model.filtration.FiltersManager
+import example.imageviewer.utils.clearCache
+import example.imageviewer.utils.isInternetAvailable
+import example.imageviewer.view.showPopUpMessage
+import example.imageviewer.R
+import java.io.File
+import java.util.concurrent.ExecutorService
+import java.util.concurrent.Executors
+
+
+object ContentState {
+
+ private lateinit var context: Context
+ private lateinit var repository: ImageRepository
+ private lateinit var uriRepository: String
+
+ fun applyContent(context: Context, uriRepository: String): ContentState {
+ if (this::uriRepository.isInitialized && this.uriRepository == uriRepository) {
+ return this
+ }
+
+ this.context = context
+ this.uriRepository = uriRepository
+ repository = ImageRepository(uriRepository)
+ appliedFilters = FiltersManager(context)
+ isAppUIReady.value = false
+
+ initData()
+
+ return this
+ }
+
+ private val executor: ExecutorService by lazy { Executors.newFixedThreadPool(2) }
+
+ private val handler: Handler by lazy { Handler(Looper.getMainLooper()) }
+
+ fun getContext(): Context {
+ return context
+ }
+
+ fun getOrientation(): Int {
+ return context.resources.configuration.orientation
+ }
+
+ private val isAppUIReady = mutableStateOf(false)
+ fun isContentReady(): Boolean {
+ return isAppUIReady.value
+ }
+
+ fun getString(id: Int): String {
+ return context.getString(id)
+ }
+
+ // drawable content
+ private val mainImage = mutableStateOf(Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888))
+ private val currentImageIndex = mutableStateOf(0)
+ private val miniatures = Miniatures()
+
+ fun getMiniatures(): List