feat(platform): add device info and network summary support

This adds DeviceInfo to the Platform interface and implements it for Android, iOS, and JVM, including battery level and active network summary where available.

Co-authored-by: Junie <junie@jetbrains.com>
This commit is contained in:
2026-07-07 16:55:27 +02:00
parent 3d52e62e81
commit 912874fd0c
5 changed files with 109 additions and 2 deletions

View File

@@ -13,6 +13,7 @@ class MainActivity : ComponentActivity() {
enableEdgeToEdge() enableEdgeToEdge()
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
attachAndroidFilePickerContext(this) attachAndroidFilePickerContext(this)
attachAndroidPlatformContext(this)
setContent { setContent {
App() App()

View File

@@ -1,6 +1,11 @@
package com.vnidrop.app package com.vnidrop.app
import android.content.Context
import android.net.ConnectivityManager
import android.net.NetworkCapabilities
import android.os.BatteryManager
import android.os.Build import android.os.Build
import java.net.NetworkInterface
class AndroidPlatform : Platform { class AndroidPlatform : Platform {
override val name: String = "Android ${Build.VERSION.SDK_INT}" override val name: String = "Android ${Build.VERSION.SDK_INT}"
@@ -8,6 +13,56 @@ class AndroidPlatform : Platform {
System.getProperty("java.io.tmpdir") ?: "/data/local/tmp/vnidrop" System.getProperty("java.io.tmpdir") ?: "/data/local/tmp/vnidrop"
override val defaultReceiveDir: String = override val defaultReceiveDir: String =
System.getProperty("java.io.tmpdir") ?: "/data/local/tmp/vnidrop-receive" System.getProperty("java.io.tmpdir") ?: "/data/local/tmp/vnidrop-receive"
override val deviceInfo: DeviceInfo = DeviceInfo(
deviceName = Build.DEVICE,
deviceModel = listOf(Build.MANUFACTURER, Build.MODEL)
.filter { it.isNotBlank() }
.joinToString(" ")
.ifBlank { null },
operatingSystem = "Android ${Build.VERSION.RELEASE} (API ${Build.VERSION.SDK_INT})",
network = activeNetworkSummary(),
batteryLevel = batteryLevel(),
)
} }
actual fun getPlatform(): Platform = AndroidPlatform() actual fun getPlatform(): Platform = AndroidPlatform()
fun attachAndroidPlatformContext(context: Context) {
AndroidPlatformContextHolder.context = context.applicationContext
}
private object AndroidPlatformContextHolder {
var context: Context? = null
}
private fun activeNetworkSummary(): String? =
runCatching {
val context = AndroidPlatformContextHolder.context ?: return@runCatching networkInterfaceName()
val manager = context.getSystemService(Context.CONNECTIVITY_SERVICE) as? ConnectivityManager
?: return@runCatching networkInterfaceName()
val activeNetwork = manager.activeNetwork ?: return@runCatching networkInterfaceName()
val capabilities = manager.getNetworkCapabilities(activeNetwork) ?: return@runCatching networkInterfaceName()
when {
capabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) -> "Wi-Fi"
capabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) -> "Mobile"
capabilities.hasTransport(NetworkCapabilities.TRANSPORT_ETHERNET) -> "Ethernet"
capabilities.hasTransport(NetworkCapabilities.TRANSPORT_VPN) -> "VPN"
else -> networkInterfaceName()
}
}.getOrNull()
private fun batteryLevel(): String? =
runCatching {
val context = AndroidPlatformContextHolder.context ?: return@runCatching null
val manager = context.getSystemService(BatteryManager::class.java)
val level = manager.getIntProperty(BatteryManager.BATTERY_PROPERTY_CAPACITY)
level.takeIf { it >= 0 }?.let { "$it%" }
}.getOrNull()
private fun networkInterfaceName(): String? =
NetworkInterface.getNetworkInterfaces()
.asSequence()
.filter { it.isUp && !it.isLoopback }
.map { it.displayName }
.firstOrNull()

View File

@@ -4,6 +4,15 @@ interface Platform {
val name: String val name: String
val defaultCoreDataDir: String val defaultCoreDataDir: String
val defaultReceiveDir: String val defaultReceiveDir: String
val deviceInfo: DeviceInfo
} }
data class DeviceInfo(
val deviceName: String?,
val deviceModel: String?,
val operatingSystem: String,
val network: String?,
val batteryLevel: String?,
)
expect fun getPlatform(): Platform expect fun getPlatform(): Platform

View File

@@ -1,12 +1,28 @@
package com.vnidrop.app package com.vnidrop.app
import platform.UIKit.UIDevice
import platform.Foundation.NSTemporaryDirectory import platform.Foundation.NSTemporaryDirectory
import platform.UIKit.UIDevice
class IOSPlatform : Platform { class IOSPlatform : Platform {
override val name: String = UIDevice.currentDevice.systemName() + " " + UIDevice.currentDevice.systemVersion private val device: UIDevice = UIDevice.currentDevice
override val name: String = device.systemName() + " " + device.systemVersion
override val defaultCoreDataDir: String = NSTemporaryDirectory() + "vnidrop" override val defaultCoreDataDir: String = NSTemporaryDirectory() + "vnidrop"
override val defaultReceiveDir: String = NSTemporaryDirectory() + "vnidrop-receive" override val defaultReceiveDir: String = NSTemporaryDirectory() + "vnidrop-receive"
override val deviceInfo: DeviceInfo = DeviceInfo(
deviceName = device.name,
deviceModel = device.model,
operatingSystem = device.systemName() + " " + device.systemVersion,
network = null,
batteryLevel = batteryLevel(device),
)
} }
actual fun getPlatform(): Platform = IOSPlatform() actual fun getPlatform(): Platform = IOSPlatform()
private fun batteryLevel(device: UIDevice): String? =
runCatching {
device.batteryMonitoringEnabled = true
val level = device.batteryLevel
if (level >= 0.0) "${(level * 100).toInt()}%" else null
}.getOrNull()

View File

@@ -1,11 +1,37 @@
package com.vnidrop.app package com.vnidrop.app
import java.net.NetworkInterface
class JVMPlatform : Platform { class JVMPlatform : Platform {
override val name: String = "Java ${System.getProperty("java.version")}" override val name: String = "Java ${System.getProperty("java.version")}"
override val defaultCoreDataDir: String = override val defaultCoreDataDir: String =
System.getProperty("user.home") + "/.vnidrop" System.getProperty("user.home") + "/.vnidrop"
override val defaultReceiveDir: String = override val defaultReceiveDir: String =
System.getProperty("user.home") + "/Downloads" 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(),
batteryLevel = null,
)
} }
actual fun getPlatform(): Platform = JVMPlatform() actual fun getPlatform(): Platform = JVMPlatform()
private fun activeNetworkSummary(): String? =
runCatching {
NetworkInterface.getNetworkInterfaces()
.asSequence()
.filter { it.isUp && !it.isLoopback }
.map { it.displayName }
.firstOrNull()
}.getOrNull()