新增蓝牙附近扫描,other模块,input devices弹框

This commit is contained in:
yuqian 2026-01-04 15:34:27 +08:00
parent 3ec46d1ac0
commit b21b706fac
9 changed files with 273 additions and 206 deletions

View File

@ -1,5 +1,6 @@
package com.xyzshell.myphoneinfo.dashboard
import android.annotation.SuppressLint
import android.bluetooth.BluetoothAdapter
import android.content.ActivityNotFoundException
import android.content.Intent
@ -43,8 +44,7 @@ class HardWareFragment : Fragment() {
refreshBluetoothInfo()
} else {
// 显示权限被拒绝的UI
// showPermissionDeniedUI()
Toast.makeText(requireContext(), "权限被拒绝", Toast.LENGTH_SHORT).show()
Toast.makeText(requireContext(), getString(R.string.permissions_required), Toast.LENGTH_SHORT).show()
}
}
@ -69,14 +69,6 @@ class HardWareFragment : Fragment() {
var intent = Intent(requireContext(), AnalysisActivity::class.java)
startActivity(intent)
}
// binding.pair2.setOnClickListener {
// dialogBlueTooth= dialogBlueTooth?: DialogBlueTooth()
// dialogBlueTooth?.show(childFragmentManager, "BlueTooth2")
// }
binding.othertext.setOnClickListener {
dialogInput = dialogInput ?: DialogInput()
dialogInput?.show(childFragmentManager, "Input")
}
binding.extensionShow.setOnClickListener {
dialogExtension?.show(childFragmentManager, "Extention")
}
@ -97,6 +89,7 @@ class HardWareFragment : Fragment() {
setMemoryInfo()
setBlueToothInfo()
setAudioInfo()
setInputInfo()
}
/**
@ -108,6 +101,34 @@ class HardWareFragment : Fragment() {
binding.check3.content.text = getString(R.string.midl)
}
/**
* Input信息
* */
private fun setInputInfo() {
val inputInfo = AndInfo.instance.input
binding.otherCheck1.content.text=getString(R.string.usb_host_support)
binding.otherCheck1.image.isSelected=inputInfo.hasUsbHostSupport()
binding.otherCheck2.content.text=getString(R.string.usb_accessory_support)
binding.otherCheck2.image.isSelected=inputInfo.hasUsbAccessorySupport()
binding.otherCheck3.content.text=getString(R.string.fingerprint)
binding.otherCheck3.image.isSelected=inputInfo.hasFingerprintSensor()
binding.otherCheck4.content.text=getString(R.string.infrared_transmitter)
binding.otherCheck4.image.isSelected=inputInfo.hasInfraredSensor()
binding.otherCheck5.content.text=getString(R.string.uwb_support)
binding.otherCheck5.image.isSelected=inputInfo.hasUwbSupport()
binding.otherCheck6.content.text=getString(R.string.nfc_support)
binding.otherCheck6.image.isSelected=inputInfo.hasNfcSupport()
binding.otherCheck7.content.text=getString(R.string.secure_nfc_support)
binding.otherCheck7.image.isSelected=inputInfo.hasSecureNfcSupport()
binding.otherCheck8.content.text=getString(R.string.gps)
binding.otherCheck8.image.isSelected=inputInfo.hasGpsSupport()
binding.othertext.setOnClickListener {
dialogInput = dialogInput ?: DialogInput()
dialogInput?.show(childFragmentManager, "Input")
}
}
/**
* memory信息
* */
@ -298,19 +319,35 @@ class HardWareFragment : Fragment() {
/**
* bluetooth相关信息*/
@SuppressLint("SuspiciousIndentation")
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()
}
//蓝牙features支持
bluetoothInfo.getBluetoothFeatures().let { features ->
binding.bluetoothLayout.blueCheck1.content.text=getString(R.string.bluetooth_le)
binding.bluetoothLayout.blueCheck1.image.isSelected=features.bluetoothLe
binding.bluetoothLayout.blueCheck2.content.text=getString(R.string.multiple_advertisement)
binding.bluetoothLayout.blueCheck2.image.isSelected=features.multipleAdvertisement
binding.bluetoothLayout.blueCheck3.content.text=getString(R.string.offloaded_filtering)
binding.bluetoothLayout.blueCheck3.image.isSelected=features.offloadedFiltering
binding.bluetoothLayout.blueCheck4.content.text=getString(R.string.offloaded_scan_batching)
binding.bluetoothLayout.blueCheck4.image.isSelected=features.offloadedScanBatching
binding.bluetoothLayout.blueCheck01.content.text=getString(R.string.le_periodic_advertising)
binding.bluetoothLayout.blueCheck01.image.isSelected=features.lePeriodicAdvertising
binding.bluetoothLayout.blueCheck02.content.text=getString(R.string.le_extended_advertising)
binding.bluetoothLayout.blueCheck02.image.isSelected=features.leExtendedAdvertising
binding.bluetoothLayout.blueCheck03.content.text=getString(R.string.le_2m_phy_high_speed)
binding.bluetoothLayout.blueCheck03.image.isSelected=features.leCodedPhy
binding.bluetoothLayout.blueCheck04.content.text=getString(R.string.le_2m_phy_low_power)
binding.bluetoothLayout.blueCheck04.image.isSelected=features.le2MbPhy
binding.bluetoothLayout.blueCheck05.content.text=getString(R.string.le_audio_support)
binding.bluetoothLayout.blueCheck05.image.isSelected=features.leAudioSupport
}
}
// 请求蓝牙权限的方法
private fun requestBluetoothPermissions() {
@ -364,7 +401,7 @@ class HardWareFragment : Fragment() {
binding.bluetoothLayout.blueList.visibility = View.GONE
// 更新UI文本
binding.bluetoothLayout.showText.text = "Bluetooth is not enabled."
binding.bluetoothLayout.showText.text = getString(R.string.bluetooth_not_enabled)
// 设置打开蓝牙按钮
binding.bluetoothLayout.show1.setOnClickListener {
@ -406,24 +443,13 @@ class HardWareFragment : Fragment() {
* 刷新蓝牙信息
*/
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}个设备"
binding.bluetoothLayout.pair1.setOnClickListener {
showPairedDevicesDialog()
}
// 打开弹窗开始扫描超过15s扫描超时结束显示扫描到的列表
binding.bluetoothLayout.pair2.setOnClickListener {
showNearByDevicesDialog()
}
}
/**

View File

@ -1,5 +1,6 @@
package com.xyzshell.myphoneinfo.dialog
import android.annotation.SuppressLint
import android.os.Bundle
import android.os.CountDownTimer
import android.view.LayoutInflater
@ -9,6 +10,7 @@ import android.widget.TextView
import android.widget.Toast
import androidx.compose.material3.Text
import com.xyzshell.andinfo.AndInfo
import com.xyzshell.andinfo.libs.DeviceInfo
import com.xyzshell.myphoneinfo.R
import com.xyzshell.myphoneinfo.base.BaseDialogFragment
import com.xyzshell.myphoneinfo.databinding.DialogBlueToothBinding
@ -16,6 +18,7 @@ import com.xyzshell.myphoneinfo.databinding.DialogBlueToothBinding
class DialogBlueTooth(val type:Int) :BaseDialogFragment<DialogBlueToothBinding>(DialogBlueToothBinding::inflate){
private var isScanning = false
private var scanTimer: CountDownTimer? = null
private val SCAN_DURATION = 15000 // 15秒
override fun getTitle(): String = resources.getString(R.string.bluetooth)
override fun getIconRes(): Int=5
@ -34,12 +37,13 @@ class DialogBlueTooth(val type:Int) :BaseDialogFragment<DialogBlueToothBinding>(
// list.add(deviceMap)
// }
if(type==0){// 显示已配对设备列表
binding.progressbar.visibility=View.GONE
val container = binding.view as? ViewGroup
container?.let {
// 遍历 list为每个设备创建 item view
if(list.isEmpty()){
it.addView(TextView(requireContext()).apply {
text = "No devices"
text = getString(R.string.no_devices)
})
}else{
list.forEach { deviceMap ->
@ -60,115 +64,154 @@ class DialogBlueTooth(val type:Int) :BaseDialogFragment<DialogBlueToothBinding>(
}
}else{// 显示扫描设备列表
// startScanning()
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()
// }
/**
* 开始扫描设备
*/
@SuppressLint("SuspiciousIndentation")
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秒倒计时
binding.progressItem.max = SCAN_DURATION / 1000 // 最大值为15
binding.progressItem.progress = SCAN_DURATION / 1000 // 初始进度为15
// 启动15秒进度条倒计时
startProgressCountdown()
// 开始扫描
try {
val container = binding.view as? ViewGroup
container?.let {
var list:List<DeviceInfo> = arrayListOf()
// 遍历 list为每个设备创建 item view
// 同时扫描经典蓝牙和BLE设备
bluetoothInfo.scanNearbyDevices(
onDeviceFound = { device ->
if (!isAdded || context == null) return@scanNearbyDevices
requireActivity().runOnUiThread {
if (!isAdded || context == null) return@runOnUiThread
// 加载 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)
tvName.text = device.name
tvAddress.text = device.address
// 添加到容器
it.addView(itemView)
binding.view.visibility = View.VISIBLE
binding.progressbar.visibility = View.GONE
}
},
onScanFinished = {
requireActivity().runOnUiThread {
stopScanning()
}
}
)
// 扫描BLE设备
if (bluetoothInfo.isBluetoothLeSupported) {
bluetoothInfo.scanLeDevices(
onDeviceFound = { device ->
if (!isAdded || context == null) return@scanLeDevices
requireActivity().runOnUiThread {
if (!isAdded || context == null) return@runOnUiThread
// 加载 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)
tvName.text = device.name
tvAddress.text = device.address
// 添加到容器
it.addView(itemView)
binding.view.visibility = View.VISIBLE
binding.progressbar.visibility = View.GONE
}
},
durationMillis = SCAN_DURATION.toLong()
)
}
}
} catch (e: SecurityException) {
Toast.makeText(requireContext(), getString(R.string.permissions_required), Toast.LENGTH_SHORT).show()
stopScanning()
}
}
/**
* 启动进度条倒计时
*/
private fun startProgressCountdown() {
scanTimer = object : CountDownTimer(SCAN_DURATION.toLong(), 1000) {
override fun onTick(millisUntilFinished: Long) {
val secondsLeft = millisUntilFinished / 1000
// 更新进度条从15递减到0
binding.progressItem.progress = secondsLeft.toInt()
}
override fun onFinish() {
// 倒计时结束,停止扫描
stopScanning()
binding.progressbar.visibility = View.GONE
}
}.start()
}
/**
* 停止扫描
*/
private fun stopScanning() {
if (!isScanning) return
isScanning = false
// 停止计时器
scanTimer?.cancel()
scanTimer = null
// 重置进度条
binding.progressItem.progress = 0
// 停止蓝牙扫描
AndInfo.instance.bluetooth.stopScan()
}
override fun onNegativeClick() {
super.onNegativeClick()
// stopScanning()
stopScanning()
}
override fun onPositiveClick() {
super.onPositiveClick()
// stopScanning()
stopScanning()
}
override fun onPause() {
super.onPause()
// 停止扫描
// stopScanning()
stopScanning()
}
}

