新增蓝牙

This commit is contained in:
yuqian 2025-12-29 18:27:17 +08:00
parent a59073b0af
commit 5743722fe2
8 changed files with 692 additions and 274 deletions

View File

@ -4,6 +4,14 @@
<uses-permission android:name="android.permission.HIGH_SAMPLING_RATE_SENSORS" />
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-feature android:name="android.hardware.bluetooth" android:required="false" />
<uses-feature android:name="android.hardware.bluetooth_le" android:required="false" />
<application
android:name=".MyApplication"

View File

@ -1,12 +1,20 @@
package com.xyzshell.myphoneinfo.dashboard
import android.bluetooth.BluetoothAdapter
import android.content.ActivityNotFoundException
import android.content.Intent
import android.content.pm.PackageManager
import android.os.Bundle
import android.os.CountDownTimer
import android.provider.Settings
import android.text.format.Formatter
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Toast
import androidx.activity.result.contract.ActivityResultContracts
import androidx.core.app.ActivityCompat
import com.xyzshell.andinfo.AndInfo
import com.xyzshell.andinfo.libs.CpuInfo
import com.xyzshell.myphoneinfo.R
@ -19,45 +27,61 @@ import com.xyzshell.myphoneinfo.dialog.DialogInput
import kotlin.math.roundToInt
class HardWareFragment : Fragment() {
private lateinit var binding: FragmentHardWareBinding
private var dialogBlueTooth: DialogBlueTooth? = null
private var dialogInput: DialogInput? = null
private var dialogExtension: DialogExtension? = null
private var dialogDiskPart: DialogDiskPart? = null
private lateinit var binding: FragmentHardWareBinding
private var dialogBlueTooth: DialogBlueTooth? = null
private var dialogNearBlueTooth: DialogBlueTooth? = null
private var dialogInput: DialogInput? = null
private var dialogExtension: DialogExtension? = null
private var dialogDiskPart: DialogDiskPart? = null
private val requestPermissionLauncher = registerForActivityResult(
ActivityResultContracts.RequestMultiplePermissions()
) { permissions ->
val allGranted = permissions.all { it.value }
if (allGranted) {
// 权限已授予,刷新蓝牙信息
refreshBluetoothInfo()
} else {
// 显示权限被拒绝的UI
// showPermissionDeniedUI()
Toast.makeText(requireContext(), "权限被拒绝", Toast.LENGTH_SHORT).show()
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
}
override fun onResume() {
super.onResume()
checkAndDisplayBluetoothInfo()
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
binding = FragmentHardWareBinding.inflate(inflater, container, false)
dialogExtension=dialogExtension?: DialogExtension()
dialogExtension = dialogExtension ?: DialogExtension()
initText()
binding.cpuBtn.setOnClickListener {
var intent=Intent(requireContext(), AnalysisActivity::class.java)
var intent = Intent(requireContext(), AnalysisActivity::class.java)
startActivity(intent)
}
binding.bluetoothLayout.pair1.setOnClickListener {
dialogBlueTooth= dialogBlueTooth?: DialogBlueTooth()
println("showBluetoothDialog"+AndInfo.instance.bluetooth.bondedDevices.size)
dialogBlueTooth?.show(childFragmentManager, "BlueTooth1")
}
// binding.pair2.setOnClickListener {
// dialogBlueTooth= dialogBlueTooth?: DialogBlueTooth()
// dialogBlueTooth?.show(childFragmentManager, "BlueTooth2")
// }
binding.othertext.setOnClickListener {
dialogInput=dialogInput?: DialogInput()
dialogInput = dialogInput ?: DialogInput()
dialogInput?.show(childFragmentManager, "Input")
}
binding.extensionShow.setOnClickListener {
dialogExtension?.show(childFragmentManager, "Extention")
}
binding.memoryLayout.disktext.setOnClickListener {
dialogDiskPart=dialogDiskPart?: DialogDiskPart()
dialogDiskPart = dialogDiskPart ?: DialogDiskPart()
dialogDiskPart?.show(childFragmentManager, "DiskPart")
}
@ -65,173 +89,376 @@ class HardWareFragment : Fragment() {
}
private fun initText() {
val cpu = AndInfo.instance.cpu//cpu信息
val device = AndInfo.instance.device//设备信息
val gpu=AndInfo.instance.gpu//gpu信息
setProcessorInfo()
setGpuInfo()
setDisplayInfo()
setMemoryInfo()
setBlueToothInfo()
setAudioInfo()
}
/**
* Audio信息
* */
private fun setAudioInfo() {
binding.check1.content.text = getString(R.string.low_audio)
binding.check2.content.text = getString(R.string.pro_audio)
binding.check3.content.text = getString(R.string.midl)
}
//processor
//处理器
binding.text0.textTitle.text = getString(R.string.processor)
binding.text0.textContent.text= cpu.getProcessorName()
//制造商
binding.text1.textTitle.text = getString(R.string.vendor)
binding.text1.textContent.text=cpu.getVendor()
//硬件
binding.text2.textTitle.text = getString(R.string.hardware)
binding.text2.textContent.text=cpu.getProcessorName()
//核心数
binding.text3.textTitle.text = getString(R.string.cores)
binding.text3.textContent.text=cpu.cores.size.toString()
//cpu内核详情
binding.text4.textTitle.text = getString(R.string.CPU)
binding.text4.textContent.text=cpu.getAllArchitectures().joinToString()
//制程工艺
binding.text5.textTitle.text = getString(R.string.process)
binding.text5.textContent.text=cpu.getProcessInfo().process
//架构
binding.text6.textTitle.text = getString(R.string.architecture)
binding.text6.textContent.text=cpu.getArchitecture()
//ABI
binding.text7.textTitle.text = getString(R.string.ABI)
binding.text7.textContent.text=cpu.getAbi()
//supported_ABls
binding.text8.textTitle.text = getString(R.string.supported_ABls)
binding.text8.textContent.text=cpu.getSupportedAbis().joinToString()
//频率
binding.text9.textTitle.text = getString(R.string.frequencies)
binding.text9.textContent.text=cpu.getFrequencyText()
/**
* memory信息
* */
private fun setMemoryInfo() {
val storageInfo = AndInfo.instance.storage//存储信息
val used =
Formatter.formatFileSize(AndInfo.instance.context, storageInfo.internalStorageUsedSpace)
val free = Formatter.formatFileSize(
AndInfo.instance.context,
storageInfo.internalStorageAvailableSpace
)
val total = Formatter.formatFileSize(
AndInfo.instance.context,
storageInfo.internalStorageTotalSpace
)
val outused = Formatter.formatFileSize(
AndInfo.instance.context,
storageInfo.externalStorageUsedSpace ?: 0
)
val outfree = Formatter.formatFileSize(
AndInfo.instance.context,
storageInfo.externalStorageAvailableSpace ?: 0
)
val outtotal = Formatter.formatFileSize(
AndInfo.instance.context,
storageInfo.externalStorageTotalSpace ?: 0
)
binding.memoryLayout.memText1.textTitle.text = getString(R.string.ram_size)
binding.memoryLayout.memText1.textContent.text = total
binding.memoryLayout.ram1.text = used + " used"
binding.memoryLayout.ram2.text = free + " free"
binding.memoryLayout.seekbar.progress =
(used.substringBefore(" ").toDouble() / total.substringBefore(" ").toDouble() * 100).toInt()
//gpu
//storage
binding.memoryLayout.memText3.textTitle.text = getString(R.string.zram)
binding.memoryLayout.memText3.textContent.text = Formatter.formatFileSize(
AndInfo.instance.context,
storageInfo.internalStorageTotalSpace
)
binding.memoryLayout.ram3.text = outused + " used"
binding.memoryLayout.ram4.text = outfree + " free"
binding.memoryLayout.seekbar2.progress =
(outused.substringBefore(" ").toDouble() / outtotal.substringBefore(" ").toDouble() * 100).toInt()
//fileSystem
binding.memoryLayout.storText1.textTitle.text = getString(R.string.filesystem)
binding.memoryLayout.storText1.textContent.text = total
binding.memoryLayout.stor1.text = used + " used"
binding.memoryLayout.stor2.text = free + " free"
binding.memoryLayout.radius1.text = used
binding.memoryLayout.radius2.text = used
binding.memoryLayout.radius3.text = free
binding.memoryLayout.seekbar3.progress =
(used.substringBefore(" ").toDouble() / total.substringBefore(" ").toDouble() * 100).toInt()
//internalStorage
binding.memoryLayout.interText1.textTitle.text = getString(R.string.filesystem)
binding.memoryLayout.interText1.textContent.text =
storageInfo.internalStorageEncryptionType.toString()
binding.memoryLayout.interText2.textTitle.text = getString(R.string.block_size)
binding.memoryLayout.interText2.textContent.text = total
binding.memoryLayout.interText3.textTitle.text = "/data"
binding.memoryLayout.interText3.textContent.text = total
binding.memoryLayout.stor3.text = used + " used"
binding.memoryLayout.stor4.text = free + " free"
binding.memoryLayout.seekbar2.progress =
(used.substringBefore(" ").toDouble() / total.substringBefore(" ").toDouble() * 100).toInt()
}
/**
* display信息
* */
private fun setDisplayInfo() {
val display = AndInfo.instance.display//显示信息
val defaultDisplayInfo = display.getDefaultDisplayInfo()
val width = defaultDisplayInfo?.widthPixels.toString()
val height = defaultDisplayInfo?.heightPixels.toString()
if (defaultDisplayInfo != null) {
binding.disText1.textContent.text = width + "x" + height
}
binding.disText1.textTitle.text = getString(R.string.resolution)
binding.disText2.textTitle.text = getString(R.string.screen_density)//这个是px
binding.disText2.textContent.text = defaultDisplayInfo?.ppi.toString()
binding.disText3.textTitle.text = getString(R.string.screen_density_d)
val dpiStr =
defaultDisplayInfo?.densityDpi.toString() + "(xxhdpi)\n" + defaultDisplayInfo?.xdpi?.roundToInt() + "dpx" + defaultDisplayInfo?.ydpi?.roundToInt() + "dp"
binding.disText3.textContent.text = dpiStr
binding.disText4.textTitle.text = getString(R.string.screen_size_e)
binding.disText5.textTitle.text = getString(R.string.aspect_ratio)
binding.disText5.textContent.text = convertToApproximateAspectRatio(
width = width.toInt(),
height = height.toInt()
).toString()
binding.disText6.textTitle.text = getString(R.string.refresh_rate)
binding.disText6.textContent.text = defaultDisplayInfo?.refreshRate.toString() + "HZ"
binding.disText7.textTitle.text = getString(R.string.wide_color_gamut)
binding.disText7.textContent.text = defaultDisplayInfo?.isWideColorGamut.toString()
binding.disText8.textTitle.text = getString(R.string.hdr_support)
if (defaultDisplayInfo?.isHdr == true) {
defaultDisplayInfo.hdrTypes.joinToString()
binding.disText8.textContent.text = defaultDisplayInfo.hdrTypes.joinToString()
} else {
binding.disText8.textContent.text = "No"
}
}
/**
* gpu信息*/
private fun setGpuInfo() {
val gpu = AndInfo.instance.gpu//gpu信息
binding.gpuText1.textTitle.text = getString(R.string.vendor)
binding.gpuText1.textContent.text=gpu.getVendorName()
binding.gpuText1.textContent.text = gpu.getVendorName()
binding.gpuText2.textTitle.text = getString(R.string.gpu)
gpu.getGpuInformation().vkPhysicalDevices?.let { devices ->
if (devices.isNotEmpty()) {
devices.forEachIndexed { index, vkPhysicalDevice ->
binding.gpuText2.textContent.text=vkPhysicalDevice.deviceName
binding.gpuText2.textContent.text = vkPhysicalDevice.deviceName
}
}
}
binding.gpuText3.textTitle.text=getString(R.string.max_frequency)
binding.gpuText4.textTitle.text=getString(R.string.architecture)
binding.gpuText5.textTitle.text=getString(R.string.cores)
binding.gpuText5.textContent.text=gpu.getOpenGLExtensionCount().toString()
binding.gpuText6.textTitle.text=getString(R.string.total_l2)
binding.gpuText7.textTitle.text=getString(R.string.bus_width)
binding.gpuText8.textTitle.text=getString(R.string.vulkan_support)
binding.gpuText8.textContent.text=gpu.isVulkanSupported().toString()
binding.gpuText9.textTitle.text=getString(R.string.vulkan_API)
binding.gpuText9.textContent.text=gpu.getVulkanApiVersion()
binding.open1.text=getString(R.string.opengl)
binding.gpuText3.textTitle.text = getString(R.string.max_frequency)
binding.gpuText4.textTitle.text = getString(R.string.architecture)
binding.gpuText5.textTitle.text = getString(R.string.cores)
binding.gpuText5.textContent.text = gpu.getOpenGLExtensionCount().toString()
binding.gpuText6.textTitle.text = getString(R.string.total_l2)
binding.gpuText7.textTitle.text = getString(R.string.bus_width)
binding.gpuText8.textTitle.text = getString(R.string.vulkan_support)
binding.gpuText8.textContent.text = gpu.isVulkanSupported().toString()
binding.gpuText9.textTitle.text = getString(R.string.vulkan_API)
binding.gpuText9.textContent.text = gpu.getVulkanApiVersion()
binding.open1.text = getString(R.string.opengl)
gpu.getGpuInformation().eglInformation?.let { eglInfo ->
eglInfo.eglExtensions?.let {
dialogExtension?.setContent(it.joinToString())
}
}
binding.openItem1.text=gpu.getVendorName()
binding.openItem2.text=gpu.getRendererName()
binding.openItem3.text=gpu.getOpenGLVersion()
//display
val display=AndInfo.instance.display//显示信息
val defaultDisplayInfo = display.getDefaultDisplayInfo()
val width = defaultDisplayInfo?.widthPixels.toString()
val height = defaultDisplayInfo?.heightPixels.toString()
if(defaultDisplayInfo!=null){
binding.disText1.textContent.text=width+"x"+height
}
binding.disText1.textTitle.text=getString(R.string.resolution)
binding.disText2.textTitle.text=getString(R.string.screen_density)//这个是px
binding.disText2.textContent.text=defaultDisplayInfo?.ppi.toString()
binding.disText3.textTitle.text=getString(R.string.screen_density_d)
val dpiStr=defaultDisplayInfo?.densityDpi.toString()+"(xxhdpi)\n"+defaultDisplayInfo?.xdpi?.roundToInt()+"dpx"+defaultDisplayInfo?.ydpi?.roundToInt()+"dp"
binding.disText3.textContent.text=dpiStr
binding.disText4.textTitle.text=getString(R.string.screen_size_e)
binding.disText5.textTitle.text=getString(R.string.aspect_ratio)
binding.disText5.textContent.text=convertToApproximateAspectRatio(width = width.toInt(), height = height.toInt()).toString()
binding.disText6.textTitle.text=getString(R.string.refresh_rate)
binding.disText6.textContent.text=defaultDisplayInfo?.refreshRate.toString()+"HZ"
binding.disText7.textTitle.text=getString(R.string.wide_color_gamut)
binding.disText7.textContent.text=defaultDisplayInfo?.isWideColorGamut.toString()
binding.disText8.textTitle.text=getString(R.string.hdr_support)
if(defaultDisplayInfo?.isHdr == true){
defaultDisplayInfo.hdrTypes.joinToString()
binding.disText8.textContent.text= defaultDisplayInfo.hdrTypes.joinToString()
}else{
binding.disText8.textContent.text= "No"
}
//memory
val storageInfo = AndInfo.instance.storage//存储信息
val used=Formatter.formatFileSize(AndInfo.instance.context, storageInfo.internalStorageUsedSpace)
val free=Formatter.formatFileSize(AndInfo.instance.context, storageInfo.internalStorageAvailableSpace)
val total=Formatter.formatFileSize(AndInfo.instance.context, storageInfo.internalStorageTotalSpace)
val outused=Formatter.formatFileSize(AndInfo.instance.context, storageInfo.externalStorageUsedSpace?: 0)
val outfree=Formatter.formatFileSize(AndInfo.instance.context, storageInfo.externalStorageAvailableSpace?: 0)
val outtotal=Formatter.formatFileSize(AndInfo.instance.context, storageInfo.externalStorageTotalSpace?: 0)
binding.memoryLayout.memText1.textTitle.text=getString(R.string.ram_size)
binding.memoryLayout.memText1.textContent.text=total
binding.memoryLayout.ram1.text=used+" used"
binding.memoryLayout.ram2.text=free+" free"
binding.memoryLayout.seekbar.progress=(used.substring(0,used.indexOf(" ")).toDouble() / total.substring(0,total.indexOf(" ")).toDouble() *100).toInt()
//storage
binding.memoryLayout.memText3.textTitle.text=getString(R.string.zram)
binding.memoryLayout.memText3.textContent.text=Formatter.formatFileSize(AndInfo.instance.context, storageInfo.internalStorageTotalSpace)
binding.memoryLayout.ram3.text=outused+" used"
binding.memoryLayout.ram4.text=outfree+" free"
binding.memoryLayout.seekbar2.progress=(outused.substring(0,outused.indexOf(" ")).toDouble() / outtotal.substring(0,outtotal.indexOf(" ")).toDouble() *100).toInt()
//fileSystem
binding.memoryLayout.storText1.textTitle.text=getString(R.string.filesystem)
binding.memoryLayout.storText1.textContent.text=total
binding.memoryLayout.stor1.text=used+" used"
binding.memoryLayout.stor2.text=free+" free"
binding.memoryLayout.radius1.text=used
binding.memoryLayout.radius2.text=used
binding.memoryLayout.radius3.text=free
binding.memoryLayout.seekbar3.progress=(used.substring(0,used.indexOf(" ")).toDouble() / total.substring(0,total.indexOf(" ")).toDouble() *100).toInt()
//internalStorage
binding.memoryLayout.interText1.textTitle.text=getString(R.string.filesystem)
binding.memoryLayout.interText1.textContent.text=storageInfo.internalStorageEncryptionType.toString()
binding.memoryLayout.interText2.textTitle.text=getString(R.string.block_size)
binding.memoryLayout.interText2.textContent.text=total
binding.memoryLayout.interText3.textTitle.text="/data"
binding.memoryLayout.interText3.textContent.text=total
binding.memoryLayout.stor3.text=used+" used"
binding.memoryLayout.stor4.text=free+" free"
binding.memoryLayout.seekbar2.progress=(used.substring(0,used.indexOf(" ")).toDouble() / total.substring(0,total.indexOf(" ")).toDouble() *100).toInt()
//Audio
binding.check1.content.text=getString(R.string.low_audio)
binding.check2.content.text=getString(R.string.pro_audio)
binding.check3.content.text=getString(R.string.midl)
binding.openItem1.text = gpu.getVendorName()
binding.openItem2.text = gpu.getRendererName()
binding.openItem3.text = gpu.getOpenGLVersion()
}
/**
* processor信息*/
private fun setProcessorInfo() {
val cpu = AndInfo.instance.cpu//cpu信息
//处理器
binding.text0.textTitle.text = getString(R.string.processor)
binding.text0.textContent.text = cpu.getProcessorName()
//制造商
binding.text1.textTitle.text = getString(R.string.vendor)
binding.text1.textContent.text = cpu.getVendor()
//硬件
binding.text2.textTitle.text = getString(R.string.hardware)
binding.text2.textContent.text = cpu.getProcessorName()
//核心数
binding.text3.textTitle.text = getString(R.string.cores)
binding.text3.textContent.text = cpu.cores.size.toString()
//cpu内核详情
binding.text4.textTitle.text = getString(R.string.CPU)
binding.text4.textContent.text = cpu.getAllArchitectures().joinToString()
//制程工艺
binding.text5.textTitle.text = getString(R.string.process)
binding.text5.textContent.text = cpu.getProcessInfo().process
//架构
binding.text6.textTitle.text = getString(R.string.architecture)
binding.text6.textContent.text = cpu.getArchitecture()
//ABI
binding.text7.textTitle.text = getString(R.string.ABI)
binding.text7.textContent.text = cpu.getAbi()
//supported_ABls
binding.text8.textTitle.text = getString(R.string.supported_ABls)
binding.text8.textContent.text = cpu.getSupportedAbis().joinToString()
//频率
binding.text9.textTitle.text = getString(R.string.frequencies)
binding.text9.textContent.text = cpu.getFrequencyText()
}
/**
* bluetooth相关信息*/
private fun setBlueToothInfo() {
val bluetoothInfo = AndInfo.instance.bluetooth//蓝牙信息
if (!bluetoothInfo.isEnabled) return
binding.bluetoothLayout.showLayout.setOnClickListener {
requestBluetoothPermissions()
}
binding.bluetoothLayout.pair1.setOnClickListener {
showPairedDevicesDialog()
}
// 打开弹窗开始扫描超过15s扫描超时结束显示扫描到的列表
binding.bluetoothLayout.pair2.setOnClickListener {
showNearByDevicesDialog()
}
}
// 请求蓝牙权限的方法
private fun requestBluetoothPermissions() {
val permissions = AndInfo.instance.bluetooth.requiredPermissions
requestPermissionLauncher.launch(permissions)
}
// 检查权限的方法
private fun checkBluetoothPermissions(): Boolean {
val bluetoothInfo = AndInfo.instance.bluetooth
return bluetoothInfo.requiredPermissions.all { permission ->
ActivityCompat.checkSelfPermission(requireContext(), permission) ==
PackageManager.PERMISSION_GRANTED
}
}
/**
* 检查并显示蓝牙信息
*/
private fun checkAndDisplayBluetoothInfo() {
val bluetoothInfo = AndInfo.instance.bluetooth
// 1. 检查权限
val hasPermissions = checkBluetoothPermissions()
if (!hasPermissions) {
// 没有权限显示权限请求UI
binding.bluetoothLayout.showLayout.visibility = View.VISIBLE
binding.bluetoothLayout.blueList.visibility = View.GONE
return
}
// 2. 检查蓝牙是否可用
if (!bluetoothInfo.isEnabled) {
// 蓝牙未启用,显示提示
showBluetoothDisabledUI()
return
}
// 3. 有权限且蓝牙已启用,显示蓝牙信息
binding.bluetoothLayout.showLayout.visibility = View.GONE
binding.bluetoothLayout.blueList.visibility = View.VISIBLE
// 刷新蓝牙信息
refreshBluetoothInfo()
}
/**
* 显示蓝牙未启用的UI
*/
private fun showBluetoothDisabledUI() {
binding.bluetoothLayout.showLayout.visibility = View.VISIBLE
binding.bluetoothLayout.blueList.visibility = View.GONE
// 更新UI文本
binding.bluetoothLayout.showText.text = "Bluetooth is not enabled."
// 设置打开蓝牙按钮
binding.bluetoothLayout.show1.setOnClickListener {
openBluetoothSettings()
}
}
/**
* 显示已配对设备弹窗
*/
private fun showPairedDevicesDialog() {
dialogBlueTooth = dialogBlueTooth ?: DialogBlueTooth()
println("showBluetoothDialog" + AndInfo.instance.bluetooth.bondedDevices.size)
dialogBlueTooth?.show(childFragmentManager, "BlueTooth1")
}
/**
* 显示扫描到附近设备弹窗
*/
private fun showNearByDevicesDialog() {
// 检查权限
if (!checkBluetoothPermissions()) {
Toast.makeText(requireContext(), "需要蓝牙权限", Toast.LENGTH_SHORT).show()
return
}
// 检查蓝牙是否启用
if (!AndInfo.instance.bluetooth.isEnabled) {
Toast.makeText(requireContext(), "请先开启蓝牙", Toast.LENGTH_SHORT).show()
return
}
dialogNearBlueTooth = dialogNearBlueTooth ?: DialogBlueTooth()
println("showBluetoothDialog" + AndInfo.instance.bluetooth.bondedDevices.size)
dialogNearBlueTooth?.show(childFragmentManager, "BlueTooth2")
}
/**
* 刷新蓝牙信息
*/
private fun refreshBluetoothInfo() {
val bluetoothInfo = AndInfo.instance.bluetooth
// 更新基本信息
// binding.blueList.tvBluetoothStatus.text = bluetoothInfo.stateText
// binding.blueList.tvBluetoothStatus.setTextColor(
// when (bluetoothInfo.state) {
// BluetoothAdapter.STATE_ON -> ContextCompat.getColor(requireContext(), R.color.success_green)
// else -> ContextCompat.getColor(requireContext(), R.color.error_red)
// }
// )
//
// binding.blueList.tvBluetoothAdapter.text = bluetoothInfo.adapterName ?: "未知"
// binding.blueList.tvBluetoothVersion.text = bluetoothInfo.getBluetoothVersionDetails()
// binding.blueList.tvBleSupport.text = if (bluetoothInfo.isBluetoothLeSupported) "支持" else "不支持"
// 更新已配对设备数量
// val pairedCount = bluetoothInfo.bondedDevices.size
// binding.blueList.tvPairedCount.text = "已配对: ${pairedCount}个设备"
}
/**
* 打开蓝牙设置
*/
private fun openBluetoothSettings() {
try {
val intent = Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE)
startActivityForResult(intent, REQUEST_ENABLE_BT)
} catch (e: ActivityNotFoundException) {
// 尝试其他方式打开设置
val intent = Intent(Settings.ACTION_BLUETOOTH_SETTINGS)
if (intent.resolveActivity(requireActivity().packageManager) != null) {
startActivity(intent)
}
}
}
/**
* 权限检查结果回调
*/
override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<out String>,
grantResults: IntArray
) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
if (grantResults.all { it == PackageManager.PERMISSION_GRANTED }) {
// 权限已授予刷新UI
checkAndDisplayBluetoothInfo()
} else {
Toast.makeText(requireContext(), "Bluetooth permission is required.", Toast.LENGTH_SHORT).show()
}
}
companion object {
private const val REQUEST_ENABLE_BT = 1001
@JvmStatic
fun newInstance() =
HardWareFragment().apply {

View File

@ -1,18 +1,21 @@
package com.xyzshell.myphoneinfo.dialog
import android.os.Bundle
import android.os.CountDownTimer
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import android.widget.Toast
import androidx.compose.material3.Text
import com.xyzshell.andinfo.AndInfo
import com.xyzshell.myphoneinfo.R
import com.xyzshell.myphoneinfo.base.BaseDialogFragment
import com.xyzshell.myphoneinfo.databinding.DialogBlueToothBinding
class DialogBlueTooth :BaseDialogFragment<DialogBlueToothBinding>(DialogBlueToothBinding::inflate){
class DialogBlueTooth(val type:Int) :BaseDialogFragment<DialogBlueToothBinding>(DialogBlueToothBinding::inflate){
private var isScanning = false
private var scanTimer: CountDownTimer? = null
override fun getTitle(): String = resources.getString(R.string.bluetooth)
override fun getIconRes(): Int=5
@ -20,50 +23,152 @@ class DialogBlueTooth :BaseDialogFragment<DialogBlueToothBinding>(DialogBlueToot
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val bluetoothInfo = AndInfo.instance.bluetooth
val list=ArrayList<Map<String, String>>()
bluetoothInfo.bondedDevices.forEach { (name, address) ->
val deviceMap = mapOf(
"name" to (name ?: "Unknown device"),
"address" to (address ?: "")
)
list.add(deviceMap)
}
val container = binding.view as? ViewGroup
val list= bluetoothInfo.getBondedDevicesInfo()
container?.let {
// 遍历 list为每个设备创建 item view
if(list.isEmpty()){
it.addView(TextView(requireContext()).apply {
text = "No devices"
})
}else{
list.forEach { deviceMap ->
// 加载 item 布局
val itemView = LayoutInflater.from(requireContext())
.inflate(R.layout.item_blue_tooth, it, false)
// ArrayList<Map<String, String>>()
// bluetoothInfo.bondedDevices.forEach { (name, address) ->
// val deviceMap = mapOf(
// "name" to (name ?: "Unknown device"),
// "address" to (address ?: "")
// )
// list.add(deviceMap)
// }
if(type==0){// 显示已配对设备列表
val container = binding.view as? ViewGroup
container?.let {
// 遍历 list为每个设备创建 item view
if(list.isEmpty()){
it.addView(TextView(requireContext()).apply {
text = "No devices"
})
}else{
list.forEach { deviceMap ->
// 加载 item 布局
val itemView = LayoutInflater.from(requireContext())
.inflate(R.layout.item_blue_tooth, it, false)
// 设置设备信息
val tvName = itemView.findViewById<TextView>(R.id.textTitle)
val tvAddress = itemView.findViewById<TextView>(R.id.textContent)
// 设置设备信息
val tvName = itemView.findViewById<TextView>(R.id.textTitle)
val tvAddress = itemView.findViewById<TextView>(R.id.textContent)
tvName.text = deviceMap["name"]
tvAddress.text = deviceMap["address"]
// 添加到容器
it.addView(itemView)
tvName.text = deviceMap.name
tvAddress.text = deviceMap.address
// 添加到容器
it.addView(itemView)
}
}
}
}
}else{// 显示扫描设备列表
// startScanning()
}
}
// /**
// * 开始扫描设备
// */
// private fun startScanning() {
// if (isScanning) return
//
// val bluetoothInfo = AndInfo.instance.bluetooth
//
// // 显示扫描UI
// isScanning = true
// binding.view.visibility=View.GONE
// binding.progressbar.visibility=View.VISIBLE
//
// // 启动15秒倒计时
// startScanTimer(scanDialog)
//
// // 开始扫描
// try {
// // 同时扫描经典蓝牙和BLE设备
// bluetoothInfo.scanNearbyDevices(
// onDeviceFound = { device ->
// requireActivity().runOnUiThread {
// scanDialog.addDevice(device)
// }
// },
// onScanFinished = {
// requireActivity().runOnUiThread {
// stopScanning()
// scanDialog.scanFinished()
// }
// }
// )
//
// // 扫描BLE设备
// if (bluetoothInfo.isBluetoothLeSupported) {
// bluetoothInfo.scanLeDevices(
// onDeviceFound = { device ->
// requireActivity().runOnUiThread {
// scanDialog.addDevice(device)
// }
// },
// durationMillis = SCAN_DURATION
// )
// }
//
// } catch (e: SecurityException) {
// Toast.makeText(requireContext(), "蓝牙权限被拒绝", Toast.LENGTH_SHORT).show()
// stopScanning()
// }
// }
//
// /**
// * 启动扫描计时器
// */
// private fun startScanTimer(scanDialog: DialogScanDevices) {
// scanTimer = object : CountDownTimer(SCAN_DURATION, 1000) {
// override fun onTick(millisUntilFinished: Long) {
// val secondsLeft = millisUntilFinished / 1000
// binding.blueList.tvScanStatus.text =
// "正在扫描附近设备... ${secondsLeft}秒后结束"
//
// // 更新弹窗中的倒计时
// scanDialog.updateTimer(secondsLeft.toInt())
// }
//
// override fun onFinish() {
// stopScanning()
// scanDialog.scanFinished()
// }
// }.start()
// }
//
// /**
// * 停止扫描
// */
// private fun stopScanning() {
// if (!isScanning) return
//
// isScanning = false
//
// // 停止计时器
// scanTimer?.cancel()
// scanTimer = null
//
// // 隐藏扫描UI
// binding.blueList.scanLayout.visibility = View.GONE
//
// // 停止蓝牙扫描
// AndInfo.instance.bluetooth.stopScan()
// }
override fun onNegativeClick() {
super.onNegativeClick()
// stopScanning()
}
override fun onPositiveClick() {
super.onPositiveClick()
// stopScanning()
}
override fun onPause() {
super.onPause()
// 停止扫描
// stopScanning()
}
}

View File

@ -88,7 +88,7 @@
android:padding="10dp"
android:textColor="@color/white"
android:text="@string/settings"
android:background="@drawable/hard_bg"
android:background="@drawable/dialog_ok_bg"
android:visibility="gone" />
<com.google.android.material.textview.MaterialTextView
android:id="@+id/text_ok"

View File

@ -6,12 +6,31 @@
android:paddingVertical="20dp"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical">
<LinearLayout
android:id="@+id/view"
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
</LinearLayout>
>
<LinearLayout
android:id="@+id/view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"/>
<LinearLayout
android:id="@+id/progressbar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:gravity="center"
android:orientation="vertical">
<ProgressBar
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:indeterminateTint="@color/module_title_color" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/scanning"/>
</LinearLayout>
</RelativeLayout>
</androidx.core.widget.NestedScrollView>

View File

@ -1,8 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:tools="http://schemas.android.com/tools">
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
@ -31,111 +32,168 @@
android:layout_weight="1" />
<ImageView
android:layout_width="23dp"
android:layout_height="23dp"
android:id="@+id/blueSetting"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:src="@mipmap/setting" />
</LinearLayout>
<LinearLayout
android:id="@+id/showLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="15dp"
android:orientation="vertical">
<com.google.android.material.textview.MaterialTextView
android:id="@+id/showText"
style="@style/LeftContent"
android:layout_width="150dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/paired_devices" />
<View
android:layout_width="0dp"
android:layout_height="1dp"
android:layout_weight="1"/>
android:text="@string/bluetooth_support" />
<com.google.android.material.textview.MaterialTextView
android:id="@+id/pair1"
android:id="@+id/show1"
style="@style/LeftContent"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="14sp"
android:paddingHorizontal="12dp"
android:paddingVertical="8dp"
android:layout_marginTop="21dp"
android:background="@drawable/hard_bg"
android:textStyle="normal"
android:paddingHorizontal="12dp"
android:paddingVertical="8dp"
android:text="@string/show"
android:textColor="@color/module_title_color"
android:textAlignment="center"
android:textColor="@color/module_title_color"
android:textSize="14sp"
tools:ignore="RelativeOverlap" />
</LinearLayout>
<!-- <LinearLayout-->
<!-- android:layout_width="match_parent"-->
<!-- android:layout_height="wrap_content"-->
<!-- android:layout_marginTop="15dp"-->
<!-- android:orientation="horizontal">-->
<!-- <com.google.android.material.textview.MaterialTextView-->
<!-- style="@style/LeftContent"-->
<!-- android:layout_width="150dp"-->
<!-- android:layout_height="27dp"-->
<!-- android:text="@string/nearby_devices" />-->
<!-- <View-->
<!-- android:layout_width="0dp"-->
<!-- android:layout_height="1dp"-->
<!-- android:layout_weight="1"/>-->
<!-- <com.google.android.material.textview.MaterialTextView-->
<!-- android:id="@+id/pair2"-->
<!-- style="@style/LeftContent"-->
<!-- android:layout_width="wrap_content"-->
<!-- android:layout_height="wrap_content"-->
<!-- android:paddingHorizontal="10dp"-->
<!-- android:text="@string/show"-->
<!-- tools:ignore="RelativeOverlap" />-->
<!-- </LinearLayout>-->
<com.google.android.material.textview.MaterialTextView
style="@style/TextHeavy20"
android:layout_width="wrap_content"
<LinearLayout
android:id="@+id/blueList"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="30dp"
android:textSize="16sp"
android:text="@string/bluetooth_4_features"
tools:ignore="RelativeOverlap" />
<include
android:id="@+id/blueCheck1"
layout="@layout/common_check_style" />
<include
android:id="@+id/blueCheck2"
layout="@layout/common_check_style" />
<include
android:id="@+id/blueCheck3"
layout="@layout/common_check_style" />
<include
android:id="@+id/blueCheck4"
layout="@layout/common_check_style" />
<com.google.android.material.textview.MaterialTextView
style="@style/TextHeavy20"
android:textSize="16sp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="30dp"
android:text="@string/bluetooth_5_features"
tools:ignore="RelativeOverlap" />
<include
android:id="@+id/blueCheck01"
layout="@layout/common_check_style" />
<include
android:id="@+id/blueCheck02"
layout="@layout/common_check_style" />
<include
android:id="@+id/blueCheck03"
layout="@layout/common_check_style" />
<include
android:id="@+id/blueCheck04"
layout="@layout/common_check_style" />
<include
android:id="@+id/blueCheck05"
layout="@layout/common_check_style" />
android:visibility="gone"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="15dp"
android:orientation="vertical">
<com.google.android.material.textview.MaterialTextView
android:id="@+id/pairText"
style="@style/LeftContent"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/paired_devices" />
<com.google.android.material.textview.MaterialTextView
android:id="@+id/pair1"
style="@style/LeftContent"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="21dp"
android:background="@drawable/hard_bg"
android:paddingHorizontal="12dp"
android:paddingVertical="8dp"
android:text="@string/show"
android:textAlignment="center"
android:textColor="@color/module_title_color"
android:textSize="14sp"
tools:ignore="RelativeOverlap" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="15dp"
android:orientation="vertical">
<com.google.android.material.textview.MaterialTextView
style="@style/LeftContent"
android:layout_width="wrap_content"
android:layout_height="27dp"
android:text="@string/nearby_devices" />
<com.google.android.material.textview.MaterialTextView
android:id="@+id/pair2"
style="@style/LeftContent"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="21dp"
android:background="@drawable/hard_bg"
android:paddingHorizontal="12dp"
android:paddingVertical="8dp"
android:text="@string/show"
android:textAlignment="center"
android:textColor="@color/module_title_color"
android:textSize="14sp" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<com.google.android.material.textview.MaterialTextView
style="@style/TextHeavy20"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="30dp"
android:text="@string/bluetooth_4_features"
android:textSize="16sp"
tools:ignore="RelativeOverlap" />
<include
android:id="@+id/blueCheck1"
layout="@layout/common_check_style" />
<include
android:id="@+id/blueCheck2"
layout="@layout/common_check_style" />
<include
android:id="@+id/blueCheck3"
layout="@layout/common_check_style" />
<include
android:id="@+id/blueCheck4"
layout="@layout/common_check_style" />
<com.google.android.material.textview.MaterialTextView
style="@style/TextHeavy20"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="30dp"
android:text="@string/bluetooth_5_features"
android:textSize="16sp"
tools:ignore="RelativeOverlap" />
<include
android:id="@+id/blueCheck01"
layout="@layout/common_check_style" />
<include
android:id="@+id/blueCheck02"
layout="@layout/common_check_style" />
<include
android:id="@+id/blueCheck03"
layout="@layout/common_check_style" />
<include
android:id="@+id/blueCheck04"
layout="@layout/common_check_style" />
<include
android:id="@+id/blueCheck05"
layout="@layout/common_check_style" />
</LinearLayout>
</LinearLayout>
</LinearLayout>
</LinearLayout>

View File

@ -442,7 +442,7 @@
android:text="@string/disk_partitions" />
<TextView
android:id="@+id/cpuBtn"
style="@style/TextDialogSubTitle"
style="@style/LeftContent"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="21dp"

View File

@ -300,4 +300,5 @@
<string name="exposure_modes">Exposure modes</string>
<string name="autofocus_modes">Autofocus modes</string>
<string name="scene_modes">Scene modes</string>
<string name="scanning">Scanning...</string>
</resources>