diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 555bd02..f604995 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -7,7 +7,7 @@ apiVersion = "82" datastore = "1.1.7" datastorePreferences = "1.1.7" googleSymbolProcessingApi = "2.0.0-1.0.24" -gson = "2.13.1" +gson = "2.13.2" jsonVersion = "20250517" kotlin = "2.0.0" coreKtx = "1.16.0" diff --git a/myphoneinfo/andinfo/build.gradle.kts b/myphoneinfo/andinfo/build.gradle.kts index 9aeed01..34ba0b3 100644 --- a/myphoneinfo/andinfo/build.gradle.kts +++ b/myphoneinfo/andinfo/build.gradle.kts @@ -50,6 +50,8 @@ dependencies { implementation(libs.androidx.datastore.preferences) // implementation("androidx.datastore:datastore-preferences-rxjava3:1.1.7") implementation(libs.androidx.datastore) + implementation(libs.squareup.okhttp) + implementation(libs.gson) // implementation("androidx.datastore:datastore-rxjava3:1.1.7") implementation("androidx.security:security-state:1.0.0-alpha04") testImplementation(libs.junit) diff --git a/myphoneinfo/andinfo/src/main/java/com/xyzshell/andinfo/libs/BluetoothInfo.kt b/myphoneinfo/andinfo/src/main/java/com/xyzshell/andinfo/libs/BluetoothInfo.kt index fba915e..e434ffc 100644 --- a/myphoneinfo/andinfo/src/main/java/com/xyzshell/andinfo/libs/BluetoothInfo.kt +++ b/myphoneinfo/andinfo/src/main/java/com/xyzshell/andinfo/libs/BluetoothInfo.kt @@ -35,11 +35,6 @@ class BluetoothInfo(private val context: Context) { val requiredPermissions: Array get() = buildList { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { - add(Manifest.permission.BLUETOOTH_CONNECT) - } else { - add(Manifest.permission.BLUETOOTH) - add(Manifest.permission.BLUETOOTH_ADMIN) - } + add(Manifest.permission.BLUETOOTH_CONNECT) }.toTypedArray() } diff --git a/myphoneinfo/andinfo/src/main/java/com/xyzshell/andinfo/libs/MemInfo.kt b/myphoneinfo/andinfo/src/main/java/com/xyzshell/andinfo/libs/MemInfo.kt index 8b0e8fe..c8dd9be 100644 --- a/myphoneinfo/andinfo/src/main/java/com/xyzshell/andinfo/libs/MemInfo.kt +++ b/myphoneinfo/andinfo/src/main/java/com/xyzshell/andinfo/libs/MemInfo.kt @@ -1,4 +1,141 @@ package com.xyzshell.andinfo.libs +import java.io.File +import java.io.RandomAccessFile + +data class MemoryInfo( + val totalRam: Long, // 总内存 (字节) + val availableRam: Long, // 可用内存 (字节) + val usedRam: Long, // 已使用内存 (字节) + val memType: String, // 内存类型 (LPDDR4/LPDDR5等) + val channels: Int, // 内存通道数 + val zramTotal: Long, // zram 总大小 (字节) + val zramUsed: Long // zram 已使用 (字节) +) + class MemInfo { + + fun getMemoryInfo(): MemoryInfo { + val totalRam = getTotalMemory() + val availableRam = getAvailableMemory() + val usedRam = totalRam - availableRam + val memType = getMemoryType() + val channels = getMemoryChannels() + val (zramTotal, zramUsed) = getZramInfo() + + return MemoryInfo( + totalRam = totalRam, + availableRam = availableRam, + usedRam = usedRam, + memType = memType, + channels = channels, + zramTotal = zramTotal, + zramUsed = zramUsed + ) + } + + private fun getTotalMemory(): Long { + return try { + val memInfo = File("/proc/meminfo") + memInfo.readLines().find { it.startsWith("MemTotal:") }?.let { + it.split("\\s+".toRegex())[1].toLong() * 1024 + } ?: 0L + } catch (e: Exception) { + 0L + } + } + + private fun getAvailableMemory(): Long { + return try { + val memInfo = File("/proc/meminfo") + memInfo.readLines().find { it.startsWith("MemAvailable:") }?.let { + it.split("\\s+".toRegex())[1].toLong() * 1024 + } ?: 0L + } catch (e: Exception) { + 0L + } + } + + private fun getMemoryType(): String { + return try { + // 尝试从多个可能的位置读取 + val paths = listOf( + "/sys/class/devfreq/ddrfreq/device/type", + "/sys/class/devfreq/soc:qcom,cpu-cpu-llcc-bw/device/type", + "/proc/device-tree/memory/device_type" + ) + + for (path in paths) { + val file = File(path) + if (file.exists()) { + val content = file.readText().trim() + if (content.isNotEmpty()) { + return content + } + } + } + + // 尝试从 getprop 获取 + Runtime.getRuntime().exec("getprop ro.boot.ddr_type").inputStream.bufferedReader().readText().trim() + .ifEmpty { "Unknown" } + } catch (e: Exception) { + "Unknown" + } + } + + private fun getMemoryChannels(): Int { + return try { + // 尝试从设备树或内核日志获取 + val dmesgChannels = Runtime.getRuntime() + .exec("dmesg | grep -i 'memory.*channel'") + .inputStream.bufferedReader().readText() + + // 解析通道数,默认返回 2 + when { + dmesgChannels.contains("dual", ignoreCase = true) -> 2 + dmesgChannels.contains("quad", ignoreCase = true) -> 4 + dmesgChannels.contains("single", ignoreCase = true) -> 1 + else -> 2 // 默认双通道 + } + } catch (e: Exception) { + 2 + } + } + + private fun getZramInfo(): Pair { + return try { + var total = 0L + var used = 0L + + // 检查所有 zram 设备 + for (i in 0..7) { + val devicePath = "/sys/block/zram$i" + if (!File(devicePath).exists()) continue + + // 读取总大小 + File("$devicePath/disksize").readText().trim().toLongOrNull()?.let { + total += it + } + + // 读取已使用大小 + File("$devicePath/mem_used_total").readText().trim().toLongOrNull()?.let { + used += it + } + } + + Pair(total, used) + } catch (e: Exception) { + Pair(0L, 0L) + } + } + + // 格式化内存大小 + fun formatBytes(bytes: Long): String { + return when { + bytes < 1024 -> "$bytes B" + bytes < 1024 * 1024 -> String.format("%.2f KB", bytes / 1024.0) + bytes < 1024 * 1024 * 1024 -> String.format("%.2f MB", bytes / (1024.0 * 1024)) + else -> String.format("%.2f GB", bytes / (1024.0 * 1024 * 1024)) + } + } } \ No newline at end of file diff --git a/myphoneinfo/andinfo/src/main/java/com/xyzshell/andinfo/libs/NetworkInfo.kt b/myphoneinfo/andinfo/src/main/java/com/xyzshell/andinfo/libs/NetworkInfo.kt new file mode 100644 index 0000000..e115237 --- /dev/null +++ b/myphoneinfo/andinfo/src/main/java/com/xyzshell/andinfo/libs/NetworkInfo.kt @@ -0,0 +1,4 @@ +package com.xyzshell.andinfo.libs + +class NetworkInfo { +} \ No newline at end of file diff --git a/myphoneinfo/andinfo/src/main/java/com/xyzshell/andinfo/utils/WebService.kt b/myphoneinfo/andinfo/src/main/java/com/xyzshell/andinfo/utils/WebService.kt new file mode 100644 index 0000000..9746309 --- /dev/null +++ b/myphoneinfo/andinfo/src/main/java/com/xyzshell/andinfo/utils/WebService.kt @@ -0,0 +1,94 @@ +package com.xyzshell.andinfo.utils + +import com.google.gson.Gson +import okhttp3.* +import okhttp3.MediaType.Companion.toMediaType +import okhttp3.RequestBody.Companion.toRequestBody +import java.io.IOException + +class WebService { + private val client = OkHttpClient() + val gson = Gson() + fun postData(url: String, jsonData: String, callback: (String?, Exception?) -> Unit) { + val mediaType = "application/json; charset=utf-8".toMediaType() + val body = jsonData.toRequestBody(mediaType) + + val request = Request.Builder() + .url(url) + .post(body) + .build() + + client.newCall(request).enqueue(object : Callback { + override fun onFailure(call: Call, e: IOException) { + callback(null, e) + } + + override fun onResponse(call: Call, response: Response) { + response.use { + if (it.isSuccessful) { + callback(it.body?.string(), null) + } else { + callback(null, Exception("HTTP ${it.code}")) + } + } + } + }) + } + + // 同步版本 + fun postDataSync(url: String, jsonData: String): String? { + val mediaType = "application/json; charset=utf-8".toMediaType() + val body = jsonData.toRequestBody(mediaType) + + val request = Request.Builder() + .url(url) + .post(body) + .build() + + client.newCall(request).execute().use { response -> + return if (response.isSuccessful) { + response.body?.string() + } else { + null + } + } + } + + // 序列化对象为 JSON + fun toJson(obj: T): String { + return gson.toJson(obj) + } + + // 反序列化 JSON 为对象 + inline fun fromJson(json: String): T? { + return try { + gson.fromJson(json, T::class.java) + } catch (e: Exception) { + null + } + } + + // 带序列化的 POST 请求 + fun postObject(url: String, obj: T, callback: (String?, Exception?) -> Unit) { + val jsonData = toJson(obj) + postData(url, jsonData, callback) + } + // 带序列化和反序列化的 POST 请求 + inline fun postAndParse( + url: String, + obj: T, + crossinline callback: (R?, Exception?) -> Unit + ) { + val jsonData = toJson(obj) + postData(url, jsonData) { response, error -> + if (error != null) { + callback(null, error) + } else if (response != null) { + val result = fromJson(response) + callback(result, null) + } else { + callback(null, Exception("Empty response")) + } + } + } +} \ No newline at end of file