|
|
@ -1,9 +1,7 @@ |
|
|
|
package example.imageviewer.view |
|
|
|
package example.imageviewer.view |
|
|
|
|
|
|
|
|
|
|
|
import androidx.compose.foundation.BorderStroke |
|
|
|
|
|
|
|
import androidx.compose.foundation.Image |
|
|
|
import androidx.compose.foundation.Image |
|
|
|
import androidx.compose.foundation.background |
|
|
|
import androidx.compose.foundation.background |
|
|
|
import androidx.compose.foundation.border |
|
|
|
|
|
|
|
import androidx.compose.foundation.clickable |
|
|
|
import androidx.compose.foundation.clickable |
|
|
|
import androidx.compose.foundation.layout.Arrangement |
|
|
|
import androidx.compose.foundation.layout.Arrangement |
|
|
|
import androidx.compose.foundation.layout.Box |
|
|
|
import androidx.compose.foundation.layout.Box |
|
|
@ -15,15 +13,13 @@ import androidx.compose.foundation.layout.fillMaxSize |
|
|
|
import androidx.compose.foundation.layout.fillMaxWidth |
|
|
|
import androidx.compose.foundation.layout.fillMaxWidth |
|
|
|
import androidx.compose.foundation.layout.height |
|
|
|
import androidx.compose.foundation.layout.height |
|
|
|
import androidx.compose.foundation.layout.padding |
|
|
|
import androidx.compose.foundation.layout.padding |
|
|
|
import androidx.compose.foundation.layout.size |
|
|
|
import androidx.compose.foundation.layout.width |
|
|
|
import androidx.compose.foundation.lazy.grid.GridCells |
|
|
|
import androidx.compose.foundation.lazy.grid.GridCells |
|
|
|
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid |
|
|
|
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid |
|
|
|
import androidx.compose.foundation.lazy.grid.itemsIndexed |
|
|
|
import androidx.compose.foundation.lazy.grid.itemsIndexed |
|
|
|
import androidx.compose.foundation.shape.CircleShape |
|
|
|
import androidx.compose.foundation.shape.CircleShape |
|
|
|
import androidx.compose.material3.ExperimentalMaterial3Api |
|
|
|
import androidx.compose.material3.ExperimentalMaterial3Api |
|
|
|
import androidx.compose.material3.MaterialTheme |
|
|
|
import androidx.compose.material3.MaterialTheme |
|
|
|
import androidx.compose.material3.Surface |
|
|
|
|
|
|
|
import androidx.compose.material3.Text |
|
|
|
|
|
|
|
import androidx.compose.material3.TopAppBar |
|
|
|
import androidx.compose.material3.TopAppBar |
|
|
|
import androidx.compose.material3.TopAppBarDefaults |
|
|
|
import androidx.compose.material3.TopAppBarDefaults |
|
|
|
import androidx.compose.runtime.Composable |
|
|
|
import androidx.compose.runtime.Composable |
|
|
@ -32,14 +28,10 @@ import androidx.compose.runtime.collectAsState |
|
|
|
import androidx.compose.runtime.getValue |
|
|
|
import androidx.compose.runtime.getValue |
|
|
|
import androidx.compose.ui.Alignment |
|
|
|
import androidx.compose.ui.Alignment |
|
|
|
import androidx.compose.ui.Modifier |
|
|
|
import androidx.compose.ui.Modifier |
|
|
|
import androidx.compose.ui.graphics.Color |
|
|
|
import androidx.compose.ui.draw.clip |
|
|
|
import androidx.compose.ui.graphics.ImageBitmap |
|
|
|
import androidx.compose.ui.graphics.ImageBitmap |
|
|
|
import androidx.compose.ui.layout.ContentScale |
|
|
|
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.dp |
|
|
|
import androidx.compose.ui.unit.sp |
|
|
|
|
|
|
|
import example.imageviewer.Dependencies |
|
|
|
import example.imageviewer.Dependencies |
|
|
|
import example.imageviewer.ExternalImageViewerEvent |
|
|
|
import example.imageviewer.ExternalImageViewerEvent |
|
|
|
import example.imageviewer.model.GalleryEntryWithMetadata |
|
|
|
import example.imageviewer.model.GalleryEntryWithMetadata |
|
|
@ -48,24 +40,9 @@ import example.imageviewer.model.GalleryPage |
|
|
|
import example.imageviewer.model.PhotoGallery |
|
|
|
import example.imageviewer.model.PhotoGallery |
|
|
|
import example.imageviewer.model.bigUrl |
|
|
|
import example.imageviewer.model.bigUrl |
|
|
|
import example.imageviewer.style.ImageviewerColors |
|
|
|
import example.imageviewer.style.ImageviewerColors |
|
|
|
import example.imageviewer.style.ImageviewerColors.kotlinHorizontalGradientBrush |
|
|
|
|
|
|
|
import org.jetbrains.compose.resources.ExperimentalResourceApi |
|
|
|
import org.jetbrains.compose.resources.ExperimentalResourceApi |
|
|
|
import org.jetbrains.compose.resources.painterResource |
|
|
|
import org.jetbrains.compose.resources.painterResource |
|
|
|
|
|
|
|
|
|
|
|
@Composable |
|
|
|
|
|
|
|
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 { |
|
|
|
enum class GalleryStyle { |
|
|
|
SQUARES, |
|
|
|
SQUARES, |
|
|
@ -91,32 +68,36 @@ internal fun GalleryScreen( |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
Column(modifier = Modifier.background(MaterialTheme.colorScheme.background)) { |
|
|
|
Column(modifier = Modifier.background(MaterialTheme.colorScheme.background)) { |
|
|
|
TitleBar( |
|
|
|
Box { |
|
|
|
onRefresh = { photoGallery.updatePictures() }, |
|
|
|
if (needShowPreview()) { |
|
|
|
onToggle = { galleryPage.toggleGalleryStyle() }, |
|
|
|
PreviewImage( |
|
|
|
dependencies |
|
|
|
getImage = { dependencies.imageRepository.loadContent(it.bigUrl) }, |
|
|
|
) |
|
|
|
picture = galleryPage.picture, onClick = { |
|
|
|
if (needShowPreview()) { |
|
|
|
galleryPage.pictureId?.let(onClickPreviewPicture) |
|
|
|
PreviewImage( |
|
|
|
}) |
|
|
|
getImage = { dependencies.imageRepository.loadContent(it.bigUrl) }, |
|
|
|
} |
|
|
|
picture = galleryPage.picture, onClick = { |
|
|
|
TitleBar( |
|
|
|
galleryPage.pictureId?.let(onClickPreviewPicture) |
|
|
|
onRefresh = { photoGallery.updatePictures() }, |
|
|
|
}) |
|
|
|
onToggle = { galleryPage.toggleGalleryStyle() }, |
|
|
|
} |
|
|
|
dependencies |
|
|
|
when (galleryPage.galleryStyle) { |
|
|
|
|
|
|
|
GalleryStyle.SQUARES -> SquaresGalleryView( |
|
|
|
|
|
|
|
pictures, |
|
|
|
|
|
|
|
galleryPage.pictureId, |
|
|
|
|
|
|
|
onSelect = { galleryPage.selectPicture(it) }, |
|
|
|
|
|
|
|
onMakeNewMemory |
|
|
|
|
|
|
|
) |
|
|
|
) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
Box(Modifier.fillMaxSize(), contentAlignment = Alignment.BottomCenter) { |
|
|
|
|
|
|
|
when (galleryPage.galleryStyle) { |
|
|
|
|
|
|
|
GalleryStyle.SQUARES -> SquaresGalleryView( |
|
|
|
|
|
|
|
pictures, |
|
|
|
|
|
|
|
galleryPage.pictureId, |
|
|
|
|
|
|
|
onSelect = { galleryPage.selectPicture(it) }, |
|
|
|
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
|
|
GalleryStyle.LIST -> ListGalleryView( |
|
|
|
GalleryStyle.LIST -> ListGalleryView( |
|
|
|
pictures, |
|
|
|
pictures, |
|
|
|
dependencies, |
|
|
|
dependencies, |
|
|
|
onSelect = { galleryPage.selectPicture(it) }, |
|
|
|
onSelect = { galleryPage.selectPicture(it) }, |
|
|
|
onFullScreen = { onClickPreviewPicture(it) } |
|
|
|
onFullScreen = { onClickPreviewPicture(it) } |
|
|
|
) |
|
|
|
) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
MakeNewMemoryMiniature(onMakeNewMemory) |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
if (pictures.isEmpty()) { |
|
|
|
if (pictures.isEmpty()) { |
|
|
@ -129,54 +110,95 @@ private fun SquaresGalleryView( |
|
|
|
images: List<GalleryEntryWithMetadata>, |
|
|
|
images: List<GalleryEntryWithMetadata>, |
|
|
|
selectedImage: GalleryId?, |
|
|
|
selectedImage: GalleryId?, |
|
|
|
onSelect: (GalleryId) -> Unit, |
|
|
|
onSelect: (GalleryId) -> Unit, |
|
|
|
onMakeNewMemory: () -> Unit, |
|
|
|
|
|
|
|
) { |
|
|
|
) { |
|
|
|
LazyVerticalGrid(columns = GridCells.Adaptive(minSize = 128.dp)) { |
|
|
|
Column { |
|
|
|
item { |
|
|
|
Spacer(Modifier.height(1.dp)) |
|
|
|
MakeNewMemoryMiniature(onMakeNewMemory) |
|
|
|
LazyVerticalGrid( |
|
|
|
} |
|
|
|
columns = GridCells.Adaptive(minSize = 130.dp), |
|
|
|
itemsIndexed(images) { idx, image -> |
|
|
|
verticalArrangement = Arrangement.spacedBy(1.dp), |
|
|
|
val isSelected = image.id == selectedImage |
|
|
|
horizontalArrangement = Arrangement.spacedBy(1.dp) |
|
|
|
val (picture, bitmap) = image |
|
|
|
) { |
|
|
|
SquareMiniature( |
|
|
|
itemsIndexed(images) { idx, image -> |
|
|
|
image.thumbnail, |
|
|
|
val isSelected = image.id == selectedImage |
|
|
|
onClick = { onSelect(picture) }, |
|
|
|
val (picture, bitmap) = image |
|
|
|
isHighlighted = isSelected |
|
|
|
SquareMiniature( |
|
|
|
) |
|
|
|
image.thumbnail, |
|
|
|
|
|
|
|
onClick = { onSelect(picture) }, |
|
|
|
|
|
|
|
isHighlighted = isSelected |
|
|
|
|
|
|
|
) |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@OptIn(ExperimentalResourceApi::class) |
|
|
|
@Composable |
|
|
|
@Composable |
|
|
|
private fun MakeNewMemoryMiniature(onClick: () -> Unit) { |
|
|
|
private fun MakeNewMemoryMiniature(onClick: () -> Unit) { |
|
|
|
Box( |
|
|
|
Column { |
|
|
|
Modifier.aspectRatio(1.0f) |
|
|
|
Box( |
|
|
|
.clickable { |
|
|
|
Modifier |
|
|
|
onClick() |
|
|
|
.clip(CircleShape) |
|
|
|
}, contentAlignment = Alignment.Center |
|
|
|
.width(52.dp) |
|
|
|
) { |
|
|
|
.background(ImageviewerColors.uiLightBlack) |
|
|
|
Text( |
|
|
|
.aspectRatio(1.0f) |
|
|
|
"+", |
|
|
|
.clickable { |
|
|
|
modifier = Modifier.fillMaxWidth(), |
|
|
|
onClick() |
|
|
|
color = MaterialTheme.colorScheme.onBackground, |
|
|
|
}, |
|
|
|
textAlign = TextAlign.Center, |
|
|
|
contentAlignment = Alignment.Center |
|
|
|
fontSize = 50.sp |
|
|
|
) { |
|
|
|
) |
|
|
|
Image( |
|
|
|
|
|
|
|
painter = painterResource("plus.png"), |
|
|
|
|
|
|
|
contentDescription = null, |
|
|
|
|
|
|
|
modifier = Modifier |
|
|
|
|
|
|
|
.width(18.dp) |
|
|
|
|
|
|
|
.height(18.dp), |
|
|
|
|
|
|
|
) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
Spacer(Modifier.height(32.dp)) |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@OptIn(ExperimentalResourceApi::class) |
|
|
|
@Composable |
|
|
|
@Composable |
|
|
|
internal fun SquareMiniature(image: ImageBitmap, isHighlighted: Boolean, onClick: () -> Unit) { |
|
|
|
internal fun SquareMiniature(image: ImageBitmap, isHighlighted: Boolean, onClick: () -> Unit) { |
|
|
|
Image( |
|
|
|
Box( |
|
|
|
bitmap = image, |
|
|
|
Modifier.aspectRatio(1.0f).clickable { onClick() }, |
|
|
|
contentDescription = null, |
|
|
|
contentAlignment = Alignment.BottomEnd |
|
|
|
modifier = Modifier.aspectRatio(1.0f).clickable { onClick() }.then( |
|
|
|
) { |
|
|
|
if (isHighlighted) { |
|
|
|
Image( |
|
|
|
Modifier.border(BorderStroke(5.dp, Color.White)) |
|
|
|
bitmap = image, |
|
|
|
} else Modifier |
|
|
|
contentDescription = null, |
|
|
|
), |
|
|
|
modifier = Modifier.fillMaxSize().clickable { onClick() }.then( |
|
|
|
contentScale = ContentScale.Crop |
|
|
|
if (isHighlighted) { |
|
|
|
) |
|
|
|
Modifier//.border(BorderStroke(5.dp, Color.White)) |
|
|
|
|
|
|
|
} else Modifier |
|
|
|
|
|
|
|
), |
|
|
|
|
|
|
|
contentScale = ContentScale.Crop |
|
|
|
|
|
|
|
) |
|
|
|
|
|
|
|
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), |
|
|
|
|
|
|
|
) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@Composable |
|
|
|
@Composable |
|
|
@ -186,8 +208,6 @@ private fun ListGalleryView( |
|
|
|
onSelect: (GalleryId) -> Unit, |
|
|
|
onSelect: (GalleryId) -> Unit, |
|
|
|
onFullScreen: (GalleryId) -> Unit |
|
|
|
onFullScreen: (GalleryId) -> Unit |
|
|
|
) { |
|
|
|
) { |
|
|
|
GalleryHeader() |
|
|
|
|
|
|
|
Spacer(modifier = Modifier.height(10.dp)) |
|
|
|
|
|
|
|
ScrollableColumn( |
|
|
|
ScrollableColumn( |
|
|
|
modifier = Modifier.fillMaxSize() |
|
|
|
modifier = Modifier.fillMaxSize() |
|
|
|
) { |
|
|
|
) { |
|
|
@ -215,44 +235,14 @@ private fun ListGalleryView( |
|
|
|
@Composable |
|
|
|
@Composable |
|
|
|
private fun TitleBar(onRefresh: () -> Unit, onToggle: () -> Unit, dependencies: Dependencies) { |
|
|
|
private fun TitleBar(onRefresh: () -> Unit, onToggle: () -> Unit, dependencies: Dependencies) { |
|
|
|
TopAppBar( |
|
|
|
TopAppBar( |
|
|
|
modifier = Modifier.background(brush = kotlinHorizontalGradientBrush), |
|
|
|
modifier = Modifier.padding(start = 12.dp, end = 12.dp), |
|
|
|
colors = TopAppBarDefaults.smallTopAppBarColors( |
|
|
|
colors = TopAppBarDefaults.smallTopAppBarColors( |
|
|
|
containerColor = ImageviewerColors.Transparent, |
|
|
|
containerColor = ImageviewerColors.Transparent, |
|
|
|
titleContentColor = MaterialTheme.colorScheme.onBackground |
|
|
|
titleContentColor = MaterialTheme.colorScheme.onBackground |
|
|
|
), |
|
|
|
), |
|
|
|
title = { |
|
|
|
title = { |
|
|
|
Row(Modifier.height(50.dp)) { |
|
|
|
Row(Modifier.height(50.dp).fillMaxWidth(), horizontalArrangement = Arrangement.End) { |
|
|
|
Text( |
|
|
|
CircularButton(painterResource("list_view.png")) { onToggle() } |
|
|
|
dependencies.localization.appName, |
|
|
|
|
|
|
|
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( |
|
|
|
|
|
|
|
painter = painterResource("list_view.png"), |
|
|
|
|
|
|
|
contentDescription = null, |
|
|
|
|
|
|
|
modifier = Modifier.size(35.dp).clickable { |
|
|
|
|
|
|
|
onToggle() |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
Surface( |
|
|
|
|
|
|
|
color = ImageviewerColors.Transparent, |
|
|
|
|
|
|
|
modifier = Modifier.padding(end = 20.dp).align(Alignment.CenterVertically), |
|
|
|
|
|
|
|
shape = CircleShape |
|
|
|
|
|
|
|
) { |
|
|
|
|
|
|
|
Image( |
|
|
|
|
|
|
|
painter = painterResource("refresh.png"), |
|
|
|
|
|
|
|
contentDescription = null, |
|
|
|
|
|
|
|
modifier = Modifier.size(35.dp).clickable { |
|
|
|
|
|
|
|
onRefresh() |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
}) |
|
|
|
}) |
|
|
|
} |
|
|
|
} |
|
|
|