完成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 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 import kotlin.math.abs
object SetNumberOrWordUtils { object SetNumberOrWordUtils {
@ -98,5 +103,36 @@ object SetNumberOrWordUtils {
fun setYesOrNo(boolean: Boolean): String { fun setYesOrNo(boolean: Boolean): String {
return if (boolean) "Yes" else "No" 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.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import com.xyzshell.andinfo.AndInfo import com.xyzshell.andinfo.AndInfo
import com.xyzshell.andinfo.libs.AppDetails 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.AppDialogFragment
import com.xyzshell.myphoneinfo.dialog.BottomDialogFragment import com.xyzshell.myphoneinfo.dialog.BottomDialogFragment
import com.xyzshell.myphoneinfo.main.MainScrollActivity 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 { class AppsFragment : Fragment(),AppListAdapter.OnShowDialogListener {
private lateinit var binding: FragmentAppsBinding private lateinit var binding: FragmentAppsBinding
@ -34,15 +39,25 @@ class AppsFragment : Fragment(),AppListAdapter.OnShowDialogListener {
val adapter= AppListAdapter() val adapter= AppListAdapter()
val installedApps = AndInfo.instance.app.getInstalledApps() val installedApps = AndInfo.instance.app.getInstalledApps()
val userSize=installedApps.size val userSize=installedApps.size
viewLifecycleOwner.lifecycleScope.launch {
delay(50) // 短暂延迟确保UI完成布局
withContext(Dispatchers.Main) {
adapter.setList(installedApps) adapter.setList(installedApps)
adapter.setOnclickListener(this) adapter.setOnclickListener(this@AppsFragment)
}}
binding.recyclerView.adapter = adapter binding.recyclerView.adapter = adapter
binding.recyclerView.layoutManager = LinearLayoutManager(requireContext()) 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 { binding.llTitle.setOnClickListener {
bottomDialog= BottomDialogFragment(type = "apps", sel = sel, invoke = {item-> bottomDialog= BottomDialogFragment(type = "apps", sel = sel, invoke = {item->
sel = item sel = item
binding.tvTitle.text=when(item){ binding.tvTitle.text=when(item){
0 -> "User$userSize" 0 -> "User($userSize)"
1 -> "System(23)" 1 -> "System(23)"
2 -> "All(23)" 2 -> "All(23)"
else -> "None" else -> "None"
@ -66,7 +81,7 @@ class AppsFragment : Fragment(),AppListAdapter.OnShowDialogListener {
} }
override fun onShowAppSelectDialog(item: AppDetails) { override fun onShowAppSelectDialog(item: AppDetails) {
dialogFragment = AppDialogFragment() dialogFragment = AppDialogFragment(item)
dialogFragment.show(requireActivity().supportFragmentManager, "AppDialogFragment") dialogFragment.show(requireActivity().supportFragmentManager, "AppDialogFragment")
} }

View File

@ -9,6 +9,7 @@ import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.Toast import android.widget.Toast
import androidx.annotation.RequiresPermission import androidx.annotation.RequiresPermission
import androidx.lifecycle.lifecycleScope
import com.xyzshell.andinfo.AndInfo import com.xyzshell.andinfo.AndInfo
import com.xyzshell.andinfo.libs.NetworkInfo import com.xyzshell.andinfo.libs.NetworkInfo
import com.xyzshell.myphoneinfo.R 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.custom.SetNumberOrWordUtils.setYesOrNo
import com.xyzshell.myphoneinfo.databinding.FragmentNetworkBinding import com.xyzshell.myphoneinfo.databinding.FragmentNetworkBinding
import com.xyzshell.myphoneinfo.dialog.ShowLoadFragment 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 import kotlin.toString
class NetworkFragment : Fragment() { class NetworkFragment : Fragment() {
@ -42,6 +47,9 @@ private lateinit var binding:FragmentNetworkBinding
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
networkInfo= AndInfo.instance.network networkInfo= AndInfo.instance.network
viewLifecycleOwner.lifecycleScope.launch {
delay(50) // 短暂延迟确保UI完成布局
withContext(Dispatchers.Main) {
refreshStatus() refreshStatus()
if(PermissionChecker.getMissingPermissions(context = requireContext()).isNotEmpty()){ if(PermissionChecker.getMissingPermissions(context = requireContext()).isNotEmpty()){
PermissionChecker.requestSimple( PermissionChecker.requestSimple(
@ -105,6 +113,8 @@ private lateinit var binding:FragmentNetworkBinding
showLoadFragment.show(requireActivity().supportFragmentManager, tag) showLoadFragment.show(requireActivity().supportFragmentManager, tag)
} }
} }
}}
} }
private fun refreshStatus() { private fun refreshStatus() {

View File

@ -1,23 +1,39 @@
package com.xyzshell.myphoneinfo.dialog package com.xyzshell.myphoneinfo.dialog
import android.graphics.Color import android.graphics.Color
import android.graphics.drawable.Drawable
import android.os.Bundle import android.os.Bundle
import android.util.Log
import android.view.Gravity import android.view.Gravity
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.view.WindowManager import android.view.WindowManager
import android.widget.LinearLayout
import androidx.core.graphics.drawable.toDrawable import androidx.core.graphics.drawable.toDrawable
import androidx.fragment.app.DialogFragment 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 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 var _binding: DialogAppClickBinding? = null
private val baseBinding get() = _binding!! private val baseBinding get() = _binding!!
private lateinit var appDetails:AppDetails
open fun onPositiveClick() {} open fun onPositiveClick() {
}
override fun onCreateView( override fun onCreateView(
inflater: LayoutInflater, inflater: LayoutInflater,
@ -25,18 +41,74 @@ class AppDialogFragment : DialogFragment() {
savedInstanceState: Bundle? savedInstanceState: Bundle?
): View { ): View {
_binding = DialogAppClickBinding.inflate(inflater, container, false) _binding = DialogAppClickBinding.inflate(inflater, container, false)
appDetails = appInfo
return baseBinding.root return baseBinding.root
} }
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
setBasicAppInfo()
loadPermissionsAsync()
baseBinding.textCancel.setOnClickListener { baseBinding.textCancel.setOnClickListener {
onPositiveClick() onPositiveClick()
dismiss() 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() { override fun onStart() {
super.onStart() super.onStart()
dialog?.window?.let { window -> dialog?.window?.let { window ->
@ -56,4 +128,6 @@ class AppDialogFragment : DialogFragment() {
super.onDestroyView() super.onDestroyView()
_binding = null _binding = null
} }
} }

View File

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

View File

@ -1,18 +1,37 @@
<?xml version="1.0" encoding="utf-8"?> <?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" xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:background="@drawable/dashboard_model_background" android:background="@drawable/dashboard_model_background"
android:orientation="vertical"> android:orientation="vertical">
<!-- 子类内容容器 -->
<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:layout_height="wrap_content"
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 <androidx.constraintlayout.utils.widget.ImageFilterView
android:id="@+id/image_icon" android:id="@+id/image_icon"
android:layout_width="66dp" android:layout_width="66dp"
android:layout_height="66dp" android:layout_height="66dp"
android:layout_marginStart="25dp" android:layout_marginStart="25dp"
android:layout_marginTop="20dp"
android:src="@mipmap/ic_launcher" android:src="@mipmap/ic_launcher"
app:round="10dp"
app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toTopOf="parent" /> app:layout_constraintTop_toTopOf="parent" />
@ -28,17 +47,7 @@
app:layout_constraintLeft_toRightOf="@id/image_icon" app:layout_constraintLeft_toRightOf="@id/image_icon"
app:layout_constraintTop_toTopOf="@id/image_icon" app:layout_constraintTop_toTopOf="@id/image_icon"
app:layout_constraintBottom_toBottomOf="@id/image_icon"/> app:layout_constraintBottom_toBottomOf="@id/image_icon"/>
<!-- 子类内容容器 --> </androidx.constraintlayout.widget.ConstraintLayout>
<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"
>
<com.xyzshell.myphoneinfo.custom.LabelValueCustomView <com.xyzshell.myphoneinfo.custom.LabelValueCustomView
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
@ -70,6 +79,11 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:id="@+id/tv6" android:id="@+id/tv6"
app:labelText="@string/installed"/> 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 <com.xyzshell.myphoneinfo.custom.LabelValueCustomView
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
@ -94,10 +108,12 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="@string/permissions" /> android:text="@string/permissions" />
<LinearLayout <LinearLayout
android:id="@+id/allowedTv"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:gravity="center" android:gravity="center"
android:layout_marginTop="10dp" android:layout_marginTop="10dp"
android:visibility="gone"
android:orientation="horizontal"> android:orientation="horizontal">
<ImageView <ImageView
android:layout_width="17dp" android:layout_width="17dp"
@ -118,13 +134,14 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:orientation="vertical"> android:orientation="vertical">
<include layout="@layout/common_check_style2" android:id="@+id/check2"/>
</LinearLayout> </LinearLayout>
<LinearLayout <LinearLayout
android:id="@+id/notAllowedTv"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:gravity="center" android:gravity="center"
android:layout_marginTop="10dp" android:layout_marginTop="10dp"
android:visibility="gone"
android:orientation="horizontal"> android:orientation="horizontal">
<ImageView <ImageView
android:layout_width="17dp" android:layout_width="17dp"
@ -145,14 +162,15 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:orientation="vertical"> android:orientation="vertical">
<include layout="@layout/common_check_style1" android:id="@+id/check1"/>
</LinearLayout> </LinearLayout>
<LinearLayout <LinearLayout
android:id="@+id/specialTv"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="10dp" android:layout_marginTop="10dp"
android:gravity="center" android:gravity="center"
android:visibility="gone"
android:orientation="horizontal"> android:orientation="horizontal">
<ImageView <ImageView
android:layout_width="17dp" android:layout_width="17dp"
@ -173,19 +191,18 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:orientation="vertical"> android:orientation="vertical">
<include layout="@layout/common_check_style3" android:id="@+id/check3"/>
</LinearLayout> </LinearLayout>
</LinearLayout> </LinearLayout>
</LinearLayout> </LinearLayout>
</androidx.core.widget.NestedScrollView>
<LinearLayout <LinearLayout
android:id="@+id/llbottom"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" 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:layout_marginBottom="10dp"
android:orientation="horizontal"> android:orientation="horizontal">
@ -219,4 +236,4 @@
/> />
</LinearLayout> </LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout> </LinearLayout>

View File

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

View File

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

View File

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