完成appinfo相关接口

This commit is contained in:
yuqian 2026-01-15 09:55:22 +08:00
parent bca6640cc3
commit d3de7d8907
9 changed files with 253 additions and 99 deletions

View File

@ -1,5 +1,10 @@
package com.xyzshell.myphoneinfo.custom
import java.text.SimpleDateFormat
import java.util.Calendar
import java.util.Date
import java.util.Locale
import java.util.TimeZone
import kotlin.math.abs
object SetNumberOrWordUtils {
@ -98,5 +103,36 @@ object SetNumberOrWordUtils {
fun setYesOrNo(boolean: Boolean): String {
return if (boolean) "Yes" else "No"
}
// Long 扩展函数:获取格式化日期字符串
fun Long.toFormattedDate(
pattern: String = "MMMM dd, yyyy h:mm a",
timeZone: TimeZone = TimeZone.getDefault()
): String {
val sdf = SimpleDateFormat(pattern, Locale.getDefault())
sdf.timeZone = timeZone
return sdf.format(Date(this))
}
// 获取各个时间字段的扩展函数
fun Long.toCalendar(): Calendar {
return Calendar.getInstance().apply {
time = Date(this@toCalendar)
}
}
fun Long.getYear(): Int = this.toCalendar().get(Calendar.YEAR)
fun Long.getMonth(): Int = this.toCalendar().get(Calendar.MONTH) + 1
fun Long.getDay(): Int = this.toCalendar().get(Calendar.DAY_OF_MONTH)
fun Long.getHour(): Int = this.toCalendar().get(Calendar.HOUR_OF_DAY)
fun Long.getMinute(): Int = this.toCalendar().get(Calendar.MINUTE)
fun Long.getSecond(): Int = this.toCalendar().get(Calendar.SECOND)
fun Long.getMillisecond(): Int = this.toCalendar().get(Calendar.MILLISECOND)
// Long 扩展函数:获取格式化大小字符串
fun Long.toSizeString(): String = when {
this < 1000 -> "$this B"
this < 1000*1000 -> "%.1f KB".format(this/1000.0)
this < 1000*1000*1000 -> "%.1f MB".format(this/(1000.0*1000))
else -> "%.1f GB".format(this/(1000.0*1000*1000))
}
}

View File

@ -6,6 +6,7 @@ import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.LinearLayoutManager
import com.xyzshell.andinfo.AndInfo
import com.xyzshell.andinfo.libs.AppDetails
@ -15,6 +16,10 @@ 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 kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
class AppsFragment : Fragment(),AppListAdapter.OnShowDialogListener {
private lateinit var binding: FragmentAppsBinding
@ -34,15 +39,25 @@ class AppsFragment : Fragment(),AppListAdapter.OnShowDialogListener {
val adapter= AppListAdapter()
val installedApps = AndInfo.instance.app.getInstalledApps()
val userSize=installedApps.size
adapter.setList(installedApps)
adapter.setOnclickListener(this)
viewLifecycleOwner.lifecycleScope.launch {
delay(50) // 短暂延迟确保UI完成布局
withContext(Dispatchers.Main) {
adapter.setList(installedApps)
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
0 -> "User($userSize)"
1 -> "System(23)"
2 -> "All(23)"
else -> "None"
}
binding.llTitle.setOnClickListener {
bottomDialog= BottomDialogFragment(type = "apps", sel = sel, invoke = {item->
sel = item
binding.tvTitle.text=when(item){
0 -> "User$userSize"
0 -> "User($userSize)"
1 -> "System(23)"
2 -> "All(23)"
else -> "None"
@ -66,7 +81,7 @@ class AppsFragment : Fragment(),AppListAdapter.OnShowDialogListener {
}
override fun onShowAppSelectDialog(item: AppDetails) {
dialogFragment = AppDialogFragment()
dialogFragment = AppDialogFragment(item)
dialogFragment.show(requireActivity().supportFragmentManager, "AppDialogFragment")
}

View File

@ -9,6 +9,7 @@ import android.view.View
import android.view.ViewGroup
import android.widget.Toast
import androidx.annotation.RequiresPermission
import androidx.lifecycle.lifecycleScope
import com.xyzshell.andinfo.AndInfo
import com.xyzshell.andinfo.libs.NetworkInfo
import com.xyzshell.myphoneinfo.R
@ -17,6 +18,10 @@ 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 kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import kotlin.toString
class NetworkFragment : Fragment() {
@ -42,69 +47,74 @@ private lateinit var binding:FragmentNetworkBinding
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
networkInfo= AndInfo.instance.network
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()
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)
//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)
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)
//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)
//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)
//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.conTexts.textTitle.text=getString(R.string.status)
binding.defText1.textTitle.text=getString(R.string.data)
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.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.networkLayout.pubShow.setOnClickListener{
val tag = "showLoadFragment"
if (requireActivity().supportFragmentManager.findFragmentByTag(tag) == null) {
val showLoadFragment = ShowLoadFragment()
showLoadFragment.show(requireActivity().supportFragmentManager, tag)
}
}
}}
}
private fun refreshStatus() {

View File

@ -1,23 +1,39 @@
package com.xyzshell.myphoneinfo.dialog
import android.graphics.Color
import android.graphics.drawable.Drawable
import android.os.Bundle
import android.util.Log
import android.view.Gravity
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.view.WindowManager
import android.widget.LinearLayout
import androidx.core.graphics.drawable.toDrawable
import androidx.fragment.app.DialogFragment
import androidx.lifecycle.lifecycleScope
import com.xyzshell.andinfo.libs.AppDetails
import com.xyzshell.myphoneinfo.R
import com.xyzshell.myphoneinfo.custom.SetNumberOrWordUtils.toFormattedDate
import com.xyzshell.myphoneinfo.custom.SetNumberOrWordUtils.toSizeString
import com.xyzshell.myphoneinfo.databinding.CommonCheckStyle2Binding
import com.xyzshell.myphoneinfo.databinding.DialogAppClickBinding
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
class AppDialogFragment : DialogFragment() {
class AppDialogFragment(var appInfo:AppDetails) : DialogFragment() {
private var _binding: DialogAppClickBinding? = null
private val baseBinding get() = _binding!!
private lateinit var appDetails:AppDetails
open fun onPositiveClick() {}
open fun onPositiveClick() {
}
override fun onCreateView(
inflater: LayoutInflater,
@ -25,18 +41,74 @@ class AppDialogFragment : DialogFragment() {
savedInstanceState: Bundle?
): View {
_binding = DialogAppClickBinding.inflate(inflater, container, false)
appDetails = appInfo
return baseBinding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
setBasicAppInfo()
loadPermissionsAsync()
baseBinding.textCancel.setOnClickListener {
onPositiveClick()
dismiss()
}
baseBinding.textManage.setOnClickListener {
dismiss()
}
}
private fun loadPermissionsAsync() {
viewLifecycleOwner.lifecycleScope.launch {
delay(50) // 短暂延迟确保UI完成布局
withContext(Dispatchers.Main) {
appDetails.permissions.forEach {
//允许
val allowedView= LayoutInflater.from(context).inflate(R.layout.common_check_style2,baseBinding.allowedLL,false)
val allowedBinding = CommonCheckStyle2Binding.bind(allowedView)
//不允许
val notAllowedView= LayoutInflater.from(context).inflate(R.layout.common_check_style1,baseBinding.notAllowedLL,false)
val notAllowedBinding = CommonCheckStyle2Binding.bind(notAllowedView)
//特殊
val specialView= LayoutInflater.from(context).inflate(R.layout.common_check_style3,baseBinding.specialLL,false)
val specialBinding = CommonCheckStyle2Binding.bind(specialView)
when(it.status){
"Allowed"->{
baseBinding.allowedTv.visibility = View.VISIBLE
allowedBinding.content.text = it.name
baseBinding.allowedLL.addView(allowedView)
}
"Denied"->{
baseBinding.notAllowedTv.visibility = View.VISIBLE
notAllowedBinding.content.text = it.name
baseBinding.notAllowedLL.addView(notAllowedView)
}
"Special Access"->{
baseBinding.specialTv.visibility = View.VISIBLE
specialBinding.content.text = it.name
baseBinding.specialLL.addView(specialView)
}
}
Log.d("AppDialogFragment", it.status)
}
}}
}
private fun setBasicAppInfo() {
baseBinding.imageIcon.setImageDrawable(appDetails.icon as? Drawable)
baseBinding.dialogTitle.text = appDetails.appName
baseBinding.tv1.setValue(appDetails.packageName)
baseBinding.tv2.setValue(appDetails.versionName.toString())
baseBinding.tv3.setValue(appDetails.targetSdkVersion.toString())
baseBinding.tv4.setValue(appDetails.minSdkVersion.toString())
// todo baseBinding.tv5.setValue(appDetails.packageName)
baseBinding.tv6.setValue(appDetails.installer.toString())
baseBinding.tv7.setValue(appDetails.size.toSizeString())
baseBinding.tv8.setValue(appDetails.uid.toString())
baseBinding.tv9.setValue("${appDetails.lastUpdateTime.toFormattedDate(pattern = "MMM dd, yyyy h:mm a")}")
}
override fun onStart() {
super.onStart()
dialog?.window?.let { window ->
@ -56,4 +128,6 @@ class AppDialogFragment : DialogFragment() {
super.onDestroyView()
_binding = null
}
}

View File

@ -34,7 +34,6 @@
android:layout_alignTop="@id/tv_label"
android:layout_marginStart="35dp"
android:layout_toEndOf="@id/tv_label"
android:gravity="center"
android:textSize="15sp"
android:textColor="#C1C5C2"
android:text="sppppppppppppppppppppp" />

View File

@ -1,44 +1,53 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/dashboard_model_background"
android:orientation="vertical">
<androidx.constraintlayout.utils.widget.ImageFilterView
android:id="@+id/image_icon"
android:layout_width="66dp"
android:layout_height="66dp"
android:layout_marginStart="25dp"
android:layout_marginTop="20dp"
android:src="@mipmap/ic_launcher"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<com.google.android.material.textview.MaterialTextView
android:id="@+id/dialog_title"
style="@style/TextHeavy20"
android:textSize="18sp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="80dp"
android:text="@string/screenreader"
app:layout_constraintLeft_toRightOf="@id/image_icon"
app:layout_constraintTop_toTopOf="@id/image_icon"
app:layout_constraintBottom_toBottomOf="@id/image_icon"/>
<!-- 子类内容容器 -->
<androidx.core.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:layout_marginTop="20dp"
android:fillViewport="true"
>
<LinearLayout
android:id="@+id/contentContainer"
android:layout_width="match_parent"
android:paddingBottom="16dp"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
app:layout_constraintTop_toBottomOf="@id/image_icon"
android:orientation="vertical"
>
android:paddingBottom="35dp"
>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
>
<androidx.constraintlayout.utils.widget.ImageFilterView
android:id="@+id/image_icon"
android:layout_width="66dp"
android:layout_height="66dp"
android:layout_marginStart="25dp"
android:src="@mipmap/ic_launcher"
app:round="10dp"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<com.google.android.material.textview.MaterialTextView
android:id="@+id/dialog_title"
style="@style/TextHeavy20"
android:textSize="18sp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="80dp"
android:text="@string/screenreader"
app:layout_constraintLeft_toRightOf="@id/image_icon"
app:layout_constraintTop_toTopOf="@id/image_icon"
app:layout_constraintBottom_toBottomOf="@id/image_icon"/>
</androidx.constraintlayout.widget.ConstraintLayout>
<com.xyzshell.myphoneinfo.custom.LabelValueCustomView
android:layout_width="match_parent"
android:layout_height="wrap_content"
@ -70,6 +79,11 @@
android:layout_height="wrap_content"
android:id="@+id/tv6"
app:labelText="@string/installed"/>
<com.xyzshell.myphoneinfo.custom.LabelValueCustomView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/tv9"
app:labelText="@string/last_updated_time"/>
<com.xyzshell.myphoneinfo.custom.LabelValueCustomView
android:layout_width="match_parent"
android:layout_height="wrap_content"
@ -94,10 +108,12 @@
android:layout_height="wrap_content"
android:text="@string/permissions" />
<LinearLayout
android:id="@+id/allowedTv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:layout_marginTop="10dp"
android:visibility="gone"
android:orientation="horizontal">
<ImageView
android:layout_width="17dp"
@ -118,13 +134,14 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<include layout="@layout/common_check_style2" android:id="@+id/check2"/>
</LinearLayout>
<LinearLayout
android:id="@+id/notAllowedTv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:layout_marginTop="10dp"
android:visibility="gone"
android:orientation="horizontal">
<ImageView
android:layout_width="17dp"
@ -145,14 +162,15 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<include layout="@layout/common_check_style1" android:id="@+id/check1"/>
</LinearLayout>
<LinearLayout
android:id="@+id/specialTv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:gravity="center"
android:visibility="gone"
android:orientation="horizontal">
<ImageView
android:layout_width="17dp"
@ -173,19 +191,18 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<include layout="@layout/common_check_style3" android:id="@+id/check3"/>
</LinearLayout>
</LinearLayout>
</LinearLayout>
</androidx.core.widget.NestedScrollView>
<LinearLayout
android:id="@+id/llbottom"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@id/contentContainer"
app:layout_constraintBottom_toBottomOf="parent"
android:layout_marginBottom="10dp"
android:orientation="horizontal">
@ -219,4 +236,4 @@
/>
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
</LinearLayout>

View File

@ -35,7 +35,7 @@
android:layout_width="wrap_content"
android:id="@+id/tvTitle"
android:layout_height="wrap_content"
android:text="User apps (23)"
android:text=""
style="@style/TextHeavy20"
android:textSize="16sp"
/>

View File

@ -1,6 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"
android:layout_height="wrap_content">
<LinearLayout
@ -10,10 +11,11 @@
android:gravity="center_vertical"
android:paddingHorizontal="20dp"
android:id="@+id/view">
<ImageView
<androidx.constraintlayout.utils.widget.ImageFilterView
android:id="@+id/ivIcon"
android:layout_width="45dp"
android:layout_height="45dp"
app:round="10dp"
android:src="@mipmap/ic_launcher"
/>
<LinearLayout

View File

@ -232,6 +232,7 @@
<string name="minimum_sdk">Minimum SDK</string>
<string name="installer_type">Installer type</string>
<string name="installed">Installed</string>
<string name="last_updated_time">Last updated time</string>
<string name="uid">UID</string>
<string name="permissions">Permissions</string>
<string name="allowed"> Allowed</string>