新增viewmodel,修改完善hardware

This commit is contained in:
yuqian 2026-01-16 11:36:43 +08:00
parent d3de7d8907
commit 596daf5fcc
11 changed files with 763 additions and 353 deletions

View File

@ -6,12 +6,18 @@ import androidx.activity.enableEdgeToEdge
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import androidx.viewbinding.ViewBinding
import com.xyzshell.myphoneinfo.R
import com.xyzshell.myphoneinfo.main.MainViewModel
abstract class BaseActivity<VB : ViewBinding> : AppCompatActivity() {
protected lateinit var binding: VB
protected val mainViewModel: MainViewModel by lazy {
ViewModelProvider(this)[MainViewModel::class.java]
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
@ -28,7 +34,9 @@ abstract class BaseActivity<VB : ViewBinding> : AppCompatActivity() {
protected abstract fun inflateBinding(inflater: LayoutInflater): VB
protected open fun initView() {}
protected open fun initView() {
mainViewModel
}
protected open fun initData() {}
}

View File

@ -0,0 +1,76 @@
package com.xyzshell.myphoneinfo.custom
import android.os.Handler
import android.os.Looper
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
object RefreshManager {
private var isLoading = false
private val handler = Handler(Looper.getMainLooper())
private var timeoutRunnable: Runnable? = null
/**
* 开始刷新
* @param swipeRefreshLayout 可选的 SwipeRefreshLayout会自动显示刷新动画
* @param timeoutMs 超时时间毫秒默认10秒
* @param onTimeout 超时回调
*/
fun load(
swipeRefreshLayout: SwipeRefreshLayout? = null,
timeoutMs: Long = 10000L,
onTimeout: (() -> Unit)? = null
) {
if (isLoading) return
isLoading = true
swipeRefreshLayout?.isRefreshing = true
// 设置超时
timeoutRunnable = Runnable {
if (isLoading) {
isLoading = false
swipeRefreshLayout?.isRefreshing = false
onTimeout?.invoke()
}
}
handler.postDelayed(timeoutRunnable!!, timeoutMs)
}
/**
* 停止刷新
* @param swipeRefreshLayout 可选的 SwipeRefreshLayout会自动停止刷新动画
*/
fun stop(swipeRefreshLayout: SwipeRefreshLayout? = null) {
if (!isLoading) return
isLoading = false
timeoutRunnable?.let { handler.removeCallbacks(it) }
timeoutRunnable = null
swipeRefreshLayout?.isRefreshing = false
}
/**
* 检查是否正在刷新
*/
fun isLoading(): Boolean = isLoading
/**
* 安全的执行刷新操作最简用法
* @param refreshAction 刷新逻辑
* @param swipeRefreshLayout SwipeRefreshLayout
* @param timeoutMs 超时时间
*/
fun safeRefresh(
refreshAction: () -> Unit,
swipeRefreshLayout: SwipeRefreshLayout,
timeoutMs: Long = 10000L
) {
if (isLoading) return
load(swipeRefreshLayout, timeoutMs) {
// 超时回调
}
refreshAction()
}
}

View File

@ -6,16 +6,20 @@ import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.activityViewModels
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.LinearLayoutManager
import com.xyzshell.andinfo.AndInfo
import com.xyzshell.andinfo.libs.AppDetails
import com.xyzshell.myphoneinfo.R
import com.xyzshell.myphoneinfo.adapter.AppListAdapter
import com.xyzshell.myphoneinfo.custom.RefreshManager.load
import com.xyzshell.myphoneinfo.custom.RefreshManager.stop
import com.xyzshell.myphoneinfo.databinding.FragmentAppsBinding
import com.xyzshell.myphoneinfo.dialog.AppDialogFragment
import com.xyzshell.myphoneinfo.dialog.BottomDialogFragment
import com.xyzshell.myphoneinfo.main.MainScrollActivity
import com.xyzshell.myphoneinfo.main.MainViewModel
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
@ -25,7 +29,9 @@ class AppsFragment : Fragment(),AppListAdapter.OnShowDialogListener {
private lateinit var binding: FragmentAppsBinding
private lateinit var dialogFragment: AppDialogFragment
private lateinit var bottomDialog: BottomDialogFragment
private val viewModel: MainViewModel by activityViewModels()
private var sel: Int? = 0
private var userSize=0
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
}
@ -34,17 +40,15 @@ class AppsFragment : Fragment(),AppListAdapter.OnShowDialogListener {
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
binding = FragmentAppsBinding.inflate(inflater, container, false)
val adapter= AppListAdapter()
val installedApps = AndInfo.instance.app.getInstalledApps()
val userSize=installedApps.size
viewLifecycleOwner.lifecycleScope.launch {
delay(50) // 短暂延迟确保UI完成布局
withContext(Dispatchers.Main) {
adapter.setList(installedApps)
binding = FragmentAppsBinding.inflate(inflater, container, false)
viewModel.installedApps.observe(viewLifecycleOwner){
appDetails ->
stop(binding.swipeRefresh)
adapter.setList(appDetails)
userSize=appDetails.size
}
adapter.setOnclickListener(this@AppsFragment)
}}
binding.recyclerView.adapter = adapter
binding.recyclerView.layoutManager = LinearLayoutManager(requireContext())
binding.tvTitle.text=when(0){//todo replace with user/system/all/none
@ -53,6 +57,10 @@ class AppsFragment : Fragment(),AppListAdapter.OnShowDialogListener {
2 -> "All(23)"
else -> "None"
}
binding.swipeRefresh.setOnRefreshListener {
load(binding.swipeRefresh)
viewModel.loadApps()
}
binding.llTitle.setOnClickListener {
bottomDialog= BottomDialogFragment(type = "apps", sel = sel, invoke = {item->
sel = item

View File

@ -9,6 +9,7 @@ import android.os.Bundle
import android.os.CountDownTimer
import android.provider.Settings
import android.text.format.Formatter
import android.util.Log
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
@ -16,8 +17,10 @@ import android.view.ViewGroup
import android.widget.Toast
import androidx.activity.result.contract.ActivityResultContracts
import androidx.core.app.ActivityCompat
import androidx.fragment.app.activityViewModels
import com.xyzshell.andinfo.AndInfo
import com.xyzshell.andinfo.libs.CpuInfo
import com.xyzshell.andinfo.libs.MemInfo
import com.xyzshell.myphoneinfo.R
import com.xyzshell.myphoneinfo.custom.SetNumberOrWordUtils.convertToApproximateAspectRatio
import com.xyzshell.myphoneinfo.databinding.FragmentHardWareBinding
@ -25,6 +28,8 @@ import com.xyzshell.myphoneinfo.dialog.DialogBlueTooth
import com.xyzshell.myphoneinfo.dialog.DialogDiskPart
import com.xyzshell.myphoneinfo.dialog.DialogExtension
import com.xyzshell.myphoneinfo.dialog.DialogInput
import com.xyzshell.myphoneinfo.main.MainViewModel
import kotlin.getValue
import kotlin.math.roundToInt
class HardWareFragment : Fragment() {
@ -34,6 +39,8 @@ class HardWareFragment : Fragment() {
private var dialogInput: DialogInput? = null
private var dialogExtension: DialogExtension? = null
private var dialogDiskPart: DialogDiskPart? = null
private val mainViewModel: MainViewModel by activityViewModels()
private val requestPermissionLauncher = registerForActivityResult(
ActivityResultContracts.RequestMultiplePermissions()
@ -59,6 +66,7 @@ class HardWareFragment : Fragment() {
override fun onResume() {
super.onResume()
checkAndDisplayBluetoothInfo()
mainViewModel.refreshHardwareInfo(requireContext())
}
@ -68,7 +76,7 @@ class HardWareFragment : Fragment() {
): View? {
binding = FragmentHardWareBinding.inflate(inflater, container, false)
dialogExtension = dialogExtension ?: DialogExtension()
initText()
initView()
binding.cpuBtn.setOnClickListener {
var intent = Intent(requireContext(), AnalysisActivity::class.java)
startActivity(intent)
@ -84,7 +92,7 @@ class HardWareFragment : Fragment() {
return binding.root
}
private fun initText() {
private fun initView() {
setProcessorInfo()
setGpuInfo()
setDisplayInfo()
@ -107,27 +115,31 @@ class HardWareFragment : Fragment() {
* 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")
}
mainViewModel.input.observe(viewLifecycleOwner) { inputInfo ->
inputInfo?.let {
binding.otherCheck1.image.isSelected = inputInfo.hasUsbHostSupport()
binding.otherCheck2.image.isSelected = inputInfo.hasUsbAccessorySupport()
binding.otherCheck3.image.isSelected = inputInfo.hasFingerprintSensor()
binding.otherCheck4.image.isSelected = inputInfo.hasInfraredSensor()
binding.otherCheck5.image.isSelected = inputInfo.hasUwbSupport()
binding.otherCheck6.image.isSelected = inputInfo.hasNfcSupport()
binding.otherCheck7.image.isSelected = inputInfo.hasSecureNfcSupport()
binding.otherCheck8.image.isSelected = inputInfo.hasGpsSupport()
}
}
}
@ -135,79 +147,111 @@ class HardWareFragment : Fragment() {
* memory信息
* */
private fun setMemoryInfo() {
val storageInfo = AndInfo.instance.storage//存储信息
// 获取详细的存储分解
val breakdown = storageInfo.getInternalStorageBreakdown()
//内部存储器
val interused = storageInfo.formatBytes(breakdown.usedSpace)
val interfree = storageInfo.formatBytes(breakdown.freeSpace)
val intertotal = storageInfo.formatBytes(breakdown.totalSpace)
mainViewModel.memInfo.observe(viewLifecycleOwner){memInfo ->
memInfo?.let {
//内存memory
val memUsed = memInfo.formatBytes(memInfo.getMemoryInfo().usedRam)
val memFree = memInfo.formatBytes(memInfo.getMemoryInfo().availableRam)
val memTotal = memInfo.formatBytes(memInfo.getMemoryInfo().totalRam)
val zramTotal = memInfo.formatBytes(memInfo.getMemoryInfo().zramTotal)
val zramUsed = memInfo.formatBytes(memInfo.getMemoryInfo().zramUsed)
val zramFree =
memInfo.formatBytes(memInfo.getMemoryInfo().zramTotal - memInfo.getMemoryInfo().zramUsed)
//总存储信息
val outused =Formatter.formatFileSize(AndInfo.instance.context, storageInfo.internalStorageUsedSpace)
val outfree = Formatter.formatFileSize(AndInfo.instance.context, storageInfo.internalStorageAvailableSpace)
val outtotal = Formatter.formatFileSize(AndInfo.instance.context, storageInfo.internalStorageTotalSpace)
//内存todo
binding.memoryLayout.memText0.textTitle.text = getString(R.string.type)
binding.memoryLayout.memText0.textContent.text = memInfo.getMemoryInfo().memType
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()
//appsAndData
val appsAndData = storageInfo.getFormattedAppsAndDataSize()
val system = storageInfo.getFormattedSystemSize()
val other= storageInfo.getFormattedFreeSpace()+storageInfo.getFormattedCacheSize()
binding.memoryLayout.memText1.textContent.text = memTotal
binding.memoryLayout.ram1.text = "$memUsed used"
binding.memoryLayout.ram2.text = "$memFree free"
binding.memoryLayout.seekbar.progress =
(memUsed.substringBefore(" ").toDouble() / memTotal
.substringBefore(" ")
.toDouble() * 100).toInt()
//zarm
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.memText3.textContent.text = zramTotal
binding.memoryLayout.ram3.text = "$zramUsed used"
binding.memoryLayout.ram4.text = "$zramFree free"
binding.memoryLayout.seekbar2.progress =
(outused.substringBefore(" ").toDouble() / outtotal.substringBefore(" ")
(zramTotal.substringBefore(" ").toDouble() / zramTotal.substringBefore(" ")
.toDouble() * 100).toInt()
}
}
//storage
// 获取总存储详细的存储分解
mainViewModel.storageInfo.observe(viewLifecycleOwner){storageInfo ->
storageInfo?.let {
val breakdown = storageInfo.getInternalStorageBreakdown()
val stoUsed = storageInfo.formatBytes(breakdown.usedSpace)
val stoFree = storageInfo.formatBytes(breakdown.freeSpace)
val stoTotal = storageInfo.formatBytes(breakdown.totalSpace)
//appsAndData
val appsAndData = storageInfo.getFormattedAppsAndDataSize()
val system = storageInfo.getFormattedSystemSize()
val other = storageInfo.getFormattedFreeSpace()
binding.memoryLayout.storText1.textTitle.text = getString(R.string.size)
binding.memoryLayout.storText1.textContent.text = outtotal
binding.memoryLayout.stor1.text = outused + " used"
binding.memoryLayout.stor2.text = outtotal + " total"
binding.memoryLayout.storText1.textContent.text = stoTotal
binding.memoryLayout.stor1.text = stoUsed + " used"
binding.memoryLayout.stor2.text = stoTotal + " total"
binding.memoryLayout.radius1.text = appsAndData
binding.memoryLayout.radius2.text = system
binding.memoryLayout.radius3.text = other
binding.memoryLayout.seekbar3.progress =
(outused.substringBefore(" ").toDouble() / outtotal.substringBefore(" ")
(stoUsed.substringBefore(" ").toDouble() / stoTotal.substringBefore(" ")
.toDouble() * 100).toInt()
//internalStorage
val interUsed = storageInfo.getInternalStorageBreakdown().usedSpace
val interFree = storageInfo.getInternalStorageBreakdown().freeSpace
val interTotal = storageInfo.getInternalStorageBreakdown().totalSpace
binding.memoryLayout.interText1.textTitle.text = getString(R.string.filesystem)
binding.memoryLayout.interText1.textContent.text =
storageInfo.internalStorageEncryptionType.toString()
storageInfo.internalStorageFileSystemType.toString()
binding.memoryLayout.interText2.textTitle.text = getString(R.string.block_size)
binding.memoryLayout.interText2.textContent.text = intertotal
binding.memoryLayout.interText2.textContent.text = storageInfo.formatBytes(interTotal)
binding.memoryLayout.interText3.textTitle.text = "/data"
binding.memoryLayout.interText3.textContent.text = intertotal
binding.memoryLayout.stor3.text = interused + " used"
binding.memoryLayout.stor4.text = interfree + " free"
binding.memoryLayout.seekbar2.progress =
(interused.substringBefore(" ").toDouble() / intertotal.substringBefore(" ")
.toDouble() * 100).toInt()
binding.memoryLayout.interText3.textContent.text = storageInfo.formatBytes(storageInfo.dataDirectoryTotalSpace)
binding.memoryLayout.stor3.text = "${storageInfo.formatBytes(interUsed)} used"
binding.memoryLayout.stor4.text = "${storageInfo.formatBytes(interFree)} free"
binding.memoryLayout.seekbar5.progress =
(interUsed.toDouble() / interTotal.toDouble() * 100).toInt()
storageInfo.abOtaPartitions?.forEach { text ->
Log.d("TTTTTTTT","disk part:${text}")
}
}
}
}
/**
* display信息
* */
private fun setDisplayInfo() {
val display = AndInfo.instance.display//显示信息
binding.disText1.textTitle.text = getString(R.string.resolution)
binding.disText2.textTitle.text = getString(R.string.screen_density)//这个是px
binding.disText3.textTitle.text = getString(R.string.screen_density_d)
binding.disText4.textTitle.text = getString(R.string.screen_size_e)
binding.disText5.textTitle.text = getString(R.string.aspect_ratio)
binding.disText6.textTitle.text = getString(R.string.refresh_rate)
binding.disText7.textTitle.text = getString(R.string.wide_color_gamut)
binding.disText8.textTitle.text = getString(R.string.hdr_support)
mainViewModel.displayInfo.observe(viewLifecycleOwner) { display ->
display?.let {
val defaultDisplayInfo = display.getDefaultDisplayInfo()
val width = defaultDisplayInfo?.widthPixels.toString()
val height = defaultDisplayInfo?.heightPixels.toString()
@ -216,26 +260,24 @@ class HardWareFragment : Fragment() {
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)
val screenSize = "${String.format("%.2f", display.getScreenSize()?.diagonalInches)}/ ${String.format("%.1f", display.getScreenSize()?.diagonalMm)} mm"
val screenSize = "${
String.format(
"%.2f",
display.getScreenSize()?.diagonalInches
)
}/ ${String.format("%.1f", display.getScreenSize()?.diagonalMm)} mm"
binding.disText4.textContent.text = screenSize
binding.disText5.textTitle.text = getString(R.string.aspect_ratio)
binding.disText5.textContent.text = convertToApproximateAspectRatio(
width = width.toInt(),
height = height.toInt()
)
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.disText6.textContent.text =
defaultDisplayInfo?.refreshRate.toString() + "HZ"
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()
@ -243,125 +285,155 @@ class HardWareFragment : Fragment() {
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.gpuText2.textTitle.text = getString(R.string.gpu)
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.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.gpuText9.textTitle.text = getString(R.string.vulkan_API)
binding.open1.text = getString(R.string.opengl)
mainViewModel.gpuInfo.observe(viewLifecycleOwner) { gpu ->
gpu?.let {
binding.gpuText1.textContent.text = gpu.getVendorName()
gpu.getGpuInformation().vkPhysicalDevices?.let { devices ->
if (devices.isNotEmpty()) {
devices.forEachIndexed { index, vkPhysicalDevice ->
binding.gpuText2.textContent.text = vkPhysicalDevice.deviceName
}
}
}
binding.gpuText3.textTitle.text = getString(R.string.max_frequency)
binding.gpuText3.textContent.text = "${gpu.getMaxFrequency()} MHz"
binding.gpuText4.textTitle.text = getString(R.string.architecture)
binding.gpuText4.textContent.text = gpu.getArchitecture()
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.gpuText6.textContent.text = "${gpu.getCacheSize()} KB"
binding.gpuText7.textTitle.text = getString(R.string.bus_width)
binding.gpuText7.textContent.text = "${gpu.getBandwidth()} GB/s"
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()
}
}
}
/**
* 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)
mainViewModel.cpuInfo.observe(viewLifecycleOwner) { cpu ->
cpu?.let {
binding.text0.textContent.text = cpu.getProcessorName()
binding.text1.textContent.text = cpu.getVendor()
binding.text2.textContent.text = cpu.getProcessorName()
binding.text3.textContent.text = cpu.cores.size.toString()
binding.text4.textContent.text = cpu.getAllArchitectures().joinToString()
binding.text5.textContent.text = cpu.getProcessInfo().process
binding.text6.textContent.text = cpu.getArchitecture()
binding.text7.textContent.text = cpu.getAbi()
binding.text8.textContent.text = cpu.getSupportedAbis().joinToString()
binding.text9.textContent.text = cpu.getFrequencyText()
}
}
}
/**
* bluetooth相关信息*/
@SuppressLint("SuspiciousIndentation")
private fun setBlueToothInfo() {
val bluetoothInfo = AndInfo.instance.bluetooth//蓝牙信息
if (!bluetoothInfo.isEnabled) return
binding.bluetoothLayout.blueCheck1.content.text = getString(R.string.bluetooth_le)
binding.bluetoothLayout.blueCheck2.content.text =
getString(R.string.multiple_advertisement)
binding.bluetoothLayout.blueCheck3.content.text =
getString(R.string.offloaded_filtering)
binding.bluetoothLayout.blueCheck4.content.text =
getString(R.string.offloaded_scan_batching)
binding.bluetoothLayout.blueCheck01.content.text =
getString(R.string.le_periodic_advertising)
binding.bluetoothLayout.blueCheck02.content.text =
getString(R.string.le_extended_advertising)
binding.bluetoothLayout.blueCheck03.content.text =
getString(R.string.le_2m_phy_high_speed)
binding.bluetoothLayout.blueCheck04.content.text =
getString(R.string.le_2m_phy_low_power)
binding.bluetoothLayout.blueCheck05.content.text = getString(R.string.le_audio_support)
binding.bluetoothLayout.showLayout.setOnClickListener {
requestBluetoothPermissions()
}
mainViewModel.bluetoothInfo.observe(viewLifecycleOwner) { features ->
features?.let {
if (!it.isEnabled) return@let
//蓝牙features支持
bluetoothInfo.getBluetoothFeatures().let { features ->
binding.bluetoothLayout.blueCheck1.content.text = getString(R.string.bluetooth_le)
it.getBluetoothFeatures().let { features ->
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.blueCheck2.image.isSelected =
features.multipleAdvertisement
binding.bluetoothLayout.blueCheck3.image.isSelected =
features.offloadedFiltering
binding.bluetoothLayout.blueCheck4.image.isSelected =
features.offloadedScanBatching
binding.bluetoothLayout.blueCheck01.image.isSelected =
features.lePeriodicAdvertising
binding.bluetoothLayout.blueCheck02.image.isSelected =
features.leExtendedAdvertising
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
}
}
}
}
// 请求蓝牙权限的方法

View File

@ -9,15 +9,20 @@ import android.view.View
import android.view.ViewGroup
import android.widget.Toast
import androidx.annotation.RequiresPermission
import androidx.fragment.app.activityViewModels
import androidx.lifecycle.lifecycleScope
import com.xyzshell.andinfo.AndInfo
import com.xyzshell.andinfo.libs.NetworkInfo
import com.xyzshell.myphoneinfo.R
import com.xyzshell.myphoneinfo.base.BaseActivity
import com.xyzshell.myphoneinfo.custom.PermissionChecker
import com.xyzshell.myphoneinfo.custom.RefreshManager.load
import com.xyzshell.myphoneinfo.custom.RefreshManager.stop
import com.xyzshell.myphoneinfo.custom.SetNumberOrWordUtils.getHoursString
import com.xyzshell.myphoneinfo.custom.SetNumberOrWordUtils.setYesOrNo
import com.xyzshell.myphoneinfo.databinding.FragmentNetworkBinding
import com.xyzshell.myphoneinfo.dialog.ShowLoadFragment
import com.xyzshell.myphoneinfo.main.MainViewModel
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
@ -26,13 +31,20 @@ import kotlin.toString
class NetworkFragment : Fragment() {
private var status=0//0:未连接1wifi 2:数据流量
private lateinit var networkInfo: NetworkInfo
private lateinit var binding:FragmentNetworkBinding
private var status = 0//0:未连接1wifi 2:数据流量
private val mainViewModel: MainViewModel by activityViewModels()
private var refreshStatus =false//刷新状态
private lateinit var binding: FragmentNetworkBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
}
override fun onResume(){
super.onResume()
checkPermissionsAndRefresh()
mainViewModel.refreshNetworkInfo(requireContext())
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
@ -40,209 +52,257 @@ private lateinit var binding:FragmentNetworkBinding
): View {
// Inflate the layout for this fragment
binding = FragmentNetworkBinding.inflate(inflater, container, false)
// binding.signalStrength.setStrength(4)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
networkInfo= AndInfo.instance.network
viewLifecycleOwner.lifecycleScope.launch {
delay(50) // 短暂延迟确保UI完成布局
withContext(Dispatchers.Main) {
refreshStatus()
if(PermissionChecker.getMissingPermissions(context = requireContext()).isNotEmpty()){
PermissionChecker.requestSimple(
requireActivity(),
onGranted = {
// 权限已授予,执行操作
refreshStatus()
},
onDenied = {
// 权限被拒绝
Toast.makeText(context, getString(R.string.permissions_required), Toast.LENGTH_SHORT).show()
}
)
}
//wifi
binding.networkLayout.wfText1.textTitle.text=getString(R.string.status)
// binding.networkLayout.wfText2.textTitle.text=getString(R.string.network)
binding.networkLayout.wfText3.textTitle.text=getString(R.string.bssid)
binding.networkLayout.wfText4.textTitle.text=getString(R.string.function)
binding.networkLayout.wfText5.textTitle.text=getString(R.string.connection_speed)
binding.networkLayout.wfText6.textTitle.text=getString(R.string.signal_strength)
binding.networkLayout.wfText7.textTitle.text=getString(R.string.frequency)
binding.networkLayout.wfText8.textTitle.text=getString(R.string.channel)
binding.networkLayout.wfText10.textTitle.text=getString(R.string.standard)
initView()
setObservers()
//dhcp
binding.networkLayout.dhText1.textTitle.text=getString(R.string.dhcp_server)
binding.networkLayout.dhText2.textTitle.text=getString(R.string.dhcp_lease_time)
binding.networkLayout.dhText3.textTitle.text=getString(R.string.gateway)
binding.networkLayout.dhText4.textTitle.text=getString(R.string.subnet_mask)
binding.networkLayout.dhText5.textTitle.text=getString(R.string.dns1)
binding.networkLayout.dhText6.textTitle.text=getString(R.string.dns2)
binding.networkLayout.dhText7.textTitle.text=getString(R.string.ip_address)
binding.networkLayout.dhText8.textTitle.text=getString(R.string.ipv6)
//Hardware
binding.networkLayout.hardCheck1.content.text=getString(R.string.standard_all)
binding.networkLayout.hardCheck2.content.text=getString(R.string.wifi_direct_support)
binding.networkLayout.hardCheck3.content.text=getString(R.string.wifi_aware_support)
binding.networkLayout.hardCheck4.content.text=getString(R.string.wifi_passpoint_support)
binding.networkLayout.hardCheck5.content.text=getString(R.string.ghz_band_support)
binding.networkLayout.hardCheck6.content.text=getString(R.string.ghz6_band_support)
//mobel
binding.mdText1.textTitle.text=getString(R.string.dual_sim_dual_standby)
binding.mdText2.textTitle.text=getString(R.string.phone_type)
binding.mdText3.textTitle.text=getString(R.string.esim)
binding.conTexts.textTitle.text=getString(R.string.status)
binding.defText1.textTitle.text=getString(R.string.data)
binding.defText2.textTitle.text=getString(R.string.voice)
binding.defText3.textTitle.text=getString(R.string.short_message)
binding.networkLayout.pubShow.setOnClickListener{
val tag = "showLoadFragment"
if (requireActivity().supportFragmentManager.findFragmentByTag(tag) == null) {
val showLoadFragment = ShowLoadFragment()
showLoadFragment.show(requireActivity().supportFragmentManager, tag)
}
}
}}
}
private fun refreshStatus() {
val currentNetworkType = networkInfo.getCurrentNetworkType()
Log.d("NetworkFragment", "currentNetworkType: $currentNetworkType")
when (currentNetworkType) {
private fun setObservers() {
mainViewModel.networkStatus.observe(viewLifecycleOwner) { type ->
stop(binding.swipeRefresh)
when (type) {
NetworkInfo.NetworkType.WIFI -> {
status = 1
setWifiDetail()
setMobileInfo()
binding.conText1.text = getString(R.string.wifi)
binding.connectData.visibility = View.VISIBLE
binding.noConnect.visibility = View.GONE
}
NetworkInfo.NetworkType.MOBILE -> {
status = 2
setWifiDetail()
setMobileInfo()
binding.conText1.text = getString(R.string.mobile)
binding.connectData.visibility = View.VISIBLE
binding.noConnect.visibility = View.GONE
}
else -> {
status = 0
setNoConnect()
}
}
}
// 观察 WiFi 详情
mainViewModel.wifiDetails.observe(viewLifecycleOwner) { wifiDetails ->
wifiDetails?.let {
setWifiDetail(it)
} ?: run {
binding.networkLayout.wifiList.visibility = View.GONE
}
}
// 观察移动网络详情
mainViewModel.mobileDetails.observe(viewLifecycleOwner) { mobileDetails ->
mobileDetails?.let {
setMobileInfo(it)
}
}
// 观察权限状态
mainViewModel.networkPermissionGranted.observe(viewLifecycleOwner) { granted ->
if (!granted) {
requestPermissions()
}
}
}
private fun checkPermissionsAndRefresh() {
mainViewModel.checkNetworkPermissions(requireContext())
}
private fun requestPermissions() {
PermissionChecker.requestSimple(
requireActivity(),
onGranted = {
mainViewModel.refreshNetworkInfo(requireContext())
},
onDenied = {
Toast.makeText(
context,
getString(R.string.permissions_required),
Toast.LENGTH_SHORT
).show()
setNoConnect()
}
)
}
private fun initView() {
binding.networkLayout.wfText1.textTitle.text = getString(R.string.status)
// binding.networkLayout.wfText2.textTitle.text=getString(R.string.network)
binding.networkLayout.wfText3.textTitle.text = getString(R.string.bssid)
binding.networkLayout.wfText4.textTitle.text = getString(R.string.function)
binding.networkLayout.wfText5.textTitle.text = getString(R.string.connection_speed)
binding.networkLayout.wfText6.textTitle.text = getString(R.string.signal_strength)
binding.networkLayout.wfText7.textTitle.text = getString(R.string.frequency)
binding.networkLayout.wfText8.textTitle.text = getString(R.string.channel)
binding.networkLayout.wfText10.textTitle.text = getString(R.string.standard)
//dhcp
binding.networkLayout.dhText1.textTitle.text = getString(R.string.dhcp_server)
binding.networkLayout.dhText2.textTitle.text = getString(R.string.dhcp_lease_time)
binding.networkLayout.dhText3.textTitle.text = getString(R.string.gateway)
binding.networkLayout.dhText4.textTitle.text = getString(R.string.subnet_mask)
binding.networkLayout.dhText5.textTitle.text = getString(R.string.dns1)
binding.networkLayout.dhText6.textTitle.text = getString(R.string.dns2)
binding.networkLayout.dhText7.textTitle.text = getString(R.string.ip_address)
binding.networkLayout.dhText8.textTitle.text = getString(R.string.ipv6)
//Hardware
binding.networkLayout.hardCheck1.content.text = getString(R.string.standard_all)
binding.networkLayout.hardCheck2.content.text = getString(R.string.wifi_direct_support)
binding.networkLayout.hardCheck3.content.text = getString(R.string.wifi_aware_support)
binding.networkLayout.hardCheck4.content.text = getString(R.string.wifi_passpoint_support)
binding.networkLayout.hardCheck5.content.text = getString(R.string.ghz_band_support)
binding.networkLayout.hardCheck6.content.text = getString(R.string.ghz6_band_support)
//mobel
binding.mdText1.textTitle.text = getString(R.string.dual_sim_dual_standby)
binding.mdText2.textTitle.text = getString(R.string.phone_type)
binding.mdText3.textTitle.text = getString(R.string.esim)
binding.conTexts.textTitle.text = getString(R.string.status)
binding.defText1.textTitle.text = getString(R.string.data)
binding.defText2.textTitle.text = getString(R.string.voice)
binding.defText3.textTitle.text = getString(R.string.short_message)
binding.networkLayout.pubShow.setOnClickListener {
val tag = "showLoadFragment"
if (requireActivity().supportFragmentManager.findFragmentByTag(tag) == null) {
val showLoadFragment = ShowLoadFragment()
showLoadFragment.show(requireActivity().supportFragmentManager, tag)
}
}
binding.swipeRefresh.setOnRefreshListener {
load(binding.swipeRefresh){
Log.d("TTTTTTTT","刷新超时")
}
mainViewModel.refreshNetworkInfo(requireContext())
}
}
private fun setNoConnect() {
binding.noConnect.visibility=View.VISIBLE
binding.connectData.visibility=View.GONE
binding.networkLayout.wfText1.textContent.text="Disconnected"
binding.networkLayout.wifiList.visibility=View.GONE
binding.networkLayout.llPubShow.visibility=View.GONE
binding.noConnect.visibility = View.VISIBLE
binding.connectData.visibility = View.GONE
binding.networkLayout.wfText1.textContent.text = getString(R.string.not_connected)
binding.networkLayout.wifiList.visibility = View.GONE
binding.networkLayout.llPubShow.visibility = View.GONE
}
/**
* 设置流量信息
* */
private fun setMobileInfo() {
binding.noConnect.visibility=View.GONE
binding.conText1.text=getString(R.string.mobile)
binding.connectData.visibility=View.VISIBLE
private fun setMobileInfo(mobileDetails: NetworkInfo.MobileDetails) {
binding.noConnect.visibility = View.GONE
binding.conText1.text = getString(R.string.mobile)
binding.connectData.visibility = View.VISIBLE
Log.d("TTTTTTTT", "wifi not connected")
binding.networkLayout.wifiList.visibility=View.GONE
binding.networkLayout.llPubShow.visibility=View.GONE
networkInfo.getMobileDetails()?.let { mobileDetails ->
binding.mdText1.textContent.text=setYesOrNo(mobileDetails.dualSim)
binding.mdText2.textContent.text="SIM${mobileDetails.phoneType}"
binding.mdText3.textContent.text=setYesOrNo(mobileDetails.esimSupported)
binding.conTexts.textContent.text=if(mobileDetails.dataEnabled) getString(R.string.connected) else getString(R.string.not_connected)
binding.defText1.textContent.text=mobileDetails.dataSim.toString()
binding.defText2.textContent.text=mobileDetails.voiceSim.toString()
binding.defText3.textContent.text=mobileDetails.smsSim.toString()
if(mobileDetails.simInfos.isNotEmpty()){
binding.simInfo1.root.visibility=View.VISIBLE
binding.simInfo1.simText1.textTitle.text=getString(R.string.status)
binding.simInfo1.simText2.textTitle.text=getString(R.string.nation)
binding.simInfo1.simText3.textTitle.text=getString(R.string.roaming)
binding.simInfo1.simText4.textTitle.text=getString(R.string.network_type)
binding.simInfo1.simText1.textContent.text=if(mobileDetails.dataEnabled) getString(R.string.ready) else getString(R.string.not_ready)
binding.simInfo1.simText2.textContent.text=mobileDetails.simInfos[0].countryIso.toString()
binding.simInfo1.simText3.textContent.text=if(mobileDetails.simInfos[0].roaming) getString(R.string.enable) else getString(R.string.disable)
binding.simInfo1.simText4.textContent.text=mobileDetails.simInfos[0].networkTypeText
if(mobileDetails.simInfos.size>1){
binding.simInfo2.root.visibility=View.VISIBLE
binding.simInfo2.simTitle.text=getString(R.string.sim_card_2)
binding.simInfo2.simText1.textTitle.text=getString(R.string.status)
binding.simInfo2.simText2.textTitle.text=getString(R.string.nation)
binding.simInfo2.simText3.textTitle.text=getString(R.string.roaming)
binding.simInfo2.simText4.textTitle.text=getString(R.string.network_type)
binding.simInfo2.simText1.textContent.text=if(mobileDetails.dataEnabled) getString(R.string.ready) else getString(R.string.not_ready)
binding.simInfo2.simText2.textContent.text=mobileDetails.simInfos[1].countryIso.toString()
binding.simInfo2.simText3.textContent.text=if(mobileDetails.simInfos[1].roaming) getString(R.string.enable) else getString(R.string.disable)
binding.simInfo2.simText4.textContent.text=mobileDetails.simInfos[1].networkTypeText
binding.networkLayout.wifiList.visibility = View.GONE
binding.networkLayout.llPubShow.visibility = View.GONE
binding.mdText1.textContent.text = setYesOrNo(mobileDetails.dualSim)
binding.mdText2.textContent.text = "SIM${mobileDetails.phoneType}"
binding.mdText3.textContent.text = setYesOrNo(mobileDetails.esimSupported)
binding.conTexts.textContent.text =
if (mobileDetails.dataEnabled) getString(R.string.connected) else getString(R.string.not_connected)
binding.defText1.textContent.text = mobileDetails.dataSim.toString()
binding.defText2.textContent.text = mobileDetails.voiceSim.toString()
binding.defText3.textContent.text = mobileDetails.smsSim.toString()
if (mobileDetails.simInfos.isNotEmpty()) {
binding.simInfo1.root.visibility = View.VISIBLE
binding.simInfo1.simText1.textTitle.text = getString(R.string.status)
binding.simInfo1.simText2.textTitle.text = getString(R.string.nation)
binding.simInfo1.simText3.textTitle.text = getString(R.string.roaming)
binding.simInfo1.simText4.textTitle.text = getString(R.string.network_type)
binding.simInfo1.simText1.textContent.text =
if (mobileDetails.dataEnabled) getString(R.string.ready) else getString(R.string.not_ready)
binding.simInfo1.simText2.textContent.text =
mobileDetails.simInfos[0].countryIso.toString()
binding.simInfo1.simText3.textContent.text =
if (mobileDetails.simInfos[0].roaming) getString(R.string.enable) else getString(R.string.disable)
binding.simInfo1.simText4.textContent.text = mobileDetails.simInfos[0].networkTypeText
if (mobileDetails.simInfos.size > 1) {
binding.simInfo2.root.visibility = View.VISIBLE
binding.simInfo2.simTitle.text = getString(R.string.sim_card_2)
binding.simInfo2.simText1.textTitle.text = getString(R.string.status)
binding.simInfo2.simText2.textTitle.text = getString(R.string.nation)
binding.simInfo2.simText3.textTitle.text = getString(R.string.roaming)
binding.simInfo2.simText4.textTitle.text = getString(R.string.network_type)
binding.simInfo2.simText1.textContent.text =
if (mobileDetails.dataEnabled) getString(R.string.ready) else getString(R.string.not_ready)
binding.simInfo2.simText2.textContent.text =
mobileDetails.simInfos[1].countryIso.toString()
binding.simInfo2.simText3.textContent.text =
if (mobileDetails.simInfos[1].roaming) getString(R.string.enable) else getString(
R.string.disable
)
binding.simInfo2.simText4.textContent.text =
mobileDetails.simInfos[1].networkTypeText
}
}else{
binding.simInfo1.root.visibility=View.GONE
binding.simInfo2.root.visibility=View.GONE
} else {
binding.simInfo1.root.visibility = View.GONE
binding.simInfo2.root.visibility = View.GONE
}
}
}
/**
* 设置wifi信息
* */
private fun setWifiDetail() {
binding.noConnect.visibility=View.GONE
binding.conText1.text=getString(R.string.wifi)
binding.connectData.visibility=View.VISIBLE
binding.networkLayout.wifiList.visibility=View.VISIBLE
binding.networkLayout.llPubShow.visibility=View.VISIBLE
networkInfo.getWifiDetails()?.let { wifiDetails ->
private fun setWifiDetail(wifiDetails: NetworkInfo.WifiDetails) {
binding.noConnect.visibility = View.GONE
binding.conText1.text = getString(R.string.wifi)
binding.connectData.visibility = View.VISIBLE
binding.networkLayout.wifiList.visibility = View.VISIBLE
binding.networkLayout.llPubShow.visibility = View.VISIBLE
Log.d("TTTTTTTT", wifiDetails.connected.toString())
binding.networkLayout.wifiList.visibility=View.VISIBLE
binding.networkLayout.wfText1.textContent.text=getString(R.string.connected)
wifiDetails.signalLevelPercent?.let { percent->
binding.networkLayout.wifiList.visibility = View.VISIBLE
binding.networkLayout.wfText1.textContent.text = getString(R.string.connected)
wifiDetails.signalLevelPercent?.let { percent ->
binding.strengthView.setStrength(calculateSignalLevel(percent = percent))
}
binding.conText2.text="${wifiDetails.ssid}·${wifiDetails.linkSpeedMbps} Mbps"
binding.conTExt3.text="${wifiDetails.signalLevelPercent}% ${wifiDetails.rssi} dBm"
binding.conText2.text = "${wifiDetails.ssid}·${wifiDetails.linkSpeedMbps} Mbps"
binding.conTExt3.text = "${wifiDetails.signalLevelPercent}% ${wifiDetails.rssi} dBm"
binding.networkLayout.wfText3.textContent.text=wifiDetails.bssid
binding.networkLayout.wfText4.textContent.text=wifiDetails.capabilities
binding.networkLayout.wfText5.textContent.text="${wifiDetails.linkSpeedMbps} Mbps"
binding.networkLayout.wfText6.textContent.text="${wifiDetails.rssi} dBm"
binding.networkLayout.wfText7.textContent.text="${wifiDetails.frequency} MHz"
binding.networkLayout.wfText8.textContent.text=wifiDetails.channel.toString()
binding.networkLayout.wfText10.textContent.text=wifiDetails.standard.toString()
binding.networkLayout.wfText3.textContent.text = wifiDetails.bssid
binding.networkLayout.wfText4.textContent.text = wifiDetails.capabilities
binding.networkLayout.wfText5.textContent.text = "${wifiDetails.linkSpeedMbps} Mbps"
binding.networkLayout.wfText6.textContent.text = "${wifiDetails.rssi} dBm"
binding.networkLayout.wfText7.textContent.text = "${wifiDetails.frequency} MHz"
binding.networkLayout.wfText8.textContent.text = wifiDetails.channel.toString()
binding.networkLayout.wfText10.textContent.text = wifiDetails.standard.toString()
binding.networkLayout.dhText1.textContent.text=wifiDetails.dhcpServer
binding.networkLayout.dhText2.textContent.text=getHoursString(wifiDetails.leaseDuration?: 0)
binding.networkLayout.dhText3.textContent.text=wifiDetails.gateway
binding.networkLayout.dhText4.textContent.text=wifiDetails.subnetMask
binding.networkLayout.dhText5.textContent.text=wifiDetails.dns1
binding.networkLayout.dhText6.textContent.text=wifiDetails.dns2
binding.networkLayout.dhText7.textContent.text=wifiDetails.ip
binding.networkLayout.dhText1.textContent.text = wifiDetails.dhcpServer
binding.networkLayout.dhText2.textContent.text =
getHoursString(wifiDetails.leaseDuration ?: 0)
binding.networkLayout.dhText3.textContent.text = wifiDetails.gateway
binding.networkLayout.dhText4.textContent.text = wifiDetails.subnetMask
binding.networkLayout.dhText5.textContent.text = wifiDetails.dns1
binding.networkLayout.dhText6.textContent.text = wifiDetails.dns2
binding.networkLayout.dhText7.textContent.text = wifiDetails.ip
val toString = wifiDetails.ipv6.toString()
val ipv6 = toString.replace("[", "").replace("]", "")
binding.networkLayout.dhText8.textContent.text=ipv6
binding.networkLayout.dhText8.textContent.text = ipv6
binding.networkLayout.hardCheck1.image.isSelected= wifiDetails.supportedStandards?.size==5
binding.networkLayout.hardCheck2.image.isSelected= wifiDetails.wifiDirect == true
binding.networkLayout.hardCheck3.image.isSelected= wifiDetails.wifiAware == true
binding.networkLayout.hardCheck4.image.isSelected= wifiDetails.wifiPasspoint == true
binding.networkLayout.hardCheck5.image.isSelected= wifiDetails.support5G == true
binding.networkLayout.hardCheck6.image.isSelected= wifiDetails.support6G == true
binding.networkLayout.hardCheck1.image.isSelected =
wifiDetails.supportedStandards?.size == 5
binding.networkLayout.hardCheck2.image.isSelected = wifiDetails.wifiDirect == true
binding.networkLayout.hardCheck3.image.isSelected = wifiDetails.wifiAware == true
binding.networkLayout.hardCheck4.image.isSelected = wifiDetails.wifiPasspoint == true
binding.networkLayout.hardCheck5.image.isSelected = wifiDetails.support5G == true
binding.networkLayout.hardCheck6.image.isSelected = wifiDetails.support6G == true
}
}
private fun calculateSignalLevel( percent: Int) : Int {
private fun calculateSignalLevel(percent: Int): Int {
return when {
percent >= 80 -> 5 // 优秀
percent >= 60 -> 4 // 良好
@ -251,6 +311,7 @@ private lateinit var binding:FragmentNetworkBinding
else -> 1 // 非常差/无信号
}
}
companion object {
@JvmStatic

View File

@ -2,6 +2,7 @@ package com.xyzshell.myphoneinfo.main
import android.util.Log
import android.view.LayoutInflater
import androidx.fragment.app.activityViewModels
import androidx.viewpager2.widget.ViewPager2
import com.google.android.material.tabs.TabLayoutMediator
import com.xyzshell.andinfo.AndInfo
@ -18,6 +19,7 @@ import com.xyzshell.myphoneinfo.dashboard.SensorsFragment
import com.xyzshell.myphoneinfo.dashboard.SystemShowFragment
import com.xyzshell.myphoneinfo.databinding.ActivityMainBinding
import com.xyzshell.myphoneinfo.dialog.SlideInPopView
import kotlin.getValue
class MainScrollActivity : BaseActivity<ActivityMainBinding>() {
override fun inflateBinding(inflater: LayoutInflater): ActivityMainBinding =
@ -48,12 +50,6 @@ class MainScrollActivity : BaseActivity<ActivityMainBinding>() {
})
}) { tab, position ->
tab.text = stringArray[position]
// tab.setCustomView(R.layout.custom_ta
//
// tab.customView?.run {
// val indicator: ImageView = findViewById(R.id.image_indicator)
// indicator.isVisible = position == 0
// }
}.attach()
}

View File

@ -0,0 +1,182 @@
package com.xyzshell.myphoneinfo.main
import android.content.Context
import android.content.pm.PackageManager
import androidx.core.app.ActivityCompat
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.xyzshell.andinfo.AndInfo
import com.xyzshell.andinfo.libs.AppDetails
import com.xyzshell.andinfo.libs.BluetoothInfo
import com.xyzshell.andinfo.libs.CpuInfo
import com.xyzshell.andinfo.libs.DisplayInfo
import com.xyzshell.andinfo.libs.GpuInfo
import com.xyzshell.andinfo.libs.InputInfo
import com.xyzshell.andinfo.libs.MemInfo
import com.xyzshell.andinfo.libs.MemoryInfo
import com.xyzshell.andinfo.libs.NetworkInfo
import com.xyzshell.andinfo.libs.StorageInfo
import com.xyzshell.myphoneinfo.custom.PermissionChecker
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
class MainViewModel: ViewModel() {
// 硬件相关数据
private val _cpuInfo = MutableLiveData<CpuInfo?>()
val cpuInfo: LiveData<CpuInfo?> = _cpuInfo
private val _gpuInfo = MutableLiveData<GpuInfo?>()
val gpuInfo: LiveData<GpuInfo?> = _gpuInfo
private val _displayInfo = MutableLiveData<DisplayInfo?>()
val displayInfo: LiveData<DisplayInfo?> = _displayInfo
private val _storageInfo = MutableLiveData<StorageInfo?>()
val storageInfo: LiveData<StorageInfo?> = _storageInfo
private val _memInfo = MutableLiveData<MemInfo?>()
val memInfo: LiveData<MemInfo?> = _memInfo
private val _bluetoothInfo = MutableLiveData<BluetoothInfo?>()
val bluetoothInfo: LiveData<BluetoothInfo?> = _bluetoothInfo
private val _inputInfo= MutableLiveData<InputInfo?>()
val input: LiveData<InputInfo?> = _inputInfo
//网络相关
private val networkInfo= AndInfo.instance.network
private val _networkStatus= MutableLiveData<NetworkInfo.NetworkType?>()
val networkStatus: LiveData<NetworkInfo.NetworkType?> = _networkStatus
private val _wifiDetails = MutableLiveData<NetworkInfo.WifiDetails?>()
val wifiDetails: LiveData<NetworkInfo.WifiDetails?> = _wifiDetails
private val _mobileDetails = MutableLiveData<NetworkInfo.MobileDetails?>()
val mobileDetails: LiveData<NetworkInfo.MobileDetails?> = _mobileDetails
private val _networkPermissionGranted = MutableLiveData<Boolean>()
val networkPermissionGranted: LiveData<Boolean> = _networkPermissionGranted
//应用列表
private val _installedApps= MutableLiveData<List<AppDetails>>()
val installedApps: LiveData<List<AppDetails>> = _installedApps
init{
loadApps()
}
fun refreshApps() {
loadApps()
}
/**
* 刷新网络信息带权限检查
*/
fun refreshNetworkInfo(context: Context){
viewModelScope.launch(Dispatchers.IO){
// 检查权限
val hasPermissions = PermissionChecker.getMissingPermissions(context).isEmpty()
_networkPermissionGranted.postValue(hasPermissions)
if (!hasPermissions) {
return@launch
}
try {
// 获取网络类型
val type = networkInfo.getCurrentNetworkType()
_networkStatus.postValue(type)
// 根据网络类型获取详细信息
when (type) {
NetworkInfo.NetworkType.WIFI -> {
_wifiDetails.postValue(networkInfo.getWifiDetails())
_mobileDetails.postValue(networkInfo.getMobileDetails())
}
NetworkInfo.NetworkType.MOBILE -> {
_mobileDetails.postValue(networkInfo.getMobileDetails())
_wifiDetails.postValue(networkInfo.getWifiDetails())
}
else -> {
_wifiDetails.postValue(null)
_mobileDetails.postValue(null)
}
}
} catch (e: Exception) {
e.printStackTrace()
// 处理异常
}
}
}
/**
* 仅检查网络权限
*/
fun checkNetworkPermissions(context: Context) {
viewModelScope.launch(Dispatchers.IO) {
val hasPermissions = PermissionChecker.getMissingPermissions(context).isEmpty()
_networkPermissionGranted.postValue(hasPermissions)
}
}
/**
* 获取当前网络状态快速检查
*/
fun getCurrentNetworkType(context: Context): NetworkInfo.NetworkType? {
return try {
if (PermissionChecker.getMissingPermissions(context).isEmpty()) {
networkInfo.getCurrentNetworkType()
} else {
null
}
} catch (e: Exception) {
null
}
}
fun loadApps() {
viewModelScope.launch(Dispatchers.IO){
val apps= AndInfo.instance.app.getInstalledApps()
_installedApps.postValue(apps)
}
}
/**
* 硬件刷新方法
* */
fun refreshHardwareInfo(context: Context){
viewModelScope.launch(Dispatchers.IO){
try {
val cpu= AndInfo.instance.cpu
_cpuInfo.postValue(cpu)
val gpu= AndInfo.instance.gpu
_gpuInfo.postValue(gpu)
val display = AndInfo.instance.display
_displayInfo.postValue(display)
val storage = AndInfo.instance.storage
_storageInfo.postValue(storage)
val mem = AndInfo.instance.memory
_memInfo.postValue(mem)
// 检查权限
val bluetoothInfo = AndInfo.instance.bluetooth
val inputInfo= AndInfo.instance.input
_inputInfo.postValue(inputInfo)
val hasPermissions= bluetoothInfo.requiredPermissions.all { permission ->
ActivityCompat.checkSelfPermission(context, permission) ==
PackageManager.PERMISSION_GRANTED
}
if (!hasPermissions) {
return@launch
}
_bluetoothInfo.postValue(bluetoothInfo)
//蓝牙信息
}catch (e: Exception){
e.printStackTrace()
}
}
}
}

View File

@ -40,11 +40,12 @@
android:id="@+id/dialog_title"
style="@style/TextHeavy20"
android:textSize="18sp"
android:layout_width="wrap_content"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="80dp"
android:text="@string/screenreader"
app:layout_constraintLeft_toRightOf="@id/image_icon"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="@id/image_icon"
app:layout_constraintBottom_toBottomOf="@id/image_icon"/>
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -1,6 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/swipeRefresh"
android:layout_width="match_parent"
android:layout_height="wrap_content"
xmlns:app="http://schemas.android.com/apk/res-auto"

View File

@ -2,6 +2,7 @@
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:id="@+id/swipeRefresh"
android:layout_height="wrap_content"
android:background="@color/background_color"
tools:context=".dashboard.NetworkFragment">

View File

@ -51,7 +51,11 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
layout="@layout/mem_text_style" />
<include
android:id="@+id/memText0"
android:layout_width="match_parent"
android:layout_height="wrap_content"
layout="@layout/mem_text_style" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"