完善摄像头信息
This commit is contained in:
parent
25b5d7b977
commit
b8f046ec87
@ -2,39 +2,517 @@ package com.xyzshell.andinfo.libs
|
|||||||
|
|
||||||
import android.Manifest
|
import android.Manifest
|
||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
|
import android.bluetooth.BluetoothAdapter
|
||||||
|
import android.bluetooth.BluetoothDevice
|
||||||
import android.bluetooth.BluetoothManager
|
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.Context
|
||||||
|
import android.content.Intent
|
||||||
|
import android.content.IntentFilter
|
||||||
import android.content.pm.PackageManager
|
import android.content.pm.PackageManager
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
|
import androidx.core.app.ActivityCompat
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 蓝牙信息工具类
|
||||||
|
* 提供蓝牙适配器信息、已配对设备、附近设备扫描、蓝牙特性检测等功能
|
||||||
|
*/
|
||||||
class BluetoothInfo(private val context: Context) {
|
class BluetoothInfo(private val context: Context) {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 蓝牙管理器
|
||||||
|
*/
|
||||||
private val bluetoothManager: BluetoothManager? = context.getSystemService(
|
private val bluetoothManager: BluetoothManager? = context.getSystemService(
|
||||||
BluetoothManager::class.java
|
BluetoothManager::class.java
|
||||||
)
|
)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 蓝牙适配器
|
||||||
|
*/
|
||||||
@get:SuppressLint("MissingPermission")
|
@get:SuppressLint("MissingPermission")
|
||||||
val bluetoothAdapter by lazy { bluetoothManager?.adapter }
|
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")
|
@get:SuppressLint("MissingPermission")
|
||||||
val adapterName: String?
|
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")
|
@get:SuppressLint("HardwareIds", "MissingPermission")
|
||||||
val adapterMacAddress: String?
|
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)
|
val isEnabled: Boolean
|
||||||
} ?: emptyList()
|
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
|
val isBluetoothLeSupported: Boolean
|
||||||
get() = context.packageManager.hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)
|
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>
|
val requiredPermissions: Array<String>
|
||||||
get() = buildList {
|
get() = buildList {
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
||||||
add(Manifest.permission.BLUETOOTH_CONNECT)
|
add(Manifest.permission.BLUETOOTH_CONNECT)
|
||||||
}.toTypedArray()
|
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 androidx.security.state.SecurityStateManagerCompat
|
||||||
import java.util.Date
|
import java.util.Date
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 系统构建信息工具类
|
||||||
|
* 提供 Android 系统构建版本、编译信息、安全补丁、JVM 信息等
|
||||||
|
*/
|
||||||
class BuildInfo(private val context: Context) {
|
class BuildInfo(private val context: Context) {
|
||||||
|
|
||||||
// private val securityStateManager = SecurityStateManagerCompat(context)
|
// private val securityStateManager = SecurityStateManagerCompat(context)
|
||||||
// private val globalSecurityState = securityStateManager.getGlobalSecurityState()
|
// 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
|
val fingerprint: String
|
||||||
get() = Build.FINGERPRINT
|
get() = Build.FINGERPRINT
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构建标签
|
||||||
|
* 通常为 "release-keys" (正式版) 或 "test-keys" (测试版)
|
||||||
|
*/
|
||||||
val tags: String
|
val tags: String
|
||||||
get() = Build.TAGS
|
get() = Build.TAGS
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构建类型
|
||||||
|
* 通常为 "user" (用户版)、"userdebug" (调试版) 或 "eng" (工程版)
|
||||||
|
*/
|
||||||
val type: String
|
val type: String
|
||||||
get() = Build.TYPE
|
get() = Build.TYPE
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 系统构建日期
|
||||||
|
* 返回构建时的时间戳
|
||||||
|
*/
|
||||||
val buildDate: Date
|
val buildDate: Date
|
||||||
get() = Date(Build.TIME)
|
get() = Date(Build.TIME)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构建主机名
|
||||||
|
* 编译系统的主机名
|
||||||
|
*/
|
||||||
val host: String
|
val host: String
|
||||||
get() = Build.HOST
|
get() = Build.HOST
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构建用户
|
||||||
|
* 执行编译的用户名
|
||||||
|
*/
|
||||||
val user: String
|
val user: String
|
||||||
get() = Build.USER
|
get() = Build.USER
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构建 ID
|
||||||
|
* 构建的唯一标识符,通常是版本号
|
||||||
|
* 示例: TP1A.220905.004
|
||||||
|
*/
|
||||||
val id: String
|
val id: String
|
||||||
get() = Build.ID
|
get() = Build.ID
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 显示 ID
|
||||||
|
* 用于向用户显示的构建标识符
|
||||||
|
*/
|
||||||
val display: String
|
val display: String
|
||||||
get() = Build.DISPLAY
|
get() = Build.DISPLAY
|
||||||
|
|
||||||
|
// ==================== Android 版本信息 ====================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Android 版本号
|
||||||
|
* 示例: "13", "12", "11" 等
|
||||||
|
*/
|
||||||
val versionRelease: String
|
val versionRelease: String
|
||||||
get() = Build.VERSION.RELEASE
|
get() = Build.VERSION.RELEASE
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 版本代号
|
||||||
|
* 开发阶段的代号,正式版通常为 "REL"
|
||||||
|
*/
|
||||||
val versionCodename: String
|
val versionCodename: String
|
||||||
get() = Build.VERSION.CODENAME
|
get() = Build.VERSION.CODENAME
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SDK 版本号(API Level)
|
||||||
|
* 示例: 33 (Android 13), 31 (Android 12), 30 (Android 11)
|
||||||
|
*/
|
||||||
val versionSdkInt: Int
|
val versionSdkInt: Int
|
||||||
get() = Build.VERSION.SDK_INT
|
get() = Build.VERSION.SDK_INT
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 预览版 SDK 版本号
|
||||||
|
* 如果是预览版返回非 0 值,正式版返回 0
|
||||||
|
*/
|
||||||
val versionPreviewSdkInt: Int
|
val versionPreviewSdkInt: Int
|
||||||
get() = Build.VERSION.PREVIEW_SDK_INT
|
get() = Build.VERSION.PREVIEW_SDK_INT
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 安全补丁级别
|
||||||
|
* 格式: YYYY-MM-DD
|
||||||
|
* 示例: "2023-12-05"
|
||||||
|
*/
|
||||||
val securityPatch: String
|
val securityPatch: String
|
||||||
get() = Build.VERSION.SECURITY_PATCH
|
get() = Build.VERSION.SECURITY_PATCH
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 基础操作系统
|
||||||
|
* 通常为空,除非是基于其他版本构建
|
||||||
|
*/
|
||||||
val baseOs: String
|
val baseOs: String
|
||||||
get() = Build.VERSION.BASE_OS
|
get() = Build.VERSION.BASE_OS
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 增量版本
|
||||||
|
* 表示在同一版本号下的递增更新
|
||||||
|
*/
|
||||||
val incremental: String
|
val incremental: String
|
||||||
get() = Build.VERSION.INCREMENTAL
|
get() = Build.VERSION.INCREMENTAL
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 版本号或代号
|
||||||
|
* Android 11+ 可用
|
||||||
|
* 如果是正式版返回版本号,预览版返回代号
|
||||||
|
*/
|
||||||
val releaseOrCodename: String?
|
val releaseOrCodename: String?
|
||||||
get() = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) Build.VERSION.RELEASE_OR_CODENAME else null
|
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
|
val mediaPerformanceClass: Int
|
||||||
get() = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) Build.VERSION.MEDIA_PERFORMANCE_CLASS else 0
|
get() = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) Build.VERSION.MEDIA_PERFORMANCE_CLASS else 0
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 版本显示字符串
|
||||||
|
* Android 13+ 可用
|
||||||
|
* 用于显示的完整版本号(包括预览版)
|
||||||
|
*/
|
||||||
val releaseOrPreviewDisplay: String?
|
val releaseOrPreviewDisplay: String?
|
||||||
get() = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) Build.VERSION.RELEASE_OR_PREVIEW_DISPLAY else null
|
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?
|
val jvmName: String?
|
||||||
get() = System.getProperty("java.vm.name")
|
get() = System.getProperty("java.vm.name")
|
||||||
|
|
||||||
|
/**
|
||||||
|
* JVM 供应商
|
||||||
|
* 示例: "The Android Project" 或 "Oracle Corporation"
|
||||||
|
*/
|
||||||
val jvmVendor: String?
|
val jvmVendor: String?
|
||||||
get() = System.getProperty("java.vm.vendor")
|
get() = System.getProperty("java.vm.vendor")
|
||||||
|
|
||||||
|
/**
|
||||||
|
* JVM 版本
|
||||||
|
* 示例: "2.1.0"
|
||||||
|
*/
|
||||||
val jvmVersion: String?
|
val jvmVersion: String?
|
||||||
get() = System.getProperty("java.vm.version")
|
get() = System.getProperty("java.vm.version")
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Java 类版本
|
||||||
|
* 示例: "50.0" (Java 6), "52.0" (Java 8)
|
||||||
|
*/
|
||||||
val jvmClassVersion: String?
|
val jvmClassVersion: String?
|
||||||
get() = System.getProperty("java.class.version")
|
get() = System.getProperty("java.class.version")
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Java 规范名称
|
||||||
|
* 示例: "Dalvik Core Library"
|
||||||
|
*/
|
||||||
val jvmSpecificationName: String?
|
val jvmSpecificationName: String?
|
||||||
get() = System.getProperty("java.specification.name")
|
get() = System.getProperty("java.specification.name")
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Java 规范供应商
|
||||||
|
* 示例: "The Android Project"
|
||||||
|
*/
|
||||||
val jvmSpecificationVendor: String?
|
val jvmSpecificationVendor: String?
|
||||||
get() = System.getProperty("java.specification.vendor")
|
get() = System.getProperty("java.specification.vendor")
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Java 规范版本
|
||||||
|
* 示例: "0.9"
|
||||||
|
*/
|
||||||
val jvmSpecificationVersion: String?
|
val jvmSpecificationVersion: String?
|
||||||
get() = System.getProperty("java.specification.version")
|
get() = System.getProperty("java.specification.version")
|
||||||
|
|
||||||
|
// ==================== 安全与内核信息 ====================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 厂商安全补丁级别
|
||||||
|
* 需要 SecurityStateManagerCompat 支持
|
||||||
|
* 某些设备可能有额外的厂商安全补丁
|
||||||
|
*/
|
||||||
val vendorSecurityPatchLevel: String?
|
val vendorSecurityPatchLevel: String?
|
||||||
get() = null // globalSecurityState.getString(SecurityStateManagerCompat.KEY_VENDOR_SPL)
|
get() = null // globalSecurityState.getString(SecurityStateManagerCompat.KEY_VENDOR_SPL)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 内核版本
|
||||||
|
* 需要 SecurityStateManagerCompat 支持
|
||||||
|
* 示例: "5.10.157"
|
||||||
|
*/
|
||||||
val kernelVersion: String?
|
val kernelVersion: String?
|
||||||
get() = null // globalSecurityState.getString(SecurityStateManagerCompat.KEY_KERNEL_VERSION)
|
get() = null // globalSecurityState.getString(SecurityStateManagerCompat.KEY_KERNEL_VERSION)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 完整内核版本
|
||||||
|
* 包含内核版本和编译信息
|
||||||
|
* 示例: "5.10.157-android13-4-00001-gf0123456789a-ab9876543"
|
||||||
|
*/
|
||||||
val kernelCompleteVersion: String?
|
val kernelCompleteVersion: String?
|
||||||
get() = System.getProperty("os.version")
|
get() = System.getProperty("os.version")
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Bootloader 版本
|
||||||
|
* 设备的引导加载程序版本
|
||||||
|
*/
|
||||||
val bootloaderVersion: String
|
val bootloaderVersion: String
|
||||||
get() = Build.BOOTLOADER
|
get() = Build.BOOTLOADER
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 基带版本(无线电版本)
|
||||||
|
* 移动网络基带固件版本
|
||||||
|
*/
|
||||||
val radioVersion: String?
|
val radioVersion: String?
|
||||||
get() = Build.getRadioVersion()
|
get() = Build.getRadioVersion()
|
||||||
|
|
||||||
|
// ==================== 分区信息 ====================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 指纹分区信息列表
|
||||||
|
* Android 10+ 可用
|
||||||
|
* 包含系统各分区的名称和指纹信息
|
||||||
|
*
|
||||||
|
* 常见分区:
|
||||||
|
* - system: 系统分区
|
||||||
|
* - vendor: 厂商分区
|
||||||
|
* - product: 产品分区
|
||||||
|
* - odm: ODM(原始设计制造商)分区
|
||||||
|
* - system_ext: 系统扩展分区
|
||||||
|
*/
|
||||||
val fingerprintedPartitions: List<PartitionInfo>
|
val fingerprintedPartitions: List<PartitionInfo>
|
||||||
get() = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
get() = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
||||||
Build.getFingerprintedPartitions().map { PartitionInfo(it.name, it.fingerprint) }
|
Build.getFingerprintedPartitions().map { PartitionInfo(it.name, it.fingerprint) }
|
||||||
@ -107,5 +263,72 @@ class BuildInfo(private val context: Context) {
|
|||||||
emptyList()
|
emptyList()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分区信息数据类
|
||||||
|
* @property name 分区名称
|
||||||
|
* @property fingerprint 分区指纹(构建标识)
|
||||||
|
*/
|
||||||
data class PartitionInfo(val name: String, val fingerprint: String)
|
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.Build
|
||||||
import android.os.Handler
|
import android.os.Handler
|
||||||
import android.os.Looper
|
import android.os.Looper
|
||||||
|
import android.util.Range
|
||||||
|
import android.util.SizeF
|
||||||
import com.xyzshell.andinfo.R
|
import com.xyzshell.andinfo.R
|
||||||
import com.xyzshell.andinfo.libs.camera.models.CameraCapability
|
import com.xyzshell.andinfo.libs.camera.models.CameraCapability
|
||||||
import com.xyzshell.andinfo.libs.camera.models.CameraFacing
|
import com.xyzshell.andinfo.libs.camera.models.CameraFacing
|
||||||
import kotlinx.coroutines.channels.awaitClose
|
import kotlinx.coroutines.channels.awaitClose
|
||||||
import kotlinx.coroutines.flow.callbackFlow
|
import kotlinx.coroutines.flow.callbackFlow
|
||||||
|
import kotlin.math.sqrt
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 摄像头信息工具类
|
||||||
|
* 提供摄像头硬件参数、特性、功能列表等详细信息
|
||||||
|
* 需要权限: CAMERA
|
||||||
|
*/
|
||||||
class CameraInfo(private val context: Context) {
|
class CameraInfo(private val context: Context) {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 摄像头管理器
|
||||||
|
*/
|
||||||
private val cameraManager: CameraManager? = context.getSystemService(
|
private val cameraManager: CameraManager? = context.getSystemService(
|
||||||
CameraManager::class.java
|
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>
|
val cameraIds: List<String>
|
||||||
get() = cameraManager?.cameraIdList?.toList() ?: emptyList()
|
get() = cameraManager?.cameraIdList?.toList() ?: emptyList()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取指定摄像头的特性对象
|
||||||
|
* @param cameraId 摄像头ID
|
||||||
|
* @return CameraCharacteristics 对象
|
||||||
|
*/
|
||||||
fun getCameraCharacteristics(cameraId: String): CameraCharacteristics? {
|
fun getCameraCharacteristics(cameraId: String): CameraCharacteristics? {
|
||||||
return cameraManager?.getCameraCharacteristics(cameraId)
|
return cameraManager?.getCameraCharacteristics(cameraId)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 监听摄像头可用性变化的 Flow
|
||||||
|
*/
|
||||||
fun cameraIdsFlow() = cameraManager?.let { manager ->
|
fun cameraIdsFlow() = cameraManager?.let { manager ->
|
||||||
callbackFlow {
|
callbackFlow {
|
||||||
val onCameraIdUpdated = {
|
val onCameraIdUpdated = {
|
||||||
@ -55,6 +141,12 @@ class CameraInfo(private val context: Context) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ==================== 基本信息 ====================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取摄像头朝向
|
||||||
|
* @return 前置/后置/外置
|
||||||
|
*/
|
||||||
fun getCameraFacing(characteristics: CameraCharacteristics): CameraFacing? {
|
fun getCameraFacing(characteristics: CameraCharacteristics): CameraFacing? {
|
||||||
return characteristics.get(CameraCharacteristics.LENS_FACING)?.let {
|
return characteristics.get(CameraCharacteristics.LENS_FACING)?.let {
|
||||||
when (it) {
|
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? {
|
fun getPixelArraySize(characteristics: CameraCharacteristics): String? {
|
||||||
return characteristics.get(CameraCharacteristics.SENSOR_INFO_PIXEL_ARRAY_SIZE)?.toString()
|
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? {
|
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? {
|
fun getAvailableApertures(characteristics: CameraCharacteristics): FloatArray? {
|
||||||
return characteristics.get(CameraCharacteristics.LENS_INFO_AVAILABLE_APERTURES)
|
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> {
|
fun getAvailableCapabilities(characteristics: CameraCharacteristics): List<CameraCapability> {
|
||||||
val capabilities = characteristics.get(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES)
|
val capabilities = characteristics.get(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES)
|
||||||
return capabilities?.toList()?.mapNotNull { capability ->
|
return capabilities?.toList()?.mapNotNull { capability ->
|
||||||
@ -111,4 +472,235 @@ class CameraInfo(private val context: Context) {
|
|||||||
}
|
}
|
||||||
} ?: emptyList()
|
} ?: 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