Browse Source

ImageViewer notch (#2822)

pull/2837/head
dima.avdeev 1 year ago committed by GitHub
parent
commit
3e610c5fdd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 21
      experimental/examples/imageviewer/iosApp/iosApp/ContentView.swift
  2. 6
      experimental/examples/imageviewer/shared/src/androidMain/kotlin/example/imageviewer/plarfom.android.kt
  3. 9
      experimental/examples/imageviewer/shared/src/androidMain/kotlin/example/imageviewer/view/PreviewImage.android.kt
  4. 5
      experimental/examples/imageviewer/shared/src/commonMain/kotlin/example/imageviewer/ImageViewer.common.kt
  5. 5
      experimental/examples/imageviewer/shared/src/commonMain/kotlin/example/imageviewer/plarfom.common.kt
  6. 29
      experimental/examples/imageviewer/shared/src/commonMain/kotlin/example/imageviewer/view/CircularButton.kt
  7. 13
      experimental/examples/imageviewer/shared/src/commonMain/kotlin/example/imageviewer/view/FullscreenImage.kt
  8. 55
      experimental/examples/imageviewer/shared/src/commonMain/kotlin/example/imageviewer/view/GalleryScreen.kt
  9. 58
      experimental/examples/imageviewer/shared/src/commonMain/kotlin/example/imageviewer/view/MemoryScreen.kt
  10. 28
      experimental/examples/imageviewer/shared/src/commonMain/kotlin/example/imageviewer/view/PreviewImage.common.kt
  11. 31
      experimental/examples/imageviewer/shared/src/commonMain/kotlin/example/imageviewer/view/TopLayout.kt
  12. 5
      experimental/examples/imageviewer/shared/src/desktopMain/kotlin/example/imageviewer/plarfom.desktop.kt
  13. 6
      experimental/examples/imageviewer/shared/src/desktopMain/kotlin/example/imageviewer/view/PreviewImage.desktop.kt
  14. 29
      experimental/examples/imageviewer/shared/src/iosMain/kotlin/example/imageviewer/platform.ios.kt
  15. 6
      experimental/examples/imageviewer/shared/src/iosMain/kotlin/example/imageviewer/view/PreviewImage.ios.kt

21
experimental/examples/imageviewer/iosApp/iosApp/ContentView.swift

@ -2,6 +2,17 @@ import UIKit
import SwiftUI
import shared
let gradient = LinearGradient(
colors: [
Color.black.opacity(0.6),
Color.black.opacity(0.6),
Color.black.opacity(0.5),
Color.black.opacity(0.3),
Color.black.opacity(0.0),
],
startPoint: .top, endPoint: .bottom
)
struct ComposeView: UIViewControllerRepresentable {
func makeUIViewController(context: Context) -> UIViewController {
Main_iosKt.MainViewController()
@ -12,8 +23,14 @@ struct ComposeView: UIViewControllerRepresentable {
struct ContentView: View {
var body: some View {
ComposeView()
.ignoresSafeArea(.keyboard) // Compose has own keyboard handler
ZStack {
ComposeView()
.ignoresSafeArea(.all) // Compose has own keyboard handler
VStack {
gradient.ignoresSafeArea(edges: .top).frame(height: 0)
Spacer()
}
}.preferredColorScheme(.dark)
}
}

6
experimental/examples/imageviewer/shared/src/androidMain/kotlin/example/imageviewer/plarfom.android.kt

@ -0,0 +1,6 @@
package example.imageviewer
import androidx.compose.ui.Modifier
import androidx.compose.foundation.layout.*
actual fun Modifier.notchPadding(): Modifier = displayCutoutPadding().statusBarsPadding()

9
experimental/examples/imageviewer/shared/src/androidMain/kotlin/example/imageviewer/view/PreviewImage.android.kt

@ -1,9 +0,0 @@
package example.imageviewer.view
import android.content.res.Configuration
import androidx.compose.runtime.Composable
import androidx.compose.ui.platform.LocalConfiguration
@Composable
internal actual fun needShowPreview(): Boolean =
LocalConfiguration.current.orientation == Configuration.ORIENTATION_PORTRAIT

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

@ -80,8 +80,9 @@ internal fun ImageViewerCommon(
is MemoryPage -> {
MemoryScreen(
page,
photoGallery,
memoryPage = page,
photoGallery = photoGallery,
localization = dependencies.localization,
onSelectRelatedMemory = { galleryId ->
navigationStack.push(MemoryPage(galleryId))
},

5
experimental/examples/imageviewer/shared/src/commonMain/kotlin/example/imageviewer/plarfom.common.kt

@ -0,0 +1,5 @@
package example.imageviewer
import androidx.compose.ui.Modifier
expect fun Modifier.notchPadding(): Modifier

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

@ -0,0 +1,29 @@
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.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.unit.dp
import example.imageviewer.style.ImageviewerColors
@Composable
internal fun CircularButton(image: Painter, onClick: () -> Unit) {
Box(
Modifier.size(40.dp).clip(CircleShape).background(ImageviewerColors.uiLightBlack)
.clickable { onClick() }, contentAlignment = Alignment.Center
) {
Image(
image,
contentDescription = null,
modifier = Modifier.size(20.dp)
)
}
}

13
experimental/examples/imageviewer/shared/src/commonMain/kotlin/example/imageviewer/view/FullscreenImage.kt

@ -140,16 +140,8 @@ private fun FullscreenImageBar(
selectedFilters: Set<FilterType>,
onSelectFilter: (FilterType) -> Unit
) {
TopAppBar(
modifier = Modifier.background(color = ImageviewerColors.fullScreenImageBackground),
colors = TopAppBarDefaults.smallTopAppBarColors(
containerColor = ImageviewerColors.Transparent,
titleContentColor = MaterialTheme.colorScheme.onBackground
),
title = {
Text("${localization.picture} ${pictureName ?: "Unknown"}")
},
navigationIcon = {
TopLayout(
alignLeftContent = {
Tooltip(localization.back) {
CircularButton(
painterResource("arrowleft.png"),
@ -157,6 +149,7 @@ private fun FullscreenImageBar(
)
}
},
alignRightContent = {},
)
}

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

@ -1,19 +1,11 @@
@file:OptIn(ExperimentalResourceApi::class)
package example.imageviewer.view
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
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.width
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
@ -39,6 +31,7 @@ import example.imageviewer.model.GalleryId
import example.imageviewer.model.GalleryPage
import example.imageviewer.model.PhotoGallery
import example.imageviewer.model.bigUrl
import example.imageviewer.notchPadding
import example.imageviewer.style.ImageviewerColors
import org.jetbrains.compose.resources.ExperimentalResourceApi
import org.jetbrains.compose.resources.painterResource
@ -69,17 +62,19 @@ internal fun GalleryScreen(
Column(modifier = Modifier.background(MaterialTheme.colorScheme.background)) {
Box {
if (needShowPreview()) {
PreviewImage(
getImage = { dependencies.imageRepository.loadContent(it.bigUrl) },
picture = galleryPage.picture, onClick = {
galleryPage.pictureId?.let(onClickPreviewPicture)
})
}
TitleBar(
onRefresh = { photoGallery.updatePictures() },
onToggle = { galleryPage.toggleGalleryStyle() },
dependencies
PreviewImage(
getImage = { dependencies.imageRepository.loadContent(it.bigUrl) },
picture = galleryPage.picture, onClick = {
galleryPage.pictureId?.let(onClickPreviewPicture)
}
)
TopLayout(
alignLeftContent = {},
alignRightContent = {
CircularButton(painterResource("list_view.png")) {
galleryPage.toggleGalleryStyle()
}
},
)
}
Box(Modifier.fillMaxSize(), contentAlignment = Alignment.BottomCenter) {
@ -230,19 +225,3 @@ private fun ListGalleryView(
}
}
}
@OptIn(ExperimentalResourceApi::class, ExperimentalMaterial3Api::class)
@Composable
private fun TitleBar(onRefresh: () -> Unit, onToggle: () -> Unit, dependencies: Dependencies) {
TopAppBar(
modifier = Modifier.padding(start = 12.dp, end = 12.dp),
colors = TopAppBarDefaults.smallTopAppBarColors(
containerColor = ImageviewerColors.Transparent,
titleContentColor = MaterialTheme.colorScheme.onBackground
),
title = {
Row(Modifier.height(50.dp).fillMaxWidth(), horizontalArrangement = Arrangement.End) {
CircularButton(painterResource("list_view.png")) { onToggle() }
}
})
}

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

@ -3,25 +3,17 @@ package example.imageviewer.view
import androidx.compose.animation.animateContentSize
import androidx.compose.animation.core.Spring
import androidx.compose.animation.core.spring
import androidx.compose.foundation.*
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.*
import androidx.compose.foundation.lazy.LazyRow
import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.LocalTextStyle
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBar
import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
@ -30,12 +22,12 @@ import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.ImageBitmap
import androidx.compose.ui.graphics.Shadow
import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.graphics.painter.Painter
import androidx.compose.ui.layout.ContentScale
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.Localization
import example.imageviewer.model.GalleryEntryWithMetadata
import example.imageviewer.model.GalleryId
import example.imageviewer.model.MemoryPage
@ -49,6 +41,7 @@ import org.jetbrains.compose.resources.painterResource
internal fun MemoryScreen(
memoryPage: MemoryPage,
photoGallery: PhotoGallery,
localization: Localization,
onSelectRelatedMemory: (GalleryId) -> Unit,
onBack: () -> Unit,
onHeaderClick: (GalleryId) -> Unit
@ -116,39 +109,20 @@ internal fun MemoryScreen(
}
}
}
TopAppBar(
modifier = Modifier.padding(start = 12.dp, end = 12.dp),
colors = TopAppBarDefaults.smallTopAppBarColors(
containerColor = ImageviewerColors.Transparent,
titleContentColor = MaterialTheme.colorScheme.onBackground
),
title = {
Text("")
},
navigationIcon = {
Tooltip("Back") {
CircularButton(painterResource("arrowleft.png"), onClick = { onBack() })
TopLayout(
alignLeftContent = {
Tooltip(localization.back) {
CircularButton(
painterResource("arrowleft.png"),
onClick = { onBack() }
)
}
},
alignRightContent = {},
)
}
}
@Composable
internal fun CircularButton(image: Painter, onClick: () -> Unit) {
Box(
Modifier.size(40.dp).clip(CircleShape).background(ImageviewerColors.uiLightBlack)
.clickable { onClick() }, contentAlignment = Alignment.Center
) {
Image(
image,
contentDescription = null,
modifier = Modifier.size(20.dp)
)
}
}
@Composable
private fun MemoryHeader(bitmap: ImageBitmap, onClick: () -> Unit) {
val interactionSource = remember { MutableInteractionSource() }

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

@ -8,6 +8,7 @@ import androidx.compose.animation.slideInHorizontally
import androidx.compose.animation.slideOutHorizontally
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.Box
import androidx.compose.foundation.layout.Spacer
@ -22,6 +23,7 @@ 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
@ -34,19 +36,13 @@ internal fun PreviewImage(
onClick: () -> Unit,
getImage: suspend (Picture) -> ImageBitmap
) {
var image by remember(picture) { mutableStateOf<ImageBitmap?>(null) }
LaunchedEffect(picture) {
if (picture != null) {
image = getImage(picture)
}
}
Box(Modifier.fillMaxWidth().height(393.dp), contentAlignment = Alignment.Center) {
Box(Modifier.fillMaxWidth().height(393.dp).background(Color.Black), contentAlignment = Alignment.Center) {
Box(
modifier = Modifier.fillMaxSize()
.clickable { onClick() },
) {
AnimatedContent(
targetState = image,
targetState = picture,
transitionSpec = {
slideInHorizontally(
initialOffsetX = { it }, animationSpec = spring(
@ -61,10 +57,16 @@ internal fun PreviewImage(
)
// slideInVertically(initialOffsetY = { it }) with slideOutVertically(targetOffsetY = { -it })
}
) { imageBitmap ->
if (imageBitmap != null) {
) { currentPicture ->
var image by remember(currentPicture) { mutableStateOf<ImageBitmap?>(null) }
LaunchedEffect(currentPicture) {
if (currentPicture != null) {
image = getImage(currentPicture)
}
}
if (image != null) {
Image(
bitmap = imageBitmap,
bitmap = image!!,
contentDescription = null,
modifier = Modifier
.fillMaxSize(),
@ -80,8 +82,4 @@ internal fun PreviewImage(
}
}
}
@Composable
internal expect fun needShowPreview(): Boolean

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

@ -0,0 +1,31 @@
package example.imageviewer.view
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import example.imageviewer.notchPadding
@Composable
internal fun TopLayout(
alignLeftContent: @Composable () -> Unit = {},
alignRightContent: @Composable () -> Unit = {},
) {
Box(
Modifier
.fillMaxWidth()
.notchPadding()
.padding(horizontal = 12.dp)
) {
Row(Modifier.align(Alignment.CenterStart)) {
alignLeftContent()
}
Row(Modifier.align(Alignment.CenterEnd)) {
alignRightContent()
}
}
}

5
experimental/examples/imageviewer/shared/src/desktopMain/kotlin/example/imageviewer/plarfom.desktop.kt

@ -0,0 +1,5 @@
package example.imageviewer
import androidx.compose.ui.Modifier
actual fun Modifier.notchPadding(): Modifier = this

6
experimental/examples/imageviewer/shared/src/desktopMain/kotlin/example/imageviewer/view/PreviewImage.desktop.kt

@ -1,6 +0,0 @@
package example.imageviewer.view
import androidx.compose.runtime.Composable
@Composable
internal actual fun needShowPreview(): Boolean = true

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

@ -0,0 +1,29 @@
package example.imageviewer
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.windowInsetsPadding
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.Density
import androidx.compose.ui.unit.LayoutDirection
import kotlinx.cinterop.useContents
import platform.UIKit.UIApplication
import platform.UIKit.safeAreaInsets
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)

6
experimental/examples/imageviewer/shared/src/iosMain/kotlin/example/imageviewer/view/PreviewImage.ios.kt

@ -1,6 +0,0 @@
package example.imageviewer.view
import androidx.compose.runtime.Composable
@Composable
internal actual fun needShowPreview(): Boolean = true
Loading…
Cancel
Save