Commit c7f9596a authored by wanglei's avatar wanglei

[新增]病毒扫描功能

parent fa7c8d39
package com.base.appzxhy.bean
import android.graphics.drawable.Drawable
data class AppInfoBean(
val appName: String,
val icon: Drawable,
val packageName: String,
var isSelected: Boolean = false,
val isInstall:Boolean?=null,
val score:Int?=null,
val apkPath:String?=null
)
package com.base.appzxhy.ui.malware
import com.base.appzxhy.MyApplication
import com.trustlook.sdk.cloudscan.CloudScanClient
import com.trustlook.sdk.data.Region
/**
*Create by SleepDog on 2025-01-24
*/
object CloudScan {
val scanClient by lazy(LazyThreadSafetyMode.NONE) {
CloudScanClient.Builder(MyApplication.appContext)
// 设置服务地区,
// 海外地区设置:Region.INTL,百度用户设置:Region.BAIDU
.setRegion(Region.INTL)
// 设置连接超时时长,单位为毫秒
.setConnectionTimeout(30000)
//设置传输超时时长,单位为毫秒
.setSocketTimeout(30000)
.build()
}
}
\ No newline at end of file
package com.base.appzxhy.ui.malware
import android.app.Activity
import android.view.LayoutInflater
import androidx.appcompat.app.AlertDialog
import androidx.constraintlayout.widget.ConstraintLayout
import com.base.appzxhy.R
import com.base.appzxhy.business.ads.AdsMgr
import com.base.appzxhy.databinding.DialogErrBinding
class ErrorScanDialog(
val activity: Activity
) {
val dialog = AlertDialog.Builder(activity).create()
val binding = DialogErrBinding.inflate(LayoutInflater.from(activity))
var action: (() -> Unit)? = null
fun showDialog() {
dialog.setView(binding.root)
dialog.setCanceledOnTouchOutside(false)
dialog.show()
val params = dialog.window?.attributes
// params?.width = ConstraintLayout.LayoutParams.MATCH_PARENT
params?.width = activity.resources.getDimensionPixelSize(R.dimen.dp_295)
params?.height = ConstraintLayout.LayoutParams.WRAP_CONTENT
// params?.gravity = Gravity.BOTTOM
dialog.window?.attributes = params
dialog.window?.setBackgroundDrawableResource(android.R.color.transparent)
AdsMgr.showNative(binding.flAd, R.layout.layout_admob_native_custom)
binding.tvSure.setOnClickListener {
dialog.dismiss()
action?.invoke()
}
}
}
\ No newline at end of file
package com.base.appzxhy.ui.malware
import android.annotation.SuppressLint
import android.content.Intent
import android.content.pm.PackageManager
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.appcompat.content.res.AppCompatResources
import androidx.core.content.ContextCompat
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.base.appzxhy.R
import com.base.appzxhy.base.BaseActivity
import com.base.appzxhy.bean.AppInfoBean
import com.base.appzxhy.business.ads.AdsMgr
import com.base.appzxhy.business.ads.AdsShowCallBack
import com.base.appzxhy.databinding.ActivityMalwareCleanBinding
import com.base.appzxhy.databinding.ItemMalwareCleanBinding
import com.base.appzxhy.utils.FileUtils
import com.trustlook.sdk.cloudscan.CloudScanListener
import com.trustlook.sdk.data.AppInfo
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import kotlin.random.Random
import androidx.core.net.toUri
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import com.base.appzxhy.BuildConfig
import com.base.appzxhy.base.LottieEnum
import com.base.appzxhy.bean.FeatureBean.Companion.ANTIVIRUS
import com.base.appzxhy.ui.cleanresult.CleanResultActivity
import com.base.appzxhy.utils.LogEx
class MalwareCleanActivity : BaseActivity<ActivityMalwareCleanBinding>(ActivityMalwareCleanBinding::inflate) {
private var appList = mutableListOf<AppInfoBean>()
private val adapter by lazy(LazyThreadSafetyMode.NONE) {
class ViewHolder(val binding: ItemMalwareCleanBinding) :
RecyclerView.ViewHolder(binding.root)
object : RecyclerView.Adapter<ViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val binding = ItemMalwareCleanBinding.inflate(
LayoutInflater.from(parent.context),
parent,
false
)
return ViewHolder(binding)
}
override fun getItemCount(): Int = appList.size
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val item = appList[position]
holder.binding.ivIcon.setImageDrawable(item.icon)
holder.binding.tvName.text = item.appName
holder.binding.tvLevel.text = getLevel(item.score ?: 0)
holder.binding.viewLine.visibility =
if (position == itemCount - 1) View.GONE else View.VISIBLE
holder.itemView.setOnClickListener {
item.isSelected = !item.isSelected
AdsMgr.showInsert(this@MalwareCleanActivity, showCallBack = object : AdsShowCallBack() {
override fun next() {
uninstall(0, listOf(item))
}
})
}
}
}
}
private fun getLevel(score: Int): String {
return if (score <= 5) {
"safe"
} else if (score in 6..7) {
"Risk"
} else {
"malware"
}
}
@SuppressLint("NotifyDataSetChanged")
private fun uninstall(position: Int, list: List<AppInfoBean>) {
val packageName = list[position].packageName
val intent = Intent(Intent.ACTION_DELETE, "package:$packageName".toUri())
launcher.launch(intent) {
if (isUninstall(packageName)) {
appList.removeIf {
it.packageName == packageName
}
runOnUiThread {
adapter.notifyDataSetChanged()
updateUninstall()
}
}
if (position < list.size - 1) uninstall(position + 1, list)
else {
updateView(false)
}
}
}
private fun isUninstall(packageName: String): Boolean {
val intent = packageManager.getLaunchIntentForPackage(packageName)
return intent == null
}
@SuppressLint("SetTextI18n")
private fun updateUninstall() {
val list = appList.filter { it.isSelected }
val size = if (list.isEmpty()) "" else " ${list.size} ${getString(R.string.apps)}"
}
@SuppressLint("NotifyDataSetChanged")
private fun malwareClean(list: List<AppInfoBean>) {
lifecycleScope.launch(Dispatchers.Default) {
val installApps = mutableListOf<AppInfoBean>()
list.forEach {
if (it.isInstall == true) installApps.add(it)
else {
it.apkPath?.also {
launch(Dispatchers.IO) {
FileUtils.deleteFile(it)
}
}
appList.remove(it)
}
}
if (installApps.isNotEmpty()) {
uninstall(0, installApps)
} else {
withContext(Dispatchers.Main) {
adapter.notifyDataSetChanged()
updateUninstall()
}
}
}
}
private var isScanning = false
private fun initData(isScan: Boolean = isScanning) {
if (isScan) return
isScanning = true
CloudScan.scanClient.startQuickScan(object : CloudScanListener() {
override fun onScanStarted() {
LogEx.logDebug(TAG, "onScanStarted")
}
override fun onScanCanceled() {
LogEx.logDebug(TAG, "onScanCanceled")
scanFinish()
}
override fun onScanProgress(p0: Int, p1: Int, p2: AppInfo?) {
LogEx.logDebug(TAG, "onScanProgress")
}
@SuppressLint("NotifyDataSetChanged")
override fun onScanFinished(dataList: MutableList<AppInfo>?) {
//Log.e("CloudScan", "onScanFinished")
scanFinishData(dataList)
}
override fun onScanError(p0: Int, p1: String?) {
LogEx.logDebug(TAG, "onScanError")
isScanning = false
scanError()
}
override fun onScanInterrupt() {
isScanning = false
scanError()
}
})
}
fun scanError() {
if (BuildConfig.DEBUG) return
val dialog = ErrorScanDialog(this@MalwareCleanActivity)
dialog.action = {
finish()
}
dialog.showDialog()
}
@SuppressLint("NotifyDataSetChanged")
fun scanFinishData(dataList: MutableList<AppInfo>?) {
lifecycleScope.launch(Dispatchers.Default) {
val realList = mutableListOf<AppInfoBean>()
dataList?.forEach {
if (!it.isSystemApp && (it.score > 5 || BuildConfig.DEBUG)) {
var isInstall: Boolean
// Log.e("CloudScan", "${it.source} ${it.apkPath} ${it.toJSON(this@MalwareCleanActivity)}")
val icon = try {
val res = packageManager.getApplicationIcon(it.packageName)
isInstall = true
res
} catch (e: PackageManager.NameNotFoundException) {
isInstall = false
val info = packageManager.getPackageArchiveInfo(
it.apkPath,
PackageManager.GET_ACTIVITIES
)
if (info != null) {
info.applicationInfo?.let { packageManager.getApplicationIcon(it) }
} else {
AppCompatResources.getDrawable(this@MalwareCleanActivity, R.drawable.icon_apk)
}
}
realList.add(
AppInfoBean(
it.appName,
icon!!,
it.packageName,
false,
isInstall,
it.score,
it.apkPath
)
)
// withContext(Dispatchers.Main) {
// adapter.notifyItemInserted(appList.size)
// }
}
}
if (BuildConfig.DEBUG) {
realList.add(
AppInfoBean(
"AntiVirus Phone Manager",
AppCompatResources.getDrawable(this@MalwareCleanActivity, R.drawable.icon_apk)!!,
"com.tool.fast.phone",
false,
true,
5,
"apkPath/apkPath/apkPath"
)
)
}
withContext(Dispatchers.Main) {
val durationTime = Random.nextLong(3000, 3500)
delay(durationTime)
appList.clear()
appList.addAll(realList)
scanFinish()
adapter.notifyDataSetChanged()
updateView(true)
}
}
}
private fun scanFinish() {
isScanning = false
stopAnimation()
}
fun stopAnimation() {
binding.layoutAnimation.lottieAnimation.clearAnimation()
binding.layoutAnimation.root.visibility = View.GONE
isBackDisable = false
}
@SuppressLint("SetTextI18n")
private fun updateView(jump: Boolean) {
binding.ivLogo.visibility = if (appList.isEmpty()) View.GONE else View.VISIBLE
binding.tvCount.visibility = if (appList.isEmpty()) View.GONE else {
binding.tvCount.text = "${appList.size} issue"
View.VISIBLE
}
binding.tvUnit.visibility = if (appList.isEmpty()) View.GONE else View.VISIBLE
binding.rvMalware.visibility = if (appList.isEmpty()) View.GONE else View.VISIBLE
binding.ivEmpty.visibility = if (appList.isEmpty()) View.VISIBLE else View.GONE
if (appList.isEmpty()) {
if (jump) {
CleanResultActivity.functionKey = ANTIVIRUS
CleanResultActivity.titleName = getString(R.string.malware_scan)
goToAc(CleanResultActivity::class.java)
}
} else {
AdsMgr.showNative(binding.flAd, R.layout.layout_admob_native_custom)
}
}
override fun useDefaultImmersive() {
ViewCompat.setOnApplyWindowInsetsListener(binding.root) { v, insets ->
val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars())
v.setPadding(systemBars.left, 0, systemBars.right, systemBars.bottom)
binding.clTop.setPadding(0, systemBars.top, 0, 0)
insets
}
}
override fun initView() {
super.initView()
showAdAnimation(LottieEnum.MALWARE_SCAN)
binding.rvMalware.adapter = adapter
binding.rvMalware.layoutManager = LinearLayoutManager(this)
initData()
if (BuildConfig.DEBUG) {
scanFinishData(null)
}
}
override fun initListener() {
super.initListener()
binding.flBack.setOnClickListener { onBackPressedDispatcher.onBackPressed() }
}
override fun handleBackCallBack() {
super.handleBackCallBack()
}
}
\ No newline at end of file
package com.base.appzxhy.ui.malware
import android.app.Activity
import android.view.LayoutInflater
import androidx.appcompat.app.AlertDialog
import com.base.appzxhy.databinding.DialogMalwareTipBinding
class MalwareDialog(
val activity: Activity
) {
val dialog = AlertDialog.Builder(activity).create()
val binding = DialogMalwareTipBinding.inflate(LayoutInflater.from(activity))
var action: (() -> Unit)? = null
fun showDialog() {
dialog.setView(binding.root)
dialog.setCanceledOnTouchOutside(false)
dialog.show()
val params = dialog.window?.attributes
// params?.width = ConstraintLayout.LayoutParams.MATCH_PARENT
// params?.width = activity.resources.getDimensionPixelSize(R.dimen.dp_295)
// params?.height = ConstraintLayout.LayoutParams.WRAP_CONTENT
// params?.gravity = Gravity.BOTTOM
dialog.window?.attributes = params
dialog.window?.setBackgroundDrawableResource(android.R.color.transparent)
binding.tvCancel.setOnClickListener {
dialog.dismiss()
}
binding.tvSure.setOnClickListener {
dialog.dismiss()
action?.invoke()
}
}
}
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/color_f7fafa">
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/cl_top"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/colorPrimary"
app:layout_constraintTop_toTopOf="parent">
<androidx.appcompat.widget.LinearLayoutCompat
android:id="@+id/ll_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:orientation="horizontal"
android:paddingVertical="6dp"
app:layout_constraintTop_toTopOf="parent">
<ImageView
android:id="@+id/fl_back"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="12dp"
android:importantForAccessibility="no"
android:padding="4dp"
android:src="@drawable/icon_return_w"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<androidx.appcompat.widget.AppCompatTextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/malware_scan"
android:textColor="@color/white"
android:textSize="20sp"
android:textStyle="bold"
app:layout_constraintBottom_toBottomOf="@id/fl_back"
app:layout_constraintStart_toEndOf="@id/fl_back"
app:layout_constraintTop_toTopOf="@id/fl_back" />
</androidx.appcompat.widget.LinearLayoutCompat>
</androidx.constraintlayout.widget.ConstraintLayout>
<com.base.appzxhy.business.ads.NativeParentView
android:id="@+id/flAd"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="12dp"
android:layout_marginTop="10dp"
android:visibility="gone"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@id/cl_top" />
<ImageView
android:id="@+id/iv_logo"
android:layout_width="@dimen/dp_150"
android:layout_height="@dimen/dp_150"
android:importantForAccessibility="no"
android:src="@drawable/img_antivirus_scan"
android:visibility="gone"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@id/flAd"
tools:visibility="visible" />
<TextView
android:id="@+id/tv_count"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="#F8461A"
android:textSize="20sp"
android:textStyle="bold"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="@id/tv_unit"
app:layout_constraintHorizontal_chainStyle="packed"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toLeftOf="@id/tv_unit"
app:layout_constraintTop_toTopOf="@id/tv_unit"
tools:text="3 issue" />
<TextView
android:id="@+id/tv_unit"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="12dp"
android:text=" found"
android:textColor="@color/black"
android:textSize="20sp"
android:textStyle="bold"
android:visibility="gone"
app:layout_constraintLeft_toRightOf="@id/tv_count"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@id/iv_logo" />
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/iv_empty"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/img_antivirus_scan_kong"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rv_malware"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_marginTop="10dp"
android:layout_marginBottom="12dp"
android:background="@color/white"
android:paddingVertical="6dp"
android:scrollbars="none"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toBottomOf="@id/tv_unit" />
<include
android:id="@+id/layout_animation"
layout="@layout/layout_animation"
tools:visibility="gone" />
</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment