Skip to content

Commit

Permalink
✨ Implement collection of image paste items (#372)
Browse files Browse the repository at this point in the history
  • Loading branch information
guiyanakuang authored Feb 24, 2024
1 parent 0f2239b commit 2ca3872
Show file tree
Hide file tree
Showing 27 changed files with 473 additions and 24 deletions.
2 changes: 2 additions & 0 deletions composeApp/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,12 @@ kotlin {

desktopMain.dependencies {
implementation(compose.desktop.currentOs)
implementation(libs.guava)
implementation(libs.jmdns)
implementation(libs.jna)
implementation(libs.jna.platform)
implementation(libs.jnativehook)
implementation(libs.jsoup)
implementation(libs.koin.core)
implementation(libs.ktor.client.content.negotiation)
implementation(libs.ktor.client.core)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
package com.clipevery.clip.item

import java.awt.Image
import androidx.compose.ui.graphics.ImageBitmap
import java.nio.file.Path

interface ClipImage {

fun getImage(): Image
fun getImage(): ImageBitmap

fun getImagePath(): Path
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ interface PathProvider {
AppFileType.LOG -> clipLogPath.resolve("logs")
AppFileType.ENCRYPT -> clipEncryptPath.resolve("encrypt")
AppFileType.DATA -> clipDataPath.resolve("data")
AppFileType.IMAGE -> clipDataPath.resolve("images")
AppFileType.IMAGE -> clipUserPath.resolve("images")
AppFileType.VIDEO -> clipUserPath.resolve("videos")
AppFileType.FILE -> clipUserPath.resolve("files")
AppFileType.KCEF -> clipUserPath.resolve("kcef")
Expand Down
45 changes: 45 additions & 0 deletions composeApp/src/commonMain/kotlin/com/clipevery/ui/base/ClipIcon.kt
Original file line number Diff line number Diff line change
Expand Up @@ -190,4 +190,49 @@ fun html(): ImageVector {
}
.build()
}
}

@Composable
fun image(): ImageVector {
return remember {
ImageVector.Builder(name = "Image", defaultWidth = 24.0.dp, defaultHeight = 24.0.dp,
viewportWidth = 960.0f, viewportHeight = 960.0f).apply {
path(fill = SolidColor(Color(0xFF000000)), stroke = null, strokeLineWidth = 0.0f,
strokeLineCap = Butt, strokeLineJoin = Miter, strokeLineMiter = 4.0f,
pathFillType = NonZero) {
moveTo(200.0f, 840.0f)
quadToRelative(-33.0f, 0.0f, -56.5f, -23.5f)
reflectiveQuadTo(120.0f, 760.0f)
verticalLineToRelative(-560.0f)
quadToRelative(0.0f, -33.0f, 23.5f, -56.5f)
reflectiveQuadTo(200.0f, 120.0f)
horizontalLineToRelative(560.0f)
quadToRelative(33.0f, 0.0f, 56.5f, 23.5f)
reflectiveQuadTo(840.0f, 200.0f)
verticalLineToRelative(560.0f)
quadToRelative(0.0f, 33.0f, -23.5f, 56.5f)
reflectiveQuadTo(760.0f, 840.0f)
lineTo(200.0f, 840.0f)
close()
moveTo(200.0f, 760.0f)
horizontalLineToRelative(560.0f)
verticalLineToRelative(-560.0f)
lineTo(200.0f, 200.0f)
verticalLineToRelative(560.0f)
close()
moveTo(240.0f, 680.0f)
horizontalLineToRelative(480.0f)
lineTo(570.0f, 480.0f)
lineTo(450.0f, 640.0f)
lineToRelative(-90.0f, -120.0f)
lineToRelative(-120.0f, 160.0f)
close()
moveTo(200.0f, 760.0f)
verticalLineToRelative(-560.0f)
verticalLineToRelative(560.0f)
close()
}
}
.build()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,8 @@ fun ClipPreviewItemView(clipData: ClipData, isLast: Boolean, clipContent: @Compo

if (it.getClipType() == ClipType.TEXT ||
it.getClipType() == ClipType.URL ||
it.getClipType() == ClipType.HTML
it.getClipType() == ClipType.HTML ||
it.getClipType() == ClipType.IMAGE
) {

var hover by remember { mutableStateOf(false) }
Expand Down Expand Up @@ -152,6 +153,7 @@ fun ClipSpecificPreviewItemView(clipData: ClipData) {
ClipType.TEXT -> TextPreviewView(clipData)
ClipType.URL -> UrlPreviewView(clipData)
ClipType.HTML -> HtmlPreviewView(clipData)
ClipType.IMAGE -> ImagePreviewView(clipData)
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
package com.clipevery.ui.clip

import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.wrapContentWidth
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.Icon
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.ImageBitmap
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.clipevery.LocalKoinApplication
import com.clipevery.clip.item.ClipImage
import com.clipevery.dao.clip.ClipData
import com.clipevery.i18n.GlobalCopywriter
import com.clipevery.ui.base.image
import com.clipevery.utils.FileUtils

@Composable
fun ImagePreviewView(clipData: ClipData) {
clipData.getClipItem(ClipImage::class)?.let {

val current = LocalKoinApplication.current
val copywriter = current.koin.get<GlobalCopywriter>()
val fileUtils = current.koin.get<FileUtils>()

val imageBitmap: ImageBitmap = remember(it) {
it.getImage()
}

val imageSize = remember(it) {
fileUtils.formatBytes(fileUtils.getFileSize(it.getImagePath()))
}

Row(modifier = Modifier.fillMaxSize()
.padding(10.dp),
horizontalArrangement = Arrangement.SpaceBetween
) {

Row {
Image(
modifier = Modifier.size(100.dp)
.clip(RoundedCornerShape(5.dp)),
bitmap = imageBitmap,
contentDescription = "Image",
contentScale = ContentScale.Crop
)

Column(
modifier = Modifier.fillMaxHeight()
.wrapContentWidth()
.padding(horizontal = 8.dp),
verticalArrangement = Arrangement.Bottom
) {
Text(
text = "${copywriter.getText("File_Name")}: ${fileUtils.getFileNameFromRelativePath(it.getImagePath())}",
color = MaterialTheme.colors.onBackground,
style = TextStyle(
fontWeight = FontWeight.Light,
color = MaterialTheme.colors.onBackground,
fontSize = 10.sp
)
)

Text(
text = "${copywriter.getText("Dimensions")}: ${imageBitmap.width} x ${imageBitmap.height}",
color = MaterialTheme.colors.onBackground,
style = TextStyle(
fontWeight = FontWeight.Light,
color = MaterialTheme.colors.onBackground,
fontSize = 10.sp
)
)

Text(
text = "${copywriter.getText("Size")}: $imageSize",
color = MaterialTheme.colors.onBackground,
style = TextStyle(
fontWeight = FontWeight.Light,
color = MaterialTheme.colors.onBackground,
fontSize = 10.sp
)
)
}
}

Row(
modifier = Modifier.wrapContentWidth()
.padding(end = 8.dp),
horizontalArrangement = Arrangement.End,
verticalAlignment = Alignment.CenterVertically
) {
Icon(
image(),
contentDescription = "Image",
modifier = Modifier.padding(3.dp).size(14.dp),
tint = MaterialTheme.colors.onBackground
)

Text(
text = copywriter.getText("Image"),
fontFamily = FontFamily.SansSerif,
style = TextStyle(
fontWeight = FontWeight.Light,
color = MaterialTheme.colors.onBackground,
fontSize = 10.sp
)
)
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,10 @@ fun getTitle(clipText: ClipText): String {

@Composable
fun TextPreviewView(clipData: ClipData) {
val current = LocalKoinApplication.current
val copywriter = current.koin.get<GlobalCopywriter>()

clipData.getClipItem(ClipText::class)?.let {
val current = LocalKoinApplication.current
val copywriter = current.koin.get<GlobalCopywriter>()

Column(
modifier = Modifier.fillMaxWidth()
.padding(8.dp)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,15 @@ import java.text.SimpleDateFormat
import java.time.Instant
import java.time.LocalDateTime
import java.time.ZoneId
import java.time.format.DateTimeFormatter
import java.time.temporal.ChronoUnit
import java.util.Calendar
import java.util.Locale

object DateUtils {

val dateFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd")

fun getPrevDay(): RealmInstant {
val calendar = Calendar.getInstance()
calendar.add(Calendar.DAY_OF_MONTH, -1)
Expand Down Expand Up @@ -42,6 +45,10 @@ object DateUtils {
return null
}

fun getYYYYMMDD(): String {
return dateFormatter.format(LocalDateTime.now())
}

fun getDateFormat(date: LocalDateTime, language: String): String {
val locale = when (language) {
"zh" -> Locale.SIMPLIFIED_CHINESE
Expand Down
22 changes: 22 additions & 0 deletions composeApp/src/commonMain/kotlin/com/clipevery/utils/FileUtils.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package com.clipevery.utils

import java.nio.file.Path

interface FileUtils {

fun formatBytes(bytesSize: Long): String

fun createRandomFileName(ext: String): String

fun getExtFromFileName(fileName: String): String?

fun createClipRelativePath(clipId: Int, fileName: String): String

fun getFileNameFromRelativePath(relativePath: Path): String

fun createClipPath(fileRelativePath: String, isFile: Boolean, isImage: Boolean = false): Path

fun getFileSize(path: Path): Long

fun getFileMd5(path: Path): String
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import com.clipevery.clip.ClipboardService
import com.clipevery.clip.DesktopTransferableConsumer
import com.clipevery.clip.TransferableConsumer
import com.clipevery.clip.getDesktopClipboardService
import com.clipevery.clip.plugin.ImageHtmlCombinePlugin
import com.clipevery.clip.plugin.MultiImagePlugin
import com.clipevery.clip.plugin.UrlTextCombinePlugin
import com.clipevery.clip.service.FileItemService
Expand Down Expand Up @@ -53,7 +54,9 @@ import com.clipevery.signal.DesktopSignedPreKeyStore
import com.clipevery.signal.getClipIdentityKeyStoreFactory
import com.clipevery.ui.DesktopThemeDetector
import com.clipevery.ui.ThemeDetector
import com.clipevery.utils.DesktopFileUtils
import com.clipevery.utils.DesktopQRCodeGenerator
import com.clipevery.utils.FileUtils
import com.clipevery.utils.IDGenerator
import com.clipevery.utils.IDGeneratorFactory
import com.clipevery.utils.QRCodeGenerator
Expand Down Expand Up @@ -92,6 +95,7 @@ object Dependencies {
single<ConfigManager> { DefaultConfigManager(get<FilePersist>().getPersist("appConfig.json", AppFileType.USER), get()) }
single<QRCodeGenerator> { DesktopQRCodeGenerator(get(), get()) }
single<IDGenerator> { IDGeneratorFactory(get()).createIDGenerator() }
single<FileUtils> { DesktopFileUtils }

// realm component
single<RealmManager> { RealmManagerImpl.createRealmManager(get()) }
Expand Down Expand Up @@ -124,7 +128,9 @@ object Dependencies {
ImageItemService(),
TextItemService(),
UrlItemService()
), listOf(UrlTextCombinePlugin(), MultiImagePlugin())) }
), listOf(UrlTextCombinePlugin,
ImageHtmlCombinePlugin,
MultiImagePlugin)) }

// ui component
single<AppUI> { AppUI(width = 460.dp, height = 710.dp) }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,13 @@ interface ClipItemService {
clipId: Int,
itemIndex: Int,
dataFlavor: DataFlavor,
dataFlavorMap: Map<String, List<DataFlavor>>,
transferable: Transferable,
clipCollector: ClipCollector
) {
try {
val transferData = transferable.getTransferData(dataFlavor)
doCreateClipItem(transferData, clipId, itemIndex, dataFlavor, transferable, clipCollector)
doCreateClipItem(transferData, clipId, itemIndex, dataFlavor, dataFlavorMap, transferable, clipCollector)
} catch (e: Exception) {
collectError(e, clipId, itemIndex, clipCollector)
}
Expand All @@ -26,6 +27,7 @@ interface ClipItemService {
clipId: Int,
itemIndex: Int,
dataFlavor: DataFlavor,
dataFlavorMap: Map<String, List<DataFlavor>>,
transferable: Transferable,
clipCollector: ClipCollector)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ open class DesktopTransferableConsumer(private val appInfo: AppInfo,
for (flavor in flavors) {
clipItemServiceMap[identifier]?.let { clipItemService ->
if (clipCollector.needCollectionItem(itemIndex, clipItemService::class)) {
clipItemService.createClipItem(clipId, itemIndex, flavor, transferable, clipCollector)
clipItemService.createClipItem(clipId, itemIndex, flavor, dataFlavorMap, transferable, clipCollector)
}
}
}
Expand Down
Loading

0 comments on commit 2ca3872

Please sign in to comment.