View File

@ -1,13 +1,14 @@
package com.xyzshell.myphoneinfo.dialog
import android.graphics.Typeface
import android.os.Bundle
import android.text.SpannableString
import android.text.style.StyleSpan
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import com.xyzshell.andinfo.AndInfo
import com.xyzshell.andinfo.libs.InputInfo
import com.xyzshell.myphoneinfo.R
import com.xyzshell.myphoneinfo.base.BaseDialogFragment
import com.xyzshell.myphoneinfo.databinding.DialogCpuInfoBinding
import com.xyzshell.myphoneinfo.databinding.DialogInputBinding
class DialogInput :BaseDialogFragment<DialogInputBinding>(DialogInputBinding::inflate){
@ -19,57 +20,40 @@ class DialogInput :BaseDialogFragment<DialogInputBinding>(DialogInputBinding::in
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding.text1.textTitle.text = "input_mt_wrapper"
binding.text1.textContent.text = "Input Device 1:\n" +
"input_mt_wrapper\n" +
"Descriptor:\n" +
"a988c03908bee014d816\n" +
"71ffdd909090304130b5\n" +
"Generation: 14\n" +
"Location: built-in\n" +
"Keyboard Type:\n" +
"non-alphabetic\n" +
"Has Vibrator: false\n" +
"Has mic: false\n" +
"Sources: 0x1103\n" +
"(keyboard touchscreen)\n" +
"AXISX:\n" +
"source=0x1002 min=0.0\n" +
"max=719.0 flat=0.0\n" +
"fuzz=0.0 resolution=0.0\n" +
"AXISY:\n" +
"source=0x1002 min=0.0\n" +
"max=1559.0 flat=0.0\n" +
"fuzz=0.0 resolution=0.0\n" +
"AXIS PRESSURE:\n" +
"source-0x1002 min=0.0\n" +
"max=1.0 flat=0.0 fuzz=0.0\n" +
"resolution=0.0\n" +
"AXIS_SIZE:\n" +
"source=0x1002 min=0.0\n" +
"max=1.0 flat=0.0 fuzz=0.0\n" +
"resolution=0.0\n" +
"AXIS TOUCH_MAJOR:\n" +
"source=0x1002 min=0.0\n" +
"max=1718.1385 flat=0.0\n" +
"fuzz=0.0 resolution=0.0\n" +
"AXISTOUCH_MINOR:\n" +
"source=0x1002 min=0.0\n" +
"max=1718.1385 flat=0.0\n" +
"fuzz=0.0 resolution=0.0\n" +
"AXIS TOOL MAJOR:\n" +
"source=0×1002 min=0.0\n" +
"max=1718.1385 flat=0.0\n" +
"fuzz=0.0 resolution=0.0\n" +
"AXIS TOOL MINOR:\n" +
"source=0x1002 min=0.0\n" +
"max=1718.1385 flat-0.0\n" +
"fuzz=0.0 resolution=0.0\n" +
"AXIS_ORIENTATION:\n" +
"source=0x1002\n" +
"min=-1.5707964\n" +
"max=1.5707964 flat=0.0\n" +
"fuzz=0.0 resolution=0.0"
val info= AndInfo.instance.input
val inputDevices = info.getInputDevices()
val container = binding.container as? ViewGroup
container?.let{
if(inputDevices.isEmpty()){
it.addView(TextView(requireContext()).apply {
text = getString(R.string.no_devices)
})
}else{
inputDevices.forEach { device ->
val view= LayoutInflater.from(requireContext()).inflate(R.layout.common_text_input,it,false)
view.findViewById<TextView>(R.id.textTitle).text = device.name
val sb = StringBuilder()
sb.append("Input Device ${device.id} :${device.name}\n")
if(device.descriptor!=null){
sb.append("Descriptor: ${device.descriptor}\n")
}
sb.append("Vendor Id: ${device.vendorId}\n")
sb.append("Product Id: ${device.productId}\n")
sb.append("Enabled: ${device.isEnabled}\n")
sb.append("Keyboard Type: ${InputInfo.InputDeviceInfo.keyboardTypeToString[device.keyboardType] ?: device.keyboardType.toString()}\n")
sb.append("Has Vibrator: ${device.hasVibrator}\n")
sb.append("Has Microphone: ${device.hasMicrophone}\n")
if(device.sources!=null){
sb.append("Sources: ${device.sources}\n")
}
view.findViewById<TextView>(R.id.textContent).text =sb
it.addView(view)
}
}
}
}
override fun onNegativeClick() {

View File

@ -12,16 +12,16 @@
android:layout_width="150dp"
android:layout_height="wrap_content"
android:text="@string/model"
android:textSize="15sp"
android:textSize="12sp"
android:textStyle="bold" />
<com.google.android.material.textview.MaterialTextView
android:id="@+id/textContent"
style="@style/TextContentRight"
style="@style/LeftContent"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="20dp"
android:text="@string/apps"
android:textSize="15sp"
tools:ignore="RelativeOverlap" />
android:textColor="@color/right_color"
android:textSize="12sp" />
</LinearLayout>

View File

@ -21,10 +21,13 @@
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:gravity="center"
android:visibility="gone"
android:orientation="vertical">
<ProgressBar
android:id="@+id/progressItem"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:max="15"
android:indeterminateTint="@color/module_title_color" />
<TextView
android:layout_width="wrap_content"

View File

@ -9,18 +9,11 @@
<androidx.core.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="wrap_content">
<LinearLayout
android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<include layout="@layout/common_text_15size_style" android:id="@+id/text1"/>
<include layout="@layout/common_text_15size_style" android:id="@+id/text2"/>
<include layout="@layout/common_text_15size_style" android:id="@+id/text3"/>
<include layout="@layout/common_text_15size_style" android:id="@+id/text4"/>
<include layout="@layout/common_text_15size_style" android:id="@+id/text5"/>
</LinearLayout>
android:orientation="vertical"/>
</androidx.core.widget.NestedScrollView>

View File

@ -73,7 +73,7 @@
android:id="@+id/blueList"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:visibility="gone"
android:visibility="visible"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"

View File

@ -2,20 +2,17 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginVertical="4dp"
android:layout_margin="4dp"
android:orientation="horizontal">
<com.google.android.material.textview.MaterialTextView
style="@style/LeftContent"
android:id="@+id/textTitle"
android:layout_width="wrap_content"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="wrap_content"
android:gravity="start"
android:textSize="12sp"
android:text="NUCBOX_K8" />
<View
android:layout_width="0dp"
android:layout_height="1dp"
android:layout_weight="1"/>
<com.google.android.material.textview.MaterialTextView
android:id="@+id/textContent"
style="@style/LeftContent"
@ -23,6 +20,7 @@
android:layout_height="wrap_content"
android:gravity="start"
android:textSize="12sp"
android:layout_marginStart="20dp"
android:textColor="@color/right_color"
android:text="D0:12:55:2C:43:FB" />
</LinearLayout>

View File

@ -301,4 +301,24 @@
<string name="autofocus_modes">Autofocus modes</string>
<string name="scene_modes">Scene modes</string>
<string name="scanning">Scanning...</string>
<string name="no_devices">No devices</string>
<string name="permissions_required">Bluetooth permission denied.</string>
<string name="bluetooth_not_enabled">Bluetooth is not enabled.</string>
<string name="bluetooth_le">Bluetooth LE (Low Energy)</string>
<string name="multiple_advertisement">Multiple advertisement</string>
<string name="offloaded_filtering">Offloaded filtering</string>
<string name="offloaded_scan_batching">Offloaded scan batching</string>
<string name="le_periodic_advertising">LE Periodic Advertising</string>
<string name="le_extended_advertising">LE Extended Advertising</string>
<string name="le_2m_phy_high_speed">LE 2M PHY (high speed)</string>
<string name="le_2m_phy_low_power">LE Coded PHY (long range)</string>
<string name="le_audio_support">LE Audio support</string>
<string name="usb_host_support">USB host support</string>
<string name="usb_accessory_support">USB accessory support</string>
<string name="infrared_transmitter">Infrared transmitter</string>
<string name="uwb_support">UWB support</string>
<string name="nfc_support">NFC support</string>
<string name="secure_nfc_support">Secure NFC support</string>
<string name="gps">GPS</string>
</resources>