package com.stand.standapp.utils import android.app.ActivityManager import android.content.Context import android.content.Intent import android.content.IntentFilter import android.net.ConnectivityManager import android.net.NetworkCapabilities import android.os.Build import android.os.Environment import android.os.Process import android.os.StatFs import org.json.JSONObject import timber.log.Timber import java.net.NetworkInterface import java.util.Collections import java.util.Locale object DeviceInfoUtils { fun getDeviceInfoJson(context: Context): JSONObject { val json = JSONObject() val appContext = context.applicationContext // Base App info is typically set at the caller level, but we populate core details try { json.put("model", Build.MODEL) json.put("osVersion", Build.VERSION.RELEASE) val abis = Build.SUPPORTED_ABIS json.put("cpuAbi", abis.joinToString(",")) val is64Bit = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { Process.is64Bit() } else { abis.any { it.contains("64") } } json.put("is64Bit", is64Bit) } catch (e: Exception) { Timber.tag("DeviceInfoUtils").e(e, "Failed to get base device info") } // 1. Screen and display info try { val screenObj = JSONObject() val metrics = appContext.resources.displayMetrics val widthPixels = metrics.widthPixels val heightPixels = metrics.heightPixels val xdpi = metrics.xdpi val ydpi = metrics.ydpi screenObj.put("width", widthPixels) screenObj.put("height", heightPixels) screenObj.put("density", metrics.density) screenObj.put("densityDpi", metrics.densityDpi) val physicalSize = if (xdpi > 0 && ydpi > 0) { val widthInches = widthPixels / xdpi val heightInches = heightPixels / ydpi Math.sqrt((widthInches * widthInches + heightInches * heightInches).toDouble()) } else { 0.0 } screenObj.put("physicalSize", Math.round(physicalSize * 10.0) / 10.0) // 1 decimal place json.put("screen", screenObj) } catch (e: Exception) { Timber.tag("DeviceInfoUtils").e(e, "Failed to get screen metrics") } // 2. Hardware and manufacturer info try { val hwObj = JSONObject() hwObj.put("manufacturer", Build.MANUFACTURER) hwObj.put("brand", Build.BRAND) hwObj.put("hardware", Build.HARDWARE) hwObj.put("fingerprint", Build.FINGERPRINT) json.put("hardware", hwObj) } catch (e: Exception) { Timber.tag("DeviceInfoUtils").e(e, "Failed to get hardware info") } // 3. System and Locale environment try { val sysObj = JSONObject() sysObj.put("sdkVersion", Build.VERSION.SDK_INT) sysObj.put("language", Locale.getDefault().language) sysObj.put("country", Locale.getDefault().country) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { sysObj.put("securityPatch", Build.VERSION.SECURITY_PATCH) } else { sysObj.put("securityPatch", "") } json.put("system", sysObj) } catch (e: Exception) { Timber.tag("DeviceInfoUtils").e(e, "Failed to get system info") } // 4. Memory Info (RAM) try { val memObj = JSONObject() val actManager = appContext.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager val memInfo = ActivityManager.MemoryInfo() actManager.getMemoryInfo(memInfo) memObj.put("totalRam", memInfo.totalMem) memObj.put("availRam", memInfo.availMem) json.put("memory", memObj) } catch (e: Exception) { Timber.tag("DeviceInfoUtils").e(e, "Failed to get memory info") } // 5. Storage Info try { val storageObj = JSONObject() val path = Environment.getDataDirectory() val stat = StatFs(path.path) val blockSize = stat.blockSizeLong val totalBlocks = stat.blockCountLong val availableBlocks = stat.availableBlocksLong storageObj.put("totalInternal", totalBlocks * blockSize) storageObj.put("availInternal", availableBlocks * blockSize) json.put("storage", storageObj) } catch (e: Exception) { Timber.tag("DeviceInfoUtils").e(e, "Failed to get storage info") } // 6. Network state try { val netObj = JSONObject() val connManager = appContext.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager var networkType = "NONE" if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { val activeNetwork = connManager.activeNetwork val capabilities = connManager.getNetworkCapabilities(activeNetwork) if (capabilities != null) { if (capabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)) { networkType = "WIFI" } else if (capabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)) { networkType = "CELLULAR" } } } else { @Suppress("DEPRECATION") val activeNetworkInfo = connManager.activeNetworkInfo if (activeNetworkInfo != null && activeNetworkInfo.isConnected) { if (activeNetworkInfo.type == ConnectivityManager.TYPE_WIFI) { networkType = "WIFI" } else if (activeNetworkInfo.type == ConnectivityManager.TYPE_MOBILE) { networkType = "CELLULAR" } } } netObj.put("type", networkType) var ipAddress = "" try { val interfaces = Collections.list(NetworkInterface.getNetworkInterfaces()) for (intf in interfaces) { val addrs = Collections.list(intf.inetAddresses) for (addr in addrs) { if (!addr.isLoopbackAddress) { val sAddr = addr.hostAddress ?: continue val isIPv4 = sAddr.indexOf(':') < 0 if (isIPv4) { ipAddress = sAddr break } } } if (ipAddress.isNotEmpty()) break } } catch (e: Exception) { Timber.tag("DeviceInfoUtils").w(e, "Failed to resolve IP address") } netObj.put("ipAddress", ipAddress) json.put("network", netObj) } catch (e: Exception) { Timber.tag("DeviceInfoUtils").e(e, "Failed to get network status") } // 7. Battery status try { val batObj = JSONObject() val batteryStatus: Intent? = appContext.registerReceiver(null, IntentFilter(Intent.ACTION_BATTERY_CHANGED)) val level = batteryStatus?.getIntExtra(android.os.BatteryManager.EXTRA_LEVEL, -1) ?: -1 val scale = batteryStatus?.getIntExtra(android.os.BatteryManager.EXTRA_SCALE, -1) ?: -1 val batteryPct = if (level >= 0 && scale > 0) (level * 100 / scale.toFloat()).toInt() else -1 batObj.put("level", batteryPct) val status = batteryStatus?.getIntExtra(android.os.BatteryManager.EXTRA_STATUS, -1) ?: -1 val statusString = when (status) { android.os.BatteryManager.BATTERY_STATUS_CHARGING -> "charging" android.os.BatteryManager.BATTERY_STATUS_DISCHARGING -> "discharging" android.os.BatteryManager.BATTERY_STATUS_FULL -> "full" android.os.BatteryManager.BATTERY_STATUS_NOT_CHARGING -> "not_charging" else -> "unknown" } batObj.put("status", statusString) val chargePlug = batteryStatus?.getIntExtra(android.os.BatteryManager.EXTRA_PLUGGED, -1) ?: -1 val plugString = when (chargePlug) { android.os.BatteryManager.BATTERY_PLUGGED_AC -> "ac" android.os.BatteryManager.BATTERY_PLUGGED_USB -> "usb" android.os.BatteryManager.BATTERY_PLUGGED_WIRELESS -> "wireless" else -> "none" } batObj.put("plugged", plugString) json.put("battery", batObj) } catch (e: Exception) { Timber.tag("DeviceInfoUtils").e(e, "Failed to get battery info") } return json } }