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 e434ffc..01eb2b7 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 @@ -2,39 +2,517 @@ package com.xyzshell.andinfo.libs import android.Manifest import android.annotation.SuppressLint +import android.bluetooth.BluetoothAdapter +import android.bluetooth.BluetoothDevice import android.bluetooth.BluetoothManager +import android.bluetooth.le.BluetoothLeScanner +import android.bluetooth.le.ScanCallback +import android.bluetooth.le.ScanResult +import android.content.BroadcastReceiver import android.content.Context +import android.content.Intent +import android.content.IntentFilter import android.content.pm.PackageManager import android.os.Build +import androidx.core.app.ActivityCompat +/** + * 蓝牙信息工具类 + * 提供蓝牙适配器信息、已配对设备、附近设备扫描、蓝牙特性检测等功能 + */ class BluetoothInfo(private val context: Context) { + /** + * 蓝牙管理器 + */ private val bluetoothManager: BluetoothManager? = context.getSystemService( BluetoothManager::class.java ) + /** + * 蓝牙适配器 + */ @get:SuppressLint("MissingPermission") val bluetoothAdapter by lazy { bluetoothManager?.adapter } + /** + * 蓝牙设备信息数据类 + */ + data class DeviceInfo( + val name: String?, // 设备名称 + val address: String, // MAC地址 + val bondState: Int, // 配对状态 + val bondStateText: String, // 配对状态文本 + val deviceClass: Int?, // 设备类型 + val deviceType: Int?, // 设备类型(LE/Classic/Dual) + val rssi: Int? = null, // 信号强度(仅扫描设备) + val uuids: List? = null // 支持的服务UUID + ) + + /** + * 蓝牙特性信息数据类 + */ + data class BluetoothFeatures( + val version: String, // 蓝牙版本 + val bluetoothLe: Boolean, // 是否支持BLE + val bluetooth: Boolean, // 是否支持经典蓝牙 + + // Bluetooth 4.x 特性 + val le2MbPhy: Boolean, // 2Mbps PHY + val leCodedPhy: Boolean, // Coded PHY + val leExtendedAdvertising: Boolean, // 扩展广播 + val lePeriodicAdvertising: Boolean, // 周期性广播 + + // Bluetooth 5.x 特性 + val bluetooth5Support: Boolean, // Bluetooth 5支持 + val leAudioSupport: Boolean, // LE Audio支持 + val leAudioBroadcast: Boolean, // LE Audio广播 + val leAudioUnicast: Boolean, // LE Audio单播 + + // 其他特性 + val offloadedFiltering: Boolean, // 硬件过滤 + val offloadedScanBatching: Boolean, // 批量扫描 + val multipleAdvertisement: Boolean, // 多广播 + val lowEnergyExtended: Boolean // 扩展LE + ) + + // ==================== 基本信息 ==================== + + /** + * 获取蓝牙适配器名称 + * 需要权限: BLUETOOTH_CONNECT (Android 12+) + */ @get:SuppressLint("MissingPermission") val adapterName: String? - get() = bluetoothAdapter?.name + get() = if (checkBluetoothConnectPermission()) { + bluetoothAdapter?.name + } else null + /** + * 获取蓝牙适配器MAC地址 + * 需要权限: BLUETOOTH_CONNECT (Android 12+) + * 注意: Android 6.0+ 返回固定地址 02:00:00:00:00:00 + */ @get:SuppressLint("HardwareIds", "MissingPermission") val adapterMacAddress: String? - get() = bluetoothAdapter?.address + get() = if (checkBluetoothConnectPermission()) { + bluetoothAdapter?.address + } else null - @get:SuppressLint("MissingPermission") - val bondedDevices: List> - get() = bluetoothAdapter?.bondedDevices?.map { - Pair(it.name, it.address) - } ?: emptyList() + /** + * 蓝牙是否已启用 + */ + val isEnabled: Boolean + get() = bluetoothAdapter?.isEnabled == true + /** + * 蓝牙适配器状态 + */ + val state: Int + get() = bluetoothAdapter?.state ?: BluetoothAdapter.STATE_OFF + + /** + * 蓝牙适配器状态文本 + */ + val stateText: String + get() = when (state) { + BluetoothAdapter.STATE_OFF -> "关闭" + BluetoothAdapter.STATE_TURNING_ON -> "正在开启" + BluetoothAdapter.STATE_ON -> "已开启" + BluetoothAdapter.STATE_TURNING_OFF -> "正在关闭" + else -> "未知" + } + + /** + * 是否支持低功耗蓝牙(BLE) + */ val isBluetoothLeSupported: Boolean get() = context.packageManager.hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE) + /** + * 是否正在扫描设备 + */ + @get:SuppressLint("MissingPermission") + val isDiscovering: Boolean + get() = if (checkBluetoothScanPermission()) { + bluetoothAdapter?.isDiscovering == true + } else false + + // ==================== 已配对设备 ==================== + + /** + * 获取已配对设备列表(简化版本,兼容旧代码) + * 需要权限: BLUETOOTH_CONNECT (Android 12+) + */ + @get:SuppressLint("MissingPermission") + val bondedDevices: List> + get() = if (checkBluetoothConnectPermission()) { + bluetoothAdapter?.bondedDevices?.map { + Pair(it.name, it.address) + } ?: emptyList() + } else emptyList() + + /** + * 获取已配对设备详细信息列表 + * 需要权限: BLUETOOTH_CONNECT (Android 12+) + */ + @SuppressLint("MissingPermission") + fun getBondedDevicesInfo(): List { + if (!checkBluetoothConnectPermission()) return emptyList() + + return try { + bluetoothAdapter?.bondedDevices?.map { device -> + DeviceInfo( + name = device.name, + address = device.address, + bondState = device.bondState, + bondStateText = getBondStateText(device.bondState), + deviceClass = device.bluetoothClass?.deviceClass, + deviceType = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) { + device.type + } else null, + uuids = device.uuids?.map { it.toString() } + ) + } ?: emptyList() + } catch (e: SecurityException) { + emptyList() + } + } + + // ==================== 附近设备扫描 ==================== + + /** + * 扫描附近的经典蓝牙设备 + * 需要权限: BLUETOOTH_SCAN, ACCESS_FINE_LOCATION (Android 12+) + * @param onDeviceFound 发现设备回调 + * @param onScanFinished 扫描完成回调 + */ + @SuppressLint("MissingPermission") + fun scanNearbyDevices( + onDeviceFound: (DeviceInfo) -> Unit, + onScanFinished: () -> Unit + ) { + if (!checkBluetoothScanPermission() || !checkLocationPermission()) { + onScanFinished() + return + } + + val receiver = object : BroadcastReceiver() { + override fun onReceive(context: Context, intent: Intent) { + when (intent.action) { + BluetoothDevice.ACTION_FOUND -> { + val device: BluetoothDevice? = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { + intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE, BluetoothDevice::class.java) + } else { + @Suppress("DEPRECATION") + intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE) + } + val rssi = intent.getShortExtra(BluetoothDevice.EXTRA_RSSI, Short.MIN_VALUE).toInt() + + device?.let { + onDeviceFound( + DeviceInfo( + name = it.name, + address = it.address, + bondState = it.bondState, + bondStateText = getBondStateText(it.bondState), + deviceClass = it.bluetoothClass?.deviceClass, + deviceType = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) { + it.type + } else null, + rssi = rssi, + uuids = it.uuids?.map { uuid -> uuid.toString() } + ) + ) + } + } + BluetoothAdapter.ACTION_DISCOVERY_FINISHED -> { + context.unregisterReceiver(this) + onScanFinished() + } + } + } + } + + val filter = IntentFilter().apply { + addAction(BluetoothDevice.ACTION_FOUND) + addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED) + } + context.registerReceiver(receiver, filter) + bluetoothAdapter?.startDiscovery() + } + + /** + * 扫描附近的BLE设备 + * 需要权限: BLUETOOTH_SCAN, ACCESS_FINE_LOCATION (Android 12+) + * @param onDeviceFound 发现设备回调 + * @param durationMillis 扫描时长(毫秒) + */ + @SuppressLint("MissingPermission") + fun scanLeDevices( + onDeviceFound: (DeviceInfo) -> Unit, + durationMillis: Long = 10000 + ) { + if (!checkBluetoothScanPermission() || !checkLocationPermission()) { + return + } + + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) return + + val leScanner: BluetoothLeScanner? = bluetoothAdapter?.bluetoothLeScanner + val scanCallback = object : ScanCallback() { + override fun onScanResult(callbackType: Int, result: ScanResult) { + val device = result.device + onDeviceFound( + DeviceInfo( + name = device.name, + address = device.address, + bondState = device.bondState, + bondStateText = getBondStateText(device.bondState), + deviceClass = device.bluetoothClass?.deviceClass, + deviceType = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) { + device.type + } else null, + rssi = result.rssi, + uuids = result.scanRecord?.serviceUuids?.map { it.toString() } + ) + ) + } + } + + leScanner?.startScan(scanCallback) + + // 定时停止扫描 + android.os.Handler(android.os.Looper.getMainLooper()).postDelayed({ + leScanner?.stopScan(scanCallback) + }, durationMillis) + } + + /** + * 停止设备扫描 + */ + @SuppressLint("MissingPermission") + fun stopScan() { + if (checkBluetoothScanPermission()) { + bluetoothAdapter?.cancelDiscovery() + } + } + + // ==================== 蓝牙特性检测 ==================== + + /** + * 获取蓝牙特性信息 + */ + fun getBluetoothFeatures(): BluetoothFeatures { + val adapter = bluetoothAdapter + + return BluetoothFeatures( + version = getBluetoothVersion(), + bluetoothLe = isBluetoothLeSupported, + bluetooth = context.packageManager.hasSystemFeature(PackageManager.FEATURE_BLUETOOTH), + + // Bluetooth 4.x 特性 + le2MbPhy = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + adapter?.isLe2MPhySupported == true + } else false, + + leCodedPhy = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + adapter?.isLeCodedPhySupported == true + } else false, + + leExtendedAdvertising = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + adapter?.isLeExtendedAdvertisingSupported == true + } else false, + + lePeriodicAdvertising = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + adapter?.isLePeriodicAdvertisingSupported == true + } else false, + + // Bluetooth 5.x 特性 + bluetooth5Support = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + adapter?.isLe2MPhySupported == true || + adapter?.isLeCodedPhySupported == true || + adapter?.isLeExtendedAdvertisingSupported == true + } else false, + + leAudioSupport = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { + context.packageManager.hasSystemFeature("android.hardware.bluetooth.le.audio") + } else false, + + leAudioBroadcast = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { + context.packageManager.hasSystemFeature("android.hardware.bluetooth.le.audio.broadcast_source") + } else false, + + leAudioUnicast = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { + context.packageManager.hasSystemFeature("android.hardware.bluetooth.le.audio.unicast_client") + } else false, + + // 其他特性 + offloadedFiltering = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + adapter?.isOffloadedFilteringSupported == true + } else false, + + offloadedScanBatching = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + adapter?.isOffloadedScanBatchingSupported == true + } else false, + + multipleAdvertisement = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + adapter?.isMultipleAdvertisementSupported == true + } else false, + + lowEnergyExtended = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + adapter?.isLeExtendedAdvertisingSupported == true + } else false + ) + } + + /** + * 获取蓝牙版本(根据支持的特性推断) + */ + private fun getBluetoothVersion(): String { + val adapter = bluetoothAdapter ?: return "未知" + + return when { + Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU && + context.packageManager.hasSystemFeature("android.hardware.bluetooth.le.audio") -> + "Bluetooth 5.2+" + + Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && + (adapter.isLe2MPhySupported || adapter.isLeCodedPhySupported) -> + "Bluetooth 5.0" + + Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && isBluetoothLeSupported -> + "Bluetooth 4.2" + + isBluetoothLeSupported -> + "Bluetooth 4.0" + + else -> + "Bluetooth 3.0 或更低" + } + } + + // ==================== 权限检查 ==================== + + /** + * 所需权限列表 + */ val requiredPermissions: Array get() = buildList { - add(Manifest.permission.BLUETOOTH_CONNECT) + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { + add(Manifest.permission.BLUETOOTH_CONNECT) + add(Manifest.permission.BLUETOOTH_SCAN) + } else { + add(Manifest.permission.BLUETOOTH) + add(Manifest.permission.BLUETOOTH_ADMIN) + } + add(Manifest.permission.ACCESS_FINE_LOCATION) }.toTypedArray() + + /** + * 检查蓝牙连接权限 + */ + private fun checkBluetoothConnectPermission(): Boolean { + return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { + ActivityCompat.checkSelfPermission( + context, + Manifest.permission.BLUETOOTH_CONNECT + ) == PackageManager.PERMISSION_GRANTED + } else { + ActivityCompat.checkSelfPermission( + context, + Manifest.permission.BLUETOOTH + ) == PackageManager.PERMISSION_GRANTED + } + } + + /** + * 检查蓝牙扫描权限 + */ + private fun checkBluetoothScanPermission(): Boolean { + return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { + ActivityCompat.checkSelfPermission( + context, + Manifest.permission.BLUETOOTH_SCAN + ) == PackageManager.PERMISSION_GRANTED + } else { + ActivityCompat.checkSelfPermission( + context, + Manifest.permission.BLUETOOTH_ADMIN + ) == PackageManager.PERMISSION_GRANTED + } + } + + /** + * 检查位置权限 + */ + private fun checkLocationPermission(): Boolean { + return ActivityCompat.checkSelfPermission( + context, + Manifest.permission.ACCESS_FINE_LOCATION + ) == PackageManager.PERMISSION_GRANTED + } + + // ==================== 辅助方法 ==================== + + /** + * 获取配对状态文本 + */ + private fun getBondStateText(state: Int): String = when (state) { + BluetoothDevice.BOND_NONE -> "未配对" + BluetoothDevice.BOND_BONDING -> "正在配对" + BluetoothDevice.BOND_BONDED -> "已配对" + else -> "未知" + } + + /** + * 获取设备类型文本 + */ + fun getDeviceTypeText(type: Int?): String = when (type) { + BluetoothDevice.DEVICE_TYPE_CLASSIC -> "经典蓝牙" + BluetoothDevice.DEVICE_TYPE_LE -> "低功耗蓝牙" + BluetoothDevice.DEVICE_TYPE_DUAL -> "双模蓝牙" + else -> "未知" + } + + /** + * 获取文本格式的蓝牙信息摘要 + */ + fun text(): String { + val sb = StringBuilder() + + // 基本信息 + sb.append("=== 蓝牙基本信息 ===\n") + sb.append("适配器名称: ${adapterName ?: "未知"}\n") + sb.append("MAC地址: ${adapterMacAddress ?: "未知"}\n") + sb.append("状态: $stateText\n") + sb.append("是否启用: ${if (isEnabled) "是" else "否"}\n") + + // 蓝牙特性 + val features = getBluetoothFeatures() + sb.append("\n=== 蓝牙特性 ===\n") + sb.append("蓝牙版本: ${features.version}\n") + sb.append("经典蓝牙: ${if (features.bluetooth) "支持" else "不支持"}\n") + sb.append("低功耗蓝牙: ${if (features.bluetoothLe) "支持" else "不支持"}\n") + sb.append("Bluetooth 5: ${if (features.bluetooth5Support) "支持" else "不支持"}\n") + sb.append("LE Audio: ${if (features.leAudioSupport) "支持" else "不支持"}\n") + sb.append("2Mbps PHY: ${if (features.le2MbPhy) "支持" else "不支持"}\n") + sb.append("Coded PHY: ${if (features.leCodedPhy) "支持" else "不支持"}\n") + sb.append("扩展广播: ${if (features.leExtendedAdvertising) "支持" else "不支持"}\n") + sb.append("周期性广播: ${if (features.lePeriodicAdvertising) "支持" else "不支持"}\n") + + // 已配对设备 + val bonded = getBondedDevicesInfo() + sb.append("\n=== 已配对设备 (${bonded.size}) ===\n") + bonded.forEach { device -> + sb.append("设备: ${device.name ?: "未知"}\n") + sb.append(" 地址: ${device.address}\n") + sb.append(" 状态: ${device.bondStateText}\n") + device.deviceType?.let { + sb.append(" 类型: ${getDeviceTypeText(it)}\n") + } + } + + return sb.toString() + } } diff --git a/myphoneinfo/andinfo/src/main/java/com/xyzshell/andinfo/libs/BuildInfo.kt b/myphoneinfo/andinfo/src/main/java/com/xyzshell/andinfo/libs/BuildInfo.kt index a36881f..a3aa6fc 100644 --- a/myphoneinfo/andinfo/src/main/java/com/xyzshell/andinfo/libs/BuildInfo.kt +++ b/myphoneinfo/andinfo/src/main/java/com/xyzshell/andinfo/libs/BuildInfo.kt @@ -5,101 +5,257 @@ import android.os.Build // import androidx.security.state.SecurityStateManagerCompat import java.util.Date +/** + * 系统构建信息工具类 + * 提供 Android 系统构建版本、编译信息、安全补丁、JVM 信息等 + */ class BuildInfo(private val context: Context) { // private val securityStateManager = SecurityStateManagerCompat(context) // private val globalSecurityState = securityStateManager.getGlobalSecurityState() + // ==================== 构建基本信息 ==================== + + /** + * 系统构建指纹 + * 格式: brand/product/device:version/id/incremental:type/tags + * 示例: google/redfin/redfin:13/TP1A.220905.004/9012412:user/release-keys + */ val fingerprint: String get() = Build.FINGERPRINT + /** + * 构建标签 + * 通常为 "release-keys" (正式版) 或 "test-keys" (测试版) + */ val tags: String get() = Build.TAGS + /** + * 构建类型 + * 通常为 "user" (用户版)、"userdebug" (调试版) 或 "eng" (工程版) + */ val type: String get() = Build.TYPE + /** + * 系统构建日期 + * 返回构建时的时间戳 + */ val buildDate: Date get() = Date(Build.TIME) + /** + * 构建主机名 + * 编译系统的主机名 + */ val host: String get() = Build.HOST + /** + * 构建用户 + * 执行编译的用户名 + */ val user: String get() = Build.USER + /** + * 构建 ID + * 构建的唯一标识符,通常是版本号 + * 示例: TP1A.220905.004 + */ val id: String get() = Build.ID + /** + * 显示 ID + * 用于向用户显示的构建标识符 + */ val display: String get() = Build.DISPLAY + // ==================== Android 版本信息 ==================== + + /** + * Android 版本号 + * 示例: "13", "12", "11" 等 + */ val versionRelease: String get() = Build.VERSION.RELEASE + /** + * 版本代号 + * 开发阶段的代号,正式版通常为 "REL" + */ val versionCodename: String get() = Build.VERSION.CODENAME + /** + * SDK 版本号(API Level) + * 示例: 33 (Android 13), 31 (Android 12), 30 (Android 11) + */ val versionSdkInt: Int get() = Build.VERSION.SDK_INT + /** + * 预览版 SDK 版本号 + * 如果是预览版返回非 0 值,正式版返回 0 + */ val versionPreviewSdkInt: Int get() = Build.VERSION.PREVIEW_SDK_INT + /** + * 安全补丁级别 + * 格式: YYYY-MM-DD + * 示例: "2023-12-05" + */ val securityPatch: String get() = Build.VERSION.SECURITY_PATCH + /** + * 基础操作系统 + * 通常为空,除非是基于其他版本构建 + */ val baseOs: String get() = Build.VERSION.BASE_OS + /** + * 增量版本 + * 表示在同一版本号下的递增更新 + */ val incremental: String get() = Build.VERSION.INCREMENTAL + /** + * 版本号或代号 + * Android 11+ 可用 + * 如果是正式版返回版本号,预览版返回代号 + */ val releaseOrCodename: String? get() = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) Build.VERSION.RELEASE_OR_CODENAME else null + /** + * 媒体性能等级 + * Android 12+ 可用 + * 返回设备的媒体性能等级,0 表示未定义 + * 示例: 31 (Android 12), 33 (Android 13) + */ val mediaPerformanceClass: Int get() = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) Build.VERSION.MEDIA_PERFORMANCE_CLASS else 0 + /** + * 版本显示字符串 + * Android 13+ 可用 + * 用于显示的完整版本号(包括预览版) + */ val releaseOrPreviewDisplay: String? get() = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) Build.VERSION.RELEASE_OR_PREVIEW_DISPLAY else null + // ==================== JVM 信息 ==================== + + /** + * JVM 名称 + * 示例: "Dalvik" (Android) 或 "Java HotSpot(TM) 64-Bit Server VM" + */ val jvmName: String? get() = System.getProperty("java.vm.name") + /** + * JVM 供应商 + * 示例: "The Android Project" 或 "Oracle Corporation" + */ val jvmVendor: String? get() = System.getProperty("java.vm.vendor") + /** + * JVM 版本 + * 示例: "2.1.0" + */ val jvmVersion: String? get() = System.getProperty("java.vm.version") + /** + * Java 类版本 + * 示例: "50.0" (Java 6), "52.0" (Java 8) + */ val jvmClassVersion: String? get() = System.getProperty("java.class.version") + /** + * Java 规范名称 + * 示例: "Dalvik Core Library" + */ val jvmSpecificationName: String? get() = System.getProperty("java.specification.name") + /** + * Java 规范供应商 + * 示例: "The Android Project" + */ val jvmSpecificationVendor: String? get() = System.getProperty("java.specification.vendor") + /** + * Java 规范版本 + * 示例: "0.9" + */ val jvmSpecificationVersion: String? get() = System.getProperty("java.specification.version") + // ==================== 安全与内核信息 ==================== + + /** + * 厂商安全补丁级别 + * 需要 SecurityStateManagerCompat 支持 + * 某些设备可能有额外的厂商安全补丁 + */ val vendorSecurityPatchLevel: String? get() = null // globalSecurityState.getString(SecurityStateManagerCompat.KEY_VENDOR_SPL) + /** + * 内核版本 + * 需要 SecurityStateManagerCompat 支持 + * 示例: "5.10.157" + */ val kernelVersion: String? get() = null // globalSecurityState.getString(SecurityStateManagerCompat.KEY_KERNEL_VERSION) + /** + * 完整内核版本 + * 包含内核版本和编译信息 + * 示例: "5.10.157-android13-4-00001-gf0123456789a-ab9876543" + */ val kernelCompleteVersion: String? get() = System.getProperty("os.version") + /** + * Bootloader 版本 + * 设备的引导加载程序版本 + */ val bootloaderVersion: String get() = Build.BOOTLOADER + /** + * 基带版本(无线电版本) + * 移动网络基带固件版本 + */ val radioVersion: String? get() = Build.getRadioVersion() + // ==================== 分区信息 ==================== + + /** + * 指纹分区信息列表 + * Android 10+ 可用 + * 包含系统各分区的名称和指纹信息 + * + * 常见分区: + * - system: 系统分区 + * - vendor: 厂商分区 + * - product: 产品分区 + * - odm: ODM(原始设计制造商)分区 + * - system_ext: 系统扩展分区 + */ val fingerprintedPartitions: List get() = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { Build.getFingerprintedPartitions().map { PartitionInfo(it.name, it.fingerprint) } @@ -107,5 +263,72 @@ class BuildInfo(private val context: Context) { emptyList() } + /** + * 分区信息数据类 + * @property name 分区名称 + * @property fingerprint 分区指纹(构建标识) + */ data class PartitionInfo(val name: String, val fingerprint: String) + + // ==================== 辅助方法 ==================== + + /** + * 获取文本格式的构建信息摘要 + * @return 格式化的文本信息 + */ + fun text(): String { + val sb = StringBuilder() + + // 基本构建信息 + sb.append("=== 系统构建信息 ===\n") + sb.append("指纹: $fingerprint\n") + sb.append("显示ID: $display\n") + sb.append("构建ID: $id\n") + sb.append("类型: $type\n") + sb.append("标签: $tags\n") + sb.append("构建日期: $buildDate\n") + sb.append("构建用户: $user\n") + sb.append("构建主机: $host\n") + + // Android 版本信息 + sb.append("\n=== Android 版本 ===\n") + sb.append("版本号: $versionRelease\n") + sb.append("SDK版本: $versionSdkInt\n") + sb.append("代号: $versionCodename\n") + sb.append("增量版本: $incremental\n") + sb.append("安全补丁: $securityPatch\n") + releaseOrCodename?.let { sb.append("版本/代号: $it\n") } + if (mediaPerformanceClass > 0) { + sb.append("媒体性能等级: $mediaPerformanceClass\n") + } + releaseOrPreviewDisplay?.let { sb.append("版本显示: $it\n") } + if (baseOs.isNotEmpty()) { + sb.append("基础OS: $baseOs\n") + } + + // JVM 信息 + sb.append("\n=== JVM 信息 ===\n") + jvmName?.let { sb.append("JVM名称: $it\n") } + jvmVendor?.let { sb.append("JVM供应商: $it\n") } + jvmVersion?.let { sb.append("JVM版本: $it\n") } + jvmClassVersion?.let { sb.append("类版本: $it\n") } + jvmSpecificationName?.let { sb.append("规范名称: $it\n") } + jvmSpecificationVersion?.let { sb.append("规范版本: $it\n") } + + // 硬件信息 + sb.append("\n=== 硬件信息 ===\n") + sb.append("Bootloader: $bootloaderVersion\n") + radioVersion?.let { sb.append("基带版本: $it\n") } + kernelCompleteVersion?.let { sb.append("内核版本: $it\n") } + + // 分区信息 + if (fingerprintedPartitions.isNotEmpty()) { + sb.append("\n=== 分区指纹 ===\n") + fingerprintedPartitions.forEach { partition -> + sb.append("${partition.name}: ${partition.fingerprint}\n") + } + } + + return sb.toString() + } } diff --git a/myphoneinfo/andinfo/src/main/java/com/xyzshell/andinfo/libs/CameraInfo.kt b/myphoneinfo/andinfo/src/main/java/com/xyzshell/andinfo/libs/CameraInfo.kt index ad59989..685dea8 100644 --- a/myphoneinfo/andinfo/src/main/java/com/xyzshell/andinfo/libs/CameraInfo.kt +++ b/myphoneinfo/andinfo/src/main/java/com/xyzshell/andinfo/libs/CameraInfo.kt @@ -8,25 +8,111 @@ import android.hardware.camera2.CameraMetadata import android.os.Build import android.os.Handler import android.os.Looper +import android.util.Range +import android.util.SizeF import com.xyzshell.andinfo.R import com.xyzshell.andinfo.libs.camera.models.CameraCapability import com.xyzshell.andinfo.libs.camera.models.CameraFacing import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.flow.callbackFlow +import kotlin.math.sqrt +/** + * 摄像头信息工具类 + * 提供摄像头硬件参数、特性、功能列表等详细信息 + * 需要权限: CAMERA + */ class CameraInfo(private val context: Context) { + /** + * 摄像头管理器 + */ private val cameraManager: CameraManager? = context.getSystemService( CameraManager::class.java ) + /** + * 摄像头详细信息数据类 + */ + data class CameraDetails( + val cameraId: String, // 摄像头ID + val facing: CameraFacing?, // 前置/后置/外置 + val facingText: String, // 方向文本 + + // 传感器信息 + val megaPixels: Double?, // 有效百万像素 + val resolution: String?, // 分辨率(宽x高) + val sensorSize: String?, // 传感器尺寸(mm) + val sensorSizeInch: Double?, // 传感器英寸 + val pixelSize: Double?, // 单像素尺寸(μm) + val colorFilterArrangement: String?, // 滤镜颜色排列(Bayer等) + + // 光学参数 + val focalLength: FloatArray?, // 焦距(mm) + val apertures: FloatArray?, // 光圈值 + val fieldOfView: String?, // 视野角度 + val focal35mmEquivalent: Double?, // 35mm等效焦距 + val cropFactor: Double?, // 裁切系数 + + // 曝光参数 + val isoRange: Range?, // ISO感光范围 + val exposureTimeRange: Range?, // 曝光时间范围(纳秒) + val shutterSpeedRange: String?, // 快门速度范围 + val exposureCompensationRange: String?, // 曝光补偿范围 + val exposureCompensationStep: Double?, // 曝光补偿步长 + + // 对焦参数 + val minimumFocusDistance: Float?, // 最小对焦距离(屈光度) + val minimumFocusDistanceCm: Double?, // 最小对焦距离(厘米) + val hyperFocalDistance: Float?, // 超焦距 + + // 功能特性 + val hasFlash: Boolean?, // 是否有闪光灯 + val hasOpticalStabilization: Boolean?, // 光学防抖(OIS) + val hasVideoStabilization: Boolean?, // 视频防抖 + val hasAutoFocus: Boolean?, // 自动对焦 + val camera2ApiSupport: String?, // Camera2 API支持级别 + + // 面部检测 + val maxFaceCount: Int?, // 最大面部检测数量 + val faceDetectionModes: List?, // 面部检测模式列表 + + // 功能列表 + val capabilities: List, // 摄像头能力 + val supportedFeatures: List, // 支持的功能(HDR、RAW等) + + // 模式列表 + val autoExposureModes: List?, // 自动曝光模式 + val autoFocusModes: List?, // 自动对焦模式 + val whiteBalanceModes: List?, // 白平衡模式 + val sceneModes: List?, // 场景模式 + val colorEffects: List?, // 色彩效果 + + // 视频能力 + val maxFrameRate: Int?, // 最大帧率 + val supportsHighSpeedVideo: Boolean, // 是否支持高速视频 + val supports4K: Boolean, // 是否支持4K录制 + val supports8K: Boolean // 是否支持8K录制 + ) + + /** + * 获取所有摄像头ID列表 + */ val cameraIds: List get() = cameraManager?.cameraIdList?.toList() ?: emptyList() + /** + * 获取指定摄像头的特性对象 + * @param cameraId 摄像头ID + * @return CameraCharacteristics 对象 + */ fun getCameraCharacteristics(cameraId: String): CameraCharacteristics? { return cameraManager?.getCameraCharacteristics(cameraId) } + /** + * 监听摄像头可用性变化的 Flow + */ fun cameraIdsFlow() = cameraManager?.let { manager -> callbackFlow { val onCameraIdUpdated = { @@ -55,6 +141,12 @@ class CameraInfo(private val context: Context) { } } + // ==================== 基本信息 ==================== + + /** + * 获取摄像头朝向 + * @return 前置/后置/外置 + */ fun getCameraFacing(characteristics: CameraCharacteristics): CameraFacing? { return characteristics.get(CameraCharacteristics.LENS_FACING)?.let { when (it) { @@ -66,22 +158,291 @@ class CameraInfo(private val context: Context) { } } + /** + * 获取摄像头朝向文本 + */ + fun getCameraFacingText(characteristics: CameraCharacteristics): String { + return when (getCameraFacing(characteristics)) { + CameraFacing.BACK -> "后置" + CameraFacing.FRONT -> "前置" + CameraFacing.EXTERNAL -> "外置" + else -> "未知" + } + } + + // ==================== 传感器信息 ==================== + + /** + * 获取像素阵列尺寸(分辨率) + * @return 格式: "宽x高" + */ fun getPixelArraySize(characteristics: CameraCharacteristics): String? { return characteristics.get(CameraCharacteristics.SENSOR_INFO_PIXEL_ARRAY_SIZE)?.toString() } + /** + * 获取有效百万像素 + */ + fun getMegaPixels(characteristics: CameraCharacteristics): Double? { + val size = characteristics.get(CameraCharacteristics.SENSOR_INFO_PIXEL_ARRAY_SIZE) + return size?.let { (it.width * it.height) / 1_000_000.0 } + } + + /** + * 获取传感器物理尺寸 + * @return 格式: "宽x高 mm" + */ fun getSensorPhysicalSize(characteristics: CameraCharacteristics): String? { - return characteristics.get(CameraCharacteristics.SENSOR_INFO_PHYSICAL_SIZE)?.toString() + return characteristics.get(CameraCharacteristics.SENSOR_INFO_PHYSICAL_SIZE)?.let { + String.format("%.2f x %.2f mm", it.width, it.height) + } } - fun hasFlashUnit(characteristics: CameraCharacteristics): Boolean? { - return characteristics.get(CameraCharacteristics.FLASH_INFO_AVAILABLE) + /** + * 获取传感器尺寸(英寸,对角线) + */ + fun getSensorSizeInch(characteristics: CameraCharacteristics): Double? { + return characteristics.get(CameraCharacteristics.SENSOR_INFO_PHYSICAL_SIZE)?.let { + val diagonal = sqrt((it.width * it.width + it.height * it.height).toDouble()) + diagonal / 25.4 // 转换为英寸 + } } + /** + * 获取单像素尺寸(微米) + */ + fun getPixelSize(characteristics: CameraCharacteristics): Double? { + val physicalSize = characteristics.get(CameraCharacteristics.SENSOR_INFO_PHYSICAL_SIZE) + val pixelArray = characteristics.get(CameraCharacteristics.SENSOR_INFO_PIXEL_ARRAY_SIZE) + + return if (physicalSize != null && pixelArray != null) { + val pixelWidth = (physicalSize.width * 1000) / pixelArray.width + val pixelHeight = (physicalSize.height * 1000) / pixelArray.height + (pixelWidth + pixelHeight) / 2.0 + } else null + } + + /** + * 获取滤镜颜色排列 + */ + fun getColorFilterArrangement(characteristics: CameraCharacteristics): String? { + return characteristics.get(CameraCharacteristics.SENSOR_INFO_COLOR_FILTER_ARRANGEMENT)?.let { + when (it) { + CameraCharacteristics.SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_RGGB -> "RGGB (Bayer)" + CameraCharacteristics.SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_GRBG -> "GRBG (Bayer)" + CameraCharacteristics.SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_GBRG -> "GBRG (Bayer)" + CameraCharacteristics.SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_BGGR -> "BGGR (Bayer)" + CameraCharacteristics.SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_RGB -> "RGB" + 5 -> "Mono/NIR (单色/近红外)" // MONO/NIR + else -> "未知" + } + } + } + + // ==================== 光学参数 ==================== + + /** + * 获取焦距数组 + */ + fun getFocalLength(characteristics: CameraCharacteristics): FloatArray? { + return characteristics.get(CameraCharacteristics.LENS_INFO_AVAILABLE_FOCAL_LENGTHS) + } + + /** + * 获取光圈值数组 + */ fun getAvailableApertures(characteristics: CameraCharacteristics): FloatArray? { return characteristics.get(CameraCharacteristics.LENS_INFO_AVAILABLE_APERTURES) } + /** + * 获取35mm等效焦距 + */ + fun getFocal35mmEquivalent(characteristics: CameraCharacteristics): Double? { + val focalLength = getFocalLength(characteristics)?.firstOrNull() + val cropFactor = getCropFactor(characteristics) + return if (focalLength != null && cropFactor != null) { + focalLength * cropFactor + } else null + } + + /** + * 获取裁切系数(相对于全画幅) + */ + fun getCropFactor(characteristics: CameraCharacteristics): Double? { + val sensorSize = characteristics.get(CameraCharacteristics.SENSOR_INFO_PHYSICAL_SIZE) + return sensorSize?.let { + val diagonal = sqrt((it.width * it.width + it.height * it.height).toDouble()) + 43.27 / diagonal // 全画幅对角线约43.27mm + } + } + + /** + * 获取视野角度 + */ + fun getFieldOfView(characteristics: CameraCharacteristics): String? { + val focalLength = getFocalLength(characteristics)?.firstOrNull() + val sensorSize = characteristics.get(CameraCharacteristics.SENSOR_INFO_PHYSICAL_SIZE) + + return if (focalLength != null && sensorSize != null) { + val hFov = 2 * Math.toDegrees(Math.atan((sensorSize.width / 2.0 / focalLength).toDouble())) + val vFov = 2 * Math.toDegrees(Math.atan((sensorSize.height / 2.0 / focalLength).toDouble())) + String.format("H: %.1f°, V: %.1f°", hFov, vFov) + } else null + } + + // ==================== 曝光参数 ==================== + + /** + * 获取ISO感光度范围 + */ + fun getIsoRange(characteristics: CameraCharacteristics): Range? { + return characteristics.get(CameraCharacteristics.SENSOR_INFO_SENSITIVITY_RANGE) + } + + /** + * 获取曝光时间范围(纳秒) + */ + fun getExposureTimeRange(characteristics: CameraCharacteristics): Range? { + return characteristics.get(CameraCharacteristics.SENSOR_INFO_EXPOSURE_TIME_RANGE) + } + + /** + * 获取快门速度范围(格式化字符串) + */ + fun getShutterSpeedRange(characteristics: CameraCharacteristics): String? { + val exposureRange = getExposureTimeRange(characteristics) + return exposureRange?.let { + val minSeconds = it.lower / 1_000_000_000.0 + val maxSeconds = it.upper / 1_000_000_000.0 + String.format("1/%.0f - %.2fs", 1.0 / minSeconds, maxSeconds) + } + } + + /** + * 获取曝光补偿范围 + */ + fun getExposureCompensationRange(characteristics: CameraCharacteristics): String? { + val range = characteristics.get(CameraCharacteristics.CONTROL_AE_COMPENSATION_RANGE) + val step = characteristics.get(CameraCharacteristics.CONTROL_AE_COMPENSATION_STEP) + + return if (range != null && step != null) { + val minEv = range.lower * step.toDouble() + val maxEv = range.upper * step.toDouble() + String.format("%.1f ~ %.1f EV", minEv, maxEv) + } else null + } + + /** + * 获取曝光补偿步长 + */ + fun getExposureCompensationStep(characteristics: CameraCharacteristics): Double? { + return characteristics.get(CameraCharacteristics.CONTROL_AE_COMPENSATION_STEP)?.toDouble() + } + + // ==================== 对焦参数 ==================== + + /** + * 获取最小对焦距离(屈光度) + */ + fun getMinimumFocusDistance(characteristics: CameraCharacteristics): Float? { + return characteristics.get(CameraCharacteristics.LENS_INFO_MINIMUM_FOCUS_DISTANCE) + } + + /** + * 获取最小对焦距离(厘米) + */ + fun getMinimumFocusDistanceCm(characteristics: CameraCharacteristics): Double? { + val diopters = getMinimumFocusDistance(characteristics) + return diopters?.let { + if (it > 0) 100.0 / it else null + } + } + + /** + * 获取超焦距 + */ + fun getHyperFocalDistance(characteristics: CameraCharacteristics): Float? { + return characteristics.get(CameraCharacteristics.LENS_INFO_HYPERFOCAL_DISTANCE) + } + + // ==================== 功能特性 ==================== + + /** + * 是否有闪光灯 + */ + fun hasFlashUnit(characteristics: CameraCharacteristics): Boolean? { + return characteristics.get(CameraCharacteristics.FLASH_INFO_AVAILABLE) + } + + /** + * 是否支持光学防抖(OIS) + */ + fun hasOpticalStabilization(characteristics: CameraCharacteristics): Boolean { + val modes = characteristics.get(CameraCharacteristics.LENS_INFO_AVAILABLE_OPTICAL_STABILIZATION) + return modes?.contains(CameraCharacteristics.LENS_OPTICAL_STABILIZATION_MODE_ON) == true + } + + /** + * 是否支持视频防抖 + */ + fun hasVideoStabilization(characteristics: CameraCharacteristics): Boolean { + val modes = characteristics.get(CameraCharacteristics.CONTROL_AVAILABLE_VIDEO_STABILIZATION_MODES) + return modes?.contains(CameraCharacteristics.CONTROL_VIDEO_STABILIZATION_MODE_ON) == true + } + + /** + * 是否支持自动对焦 + */ + fun hasAutoFocus(characteristics: CameraCharacteristics): Boolean { + val minDistance = getMinimumFocusDistance(characteristics) + return minDistance != null && minDistance > 0 + } + + /** + * 获取Camera2 API支持级别 + */ + fun getCamera2ApiSupport(characteristics: CameraCharacteristics): String? { + return characteristics.get(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL)?.let { + when (it) { + CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED -> "LIMITED" + CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_FULL -> "FULL" + CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY -> "LEGACY" + CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_3 -> "LEVEL_3" + CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_EXTERNAL -> if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) "EXTERNAL" else "未知" + else -> "未知" + } + } + } + + // ==================== 面部检测 ==================== + + /** + * 获取最大面部检测数量 + */ + fun getMaxFaceCount(characteristics: CameraCharacteristics): Int? { + return characteristics.get(CameraCharacteristics.STATISTICS_INFO_MAX_FACE_COUNT) + } + + /** + * 获取面部检测模式列表 + */ + fun getFaceDetectionModes(characteristics: CameraCharacteristics): List? { + return characteristics.get(CameraCharacteristics.STATISTICS_INFO_AVAILABLE_FACE_DETECT_MODES)?.map { + when (it) { + CameraCharacteristics.STATISTICS_FACE_DETECT_MODE_OFF -> "关闭" + CameraCharacteristics.STATISTICS_FACE_DETECT_MODE_SIMPLE -> "简单" + CameraCharacteristics.STATISTICS_FACE_DETECT_MODE_FULL -> "完整" + else -> "未知" + } + } + } + + // ==================== 能力列表 ==================== + + /** + * 获取摄像头能力列表 + */ fun getAvailableCapabilities(characteristics: CameraCharacteristics): List { val capabilities = characteristics.get(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES) return capabilities?.toList()?.mapNotNull { capability -> @@ -111,4 +472,235 @@ class CameraInfo(private val context: Context) { } } ?: emptyList() } + + /** + * 获取支持的功能列表(人类可读) + */ + fun getSupportedFeatures(characteristics: CameraCharacteristics): List { + val features = mutableListOf() + val capabilities = getAvailableCapabilities(characteristics) + + if (capabilities.contains(CameraCapability.RAW)) features.add("RAW拍摄") + if (capabilities.contains(CameraCapability.BURST_CAPTURE)) features.add("连拍") + if (capabilities.contains(CameraCapability.DEPTH_OUTPUT)) features.add("景深输出") + if (capabilities.contains(CameraCapability.CONSTRAINED_HIGH_SPEED_VIDEO)) features.add("高速视频") + if (capabilities.contains(CameraCapability.MANUAL_SENSOR)) features.add("手动模式") + if (capabilities.contains(CameraCapability.MONOCHROME)) features.add("黑白模式") + if (capabilities.contains(CameraCapability.ULTRA_HIGH_RESOLUTION_SENSOR)) features.add("超高分辨率") + if (capabilities.contains(CameraCapability.DYNAMIC_RANGE_TEN_BIT)) features.add("10-bit HDR") + + if (hasOpticalStabilization(characteristics)) features.add("光学防抖") + if (hasVideoStabilization(characteristics)) features.add("视频防抖") + + return features + } + + // ==================== 模式列表 ==================== + + /** + * 获取自动曝光模式列表 + */ + fun getAutoExposureModes(characteristics: CameraCharacteristics): List? { + return characteristics.get(CameraCharacteristics.CONTROL_AE_AVAILABLE_MODES)?.map { + when (it) { + CameraCharacteristics.CONTROL_AE_MODE_OFF -> "关闭" + CameraCharacteristics.CONTROL_AE_MODE_ON -> "自动" + CameraCharacteristics.CONTROL_AE_MODE_ON_AUTO_FLASH -> "自动闪光" + CameraCharacteristics.CONTROL_AE_MODE_ON_ALWAYS_FLASH -> "强制闪光" + CameraCharacteristics.CONTROL_AE_MODE_ON_AUTO_FLASH_REDEYE -> "防红眼" + CameraCharacteristics.CONTROL_AE_MODE_ON_EXTERNAL_FLASH -> if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) "外置闪光" else "未知" + else -> "未知" + } + } + } + + /** + * 获取自动对焦模式列表 + */ + fun getAutoFocusModes(characteristics: CameraCharacteristics): List? { + return characteristics.get(CameraCharacteristics.CONTROL_AF_AVAILABLE_MODES)?.map { + when (it) { + CameraCharacteristics.CONTROL_AF_MODE_OFF -> "关闭" + CameraCharacteristics.CONTROL_AF_MODE_AUTO -> "自动" + CameraCharacteristics.CONTROL_AF_MODE_MACRO -> "微距" + CameraCharacteristics.CONTROL_AF_MODE_CONTINUOUS_VIDEO -> "连续视频" + CameraCharacteristics.CONTROL_AF_MODE_CONTINUOUS_PICTURE -> "连续拍照" + CameraCharacteristics.CONTROL_AF_MODE_EDOF -> "EDOF" + else -> "未知" + } + } + } + + /** + * 获取白平衡模式列表 + */ + fun getWhiteBalanceModes(characteristics: CameraCharacteristics): List? { + return characteristics.get(CameraCharacteristics.CONTROL_AWB_AVAILABLE_MODES)?.map { + when (it) { + CameraCharacteristics.CONTROL_AWB_MODE_OFF -> "关闭" + CameraCharacteristics.CONTROL_AWB_MODE_AUTO -> "自动" + CameraCharacteristics.CONTROL_AWB_MODE_INCANDESCENT -> "白炽灯" + CameraCharacteristics.CONTROL_AWB_MODE_FLUORESCENT -> "荧光灯" + CameraCharacteristics.CONTROL_AWB_MODE_WARM_FLUORESCENT -> "暖荧光" + CameraCharacteristics.CONTROL_AWB_MODE_DAYLIGHT -> "日光" + CameraCharacteristics.CONTROL_AWB_MODE_CLOUDY_DAYLIGHT -> "阴天" + CameraCharacteristics.CONTROL_AWB_MODE_TWILIGHT -> "黄昏" + CameraCharacteristics.CONTROL_AWB_MODE_SHADE -> "阴影" + else -> "未知" + } + } + } + + /** + * 获取场景模式列表 + */ + fun getSceneModes(characteristics: CameraCharacteristics): List? { + return characteristics.get(CameraCharacteristics.CONTROL_AVAILABLE_SCENE_MODES)?.map { + when (it) { + CameraCharacteristics.CONTROL_SCENE_MODE_DISABLED -> "禁用" + CameraCharacteristics.CONTROL_SCENE_MODE_FACE_PRIORITY -> "人像优先" + CameraCharacteristics.CONTROL_SCENE_MODE_ACTION -> "运动" + CameraCharacteristics.CONTROL_SCENE_MODE_PORTRAIT -> "人像" + CameraCharacteristics.CONTROL_SCENE_MODE_LANDSCAPE -> "风景" + CameraCharacteristics.CONTROL_SCENE_MODE_NIGHT -> "夜景" + CameraCharacteristics.CONTROL_SCENE_MODE_NIGHT_PORTRAIT -> "夜景人像" + CameraCharacteristics.CONTROL_SCENE_MODE_THEATRE -> "剧院" + CameraCharacteristics.CONTROL_SCENE_MODE_BEACH -> "海滩" + CameraCharacteristics.CONTROL_SCENE_MODE_SNOW -> "雪景" + CameraCharacteristics.CONTROL_SCENE_MODE_SUNSET -> "日落" + CameraCharacteristics.CONTROL_SCENE_MODE_STEADYPHOTO -> "防抖" + CameraCharacteristics.CONTROL_SCENE_MODE_FIREWORKS -> "烟火" + CameraCharacteristics.CONTROL_SCENE_MODE_SPORTS -> "体育" + CameraCharacteristics.CONTROL_SCENE_MODE_PARTY -> "派对" + CameraCharacteristics.CONTROL_SCENE_MODE_CANDLELIGHT -> "烛光" + CameraCharacteristics.CONTROL_SCENE_MODE_BARCODE -> "条形码" + CameraCharacteristics.CONTROL_SCENE_MODE_HIGH_SPEED_VIDEO -> if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) "高速视频" else "未知" + CameraCharacteristics.CONTROL_SCENE_MODE_HDR -> "HDR" + else -> "未知" + } + } + } + + /** + * 获取色彩效果列表 + */ + fun getColorEffects(characteristics: CameraCharacteristics): List? { + return characteristics.get(CameraCharacteristics.CONTROL_AVAILABLE_EFFECTS)?.map { + when (it) { + CameraCharacteristics.CONTROL_EFFECT_MODE_OFF -> "关闭" + CameraCharacteristics.CONTROL_EFFECT_MODE_MONO -> "黑白" + CameraCharacteristics.CONTROL_EFFECT_MODE_NEGATIVE -> "负片" + CameraCharacteristics.CONTROL_EFFECT_MODE_SOLARIZE -> "曝光" + CameraCharacteristics.CONTROL_EFFECT_MODE_SEPIA -> "棕褐色" + CameraCharacteristics.CONTROL_EFFECT_MODE_POSTERIZE -> "海报化" + CameraCharacteristics.CONTROL_EFFECT_MODE_WHITEBOARD -> "白板" + CameraCharacteristics.CONTROL_EFFECT_MODE_BLACKBOARD -> "黑板" + CameraCharacteristics.CONTROL_EFFECT_MODE_AQUA -> "水蓝" + else -> "未知" + } + } + } + + // ==================== 视频能力 ==================== + + /** + * 获取最大帧率 + */ + fun getMaxFrameRate(characteristics: CameraCharacteristics): Int? { + val ranges = characteristics.get(CameraCharacteristics.CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES) + return ranges?.maxOfOrNull { it.upper } + } + + /** + * 是否支持高速视频 + */ + fun supportsHighSpeedVideo(characteristics: CameraCharacteristics): Boolean { + return getAvailableCapabilities(characteristics) + .contains(CameraCapability.CONSTRAINED_HIGH_SPEED_VIDEO) + } + + /** + * 是否支持4K录制 + */ + fun supports4K(characteristics: CameraCharacteristics): Boolean { + // 简化判断,实际需要检查 StreamConfigurationMap + val resolution = getPixelArraySize(characteristics) + return resolution?.contains("3840") == true || resolution?.contains("4096") == true + } + + /** + * 是否支持8K录制 + */ + fun supports8K(characteristics: CameraCharacteristics): Boolean { + val resolution = getPixelArraySize(characteristics) + return resolution?.contains("7680") == true + } + + // ==================== 综合信息 ==================== + + /** + * 获取摄像头详细信息 + */ + fun getCameraDetails(cameraId: String): CameraDetails? { + val characteristics = getCameraCharacteristics(cameraId) ?: return null + + return CameraDetails( + cameraId = cameraId, + facing = getCameraFacing(characteristics), + facingText = getCameraFacingText(characteristics), + + megaPixels = getMegaPixels(characteristics), + resolution = getPixelArraySize(characteristics), + sensorSize = getSensorPhysicalSize(characteristics), + sensorSizeInch = getSensorSizeInch(characteristics), + pixelSize = getPixelSize(characteristics), + colorFilterArrangement = getColorFilterArrangement(characteristics), + + focalLength = getFocalLength(characteristics), + apertures = getAvailableApertures(characteristics), + fieldOfView = getFieldOfView(characteristics), + focal35mmEquivalent = getFocal35mmEquivalent(characteristics), + cropFactor = getCropFactor(characteristics), + + isoRange = getIsoRange(characteristics), + exposureTimeRange = getExposureTimeRange(characteristics), + shutterSpeedRange = getShutterSpeedRange(characteristics), + exposureCompensationRange = getExposureCompensationRange(characteristics), + exposureCompensationStep = getExposureCompensationStep(characteristics), + + minimumFocusDistance = getMinimumFocusDistance(characteristics), + minimumFocusDistanceCm = getMinimumFocusDistanceCm(characteristics), + hyperFocalDistance = getHyperFocalDistance(characteristics), + + hasFlash = hasFlashUnit(characteristics), + hasOpticalStabilization = hasOpticalStabilization(characteristics), + hasVideoStabilization = hasVideoStabilization(characteristics), + hasAutoFocus = hasAutoFocus(characteristics), + camera2ApiSupport = getCamera2ApiSupport(characteristics), + + maxFaceCount = getMaxFaceCount(characteristics), + faceDetectionModes = getFaceDetectionModes(characteristics), + + capabilities = getAvailableCapabilities(characteristics), + supportedFeatures = getSupportedFeatures(characteristics), + + autoExposureModes = getAutoExposureModes(characteristics), + autoFocusModes = getAutoFocusModes(characteristics), + whiteBalanceModes = getWhiteBalanceModes(characteristics), + sceneModes = getSceneModes(characteristics), + colorEffects = getColorEffects(characteristics), + + maxFrameRate = getMaxFrameRate(characteristics), + supportsHighSpeedVideo = supportsHighSpeedVideo(characteristics), + supports4K = supports4K(characteristics), + supports8K = supports8K(characteristics) + ) + } + + /** + * 获取所有摄像头的详细信息列表 + */ + fun getAllCameraDetails(): List { + return cameraIds.mapNotNull { getCameraDetails(it) } + } }