mirror of
https://github.com/sudosylabs/vnidrop.git
synced 2026-08-05 10:29:58 +02:00
refactor(ui): establish modular Compose foundation
This commit is contained in:
@@ -1,37 +1,42 @@
|
||||
package com.vnidrop.app
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import com.vnidrop.app.core.rememberFileSystemService
|
||||
import com.vnidrop.app.notifications.JvmLocalNotificationService
|
||||
import java.net.NetworkInterface
|
||||
|
||||
class JVMPlatform : Platform {
|
||||
override val name: String = "Java ${System.getProperty("java.version")}"
|
||||
override val defaultCoreDataDir: String =
|
||||
System.getProperty("user.home") + "/.vnidrop"
|
||||
override val defaultReceiveDir: String =
|
||||
System.getProperty("user.home") + "/Downloads"
|
||||
override val deviceInfo: DeviceInfo = DeviceInfo(
|
||||
deviceName = System.getenv("COMPUTERNAME")
|
||||
?: System.getenv("HOSTNAME")
|
||||
?: System.getProperty("user.name"),
|
||||
deviceModel = listOfNotNull(
|
||||
System.getProperty("os.arch"),
|
||||
System.getProperty("java.vm.name"),
|
||||
).joinToString(" | ").ifBlank { null },
|
||||
operatingSystem = listOfNotNull(
|
||||
System.getProperty("os.name"),
|
||||
System.getProperty("os.version"),
|
||||
).joinToString(" ").ifBlank { name },
|
||||
network = activeNetworkSummary(),
|
||||
@Composable
|
||||
fun rememberJvmAppDependencies(): AppDependencies {
|
||||
val fileSystemService = rememberFileSystemService()
|
||||
return remember(fileSystemService) {
|
||||
AppDependencies(
|
||||
environment = PlatformEnvironment(
|
||||
name = "Java ${System.getProperty("java.version")}",
|
||||
appVersion = AppDependencies::class.java.`package`.implementationVersion ?: "0.1.0",
|
||||
defaultCoreDataDir = System.getProperty("user.home") + "/.vnidrop",
|
||||
defaultUsername = System.getenv("COMPUTERNAME") ?: System.getenv("HOSTNAME") ?: System.getProperty("user.name") ?: "Receiver",
|
||||
),
|
||||
deviceInfoProvider = JvmDeviceInfoProvider,
|
||||
fileSystemService = fileSystemService,
|
||||
localNotificationService = JvmLocalNotificationService(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private object JvmDeviceInfoProvider : DeviceInfoProvider {
|
||||
override suspend fun load(): DeviceInfo = DeviceInfo(
|
||||
deviceName = System.getenv("COMPUTERNAME") ?: System.getenv("HOSTNAME") ?: System.getProperty("user.name"),
|
||||
deviceModel = listOfNotNull(System.getProperty("os.arch"), System.getProperty("java.vm.name"))
|
||||
.joinToString(" | ").ifBlank { null },
|
||||
operatingSystem = listOfNotNull(System.getProperty("os.name"), System.getProperty("os.version"))
|
||||
.joinToString(" ").ifBlank { "Java ${System.getProperty("java.version")}" },
|
||||
network = runCatching {
|
||||
NetworkInterface.getNetworkInterfaces().asSequence()
|
||||
.filter { it.isUp && !it.isLoopback }
|
||||
.map { it.displayName }
|
||||
.firstOrNull()
|
||||
}.getOrNull(),
|
||||
batteryLevel = null,
|
||||
)
|
||||
}
|
||||
|
||||
actual fun getPlatform(): Platform = JVMPlatform()
|
||||
|
||||
private fun activeNetworkSummary(): String? =
|
||||
runCatching {
|
||||
NetworkInterface.getNetworkInterfaces()
|
||||
.asSequence()
|
||||
.filter { it.isUp && !it.isLoopback }
|
||||
.map { it.displayName }
|
||||
.firstOrNull()
|
||||
}.getOrNull()
|
||||
|
||||
@@ -44,15 +44,6 @@ actual fun rememberReceiveFolderPicker(
|
||||
}
|
||||
}
|
||||
|
||||
actual suspend fun sharePickedFile(
|
||||
repository: CoreRepository,
|
||||
file: PickedShareFile,
|
||||
transferName: String,
|
||||
senderName: String,
|
||||
) {
|
||||
repository.sharePath(file.value, transferName, senderName)
|
||||
}
|
||||
|
||||
private fun openPicker(
|
||||
onError: (String) -> Unit,
|
||||
block: () -> Unit,
|
||||
|
||||
@@ -27,4 +27,11 @@ private class JvmFileSystemService : FileSystemService {
|
||||
}
|
||||
|
||||
override fun createReceiveOutputSink(folder: ReceiveFolder): ReceiveOutputSink? = null
|
||||
|
||||
override suspend fun sharePickedFile(
|
||||
repository: CoreGateway,
|
||||
file: PickedShareFile,
|
||||
transferName: String,
|
||||
senderName: String,
|
||||
): Result<Share> = repository.sharePath(file.value, transferName, senderName)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
package com.vnidrop.app.notifications
|
||||
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
import java.awt.Color
|
||||
import java.awt.Frame
|
||||
import java.awt.Graphics2D
|
||||
import java.awt.SystemTray
|
||||
import java.awt.TrayIcon
|
||||
import java.awt.image.BufferedImage
|
||||
|
||||
class JvmLocalNotificationService : LocalNotificationService {
|
||||
private val _permission = MutableStateFlow(
|
||||
if (SystemTray.isSupported()) NotificationPermission.Granted else NotificationPermission.Unsupported,
|
||||
)
|
||||
override val permission: StateFlow<NotificationPermission> = _permission.asStateFlow()
|
||||
private var trayIcon: TrayIcon? = null
|
||||
|
||||
override suspend fun refreshPermission(): NotificationPermission = permission.value
|
||||
override suspend fun requestPermission(): NotificationPermission = permission.value
|
||||
override suspend fun openSettings(): Result<Unit> = Result.failure(
|
||||
UnsupportedOperationException("Notification settings are not available on this platform"),
|
||||
)
|
||||
|
||||
override suspend fun publish(notification: LocalNotification): Result<Unit> = runCatching {
|
||||
check(permission.value == NotificationPermission.Granted) { "System notifications are not supported" }
|
||||
val icon = trayIcon ?: createTrayIcon().also {
|
||||
SystemTray.getSystemTray().add(it)
|
||||
trayIcon = it
|
||||
}
|
||||
icon.displayMessage(notification.title, notification.body, TrayIcon.MessageType.INFO)
|
||||
}
|
||||
|
||||
override suspend fun cancel(id: String) = Unit
|
||||
|
||||
override suspend fun cancelAll() {
|
||||
trayIcon?.let { SystemTray.getSystemTray().remove(it) }
|
||||
trayIcon = null
|
||||
}
|
||||
|
||||
private fun createTrayIcon(): TrayIcon {
|
||||
val image = BufferedImage(32, 32, BufferedImage.TYPE_INT_ARGB)
|
||||
val graphics = image.graphics as Graphics2D
|
||||
try {
|
||||
graphics.color = Color(83, 82, 237)
|
||||
graphics.fillOval(2, 2, 28, 28)
|
||||
graphics.color = Color.WHITE
|
||||
graphics.fillRect(14, 8, 4, 16)
|
||||
} finally {
|
||||
graphics.dispose()
|
||||
}
|
||||
return TrayIcon(image, "VniDrop").apply {
|
||||
isImageAutoSize = true
|
||||
addActionListener {
|
||||
Frame.getFrames().firstOrNull { it.isDisplayable }?.let { frame ->
|
||||
frame.isVisible = true
|
||||
frame.toFront()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user