From 596daf5fcc7d6cd53a613a99deedabe3b95363fd Mon Sep 17 00:00:00 2001 From: yuqian Date: Fri, 16 Jan 2026 11:36:43 +0800 Subject: [PATCH] =?UTF-8?q?=E6=96=B0=E5=A2=9Eviewmodel=EF=BC=8C=E4=BF=AE?= =?UTF-8?q?=E6=94=B9=E5=AE=8C=E5=96=84hardware?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../xyzshell/myphoneinfo/base/BaseActivity.kt | 10 +- .../myphoneinfo/custom/RefreshManager.kt | 76 ++++ .../myphoneinfo/dashboard/AppsFragment.kt | 28 +- .../myphoneinfo/dashboard/HardWareFragment.kt | 388 +++++++++------- .../myphoneinfo/dashboard/NetworkFragment.kt | 413 ++++++++++-------- .../myphoneinfo/main/MainScrollActivity.kt | 8 +- .../myphoneinfo/main/MainViewModel.kt | 182 ++++++++ .../src/main/res/layout/dialog_app_click.xml | 3 +- .../src/main/res/layout/fragment_apps.xml | 1 + .../src/main/res/layout/fragment_network.xml | 1 + .../src/main/res/layout/hard_memory.xml | 6 +- 11 files changed, 763 insertions(+), 353 deletions(-) create mode 100644 myphoneinfo/src/main/java/com/xyzshell/myphoneinfo/custom/RefreshManager.kt create mode 100644 myphoneinfo/src/main/java/com/xyzshell/myphoneinfo/main/MainViewModel.kt diff --git a/myphoneinfo/src/main/java/com/xyzshell/myphoneinfo/base/BaseActivity.kt b/myphoneinfo/src/main/java/com/xyzshell/myphoneinfo/base/BaseActivity.kt index e817d7a..d40d89f 100644 --- a/myphoneinfo/src/main/java/com/xyzshell/myphoneinfo/base/BaseActivity.kt +++ b/myphoneinfo/src/main/java/com/xyzshell/myphoneinfo/base/BaseActivity.kt @@ -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 : 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 : AppCompatActivity() { protected abstract fun inflateBinding(inflater: LayoutInflater): VB - protected open fun initView() {} + protected open fun initView() { + mainViewModel + } protected open fun initData() {} } \ No newline at end of file diff --git a/myphoneinfo/src/main/java/com/xyzshell/myphoneinfo/custom/RefreshManager.kt b/myphoneinfo/src/main/java/com/xyzshell/myphoneinfo/custom/RefreshManager.kt new file mode 100644 index 0000000..7fc9815 --- /dev/null +++ b/myphoneinfo/src/main/java/com/xyzshell/myphoneinfo/custom/RefreshManager.kt @@ -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() + } +} \ No newline at end of file diff --git a/myphoneinfo/src/main/java/com/xyzshell/myphoneinfo/dashboard/AppsFragment.kt b/myphoneinfo/src/main/java/com/xyzshell/myphoneinfo/dashboard/AppsFragment.kt index 2e4df6e..f411487 100644 --- a/myphoneinfo/src/main/java/com/xyzshell/myphoneinfo/dashboard/AppsFragment.kt +++ b/myphoneinfo/src/main/java/com/xyzshell/myphoneinfo/dashboard/AppsFragment.kt @@ -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) - adapter.setOnclickListener(this@AppsFragment) - }} + 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 diff --git a/myphoneinfo/src/main/java/com/xyzshell/myphoneinfo/dashboard/HardWareFragment.kt b/myphoneinfo/src/main/java/com/xyzshell/myphoneinfo/dashboard/HardWareFragment.kt index 3244bc2..731b212 100644 --- a/myphoneinfo/src/main/java/com/xyzshell/myphoneinfo/dashboard/HardWareFragment.kt +++ b/myphoneinfo/src/main/java/com/xyzshell/myphoneinfo/dashboard/HardWareFragment.kt @@ -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,233 +147,293 @@ 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) + 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 = 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 - //内存:todo - 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() + binding.memoryLayout.memText3.textTitle.text = getString(R.string.zram) + binding.memoryLayout.memText3.textContent.text = zramTotal + binding.memoryLayout.ram3.text = "$zramUsed used" + binding.memoryLayout.ram4.text = "$zramFree free" + binding.memoryLayout.seekbar2.progress = + (zramTotal.substringBefore(" ").toDouble() / zramTotal.substringBefore(" ") + .toDouble() * 100).toInt() + } + } - //appsAndData - val appsAndData = storageInfo.getFormattedAppsAndDataSize() - val system = storageInfo.getFormattedSystemSize() - val other= storageInfo.getFormattedFreeSpace()+storageInfo.getFormattedCacheSize() - //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.seekbar2.progress = - (outused.substringBefore(" ").toDouble() / outtotal.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.radius1.text = appsAndData - binding.memoryLayout.radius2.text = system - binding.memoryLayout.radius3.text = other - binding.memoryLayout.seekbar3.progress = - (outused.substringBefore(" ").toDouble() / outtotal.substringBefore(" ") - .toDouble() * 100).toInt() + binding.memoryLayout.storText1.textTitle.text = getString(R.string.size) + 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 = + (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.internalStorageFileSystemType.toString() + binding.memoryLayout.interText2.textTitle.text = getString(R.string.block_size) + binding.memoryLayout.interText2.textContent.text = storageInfo.formatBytes(interTotal) + binding.memoryLayout.interText3.textTitle.text = "/data" + 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}") + + } + } + } - //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 = 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() } /** * 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) - 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.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" + mainViewModel.displayInfo.observe(viewLifecycleOwner) { display -> + display?.let { + 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.disText2.textContent.text = defaultDisplayInfo?.ppi.toString() + val dpiStr = + defaultDisplayInfo?.densityDpi.toString() + "(xxhdpi)\n" + defaultDisplayInfo?.xdpi?.roundToInt() + "dpx" + defaultDisplayInfo?.ydpi?.roundToInt() + "dp" + binding.disText3.textContent.text = dpiStr + val screenSize = "${ + String.format( + "%.2f", + display.getScreenSize()?.diagonalInches + ) + }/ ${String.format("%.1f", display.getScreenSize()?.diagonalMm)} mm" + binding.disText4.textContent.text = screenSize + binding.disText5.textContent.text = convertToApproximateAspectRatio( + width = width.toInt(), + height = height.toInt() + ) + binding.disText6.textContent.text = + defaultDisplayInfo?.refreshRate.toString() + "HZ" + binding.disText7.textContent.text = defaultDisplayInfo?.isWideColorGamut.toString() + 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.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.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()) + 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.textContent.text = "${gpu.getMaxFrequency()} MHz" + binding.gpuText4.textContent.text = gpu.getArchitecture() + binding.gpuText5.textContent.text = gpu.getOpenGLExtensionCount().toString() + binding.gpuText6.textContent.text = "${gpu.getCacheSize()} KB" + binding.gpuText7.textContent.text = "${gpu.getBandwidth()} GB/s" + binding.gpuText8.textContent.text = gpu.isVulkanSupported().toString() + binding.gpuText9.textContent.text = gpu.getVulkanApiVersion() + gpu.getGpuInformation().eglInformation?.let { eglInfo -> + eglInfo.eglExtensions?.let { + dialogExtension?.setContent(it.joinToString()) + } + } + binding.openItem3.text = gpu.getOpenGLVersion() + + } } -// 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() + 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() } - //蓝牙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 - } + mainViewModel.bluetoothInfo.observe(viewLifecycleOwner) { features -> + features?.let { + if (!it.isEnabled) return@let + //蓝牙features支持 + it.getBluetoothFeatures().let { features -> + binding.bluetoothLayout.blueCheck1.image.isSelected = features.bluetoothLe + + 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.image.isSelected = features.le2MbPhy + + binding.bluetoothLayout.blueCheck05.image.isSelected = features.leAudioSupport + } + } + } } // 请求蓝牙权限的方法 diff --git a/myphoneinfo/src/main/java/com/xyzshell/myphoneinfo/dashboard/NetworkFragment.kt b/myphoneinfo/src/main/java/com/xyzshell/myphoneinfo/dashboard/NetworkFragment.kt index 97fe63a..61e2154 100644 --- a/myphoneinfo/src/main/java/com/xyzshell/myphoneinfo/dashboard/NetworkFragment.kt +++ b/myphoneinfo/src/main/java/com/xyzshell/myphoneinfo/dashboard/NetworkFragment.kt @@ -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:未连接,1:wifi 2:数据流量 - private lateinit var networkInfo: NetworkInfo -private lateinit var binding:FragmentNetworkBinding + private var status = 0//0:未连接,1:wifi 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,210 +52,258 @@ 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) { - NetworkInfo.NetworkType.WIFI -> { - status = 1 - setWifiDetail() - setMobileInfo() + private fun setObservers() { + mainViewModel.networkStatus.observe(viewLifecycleOwner) { type -> + stop(binding.swipeRefresh) + when (type) { + NetworkInfo.NetworkType.WIFI -> { + status = 1 + binding.conText1.text = getString(R.string.wifi) + binding.connectData.visibility = View.VISIBLE + binding.noConnect.visibility = View.GONE + } + + NetworkInfo.NetworkType.MOBILE -> { + status = 2 + binding.conText1.text = getString(R.string.mobile) + binding.connectData.visibility = View.VISIBLE + binding.noConnect.visibility = View.GONE + } + + else -> { + status = 0 + setNoConnect() + } } - NetworkInfo.NetworkType.MOBILE -> { - status = 2 - setWifiDetail() - setMobileInfo() + } + // 观察 WiFi 详情 + mainViewModel.wifiDetails.observe(viewLifecycleOwner) { wifiDetails -> + wifiDetails?.let { + setWifiDetail(it) + } ?: run { + binding.networkLayout.wifiList.visibility = View.GONE } - else -> { - status = 0 - setNoConnect() + } + + // 观察移动网络详情 + 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 - } - }else{ - binding.simInfo1.root.visibility=View.GONE - binding.simInfo2.root.visibility=View.GONE - + 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 + } } /** * 设置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 -> - 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.strengthView.setStrength(calculateSignalLevel(percent = percent)) - } - - 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.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.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 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.strengthView.setStrength(calculateSignalLevel(percent = percent)) } + + 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.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.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 { - return when { + private fun calculateSignalLevel(percent: Int): Int { + return when { percent >= 80 -> 5 // 优秀 percent >= 60 -> 4 // 良好 percent >= 40 -> 3 // 一般 @@ -251,6 +311,7 @@ private lateinit var binding:FragmentNetworkBinding else -> 1 // 非常差/无信号 } } + companion object { @JvmStatic diff --git a/myphoneinfo/src/main/java/com/xyzshell/myphoneinfo/main/MainScrollActivity.kt b/myphoneinfo/src/main/java/com/xyzshell/myphoneinfo/main/MainScrollActivity.kt index bc0ab9b..b948c8e 100644 --- a/myphoneinfo/src/main/java/com/xyzshell/myphoneinfo/main/MainScrollActivity.kt +++ b/myphoneinfo/src/main/java/com/xyzshell/myphoneinfo/main/MainScrollActivity.kt @@ -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() { override fun inflateBinding(inflater: LayoutInflater): ActivityMainBinding = @@ -48,12 +50,6 @@ class MainScrollActivity : BaseActivity() { }) }) { 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() } diff --git a/myphoneinfo/src/main/java/com/xyzshell/myphoneinfo/main/MainViewModel.kt b/myphoneinfo/src/main/java/com/xyzshell/myphoneinfo/main/MainViewModel.kt new file mode 100644 index 0000000..06ecf86 --- /dev/null +++ b/myphoneinfo/src/main/java/com/xyzshell/myphoneinfo/main/MainViewModel.kt @@ -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() + val cpuInfo: LiveData = _cpuInfo + + private val _gpuInfo = MutableLiveData() + val gpuInfo: LiveData = _gpuInfo + + private val _displayInfo = MutableLiveData() + val displayInfo: LiveData = _displayInfo + + private val _storageInfo = MutableLiveData() + val storageInfo: LiveData = _storageInfo + private val _memInfo = MutableLiveData() + val memInfo: LiveData = _memInfo + + private val _bluetoothInfo = MutableLiveData() + val bluetoothInfo: LiveData = _bluetoothInfo + + private val _inputInfo= MutableLiveData() + val input: LiveData = _inputInfo + + //网络相关 + private val networkInfo= AndInfo.instance.network + private val _networkStatus= MutableLiveData() + val networkStatus: LiveData = _networkStatus + private val _wifiDetails = MutableLiveData() + val wifiDetails: LiveData = _wifiDetails + + private val _mobileDetails = MutableLiveData() + val mobileDetails: LiveData = _mobileDetails + + private val _networkPermissionGranted = MutableLiveData() + val networkPermissionGranted: LiveData = _networkPermissionGranted + + + //应用列表 + private val _installedApps= MutableLiveData>() + + + val installedApps: LiveData> = _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() + } + } + } +} \ No newline at end of file diff --git a/myphoneinfo/src/main/res/layout/dialog_app_click.xml b/myphoneinfo/src/main/res/layout/dialog_app_click.xml index 6b501f0..90ada73 100644 --- a/myphoneinfo/src/main/res/layout/dialog_app_click.xml +++ b/myphoneinfo/src/main/res/layout/dialog_app_click.xml @@ -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"/> diff --git a/myphoneinfo/src/main/res/layout/fragment_apps.xml b/myphoneinfo/src/main/res/layout/fragment_apps.xml index 1fe888b..0d2f37e 100644 --- a/myphoneinfo/src/main/res/layout/fragment_apps.xml +++ b/myphoneinfo/src/main/res/layout/fragment_apps.xml @@ -1,6 +1,7 @@ diff --git a/myphoneinfo/src/main/res/layout/hard_memory.xml b/myphoneinfo/src/main/res/layout/hard_memory.xml index 3a75244..cb6205f 100644 --- a/myphoneinfo/src/main/res/layout/hard_memory.xml +++ b/myphoneinfo/src/main/res/layout/hard_memory.xml @@ -51,7 +51,11 @@ android:layout_width="match_parent" android:layout_height="wrap_content" layout="@layout/mem_text_style" /> - +