diff --git a/androidApp/src/main/kotlin/com/vnidrop/app/MainActivity.kt b/androidApp/src/main/kotlin/com/vnidrop/app/MainActivity.kt index 6fe4dbf..c1bef0a 100644 --- a/androidApp/src/main/kotlin/com/vnidrop/app/MainActivity.kt +++ b/androidApp/src/main/kotlin/com/vnidrop/app/MainActivity.kt @@ -13,6 +13,7 @@ class MainActivity : ComponentActivity() { enableEdgeToEdge() super.onCreate(savedInstanceState) attachAndroidFilePickerContext(this) + attachAndroidPlatformContext(this) setContent { App() diff --git a/shared/src/androidMain/kotlin/com/vnidrop/app/Platform.android.kt b/shared/src/androidMain/kotlin/com/vnidrop/app/Platform.android.kt index 3c414bf..34faa26 100644 --- a/shared/src/androidMain/kotlin/com/vnidrop/app/Platform.android.kt +++ b/shared/src/androidMain/kotlin/com/vnidrop/app/Platform.android.kt @@ -1,6 +1,11 @@ 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 java.net.NetworkInterface class AndroidPlatform : Platform { 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" override val defaultReceiveDir: String = 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() + +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() diff --git a/shared/src/commonMain/kotlin/com/vnidrop/app/Platform.kt b/shared/src/commonMain/kotlin/com/vnidrop/app/Platform.kt index 963133a..cee02ae 100644 --- a/shared/src/commonMain/kotlin/com/vnidrop/app/Platform.kt +++ b/shared/src/commonMain/kotlin/com/vnidrop/app/Platform.kt @@ -4,6 +4,15 @@ interface Platform { val name: String val defaultCoreDataDir: 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 diff --git a/shared/src/iosMain/kotlin/com/vnidrop/app/Platform.ios.kt b/shared/src/iosMain/kotlin/com/vnidrop/app/Platform.ios.kt index c84b5b4..7934fb8 100644 --- a/shared/src/iosMain/kotlin/com/vnidrop/app/Platform.ios.kt +++ b/shared/src/iosMain/kotlin/com/vnidrop/app/Platform.ios.kt @@ -1,12 +1,28 @@ package com.vnidrop.app -import platform.UIKit.UIDevice import platform.Foundation.NSTemporaryDirectory +import platform.UIKit.UIDevice 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 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() + +private fun batteryLevel(device: UIDevice): String? = + runCatching { + device.batteryMonitoringEnabled = true + val level = device.batteryLevel + if (level >= 0.0) "${(level * 100).toInt()}%" else null + }.getOrNull() diff --git a/shared/src/jvmMain/kotlin/com/vnidrop/app/Platform.jvm.kt b/shared/src/jvmMain/kotlin/com/vnidrop/app/Platform.jvm.kt index b98cfc1..085fa32 100644 --- a/shared/src/jvmMain/kotlin/com/vnidrop/app/Platform.jvm.kt +++ b/shared/src/jvmMain/kotlin/com/vnidrop/app/Platform.jvm.kt @@ -1,11 +1,37 @@ package com.vnidrop.app +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(), + 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()