完善摄像头信息
This commit is contained in:
parent
25b5d7b977
commit
b8f046ec87
@ -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<String>? = 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<Pair<String?, String>>
|
||||
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<Pair<String?, String>>
|
||||
get() = if (checkBluetoothConnectPermission()) {
|
||||
bluetoothAdapter?.bondedDevices?.map {
|
||||
Pair(it.name, it.address)
|
||||
} ?: emptyList()
|
||||
} else emptyList()
|
||||
|
||||
/**
|
||||
* 获取已配对设备详细信息列表
|
||||
* 需要权限: BLUETOOTH_CONNECT (Android 12+)
|
||||
*/
|
||||
@SuppressLint("MissingPermission")
|
||||
fun getBondedDevicesInfo(): List<DeviceInfo> {
|
||||
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<String>
|
||||
get() = buildList {
|
||||
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()
|
||||
}
|
||||
}
|
||||
|
||||
@ -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<PartitionInfo>
|
||||
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()
|
||||
}
|
||||
}
|
||||
|
||||
@ -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<Int>?, // ISO感光范围
|
||||
val exposureTimeRange: Range<Long>?, // 曝光时间范围(纳秒)
|
||||
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<String>?, // 面部检测模式列表
|
||||
|
||||
// 功能列表
|
||||
val capabilities: List<CameraCapability>, // 摄像头能力
|
||||
val supportedFeatures: List<String>, // 支持的功能(HDR、RAW等)
|
||||
|
||||
// 模式列表
|
||||
val autoExposureModes: List<String>?, // 自动曝光模式
|
||||
val autoFocusModes: List<String>?, // 自动对焦模式
|
||||
val whiteBalanceModes: List<String>?, // 白平衡模式
|
||||
val sceneModes: List<String>?, // 场景模式
|
||||
val colorEffects: List<String>?, // 色彩效果
|
||||
|
||||
// 视频能力
|
||||
val maxFrameRate: Int?, // 最大帧率
|
||||
val supportsHighSpeedVideo: Boolean, // 是否支持高速视频
|
||||
val supports4K: Boolean, // 是否支持4K录制
|
||||
val supports8K: Boolean // 是否支持8K录制
|
||||
)
|
||||
|
||||
/**
|
||||
* 获取所有摄像头ID列表
|
||||
*/
|
||||
val cameraIds: List<String>
|
||||
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<Int>? {
|
||||
return characteristics.get(CameraCharacteristics.SENSOR_INFO_SENSITIVITY_RANGE)
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取曝光时间范围(纳秒)
|
||||
*/
|
||||
fun getExposureTimeRange(characteristics: CameraCharacteristics): Range<Long>? {
|
||||
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<String>? {
|
||||
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<CameraCapability> {
|
||||
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<String> {
|
||||
val features = mutableListOf<String>()
|
||||
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<String>? {
|
||||
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<String>? {
|
||||
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<String>? {
|
||||
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<String>? {
|
||||
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<String>? {
|
||||
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<CameraDetails> {
|
||||
return cameraIds.mapNotNull { getCameraDetails(it) }
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user