Browse Source

Adopt ImageViewer to Compose 1.5.0-dev1114 and Kotlin 1.9.0 (#3400)

* ImageViewer. Add WindowsInsets.ime and update resources

* Update gradle properties

* Fix HorizontalPager usage

* Add ExperimentalForeignApi annotation

---------

Co-authored-by: dima.avdeev <dima.avdeev@jetbrains.com>
support/1.5.0-replace-warnings-with-errors
Ivan Matkov 11 months ago committed by GitHub
parent
commit
c09b344c03
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 3
      examples/imageviewer/README.md
  2. 4
      examples/imageviewer/gradle.properties
  3. 2
      examples/imageviewer/shared/src/androidMain/kotlin/example/imageviewer/platform.android.kt
  4. 2
      examples/imageviewer/shared/src/commonMain/kotlin/example/imageviewer/platform.common.kt
  5. 8
      examples/imageviewer/shared/src/commonMain/kotlin/example/imageviewer/view/GalleryScreen.kt
  6. 6
      examples/imageviewer/shared/src/commonMain/kotlin/example/imageviewer/view/TopLayout.kt
  7. 2
      examples/imageviewer/shared/src/desktopMain/kotlin/example/imageviewer/platform.desktop.kt
  8. 28
      examples/imageviewer/shared/src/iosMain/kotlin/example/imageviewer/platform.ios.kt
  9. 2
      examples/imageviewer/shared/src/iosMain/kotlin/example/imageviewer/storage/FileExtensions.kt
  10. 3
      examples/imageviewer/shared/src/iosMain/kotlin/example/imageviewer/storage/IosImageStorage.ios.kt
  11. 3
      examples/imageviewer/shared/src/iosMain/kotlin/example/imageviewer/view/CameraView.ios.kt
  12. 2
      examples/imageviewer/shared/src/iosMain/kotlin/example/imageviewer/view/LocationVisualizer.ios.kt

3
examples/imageviewer/README.md

@ -9,6 +9,9 @@ To setup the environment, please consult these [instructions](https://github.com
## How to run ## How to run
If you already runned this sample before, then you need to execute command at least once:
`/gradlew podInstall`
Choose a run configuration for an appropriate target in IDE and run it. Choose a run configuration for an appropriate target in IDE and run it.
![run-configurations.png](screenshots/run-configurations.png) ![run-configurations.png](screenshots/run-configurations.png)

4
examples/imageviewer/gradle.properties

@ -11,6 +11,6 @@ kotlin.mpp.androidSourceSetLayoutVersion=2
kotlin.native.useEmbeddableCompilerJar=true kotlin.native.useEmbeddableCompilerJar=true
# Enable kotlin/native experimental memory model # Enable kotlin/native experimental memory model
kotlin.native.binary.memoryModel=experimental kotlin.native.binary.memoryModel=experimental
kotlin.version=1.8.20 kotlin.version=1.9.0
agp.version=7.1.3 agp.version=7.1.3
compose.version=1.4.1 compose.version=1.5.0-dev1114

2
examples/imageviewer/shared/src/androidMain/kotlin/example/imageviewer/platform.android.kt

@ -10,8 +10,6 @@ import androidx.compose.ui.graphics.vector.ImageVector
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import java.util.UUID import java.util.UUID
actual fun Modifier.notchPadding(): Modifier = this.displayCutoutPadding().statusBarsPadding()
class AndroidStorableImage( class AndroidStorableImage(
val imageBitmap: ImageBitmap val imageBitmap: ImageBitmap
) )

2
examples/imageviewer/shared/src/commonMain/kotlin/example/imageviewer/platform.common.kt

@ -4,8 +4,6 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.graphics.vector.ImageVector
import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineDispatcher
expect fun Modifier.notchPadding(): Modifier
expect class PlatformStorableImage expect class PlatformStorableImage
expect fun createUUID(): String expect fun createUUID(): String

8
examples/imageviewer/shared/src/commonMain/kotlin/example/imageviewer/view/GalleryScreen.kt

@ -56,7 +56,11 @@ fun GalleryScreen(
val imageProvider = LocalImageProvider.current val imageProvider = LocalImageProvider.current
val viewScope = rememberCoroutineScope() val viewScope = rememberCoroutineScope()
val pagerState = rememberPagerState(initialPage = selectedPictureIndex.value) val pagerState = rememberPagerState(
initialPage = selectedPictureIndex.value,
initialPageOffsetFraction = 0f,
pageCount = { pictures.size },
)
LaunchedEffect(pagerState) { LaunchedEffect(pagerState) {
// Subscribe to page changes // Subscribe to page changes
snapshotFlow { pagerState.currentPage }.collect { page -> snapshotFlow { pagerState.currentPage }.collect { page ->
@ -116,7 +120,7 @@ fun GalleryScreen(
onClickPreviewPicture(pagerState.currentPage) onClickPreviewPicture(pagerState.currentPage)
} }
) { ) {
HorizontalPager(pictures.size, state = pagerState) { index -> HorizontalPager(state = pagerState) { index ->
val picture = pictures[index] val picture = pictures[index]
var image: ImageBitmap? by remember(picture) { mutableStateOf(null) } var image: ImageBitmap? by remember(picture) { mutableStateOf(null) }
LaunchedEffect(picture) { LaunchedEffect(picture) {

6
examples/imageviewer/shared/src/commonMain/kotlin/example/imageviewer/view/TopLayout.kt

@ -2,13 +2,15 @@ package example.imageviewer.view
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.systemBars
import androidx.compose.foundation.layout.windowInsetsPadding
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import example.imageviewer.notchPadding
@Composable @Composable
fun TopLayout( fun TopLayout(
@ -18,7 +20,7 @@ fun TopLayout(
Box( Box(
Modifier Modifier
.fillMaxWidth() .fillMaxWidth()
.notchPadding() .windowInsetsPadding(WindowInsets.systemBars)
.padding(12.dp) .padding(12.dp)
) { ) {
Row(Modifier.align(Alignment.CenterStart)) { Row(Modifier.align(Alignment.CenterStart)) {

2
examples/imageviewer/shared/src/desktopMain/kotlin/example/imageviewer/platform.desktop.kt

@ -10,8 +10,6 @@ import androidx.compose.ui.unit.dp
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import java.util.UUID import java.util.UUID
actual fun Modifier.notchPadding(): Modifier = Modifier.padding(top = 12.dp)
class DesktopStorableImage( class DesktopStorableImage(
val imageBitmap: ImageBitmap val imageBitmap: ImageBitmap
) )

28
examples/imageviewer/shared/src/iosMain/kotlin/example/imageviewer/platform.ios.kt

@ -1,46 +1,22 @@
package example.imageviewer package example.imageviewer
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.windowInsetsPadding
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.unit.Density
import androidx.compose.ui.unit.LayoutDirection
import example.imageviewer.icon.IconIosShare import example.imageviewer.icon.IconIosShare
import kotlinx.cinterop.useContents import kotlinx.cinterop.ExperimentalForeignApi
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.IO import kotlinx.coroutines.IO
import platform.CoreFoundation.CFUUIDCreate import platform.CoreFoundation.CFUUIDCreate
import platform.CoreFoundation.CFUUIDCreateString import platform.CoreFoundation.CFUUIDCreateString
import platform.Foundation.CFBridgingRelease import platform.Foundation.CFBridgingRelease
import platform.UIKit.UIApplication
import platform.UIKit.UIImage import platform.UIKit.UIImage
private val iosNotchInset = object : WindowInsets {
override fun getTop(density: Density): Int {
val safeAreaInsets = UIApplication.sharedApplication.keyWindow?.safeAreaInsets
return if (safeAreaInsets != null) {
val topInset = safeAreaInsets.useContents { this.top }
(topInset * density.density).toInt()
} else {
0
}
}
override fun getLeft(density: Density, layoutDirection: LayoutDirection): Int = 0
override fun getRight(density: Density, layoutDirection: LayoutDirection): Int = 0
override fun getBottom(density: Density): Int = 0
}
actual fun Modifier.notchPadding(): Modifier =
this.windowInsetsPadding(iosNotchInset)
class IosStorableImage( class IosStorableImage(
val rawValue: UIImage val rawValue: UIImage
) )
actual typealias PlatformStorableImage = IosStorableImage actual typealias PlatformStorableImage = IosStorableImage
@OptIn(ExperimentalForeignApi::class)
actual fun createUUID(): String = actual fun createUUID(): String =
CFBridgingRelease(CFUUIDCreateString(null, CFUUIDCreate(null))) as String CFBridgingRelease(CFUUIDCreateString(null, CFUUIDCreate(null))) as String

2
examples/imageviewer/shared/src/iosMain/kotlin/example/imageviewer/storage/FileExtensions.kt

@ -1,3 +1,5 @@
@file:OptIn(ExperimentalForeignApi::class)
package example.imageviewer.storage package example.imageviewer.storage
import kotlinx.cinterop.* import kotlinx.cinterop.*

3
examples/imageviewer/shared/src/iosMain/kotlin/example/imageviewer/storage/IosImageStorage.ios.kt

@ -7,6 +7,7 @@ import example.imageviewer.PlatformStorableImage
import example.imageviewer.model.PictureData import example.imageviewer.model.PictureData
import example.imageviewer.toImageBitmap import example.imageviewer.toImageBitmap
import kotlinx.cinterop.CValue import kotlinx.cinterop.CValue
import kotlinx.cinterop.ExperimentalForeignApi
import kotlinx.cinterop.useContents import kotlinx.cinterop.useContents
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
@ -122,6 +123,7 @@ class IosImageStorage(
} }
} }
@OptIn(ExperimentalForeignApi::class)
private fun UIImage.fitInto(px: Int): UIImage { private fun UIImage.fitInto(px: Int): UIImage {
val targetScale = maxOf( val targetScale = maxOf(
px.toFloat() / size.useContents { width }, px.toFloat() / size.useContents { width },
@ -131,6 +133,7 @@ private fun UIImage.fitInto(px: Int): UIImage {
return resize(newSize) return resize(newSize)
} }
@OptIn(ExperimentalForeignApi::class)
private fun UIImage.resize(targetSize: CValue<CGSize>): UIImage { private fun UIImage.resize(targetSize: CValue<CGSize>): UIImage {
val currentSize = this.size val currentSize = this.size
val widthRatio = targetSize.useContents { width } / currentSize.useContents { width } val widthRatio = targetSize.useContents { width } / currentSize.useContents { width }

3
examples/imageviewer/shared/src/iosMain/kotlin/example/imageviewer/view/CameraView.ios.kt

@ -18,6 +18,7 @@ import example.imageviewer.model.GpsPosition
import example.imageviewer.model.PictureData import example.imageviewer.model.PictureData
import example.imageviewer.model.createCameraPictureData import example.imageviewer.model.createCameraPictureData
import kotlinx.cinterop.CValue import kotlinx.cinterop.CValue
import kotlinx.cinterop.ExperimentalForeignApi
import kotlinx.cinterop.ObjCAction import kotlinx.cinterop.ObjCAction
import kotlinx.cinterop.useContents import kotlinx.cinterop.useContents
import platform.AVFoundation.* import platform.AVFoundation.*
@ -121,6 +122,7 @@ private fun BoxScope.AuthorizedCamera(
} }
} }
@OptIn(ExperimentalForeignApi::class)
@Composable @Composable
private fun BoxScope.RealDeviceCamera( private fun BoxScope.RealDeviceCamera(
camera: AVCaptureDevice, camera: AVCaptureDevice,
@ -272,6 +274,7 @@ private fun BoxScope.RealDeviceCamera(
} }
} }
@OptIn(ExperimentalForeignApi::class)
fun CLLocation.toGps() = fun CLLocation.toGps() =
GpsPosition( GpsPosition(
latitude = coordinate.useContents { latitude }, latitude = coordinate.useContents { latitude },

2
examples/imageviewer/shared/src/iosMain/kotlin/example/imageviewer/view/LocationVisualizer.ios.kt

@ -6,11 +6,13 @@ import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.interop.UIKitView import androidx.compose.ui.interop.UIKitView
import example.imageviewer.model.GpsPosition import example.imageviewer.model.GpsPosition
import kotlinx.cinterop.ExperimentalForeignApi
import platform.CoreLocation.CLLocationCoordinate2DMake import platform.CoreLocation.CLLocationCoordinate2DMake
import platform.MapKit.MKCoordinateRegionMakeWithDistance import platform.MapKit.MKCoordinateRegionMakeWithDistance
import platform.MapKit.MKMapView import platform.MapKit.MKMapView
import platform.MapKit.MKPointAnnotation import platform.MapKit.MKPointAnnotation
@OptIn(ExperimentalForeignApi::class)
@Composable @Composable
actual fun LocationVisualizer( actual fun LocationVisualizer(
modifier: Modifier, modifier: Modifier,

Loading…
Cancel
Save