StandAPP/app/src/main/java/com/stand/standapp/utils/DeviceInfoUtils.kt

217 lines
8.8 KiB
Kotlin

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
}
}