diff --git a/myphoneinfo/andinfo/src/main/java/com/xyzshell/andinfo/libs/BluetoothInfo.kt b/myphoneinfo/andinfo/src/main/java/com/xyzshell/andinfo/libs/BluetoothInfo.kt index 1244b30..c02a077 100644 --- a/myphoneinfo/andinfo/src/main/java/com/xyzshell/andinfo/libs/BluetoothInfo.kt +++ b/myphoneinfo/andinfo/src/main/java/com/xyzshell/andinfo/libs/BluetoothInfo.kt @@ -13,8 +13,11 @@ import android.content.Context import android.content.Intent import android.content.IntentFilter import android.content.pm.PackageManager +import android.media.MediaCodecInfo +import android.media.MediaCodecList import android.os.Build import androidx.core.app.ActivityCompat +import kotlin.text.compareTo /** * 蓝牙信息工具类 @@ -73,7 +76,11 @@ class BluetoothInfo(private val context: Context) { val offloadedFiltering: Boolean, // 硬件过滤 val offloadedScanBatching: Boolean, // 批量扫描 val multipleAdvertisement: Boolean, // 多广播 - val lowEnergyExtended: Boolean // 扩展LE + val lowEnergyExtended: Boolean, // 扩展LE + val lowLatencyAudio: Boolean, // 低延迟音频 + val proAudio: Boolean, // 专业音频 + val midiSupport: Boolean, // MIDI支持 + val supportedCodecs: List // 支持的编解码器 ) // ==================== 基本信息 ==================== @@ -305,6 +312,7 @@ class BluetoothInfo(private val context: Context) { */ fun getBluetoothFeatures(): BluetoothFeatures { val adapter = bluetoothAdapter + val pm = context.packageManager return BluetoothFeatures( version = getBluetoothVersion(), @@ -336,15 +344,15 @@ class BluetoothInfo(private val context: Context) { } else false, leAudioSupport = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { - context.packageManager.hasSystemFeature("android.hardware.bluetooth.le.audio") + pm.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") + pm.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") + pm.hasSystemFeature("android.hardware.bluetooth.le.audio.unicast_client") } else false, // 其他特性 @@ -362,10 +370,40 @@ class BluetoothInfo(private val context: Context) { lowEnergyExtended = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { adapter?.isLeExtendedAdvertisingSupported == true - } else false + } else false, + // 音频和MIDI特性 + lowLatencyAudio = pm.hasSystemFeature(PackageManager.FEATURE_AUDIO_LOW_LATENCY), + proAudio = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + pm.hasSystemFeature(PackageManager.FEATURE_AUDIO_PRO) + } else false, + midiSupport = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + pm.hasSystemFeature(PackageManager.FEATURE_MIDI) + } else false, + + // 支持的编解码器 + supportedCodecs = getSupportedCodecs() ) } + private fun getSupportedAudioCodecs(): List { + val codecList = MediaCodecList(MediaCodecList.ALL_CODECS) + return codecList.codecInfos.filter { codecInfo -> + !codecInfo.isEncoder && // 解码器(如果你要编码器,改成 true) + codecInfo.supportedTypes.any { it.startsWith("audio/") } + } + } + + /** + * 获取支持的蓝牙音频编解码器 + */ + @SuppressLint("MissingPermission") + private fun getSupportedCodecs(): List { + return getSupportedAudioCodecs().flatMap { codecInfo -> + codecInfo.supportedTypes.map { it } + }.distinct().sorted() + } + + /** * 获取蓝牙版本(根据支持的特性推断) diff --git a/myphoneinfo/andinfo/src/main/java/com/xyzshell/andinfo/libs/BuildInfo.kt b/myphoneinfo/andinfo/src/main/java/com/xyzshell/andinfo/libs/BuildInfo.kt index 133bee5..d8bf26b 100644 --- a/myphoneinfo/andinfo/src/main/java/com/xyzshell/andinfo/libs/BuildInfo.kt +++ b/myphoneinfo/andinfo/src/main/java/com/xyzshell/andinfo/libs/BuildInfo.kt @@ -2,6 +2,7 @@ package com.xyzshell.andinfo.libs import android.content.Context import android.content.pm.PackageManager +import android.net.Uri import android.os.Build import android.provider.Settings // import androidx.security.state.SecurityStateManagerCompat @@ -548,6 +549,29 @@ class BuildInfo(private val context: Context) { val googleServicesFrameworkVersion: String? get() = getPackageVersion("com.google.android.gsf") + /** + * GSF ID(Google Services Framework ID) + * 设备的唯一标识符 + */ + val googleServicesFrameworkId: String? + get() = getGSFId(context) + + + private fun getGSFId(context: Context): String? { + return try { + val uri = Uri.parse("content://com.google.android.gsf.gservices") + val params = arrayOf("android_id") + context.contentResolver.query(uri, null, null, params, null)?.use { cursor -> + if (cursor.moveToFirst() && cursor.columnCount >= 2) { + cursor.getString(1) + } else { + null + } + } + } catch (e: Exception) { + null + } + } /** * 获取包版本名称 */ diff --git a/myphoneinfo/andinfo/src/main/java/com/xyzshell/andinfo/libs/CameraInfo.kt b/myphoneinfo/andinfo/src/main/java/com/xyzshell/andinfo/libs/CameraInfo.kt index 27f5da1..dbc94d2 100644 --- a/myphoneinfo/andinfo/src/main/java/com/xyzshell/andinfo/libs/CameraInfo.kt +++ b/myphoneinfo/andinfo/src/main/java/com/xyzshell/andinfo/libs/CameraInfo.kt @@ -2,6 +2,7 @@ package com.xyzshell.andinfo.libs import android.Manifest import android.content.Context +import android.graphics.ImageFormat import android.hardware.camera2.CameraCharacteristics import android.hardware.camera2.CameraManager import android.hardware.camera2.CameraMetadata @@ -9,12 +10,14 @@ import android.os.Build import android.os.Handler import android.os.Looper import android.util.Range +import android.util.Size 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.roundToInt import kotlin.math.sqrt /** @@ -92,6 +95,7 @@ class CameraInfo(private val context: Context) { val maxFrameRate: Int?, // 最大帧率 val supportsHighSpeedVideo: Boolean, // 是否支持高速视频 val supports4K: Boolean, // 是否支持4K录制 + val streamConfigurations: List?, // 视频-型材 ; 视频采集流配置列表 (格式: 宽x高 @ 帧率 Hz, 格式) val supports8K: Boolean // 是否支持8K录制 ) @@ -636,6 +640,56 @@ class CameraInfo(private val context: Context) { return resolution?.contains("7680") == true } + // ==================== 视频能力 ==================== + + /** + * 获取支持的视频流配置 (分辨率@帧率) + */ + fun getStreamConfigurations(characteristics: CameraCharacteristics): List? { + val map = characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP) ?: return null + val configs = mutableMapOf>() + + // 遍历所有支持的尺寸 + val outputSizes = map.getOutputSizes(ImageFormat.YUV_420_888) ?: map.getOutputSizes(ImageFormat.PRIVATE) + outputSizes?.forEach { size -> + // 获取该尺寸下的最小帧间隔(纳秒),从而计算出最大帧率 + val minFrameDuration = map.getOutputMinFrameDuration(ImageFormat.YUV_420_888, size) + if (minFrameDuration > 0) { + val maxFps = (1_000_000_000.0 / minFrameDuration).roundToInt() + if (!configs.containsKey(size)) { + configs[size] = mutableSetOf() + } + configs[size]?.add(maxFps) + } + } + + // 检查高速视频配置 + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + map.highSpeedVideoSizes.forEach { size -> + map.getHighSpeedVideoFpsRangesFor(size).forEach { range -> + if (!configs.containsKey(size)) { + configs[size] = mutableSetOf() + } + // 通常高速视频会使用固定的高帧率 + configs[size]?.add(range.upper) + } + } + } + + // 格式化输出 + return configs.map { (size, fpsSet) -> + val resolutionName = when { + size.width == 7680 && size.height == 4320 -> "8K UHD" + size.width == 3840 && size.height == 2160 -> "4K UHD" + size.width == 1920 && size.height == 1080 -> "1080p" + size.width == 1280 && size.height == 720 -> "720p" + else -> "${size.width}x${size.height}" + } + val fpsString = fpsSet.sortedDescending().joinToString("/") + "$resolutionName @ $fpsString Hz" + }.sortedDescending() // 按分辨率排序 + } + // ==================== 综合信息 ==================== /** @@ -693,6 +747,7 @@ class CameraInfo(private val context: Context) { maxFrameRate = getMaxFrameRate(characteristics), supportsHighSpeedVideo = supportsHighSpeedVideo(characteristics), supports4K = supports4K(characteristics), + streamConfigurations = getStreamConfigurations(characteristics), supports8K = supports8K(characteristics) ) }