Browse Source

ImageViewer Pager and icons (#2982)

pull/2995/head
dima.avdeev 1 year ago committed by GitHub
parent
commit
968af859c3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      examples/imageviewer/shared/build.gradle.kts
  2. 9
      examples/imageviewer/shared/src/androidMain/kotlin/example/imageviewer/platform.android.kt
  3. 98
      examples/imageviewer/shared/src/androidMain/kotlin/example/imageviewer/view/CameraView.android.kt
  4. 2
      examples/imageviewer/shared/src/commonMain/kotlin/example/imageviewer/ImageViewer.common.kt
  5. 24
      examples/imageviewer/shared/src/commonMain/kotlin/example/imageviewer/icon/IconArrowLeft.kt
  6. 53
      examples/imageviewer/shared/src/commonMain/kotlin/example/imageviewer/icon/IconAutoFixHigh.kt
  7. 39
      examples/imageviewer/shared/src/commonMain/kotlin/example/imageviewer/icon/IconIosShare.kt
  8. 32
      examples/imageviewer/shared/src/commonMain/kotlin/example/imageviewer/icon/IconMap.kt
  9. 28
      examples/imageviewer/shared/src/commonMain/kotlin/example/imageviewer/icon/IconMenu.kt
  10. 28
      examples/imageviewer/shared/src/commonMain/kotlin/example/imageviewer/icon/IconMoreVert.kt
  11. 36
      examples/imageviewer/shared/src/commonMain/kotlin/example/imageviewer/icon/IconPhotoCamera.kt
  12. 28
      examples/imageviewer/shared/src/commonMain/kotlin/example/imageviewer/icon/IconVisibility.kt
  13. 49
      examples/imageviewer/shared/src/commonMain/kotlin/example/imageviewer/view/CircularButton.kt
  14. 184
      examples/imageviewer/shared/src/commonMain/kotlin/example/imageviewer/view/GalleryScreen.kt
  15. 8
      examples/imageviewer/shared/src/commonMain/kotlin/example/imageviewer/view/MemoryScreen.kt
  16. 85
      examples/imageviewer/shared/src/commonMain/kotlin/example/imageviewer/view/PreviewImage.common.kt
  17. 27
      examples/imageviewer/shared/src/commonMain/kotlin/example/imageviewer/view/Thumbnail.kt
  18. BIN
      examples/imageviewer/shared/src/commonMain/resources/arrowleft.png
  19. BIN
      examples/imageviewer/shared/src/commonMain/resources/dots.png
  20. BIN
      examples/imageviewer/shared/src/commonMain/resources/eye.png
  21. BIN
      examples/imageviewer/shared/src/commonMain/resources/list_view.png
  22. BIN
      examples/imageviewer/shared/src/commonMain/resources/magic.png
  23. BIN
      examples/imageviewer/shared/src/commonMain/resources/plus.png
  24. BIN
      examples/imageviewer/shared/src/commonMain/resources/trash.png
  25. 8
      examples/imageviewer/shared/src/desktopMain/kotlin/example/imageviewer/view/CameraView.desktop.kt
  26. 5
      examples/imageviewer/shared/src/desktopMain/kotlin/example/imageviewer/view/ImageViewer.desktop.kt
  27. 41
      examples/imageviewer/shared/src/iosMain/kotlin/example/imageviewer/IosShareIcon.kt
  28. 3
      examples/imageviewer/shared/src/iosMain/kotlin/example/imageviewer/platform.ios.kt
  29. 39
      examples/imageviewer/shared/src/iosMain/kotlin/example/imageviewer/view/CameraView.ios.kt

2
examples/imageviewer/shared/build.gradle.kts

@ -35,7 +35,7 @@ kotlin {
implementation(compose.runtime)
implementation(compose.foundation)
implementation(compose.material)
//implementation(compose.materialIconsExtended) // TODO not working on iOS
//implementation(compose.materialIconsExtended) // TODO not working on iOS for now
@OptIn(org.jetbrains.compose.ExperimentalComposeLibrary::class)
implementation(compose.components.resources)
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.5.0")

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

@ -2,14 +2,13 @@ package example.imageviewer
import androidx.compose.foundation.layout.displayCutoutPadding
import androidx.compose.foundation.layout.statusBarsPadding
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.ImageBitmap
import example.imageviewer.model.PictureData
import kotlinx.coroutines.Dispatchers
import java.util.*
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Share
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.ImageBitmap
import androidx.compose.ui.graphics.vector.ImageVector
import kotlinx.coroutines.Dispatchers
import java.util.UUID
actual fun Modifier.notchPadding(): Modifier = displayCutoutPadding().statusBarsPadding()

98
examples/imageviewer/shared/src/androidMain/kotlin/example/imageviewer/view/CameraView.android.kt

@ -1,7 +1,6 @@
package example.imageviewer.view
import android.annotation.SuppressLint
import android.location.Location
import androidx.camera.core.CameraSelector
import androidx.camera.core.ImageCapture
import androidx.camera.core.ImageCapture.OnImageCapturedCallback
@ -11,10 +10,9 @@ import androidx.camera.lifecycle.ProcessCameraProvider
import androidx.camera.view.PreviewView
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.material.Button
import androidx.compose.material.CircularProgressIndicator
import androidx.compose.material.Text
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
@ -28,8 +26,8 @@ import com.google.accompanist.permissions.ExperimentalPermissionsApi
import com.google.accompanist.permissions.rememberMultiplePermissionsState
import com.google.android.gms.location.CurrentLocationRequest
import com.google.android.gms.location.LocationServices
import com.google.android.gms.tasks.Task
import example.imageviewer.*
import example.imageviewer.icon.IconPhotoCamera
import example.imageviewer.model.GpsPosition
import example.imageviewer.model.PictureData
import example.imageviewer.model.createCameraPictureData
@ -104,57 +102,57 @@ private fun CameraWithGrantedPermission(
}
val nameAndDescription = createNewPhotoNameAndDescription()
var capturePhotoStarted by remember { mutableStateOf(false) }
Box(contentAlignment = Alignment.BottomCenter, modifier = modifier) {
Box(modifier = modifier) {
AndroidView({ previewView }, modifier = Modifier.fillMaxSize())
Button(
CircularButton(
imageVector = IconPhotoCamera,
modifier = Modifier.align(Alignment.BottomCenter).padding(36.dp),
enabled = !capturePhotoStarted,
onClick = {
fun addLocationInfoAndReturnResult(imageBitmap: ImageBitmap) {
fun sendToStorage(gpsPosition: GpsPosition) {
onCapture(
createCameraPictureData(
name = nameAndDescription.name,
description = nameAndDescription.description,
gps = gpsPosition
),
AndroidStorableImage(imageBitmap)
)
capturePhotoStarted = false
}
LocationServices.getFusedLocationProviderClient(context)
.getCurrentLocation(CurrentLocationRequest.Builder().build(), null)
.apply {
addOnSuccessListener {
sendToStorage(GpsPosition(it.latitude, it.longitude))
}
addOnFailureListener {
sendToStorage(GpsPosition(0.0, 0.0))
}
}
) {
fun addLocationInfoAndReturnResult(imageBitmap: ImageBitmap) {
fun sendToStorage(gpsPosition: GpsPosition) {
onCapture(
createCameraPictureData(
name = nameAndDescription.name,
description = nameAndDescription.description,
gps = gpsPosition
),
AndroidStorableImage(imageBitmap)
)
capturePhotoStarted = false
}
capturePhotoStarted = true
imageCapture.takePicture(executor, object : OnImageCapturedCallback() {
override fun onCaptureSuccess(image: ImageProxy) {
val byteArray: ByteArray = image.planes[0].buffer.toByteArray()
val imageBitmap = byteArray.toImageBitmap()
image.close()
addLocationInfoAndReturnResult(imageBitmap)
}
})
viewScope.launch {
// TODO: There is a known issue with Android emulator
// https://partnerissuetracker.corp.google.com/issues/161034252
// After 5 seconds delay, let's assume that the bug appears and publish a prepared photo
delay(5000)
if (capturePhotoStarted) {
addLocationInfoAndReturnResult(
resource("android-emulator-photo.jpg").readBytes().toImageBitmap()
)
LocationServices.getFusedLocationProviderClient(context)
.getCurrentLocation(CurrentLocationRequest.Builder().build(), null)
.apply {
addOnSuccessListener {
sendToStorage(GpsPosition(it.latitude, it.longitude))
}
addOnFailureListener {
sendToStorage(GpsPosition(0.0, 0.0))
}
}
}
capturePhotoStarted = true
imageCapture.takePicture(executor, object : OnImageCapturedCallback() {
override fun onCaptureSuccess(image: ImageProxy) {
val byteArray: ByteArray = image.planes[0].buffer.toByteArray()
val imageBitmap = byteArray.toImageBitmap()
image.close()
addLocationInfoAndReturnResult(imageBitmap)
}
}) {
Text(LocalLocalization.current.takePhoto, color = Color.White)
})
viewScope.launch {
// TODO: There is a known issue with Android emulator
// https://partnerissuetracker.corp.google.com/issues/161034252
// After 5 seconds delay, let's assume that the bug appears and publish a prepared photo
delay(5000)
if (capturePhotoStarted) {
addLocationInfoAndReturnResult(
resource("android-emulator-photo.jpg").readBytes().toImageBitmap()
)
}
}
}
if (capturePhotoStarted) {
CircularProgressIndicator(

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

@ -33,7 +33,7 @@ internal fun ImageViewerCommon(
internal fun ImageViewerWithProvidedDependencies(
pictures: SnapshotStateList<PictureData>
) {
val selectedPictureIndex: MutableState<Int> = mutableStateOf(0)
val selectedPictureIndex = remember { mutableStateOf(0) }
val navigationStack = remember { NavigationStack<Page>(GalleryPage()) }
val externalEvents = LocalInternalEvents.current
LaunchedEffect(Unit) {

24
examples/imageviewer/shared/src/commonMain/kotlin/example/imageviewer/icon/IconArrowLeft.kt

@ -0,0 +1,24 @@
package example.imageviewer.icon
import androidx.compose.material.icons.materialIcon
import androidx.compose.material.icons.materialPath
val IconCustomArrowBack = materialIcon("Filled.CustomArrowBack") {
val startY = 12f
val startX = 1f
val arrowWidth = 8f
val arrowHeight = 14f
val lineWidth = 14f
val lineHeight = 2f
materialPath {
moveTo(startX, startY)
lineToRelative(arrowWidth, arrowHeight / 2)
verticalLineToRelative(-arrowHeight)
close()
moveTo(startX + arrowWidth, startY + lineHeight / 2)
verticalLineToRelative(-lineHeight)
horizontalLineToRelative(lineWidth)
verticalLineToRelative(lineHeight)
close()
}
}

53
examples/imageviewer/shared/src/commonMain/kotlin/example/imageviewer/icon/IconAutoFixHigh.kt

@ -0,0 +1,53 @@
package example.imageviewer.icon
import androidx.compose.material.icons.materialIcon
import androidx.compose.material.icons.materialPath
// TODO Copied from "material:material-icons-extended", because this artifact is not working on iOS for now
val IconAutoFixHigh = materialIcon(name = "Filled.AutoFixHigh") {
materialPath {
moveTo(7.5f, 5.6f)
lineTo(10.0f, 7.0f)
lineTo(8.6f, 4.5f)
lineTo(10.0f, 2.0f)
lineTo(7.5f, 3.4f)
lineTo(5.0f, 2.0f)
lineToRelative(1.4f, 2.5f)
lineTo(5.0f, 7.0f)
close()
moveTo(19.5f, 15.4f)
lineTo(17.0f, 14.0f)
lineToRelative(1.4f, 2.5f)
lineTo(17.0f, 19.0f)
lineToRelative(2.5f, -1.4f)
lineTo(22.0f, 19.0f)
lineToRelative(-1.4f, -2.5f)
lineTo(22.0f, 14.0f)
close()
moveTo(22.0f, 2.0f)
lineToRelative(-2.5f, 1.4f)
lineTo(17.0f, 2.0f)
lineToRelative(1.4f, 2.5f)
lineTo(17.0f, 7.0f)
lineToRelative(2.5f, -1.4f)
lineTo(22.0f, 7.0f)
lineToRelative(-1.4f, -2.5f)
close()
moveTo(14.37f, 7.29f)
curveToRelative(-0.39f, -0.39f, -1.02f, -0.39f, -1.41f, 0.0f)
lineTo(1.29f, 18.96f)
curveToRelative(-0.39f, 0.39f, -0.39f, 1.02f, 0.0f, 1.41f)
lineToRelative(2.34f, 2.34f)
curveToRelative(0.39f, 0.39f, 1.02f, 0.39f, 1.41f, 0.0f)
lineTo(16.7f, 11.05f)
curveToRelative(0.39f, -0.39f, 0.39f, -1.02f, 0.0f, -1.41f)
lineToRelative(-2.33f, -2.35f)
close()
moveTo(13.34f, 12.78f)
lineToRelative(-2.12f, -2.12f)
lineToRelative(2.44f, -2.44f)
lineToRelative(2.12f, 2.12f)
lineToRelative(-2.44f, 2.44f)
close()
}
}

39
examples/imageviewer/shared/src/commonMain/kotlin/example/imageviewer/icon/IconIosShare.kt

@ -0,0 +1,39 @@
package example.imageviewer.icon
import androidx.compose.material.icons.materialIcon
import androidx.compose.material.icons.materialPath
// TODO Copied from "material:material-icons-extended", because this artifact is not working on iOS for now
val IconIosShare = materialIcon(name = "Filled.IosShare") {
materialPath {
moveTo(16.0f, 5.0f)
lineToRelative(-1.42f, 1.42f)
lineToRelative(-1.59f, -1.59f)
lineTo(12.99f, 16.0f)
horizontalLineToRelative(-1.98f)
lineTo(11.01f, 4.83f)
lineTo(9.42f, 6.42f)
lineTo(8.0f, 5.0f)
lineToRelative(4.0f, -4.0f)
lineToRelative(4.0f, 4.0f)
close()
moveTo(20.0f, 10.0f)
verticalLineToRelative(11.0f)
curveToRelative(0.0f, 1.1f, -0.9f, 2.0f, -2.0f, 2.0f)
lineTo(6.0f, 23.0f)
curveToRelative(-1.11f, 0.0f, -2.0f, -0.9f, -2.0f, -2.0f)
lineTo(4.0f, 10.0f)
curveToRelative(0.0f, -1.11f, 0.89f, -2.0f, 2.0f, -2.0f)
horizontalLineToRelative(3.0f)
verticalLineToRelative(2.0f)
lineTo(6.0f, 10.0f)
verticalLineToRelative(11.0f)
horizontalLineToRelative(12.0f)
lineTo(18.0f, 10.0f)
horizontalLineToRelative(-3.0f)
lineTo(15.0f, 8.0f)
horizontalLineToRelative(3.0f)
curveToRelative(1.1f, 0.0f, 2.0f, 0.89f, 2.0f, 2.0f)
close()
}
}

32
examples/imageviewer/shared/src/commonMain/kotlin/example/imageviewer/icon/IconMap.kt

@ -0,0 +1,32 @@
package example.imageviewer.icon
import androidx.compose.material.icons.materialIcon
import androidx.compose.material.icons.materialPath
// TODO Copied from "material:material-icons-extended", because this artifact is not working on iOS for now
val IconMap = materialIcon(name = "Filled.Map") {
materialPath {
moveTo(20.5f, 3.0f)
lineToRelative(-0.16f, 0.03f)
lineTo(15.0f, 5.1f)
lineTo(9.0f, 3.0f)
lineTo(3.36f, 4.9f)
curveToRelative(-0.21f, 0.07f, -0.36f, 0.25f, -0.36f, 0.48f)
verticalLineTo(20.5f)
curveToRelative(0.0f, 0.28f, 0.22f, 0.5f, 0.5f, 0.5f)
lineToRelative(0.16f, -0.03f)
lineTo(9.0f, 18.9f)
lineToRelative(6.0f, 2.1f)
lineToRelative(5.64f, -1.9f)
curveToRelative(0.21f, -0.07f, 0.36f, -0.25f, 0.36f, -0.48f)
verticalLineTo(3.5f)
curveToRelative(0.0f, -0.28f, -0.22f, -0.5f, -0.5f, -0.5f)
close()
moveTo(15.0f, 19.0f)
lineToRelative(-6.0f, -2.11f)
verticalLineTo(5.0f)
lineToRelative(6.0f, 2.11f)
verticalLineTo(19.0f)
close()
}
}

28
examples/imageviewer/shared/src/commonMain/kotlin/example/imageviewer/icon/IconMenu.kt

@ -0,0 +1,28 @@
package example.imageviewer.icon
import androidx.compose.material.icons.materialIcon
import androidx.compose.material.icons.materialPath
// TODO Copied from "material:material-icons-extended", because this artifact is not working on iOS for now
val IconMenu = materialIcon(name = "Filled.Menu") {
materialPath {
moveTo(3.0f, 18.0f)
horizontalLineToRelative(18.0f)
verticalLineToRelative(-2.0f)
lineTo(3.0f, 16.0f)
verticalLineToRelative(2.0f)
close()
moveTo(3.0f, 13.0f)
horizontalLineToRelative(18.0f)
verticalLineToRelative(-2.0f)
lineTo(3.0f, 11.0f)
verticalLineToRelative(2.0f)
close()
moveTo(3.0f, 6.0f)
verticalLineToRelative(2.0f)
horizontalLineToRelative(18.0f)
lineTo(21.0f, 6.0f)
lineTo(3.0f, 6.0f)
close()
}
}

28
examples/imageviewer/shared/src/commonMain/kotlin/example/imageviewer/icon/IconMoreVert.kt

@ -0,0 +1,28 @@
package example.imageviewer.icon
import androidx.compose.material.icons.materialIcon
import androidx.compose.material.icons.materialPath
// TODO Copied from "material:material-icons-extended", because this artifact is not working on iOS for now
val IconMoreVert = materialIcon(name = "Filled.MoreVert") {
materialPath {
moveTo(12.0f, 8.0f)
curveToRelative(1.1f, 0.0f, 2.0f, -0.9f, 2.0f, -2.0f)
reflectiveCurveToRelative(-0.9f, -2.0f, -2.0f, -2.0f)
reflectiveCurveToRelative(-2.0f, 0.9f, -2.0f, 2.0f)
reflectiveCurveToRelative(0.9f, 2.0f, 2.0f, 2.0f)
close()
moveTo(12.0f, 10.0f)
curveToRelative(-1.1f, 0.0f, -2.0f, 0.9f, -2.0f, 2.0f)
reflectiveCurveToRelative(0.9f, 2.0f, 2.0f, 2.0f)
reflectiveCurveToRelative(2.0f, -0.9f, 2.0f, -2.0f)
reflectiveCurveToRelative(-0.9f, -2.0f, -2.0f, -2.0f)
close()
moveTo(12.0f, 16.0f)
curveToRelative(-1.1f, 0.0f, -2.0f, 0.9f, -2.0f, 2.0f)
reflectiveCurveToRelative(0.9f, 2.0f, 2.0f, 2.0f)
reflectiveCurveToRelative(2.0f, -0.9f, 2.0f, -2.0f)
reflectiveCurveToRelative(-0.9f, -2.0f, -2.0f, -2.0f)
close()
}
}

36
examples/imageviewer/shared/src/commonMain/kotlin/example/imageviewer/icon/IconPhotoCamera.kt

@ -0,0 +1,36 @@
package example.imageviewer.icon
import androidx.compose.material.icons.materialIcon
import androidx.compose.material.icons.materialPath
// TODO Copied from "material:material-icons-extended", because this artifact is not working on iOS for now
val IconPhotoCamera = materialIcon(name = "Filled.PhotoCamera") {
materialPath {
moveTo(12.0f, 12.0f)
moveToRelative(-3.2f, 0.0f)
arcToRelative(3.2f, 3.2f, 0.0f, true, true, 6.4f, 0.0f)
arcToRelative(3.2f, 3.2f, 0.0f, true, true, -6.4f, 0.0f)
}
materialPath {
moveTo(9.0f, 2.0f)
lineTo(7.17f, 4.0f)
lineTo(4.0f, 4.0f)
curveToRelative(-1.1f, 0.0f, -2.0f, 0.9f, -2.0f, 2.0f)
verticalLineToRelative(12.0f)
curveToRelative(0.0f, 1.1f, 0.9f, 2.0f, 2.0f, 2.0f)
horizontalLineToRelative(16.0f)
curveToRelative(1.1f, 0.0f, 2.0f, -0.9f, 2.0f, -2.0f)
lineTo(22.0f, 6.0f)
curveToRelative(0.0f, -1.1f, -0.9f, -2.0f, -2.0f, -2.0f)
horizontalLineToRelative(-3.17f)
lineTo(15.0f, 2.0f)
lineTo(9.0f, 2.0f)
close()
moveTo(12.0f, 17.0f)
curveToRelative(-2.76f, 0.0f, -5.0f, -2.24f, -5.0f, -5.0f)
reflectiveCurveToRelative(2.24f, -5.0f, 5.0f, -5.0f)
reflectiveCurveToRelative(5.0f, 2.24f, 5.0f, 5.0f)
reflectiveCurveToRelative(-2.24f, 5.0f, -5.0f, 5.0f)
close()
}
}

28
examples/imageviewer/shared/src/commonMain/kotlin/example/imageviewer/icon/IconVisibility.kt

@ -0,0 +1,28 @@
package example.imageviewer.icon
import androidx.compose.material.icons.materialIcon
import androidx.compose.material.icons.materialPath
// TODO Copied from "material:material-icons-extended", because this artifact is not working on iOS for now
val IconVisibility = materialIcon(name = "Filled.Visibility") {
materialPath {
moveTo(12.0f, 4.5f)
curveTo(7.0f, 4.5f, 2.73f, 7.61f, 1.0f, 12.0f)
curveToRelative(1.73f, 4.39f, 6.0f, 7.5f, 11.0f, 7.5f)
reflectiveCurveToRelative(9.27f, -3.11f, 11.0f, -7.5f)
curveToRelative(-1.73f, -4.39f, -6.0f, -7.5f, -11.0f, -7.5f)
close()
moveTo(12.0f, 17.0f)
curveToRelative(-2.76f, 0.0f, -5.0f, -2.24f, -5.0f, -5.0f)
reflectiveCurveToRelative(2.24f, -5.0f, 5.0f, -5.0f)
reflectiveCurveToRelative(5.0f, 2.24f, 5.0f, 5.0f)
reflectiveCurveToRelative(-2.24f, 5.0f, -5.0f, 5.0f)
close()
moveTo(12.0f, 9.0f)
curveToRelative(-1.66f, 0.0f, -3.0f, 1.34f, -3.0f, 3.0f)
reflectiveCurveToRelative(1.34f, 3.0f, 3.0f, 3.0f)
reflectiveCurveToRelative(3.0f, -1.34f, 3.0f, -3.0f)
reflectiveCurveToRelative(-1.34f, -3.0f, -3.0f, -3.0f)
close()
}
}

49
examples/imageviewer/shared/src/commonMain/kotlin/example/imageviewer/view/CircularButton.kt

@ -1,46 +1,67 @@
package example.imageviewer.view
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material.Icon
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.painter.Painter
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.unit.dp
import example.imageviewer.LocalLocalization
import example.imageviewer.icon.IconCustomArrowBack
import example.imageviewer.style.ImageviewerColors
import org.jetbrains.compose.resources.ExperimentalResourceApi
import org.jetbrains.compose.resources.painterResource
@Composable
internal fun CircularButton(
image: Painter,
content: @Composable () -> Unit,
modifier: Modifier = Modifier,
enabled: Boolean,
onClick: () -> Unit,
) {
Box(
modifier.size(54.dp).clip(CircleShape).background(ImageviewerColors.uiLightBlack)
.clickable { onClick() }, contentAlignment = Alignment.Center
modifier
.size(60.dp)
.clip(CircleShape)
.background(ImageviewerColors.uiLightBlack)
.run {
if (enabled) {
clickable { onClick() }
} else this
},
contentAlignment = Alignment.Center,
) {
Image(
image,
contentDescription = null,
modifier = Modifier.size(20.dp)
)
content()
}
}
@OptIn(ExperimentalResourceApi::class)
@Composable
internal fun CircularButton(
imageVector: ImageVector,
modifier: Modifier = Modifier,
enabled: Boolean = true,
onClick: () -> Unit,
) {
CircularButton(
modifier = modifier,
content = {
Icon(imageVector, null, Modifier.size(34.dp), Color.White)
},
enabled = enabled,
onClick = onClick
)
}
@Composable
internal fun BackButton(onClick: () -> Unit) {
Tooltip(LocalLocalization.current.back) {
CircularButton(
painterResource("arrowleft.png"),
imageVector = IconCustomArrowBack,
onClick = onClick
)
}

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

@ -1,7 +1,12 @@
@file:OptIn(ExperimentalResourceApi::class)
package example.imageviewer.view
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.core.AnimationConstants
import androidx.compose.animation.core.LinearOutSlowInEasing
import androidx.compose.animation.core.tween
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
@ -9,26 +14,38 @@ import androidx.compose.foundation.layout.*
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.pager.HorizontalPager
import androidx.compose.foundation.pager.PagerState
import androidx.compose.foundation.pager.rememberPagerState
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material.Icon
import androidx.compose.material.MaterialTheme
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Add
import androidx.compose.runtime.*
import androidx.compose.runtime.snapshots.SnapshotStateList
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.graphicsLayer
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.unit.dp
import example.imageviewer.*
import example.imageviewer.icon.IconMenu
import example.imageviewer.icon.IconVisibility
import example.imageviewer.model.*
import example.imageviewer.style.ImageviewerColors
import org.jetbrains.compose.resources.ExperimentalResourceApi
import org.jetbrains.compose.resources.painterResource
import kotlinx.coroutines.launch
import kotlin.math.absoluteValue
enum class GalleryStyle {
SQUARES,
LIST
}
@OptIn(ExperimentalResourceApi::class)
@OptIn(ExperimentalFoundationApi::class)
@Composable
internal fun GalleryScreen(
pictures: SnapshotStateList<PictureData>,
@ -36,22 +53,45 @@ internal fun GalleryScreen(
onClickPreviewPicture: (PictureData) -> Unit,
onMakeNewMemory: () -> Unit
) {
val imageProvider = LocalImageProvider.current
val viewScope = rememberCoroutineScope()
val pagerState = rememberPagerState(initialPage = selectedPictureIndex.value)
LaunchedEffect(pagerState) {
// Subscribe to page changes
snapshotFlow { pagerState.currentPage }.collect { page ->
selectedPictureIndex.value = page
}
}
fun nextImage() {
selectedPictureIndex.value =
(selectedPictureIndex.value + 1).mod(pictures.size)
viewScope.launch {
pagerState.animateScrollToPage(
(pagerState.currentPage + 1).mod(pictures.size)
)
}
}
fun previousImage() {
selectedPictureIndex.value =
(selectedPictureIndex.value - 1).mod(pictures.size)
viewScope.launch {
pagerState.animateScrollToPage(
(pagerState.currentPage - 1).mod(pictures.size)
)
}
}
fun selectPicture(picture: PictureData) {
selectedPictureIndex.value = pictures.indexOfFirst { it == picture }
fun selectPicture(index: Int) {
viewScope.launch {
pagerState.animateScrollToPage(
index,
animationSpec = tween(
easing = LinearOutSlowInEasing,
durationMillis = AnimationConstants.DefaultDurationMillis * 2
)
)
}
}
val picture = pictures.getOrNull(selectedPictureIndex.value)
var galleryStyle by remember { mutableStateOf(GalleryStyle.SQUARES) }
val externalEvents = LocalInternalEvents.current
LaunchedEffect(Unit) {
@ -63,20 +103,43 @@ internal fun GalleryScreen(
}
}
}
Column(modifier = Modifier.background(MaterialTheme.colors.background)) {
Box {
picture?.let {
PreviewImage(
picture = it, onClick = {
onClickPreviewPicture(it)
Box(
Modifier.fillMaxWidth().height(393.dp)
.background(Color.Black),
contentAlignment = Alignment.Center
) {
Box(
Modifier.fillMaxSize()
.clickable {
onClickPreviewPicture(pictures[pagerState.currentPage])
}
) {
HorizontalPager(pictures.size, state = pagerState) { idx ->
val picture = pictures[idx]
var image: ImageBitmap? by remember(picture) { mutableStateOf(null) }
LaunchedEffect(picture) {
image = imageProvider.getImage(picture)
}
if (image != null) {
Box(Modifier.fillMaxSize().animatePageChanges(pagerState, idx)) {
Image(
bitmap = image!!,
contentDescription = null,
modifier = Modifier.fillMaxSize(),
contentScale = ContentScale.Crop
)
MemoryTextOverlay(picture)
}
}
}
)
}
}
TopLayout(
alignLeftContent = {},
alignRightContent = {
CircularButton(painterResource("list_view.png")) {
CircularButton(imageVector = IconMenu) {
galleryStyle = when (galleryStyle) {
GalleryStyle.SQUARES -> GalleryStyle.LIST
GalleryStyle.LIST -> GalleryStyle.SQUARES
@ -89,7 +152,7 @@ internal fun GalleryScreen(
when (galleryStyle) {
GalleryStyle.SQUARES -> SquaresGalleryView(
images = pictures,
selectedImage = picture,
pagerState = pagerState,
onSelect = { selectPicture(it) },
)
@ -100,7 +163,7 @@ internal fun GalleryScreen(
)
}
CircularButton(
image = painterResource("plus.png"),
Icons.Filled.Add,
modifier = Modifier.align(Alignment.BottomCenter).padding(36.dp),
onClick = onMakeNewMemory,
)
@ -108,11 +171,12 @@ internal fun GalleryScreen(
}
}
@OptIn(ExperimentalFoundationApi::class)
@Composable
private fun SquaresGalleryView(
images: List<PictureData>,
selectedImage: PictureData?,
onSelect: (PictureData) -> Unit,
pagerState: PagerState,
onSelect: (Int) -> Unit,
) {
LazyVerticalGrid(
modifier = Modifier.padding(top = 4.dp),
@ -121,17 +185,15 @@ private fun SquaresGalleryView(
horizontalArrangement = Arrangement.spacedBy(1.dp)
) {
itemsIndexed(images) { idx, picture ->
val isSelected = picture == selectedImage
SquareThumbnail(
picture = picture,
onClick = { onSelect(picture) },
isHighlighted = isSelected
onClick = { onSelect(idx) },
isHighlighted = pagerState.targetPage == idx
)
}
}
}
@OptIn(ExperimentalResourceApi::class)
@Composable
internal fun SquareThumbnail(
picture: PictureData,
@ -139,8 +201,7 @@ internal fun SquareThumbnail(
onClick: () -> Unit
) {
Box(
Modifier.aspectRatio(1.0f).clickable(onClick = onClick),
contentAlignment = Alignment.BottomEnd
Modifier.aspectRatio(1.0f).clickable(onClick = onClick)
) {
Tooltip(picture.name) {
ThumbnailImage(
@ -148,27 +209,33 @@ internal fun SquareThumbnail(
picture = picture,
)
}
if (isHighlighted) {
Box(Modifier.fillMaxSize().background(ImageviewerColors.uiLightBlack))
Box(
Modifier
.padding(end = 4.dp, bottom = 4.dp)
.clip(CircleShape)
.width(32.dp)
.background(ImageviewerColors.uiLightBlack)
.aspectRatio(1.0f)
.clickable {
onClick()
},
contentAlignment = Alignment.Center
) {
Image(
painter = painterResource("eye.png"),
contentDescription = null,
modifier = Modifier
.width(17.dp)
.height(17.dp),
)
val tween = tween<Float>(
durationMillis = AnimationConstants.DefaultDurationMillis * 3,
delayMillis = 100,
easing = LinearOutSlowInEasing,
)
AnimatedVisibility(isHighlighted, enter = fadeIn(tween), exit = fadeOut(tween)) {
Box(Modifier.fillMaxSize().background(ImageviewerColors.uiLightBlack)) {
Box(
Modifier
.align(Alignment.BottomEnd)
.padding(end = 4.dp, bottom = 4.dp)
.clip(CircleShape)
.width(32.dp)
.background(ImageviewerColors.uiLightBlack)
.aspectRatio(1.0f)
.clickable {
onClick()
},
contentAlignment = Alignment.Center
) {
Icon(
imageVector = IconVisibility,
contentDescription = null,
modifier = Modifier.size(17.dp),
tint = Color.White,
)
}
}
}
}
@ -177,7 +244,7 @@ internal fun SquareThumbnail(
@Composable
private fun ListGalleryView(
pictures: List<PictureData>,
onSelect: (PictureData) -> Unit,
onSelect: (Int) -> Unit,
onFullScreen: (PictureData) -> Unit,
) {
val notification = LocalNotification.current
@ -189,7 +256,7 @@ private fun ListGalleryView(
Thumbnail(
picture = p.value,
onClickSelect = {
onSelect(p.value)
onSelect(p.index)
},
onClickFullScreen = {
onFullScreen(p.value)
@ -202,3 +269,14 @@ private fun ListGalleryView(
}
}
}
@OptIn(ExperimentalFoundationApi::class)
private fun Modifier.animatePageChanges(pagerState: PagerState, index: Int) =
graphicsLayer {
val x = (pagerState.currentPage - index + pagerState.currentPageOffsetFraction) * 2
alpha = 1f - (x.absoluteValue * 0.7f).coerceIn(0f, 0.7f)
val scale = 1f - (x.absoluteValue * 0.4f).coerceIn(0f, 0.4f)
scaleX = scale
scaleY = scale
rotationY = x * 15f
}

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

@ -32,6 +32,7 @@ import androidx.compose.ui.unit.sp
import example.imageviewer.LocalImageProvider
import example.imageviewer.LocalSharePicture
import example.imageviewer.filter.getPlatformContext
import example.imageviewer.icon.IconAutoFixHigh
import example.imageviewer.isShareFeatureSupported
import example.imageviewer.model.*
import example.imageviewer.shareIcon
@ -182,9 +183,12 @@ private fun MemoryHeader(bitmap: ImageBitmap, picture: PictureData, onClick: ()
@Composable
internal fun BoxScope.MagicButtonOverlay(onClick: () -> Unit) {
Column(
modifier = Modifier.align(Alignment.BottomEnd).padding(end = 12.dp, bottom = 16.dp)
modifier = Modifier.align(Alignment.BottomEnd).padding(12.dp)
) {
CircularButton(painterResource("magic.png"), onClick = onClick)
CircularButton(
imageVector = IconAutoFixHigh,
onClick = onClick,
)
}
}

85
examples/imageviewer/shared/src/commonMain/kotlin/example/imageviewer/view/PreviewImage.common.kt

@ -1,85 +0,0 @@
package example.imageviewer.view
import androidx.compose.animation.AnimatedContent
import androidx.compose.animation.AnimatedContentScope
import androidx.compose.animation.ExperimentalAnimationApi
import androidx.compose.animation.core.FastOutSlowInEasing
import androidx.compose.animation.core.tween
import androidx.compose.animation.with
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.Box
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.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.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.unit.dp
import example.imageviewer.LocalImageProvider
import example.imageviewer.model.PictureData
@OptIn(ExperimentalAnimationApi::class)
@Composable
internal fun PreviewImage(
picture: PictureData,
onClick: () -> Unit,
) {
val imageProvider = LocalImageProvider.current
val interactionSource = remember { MutableInteractionSource() }
Box(
Modifier.fillMaxWidth().height(393.dp).background(Color.Black),
contentAlignment = Alignment.Center
) {
Box(
modifier = Modifier
.fillMaxSize()
.clickable(interactionSource, indication = null, onClick = onClick),
) {
AnimatedContent(
targetState = picture,
transitionSpec = {
slideIntoContainer(
towards = AnimatedContentScope.SlideDirection.Left,
animationSpec = tween(durationMillis = 500, easing = FastOutSlowInEasing)
) with slideOutOfContainer(
towards = AnimatedContentScope.SlideDirection.Left,
animationSpec = tween(durationMillis = 500, easing = FastOutSlowInEasing)
)
}
) { currentPicture ->
var image: ImageBitmap? by remember(currentPicture) { mutableStateOf(null) }
LaunchedEffect(currentPicture) {
image = imageProvider.getImage(currentPicture)
}
if (image != null) {
Box(Modifier.fillMaxSize()) {
Image(
bitmap = image!!,
contentDescription = null,
modifier = Modifier
.fillMaxSize(),
contentScale = ContentScale.Crop
)
MemoryTextOverlay(currentPicture)
}
} else {
Spacer(
modifier = Modifier.fillMaxSize()
)
}
}
}
}
}

27
examples/imageviewer/shared/src/commonMain/kotlin/example/imageviewer/view/Thumbnail.kt

@ -1,25 +1,30 @@
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.foundation.layout.Box
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.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.*
import androidx.compose.material.Card
import androidx.compose.material.Icon
import androidx.compose.material.MaterialTheme
import androidx.compose.material.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.layout.ContentScale
import androidx.compose.ui.unit.dp
import example.imageviewer.icon.IconMoreVert
import example.imageviewer.model.PictureData
import org.jetbrains.compose.resources.ExperimentalResourceApi
import org.jetbrains.compose.resources.painterResource
@OptIn(ExperimentalResourceApi::class)
@Composable
internal fun Thumbnail(
picture: PictureData,
@ -53,14 +58,14 @@ internal fun Thumbnail(
style = MaterialTheme.typography.subtitle1
)
Image(
painterResource("dots.png"),
contentDescription = null,
Icon(
imageVector = IconMoreVert,
contentDescription = "more info",
modifier = Modifier.height(70.dp)
.width(30.dp)
.padding(start = 1.dp, top = 25.dp, end = 1.dp, bottom = 25.dp)
.clickable { onClickInfo() },
contentScale = ContentScale.FillHeight
tint = Color.DarkGray
)
}
}

BIN
examples/imageviewer/shared/src/commonMain/resources/arrowleft.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.9 KiB

BIN
examples/imageviewer/shared/src/commonMain/resources/dots.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

BIN
examples/imageviewer/shared/src/commonMain/resources/eye.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.8 KiB

BIN
examples/imageviewer/shared/src/commonMain/resources/list_view.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.4 KiB

BIN
examples/imageviewer/shared/src/commonMain/resources/magic.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.6 KiB

BIN
examples/imageviewer/shared/src/commonMain/resources/plus.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

BIN
examples/imageviewer/shared/src/commonMain/resources/trash.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.3 KiB

8
examples/imageviewer/shared/src/desktopMain/kotlin/example/imageviewer/view/CameraView.desktop.kt

@ -15,6 +15,7 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp
import example.imageviewer.*
import example.imageviewer.icon.IconPhotoCamera
import example.imageviewer.model.PictureData
import example.imageviewer.model.createCameraPictureData
import org.jetbrains.compose.resources.ExperimentalResourceApi
@ -51,7 +52,10 @@ internal actual fun CameraView(
.padding(20.dp)
)
val nameAndDescription = createNewPhotoNameAndDescription()
Button(onClick = {
CircularButton(
imageVector = IconPhotoCamera,
modifier = Modifier.align(Alignment.BottomCenter).padding(36.dp),
) {
onCapture(
createCameraPictureData(
name = nameAndDescription.name,
@ -60,8 +64,6 @@ internal actual fun CameraView(
),
DesktopStorableImage(imageBitmap)
)
}, Modifier.align(Alignment.BottomCenter)) {
Text(LocalLocalization.current.takePhoto)
}
}
}

5
examples/imageviewer/shared/src/desktopMain/kotlin/example/imageviewer/view/ImageViewer.desktop.kt

@ -29,8 +29,9 @@ import java.awt.Toolkit
class ExternalNavigationEventBus {
private val _events = MutableSharedFlow<ExternalImageViewerEvent>(
replay = 1,
onBufferOverflow = BufferOverflow.DROP_LATEST
replay = 0,
onBufferOverflow = BufferOverflow.DROP_OLDEST,
extraBufferCapacity = 1,
)
val events = _events.asSharedFlow()

41
examples/imageviewer/shared/src/iosMain/kotlin/example/imageviewer/IosShareIcon.kt

@ -1,41 +0,0 @@
package example.imageviewer
import androidx.compose.material.icons.materialIcon
import androidx.compose.material.icons.materialPath
import androidx.compose.ui.graphics.vector.ImageVector
// TODO Copied from material3, because "material:material-icons-extended" not working on iOS for now
val IosShareIcon: ImageVector =
materialIcon(name = "Filled.IosShare") {
materialPath {
moveTo(16.0f, 5.0f)
lineToRelative(-1.42f, 1.42f)
lineToRelative(-1.59f, -1.59f)
lineTo(12.99f, 16.0f)
horizontalLineToRelative(-1.98f)
lineTo(11.01f, 4.83f)
lineTo(9.42f, 6.42f)
lineTo(8.0f, 5.0f)
lineToRelative(4.0f, -4.0f)
lineToRelative(4.0f, 4.0f)
close()
moveTo(20.0f, 10.0f)
verticalLineToRelative(11.0f)
curveToRelative(0.0f, 1.1f, -0.9f, 2.0f, -2.0f, 2.0f)
lineTo(6.0f, 23.0f)
curveToRelative(-1.11f, 0.0f, -2.0f, -0.9f, -2.0f, -2.0f)
lineTo(4.0f, 10.0f)
curveToRelative(0.0f, -1.11f, 0.89f, -2.0f, 2.0f, -2.0f)
horizontalLineToRelative(3.0f)
verticalLineToRelative(2.0f)
lineTo(6.0f, 10.0f)
verticalLineToRelative(11.0f)
horizontalLineToRelative(12.0f)
lineTo(18.0f, 10.0f)
horizontalLineToRelative(-3.0f)
lineTo(15.0f, 8.0f)
horizontalLineToRelative(3.0f)
curveToRelative(1.1f, 0.0f, 2.0f, 0.89f, 2.0f, 2.0f)
close()
}
}

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

@ -6,6 +6,7 @@ import androidx.compose.ui.Modifier
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 kotlinx.cinterop.useContents
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.IO
@ -48,4 +49,4 @@ actual val ioDispatcher = Dispatchers.IO
actual val isShareFeatureSupported: Boolean = true
actual val shareIcon: ImageVector = IosShareIcon
actual val shareIcon: ImageVector = IconIosShare

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

@ -2,7 +2,6 @@ package example.imageviewer.view
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.*
import androidx.compose.material.Button
import androidx.compose.material.CircularProgressIndicator
import androidx.compose.material.Text
import androidx.compose.runtime.*
@ -12,9 +11,9 @@ import androidx.compose.ui.graphics.Color
import androidx.compose.ui.interop.UIKitView
import androidx.compose.ui.unit.dp
import example.imageviewer.IosStorableImage
import example.imageviewer.LocalLocalization
import example.imageviewer.PlatformStorableImage
import example.imageviewer.createNewPhotoNameAndDescription
import example.imageviewer.icon.IconPhotoCamera
import example.imageviewer.model.GpsPosition
import example.imageviewer.model.PictureData
import example.imageviewer.model.createCameraPictureData
@ -244,27 +243,25 @@ private fun BoxScope.RealDeviceCamera(
CATransaction.commit()
},
)
Button(
modifier = Modifier.align(Alignment.BottomCenter).padding(44.dp),
CircularButton(
imageVector = IconPhotoCamera,
modifier = Modifier.align(Alignment.BottomCenter).padding(36.dp),
enabled = !capturePhotoStarted,
onClick = {
capturePhotoStarted = true
val photoSettings = AVCapturePhotoSettings.photoSettingsWithFormat(
format = mapOf(AVVideoCodecKey to AVVideoCodecTypeJPEG)
)
if (camera.position == AVCaptureDevicePositionFront) {
capturePhotoOutput.connectionWithMediaType(AVMediaTypeVideo)
?.automaticallyAdjustsVideoMirroring = false
capturePhotoOutput.connectionWithMediaType(AVMediaTypeVideo)
?.videoMirrored = true
}
capturePhotoOutput.capturePhotoWithSettings(
settings = photoSettings,
delegate = photoCaptureDelegate
)
}
) {
Text(LocalLocalization.current.takePhoto)
capturePhotoStarted = true
val photoSettings = AVCapturePhotoSettings.photoSettingsWithFormat(
format = mapOf(AVVideoCodecKey to AVVideoCodecTypeJPEG)
)
if (camera.position == AVCaptureDevicePositionFront) {
capturePhotoOutput.connectionWithMediaType(AVMediaTypeVideo)
?.automaticallyAdjustsVideoMirroring = false
capturePhotoOutput.connectionWithMediaType(AVMediaTypeVideo)
?.videoMirrored = true
}
capturePhotoOutput.capturePhotoWithSettings(
settings = photoSettings,
delegate = photoCaptureDelegate
)
}
if (capturePhotoStarted) {
CircularProgressIndicator(

Loading…
Cancel
Save