Commit 8fcabf74 authored by wanglei's avatar wanglei

初始化项目

parent c175ca14
Pipeline #1141 failed with stages
package com.test.basd.smartjunkcleaner
import android.annotation.SuppressLint
import android.app.Activity
import android.app.job.JobInfo
import android.app.job.JobScheduler
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.os.Bundle
import android.util.Log
import com.blankj.utilcode.util.ActivityUtils
import com.blankj.utilcode.util.AppUtils
import com.google.android.gms.ads.MobileAds
import com.google.android.gms.ads.identifier.AdvertisingIdClient
import com.google.firebase.FirebaseApp
import com.test.basd.smartjunkcleaner.activity.splash.NewSplashActivity
import com.test.basd.smartjunkcleaner.display.ActionBroadcast.Companion.initBroadcast
import com.test.basd.smartjunkcleaner.display.fcm.FcmHelper
import com.test.basd.smartjunkcleaner.display.fcm.NotificationJobService
import com.test.basd.smartjunkcleaner.helps.BaseApplication
import com.test.basd.smartjunkcleaner.helps.ComUtils.requestCfg
import com.test.basd.smartjunkcleaner.helps.ConfigHelper
import com.test.basd.smartjunkcleaner.helps.InstallHelps
import com.test.basd.smartjunkcleaner.helps.ads.AdmobUtils
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.MainScope
import kotlinx.coroutines.launch
class MyApplication : BaseApplication() {
companion object {
var PAUSED_VALUE = 0
}
override fun init() {
initApp()
}
fun initApp() {
if (ConfigHelper.ifAgreePrivacy) {
MainScope().launch(Dispatchers.IO) {
try {
ConfigHelper.gid = AdvertisingIdClient.getAdvertisingIdInfo(context).id ?: ""
} catch (_: Exception) {
}
}
initNotificationWork()
context.initBroadcast()
MainScope().launch {
requestCfg()
InstallHelps.init()
}
}
MobileAds.initialize(this) { initializationStatus ->
val statusMap =
initializationStatus.adapterStatusMap
for (adapterClass in statusMap.keys) {
val status = statusMap[adapterClass]
Log.d(
"MyApp", String.format(
"Adapter name: %s, Description: %s, Latency: %d",
adapterClass, status!!.description, status.latency
)
)
}
}
initLifeListener()
}
private fun initNotificationWork() {
FirebaseApp.initializeApp(this)
FcmHelper.getToken()
FcmHelper.subscribeToTopic()
FcmHelper.startFCMCheckAlarm(this)
initJob()
}
private fun initJob() {
val jobScheduler = getSystemService(Context.JOB_SCHEDULER_SERVICE) as JobScheduler
val job = JobInfo.Builder(0, ComponentName(this, NotificationJobService::class.java))
.setMinimumLatency(0L)
.setOverrideDeadline(0L)
.setPersisted(true)
.build()
jobScheduler.schedule(job)
}
@SuppressLint("UnspecifiedRegisterReceiverFlag")
private fun initLifeListener() {
registerActivityLifecycleCallbacks(object : ActivityLifecycleCallbacks {
private var count = 0
override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) {}
override fun onActivityStarted(activity: Activity) {
// Log.e("onActivityStarted", "activity=$activity")
count++
if (count == 1 && !AppUtils.isAppForeground()) {
val topActivity = ActivityUtils.getTopActivity()
if (ConfigHelper.noLoadingActivities.all {
!topActivity.localClassName.contains(
it, true
)
}) {
if (AdmobUtils.isOpenAdLoaded()) {
AdmobUtils.showAppOpenAd(activity)
} else {
topActivity.startActivity(
Intent(
topActivity,
NewSplashActivity::class.java
).apply {
putExtra("isHotLaunch", true)
})
}
}
}
}
override fun onActivityResumed(activity: Activity) {
PAUSED_VALUE = 1
}
override fun onActivityPaused(activity: Activity) {
PAUSED_VALUE = 2
}
override fun onActivityStopped(activity: Activity) {
count--
}
override fun onActivitySaveInstanceState(activity: Activity, outState: Bundle) {}
override fun onActivityDestroyed(activity: Activity) {}
})
}
}
\ No newline at end of file
package com.test.basd.smartjunkcleaner.activity
import android.annotation.SuppressLint
import android.app.AppOpsManager
import android.app.usage.UsageStats
import android.app.usage.UsageStatsManager
import android.content.Context
import android.content.Intent
import android.content.pm.ApplicationInfo
import android.content.pm.PackageInfo
import android.content.pm.PackageManager
import android.net.Uri
import android.os.Build
import android.util.Log
import android.view.View
import androidx.activity.addCallback
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import androidx.core.view.isVisible
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentActivity
import androidx.lifecycle.lifecycleScope
import androidx.viewpager2.adapter.FragmentStateAdapter
import androidx.viewpager2.widget.ViewPager2
import com.test.basd.smartjunkcleaner.R
import com.test.basd.smartjunkcleaner.bean.AppBean
import com.test.basd.smartjunkcleaner.databinding.ActivityAppManagerBinding
import com.test.basd.smartjunkcleaner.fragment.AppListFragment
import com.test.basd.smartjunkcleaner.fragment.AppListFragment.Companion.APP_LIST_TYPE_INSTALL
import com.test.basd.smartjunkcleaner.fragment.AppListFragment.Companion.APP_LIST_TYPE_LAST_USE
import com.test.basd.smartjunkcleaner.fragment.AppListFragment.Companion.APP_LIST_TYPE_NAME
import com.test.basd.smartjunkcleaner.fragment.AppListFragment.Companion.APP_LIST_TYPE_SIZE
import com.test.basd.smartjunkcleaner.helps.ActivityLauncher
import com.test.basd.smartjunkcleaner.helps.AppSizeUtils
import com.test.basd.smartjunkcleaner.helps.BaseActivity
import com.test.basd.smartjunkcleaner.helps.LogEx
import com.test.basd.smartjunkcleaner.helps.TimeUtils.THIRTY_DAYS_QUERY
import com.test.basd.smartjunkcleaner.helps.TimeUtils.timePair
import com.test.basd.smartjunkcleaner.helps.ads.AdmobUtils
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.async
import kotlinx.coroutines.cancel
import kotlinx.coroutines.delay
import kotlinx.coroutines.isActive
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import org.json.JSONObject
import java.util.concurrent.LinkedBlockingDeque
/**
* 所需权限:
* 使用情况 last use
* android.permission.PACKAGE_USAGE_STATS
* 卸载
* android.permission.REQUEST_DELETE_PACKAGES
*/
class AppManagerActivity : BaseActivity<ActivityAppManagerBinding>() {
override val isLightMode = true
private lateinit var launcher: ActivityLauncher
private val dataList = ArrayList<AppBean>()
private val pages = arrayListOf<Fragment>()
private lateinit var pagerAdapter: ScreenSlidePagerAdapter
private val TAG: String = "AppManager2Activity"
private var currentPosition: Int = 0
private lateinit var context: Context
var animationFinish: Boolean = false
override val binding: ActivityAppManagerBinding by lazy {
ActivityAppManagerBinding.inflate(layoutInflater)
}
override fun initView() {
context = this
launcher = ActivityLauncher(this)
setContentView(binding.root)
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main)) { v, insets ->
val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars())
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom)
insets
}
initViewPager()
playLottie()
CoroutineScope(Dispatchers.Main).launch {
initAppData()
if (checkUsageAccessSettings()) {
async(Dispatchers.IO) {
setAppBeanSize()
setAppLastUseTime()
}.await()
}
pages.forEachIndexed { index, fragment ->
val appListFragment = (fragment as AppListFragment)
appListFragment.refreshFragmentData(dataList, index == currentPosition)
}
}
binding.llName.setOnClickListener {
if (currentPosition == 0) {
val fragment = (pages[0] as AppListFragment)
fragment.reverseOrder()
val isAsc = fragment.isAsc
changeAsc(0, isAsc)
}
binding.viewpager2.setCurrentItem(0, false)
}
binding.llInstall.setOnClickListener {
if (currentPosition == 1) {
val fragment = (pages[1] as AppListFragment)
fragment.reverseOrder()
val isAsc = fragment.isAsc
changeAsc(1, isAsc)
}
binding.viewpager2.setCurrentItem(1, false)
}
binding.llSize.setOnClickListener {
if (currentPosition == 2) {
val fragment = (pages[2] as AppListFragment)
fragment.reverseOrder()
val isAsc = fragment.isAsc
changeAsc(2, isAsc)
}
binding.viewpager2.setCurrentItem(2, false)
}
binding.llLastUse.setOnClickListener {
if (currentPosition == 3) {
val fragment = (pages[3] as AppListFragment)
fragment.reverseOrder()
val isAsc = fragment.isAsc
changeAsc(3, isAsc)
}
binding.viewpager2.setCurrentItem(3, false)
}
binding.flBack.setOnClickListener {
AdmobUtils.showInterstitialAd(this) {
finishToMain()
}
}
onBackPressedDispatcher.addCallback {
AdmobUtils.showInterstitialAd(this@AppManagerActivity) {
finishToMain()
}
}
}
private fun playLottie() {
LogEx.logDebug(TAG, "playLottie")
binding.llLottie.isVisible = true
binding.lottie.imageAssetsFolder = "guan_smax_li/images/"
binding.lottie.setAnimation("guan_smax_li/data.json")
binding.lottie.playAnimation()
binding.root.postDelayed({
AdmobUtils.showInterstitialAd(this) {
animationFinish = true
binding.llLottie.isVisible = false
}
}, 6000)
}
fun refreshUsageAccessData() {
val obj = JSONObject()
obj.put("activity", javaClass.simpleName)
if (checkUsageAccessSettings()) {
pages.forEachIndexed { index, fragment ->
val appListFragment = (fragment as AppListFragment)
appListFragment.showContent(index == currentPosition)
}
lifecycleScope.launch(Dispatchers.IO) {
setAppBeanSize()
setAppLastUseTime()
launch(Dispatchers.Main) {
pages.forEachIndexed { index, fragment ->
val appListFragment = (fragment as AppListFragment)
appListFragment.refreshFragmentData(dataList, index == currentPosition, index)
}
}
}
}
}
private fun changeAsc(position: Int, isAsc: Boolean) {
when (position) {
0 -> {
binding.arrowInstall.setAscOrder(false)
binding.arrowSize.setAscOrder(false)
binding.arrowLastUse.setAscOrder(false)
binding.arrowName.setAscOrder(true, isAsc)
}
1 -> {
binding.arrowName.setAscOrder(false)
binding.arrowSize.setAscOrder(false)
binding.arrowLastUse.setAscOrder(false)
binding.arrowInstall.setAscOrder(true, isAsc)
}
2 -> {
binding.arrowName.setAscOrder(false)
binding.arrowInstall.setAscOrder(false)
binding.arrowLastUse.setAscOrder(false)
binding.arrowSize.setAscOrder(true, isAsc)
}
3 -> {
binding.arrowName.setAscOrder(false)
binding.arrowInstall.setAscOrder(false)
binding.arrowSize.setAscOrder(false)
binding.arrowLastUse.setAscOrder(true, isAsc)
}
}
}
private fun changeIndicator(position: Int) {
when (position) {
0 -> {
binding.vName.visibility = View.VISIBLE
binding.vInstall.visibility = View.INVISIBLE
binding.vSize.visibility = View.INVISIBLE
binding.vLastUse.visibility = View.INVISIBLE
}
1 -> {
binding.vName.visibility = View.INVISIBLE
binding.vInstall.visibility = View.VISIBLE
binding.vSize.visibility = View.INVISIBLE
binding.vLastUse.visibility = View.INVISIBLE
}
2 -> {
binding.vName.visibility = View.INVISIBLE
binding.vInstall.visibility = View.INVISIBLE
binding.vSize.visibility = View.VISIBLE
binding.vLastUse.visibility = View.INVISIBLE
}
3 -> {
binding.vName.visibility = View.INVISIBLE
binding.vInstall.visibility = View.INVISIBLE
binding.vSize.visibility = View.INVISIBLE
binding.vLastUse.visibility = View.VISIBLE
}
}
}
private fun initViewPager() {
pages.add(AppListFragment().apply { setInitData(launcher, APP_LIST_TYPE_NAME, true) })
pages.add(AppListFragment().apply { setInitData(launcher, APP_LIST_TYPE_INSTALL, false) })
pages.add(AppListFragment().apply { setInitData(launcher, APP_LIST_TYPE_SIZE, false, needPermission = true) })
pages.add(AppListFragment().apply { setInitData(launcher, APP_LIST_TYPE_LAST_USE, false, needPermission = true) })
pagerAdapter = ScreenSlidePagerAdapter(this@AppManagerActivity)
binding.viewpager2.adapter = pagerAdapter
binding.viewpager2.registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() {
override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) {
super.onPageScrolled(position, positionOffset, positionOffsetPixels)
currentPosition = position
changeIndicator(position)
val fragment = (pages[position] as AppListFragment)
fragment.setDataRefresh(dataList)
changeAsc(position, fragment.isAsc)
}
})
}
@SuppressLint("QueryPermissionsNeeded")
private fun initAppData() {
dataList.clear()
val pm = packageManager
val packages = pm.getInstalledPackages(0)
packages.forEach { app ->
if (isLaunchApp(context, app)) {
val appBean = AppBean(
app.applicationInfo.loadIcon(pm),
app.applicationInfo.loadLabel(pm).toString(),
app.applicationInfo.packageName,
false
)
//安装时间
setAppInstallTime(appBean, context)
dataList.add(appBean)
}
}
LogEx.logDebug(TAG, "dataList.size=${dataList.size}")
}
private fun setAppBeanSize() {
dataList.forEach { app ->
val size = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
AppSizeUtils.instance?.getAppSizeO(context, app.pkg)
} else {
AppSizeUtils.instance?.getAppSize(context, app.pkg)
}
app.appSize = size ?: 0
}
}
private fun setAppLastUseTime(
queryTime: Int = THIRTY_DAYS_QUERY
) {
val pair = timePair(queryTime)
val startTime: Long = pair.first
val endTime: Long = pair.second
val usageList = getUsageList(context, startTime, endTime)
dataList.forEach { app ->
val filterList = usageList.filter { it.packageName == app.pkg }
app.usageStats = filterList
app.lastUsedTime = if (filterList.isNotEmpty()) filterList.maxOf { it.lastTimeUsed } else -2//默认30天没数据
}
}
@SuppressLint("NotifyDataSetChanged")
fun otherPageRemove(appBean: AppBean, fragment: AppListFragment) {
dataList.remove(appBean)
pages.forEach {
if (it != fragment) {
(it as AppListFragment).setData(dataList)
}
}
}
private fun allPageRemove(unInstalled: List<AppBean>) {
Log.e(TAG, "unInstalled=$unInstalled")
dataList.removeAll(unInstalled.toSet())
pages.forEachIndexed { index, fragment ->
val fg = fragment as AppListFragment
if (index == currentPosition) {
Log.e(TAG, "dataList=$dataList currentPosition=$currentPosition")
lifecycleScope.launch(Dispatchers.Main) {
delay(1500)
fg.setDataRefresh(dataList)
}
} else {
Log.e(TAG, "dataList=$dataList index=$index")
fg.setData(dataList)
}
}
}
@SuppressLint("StringFormatMatches", "SetTextI18n")
fun showUnInstall(selectList: List<AppBean>) {
if (selectList.isNotEmpty()) {
binding.tvUninstall.isVisible = true
binding.tvUninstall.text = "Uninstall(${selectList.size})"
binding.tvUninstall.setOnClickListener {
binding.tvUninstall.isVisible = false
binding.tvUninstall.setOnClickListener { }
unInstallApps(selectList) { unInstalled ->
allPageRemove(unInstalled)
}
}
} else {
binding.tvUninstall.isVisible = false
binding.tvUninstall.setOnClickListener { }
}
}
private fun unInstallApps(selectList: List<AppBean>, finishUnInstall: (unInstalled: List<AppBean>) -> Unit) =
lifecycleScope.launch(Dispatchers.IO) {
val unInstall = arrayListOf<AppBean>()//待卸载列表
unInstall.addAll(selectList)
val unInstalled = arrayListOf<AppBean>()//已卸载列表
val queue = LinkedBlockingDeque<AppBean>()
val first = unInstall[0]
queue.put(first)
while (isActive) {
val appBean = withContext(Dispatchers.IO) {
queue.take()
}
delay(250)
LogEx.logDebug(TAG, "循环。。。。。。。。。。。。。。。。。。。。。。。。")
val intent = Intent(Intent.ACTION_DELETE, Uri.parse("package:${appBean.pkg}"))
launcher.launch(intent) {
val flag = isInstalled(
this@AppManagerActivity,
appBean.pkg,
error = {
unInstall.remove(appBean)
unInstalled.add(appBean)
})
if (!flag) {
unInstalled.add(appBean)
}
unInstall.remove(appBean)
if (unInstall.isNotEmpty()) {
val next = unInstall[0]
LogEx.logDebug(TAG, "卸载回调 next=$next")
queue.put(next)
} else {
LogEx.logDebug(TAG, "卸载完成回调 finishUnInstall unInstalled=$unInstalled")
finishUnInstall.invoke(unInstalled)
cancel()
}
}
}
}
inner class ScreenSlidePagerAdapter(fa: FragmentActivity) : FragmentStateAdapter(fa) {
override fun getItemCount(): Int = pages.size
override fun createFragment(position: Int): Fragment = pages[position]
}
fun checkUsageAccessSettings(): Boolean {
val appOpsManager = getSystemService(Context.APP_OPS_SERVICE) as AppOpsManager
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
appOpsManager.unsafeCheckOpNoThrow(
AppOpsManager.OPSTR_GET_USAGE_STATS,
android.os.Process.myUid(),
this.packageName
) == AppOpsManager.MODE_ALLOWED
} else {
appOpsManager.checkOpNoThrow(
AppOpsManager.OPSTR_GET_USAGE_STATS,
android.os.Process.myUid(),
this.packageName
) == AppOpsManager.MODE_ALLOWED
}
}
fun isLaunchApp(context: Context, app: PackageInfo, filterSystem: Boolean = true): Boolean {
val flagSystem = (app.applicationInfo.flags and ApplicationInfo.FLAG_SYSTEM) < 1
val flag = if (filterSystem) flagSystem else true
return flag && app.applicationInfo.packageName != context.packageName
}
fun setAppInstallTime(appBean: AppBean, context: Context) {
//安装时间
var firstInstallTime = 0L
try {
val packageManager = context.packageManager
val packageInfo: PackageInfo = packageManager.getPackageInfo(appBean.pkg, 0)
//应用装时间
firstInstallTime = packageInfo.firstInstallTime
//应用最后一次更新时间
val lastUpdateTime = packageInfo.lastUpdateTime
// LogEx.logDebug(TAG, "lastUpdateTime=$lastUpdateTime")
} catch (e: PackageManager.NameNotFoundException) {
e.printStackTrace()
}
appBean.installTime = firstInstallTime
}
/**
* 前台运行App+后台运行App=总的启动App
*/
private fun getUsageList(context: Context, startTime: Long, endTime: Long): ArrayList<UsageStats> {
val list = arrayListOf<UsageStats>()
val mUsmManager = context.getSystemService(Context.USAGE_STATS_SERVICE) as UsageStatsManager
val map = mUsmManager.queryAndAggregateUsageStats(startTime, endTime)
map.values.forEach { stats ->
if (stats.totalTimeInForeground > 0) {
list.add(stats)
} else {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
if (stats.totalTimeForegroundServiceUsed > 0) {
list.add(stats)
}
}
}
}
return list
}
private fun isInstalled(context: Context, pkg: String, error: (() -> Unit)? = null): Boolean {
return try {
context.packageManager.getPackageInfo(pkg, 0)
true
} catch (e: Exception) {
error?.invoke()
false
}
}
companion object {
}
}
package com.test.basd.smartjunkcleaner.activity
import android.animation.Animator
import android.annotation.SuppressLint
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.hardware.camera2.CameraAccessException
import android.hardware.camera2.CameraManager
import android.location.LocationManager
import android.os.BatteryManager
import android.os.Build
import android.os.SystemClock
import android.provider.Settings
import android.view.View
import android.widget.Toast
import androidx.activity.OnBackPressedCallback
import androidx.activity.addCallback
import androidx.activity.result.contract.ActivityResultContracts
import androidx.core.view.isVisible
import com.test.basd.smartjunkcleaner.R
import com.test.basd.smartjunkcleaner.databinding.ActivityBatteryInfoBinding
import com.test.basd.smartjunkcleaner.helps.BaseActivity
import com.test.basd.smartjunkcleaner.helps.ads.AdmobUtils
import com.test.basd.smartjunkcleaner.view.AFunOb
import java.util.Calendar
import java.util.Date
import kotlin.math.roundToInt
@SuppressLint("SetTextI18n")
class BatteryInfoActivity : BaseActivity<ActivityBatteryInfoBinding>() {
override val isLightMode = true
private lateinit var receiver: BatteryReceiver
private lateinit var cm: CameraManager
private lateinit var cameraId: String
private var isTorchOn = false
override val binding: ActivityBatteryInfoBinding by lazy {
ActivityBatteryInfoBinding.inflate(layoutInflater)
}
override fun initView() {
receiver = BatteryReceiver()
val filter = IntentFilter(Intent.ACTION_BATTERY_CHANGED)
registerReceiver(receiver, filter)
cm = getSystemService(Context.CAMERA_SERVICE) as CameraManager
try {
cameraId = cm.cameraIdList[0]
} catch (e: CameraAccessException) {
e.printStackTrace()
}
setFilter()
binding.idBatteryLottie.imageAssetsFolder = "dian_smax_chi/images/"
binding.idBatteryLottie.setAnimation("dian_smax_chi/data.json")
binding.idBatteryLottie.playAnimation()
binding.root.postDelayed({
playFinish()
}, 5000)
onBackPressedDispatcher.addCallback(object : OnBackPressedCallback(true) {
override fun handleOnBackPressed() {
AdmobUtils.showInterstitialAd(this@BatteryInfoActivity) {
finishToMain()
}
}
})
binding.switchTwo.setOnClickListener {
turnOnBluetooth()
}
binding.switchThree.setOnCheckedChangeListener { _, isChecked ->
if (isChecked) {
turnOnFlashLight()
} else {
turnOffFlashLight()
}
}
binding.switchFour.setOnClickListener {
jumpLocationSettings()
}
binding.btOk.setOnClickListener {
binding.btOk.setBackgroundResource(R.drawable.bg_shape_set_click)
startActivity(Intent(this, ResultActivity::class.java).apply {
putExtra("from", AFunOb.BATTERY_INFO)
})
finish()
}
binding.ivBack.setOnClickListener {
AdmobUtils.showInterstitialAd(this@BatteryInfoActivity) {
finishToMain()
}
}
onBackPressedDispatcher.addCallback {
AdmobUtils.showInterstitialAd(this@BatteryInfoActivity) {
finishToMain()
}
}
}
private val result1 =
registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
}
private val result2 =
registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
}
private val settingResult =
registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
}
private inner class BatteryReceiver : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
val current = intent?.extras?.getInt("level")?:0
val total = intent?.extras?.getInt("scale")?:0
val percent = current * 100 / total
if (percent >= 30) {
binding.imagePower.setImageResource(R.mipmap.batteryl)
} else {
binding.imagePower.setImageResource(R.mipmap.batteryh)
}
binding.tvLevel.text = "$percent%"
}
}
private fun playFinish() {
binding.idConsOne.isVisible = false
binding.idConsBatteryFinish.isVisible = true
binding.idBatteryFinish.addAnimatorListener(object : Animator.AnimatorListener {
override fun onAnimationStart(p0: Animator) {
}
override fun onAnimationEnd(p0: Animator) {
AdmobUtils.showInterstitialAd(this@BatteryInfoActivity) {
updateUI()
}
}
override fun onAnimationCancel(p0: Animator) {
}
override fun onAnimationRepeat(p0: Animator) {
}
})
}
private fun checkFlashLight() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
cm.registerTorchCallback(object : CameraManager.TorchCallback() {
override fun onTorchModeChanged(cameraId: String, enabled: Boolean) {
super.onTorchModeChanged(cameraId, enabled)
if (cameraId == this@BatteryInfoActivity.cameraId) {
if (enabled && !isTorchOn) {
isTorchOn = true
binding.switchThree.isChecked = true
} else if (!enabled && isTorchOn) {
isTorchOn = false
binding.switchThree.isChecked = false
}
}
}
}, null)
} else {
Toast.makeText(this, "don't support you phone", Toast.LENGTH_SHORT).show()
}
}
private fun turnOnFlashLight() {
try {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
cm.setTorchMode(cameraId, true)
} // 打开手电筒
} catch (e: CameraAccessException) {
e.printStackTrace()
}
}
private fun turnOffFlashLight() {
try {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
cm.setTorchMode(cameraId, false)
} // 打开手电筒
} catch (e: CameraAccessException) {
e.printStackTrace()
}
}
private fun jumpLocationSettings() {
val intent = Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS)
settingResult.launch(intent)
}
private fun checkLocation() {
binding.switchFour.isChecked = isLocationEnabled(this)
}
private fun isLocationEnabled(context: Context): Boolean {
val locationManager = context.getSystemService(Context.LOCATION_SERVICE) as LocationManager
return locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER) ||
locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER)
}
private fun setFilter() {
val filter1 = IntentFilter(Intent.ACTION_BATTERY_CHANGED)
binding.tvVo.text = "${
(registerReceiver(null, filter1)?.getIntExtra("voltage", 0)
?.toFloat()!!).roundToInt() / 1000.0
}V"
binding.tvTemp.text = "${
(registerReceiver(null, filter1)?.getIntExtra("temperature", 0)
?.toFloat()!!).roundToInt() / 10.0
}°C"
val uptime = SystemClock.elapsedRealtime()
val currantTime = Calendar.getInstance().time
val batteryUseTime = currantTime.time - uptime
val batteryDate = Date(batteryUseTime)
binding.tvTime.text = "${batteryDate.hours} H ${batteryDate.minutes} M"
val bm = getSystemService(Context.BATTERY_SERVICE) as BatteryManager
val chargeCounter = bm.getIntProperty(BatteryManager.BATTERY_PROPERTY_CHARGE_COUNTER)
val pCapacity = bm.getIntProperty(BatteryManager.BATTERY_PROPERTY_CAPACITY)
if (chargeCounter != Int.MIN_VALUE && pCapacity != Int.MIN_VALUE) {
binding.tvMa.text = "${
String.format("%.1f", (chargeCounter / (pCapacity.toFloat() / 100f)) / 1000f)
} mAh"
}
}
private fun updateUI() {
binding.idBatteryLottie.visibility = View.GONE
binding.idConsBatteryFinish.isVisible = false
binding.llContent.isVisible = true
}
override fun onResume() {
super.onResume()
binding.btOk.setBackgroundResource(R.drawable.bg_shape_set)
checkFlashLight()
checkLocation()
}
fun turnOnBluetooth() {
val intent2 = Intent(Settings.ACTION_BLUETOOTH_SETTINGS)
result1.launch(intent2)
}
override fun onDestroy() {
super.onDestroy()
unregisterReceiver(receiver);
}
}
\ No newline at end of file
package com.test.basd.smartjunkcleaner.activity
import android.content.Intent
import android.graphics.Color
import androidx.core.view.isVisible
import androidx.core.view.updatePadding
import com.blankj.utilcode.util.BarUtils
import com.test.basd.smartjunkcleaner.databinding.ActivityLayoutCleanGuestBinding
import com.test.basd.smartjunkcleaner.helps.BaseActivity
/**
* 清理功能引导
*/
class CleanGuestActivity : BaseActivity<ActivityLayoutCleanGuestBinding>() {
override val binding: ActivityLayoutCleanGuestBinding by lazy {
ActivityLayoutCleanGuestBinding.inflate(layoutInflater)
}
override fun initView() {
BarUtils.setStatusBarLightMode(this, true)
BarUtils.setStatusBarColor(this, Color.TRANSPARENT)
binding.root.updatePadding(top = BarUtils.getStatusBarHeight())
binding.root.postDelayed({
binding.idTvSkip.isVisible = true
}, 3000)
binding.idTvSkip.setOnClickListener {
finishToMain()
}
binding.idTvClean.setOnClickListener {
startActivity(Intent(this, PrepareScanActivity::class.java))
finish()
}
}
}
\ No newline at end of file
package com.test.basd.smartjunkcleaner.activity
import android.animation.ValueAnimator
import android.annotation.SuppressLint
import android.content.Intent
import android.content.pm.PackageManager
import android.graphics.Color
import android.util.Log
import android.view.LayoutInflater
import android.view.ViewGroup
import android.view.animation.LinearInterpolator
import androidx.activity.OnBackPressedCallback
import androidx.core.view.isVisible
import androidx.core.view.updatePadding
import androidx.recyclerview.widget.LinearLayoutManager
import com.blankj.utilcode.util.BarUtils
import com.test.basd.smartjunkcleaner.R
import com.test.basd.smartjunkcleaner.bean.ChildBean
import com.test.basd.smartjunkcleaner.bean.ParentBean
import com.test.basd.smartjunkcleaner.databinding.ActivityLayoutCleanJunkBinding
import com.test.basd.smartjunkcleaner.databinding.ItemChildBinding
import com.test.basd.smartjunkcleaner.databinding.ItemParentBinding
import com.test.basd.smartjunkcleaner.helps.BaseActivity
import com.test.basd.smartjunkcleaner.helps.FileHelps
import com.test.basd.smartjunkcleaner.helps.KotlinExt.toFormatSize
import com.test.basd.smartjunkcleaner.helps.ads.AdmobUtils
import com.test.basd.smartjunkcleaner.view.AFunOb
import pokercc.android.expandablerecyclerview.ExpandableAdapter
class CleanJunkActivity : BaseActivity<ActivityLayoutCleanJunkBinding>() {
override val isLightMode = false
private val parentList by lazy {
mutableListOf(
ParentBean(
title = "Useless installation package",
childItem = child3,
parentSize = sizes[0],
isParentSelected = true,
expanded = false
),
ParentBean(
title = "Residual cache junk",
childItem = child4,
parentSize = sizes[1],
isParentSelected = true,
expanded = false
),
ParentBean(
title = "Clean up more",
childItem = child5,
parentSize = sizes[2],
isParentSelected = true,
expanded = false
)
)
}
private val child1 = ArrayList<ChildBean>()
private val child2 = ArrayList<ChildBean>()
private val child3 = ArrayList<ChildBean>()
private val child4 = ArrayList<ChildBean>()
private val child5 = ArrayList<ChildBean>()
private val sizes = MutableList(3) { 0L }
private var ScanCount = 0
private val selectList = mutableListOf<String>()
private var selectSize = 0L
override val binding: ActivityLayoutCleanJunkBinding by lazy {
ActivityLayoutCleanJunkBinding.inflate(layoutInflater)
}
override fun initView() {
BarUtils.setStatusBarColor(this, Color.TRANSPARENT)
binding.root.updatePadding(top = BarUtils.getStatusBarHeight())
setAdapter()
binding.idJunksBack.setOnClickListener {
onBackPressedDispatcher.onBackPressed()
}
onBackPressedDispatcher.addCallback(object : OnBackPressedCallback(true) {
override fun handleOnBackPressed() {
finishToMain()
}
})
binding.idClBtn.setOnClickListener {
startActivity(
Intent(this, CleaningActivity::class.java)
.putExtra("list", selectList.toTypedArray())
.putExtra("size", selectSize)
)
finish()
}
}
private fun setAdapter() {
binding.idExRl.adapter = mAdapter
binding.idExRl.layoutManager = LinearLayoutManager(this)
scanJunk()
}
private fun scanJunk() {
FileHelps.getFileList({
}, {
// if (mData.isEmpty()) {
//
//
// }
FileHelps.fileList.toList().let { it ->
if (it.isNotEmpty()) {
// child3.clear()
// child4.clear()
// child5.clear()
// sizes[0] = 0
// sizes[1] = 0
// sizes[2] = 0
val a = it.filter { it.isApk() }
val halfSize = if (a.size % 2 == 1) a.size / 2 + 1 else a.size / 2
val firstHalf = a.subList(0, halfSize)
firstHalf.forEach { l ->
sizes[0] += l.size
// junkList.add(p0.path)
val bean = ChildBean(R.mipmap.hapk, l.name, l.path, l.size)
child3.add(bean)
}
ScanCount += 1
updateSize(doneIndex = 0)
// mData.addAll(it)
val b = it.filter { it.isJunk() }
val bhalfSize = if (b.size % 2 == 1) b.size / 2 + 1 else b.size / 2
val bfirstHalf = b.subList(0, bhalfSize)
bfirstHalf.forEach { l ->
sizes[1] += l.size
// junkList.add(p0.path)
val bean = ChildBean(null, l.name, l.path, l.size)
child4.add(bean)
}
ScanCount += 1
updateSize(doneIndex = 1)
val c = it.filter { it.isOtherTrash() }
val chalfSize = if (c.size % 2 == 1) c.size / 2 + 1 else c.size / 2
val cfirstHalf = c.subList(0, chalfSize)
cfirstHalf.forEach { l ->
sizes[2] += l.size
// junkList.add(p0.path)
val bean = ChildBean(null, l.name, l.path, l.size)
child5.add(bean)
}
ScanCount += 2
updateSize(doneIndex = 2)
}
}
})
}
private fun updateSize(doneIndex: Int = -1) {
addData(index = doneIndex)
val split = sizes.sum().toFormatSize(1).split(' ')
binding.idKeCl.text = split[0]
binding.idSizeUnit.text = split[1]
if (ScanCount >= 3) {
selectList.clear()
selectSize = 0L
for (i in parentList.indices) {
if (parentList[i].childItem.isNotEmpty()) {
val childsize = parentList[i].childItem.filter {
it.isChildSelected
}.sumOf {
it.childSize
}
for (a in parentList[i].childItem) {
if (a.isChildSelected) {
if (a.pathList?.isNotEmpty() == true) {
for (o in a.pathList!!) {
selectList.add(o)
}
}
}
}
selectSize += childsize
} else {
if (parentList[i].isParentSelected) {
selectSize += parentList[i].parentSize
}
}
}
parentList.forEach { parent ->
selectList.addAll(parent.childItem.filter {
it.isChildSelected
}.map { it.chilepath })
}
}
binding.idClBtn.isVisible = ScanCount >= 3
binding.idClBtn.isEnabled = sizes.sum() > 0
if (ScanCount >= 3 && sizes.sum().toInt() == 0) {
AdmobUtils.showInterstitialAd(this) {
startActivity(
Intent(this, ResultActivity::class.java).putExtra(
"from",
AFunOb.JUNK_CLEANER
).putExtra("clean_size", 0L)
)
finish()
}
}
binding.idClBtn.text = "Clean up ${selectSize.toFormatSize(1)}"
}
@SuppressLint("NotifyDataSetChanged")
private fun addData(index: Int) {
if (parentList.isNotEmpty()) {
Log.e("MXL", "addData: "+index )
parentList[index].isfinish = true
parentList[index].parentSize = sizes[index]
mAdapter.notifyDataSetChanged()
}
}
private fun updateView() {
selectList.clear()
var allSize = 0L
for (i in parentList.indices) {
if (parentList[i].childItem.isNotEmpty()) {
val childsize = parentList[i].childItem.filter {
it.isChildSelected
}.sumOf {
it.childSize
}
for (a in parentList[i].childItem) {
if (a.isChildSelected) {
if (a.pathList?.isNotEmpty() == true) {
for (o in a.pathList!!) {
selectList.add(o)
}
}
}
}
allSize += childsize
} else {
if (parentList[i].isParentSelected) {
allSize += parentList[i].parentSize
}
}
}
selectSize = allSize
binding.idClBtn.text = "Clean up ${allSize.toFormatSize()}"
binding.idClBtn.isEnabled = allSize > 0
parentList.forEach { parent ->
selectList.addAll(parent.childItem.filter {
it.isChildSelected
}.map { it.chilepath })
}
// for (item in parentList) {
// if (item.isParentSelected) {
// Log.e("MXL", "updateView: " + item)
// }
// }
}
private val mAdapter by lazy {
class ParentViewHolder(val binding: ItemParentBinding) :
ExpandableAdapter.ViewHolder(binding.root)
class ChildViewHolder(val binding: ItemChildBinding) :
ExpandableAdapter.ViewHolder(binding.root)
object : ExpandableAdapter<ExpandableAdapter.ViewHolder>() {
override fun getChildCount(groupPosition: Int): Int {
return parentList[groupPosition].childItem.size
}
override fun getGroupCount(): Int {
return parentList.size
}
override fun onCreateChildViewHolder(viewGroup: ViewGroup, viewType: Int): ViewHolder {
val inflater = LayoutInflater.from(viewGroup.context)
val binding = ItemChildBinding.inflate(inflater, viewGroup, false)
return ChildViewHolder(binding)
}
override fun onCreateGroupViewHolder(viewGroup: ViewGroup, viewType: Int): ViewHolder {
val inflater = LayoutInflater.from(viewGroup.context)
val binding = ItemParentBinding.inflate(inflater, viewGroup, false)
return ParentViewHolder(binding)
}
override fun onGroupViewHolderExpandChange(
holder: ViewHolder,
groupPosition: Int,
animDuration: Long,
expand: Boolean
) {
}
override fun onBindGroupViewHolder(
holder: ViewHolder,
groupPosition: Int,
expand: Boolean,
payloads: List<Any>
) {
val viewHolder = holder as? ParentViewHolder ?: return
val parentType = parentList[groupPosition]
viewHolder.binding.idTypeName.text = parentType.title
parentType.expanded = expand
if (parentType.expanded) {
viewHolder.binding.idXiala.setImageResource(R.mipmap.shouqi)
} else {
viewHolder.binding.idXiala.setImageResource(R.mipmap.xiala)
}
if (parentType.isfinish) {
viewHolder.binding.idImgLoad.isVisible = false
viewHolder.binding.idImgChoose.isVisible = true
} else {
ValueAnimator.ofFloat(0f, 360f).run {
duration = 1000
repeatMode = ValueAnimator.RESTART
repeatCount = ValueAnimator.INFINITE
interpolator = LinearInterpolator()
addUpdateListener {
viewHolder.binding.idImgLoad.rotation = it.animatedValue as Float
}
start()
}
viewHolder.binding.idImgLoad.isVisible = true
viewHolder.binding.idImgChoose.isVisible = false
}
viewHolder.binding.idTypeSize.text = parentType.parentSize.toFormatSize(1)
viewHolder.binding.idXiala.isVisible = parentType.childItem.isNotEmpty()
viewHolder.binding.idImgChoose.isSelected = parentType.isParentSelected
viewHolder.binding.idImgChoose.setOnClickListener {
viewHolder.binding.idImgChoose.isSelected =
!viewHolder.binding.idImgChoose.isSelected
parentType.isParentSelected = !parentType.isParentSelected
if (parentType.isParentSelected) {
for (app in parentType.childItem) {
app.isChildSelected = true
notifyDataSetChanged()
}
} else {
for (app in parentType.childItem) {
app.isChildSelected = false
notifyDataSetChanged()
}
}
updateView()
}
}
override fun onBindChildViewHolder(
holder: ViewHolder,
groupPosition: Int,
childPosition: Int,
payloads: List<Any>
) {
val viewHolder = holder as? ChildViewHolder ?: return
val childType = parentList[groupPosition].childItem[childPosition]
when (groupPosition) {
0 -> {
// val c = ConfigHelper.appList?.firstOrNull {
// it.name == childType.childname
// }
// viewHolder.binding.idImgIcon.setImageDrawable(c?.icon)
val packageManager: PackageManager = this@CleanJunkActivity.packageManager
val apkFilePath = childType.chilepath // 替换成您的APK文件路径
val packageInfo =
packageManager.getPackageArchiveInfo(
apkFilePath,
PackageManager.GET_ACTIVITIES
)
if (packageInfo != null) {
val applicationInfo = packageInfo.applicationInfo
val appIcon = packageManager.getApplicationIcon(applicationInfo)
viewHolder.binding.idImgIcon.setImageDrawable(appIcon)
}
}
1 -> {
viewHolder.binding.idImgIcon.setImageResource(R.mipmap.qingchuicon)
}
2 -> {
viewHolder.binding.idImgIcon.setImageResource(R.mipmap.del)
}
3 -> {
}
4 -> {
}
}
viewHolder.binding.idTvAppName.text = childType.childname
viewHolder.binding.idImgSelect.isSelected = childType.isChildSelected
viewHolder.binding.idTvSize.text = "${childType.childSize.toFormatSize()}"
viewHolder.itemView.setOnClickListener {
when (childPosition) {
childPosition -> {
viewHolder.binding.idImgSelect.isSelected =
!viewHolder.binding.idImgSelect.isSelected
childType.isChildSelected = !childType.isChildSelected
if (!childType.isChildSelected) {
parentList[groupPosition].isParentSelected = false
notifyDataSetChanged()
}
updateView()
}
}
}
}
}
}
}
\ No newline at end of file
package com.test.basd.smartjunkcleaner.activity
import android.animation.ValueAnimator
import android.content.Intent
import android.graphics.Color
import android.view.animation.LinearInterpolator
import androidx.core.animation.doOnEnd
import androidx.core.view.updatePadding
import com.blankj.utilcode.util.BarUtils
import com.test.basd.smartjunkcleaner.databinding.ActivityLayoutCleanupingBinding
import com.test.basd.smartjunkcleaner.helps.BaseActivity
import com.test.basd.smartjunkcleaner.helps.ConfigHelper
import com.test.basd.smartjunkcleaner.helps.FileHelps
import com.test.basd.smartjunkcleaner.helps.ads.AdmobUtils
import com.test.basd.smartjunkcleaner.view.AFunOb
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.MainScope
import kotlinx.coroutines.launch
import java.io.File
import kotlin.random.Random
class CleaningActivity : BaseActivity<ActivityLayoutCleanupingBinding>() {
override val isLightMode = true
override val binding: ActivityLayoutCleanupingBinding by lazy {
ActivityLayoutCleanupingBinding.inflate(layoutInflater)
}
private val intentSize by lazy {
intent.getLongExtra("size", -1)
}
private val listPath by lazy {
intent.getStringArrayExtra("list") ?: arrayOf()
}
var isDeleSuc = false
private var size = 0L
override fun initView() {
BarUtils.setStatusBarColor(this, Color.TRANSPARENT)
binding.root.updatePadding(top = BarUtils.getStatusBarHeight())
ConfigHelper.junkSizeClean = ConfigHelper.junkSizeClean+intentSize
MainScope().launch(Dispatchers.IO) {
try {
FileHelps.fileList.clear()
listPath.forEach {
val file = File(it)
if (FileHelps.deleteFile(file)) {
isDeleSuc = true
}
}
} catch (_: Exception) {
} finally {
}
}
playAnm()
}
private fun playAnm() {
ValueAnimator.ofFloat(0f, 360f).run {
duration = 1000
repeatMode = ValueAnimator.RESTART
repeatCount = ValueAnimator.INFINITE
interpolator = LinearInterpolator()
addUpdateListener {
binding.idYuan.rotation = it.animatedValue as Float
}
start()
}
ValueAnimator.ofInt(0, 100).run {
duration = Random.nextLong(7000, 8000)
interpolator = LinearInterpolator()
addUpdateListener {
binding.idTvJd.text = "${it.animatedValue as Int}"
}
doOnEnd {
AdmobUtils.showInterstitialAd(this@CleaningActivity) {
startActivity(Intent(this@CleaningActivity, ResultActivity::class.java).putExtra("from", AFunOb.JUNK_CLEANER).putExtra("clean_size", intentSize))
finish()
}
}
start()
}
ValueAnimator.ofFloat(0f, 360f).run {
duration = 1000
repeatMode = ValueAnimator.RESTART
repeatCount = ValueAnimator.INFINITE
interpolator = LinearInterpolator()
addUpdateListener {
binding.idView1.rotation = it.animatedValue as Float
}
start()
}
ValueAnimator.ofFloat(0f, 360f).run {
duration = 1000
repeatMode = ValueAnimator.RESTART
repeatCount = ValueAnimator.INFINITE
interpolator = LinearInterpolator()
addUpdateListener {
binding.idView2.rotation = it.animatedValue as Float
}
start()
}
ValueAnimator.ofFloat(0f, 360f).run {
duration = 1000
repeatMode = ValueAnimator.RESTART
repeatCount = ValueAnimator.INFINITE
interpolator = LinearInterpolator()
addUpdateListener {
binding.idView3.rotation = it.animatedValue as Float
}
start()
}
}
}
\ No newline at end of file
package com.test.basd.smartjunkcleaner.activity
import android.content.Intent
import android.graphics.Color
import com.blankj.utilcode.util.BarUtils
import com.test.basd.smartjunkcleaner.databinding.ActivityLayoutGuestBinding
import com.test.basd.smartjunkcleaner.helps.BaseActivity
import com.test.basd.smartjunkcleaner.helps.ConfigHelper
import kotlin.random.Random
/**
* 引导动画页面
*/
class GuestActivity : BaseActivity<ActivityLayoutGuestBinding>() {
override val binding: ActivityLayoutGuestBinding by lazy {
ActivityLayoutGuestBinding.inflate(layoutInflater)
}
private var mProgress = 0
override fun initView() {
BarUtils.setStatusBarLightMode(this, true)
BarUtils.setStatusBarColor(this, Color.TRANSPARENT)
ConfigHelper.ifGuest = true
binding.idLottie.imageAssetsFolder = "sao_smax_miao/images/"
binding.idLottie.setAnimation("sao_smax_miao/data.json")
binding.idLottie.playAnimation()
loadPro()
}
private fun loadPro() {
if (mProgress >= 100) {
binding.idSJindu.progress = mProgress
startActivity(Intent(this, CleanGuestActivity::class.java))
finish()
return
}
binding.idSJindu.postDelayed({
mProgress += Random.nextInt(5)
binding.idSJindu.progress = mProgress
loadPro()
}, 80)
}
}
\ No newline at end of file
package com.test.basd.smartjunkcleaner.activity
import android.annotation.SuppressLint
import android.graphics.Color
import android.graphics.drawable.ColorDrawable
import android.os.Build
import android.os.Environment
import android.text.format.Formatter
import android.view.View
import android.view.ViewGroup
import android.widget.PopupWindow
import androidx.activity.addCallback
import androidx.core.content.ContextCompat
import androidx.core.view.isVisible
import androidx.recyclerview.widget.RecyclerView
import com.blankj.utilcode.constant.PermissionConstants
import com.blankj.utilcode.util.PermissionUtils
import com.test.basd.smartjunkcleaner.R
import com.test.basd.smartjunkcleaner.activity.LargeFileCleanActivity.Filter.Companion.KEY_1_MONTH
import com.test.basd.smartjunkcleaner.activity.LargeFileCleanActivity.Filter.Companion.KEY_1_WEEK
import com.test.basd.smartjunkcleaner.activity.LargeFileCleanActivity.Filter.Companion.KEY_1_YEAR
import com.test.basd.smartjunkcleaner.activity.LargeFileCleanActivity.Filter.Companion.KEY_3_MONTHS
import com.test.basd.smartjunkcleaner.activity.LargeFileCleanActivity.Filter.Companion.KEY_6_MONTHS
import com.test.basd.smartjunkcleaner.activity.LargeFileCleanActivity.Filter.Companion.KEY_ALL_TIME
import com.test.basd.smartjunkcleaner.activity.LargeFileCleanActivity.Filter.Companion.KEY_ALL_TYPES
import com.test.basd.smartjunkcleaner.activity.LargeFileCleanActivity.Filter.Companion.KEY_APK
import com.test.basd.smartjunkcleaner.activity.LargeFileCleanActivity.Filter.Companion.KEY_ARCHIVES
import com.test.basd.smartjunkcleaner.activity.LargeFileCleanActivity.Filter.Companion.KEY_AUDIO
import com.test.basd.smartjunkcleaner.activity.LargeFileCleanActivity.Filter.Companion.KEY_DOCUMENT
import com.test.basd.smartjunkcleaner.activity.LargeFileCleanActivity.Filter.Companion.KEY_IMAGE
import com.test.basd.smartjunkcleaner.activity.LargeFileCleanActivity.Filter.Companion.KEY_OTHER_TYPES
import com.test.basd.smartjunkcleaner.activity.LargeFileCleanActivity.Filter.Companion.KEY_SIZE_100_MB
import com.test.basd.smartjunkcleaner.activity.LargeFileCleanActivity.Filter.Companion.KEY_SIZE_10_MB
import com.test.basd.smartjunkcleaner.activity.LargeFileCleanActivity.Filter.Companion.KEY_SIZE_1_GB
import com.test.basd.smartjunkcleaner.activity.LargeFileCleanActivity.Filter.Companion.KEY_SIZE_500_MB
import com.test.basd.smartjunkcleaner.activity.LargeFileCleanActivity.Filter.Companion.KEY_SIZE_50_MB
import com.test.basd.smartjunkcleaner.activity.LargeFileCleanActivity.Filter.Companion.KEY_VIDEO
import com.test.basd.smartjunkcleaner.adapter.LargeFileAdapter
import com.test.basd.smartjunkcleaner.bean.FileBean
import com.test.basd.smartjunkcleaner.databinding.ActivityLayoutLargeFileBinding
import com.test.basd.smartjunkcleaner.databinding.ItemFileFilterBinding
import com.test.basd.smartjunkcleaner.databinding.PopupwindowFileFilterBinding
import com.test.basd.smartjunkcleaner.helps.BaseActivity
import com.test.basd.smartjunkcleaner.helps.FileHelps
import com.test.basd.smartjunkcleaner.helps.LogEx
import com.test.basd.smartjunkcleaner.helps.TimeUtils
import com.test.basd.smartjunkcleaner.helps.ads.AdmobUtils
import com.test.basd.smartjunkcleaner.view.FileDeleteDialog.showFileDeleteDialog
import com.test.basd.smartjunkcleaner.view.FileDetailDialog.showFileDetailDialog
import com.test.basd.smartjunkcleaner.view.XmlEx.inflate
import java.io.File
/**
* 大文件清理
*/
class LargeFileCleanActivity : BaseActivity<ActivityLayoutLargeFileBinding>() {
override val isLightMode = true
private val TAG = "LargeFileCleanActivity"
private val listTypes = listOf(
Filter(KEY_ALL_TYPES, true),
Filter(KEY_IMAGE),
Filter(KEY_VIDEO),
Filter(KEY_AUDIO),
Filter(KEY_DOCUMENT),
Filter(KEY_ARCHIVES),
Filter(KEY_APK),
Filter(KEY_OTHER_TYPES),
)
private val listSizes = listOf(
Filter(KEY_SIZE_10_MB, true),
Filter(KEY_SIZE_50_MB),
Filter(KEY_SIZE_100_MB),
Filter(KEY_SIZE_500_MB),
Filter(KEY_SIZE_1_GB),
)
private val listTimes = listOf(
Filter(KEY_ALL_TIME, true),
Filter(KEY_1_WEEK),
Filter(KEY_1_MONTH),
Filter(KEY_3_MONTHS),
Filter(KEY_6_MONTHS),
Filter(KEY_1_YEAR),
)
private val largeFileList = arrayListOf<FileBean>()
private lateinit var adapter: LargeFileAdapter
private var fileType = KEY_ALL_TYPES
private var filterSize = KEY_SIZE_10_MB
private var filterTime = KEY_ALL_TIME
override val binding: ActivityLayoutLargeFileBinding by lazy {
ActivityLayoutLargeFileBinding.inflate(layoutInflater)
}
@SuppressLint("SetTextI18n")
override fun initView() {
binding.llType.isEnabled = false
binding.llSize.isEnabled = false
binding.llTime.isEnabled = false
adapter = LargeFileAdapter(
itemClick = { fileBean ->
this.showFileDetailDialog(fileBean)
},
selectClick = { files ->
if (files.isNotEmpty()) {
binding.tvDelete.background = ContextCompat.getDrawable(this, R.drawable.bg_corners_4772ff)
binding.tvDelete.isEnabled = true
val size = files.sumOf { it.size }
val sizeF = Formatter.formatFileSize(this, size)
binding.tvDelete.text = "DELETE $sizeF"
} else {
binding.tvDelete.background = ContextCompat.getDrawable(this, R.drawable.bg_corners_bfbfbf_26)
binding.tvDelete.isEnabled = false
binding.tvDelete.text = "DELETE"
}
}
)
binding.rv.adapter = adapter
binding.tvDelete.setOnClickListener {
this.showFileDeleteDialog {
val deleteList = arrayListOf<FileBean>()
adapter.getSelectData().forEach {
val file = File(it.path)
try {
if (file.delete()) {
deleteList.add(it)
}
} catch (e: Exception) {
}
}
largeFileList.removeAll(deleteList.toSet())
adapter.removeData(deleteList)
binding.tvDelete.background = ContextCompat.getDrawable(this, R.drawable.bg_corners_bfbfbf_26)
binding.tvDelete.isEnabled = false
binding.tvDelete.text = "DELETE"
}
}
binding.llType.setOnClickListener {
showPopupWindow(binding.llFilter, 0, listTypes)
}
binding.llSize.setOnClickListener {
showPopupWindow(binding.llFilter, 1, listSizes)
}
binding.llTime.setOnClickListener {
showPopupWindow(binding.llFilter, 2, listTimes)
}
binding.flBack.setOnClickListener {
AdmobUtils.showInterstitialAd(this) {
finishToMain()
}
}
onBackPressedDispatcher.addCallback {
AdmobUtils.showInterstitialAd(this@LargeFileCleanActivity) {
finishToMain()
}
}
binding.tvType.text = fileType
binding.tvSize.text = filterSize
binding.tvTime.text = filterTime
val flag = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
Environment.isExternalStorageManager()
} else {
PermissionUtils.isGranted(PermissionConstants.STORAGE)
}
if (flag) {
isplay += 1
playLottie()
}
checkPermission()
}
var isplay = 0
override fun onPermissionsResult(isGranted: Boolean) {
super.onPermissionsResult(isGranted)
if (isGranted) {
isplay += 1
if (isplay == 1) {
playLottie()
}
initList()
} else {
finishToMain()
}
}
private fun initList() {
FileHelps.getFileList(onUpdate = {
FileHelps.fileList.toList().filter {
it.isLargeFile() && it !in largeFileList
}.onEach {
it.isSelect = false
}.let {
if (it.isNotEmpty()) {
LogEx.logDebug(TAG, "it=${it}")
if (!binding.llType.isEnabled) {
binding.llType.isEnabled = true
binding.llSize.isEnabled = true
binding.llTime.isEnabled = true
}
largeFileList.addAll(it)
binding.progressbar.visibility = View.GONE
//adapter.notifyItemRangeInserted(bigFileList.size, it.size)
adapter.setData(largeFileList)
}
}
}, onEnd = {
LogEx.logDebug(TAG, "onEnd=调用")
binding.progressbar.visibility = View.GONE
adapter.setData(largeFileList)
if (largeFileList.isEmpty()) {
binding.ivEmpty.visibility = View.VISIBLE
}
})
}
data class Filter(
val keyName: String = "",
var isSelect: Boolean = false,
) {
companion object {
const val KEY_ALL_TYPES = "All types"
const val KEY_IMAGE = "Image"
const val KEY_VIDEO = "Video"
const val KEY_AUDIO = "Audio"
const val KEY_DOCUMENT = "Document"
const val KEY_ARCHIVES = "Archives"
const val KEY_APK = "APK"
const val KEY_OTHER_TYPES = "Other types"
const val KEY_SIZE_10_MB = "10 MB"
const val KEY_SIZE_50_MB = "50 MB"
const val KEY_SIZE_100_MB = "100 MB"
const val KEY_SIZE_500_MB = "500 MB"
const val KEY_SIZE_1_GB = "1 GB"
const val KEY_ALL_TIME = "All time"
const val KEY_1_WEEK = "1 week"
const val KEY_1_MONTH = "1 month"
const val KEY_3_MONTHS = "3 months"
const val KEY_6_MONTHS = "6 months"
const val KEY_1_YEAR = "1 year"
}
}
private fun playLottie(showFinish: (() -> Unit)? = null) {
binding.llLottie.isVisible = true
LogEx.logDebug(TAG, "playLottie")
binding.root.postDelayed({
AdmobUtils.showInterstitialAd(this) {
binding.llLottie.isVisible = false
showFinish?.invoke()
LogEx.logDebug(TAG, "playLottie finish")
}
}, 6000)
}
@SuppressLint("SetTextI18n")
private fun showPopupWindow(view: View, type: Int = 0, list: List<Filter>) {
val popupWindowView = layoutInflater.inflate(R.layout.popupwindow_file_filter, null)
val location = IntArray(2)
binding.llFilter.getLocationOnScreen(location)
val anchorBottom: Int = location[1] + binding.llFilter.height
val screenHeight: Int = getResources().displayMetrics.heightPixels
val height = screenHeight - anchorBottom + this.resources.getDimensionPixelSize(R.dimen.dp_30)
// val maxHeight = min(height.toDouble(), (screenHeight * 3 / 4).toDouble()).toInt() // 举例,最大高度为屏幕高度的 3/4
// val popupWindowHeight =
// ViewGroup.LayoutParams.MATCH_PARENT
val popupWindow = PopupWindow(popupWindowView, view.width, height, true)
popupWindow.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT))
popupWindow.isOutsideTouchable = true
popupWindow.isTouchable = true
val pBinding = PopupwindowFileFilterBinding.bind(popupWindowView)
if (type == 0) {
pBinding.tvTitle.visibility = View.GONE
}
if (type == 1) {
pBinding.tvTitle.text = "Large than"
}
if (type == 2) {
pBinding.tvTitle.text = "Earlier than"
}
class KKK(view: View) : RecyclerView.ViewHolder(view)
val filterAdapter = object : RecyclerView.Adapter<KKK>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): KKK {
return KKK(R.layout.item_file_filter.inflate(parent))
}
override fun getItemCount(): Int {
return list.size
}
override fun onBindViewHolder(holder: KKK, position: Int) {
val binding = ItemFileFilterBinding.bind(holder.itemView)
val context = holder.itemView.context
val data = list[position]
binding.tv.text = data.keyName
if (data.isSelect) {
binding.tv.setTextColor(ContextCompat.getColor(context, R.color.color_365cd9))
binding.ivSelector.visibility = View.VISIBLE
} else {
binding.tv.setTextColor(ContextCompat.getColor(context, R.color.color_666666))
binding.ivSelector.visibility = View.INVISIBLE
}
binding.root.setOnClickListener {
val old = list.find { it.isSelect }
if (data == old) {
return@setOnClickListener
} else {
data.isSelect = true
old?.isSelect = false
filterTypeList(type, data.keyName)
popupWindow.dismiss()
}
}
}
}
pBinding.rv.adapter = filterAdapter
pBinding.root.setOnClickListener {
popupWindow.dismiss()
}
popupWindow.showAsDropDown(view)
}
private fun filterTypeList(filterType: Int, keyName: String) {
LogEx.logDebug(TAG, "type=$filterType keyName=$keyName")
if (filterType == 0) {
binding.tvType.text = keyName
fileType = keyName
}
if (filterType == 1) {
binding.tvSize.text = keyName
filterSize = keyName
}
if (filterType == 2) {
binding.tvTime.text = keyName
filterTime = keyName
}
val tempList1 = when (fileType) {
KEY_ALL_TYPES -> largeFileList
KEY_IMAGE -> largeFileList.filter { it.isImage() }
KEY_VIDEO -> largeFileList.filter { it.isVideo() }
KEY_AUDIO -> largeFileList.filter { it.isAudio() }
KEY_DOCUMENT -> largeFileList.filter { it.isDoc() }
KEY_ARCHIVES -> largeFileList.filter { it.isZip() }
KEY_APK -> largeFileList.filter { it.isApk() }
KEY_OTHER_TYPES -> largeFileList.filter { it.isOther() }
else -> largeFileList
}
val tempList2 = when (filterSize) {
KEY_SIZE_10_MB -> tempList1.filter { it.size > 1024 * 1024 * 10 }
KEY_SIZE_50_MB -> tempList1.filter { it.size > 1024 * 1024 * 50 }
KEY_SIZE_100_MB -> tempList1.filter { it.size > 1024 * 1024 * 100 }
KEY_SIZE_500_MB -> tempList1.filter { it.size > 1024 * 1024 * 500 }
KEY_SIZE_1_GB -> tempList1.filter { it.size > 1024 * 1024 * 1024 }
else -> tempList1
}
val tempList3 = when (filterTime) {
KEY_ALL_TIME -> tempList2
KEY_1_WEEK -> tempList2.filter {
val flag = TimeUtils.isDaysAgo(it.time, 7)
// val time = simpleDateFormat.format(it.time)
// LogEx.logDebug(TAG, "filterTime=$filterTime file.time=$time flag=$flag")
flag
}
KEY_1_MONTH -> tempList2.filter {
val flag = TimeUtils.isDaysAgo(it.time, 30)
// val time = simpleDateFormat.format(it.time)
// LogEx.logDebug(TAG, "filterTime=$filterTime file.time=$time flag=$flag")
flag
}
KEY_3_MONTHS -> tempList2.filter { TimeUtils.isDaysAgo(it.time, 90) }
KEY_6_MONTHS -> tempList2.filter { TimeUtils.isDaysAgo(it.time, 180) }
KEY_1_YEAR -> tempList2.filter { TimeUtils.isDaysAgo(it.time, 365) }
else -> tempList2
}
adapter.setData(tempList3)
}
}
\ No newline at end of file
package com.test.basd.smartjunkcleaner.activity
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.graphics.Color
import android.graphics.Typeface
import androidx.fragment.app.Fragment
import androidx.lifecycle.lifecycleScope
import androidx.viewpager2.adapter.FragmentStateAdapter
import androidx.viewpager2.widget.ViewPager2
import com.blankj.utilcode.util.AppUtils
import com.blankj.utilcode.util.BarUtils
import com.test.basd.smartjunkcleaner.databinding.ActivityMainBinding
import com.test.basd.smartjunkcleaner.fragment.HomeFragment
import com.test.basd.smartjunkcleaner.fragment.ToolsFragment
import com.test.basd.smartjunkcleaner.helps.BaseActivity
import com.test.basd.smartjunkcleaner.helps.ads.AdmobUtils
import com.test.basd.smartjunkcleaner.view.RateStarPop
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
class MainActivity : BaseActivity<ActivityMainBinding>() {
override val isLightMode = true
override val binding: ActivityMainBinding by lazy {
ActivityMainBinding.inflate(layoutInflater)
}
private val homeFragment by lazy {
HomeFragment()
}
private val toolsFragment by lazy {
ToolsFragment()
}
private val fragments by lazy {
mutableListOf(homeFragment, toolsFragment)
}
private lateinit var batteryReceiver: BatteryReceiver
override fun initView() {
BarUtils.setStatusBarColor(this, Color.TRANSPARENT)
AdmobUtils.loadNativeAd()
// binding.root.updatePadding(top = BarUtils.getStatusBarHeight())
binding.idVp.run {
adapter = object : FragmentStateAdapter(this@MainActivity) {
override fun getItemCount(): Int {
return fragments.size
}
override fun createFragment(position: Int): Fragment {
return fragments[position]
}
}
}
binding.idVp.registerOnPageChangeCallback(object :
ViewPager2.OnPageChangeCallback() {
override fun onPageSelected(position: Int) {
setPage(position)
}
})
binding.idBottom1.setOnClickListener {
binding.idVp.currentItem = 0
}
binding.idBottom2.setOnClickListener {
binding.idVp.currentItem = 1
}
batteryReceiver = BatteryReceiver()
val filter = IntentFilter(Intent.ACTION_BATTERY_CHANGED)
registerReceiver(batteryReceiver, filter)
}
private fun setPage(p: Int) {
binding.idBottom1.isSelected = false
binding.idBottom2.isSelected = false
binding.idTv1.typeface = Typeface.DEFAULT
binding.idTv2.typeface = Typeface.DEFAULT
when (p) {
0 -> {
binding.idBottom1.isSelected = true
binding.idTv1.typeface = Typeface.DEFAULT_BOLD
}
1 -> {
binding.idBottom2.isSelected = true
binding.idTv2.typeface = Typeface.DEFAULT_BOLD
}
}
}
var level = 0
private inner class BatteryReceiver : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
val current = intent?.extras!!.getInt("level")
val total = intent.extras!!.getInt("scale")
level = current * 100 / total
}
}
override fun onDestroy() {
super.onDestroy()
unregisterReceiver(batteryReceiver)
}
override fun onResume() {
super.onResume()
if (AppUtils.isAppForeground()) {
lifecycleScope.launch(Dispatchers.Main) {
delay(1000)
RateStarPop.show(this@MainActivity)
}
}
}
}
\ No newline at end of file
package com.test.basd.smartjunkcleaner.activity
import android.annotation.SuppressLint
import android.app.usage.NetworkStats
import android.app.usage.NetworkStatsManager
import android.content.Context
import android.graphics.Color
import android.net.ConnectivityManager
import android.util.SparseLongArray
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.ProgressBar
import android.widget.TextView
import androidx.activity.OnBackPressedCallback
import androidx.core.util.forEach
import androidx.core.view.isVisible
import androidx.core.view.updatePadding
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.blankj.utilcode.util.AppUtils
import com.blankj.utilcode.util.BarUtils
import com.test.basd.smartjunkcleaner.R
import com.test.basd.smartjunkcleaner.bean.TrafficBean
import com.test.basd.smartjunkcleaner.databinding.ActivityLayoutNetworkBinding
import com.test.basd.smartjunkcleaner.helps.BaseActivity
import com.test.basd.smartjunkcleaner.helps.ConfigHelper
import com.test.basd.smartjunkcleaner.helps.KotlinExt.toFormatSize
import com.test.basd.smartjunkcleaner.helps.NetworkStatsHelper
import com.test.basd.smartjunkcleaner.helps.ads.AdmobUtils
import com.test.basd.smartjunkcleaner.view.DialogViews
class NetWorkActivity : BaseActivity<ActivityLayoutNetworkBinding>() {
private val wifiList = mutableListOf<TrafficBean>()
private val mobileList = mutableListOf<TrafficBean>()
private val AllList = mutableListOf<TrafficBean>()
private var startTime = NetworkStatsHelper.getTimesMonthmorning()
private var types = 2
override fun onStart() {
super.onStart()
checkAccesSettings()
}
var isplay = 0
override fun onAccesSettingsResult(isGranted: Boolean) {
binding.idLlNoAccpermion.isVisible = !isGranted
if (isGranted) {
isplay += 1
binding.idLlNetDh.isVisible = false
initList()
if (isplay == 1) {
playlottie()
}
} else {
finishToMain()
}
}
private fun playlottie() {
binding.idLlNetDh.isVisible = true
binding.idLottieNetwork.imageAssetsFolder = "wan_smax_gluo/images/"
binding.idLottieNetwork.setAnimation("wan_smax_gluo/data.json")
binding.idLottieNetwork.playAnimation()
binding.root.postDelayed({
AdmobUtils.showInterstitialAd(this) {
binding.idLlNetDh.isVisible = false
}
}, 4000)
}
override val binding: ActivityLayoutNetworkBinding by lazy {
ActivityLayoutNetworkBinding.inflate(layoutInflater)
}
override fun initView() {
BarUtils.setStatusBarLightMode(this, true)
BarUtils.setStatusBarColor(this, Color.TRANSPARENT)
binding.root.updatePadding(top = BarUtils.getStatusBarHeight())
binding.idTvThisMonth.isSelected = true
}
override fun initListener() {
binding.idBackNetwork.setOnClickListener {
onBackPressedDispatcher.onBackPressed()
}
onBackPressedDispatcher.addCallback(object : OnBackPressedCallback(true) {
override fun handleOnBackPressed() {
AdmobUtils.showInterstitialAd(this@NetWorkActivity) {
finishToMain()
}
}
})
binding.idGant.setOnClickListener {
checkAccesSettings(true)
}
binding.idTvThisMonth.setOnClickListener {
clickTop(0)
startTime = NetworkStatsHelper.getTimesMonthmorning()
setData()
}
binding.id30Day.setOnClickListener {
clickTop(1)
startTime = NetworkStatsHelper.getNeedTime(30)
setData()
}
binding.id24Hours.setOnClickListener {
clickTop(2)
startTime = NetworkStatsHelper.getNeedTime(1)
setData()
}
binding.idTvTypes.setOnClickListener {
DialogViews.showBothTypes(this) {
when (it) {
0 -> {
binding.idTvTypes.text = "Mobile Network"
types = 0
}
1 -> {
binding.idTvTypes.text = "Wi-Fi"
types = 1
}
2 -> {
binding.idTvTypes.text = "Both types"
types = 2
}
}
setData()
}
}
}
private fun clickTop(p: Int) {
listOf(binding.idTvThisMonth, binding.id30Day, binding.id24Hours).forEachIndexed { index, view ->
view.isSelected = p == index
}
}
private fun initList() {
setData()
// setMobileData(allMobide)
}
private fun setData(
) {
val allMobide = getNetworkStats(
ConnectivityManager.TYPE_MOBILE,
startTime,
System.currentTimeMillis()
)
val allWifi = getNetworkStats(
ConnectivityManager.TYPE_WIFI,
startTime, System.currentTimeMillis()
)
AllList.clear()
mobileList.clear()
wifiList.clear()
var mobileSize = 0L
allMobide.forEach { key, value ->
mobileSize += value
val appPkg = packageManager.getPackagesForUid(key)
if (appPkg?.isNotEmpty() == true) {
val bean = TrafficBean(1, appPkg[0], 0, value)
mobileList.add(bean)
}
}
mobileList.sortByDescending { it.mobileUsed }
binding.idMobileLiuliang.text = mobileSize.toFormatSize()
var wifi = 0L
allWifi.forEach { key, value ->
wifi += value
val appPkg = packageManager.getPackagesForUid(key)
if (appPkg?.isNotEmpty() == true) {
val bean = TrafficBean(2, appPkg[0], value, 0)
wifiList.add(bean)
}
}
binding.idWifiLiuliang.text = wifi.toFormatSize()
wifiList.sortByDescending { it.wifiUsed }
// mobileList.addAll(wifiList)
// AllList.addAll(mobileList)
val map = hashMapOf<String, TrafficBean>()
if (types != 0) {
for (bean in wifiList) {
map[bean.packageName] = bean
}
}
if (types != 1) {
for (bean in mobileList) {
if (map.containsKey(bean.packageName)) {
val oldBean = map[bean.packageName]
oldBean?.type = oldBean?.type?.plus(bean.type) ?: bean.type
oldBean?.mobileUsed =
oldBean?.mobileUsed?.plus(bean.mobileUsed) ?: bean.mobileUsed
oldBean?.wifiUsed = oldBean?.wifiUsed?.plus(bean.wifiUsed) ?: bean.wifiUsed
} else {
map[bean.packageName] = bean
}
}
}
for (value in map.values) {
AllList.add(value)
}
AllList.sortByDescending { it.mobileUsed + it.wifiUsed }
AllList.removeAll(AllList.filter {
it.packageName == this.packageName
})
// AllList.sortByDescending { it.wifiUsed }
// Log.e("MXL", "所有的数据: " + AllList.size + AllList.toString())
if (AllList.isNotEmpty()) {
binding.idTvNoData.isVisible = false
binding.idRlNetworkTra.run {
layoutManager = LinearLayoutManager(context)
adapter = mAdapter
}
} else {
mAdapter.notifyDataSetChanged()
binding.idTvNoData.isVisible = true
}
}
fun getNetworkStats(uid: Int, start: Long, end: Long): SparseLongArray {
val result = SparseLongArray()
val bucket = NetworkStats.Bucket()
try {
val manager =
this.getSystemService(Context.NETWORK_STATS_SERVICE) as NetworkStatsManager
val stats = manager.querySummary(uid, null, start, end)
while (stats.hasNextBucket()) {
stats.getNextBucket(bucket)
val currentUid = bucket.uid
val txBytes = bucket.txBytes
val rxBytes = bucket.rxBytes
val totalBytes = txBytes + rxBytes
val existingBytes = result[currentUid, 0]
if (existingBytes == 0L) {
result.put(currentUid, totalBytes)
} else {
result.put(currentUid, totalBytes + existingBytes)
}
}
var b = 0L
result.forEach { key, value ->
val a = packageManager.getPackagesForUid(key)
if (a?.isNotEmpty() == true) {
b += value
}
}
stats.close()
} catch (_: Exception) {
}
return result
}
private val mAdapter by lazy {
class ViewHolder(view: View) : RecyclerView.ViewHolder(view) {
val ivImage: ImageView
val tvName: TextView
val tvTraffic: TextView
val wifiPro: ProgressBar
val mobilePro: ProgressBar
val tvstop: TextView
init {
ivImage = view.findViewById(R.id.id_icon)
tvName = view.findViewById(R.id.id_name)
tvTraffic = view.findViewById(R.id.id_tra_used)
wifiPro = view.findViewById(R.id.id_wifi_pro)
mobilePro = view.findViewById(R.id.id_mobile_pro)
tvstop = view.findViewById(R.id.id_tv_stop)
}
}
object : RecyclerView.Adapter<ViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val view = LayoutInflater.from(this@NetWorkActivity).inflate(
R.layout.item_app_used_traffic, parent, false
)
return ViewHolder(view)
}
override fun getItemCount() = AllList.size
@SuppressLint("SetTextI18n")
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val c = ConfigHelper.appList?.firstOrNull {
it.packageName == AllList[position].packageName
}
holder.ivImage.setImageDrawable(c?.icon)
holder.tvName.text = c?.name
holder.tvTraffic.text =
(AllList[position].wifiUsed + AllList[position].mobileUsed).toFormatSize()
holder.wifiPro.max = (AllList[0].wifiUsed + AllList[0].mobileUsed).toInt()
holder.mobilePro.max = (AllList[0].wifiUsed + AllList[0].mobileUsed).toInt()
holder.wifiPro.progress =
(AllList[position].wifiUsed + AllList[position].mobileUsed).toInt()
holder.mobilePro.progress = (AllList[position].mobileUsed).toInt()
holder.tvstop.isEnabled =
NetworkStatsHelper.canStop(this@NetWorkActivity, AllList[position].packageName)
holder.tvstop.setOnClickListener {
AppUtils.launchAppDetailsSettings(AllList[position].packageName)
}
holder.itemView.setOnClickListener {
}
}
override fun onBindViewHolder(
holder: ViewHolder,
position: Int,
payloads: MutableList<Any>
) {
super.onBindViewHolder(holder, position, payloads)
}
}
}
}
\ No newline at end of file
package com.test.basd.smartjunkcleaner.activity
import android.Manifest
import android.annotation.SuppressLint
import android.content.Intent
import android.content.pm.PackageInfo
import android.content.pm.PackageManager
import android.content.pm.PackageManager.NameNotFoundException
import android.net.Uri
import android.provider.Settings
import android.view.View
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import com.test.basd.smartjunkcleaner.R
import com.test.basd.smartjunkcleaner.adapter.PermissionAdapter
import com.test.basd.smartjunkcleaner.bean.AppBean
import com.test.basd.smartjunkcleaner.bean.PermissionBean
import com.test.basd.smartjunkcleaner.bean.PermissionBean.Companion.setDesc
import com.test.basd.smartjunkcleaner.bean.PermissionBean.Companion.setTittle
import com.test.basd.smartjunkcleaner.databinding.ActivityPermissionManagerBinding
import com.test.basd.smartjunkcleaner.helps.BaseActivity
import com.test.basd.smartjunkcleaner.helps.LogEx
import com.test.basd.smartjunkcleaner.view.PermissionFilterDialog.showPermissionFilterDialog
class PermissionManagerActivity : BaseActivity<ActivityPermissionManagerBinding>() {
override val isLightMode = true
private val TAG = "PermissionManagerActivity"
private var appBean: AppBean? = null
private val appNormalPermissions = arrayListOf<PermissionBean>()
private val appSensitivePermissions = arrayListOf<PermissionBean>()
private lateinit var normalAdapter: PermissionAdapter
private lateinit var sensitiveAdapter: PermissionAdapter
private var normalExpand = true
private var sensitiveExpand = true
private var isAllPermission = true//是否所有权限
override val binding: ActivityPermissionManagerBinding by lazy {
ActivityPermissionManagerBinding.inflate(layoutInflater)
}
override fun initView() {
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main)) { v, insets ->
val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars())
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom)
insets
}
val json = intent.extras?.getString("AppBean")
LogEx.logDebug(TAG, "json=$json")
appBean = json?.let { AppBean.appBeanGson.fromJson(it, AppBean::class.java) }
LogEx.logDebug(TAG, "appBean=$appBean")
appBean?.apply {
val applicationInfo = packageManager.getApplicationInfo(this.pkg, PackageManager.GET_META_DATA)
binding.ivIcon.setImageDrawable(packageManager.getApplicationIcon(applicationInfo))
binding.tvName.text = this.appName
getAppPermissions(this.pkg)
}
}
override fun initListener() {
binding.flBack.setOnClickListener {
finish()
}
binding.tvAdministration.setOnClickListener {
val intent = Intent()
intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS)
intent.setData(Uri.parse("package:" + appBean?.pkg))
startActivity(intent)
}
binding.llNormal.setOnClickListener {
normalExpand = !normalExpand
setExpandCollapseNormal()
}
binding.llSensitive.setOnClickListener {
sensitiveExpand = !sensitiveExpand
setExpandCollapseSensitive()
}
binding.flFilter.setOnClickListener {
showPermissionFilterDialog(
allClick = {
if (!isAllPermission) {
changeAllPermission()
normalExpand = true
setExpandCollapseNormal()
}
isAllPermission = true
},
authorizedClick = {
if (isAllPermission) {
changeOnlyAuthorized()
sensitiveExpand = true
setExpandCollapseSensitive()
}
isAllPermission = false
}
)
}
}
private fun changeOnlyAuthorized() {
val list1 = appNormalPermissions.filter { it.isAuthorized }
normalAdapter.setData(list1)
binding.tvNormalNumber.text = list1.size.toString()
val list2 = appSensitivePermissions.filter { it.isAuthorized }
sensitiveAdapter.setData(list2)
binding.tvSensitiveNumber.text = list2.size.toString()
}
private fun changeAllPermission() {
normalAdapter.setData(appNormalPermissions)
binding.tvNormalNumber.text = appNormalPermissions.size.toString()
sensitiveAdapter.setData(appSensitivePermissions)
binding.tvSensitiveNumber.text = appSensitivePermissions.size.toString()
}
@SuppressLint("StringFormatMatches", "SetTextI18n")
private fun getAppPermissions(pkg: String) {
val pm = this.packageManager
val info: PackageInfo
try {
info = pm.getPackageInfo(pkg, PackageManager.GET_PERMISSIONS)
val packagePermissions = info.requestedPermissions
packagePermissions.forEach { permission ->
LogEx.logDebug(TAG, "permission=$permission")
val common = commonPermissions.find { it.permissionMatch == permission }
common?.let {
val ss = pm.checkPermission(it.permissionMatch, pkg) == PackageManager.PERMISSION_GRANTED
if (it.permissionMatch == Manifest.permission.INTERNET) {
LogEx.logDebug(TAG, "ss=$ss")
}
it.isAuthorized = ss
appNormalPermissions.add(it)
}
val sensitive = sensitivePermissions.find { it.permissionMatch == permission }
sensitive?.let {
it.isAuthorized = pm.checkPermission(it.permissionMatch, pkg) == PackageManager.PERMISSION_GRANTED
appSensitivePermissions.add(it)
}
}
} catch (e: NameNotFoundException) {
e.printStackTrace()
}
binding.tvDesc.text = "${appSensitivePermissions.size} Sensitive permissions"
appNormalPermissions.forEach {
it.setTittle()
it.setDesc()
}
sensitivePermissions.forEach {
it.setTittle()
it.setDesc()
}
binding.tvNormalNumber.text = appNormalPermissions.size.toString()
binding.tvSensitiveNumber.text = appSensitivePermissions.size.toString()
intiRv()
}
private fun intiRv() {
normalAdapter = PermissionAdapter()
binding.rvNormal.adapter = normalAdapter
normalAdapter.setData(appNormalPermissions)
sensitiveAdapter = PermissionAdapter()
binding.rvSensitive.adapter = sensitiveAdapter
sensitiveAdapter.setData(appSensitivePermissions)
setExpandCollapseSensitive()
setExpandCollapseNormal()
}
private fun setExpandCollapseSensitive() {
if (sensitiveExpand) {
binding.ivArrowSensitive.setImageResource(R.mipmap.hxiala_s)
binding.rvSensitive.visibility = View.VISIBLE
} else {
binding.ivArrowSensitive.setImageResource(R.mipmap.hxiala_x)
binding.rvSensitive.visibility = View.GONE
}
}
private fun setExpandCollapseNormal() {
if (normalExpand) {
binding.ivArrowNormal.setImageResource(R.mipmap.aa_xiala_s)
binding.rvNormal.visibility = View.VISIBLE
} else {
binding.ivArrowNormal.setImageResource(R.mipmap.aa_xiala_x)
binding.rvNormal.visibility = View.GONE
}
}
companion object {
@SuppressLint("InlinedApi")
val commonPermissions = arrayListOf(
PermissionBean(Manifest.permission.ACCESS_NETWORK_STATE),
PermissionBean(Manifest.permission.ACCESS_WIFI_STATE),
PermissionBean(Manifest.permission.BLUETOOTH),
PermissionBean(Manifest.permission.BLUETOOTH_ADMIN),
PermissionBean(Manifest.permission.BLUETOOTH_CONNECT),
PermissionBean(Manifest.permission.BLUETOOTH_PRIVILEGED),
PermissionBean(Manifest.permission.BLUETOOTH_SCAN),
PermissionBean(Manifest.permission.CHANGE_NETWORK_STATE),
PermissionBean(Manifest.permission.CHANGE_WIFI_STATE),
PermissionBean(Manifest.permission.CONTROL_LOCATION_UPDATES),
PermissionBean(Manifest.permission.DELETE_PACKAGES),
PermissionBean(Manifest.permission.INSTALL_PACKAGES),
PermissionBean(Manifest.permission.INTERNET),
PermissionBean(Manifest.permission.MANAGE_EXTERNAL_STORAGE),
PermissionBean(Manifest.permission.MANAGE_MEDIA),
PermissionBean(Manifest.permission.MANAGE_WIFI_NETWORK_SELECTION),
PermissionBean(Manifest.permission.MODIFY_AUDIO_SETTINGS),
PermissionBean(Manifest.permission.NFC),
PermissionBean(Manifest.permission.READ_EXTERNAL_STORAGE),
PermissionBean(Manifest.permission.READ_MEDIA_AUDIO),
PermissionBean(Manifest.permission.READ_MEDIA_IMAGES),
PermissionBean(Manifest.permission.READ_MEDIA_VIDEO),
PermissionBean(Manifest.permission.READ_SYNC_SETTINGS),
PermissionBean(Manifest.permission.READ_SYNC_STATS),
PermissionBean(Manifest.permission.REQUEST_DELETE_PACKAGES),
PermissionBean(Manifest.permission.UPDATE_PACKAGES_WITHOUT_USER_ACTION),
PermissionBean(Manifest.permission.USE_FINGERPRINT),
PermissionBean(Manifest.permission.WRITE_EXTERNAL_STORAGE),
PermissionBean(Manifest.permission.WRITE_SETTINGS),
PermissionBean(Manifest.permission.WRITE_SYNC_SETTINGS),
)
@SuppressLint("InlinedApi")
val sensitivePermissions = arrayListOf(
PermissionBean(Manifest.permission.ACCESS_BACKGROUND_LOCATION, isSensitive = true),
PermissionBean(Manifest.permission.ACCESS_COARSE_LOCATION, isSensitive = true),
PermissionBean(Manifest.permission.ACCESS_FINE_LOCATION, isSensitive = true),
PermissionBean(Manifest.permission.ACCESS_MEDIA_LOCATION, isSensitive = true),
PermissionBean(Manifest.permission.CAMERA, isSensitive = true),
PermissionBean(Manifest.permission.RECORD_AUDIO, isSensitive = true),
PermissionBean(Manifest.permission.RECEIVE_BOOT_COMPLETED, isSensitive = true),
PermissionBean(Manifest.permission.READ_CONTACTS, isSensitive = true),
PermissionBean(Manifest.permission.WRITE_CONTACTS, isSensitive = true),
PermissionBean(Manifest.permission.GET_ACCOUNTS, isSensitive = true),
PermissionBean(Manifest.permission.READ_PHONE_STATE, isSensitive = true),
PermissionBean(Manifest.permission.READ_PHONE_NUMBERS, isSensitive = true),
PermissionBean(Manifest.permission.CALL_PHONE, isSensitive = true),
PermissionBean(Manifest.permission.READ_CALL_LOG, isSensitive = true),
PermissionBean(Manifest.permission.WRITE_CALL_LOG, isSensitive = true),
PermissionBean(Manifest.permission.ANSWER_PHONE_CALLS, isSensitive = true),
PermissionBean(Manifest.permission.BODY_SENSORS, isSensitive = true),
PermissionBean(Manifest.permission.ACTIVITY_RECOGNITION, isSensitive = true),
PermissionBean(Manifest.permission.SEND_SMS, isSensitive = true),
PermissionBean(Manifest.permission.RECEIVE_SMS, isSensitive = true),
PermissionBean(Manifest.permission.READ_SMS, isSensitive = true),
PermissionBean(Manifest.permission.READ_CALENDAR, isSensitive = true),
PermissionBean(Manifest.permission.WRITE_CALENDAR, isSensitive = true),
PermissionBean(Manifest.permission.QUERY_ALL_PACKAGES, isSensitive = true),
)
}
}
\ No newline at end of file
package com.test.basd.smartjunkcleaner.activity
import android.app.Activity
import android.content.Intent
import android.os.Bundle
import android.os.Handler
import androidx.appcompat.app.AppCompatActivity
import com.test.basd.smartjunkcleaner.R
import com.test.basd.smartjunkcleaner.view.PermisonPopWindow
class PermissionTripActivity : AppCompatActivity() {
companion object {
fun launch(activity: Activity) {
activity.startActivity(
Intent(activity, PermissionTripActivity::class.java)
)
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_layout_permisson_trip)
initView()
}
private fun initView() {
Handler().postDelayed({
PermisonPopWindow(this) {
finish()
}.show()
}, 500)
}
}
\ No newline at end of file
package com.test.basd.smartjunkcleaner.activity
import android.animation.ValueAnimator
import android.content.Intent
import android.graphics.Color
import android.view.animation.LinearInterpolator
import androidx.activity.OnBackPressedCallback
import androidx.core.view.updatePadding
import com.blankj.utilcode.util.BarUtils
import com.test.basd.smartjunkcleaner.databinding.ActivityLayoutParepreScanBinding
import com.test.basd.smartjunkcleaner.helps.BaseActivity
import com.test.basd.smartjunkcleaner.helps.ads.AdmobUtils
/**
* 引导清理扫描页
*/
class PrepareScanActivity : BaseActivity<ActivityLayoutParepreScanBinding>() {
override val binding: ActivityLayoutParepreScanBinding by lazy {
ActivityLayoutParepreScanBinding.inflate(layoutInflater)
}
override fun initView() {
BarUtils.setStatusBarLightMode(this, true)
BarUtils.setStatusBarColor(this, Color.TRANSPARENT)
binding.root.updatePadding(top = BarUtils.getStatusBarHeight())
binding.idBackJunkScan.setOnClickListener {
onBackPressedDispatcher.onBackPressed()
}
onBackPressedDispatcher.addCallback(object : OnBackPressedCallback(true) {
override fun handleOnBackPressed() {
finishToMain()
}
})
}
override fun onStart() {
super.onStart()
checkPermission()
}
var isplay = 0
override fun onPermissionsResult(isGranted: Boolean) {
// Log.e("MXL", "权限回调: " + isGranted)
if (isGranted) {
isplay += 1
if (isplay == 1) {
playLottie()
}
} else {
finishToMain()
}
}
private fun playLottie() {
ValueAnimator.ofFloat(0f, 360f).run {
duration = 1000
repeatMode = ValueAnimator.RESTART
repeatCount = ValueAnimator.INFINITE
interpolator = LinearInterpolator()
addUpdateListener {
binding.idView1.rotation = it.animatedValue as Float
}
start()
}
ValueAnimator.ofFloat(0f, 360f).run {
duration = 1000
repeatMode = ValueAnimator.RESTART
repeatCount = ValueAnimator.INFINITE
interpolator = LinearInterpolator()
addUpdateListener {
binding.idView2.rotation = it.animatedValue as Float
}
start()
}
ValueAnimator.ofFloat(0f, 360f).run {
duration = 1000
repeatMode = ValueAnimator.RESTART
repeatCount = ValueAnimator.INFINITE
interpolator = LinearInterpolator()
addUpdateListener {
binding.idView3.rotation = it.animatedValue as Float
}
start()
}
binding.idJunkScan.imageAssetsFolder = "sao_smax_miao/images/"
binding.idJunkScan.setAnimation("sao_smax_miao/data.json")
binding.idJunkScan.playAnimation()
binding.root.postDelayed({
AdmobUtils.showInterstitialAd(this) {
startActivity(Intent(this, CleanJunkActivity::class.java))
finish()
}
}, 6000)
}
}
\ No newline at end of file
package com.test.basd.smartjunkcleaner.activity
import android.annotation.SuppressLint
import android.graphics.Color
import android.webkit.WebView
import android.webkit.WebViewClient
import androidx.core.view.updatePadding
import com.blankj.utilcode.util.BarUtils
import com.test.basd.smartjunkcleaner.databinding.ActivityLayoutWebPrivacyBinding
import com.test.basd.smartjunkcleaner.helps.BaseActivity
class PrivacyWebActivity : BaseActivity<ActivityLayoutWebPrivacyBinding>() {
override val binding: ActivityLayoutWebPrivacyBinding by lazy {
ActivityLayoutWebPrivacyBinding.inflate(layoutInflater)
}
@SuppressLint("SetJavaScriptEnabled")
override fun initView() {
BarUtils.setStatusBarLightMode(this, true)
BarUtils.setStatusBarColor(this, Color.TRANSPARENT)
binding.root.updatePadding(top = BarUtils.getStatusBarHeight())
binding.idWeb.settings.setJavaScriptEnabled(true)
binding.idWeb.webViewClient = object : WebViewClient() {
override fun shouldOverrideUrlLoading(view: WebView?, url: String?): Boolean {
view?.loadUrl(url!!)
return true
}
}
binding.idWeb.loadUrl("https://sites.google.com/view/cleanmastermax/home")
binding.idBack.setOnClickListener {
finishToMain()
}
}
}
\ No newline at end of file
package com.test.basd.smartjunkcleaner.activity
import android.annotation.SuppressLint
import android.app.AppOpsManager
import android.app.usage.UsageEvents
import android.app.usage.UsageStats
import android.app.usage.UsageStatsManager
import android.content.Context
import android.content.Intent
import android.content.pm.ApplicationInfo
import android.content.pm.PackageInfo
import android.content.pm.PackageManager
import android.net.Uri
import android.os.Build
import android.provider.Settings
import android.util.Log
import android.view.View
import androidx.activity.addCallback
import androidx.core.view.isVisible
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentActivity
import androidx.lifecycle.lifecycleScope
import androidx.viewpager2.adapter.FragmentStateAdapter
import com.google.android.material.tabs.TabLayoutMediator
import com.test.basd.smartjunkcleaner.bean.AppBean
import com.test.basd.smartjunkcleaner.fragment.LaunchesFragment
import com.test.basd.smartjunkcleaner.fragment.ScreenTimeFragment
import com.test.basd.smartjunkcleaner.helps.ActivityLauncher
import com.test.basd.smartjunkcleaner.helps.BaseActivity
import com.test.basd.smartjunkcleaner.helps.LogEx
import com.test.basd.smartjunkcleaner.helps.TimeUtils.TODAY_QUERY
import com.test.basd.smartjunkcleaner.helps.TimeUtils.YESTERDAY_QUERY
import com.test.basd.smartjunkcleaner.helps.TimeUtils.timePair
import com.test.basd.smartjunkcleaner.helps.ads.AdmobUtils
import com.test.basd.smartjunkcleaner.helps.recentapp.LaunchTimeStat
import com.test.basd.smartjunkcleaner.databinding.ActivityRecentAppBinding
import com.test.basd.smartjunkcleaner.helps.recentapp.ScreenTimeStat
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import org.json.JSONObject
class RecentAppActivity : BaseActivity<ActivityRecentAppBinding>() {
override val isLightMode = true
private val TAG = "RecentAppActivity"
private val pages = arrayListOf<Fragment>()
private lateinit var pagerAdapter: ScreenSlidePagerAdapter
private lateinit var tabs: Array<String>
private var dataList = ArrayList<AppBean>()
private lateinit var context: Context
private lateinit var launcher: ActivityLauncher
override val binding: ActivityRecentAppBinding by lazy {
ActivityRecentAppBinding.inflate(layoutInflater)
}
override fun initView() {
launcher = ActivityLauncher(this)
context = this
initPage()
intTab()
if (checkUsageAccessSettings()) {
Log.e("MXL", "onCreate: " + checkUsageAccessSettings())
intData()
} else {
binding.llContent.visibility = View.GONE
binding.flPermission.visibility = View.VISIBLE
}
playLottie()
}
override fun initListener() {
binding.flBack.setOnClickListener {
AdmobUtils.showInterstitialAd(this) {
finishToMain()
}
}
onBackPressedDispatcher.addCallback {
LogEx.logDebug(TAG, "onBackPressedDispatcher")
AdmobUtils.showInterstitialAd(this@RecentAppActivity) {
finishToMain()
}
}
binding.tvGrand.setOnClickListener {
val intent = Intent(Settings.ACTION_USAGE_ACCESS_SETTINGS)
intent.addCategory("android.intent.category.DEFAULT")
intent.data = Uri.parse("package:${context.packageName}")
launcher.launch(intent) {
LogEx.logDebug(TAG, "launcher callback")
val obj = JSONObject()
obj.put("activity", javaClass.simpleName)
if (checkUsageAccessSettings()) {
binding.flPermission.visibility = View.GONE
binding.llContent.visibility = View.VISIBLE
intData()
} else {
finishToMain()
}
}
}
}
private fun playLottie(showFinish: (() -> Unit)? = null) {
binding.llLottie.isVisible = true
binding.lottie.imageAssetsFolder = "zui_smax_jing/images/"
binding.lottie.setAnimation("zui_smax_jing/data.json")
binding.lottie.playAnimation()
binding.root.postDelayed({
AdmobUtils.showInterstitialAd(this) {
binding.llLottie.isVisible = false
showFinish?.invoke()
}
}, 6000)
}
private fun intData() {
CoroutineScope(Dispatchers.IO).launch {
//测试打印数据
// printEventList(context, lifecycleScope).await()
//构建AppData列表
initAppData()
//设置启动数据
setLaunchesData()
//屏幕时间数据
setScreenTimeData()
//更新UI数据
launch(Dispatchers.Main) {
updateFragmentData(0, 1)
}
}
}
private fun initAppData() {
dataList.clear()
val pm = packageManager
val packages = pm.getInstalledPackages(0)
packages.forEach { app ->
if (isLaunchApp(context, app)) {
val appBean = AppBean(
app.applicationInfo.loadIcon(pm),
app.applicationInfo.loadLabel(pm).toString(),
app.applicationInfo.packageName,
false
)
//安装时间
setAppInstallTime(appBean, context)
dataList.add(appBean)
}
}
}
private fun updateFragmentData(vararg position: Int) {
if (position.contains(0)) {
val launchesFragment = pages[0] as LaunchesFragment
launchesFragment.setAppUseData(dataList)
}
if (position.contains(1)) {
val screenTimeFragment = pages[1] as ScreenTimeFragment
screenTimeFragment.setScreenData(dataList)
}
}
private fun setScreenTimeData(queryTime: Int = TODAY_QUERY) {
val pair = timePair(queryTime)
val screenTimeMap = ScreenTimeStat(pair.first, pair.second).run(context)
//key=com.vivo.appfilter pkg=com.vivo.appfilter f30766a=防拉起服务 f30767b=com.vivo.appfilter c=1132
dataList.forEach { app ->
if (screenTimeMap.keys.contains(app.pkg)) {
val value = screenTimeMap[app.pkg]
value?.let { b ->
app.screenTime = b.c
}
}
}
}
private fun setLaunchesData(queryTime: Int = TODAY_QUERY) {
val tempList = arrayListOf<AppBean>()
tempList.addAll(dataList)
//后台启动数据
dataList = getAppUseAnalyze(context, queryTime, tempList)
//前台启动数据
val pair = timePair(queryTime)
val ast = LaunchTimeStat(pair.first, pair.second)
val hashMap = ast.run(context)
dataList.forEach { app ->
if (hashMap.keys.contains(app.pkg)) {
val value = hashMap[app.pkg]
if (value != null) {
val tempList2 = arrayListOf<UsageEvents.Event>()
tempList2.addAll(value)
app.usageEvents?.also { evens -> tempList2.addAll(evens) }
app.usageEvents = tempList2
}
}
}
}
fun changeTimeRefresh(queryTime: Int, fragmentPosition: Int) {
lifecycleScope.launch(Dispatchers.IO) {
initAppData()
//设置启动数据
setLaunchesData(queryTime)
//屏幕时间数据
setScreenTimeData(queryTime)
//更新UI数据
launch(Dispatchers.Main) { updateFragmentData(fragmentPosition) }
}
}
private fun intTab() {
//有个问题结合ViewPager2就无法用静态xml中的TabItem
tabs = arrayOf("LAUNCHES", "SCREEN TIME")
TabLayoutMediator(binding.tabLayout, binding.viewpager2) { tab, position ->
tab.text = tabs[position]
binding.viewpager2.currentItem = position
}.attach()
binding.viewpager2.currentItem = 0
}
private fun initPage() {
pages.add(LaunchesFragment())
pages.add(ScreenTimeFragment())
pagerAdapter = ScreenSlidePagerAdapter(this)
binding.viewpager2.adapter = pagerAdapter
}
inner class ScreenSlidePagerAdapter(fa: FragmentActivity) : FragmentStateAdapter(fa) {
override fun getItemCount(): Int = pages.size
override fun createFragment(position: Int): Fragment = pages[position]
}
private fun checkUsageAccessSettings(): Boolean {
val appOpsManager = getSystemService(Context.APP_OPS_SERVICE) as AppOpsManager
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
appOpsManager.unsafeCheckOpNoThrow(
AppOpsManager.OPSTR_GET_USAGE_STATS,
android.os.Process.myUid(),
this.packageName
) == AppOpsManager.MODE_ALLOWED
} else {
appOpsManager.checkOpNoThrow(
AppOpsManager.OPSTR_GET_USAGE_STATS,
android.os.Process.myUid(),
this.packageName
) == AppOpsManager.MODE_ALLOWED
}
}
private fun isLaunchApp(context: Context, app: PackageInfo, filterSystem: Boolean = true): Boolean {
val flagSystem = (app.applicationInfo.flags and ApplicationInfo.FLAG_SYSTEM) < 1
val flag = if (filterSystem) flagSystem else true
return flag && app.applicationInfo.packageName != context.packageName
}
private fun setAppInstallTime(appBean: AppBean, context: Context) {
//安装时间
var firstInstallTime = 0L
try {
val packageManager = context.packageManager
val packageInfo: PackageInfo = packageManager.getPackageInfo(appBean.pkg, 0)
//应用装时间
firstInstallTime = packageInfo.firstInstallTime
//应用最后一次更新时间
val lastUpdateTime = packageInfo.lastUpdateTime
// LogEx.logDebug(TAG, "lastUpdateTime=$lastUpdateTime")
} catch (e: PackageManager.NameNotFoundException) {
e.printStackTrace()
}
appBean.installTime = firstInstallTime
}
/**
* 设置最近使用分析数据
*/
private fun getAppUseAnalyze(
context: Context,
queryTime: Int = YESTERDAY_QUERY,
dataList: List<AppBean>,
): ArrayList<AppBean> {
val launchList = arrayListOf<AppBean>()
val pair = timePair(queryTime)
val startTime: Long = pair.first
val endTime: Long = pair.second
val stateList = getUsageList(context, startTime, endTime)
val evenMap = getEventList2(context, startTime, endTime)
// evenList.forEach { event -> printEvent(event) }
// stateList.forEach { state -> printState(state) }
dataList.forEach { app ->
//这里只使用后台启动数据
//FOREGROUND_SERVICE_START 19
app.usageEvents = evenMap[app.pkg]?.filter { it.eventType == 19 }
app.usageStats = stateList.filter { it.packageName == app.pkg }
}
launchList.addAll(dataList)
return launchList
}
/**
* 前台运行App+后台运行App=总的启动App
*/
fun getUsageList(context: Context, startTime: Long, endTime: Long): ArrayList<UsageStats> {
val list = arrayListOf<UsageStats>()
val mUsmManager = context.getSystemService(Context.USAGE_STATS_SERVICE) as UsageStatsManager
val map = mUsmManager.queryAndAggregateUsageStats(startTime, endTime)
map.values.forEach { stats ->
if (stats.totalTimeInForeground > 0) {
list.add(stats)
} else {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
if (stats.totalTimeForegroundServiceUsed > 0) {
list.add(stats)
}
}
}
}
return list
}
@SuppressLint("InlinedApi")
fun getEventList2(
context: Context,
startTime: Long,
endTime: Long,
): HashMap<String, ArrayList<UsageEvents.Event>> {
val mUsmManager = context.getSystemService(Context.USAGE_STATS_SERVICE) as UsageStatsManager
//链表记录所有的Event.快速查找
val allList = ArrayList<UsageEvents.Event>()
try {
val events = mUsmManager.queryEvents(startTime, endTime)
while (events.hasNextEvent()) {
val event = UsageEvents.Event()
events.getNextEvent(event)
val flag2 =
(event.eventType != UsageEvents.Event.ACTIVITY_STOPPED) && (event.eventType != UsageEvents.Event.STANDBY_BUCKET_CHANGED)
if (flag2) {
allList.add(event)
}
}
} catch (e: Exception) {
LogEx.logDebug(TAG, "Exception ${e.printStackTrace()}")
e.printStackTrace()
}
//记录数据结构
val hashMap = HashMap<String, ArrayList<UsageEvents.Event>>()
allList.forEachIndexed { index, event ->
//例子:CCAAVCSAABBBCDEFGABCDEFAABB
//过滤的结果
//CAASAAGAFAA
val list = hashMap[event.packageName]
if (list == null) {
hashMap[event.packageName] = arrayListOf()
}
//上个Event是否存在
var lastEvent: UsageEvents.Event? = null
val lastIndex = index - 1
if (index > 0) {
lastEvent = allList[lastIndex]
}
//上一个Event是否相同包
if (lastEvent != null && lastEvent.packageName != event.packageName) {
list?.add(lastEvent)
}
list?.add(event)
}
return hashMap
}
}
\ No newline at end of file
package com.test.basd.smartjunkcleaner.activity
import android.graphics.Color
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import androidx.activity.OnBackPressedCallback
import androidx.core.view.isVisible
import androidx.core.view.updatePadding
import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.blankj.utilcode.util.BarUtils
import com.blankj.utilcode.util.FileUtils
import com.blankj.utilcode.util.ToastUtils
import com.bumptech.glide.Glide
import com.test.basd.smartjunkcleaner.R
import com.test.basd.smartjunkcleaner.bean.ImageDataBean
import com.test.basd.smartjunkcleaner.databinding.ActivityLayoutRepeaterdPhotoBinding
import com.test.basd.smartjunkcleaner.helps.BaseActivity
import com.test.basd.smartjunkcleaner.helps.BaseApplication
import com.test.basd.smartjunkcleaner.helps.FileHelps
import com.test.basd.smartjunkcleaner.helps.ads.AdmobUtils
class RepeaterdPhotoActivity: BaseActivity<ActivityLayoutRepeaterdPhotoBinding>() {
private var isSelectAll = false
override val binding: ActivityLayoutRepeaterdPhotoBinding by lazy {
ActivityLayoutRepeaterdPhotoBinding.inflate(layoutInflater)
}
override fun initView() {
BarUtils.setStatusBarLightMode(this, true)
BarUtils.setStatusBarColor(this, Color.TRANSPARENT)
binding.root.updatePadding(top = BarUtils.getStatusBarHeight())
binding.idTvDelete.isEnabled = false
initListener()
}
override fun initListener(){
binding.idBack.setOnClickListener {
onBackPressedDispatcher.onBackPressed()
}
onBackPressedDispatcher.addCallback(object : OnBackPressedCallback(true) {
override fun handleOnBackPressed() {
AdmobUtils.showInterstitialAd(this@RepeaterdPhotoActivity){
finishToMain()
}
}
})
binding.idImgSelect.setOnClickListener {
if (isSelectAll) {
isSelectAll = false
binding.idTvAll.text = "All"
binding.idImgSelect.setImageResource(R.mipmap.xuan_3)
} else {
isSelectAll = true
binding.idTvAll.text = "Cancel"
binding.idImgSelect.setImageResource(R.mipmap.danxuanxe)
}
fileList.forEach { it.isSelect = isSelectAll }
mAdapter.notifyItemRangeChanged(0, fileList.size, 1)
setTvStatus()
}
binding.idTvDelete.setOnClickListener {
AdmobUtils.showInterstitialAd(this){
val list = fileList.toList().filter { it.isSelect }
if (list.isNotEmpty() && list.all { FileUtils.delete(it.path) }) {
fileList.removeAll(list)
FileHelps.similarImageList.removeAll(list)
mAdapter.notifyDataSetChanged()
setTvStatus()
ToastUtils.showShort("Delete Successful")
} else {
ToastUtils.showShort("Delete failed.")
}
}
}
}
private var fileList = mutableListOf<ImageDataBean>()
override fun onStart() {
super.onStart()
checkPermission()
}
override fun onPermissionsResult(isGranted: Boolean) {
if(isGranted){
FileHelps.getImageFiles {
AdmobUtils.showInterstitialAd(this)
binding.idLlScaning.isVisible = false
binding.idLlResult.isVisible = true
fileList = FileHelps.similarImageList
binding.idTvNoData.isVisible = fileList.size == 0
binding.idTvDelete.isVisible = fileList.size > 0
binding.idTvAll.isVisible = fileList.size > 0
binding.idImgSelect.isVisible = fileList.size > 0
binding.idRlList.layoutManager = GridLayoutManager(this, 3)
binding.idRlList.adapter = mAdapter
}
}else{
finishToMain()
}
}
private fun setTvStatus() {
val it = fileList.filter { it.isSelect }
binding.idTvDelete.isEnabled = it.isNotEmpty()
}
private val mAdapter by lazy {
class ViewHolder(view: View) : RecyclerView.ViewHolder(view) {
val ivImage: ImageView
val ivSelect: ImageView
init {
ivImage = view.findViewById(R.id.iv_image)
ivSelect = view.findViewById(R.id.id_img_sel)
}
}
object : RecyclerView.Adapter<ViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val view = LayoutInflater.from(this@RepeaterdPhotoActivity).inflate(
R.layout.item_layout_repeater_photo, parent, false
)
return ViewHolder(view)
}
override fun getItemCount() = fileList.size
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val item = fileList[position]
Glide.with(BaseApplication.context).load(item.path).into(holder.ivImage)
holder.ivSelect.isVisible = true
holder.itemView.setOnClickListener {
item.isSelect = !item.isSelect
notifyItemChanged(position, 1)
if (fileList.all { it.isSelect }) {
isSelectAll = true
binding.idTvAll.text = "Cancel"
binding.idImgSelect.setImageResource(R.mipmap.danxuanxe)
} else {
isSelectAll = false
binding.idTvAll.text = "All"
binding.idImgSelect.setImageResource(R.mipmap.xuan_3)
}
setTvStatus()
}
}
override fun onBindViewHolder(
holder: ViewHolder,
position: Int,
payloads: MutableList<Any>
) {
super.onBindViewHolder(holder, position, payloads)
val item = fileList[position]
holder.ivSelect.setImageResource(
if (item.isSelect) {
R.mipmap.danxuanxe
} else {
R.mipmap.xuan_3
}
)
}
}
}
}
\ No newline at end of file
package com.test.basd.smartjunkcleaner.activity
import android.content.Intent
import android.graphics.Color
import androidx.activity.OnBackPressedCallback
import androidx.core.view.updatePadding
import com.blankj.utilcode.util.BarUtils
import com.blankj.utilcode.util.SPUtils
import com.test.basd.smartjunkcleaner.activity.photocompress.photo.StartCompressionPhotoActivity
import com.test.basd.smartjunkcleaner.adapter.AppFunctionAdapter
import com.test.basd.smartjunkcleaner.databinding.ActivityLayoutResultBinding
import com.test.basd.smartjunkcleaner.helps.BaseActivity
import com.test.basd.smartjunkcleaner.helps.KotlinExt.toFormatSize
import com.test.basd.smartjunkcleaner.notificationclean.NotificationCleanActivity
import com.test.basd.smartjunkcleaner.notificationclean.NotificationGuestActivity
import com.test.basd.smartjunkcleaner.view.AFunOb.APP_MANAGER
import com.test.basd.smartjunkcleaner.view.AFunOb.APP_SPEED
import com.test.basd.smartjunkcleaner.view.AFunOb.BATTERY_INFO
import com.test.basd.smartjunkcleaner.view.AFunOb.BATTERY_OPTIMIZER
import com.test.basd.smartjunkcleaner.view.AFunOb.EMPTY_FILE_CLEANER
import com.test.basd.smartjunkcleaner.view.AFunOb.JUNK_CLEANER
import com.test.basd.smartjunkcleaner.view.AFunOb.LARGE_FILE_CLEANER
import com.test.basd.smartjunkcleaner.view.AFunOb.NETWORK_TRAFFIC
import com.test.basd.smartjunkcleaner.view.AFunOb.NOTIFICATION_CLEANER
import com.test.basd.smartjunkcleaner.view.AFunOb.PHOTO_COMPRESS
import com.test.basd.smartjunkcleaner.view.AFunOb.RECENT_APP_USAGE
import com.test.basd.smartjunkcleaner.view.AFunOb.SIMILAR_PHOTOS
class ResultActivity : BaseActivity<ActivityLayoutResultBinding>() {
override val binding: ActivityLayoutResultBinding by lazy {
ActivityLayoutResultBinding.inflate(layoutInflater)
}
private lateinit var adapter: AppFunctionAdapter
override fun initView() {
BarUtils.setStatusBarColor(this, Color.TRANSPARENT)
binding.clTop.updatePadding(top = BarUtils.getStatusBarHeight())
adapter = AppFunctionAdapter {
when (it) {
JUNK_CLEANER -> {
startActivity(Intent(this, ScanJunkActivity::class.java))
}
// VIRUS_SCAN -> {
// startActivity(Intent(this, VirusActivity::class.java))
// }
RECENT_APP_USAGE -> {
startActivity(Intent(this, RecentAppActivity::class.java))
}
LARGE_FILE_CLEANER -> {
startActivity(Intent(this, LargeFileCleanActivity::class.java))
}
NOTIFICATION_CLEANER -> {
if (SPUtils.getInstance().getBoolean("notification_guest", false)) {
startActivity(
Intent(
this,
NotificationCleanActivity::class.java
)
)
} else {
startActivity(
Intent(
this,
NotificationGuestActivity::class.java
)
)
}
}
NETWORK_TRAFFIC -> {
startActivity(Intent(this, NetWorkActivity::class.java))
}
APP_MANAGER -> {
startActivity(Intent(this, AppManagerActivity::class.java))
}
BATTERY_INFO -> {
startActivity(Intent(this, BatteryInfoActivity::class.java))
}
SIMILAR_PHOTOS -> {
startActivity(Intent(this, RepeaterdPhotoActivity::class.java))
}
PHOTO_COMPRESS -> {
startActivity(Intent(this, StartCompressionPhotoActivity::class.java))
}
}
finish()
}
binding.rvFun.adapter = adapter
adapter.updateListPostion()
adapter.notifyDataSetChanged()
val wenBen = intent.getStringExtra("wenBen")
binding.tvInfo.text = wenBen
val from = intent.getStringExtra("from")
when (from) {
JUNK_CLEANER -> {
if (intent.getLongExtra("clean_size", 0L) > 0) {
binding.tvInfo.text =
"Cleaned up ${
intent.getLongExtra("clean_size", 0L).toFormatSize(1)
}"
} else {
binding.tvInfo.text = "No junk files found."
}
SPUtils.getInstance().put("last_use_junk_cleaner", System.currentTimeMillis())
}
RECENT_APP_USAGE -> {
binding.tvInfo.text = ""
}
LARGE_FILE_CLEANER -> {
binding.tvInfo.text = ""
}
NOTIFICATION_CLEANER -> {
binding.tvInfo.text = ""
}
NETWORK_TRAFFIC -> {
binding.tvInfo.text = ""
}
APP_MANAGER -> {
binding.tvInfo.text = ""
}
BATTERY_INFO -> {
binding.tvInfo.text = "Battery scan completed."
SPUtils.getInstance().put("last_use_battery_info", System.currentTimeMillis())
}
EMPTY_FILE_CLEANER -> {
binding.tvInfo.text = ""
}
PHOTO_COMPRESS -> {
binding.tvInfo.text =
"Compress ${intent.getIntExtra("num", 0)} photo, ${
intent.getLongExtra(
"size",
0L
).toFormatSize(1)
} space freed"
}
BATTERY_OPTIMIZER -> {
binding.tvInfo.text = "Optimization completed"
}
APP_SPEED -> {
binding.tvInfo.text = "Optimization completed"
}
else -> {}
}
from?.let {
binding.tvTitle.text = it
adapter.removeItem(it)
}
//load gif
// Glide.with(this)
// .asGif()
// .load(R.mipmap.success)
// .into(object : ImageViewTarget<GifDrawable>(vb.icSuccess) {
// override fun onResourceReady(
// resource: GifDrawable,
// transition: Transition<in GifDrawable>?
// ) {
// super.onResourceReady(resource, transition)
// resource.setLoopCount(1)
// resource.start()
// }
//
// override fun setResource(resource: GifDrawable?) {
// view.setImageDrawable(resource)
// }
//
// })
onBackPressedDispatcher.addCallback(object : OnBackPressedCallback(true) {
override fun handleOnBackPressed() {
finishToMain()
// AdmobUtils.showInterstitialAd(this@ResultActivity) {
//
// }
}
})
binding.ivBack.setOnClickListener {
finishToMain()
}
}
override fun onResume() {
super.onResume()
}
override fun onDestroy() {
super.onDestroy()
}
}
\ No newline at end of file
package com.test.basd.smartjunkcleaner.activity
import android.animation.ValueAnimator
import android.content.Intent
import android.view.animation.LinearInterpolator
import androidx.activity.OnBackPressedCallback
import com.test.basd.smartjunkcleaner.databinding.ActivityLayoutScanJunkBinding
import com.test.basd.smartjunkcleaner.helps.BaseActivity
import com.test.basd.smartjunkcleaner.helps.ads.AdmobUtils
class ScanJunkActivity : BaseActivity<ActivityLayoutScanJunkBinding>() {
override val isLightMode = true
override val binding: ActivityLayoutScanJunkBinding by lazy {
ActivityLayoutScanJunkBinding.inflate(layoutInflater)
}
override fun initView() {
binding.idBackJunkScan.setOnClickListener {
onBackPressedDispatcher.onBackPressed()
}
onBackPressedDispatcher.addCallback(object : OnBackPressedCallback(true) {
override fun handleOnBackPressed() {
finishToMain()
}
})
}
override fun onStart() {
super.onStart()
checkPermission()
}
var isplay = 0
override fun onPermissionsResult(isGranted: Boolean) {
if (isGranted) {
isplay += 1
if (isplay == 1) {
playLottie()
}
} else {
finishToMain()
}
}
private fun playLottie() {
ValueAnimator.ofFloat(0f, 360f).run {
duration = 1000
repeatMode = ValueAnimator.RESTART
repeatCount = ValueAnimator.INFINITE
interpolator = LinearInterpolator()
addUpdateListener {
binding.idView1.rotation = it.animatedValue as Float
}
start()
}
ValueAnimator.ofFloat(0f, 360f).run {
duration = 1000
repeatMode = ValueAnimator.RESTART
repeatCount = ValueAnimator.INFINITE
interpolator = LinearInterpolator()
addUpdateListener {
binding.idView2.rotation = it.animatedValue as Float
}
start()
}
ValueAnimator.ofFloat(0f, 360f).run {
duration = 1000
repeatMode = ValueAnimator.RESTART
repeatCount = ValueAnimator.INFINITE
interpolator = LinearInterpolator()
addUpdateListener {
binding.idView3.rotation = it.animatedValue as Float
}
start()
}
binding.idJunkScan.imageAssetsFolder = "sao_smax_miao/images/"
binding.idJunkScan.setAnimation("sao_smax_miao/data.json")
binding.idJunkScan.playAnimation()
binding.root.postDelayed({
AdmobUtils.showInterstitialAd(this) {
startActivity(Intent(this, CleanJunkActivity::class.java))
finish()
}
}, 5000)
}
}
\ No newline at end of file
package com.test.basd.smartjunkcleaner.activity
import android.animation.Animator
import android.graphics.Color
import android.media.AudioManager
import android.media.MediaPlayer
import androidx.activity.OnBackPressedCallback
import androidx.core.view.isVisible
import androidx.core.view.updatePadding
import com.blankj.utilcode.util.BarUtils
import com.blankj.utilcode.util.ToastUtils
import com.test.basd.smartjunkcleaner.R
import com.test.basd.smartjunkcleaner.databinding.ActivityLayoutSpeakerCleaningBinding
import com.test.basd.smartjunkcleaner.helps.BaseActivity
import com.test.basd.smartjunkcleaner.helps.ads.AdmobUtils
class SpeakCleaningActivity : BaseActivity<ActivityLayoutSpeakerCleaningBinding>() {
private val zAudio by lazy {
MediaPlayer.create(this, R.raw.z)
}
override val binding: ActivityLayoutSpeakerCleaningBinding by lazy {
ActivityLayoutSpeakerCleaningBinding.inflate(layoutInflater)
}
override fun initView() {
val audioManager = this.getSystemService(AUDIO_SERVICE) as AudioManager
val maxVolume = audioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC)
audioManager.setStreamVolume(AudioManager.STREAM_MUSIC, maxVolume, 0)
BarUtils.setStatusBarColor(this, Color.TRANSPARENT)
binding.root.updatePadding(top = BarUtils.getStatusBarHeight())
binding.idSpeakCleanLottie.imageAssetsFolder = "speak_smax_clean/images/"
binding.idSpeakCleanLottie.setAnimation("speak_smax_clean/data.json")
binding.idSpeakCleanLottie.playAnimation()
binding.root.postDelayed({
zAudio.stop()
binding.idLlP1.isVisible = false
binding.idLlP2.isVisible = true
playComplete()
}, 30000)
onBackPressedDispatcher.addCallback(object : OnBackPressedCallback(true) {
override fun handleOnBackPressed() {
ToastUtils.showShort("Cleaning,Please wait a moment")
}
})
}
override fun onStart() {
super.onStart()
zAudio.start()
}
override fun onStop() {
super.onStop()
try {
zAudio.pause()
} catch (e: Exception) {
zAudio.stop()
}
}
override fun onDestroy() {
super.onDestroy()
zAudio.release()
}
private fun playComplete() {
binding.idLottieComplete.playAnimation()
binding.idLottieComplete.addLottieOnCompositionLoadedListener {
}
binding.idLottieComplete.addAnimatorListener(object : Animator.AnimatorListener {
override fun onAnimationStart(p0: Animator) {
}
override fun onAnimationEnd(p0: Animator) {
AdmobUtils.showInterstitialAd(this@SpeakCleaningActivity) {
finish()
}
}
override fun onAnimationCancel(p0: Animator) {
}
override fun onAnimationRepeat(p0: Animator) {
}
})
}
}
\ No newline at end of file
package com.test.basd.smartjunkcleaner.activity
import android.content.Intent
import android.graphics.Color
import androidx.activity.OnBackPressedCallback
import androidx.core.view.updatePadding
import com.blankj.utilcode.util.BarUtils
import com.test.basd.smartjunkcleaner.databinding.ActivityLayoutSpeakerCleanBinding
import com.test.basd.smartjunkcleaner.helps.BaseActivity
import com.test.basd.smartjunkcleaner.helps.ads.AdmobUtils
class SpeakerCleanerActivity : BaseActivity<ActivityLayoutSpeakerCleanBinding>() {
override val binding: ActivityLayoutSpeakerCleanBinding by lazy {
ActivityLayoutSpeakerCleanBinding.inflate(layoutInflater)
}
override fun initView() {
BarUtils.setStatusBarLightMode(this, true)
BarUtils.setStatusBarColor(this, Color.TRANSPARENT)
binding.root.updatePadding(top = BarUtils.getStatusBarHeight())
initListener()
}
override fun initListener() {
binding.idBackSpeakCleaner.setOnClickListener {
onBackPressedDispatcher.onBackPressed()
}
onBackPressedDispatcher.addCallback(object : OnBackPressedCallback(true) {
override fun handleOnBackPressed() {
AdmobUtils.showInterstitialAd(this@SpeakerCleanerActivity){
finishToMain()
}
}
})
binding.idCleanDust.setOnClickListener {
startActivity(Intent(this, SpeakCleaningActivity::class.java))
}
}
}
\ No newline at end of file
package com.test.basd.smartjunkcleaner.activity.browser
import android.app.Activity
import android.graphics.Bitmap
import android.os.Build
import android.webkit.CookieManager
import android.webkit.WebChromeClient
import android.webkit.WebSettings
import android.webkit.WebView
import android.webkit.WebViewClient
import com.blankj.utilcode.util.SPUtils
class BrowserView(listener: WebViewListener, val str: String) {
companion object {
private const val DESKTOP_USER_AGENT = "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/37.0.2049.0 Safari/537.36"
private var mDefaultUserAgent = ""
}
private val mWebView = WebView(listener.activity)
private val settings = mWebView.settings
var icon: Bitmap? = null
var title = str
var progress = 0
var url = str
var isHome = false
init {
mWebView.drawingCacheBackgroundColor = -1
mWebView.isFocusableInTouchMode = true
mWebView.isFocusable = true
mWebView.isDrawingCacheEnabled = false
mWebView.setWillNotCacheDrawing(true)
if (Build.VERSION.SDK_INT <= 22) {
mWebView.isAnimationCacheEnabled = false
mWebView.isAlwaysDrawnWithCacheEnabled = false
}
mWebView.setBackgroundColor(-1)
mWebView.isScrollbarFadingEnabled = true
mWebView.isSaveEnabled = true
mWebView.setNetworkAvailable(true)
mWebView.webChromeClient = object : WebChromeClient() {
override fun onProgressChanged(view: WebView?, newProgress: Int) {
progress = newProgress
listener.onProgressChanged(newProgress)
}
override fun onReceivedTitle(view: WebView?, title: String?) {
this@BrowserView.title = title?.takeIf { it.isNotBlank() } ?: str
listener.onReceivedTitle(title ?: str)
}
override fun onReceivedIcon(view: WebView?, icon: Bitmap?) {
this@BrowserView.icon = icon
listener.onReceivedIcon(icon)
}
}
mWebView.webViewClient = object : WebViewClient() {
override fun onPageFinished(view: WebView?, url: String?) {
this@BrowserView.url = url ?: ""
listener.onPageFinished(view, url ?: "")
}
override fun onPageStarted(view: WebView?, url: String?, favicon: Bitmap?) {
this@BrowserView.url = url ?: ""
listener.onPageStarted(url ?: "", favicon)
}
}
settings.setGeolocationEnabled(false)
settings.userAgentString = WebSettings.getDefaultUserAgent(listener.activity)
settings.saveFormData = false
settings.javaScriptEnabled = true
settings.javaScriptCanOpenWindowsAutomatically = true
settings.layoutAlgorithm = WebSettings.LayoutAlgorithm.NORMAL
try {
settings.layoutAlgorithm = WebSettings.LayoutAlgorithm.TEXT_AUTOSIZING
} catch (_: Exception) {}
settings.loadsImagesAutomatically = true
settings.setSupportMultipleWindows(true)
settings.useWideViewPort = true
settings.loadWithOverviewMode = true
settings.textZoom = 100
mDefaultUserAgent = settings.userAgentString
CookieManager.getInstance().setAcceptThirdPartyCookies(mWebView, true)
if (SPUtils.getInstance().getBoolean("desktop_mode", false)) {
setDesktop()
} else {
setPhone()
}
if (str.trim().isNotEmpty()) {
mWebView.loadUrl(str)
}
}
fun loadUrl(url: String) {
mWebView.loadUrl(url)
}
fun setDesktop() {
settings.userAgentString = DESKTOP_USER_AGENT
}
fun setPhone() {
settings.userAgentString = mDefaultUserAgent
}
fun reload() {
mWebView.reload()
}
fun canGoBack() = mWebView.canGoBack()
fun goBack() = mWebView.goBack()
fun canGoForward() = mWebView.canGoForward()
fun goForward() = mWebView.goForward()
fun getWebView() = mWebView
interface WebViewListener {
val activity: Activity
fun onProgressChanged(progress: Int)
fun onReceivedTitle(title: String)
fun onReceivedIcon(icon: Bitmap?)
fun onPageStarted(url: String, icon: Bitmap?)
fun onPageFinished(view: WebView?, url: String)
}
}
\ No newline at end of file
package com.test.basd.smartjunkcleaner.activity.browser
import android.graphics.Bitmap
import android.graphics.Color
import android.view.ViewGroup.LayoutParams.MATCH_PARENT
import android.webkit.WebView
import androidx.core.view.isVisible
import androidx.core.view.updatePadding
import com.blankj.utilcode.util.BarUtils
import com.test.basd.smartjunkcleaner.databinding.ActivityLayoutSecureBrowserBinding
import com.test.basd.smartjunkcleaner.helps.BaseActivity
class SecureBrowserActivity : BaseActivity<ActivityLayoutSecureBrowserBinding>() {
private val tabs = mutableListOf<BrowserView?>(null)
private var currentTab = 0
override val binding: ActivityLayoutSecureBrowserBinding by lazy {
ActivityLayoutSecureBrowserBinding.inflate(layoutInflater)
}
override fun initView() {
BarUtils.setStatusBarColor(this, Color.TRANSPARENT)
binding.root.updatePadding(top = BarUtils.getStatusBarHeight())
}
override fun initListener() {
binding.idGoogle.setOnClickListener {
}
binding.idYoutube.setOnClickListener {
loadUrl("https://www.youtube.com/")
}
binding.idFacebook.setOnClickListener {
}
binding.idAmazon.setOnClickListener {
}
binding.idInstagram.setOnClickListener {
}
binding.idTwitter.setOnClickListener {
}
binding.idReddit.setOnClickListener {
}
binding.idTiktok.setOnClickListener {
}
}
private fun loadUrl(url: String) {
binding.idContainer.isVisible = true
binding.idContent.isVisible=false
if (tabs[currentTab] == null) {
tabs[currentTab] = BrowserView(
object : BrowserView.WebViewListener {
override val activity = this@SecureBrowserActivity
override fun onProgressChanged(progress: Int) {
}
override fun onReceivedTitle(title: String) {
// binding.idAddressEdit.setText(title)
}
override fun onReceivedIcon(icon: Bitmap?) {
// icon?.let {
// binding.idIcon.setImageBitmap(it)
// }
}
override fun onPageStarted(url: String, icon: Bitmap?) {
}
override fun onPageFinished(view: WebView?, url: String) {
binding.idBack.isSelected = view?.canGoBack() == true
binding.idForward.isSelected = view?.canGoForward() == true
// refreshTabs()
}
},
url
)
} else {
tabs[currentTab]?.loadUrl(url)
}
tabs[currentTab]?.isHome = false
binding.idContainer.removeAllViews()
binding.idContainer.addView(tabs[currentTab]?.getWebView(), 0, MATCH_PARENT)
}
}
\ No newline at end of file
package com.test.basd.smartjunkcleaner.activity.photocompress.photo
class AlbumBean {
var name: String = ""
var num: Int = 0
var src: String = ""
constructor(name: String, num: Int, src: String) {
this.name = name
this.num = num
this.src = src
}
}
\ No newline at end of file
package com.test.basd.smartjunkcleaner.activity.photocompress.photo
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView
import androidx.recyclerview.widget.RecyclerView.ViewHolder
import com.bumptech.glide.Glide
import com.test.basd.smartjunkcleaner.R
class AlbumListAdapter : RecyclerView.Adapter<AlbumListAdapter.ContentViewHolder> {
private var mList: List<AlbumBean>
private val mListener: OnItemClickListener<AlbumBean>
constructor(mList: List<AlbumBean>, mListener: OnItemClickListener<AlbumBean>) : super() {
this.mList = mList
this.mListener = mListener
}
fun updateData(mList: List<AlbumBean>) {
this.mList = mList
notifyDataSetChanged()
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ContentViewHolder {
return ContentViewHolder(
LayoutInflater.from(parent.context).inflate(R.layout.item_album, parent, false)
)
}
override fun onBindViewHolder(holder: ContentViewHolder, position: Int) {
val entity = mList[position]
holder.mName.text = entity.name
holder.mNum.text = entity.num.toString()
Glide.with(holder.itemView.context).load(entity.src).into(holder.mImage)
}
override fun getItemCount(): Int = mList.size
inner class ContentViewHolder : ViewHolder {
val mName: TextView
val mNum: TextView
val mImage: ImageView
constructor(itemView: View) : super(itemView) {
mName = itemView.findViewById(R.id.name)
mNum = itemView.findViewById(R.id.num)
mImage = itemView.findViewById(R.id.image)
itemView.setOnClickListener {
if(position>=0&&position<mList.size){
val entity = mList[position]
mListener.onItemClick(position, entity)
}
}
}
}
}
\ No newline at end of file
package com.test.basd.smartjunkcleaner.activity.photocompress.photo
interface AlbumsSelectListener {
fun onSelectAlbum(albums: AlbumBean)
}
\ No newline at end of file
package com.test.basd.smartjunkcleaner.activity.photocompress.photo
import android.app.Activity
import android.view.View
import android.view.View.OnClickListener
import android.widget.LinearLayout
import android.widget.TextView
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.google.android.material.bottomsheet.BottomSheetBehavior
import com.test.basd.smartjunkcleaner.R
class BottomSheetManager : BottomSheetBehavior.BottomSheetCallback, OnClickListener,
OnItemClickListener<AlbumBean> {
private val mContext: Activity
private val mRecycleView: RecyclerView
private val blackOverlay: View
private val mAlbumName: TextView
private val bottomSheetBehavior: BottomSheetBehavior<LinearLayout>
private val listener: AlbumsSelectListener
constructor(mContext: Activity, listener: AlbumsSelectListener) {
this.mContext = mContext
this.listener = listener
mRecycleView = mContext.findViewById(R.id.album_content)
blackOverlay = mContext.findViewById(R.id.black_overlay)
val bottomSheet = mContext.findViewById<LinearLayout>(R.id.bottom_sheet)
bottomSheetBehavior = BottomSheetBehavior.from(bottomSheet)
mAlbumName = mContext.findViewById(R.id.albumname)
init()
}
private fun init() {
bottomSheetBehavior.addBottomSheetCallback(this)
blackOverlay.setOnClickListener(this)
mAlbumName?.setOnClickListener(this)
mRecycleView.layoutManager = LinearLayoutManager(mContext)
val mList = ImagesMediaUtils.getAlbums(mContext)
val mAdapter = AlbumListAdapter(mList, this)
mRecycleView.adapter = mAdapter
if (mList.isNotEmpty()) {
onItemClick(0, mList[0])
}
}
override fun onStateChanged(bottomSheet: View, newState: Int) {
when (newState) {
BottomSheetBehavior.STATE_HIDDEN -> {
blackOverlay.visibility = View.GONE
}
BottomSheetBehavior.STATE_COLLAPSED -> {
blackOverlay.visibility = View.GONE
}
else -> {
blackOverlay.visibility = View.VISIBLE
}
}
}
override fun onSlide(bottomSheet: View, slideOffset: Float) {
}
override fun onClick(v: View?) {
when (v?.id) {
R.id.albumname -> {
if (bottomSheetBehavior.state != BottomSheetBehavior.STATE_EXPANDED) {
bottomSheetBehavior.state = BottomSheetBehavior.STATE_EXPANDED
}
}
R.id.black_overlay -> {
bottomSheetBehavior.state = BottomSheetBehavior.STATE_HIDDEN
}
}
}
override fun onUpdateBtn(boolean: Boolean, entity: AlbumBean) {
}
override fun onItemClick(position: Int, entity: AlbumBean) {
mAlbumName.text = entity.name
bottomSheetBehavior.state = BottomSheetBehavior.STATE_HIDDEN
listener.onSelectAlbum(entity)
}
}
package com.test.basd.smartjunkcleaner.activity.photocompress.photo
import android.content.Context
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.graphics.Matrix
import android.media.ExifInterface
import android.media.MediaScannerConnection
import android.os.Handler
import android.os.Looper
import android.util.Log
import java.io.File
import java.io.FileOutputStream
import java.io.IOException
private const val s = "temp"
object CompressionImageUtils {
interface BitmapCallback {
fun onBitmapReady(bitmap: Bitmap?, size: Long, position: Int)
}
fun compressImage(filePath: String, mode: Int, position: Int, callback: BitmapCallback) {
Thread {
try {
val quality = when (mode) {
0 -> 95
50 -> 90
else -> 85
}
// 读取照片信息
val options = BitmapFactory.Options()
options.inJustDecodeBounds = true
BitmapFactory.decodeFile(filePath, options)
// 计算缩放比例
val maxWidth = 1080
val maxHeight = 1920
val width = options.outWidth
val height = options.outHeight
var scale = 1
while (width / scale > maxWidth || height / scale > maxHeight) {
scale *= 2
}
// 读取照片数据
options.inJustDecodeBounds = false
options.inSampleSize = scale
val bitmap = BitmapFactory.decodeFile(filePath, options)
if (bitmap == null) {
callback.onBitmapReady(null, 0, position)
return@Thread
}
// 旋转照片
val orientation: Int = getOrientation(filePath)
val matrix = Matrix()
if (orientation == 90) {
matrix.postRotate(90f)
} else if (orientation == 180) {
matrix.postRotate(180f)
} else if (orientation == 270) {
matrix.postRotate(270f)
}
val rotatedBitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.width, bitmap.height, matrix, true)
// 压缩照片
val cacheFile = File.createTempFile(System.currentTimeMillis().toString(), ".jpg")
val outputStream = FileOutputStream(cacheFile)
rotatedBitmap.compress(Bitmap.CompressFormat.JPEG, quality, outputStream)
outputStream.flush()
outputStream.close()
val compressedBitmap = BitmapFactory.decodeFile(cacheFile.path)
val cacheSize = cacheFile.length()
if (cacheFile.exists()) {
cacheFile.delete()
}
Log.d("glc","压缩后大小:"+cacheSize)
callback.onBitmapReady(compressedBitmap, cacheSize, position)
} catch (e: IOException) {
callback.onBitmapReady(null, 0, position)
}
}.start()
}
private fun getOrientation(filePath: String): Int {
return try {
val exif = ExifInterface(filePath)
val orientation: Int = exif.getAttributeInt(
ExifInterface.TAG_ORIENTATION,
ExifInterface.ORIENTATION_UNDEFINED
)
if (orientation == ExifInterface.ORIENTATION_ROTATE_90) {
90
} else if (orientation == ExifInterface.ORIENTATION_ROTATE_180) {
180
} else if (orientation == ExifInterface.ORIENTATION_ROTATE_270) {
270
} else {
0
}
} catch (e: IOException) {
0
}
}
interface OnSaveBitmapListener {
fun onSaveSuccess()
fun onSaveFailed()
}
fun saveBitmapToFile(filePath: String?, mode: Int, bitmap: Bitmap,context: Context, listener: OnSaveBitmapListener) {
Thread {
val file = File(filePath)
if (file.exists()) {
file.delete()
}
try {
val outputStream = FileOutputStream(file)
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, outputStream)
updateMedia(filePath?:"",context)
outputStream.flush()
outputStream.close()
if (listener != null) {
Handler(Looper.getMainLooper()).post(Runnable { listener.onSaveSuccess() })
}
} catch (e: IOException) {
e.printStackTrace()
if (listener != null) {
Handler(Looper.getMainLooper()).post(Runnable { listener.onSaveFailed() })
}
}
}.start()
}
private fun updateMedia(file:String,context:Context){
MediaScannerConnection.scanFile(context, arrayOf(file), null, null)
}
}
package com.test.basd.smartjunkcleaner.activity.photocompress.photo
import android.graphics.Color
import androidx.activity.OnBackPressedCallback
import androidx.core.view.updatePadding
import com.blankj.utilcode.util.BarUtils
import com.test.basd.smartjunkcleaner.databinding.ActivityCompressionPhotoListBinding
import com.test.basd.smartjunkcleaner.helps.BaseActivity
import com.test.basd.smartjunkcleaner.helps.ads.AdmobUtils
class CompressionPhotoListActivity : BaseActivity<ActivityCompressionPhotoListBinding>(),
AlbumsSelectListener {
override val isLightMode =true
private var mPhotoListManager: PhotoListManager? = null
companion object {
var mActivity: CompressionPhotoListActivity? = null
}
override val binding: ActivityCompressionPhotoListBinding by lazy {
ActivityCompressionPhotoListBinding.inflate(layoutInflater)
}
override fun initView() {
BarUtils.setStatusBarColor(this, Color.TRANSPARENT)
binding.root.updatePadding(top = BarUtils.getStatusBarHeight())
binding.fanhui.setOnClickListener {
onBackPressedDispatcher.onBackPressed()
}
onBackPressedDispatcher.addCallback(object : OnBackPressedCallback(true) {
override fun handleOnBackPressed() {
AdmobUtils.showInterstitialAd(this@CompressionPhotoListActivity){
PhotoCache.getInstance().clearCache()
finishToMain()
}
}
})
mActivity = this
mPhotoListManager = PhotoListManager(this)
BottomSheetManager(this, this)
}
override fun onBackPressed() {
PhotoCache.getInstance().clearCache()
super.onBackPressed()
}
override fun onSelectAlbum(albums: AlbumBean) {
mPhotoListManager?.onSelectAlbum(albums)
}
}
\ No newline at end of file
package com.test.basd.smartjunkcleaner.activity.photocompress.photo
import android.text.TextUtils
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.CheckBox
import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView
import androidx.recyclerview.widget.RecyclerView.ViewHolder
import com.bumptech.glide.Glide
import com.makeramen.roundedimageview.RoundedImageView
import com.test.basd.smartjunkcleaner.R
class CompressionPhotoListAdapter : RecyclerView.Adapter<ViewHolder> {
private val HEAD = 10081
private val CONTENT = 10082
private var mList: MutableList<PhotoBean> = ArrayList()
private var mHastMap: HashMap<Int, Boolean> = HashMap()
private val mListener: OnItemClickListener<PhotoBean>
constructor(mListener: OnItemClickListener<PhotoBean>) : super() {
this.mListener = mListener
}
fun updateData(mList: List<PhotoBean>) {
this.mList?.clear()
this.mList?.addAll(mList)
this.mHastMap.clear()
notifyDataSetChanged()
}
fun getData(): List<PhotoBean>? {
return mList
}
fun isSelectAll(): Boolean {
if (mHastMap.count() == itemCount) {
return mHastMap.all { it.value }
}
return false
}
fun setNoOrYesselectAll(boolean: Boolean) {
for (i in 0 until itemCount) {
mHastMap[i] = boolean
val entity = mList[i]
if (entity.type == 0) {
mListener.onUpdateBtn(boolean, entity)
}
}
notifyItemRangeChanged(0, itemCount)
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
if (viewType == HEAD) {
return HeadViewHolder(
LayoutInflater.from(parent.context).inflate(R.layout.item_head, parent, false)
)
} else {
return ContentViewHolder(
LayoutInflater.from(parent.context).inflate(R.layout.item_content, parent, false)
)
}
}
override fun getItemViewType(position: Int): Int {
val data = mList[position]
return if (data.type == 0) {
CONTENT
} else {
HEAD
}
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val entity = mList[position]
if (holder is ContentViewHolder) {
holder.mCheck.isChecked = mHastMap[position] ?: false
Glide.with(holder.mImageView.context).load(entity.src)
.into(holder.mImageView)
holder.mRatio.text = entity.whRatio
holder.mSize.text = entity.sizeText
} else if (holder is HeadViewHolder) {
holder.mTitle.text = entity.date
holder.mSelectAll.text = if (mHastMap[position] == true)
"DESELECT ALL" else "SELECT ALL"
}
}
override fun getItemCount(): Int = mList?.size ?: 0
inner class HeadViewHolder : ViewHolder {
val mSelectAll: TextView
val mTitle: TextView
constructor(itemView: View) : super(itemView) {
mSelectAll = itemView.findViewById(R.id.select_all)
mTitle = itemView.findViewById(R.id.title)
mSelectAll.setOnClickListener {
val text = mSelectAll.text
if(position>=0&&position<mList.size){
val data = mList[position]
val date = data?.date
mList?.forEachIndexed { index, photoBean ->
if (TextUtils.equals(photoBean.date, date)) {
val boolean = !TextUtils.equals("DESELECT ALL", text)
mHastMap[index] = boolean
if (photoBean.type == 0) {
mListener.onUpdateBtn(boolean, photoBean)
}
}
}
mSelectAll.text = when (text) {
"DESELECT ALL" -> "SELECT ALL"
else -> "DESELECT ALL"
}
notifyItemRangeChanged(0, mList?.size ?: 0)
}
}
}
}
inner class ContentViewHolder : ViewHolder {
val mCheck: CheckBox
val mImageView: RoundedImageView
val mSize: TextView
val mRatio: TextView
constructor(itemView: View) : super(itemView) {
mCheck = itemView.findViewById(R.id.checkbox)
mImageView = itemView.findViewById(R.id.image)
mSize = itemView.findViewById(R.id.size)
mRatio = itemView.findViewById(R.id.ratio)
val layoutParams = mImageView.layoutParams
layoutParams.height = Utils.calculateImageWidth(itemView.context)
mImageView.layoutParams = layoutParams
itemView.setOnClickListener {
mCheck.performClick()
if(position>=0&&position<mList.size){
val entity = mList[position]
val index =
mList.indexOfFirst { it.type == 1 && TextUtils.equals(entity.date, it.date) }
notifyItemChanged(index)
}
}
mCheck.setOnCheckedChangeListener { buttonView, isChecked ->
if(position>=0&&position<mList.size){
val entity = mList[position]
mHastMap.put(position, isChecked)
mListener.onUpdateBtn(isChecked, entity)
val indexList: MutableList<Int> = ArrayList()
val date = entity?.date ?: ""
mList?.forEachIndexed { index, photoBean ->
if (photoBean.type == 0 && TextUtils.equals(photoBean.date, date)) {
indexList.add(index)
}
}
val allTrue = (indexList.first()..indexList.last()).all { i ->
mHastMap.getOrDefault(
i,
false
)
}
mHastMap.put(indexList.first() - 1, allTrue)
}
}
}
}
}
\ No newline at end of file
package com.test.basd.smartjunkcleaner.activity.photocompress.photo
import android.annotation.SuppressLint
import android.graphics.Color
import android.widget.TextView
import androidx.core.view.updatePadding
import com.blankj.utilcode.util.BarUtils
import com.test.basd.smartjunkcleaner.R
import com.test.basd.smartjunkcleaner.databinding.ActivityCompressionSuccessBinding
import com.test.basd.smartjunkcleaner.helps.BaseActivity
class CompressionSuccessActivity : BaseActivity<ActivityCompressionSuccessBinding>() {
override val binding: ActivityCompressionSuccessBinding
get() = ActivityCompressionSuccessBinding.inflate(layoutInflater)
@SuppressLint("SetTextI18n")
override fun initView() {
BarUtils.setStatusBarLightMode(this, true)
BarUtils.setStatusBarColor(this, Color.TRANSPARENT)
binding.root.updatePadding(top = BarUtils.getStatusBarHeight())
val num = intent.getIntExtra("num", 0)
val size = intent.getLongExtra("size", 0)
findViewById<TextView>(R.id.tv_text).text =
"$num images compressed " + ImagesMediaUtils.formatFileSize(size)
}
}
\ No newline at end of file
package com.test.basd.smartjunkcleaner.activity.photocompress.photo
import android.content.Intent
import android.graphics.Bitmap
import android.graphics.Color
import android.widget.ProgressBar
import android.widget.TextView
import androidx.core.view.updatePadding
import com.blankj.utilcode.util.BarUtils
import com.test.basd.smartjunkcleaner.R
import com.test.basd.smartjunkcleaner.activity.ResultActivity
import com.test.basd.smartjunkcleaner.databinding.ActivityCompressioningBinding
import com.test.basd.smartjunkcleaner.helps.BaseActivity
import com.test.basd.smartjunkcleaner.helps.ads.AdmobUtils
import com.test.basd.smartjunkcleaner.view.AFunOb
class CompressioningActivity : BaseActivity<ActivityCompressioningBinding>(), CompressionImageUtils.BitmapCallback,
CompressionImageUtils.OnSaveBitmapListener {
private var mTextProgress: TextView? = null
private var mProgressBar: ProgressBar? = null
private var mNum = 0
private var mode = 50;
override val binding: ActivityCompressioningBinding by lazy {
ActivityCompressioningBinding.inflate(layoutInflater)
}
override fun initView() {
BarUtils.setStatusBarLightMode(this, true)
BarUtils.setStatusBarColor(this, Color.TRANSPARENT)
binding.root.updatePadding(top = BarUtils.getStatusBarHeight())
binding.fanhui.setOnClickListener {
finishToMain()
}
mTextProgress = findViewById(R.id.text_progress)
mProgressBar = findViewById(R.id.compressing_progress)
mode = intent.getIntExtra("mode", 50)
PhotoCache.getInstance().photoList?.forEachIndexed { index, it ->
CompressionImageUtils.compressImage(it.src, mode, index, this@CompressioningActivity)
}
mProgressBar?.max = PhotoCache.getInstance().count
updateProgress()
}
private var mSizeCount: Long = 0L
override fun onBitmapReady(bitmap: Bitmap?, size: Long, position: Int) {
val entity = PhotoCache.getInstance().getPositionData(position)
if (bitmap != null && entity != null) {
mSizeCount += (entity.size - size)
CompressionImageUtils.saveBitmapToFile(entity.src, mode, bitmap, this, this)
}
}
override fun onDestroy() {
super.onDestroy()
PhotoCache.getInstance().clearCache()
}
override fun onSaveFailed() {
}
override fun onSaveSuccess() {
mNum += 1
updateProgress()
}
private fun updateProgress() {
mTextProgress?.text = mNum.toString() + "/" + PhotoCache.getInstance().count
mProgressBar?.progress = mNum
if (mNum == mProgressBar?.max) {
onSuccess()
}
}
private fun onSuccess() {
AdmobUtils.showInterstitialAd(this){
val intent = Intent(this, ResultActivity::class.java)
intent.putExtra("from", AFunOb.PHOTO_COMPRESS)
intent.putExtra("num", mNum)
intent.putExtra("size", mSizeCount)
startActivity(intent)
finish()
}
}
override fun onBackPressed() {
// val dialog = CustomDialog(this, R.layout.dialog_exit)
// dialog.setButtonClickListener(R.id.cancel) {
// dialog.dismiss()
// }
//
// dialog.setButtonClickListener(R.id.btnContinue) {
super.onBackPressed()
// }
// dialog.show()
}
}
\ No newline at end of file
package com.test.basd.smartjunkcleaner.activity.photocompress.photo
import android.app.Dialog
import android.content.Context
import android.view.Gravity
import android.view.View
import android.widget.TextView
class CustomDialog(context: Context, layoutId: Int) : Dialog(context) {
init {
setContentView(layoutId)
val window = window
window?.apply {
setGravity(Gravity.CENTER)
setBackgroundDrawableResource(android.R.color.transparent)
}
setCancelable(false)
}
fun setButtonClickListener(viewId: Int, listener: () -> Unit) {
val button = findViewById<View>(viewId) as? TextView
button?.setOnClickListener {
listener.invoke()
}
}
}
package com.test.basd.smartjunkcleaner.activity.photocompress.photo;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import com.bumptech.glide.Glide;
import com.test.basd.smartjunkcleaner.R;
import java.util.List;
public class GalleryAdapter extends RecyclerView.Adapter<GalleryAdapter.GalleryViewHolder> {
private Context context;
private int index = 0;
public void setPos(int index) {
this.index = index;
}
private List<PhotoBean> list;
public GalleryAdapter(Context context, List<PhotoBean> list) {
this.context = context;
this.list = list;
}
@NonNull
@Override
public GalleryViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(context).inflate(R.layout.item_gallery, parent, false);
return new GalleryViewHolder(view);
}
@Override
public void onBindViewHolder(@NonNull GalleryViewHolder holder, int position) {
if (position < list.size()) {
PhotoBean bean = list.get(position);
Glide.with(holder.itemView.getContext()).load(bean.getSrc()).into((ImageView) holder.itemView);
}
}
@Override
public int getItemCount() {
return list.size();
}
public static class GalleryViewHolder extends RecyclerView.ViewHolder {
public GalleryViewHolder(@NonNull View itemView) {
super(itemView);
}
}
}
\ No newline at end of file
package com.test.basd.smartjunkcleaner.activity.photocompress.photo;
import static androidx.recyclerview.widget.RecyclerView.SCROLL_STATE_IDLE;
import android.content.Context;
import android.graphics.PointF;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.util.Log;
import android.util.SparseArray;
import android.view.View;
import android.view.ViewGroup;
import androidx.recyclerview.widget.LinearSmoothScroller;
import androidx.recyclerview.widget.LinearSnapHelper;
import androidx.recyclerview.widget.OrientationHelper;
import androidx.recyclerview.widget.RecyclerView;
import androidx.viewpager.widget.ViewPager;
import com.makeramen.roundedimageview.BuildConfig;
/**
* A custom LayoutManager to build a {@link android.widget.Gallery} or a {@link ViewPager}like {@link RecyclerView} and
* support both {@link GalleryLayoutManager#HORIZONTAL} and {@link GalleryLayoutManager#VERTICAL} scroll.
* Created by chensuilun on 2016/11/18.
*/
public class GalleryLayoutManager extends RecyclerView.LayoutManager implements RecyclerView.SmoothScroller.ScrollVectorProvider {
private static final String TAG = "GalleryLayoutManager";
final static int LAYOUT_START = -1;
final static int LAYOUT_END = 1;
public static final int HORIZONTAL = OrientationHelper.HORIZONTAL;
public static final int VERTICAL = OrientationHelper.VERTICAL;
private int mFirstVisiblePosition = 0;
private int mLastVisiblePos = 0;
private int mInitialSelectedPosition = 0;
int mCurSelectedPosition = -1;
View mCurSelectedView;
/**
* Scroll state
*/
private State mState;
private LinearSnapHelper mSnapHelper = new LinearSnapHelper();
private InnerScrollListener mInnerScrollListener = new InnerScrollListener();
private boolean mCallbackInFling = false;
/**
* Current orientation. Either {@link #HORIZONTAL} or {@link #VERTICAL}
*/
private int mOrientation = HORIZONTAL;
private OrientationHelper mHorizontalHelper;
private OrientationHelper mVerticalHelper;
public GalleryLayoutManager(int orientation) {
mOrientation = orientation;
}
public int getOrientation() {
return mOrientation;
}
public int getCurSelectedPosition() {
return mCurSelectedPosition;
}
@Override
public RecyclerView.LayoutParams generateDefaultLayoutParams() {
if (mOrientation == VERTICAL) {
return new LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT);
} else {
return new LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.MATCH_PARENT);
}
}
@Override
public RecyclerView.LayoutParams generateLayoutParams(Context c, AttributeSet attrs) {
return new LayoutParams(c, attrs);
}
@Override
public RecyclerView.LayoutParams generateLayoutParams(ViewGroup.LayoutParams lp) {
if (lp instanceof ViewGroup.MarginLayoutParams) {
return new LayoutParams((ViewGroup.MarginLayoutParams) lp);
} else {
return new LayoutParams(lp);
}
}
@Override
public boolean checkLayoutParams(RecyclerView.LayoutParams lp) {
return lp instanceof LayoutParams;
}
@Override
public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
if (BuildConfig.DEBUG) {
Log.d(TAG, "onLayoutChildren() called with: state = [" + state + "]");
}
if (getItemCount() == 0) {
reset();
detachAndScrapAttachedViews(recycler);
return;
}
if (state.isPreLayout()) {
return;
}
if (state.getItemCount() != 0 && !state.didStructureChange()) {
if (BuildConfig.DEBUG) {
Log.d(TAG, "onLayoutChildren: ignore extra layout step");
}
return;
}
if (getChildCount() == 0 || state.didStructureChange()) {
reset();
}
mInitialSelectedPosition = Math.min(Math.max(0, mInitialSelectedPosition), getItemCount() - 1);
detachAndScrapAttachedViews(recycler);
firstFillCover(recycler, state, 0);
}
private void reset() {
if (BuildConfig.DEBUG) {
Log.d(TAG, "reset: ");
}
if (mState != null) {
mState.mItemsFrames.clear();
}
//when data set update keep the last selected position
if (mCurSelectedPosition != -1) {
mInitialSelectedPosition = mCurSelectedPosition;
}
mInitialSelectedPosition = Math.min(Math.max(0, mInitialSelectedPosition), getItemCount() - 1);
mFirstVisiblePosition = mInitialSelectedPosition;
mLastVisiblePos = mInitialSelectedPosition;
mCurSelectedPosition = -1;
if (mCurSelectedView != null) {
mCurSelectedView.setSelected(false);
mCurSelectedView = null;
}
}
private void firstFillCover(RecyclerView.Recycler recycler, RecyclerView.State state, int scrollDelta) {
if (mOrientation == HORIZONTAL) {
firstFillWithHorizontal(recycler, state);
} else {
firstFillWithVertical(recycler, state);
}
if (BuildConfig.DEBUG) {
Log.d(TAG, "firstFillCover finish:first: " + mFirstVisiblePosition + ",last:" + mLastVisiblePos);
}
if (mItemTransformer != null) {
View child;
for (int i = 0; i < getChildCount(); i++) {
child = getChildAt(i);
mItemTransformer.transformItem(this, child, calculateToCenterFraction(child, scrollDelta));
}
}
mInnerScrollListener.onScrolled(mRecyclerView, 0, 0);
Log.d(TAG,"mInnerScrollListener.onScrolled");
}
/**
* Layout the item view witch position specified by {@link GalleryLayoutManager#mInitialSelectedPosition} first and then layout the other
*
* @param recycler
* @param state
*/
private void firstFillWithHorizontal(RecyclerView.Recycler recycler, RecyclerView.State state) {
detachAndScrapAttachedViews(recycler);
int leftEdge = getOrientationHelper().getStartAfterPadding();
int rightEdge = getOrientationHelper().getEndAfterPadding();
int startPosition = mInitialSelectedPosition;
int scrapWidth, scrapHeight;
Rect scrapRect = new Rect();
int height = getVerticalSpace();
int topOffset;
//layout the init position view
View scrap = recycler.getViewForPosition(mInitialSelectedPosition);
addView(scrap, 0);
measureChildWithMargins(scrap, 0, 0);
scrapWidth = getDecoratedMeasuredWidth(scrap);
scrapHeight = getDecoratedMeasuredHeight(scrap);
topOffset = (int) (getPaddingTop() + (height - scrapHeight) / 2.0f);
int left = (int) (getPaddingLeft() + (getHorizontalSpace() - scrapWidth) / 2.f);
scrapRect.set(left, topOffset, left + scrapWidth, topOffset + scrapHeight);
layoutDecorated(scrap, scrapRect.left, scrapRect.top, scrapRect.right, scrapRect.bottom);
if (getState().mItemsFrames.get(startPosition) == null) {
getState().mItemsFrames.put(startPosition, scrapRect);
} else {
getState().mItemsFrames.get(startPosition).set(scrapRect);
}
mFirstVisiblePosition = mLastVisiblePos = startPosition;
int leftStartOffset = getDecoratedLeft(scrap);
int rightStartOffset = getDecoratedRight(scrap);
//fill left of center
fillLeft(recycler, mInitialSelectedPosition - 1, leftStartOffset, leftEdge);
//fill right of center
fillRight(recycler, mInitialSelectedPosition + 1, rightStartOffset, rightEdge);
}
@Override
public void onItemsRemoved(RecyclerView recyclerView, int positionStart, int itemCount) {
super.onItemsRemoved(recyclerView, positionStart, itemCount);
}
/**
* Layout the item view witch position special by {@link GalleryLayoutManager#mInitialSelectedPosition} first and then layout the other
*
* @param recycler
* @param state
*/
private void firstFillWithVertical(RecyclerView.Recycler recycler, RecyclerView.State state) {
detachAndScrapAttachedViews(recycler);
int topEdge = getOrientationHelper().getStartAfterPadding();
int bottomEdge = getOrientationHelper().getEndAfterPadding();
int startPosition = mInitialSelectedPosition;
int scrapWidth, scrapHeight;
Rect scrapRect = new Rect();
int width = getHorizontalSpace();
int leftOffset;
//layout the init position view
View scrap = recycler.getViewForPosition(mInitialSelectedPosition);
addView(scrap, 0);
measureChildWithMargins(scrap, 0, 0);
scrapWidth = getDecoratedMeasuredWidth(scrap);
scrapHeight = getDecoratedMeasuredHeight(scrap);
leftOffset = (int) (getPaddingLeft() + (width - scrapWidth) / 2.0f);
int top = (int) (getPaddingTop() + (getVerticalSpace() - scrapHeight) / 2.f);
scrapRect.set(leftOffset, top, leftOffset + scrapWidth, top + scrapHeight);
layoutDecorated(scrap, scrapRect.left, scrapRect.top, scrapRect.right, scrapRect.bottom);
if (getState().mItemsFrames.get(startPosition) == null) {
getState().mItemsFrames.put(startPosition, scrapRect);
} else {
getState().mItemsFrames.get(startPosition).set(scrapRect);
}
mFirstVisiblePosition = mLastVisiblePos = startPosition;
int topStartOffset = getDecoratedTop(scrap);
int bottomStartOffset = getDecoratedBottom(scrap);
//fill left of center
fillTop(recycler, mInitialSelectedPosition - 1, topStartOffset, topEdge);
//fill right of center
fillBottom(recycler, mInitialSelectedPosition + 1, bottomStartOffset, bottomEdge);
}
/**
* Fill left of the center view
*
* @param recycler
* @param startPosition start position to fill left
* @param startOffset layout start offset
* @param leftEdge
*/
private void fillLeft(RecyclerView.Recycler recycler, int startPosition, int startOffset, int leftEdge) {
View scrap;
int topOffset;
int scrapWidth, scrapHeight;
Rect scrapRect = new Rect();
int height = getVerticalSpace();
for (int i = startPosition; i >= 0 && startOffset > leftEdge; i--) {
scrap = recycler.getViewForPosition(i);
addView(scrap, 0);
measureChildWithMargins(scrap, 0, 0);
scrapWidth = getDecoratedMeasuredWidth(scrap);
scrapHeight = getDecoratedMeasuredHeight(scrap);
topOffset = (int) (getPaddingTop() + (height - scrapHeight) / 2.0f);
scrapRect.set(startOffset - scrapWidth, topOffset, startOffset, topOffset + scrapHeight);
layoutDecorated(scrap, scrapRect.left, scrapRect.top, scrapRect.right, scrapRect.bottom);
startOffset = scrapRect.left;
mFirstVisiblePosition = i;
if (getState().mItemsFrames.get(i) == null) {
getState().mItemsFrames.put(i, scrapRect);
} else {
getState().mItemsFrames.get(i).set(scrapRect);
}
}
}
/**
* Fill right of the center view
*
* @param recycler
* @param startPosition start position to fill right
* @param startOffset layout start offset
* @param rightEdge
*/
private void fillRight(RecyclerView.Recycler recycler, int startPosition, int startOffset, int rightEdge) {
View scrap;
int topOffset;
int scrapWidth, scrapHeight;
Rect scrapRect = new Rect();
int height = getVerticalSpace();
for (int i = startPosition; i < getItemCount() && startOffset < rightEdge; i++) {
scrap = recycler.getViewForPosition(i);
addView(scrap);
measureChildWithMargins(scrap, 0, 0);
scrapWidth = getDecoratedMeasuredWidth(scrap);
scrapHeight = getDecoratedMeasuredHeight(scrap);
topOffset = (int) (getPaddingTop() + (height - scrapHeight) / 2.0f);
scrapRect.set(startOffset, topOffset, startOffset + scrapWidth, topOffset + scrapHeight);
layoutDecorated(scrap, scrapRect.left, scrapRect.top, scrapRect.right, scrapRect.bottom);
startOffset = scrapRect.right;
mLastVisiblePos = i;
if (getState().mItemsFrames.get(i) == null) {
getState().mItemsFrames.put(i, scrapRect);
} else {
getState().mItemsFrames.get(i).set(scrapRect);
}
}
}
/**
* Fill top of the center view
*
* @param recycler
* @param startPosition start position to fill top
* @param startOffset layout start offset
* @param topEdge top edge of the RecycleView
*/
private void fillTop(RecyclerView.Recycler recycler, int startPosition, int startOffset, int topEdge) {
View scrap;
int leftOffset;
int scrapWidth, scrapHeight;
Rect scrapRect = new Rect();
int width = getHorizontalSpace();
for (int i = startPosition; i >= 0 && startOffset > topEdge; i--) {
scrap = recycler.getViewForPosition(i);
addView(scrap, 0);
measureChildWithMargins(scrap, 0, 0);
scrapWidth = getDecoratedMeasuredWidth(scrap);
scrapHeight = getDecoratedMeasuredHeight(scrap);
leftOffset = (int) (getPaddingLeft() + (width - scrapWidth) / 2.0f);
scrapRect.set(leftOffset, startOffset - scrapHeight, leftOffset + scrapWidth, startOffset);
layoutDecorated(scrap, scrapRect.left, scrapRect.top, scrapRect.right, scrapRect.bottom);
startOffset = scrapRect.top;
mFirstVisiblePosition = i;
if (getState().mItemsFrames.get(i) == null) {
getState().mItemsFrames.put(i, scrapRect);
} else {
getState().mItemsFrames.get(i).set(scrapRect);
}
}
}
/**
* Fill bottom of the center view
*
* @param recycler
* @param startPosition start position to fill bottom
* @param startOffset layout start offset
* @param bottomEdge bottom edge of the RecycleView
*/
private void fillBottom(RecyclerView.Recycler recycler, int startPosition, int startOffset, int bottomEdge) {
View scrap;
int leftOffset;
int scrapWidth, scrapHeight;
Rect scrapRect = new Rect();
int width = getHorizontalSpace();
for (int i = startPosition; i < getItemCount() && startOffset < bottomEdge; i++) {
scrap = recycler.getViewForPosition(i);
addView(scrap);
measureChildWithMargins(scrap, 0, 0);
scrapWidth = getDecoratedMeasuredWidth(scrap);
scrapHeight = getDecoratedMeasuredHeight(scrap);
leftOffset = (int) (getPaddingLeft() + (width - scrapWidth) / 2.0f);
scrapRect.set(leftOffset, startOffset, leftOffset + scrapWidth, startOffset + scrapHeight);
layoutDecorated(scrap, scrapRect.left, scrapRect.top, scrapRect.right, scrapRect.bottom);
startOffset = scrapRect.bottom;
mLastVisiblePos = i;
if (getState().mItemsFrames.get(i) == null) {
getState().mItemsFrames.put(i, scrapRect);
} else {
getState().mItemsFrames.get(i).set(scrapRect);
}
}
}
private void fillCover(RecyclerView.Recycler recycler, RecyclerView.State state, int scrollDelta) {
if (getItemCount() == 0) {
return;
}
if (mOrientation == HORIZONTAL) {
fillWithHorizontal(recycler, state, scrollDelta);
} else {
fillWithVertical(recycler, state, scrollDelta);
}
if (mItemTransformer != null) {
View child;
for (int i = 0; i < getChildCount(); i++) {
child = getChildAt(i);
mItemTransformer.transformItem(this, child, calculateToCenterFraction(child, scrollDelta));
}
}
}
private float calculateToCenterFraction(View child, float pendingOffset) {
int distance = calculateDistanceCenter(child, pendingOffset);
int childLength = mOrientation == GalleryLayoutManager.HORIZONTAL ? child.getWidth() : child.getHeight();
if (BuildConfig.DEBUG) {
Log.d(TAG, "calculateToCenterFraction: distance:" + distance + ",childLength:" + childLength);
}
return Math.max(-1.f, Math.min(1.f, distance * 1.f / childLength));
}
/**
* @param child
* @param pendingOffset child view will scroll by
* @return
*/
private int calculateDistanceCenter(View child, float pendingOffset) {
OrientationHelper orientationHelper = getOrientationHelper();
int parentCenter = (orientationHelper.getEndAfterPadding() - orientationHelper.getStartAfterPadding()) / 2 + orientationHelper.getStartAfterPadding();
if (mOrientation == GalleryLayoutManager.HORIZONTAL) {
return (int) (child.getWidth() / 2 - pendingOffset + child.getLeft() - parentCenter);
} else {
return (int) (child.getHeight() / 2 - pendingOffset + child.getTop() - parentCenter);
}
}
/**
* @param recycler
* @param state
* @param dy
*/
private void fillWithVertical(RecyclerView.Recycler recycler, RecyclerView.State state, int dy) {
if (BuildConfig.DEBUG) {
Log.d(TAG, "fillWithVertical: dy:" + dy);
}
int topEdge = getOrientationHelper().getStartAfterPadding();
int bottomEdge = getOrientationHelper().getEndAfterPadding();
//1.remove and recycle the view that disappear in screen
View child;
if (getChildCount() > 0) {
if (dy >= 0) {
//remove and recycle the top off screen view
int fixIndex = 0;
for (int i = 0; i < getChildCount(); i++) {
child = getChildAt(i + fixIndex);
if (getDecoratedBottom(child) - dy < topEdge) {
if (BuildConfig.DEBUG) {
Log.v(TAG, "fillWithVertical: removeAndRecycleView:" + getPosition(child) + ",bottom:" + getDecoratedBottom(child));
}
removeAndRecycleView(child, recycler);
mFirstVisiblePosition++;
fixIndex--;
} else {
if (BuildConfig.DEBUG) {
Log.d(TAG, "fillWithVertical: break:" + getPosition(child) + ",bottom:" + getDecoratedBottom(child));
}
break;
}
}
} else { //dy<0
//remove and recycle the bottom off screen view
for (int i = getChildCount() - 1; i >= 0; i--) {
child = getChildAt(i);
if (getDecoratedTop(child) - dy > bottomEdge) {
if (BuildConfig.DEBUG) {
Log.v(TAG, "fillWithVertical: removeAndRecycleView:" + getPosition(child));
}
removeAndRecycleView(child, recycler);
mLastVisiblePos--;
} else {
break;
}
}
}
}
int startPosition = mFirstVisiblePosition;
int startOffset = -1;
int scrapWidth, scrapHeight;
Rect scrapRect;
int width = getHorizontalSpace();
int leftOffset;
View scrap;
//2.Add or reattach item view to fill screen
if (dy >= 0) {
if (getChildCount() != 0) {
View lastView = getChildAt(getChildCount() - 1);
startPosition = getPosition(lastView) + 1;
startOffset = getDecoratedBottom(lastView);
}
for (int i = startPosition; i < getItemCount() && startOffset < bottomEdge + dy; i++) {
scrapRect = getState().mItemsFrames.get(i);
scrap = recycler.getViewForPosition(i);
addView(scrap);
if (scrapRect == null) {
scrapRect = new Rect();
getState().mItemsFrames.put(i, scrapRect);
}
measureChildWithMargins(scrap, 0, 0);
scrapWidth = getDecoratedMeasuredWidth(scrap);
scrapHeight = getDecoratedMeasuredHeight(scrap);
leftOffset = (int) (getPaddingLeft() + (width - scrapWidth) / 2.0f);
if (startOffset == -1 && startPosition == 0) {
//layout the first position item in center
int top = (int) (getPaddingTop() + (getVerticalSpace() - scrapHeight) / 2.f);
scrapRect.set(leftOffset, top, leftOffset + scrapWidth, top + scrapHeight);
} else {
scrapRect.set(leftOffset, startOffset, leftOffset + scrapWidth, startOffset + scrapHeight);
}
layoutDecorated(scrap, scrapRect.left, scrapRect.top, scrapRect.right, scrapRect.bottom);
startOffset = scrapRect.bottom;
mLastVisiblePos = i;
if (BuildConfig.DEBUG) {
Log.d(TAG, "fillWithVertical: add view:" + i + ",startOffset:" + startOffset + ",mLastVisiblePos:" + mLastVisiblePos + ",bottomEdge" + bottomEdge);
}
}
} else {
//dy<0
if (getChildCount() > 0) {
View firstView = getChildAt(0);
startPosition = getPosition(firstView) - 1; //前一个View的position
startOffset = getDecoratedTop(firstView);
}
for (int i = startPosition; i >= 0 && startOffset > topEdge + dy; i--) {
scrapRect = getState().mItemsFrames.get(i);
scrap = recycler.getViewForPosition(i);
addView(scrap, 0);
if (scrapRect == null) {
scrapRect = new Rect();
getState().mItemsFrames.put(i, scrapRect);
}
measureChildWithMargins(scrap, 0, 0);
scrapWidth = getDecoratedMeasuredWidth(scrap);
scrapHeight = getDecoratedMeasuredHeight(scrap);
leftOffset = (int) (getPaddingLeft() + (width - scrapWidth) / 2.0f);
scrapRect.set(leftOffset, startOffset - scrapHeight, leftOffset + scrapWidth, startOffset);
layoutDecorated(scrap, scrapRect.left, scrapRect.top, scrapRect.right, scrapRect.bottom);
startOffset = scrapRect.top;
mFirstVisiblePosition = i;
}
}
}
/**
* @param recycler
* @param state
*/
private void fillWithHorizontal(RecyclerView.Recycler recycler, RecyclerView.State state, int dx) {
int leftEdge = getOrientationHelper().getStartAfterPadding();
int rightEdge = getOrientationHelper().getEndAfterPadding();
if (BuildConfig.DEBUG) {
Log.v(TAG, "fillWithHorizontal() called with: dx = [" + dx + "],leftEdge:" + leftEdge + ",rightEdge:" + rightEdge);
}
//1.remove and recycle the view that disappear in screen
View child;
if (getChildCount() > 0) {
if (dx >= 0) {
//remove and recycle the left off screen view
int fixIndex = 0;
for (int i = 0; i < getChildCount(); i++) {
child = getChildAt(i + fixIndex);
if (getDecoratedRight(child) - dx < leftEdge) {
removeAndRecycleView(child, recycler);
mFirstVisiblePosition++;
fixIndex--;
if (BuildConfig.DEBUG) {
Log.v(TAG, "fillWithHorizontal:removeAndRecycleView:" + getPosition(child) + " mFirstVisiblePosition change to:" + mFirstVisiblePosition);
}
} else {
break;
}
}
} else { //dx<0
//remove and recycle the right off screen view
for (int i = getChildCount() - 1; i >= 0; i--) {
child = getChildAt(i);
if (getDecoratedLeft(child) - dx > rightEdge) {
removeAndRecycleView(child, recycler);
mLastVisiblePos--;
if (BuildConfig.DEBUG) {
Log.v(TAG, "fillWithHorizontal:removeAndRecycleView:" + getPosition(child) + "mLastVisiblePos change to:" + mLastVisiblePos);
}
}
}
}
}
//2.Add or reattach item view to fill screen
int startPosition = mFirstVisiblePosition;
int startOffset = -1;
int scrapWidth, scrapHeight;
Rect scrapRect;
int height = getVerticalSpace();
int topOffset;
View scrap;
if (dx >= 0) {
if (getChildCount() != 0) {
View lastView = getChildAt(getChildCount() - 1);
startPosition = getPosition(lastView) + 1; //start layout from next position item
startOffset = getDecoratedRight(lastView);
if (BuildConfig.DEBUG) {
Log.d(TAG, "fillWithHorizontal:to right startPosition:" + startPosition + ",startOffset:" + startOffset + ",rightEdge:" + rightEdge);
}
}
for (int i = startPosition; i < getItemCount() && startOffset < rightEdge + dx; i++) {
scrapRect = getState().mItemsFrames.get(i);
scrap = recycler.getViewForPosition(i);
addView(scrap);
if (scrapRect == null) {
scrapRect = new Rect();
getState().mItemsFrames.put(i, scrapRect);
}
measureChildWithMargins(scrap, 0, 0);
scrapWidth = getDecoratedMeasuredWidth(scrap);
scrapHeight = getDecoratedMeasuredHeight(scrap);
topOffset = (int) (getPaddingTop() + (height - scrapHeight) / 2.0f);
if (startOffset == -1 && startPosition == 0) {
// layout the first position item in center
int left = (int) (getPaddingLeft() + (getHorizontalSpace() - scrapWidth) / 2.f);
scrapRect.set(left, topOffset, left + scrapWidth, topOffset + scrapHeight);
} else {
scrapRect.set(startOffset, topOffset, startOffset + scrapWidth, topOffset + scrapHeight);
}
layoutDecorated(scrap, scrapRect.left, scrapRect.top, scrapRect.right, scrapRect.bottom);
startOffset = scrapRect.right;
mLastVisiblePos = i;
if (BuildConfig.DEBUG) {
Log.d(TAG, "fillWithHorizontal,layout:mLastVisiblePos: " + mLastVisiblePos);
}
}
} else {
//dx<0
if (getChildCount() > 0) {
View firstView = getChildAt(0);
startPosition = getPosition(firstView) - 1; //start layout from previous position item
startOffset = getDecoratedLeft(firstView);
if (BuildConfig.DEBUG) {
Log.d(TAG, "fillWithHorizontal:to left startPosition:" + startPosition + ",startOffset:" + startOffset + ",leftEdge:" + leftEdge + ",child count:" + getChildCount());
}
}
for (int i = startPosition; i >= 0 && startOffset > leftEdge + dx; i--) {
scrapRect = getState().mItemsFrames.get(i);
scrap = recycler.getViewForPosition(i);
addView(scrap, 0);
if (scrapRect == null) {
scrapRect = new Rect();
getState().mItemsFrames.put(i, scrapRect);
}
measureChildWithMargins(scrap, 0, 0);
scrapWidth = getDecoratedMeasuredWidth(scrap);
scrapHeight = getDecoratedMeasuredHeight(scrap);
topOffset = (int) (getPaddingTop() + (height - scrapHeight) / 2.0f);
scrapRect.set(startOffset - scrapWidth, topOffset, startOffset, topOffset + scrapHeight);
layoutDecorated(scrap, scrapRect.left, scrapRect.top, scrapRect.right, scrapRect.bottom);
startOffset = scrapRect.left;
mFirstVisiblePosition = i;
}
}
}
private int getHorizontalSpace() {
return getWidth() - getPaddingRight() - getPaddingLeft();
}
private int getVerticalSpace() {
return getHeight() - getPaddingBottom() - getPaddingTop();
}
public State getState() {
if (mState == null) {
mState = new State();
}
return mState;
}
private int calculateScrollDirectionForPosition(int position) {
if (getChildCount() == 0) {
return LAYOUT_START;
}
final int firstChildPos = mFirstVisiblePosition;
return position < firstChildPos ? LAYOUT_START : LAYOUT_END;
}
@Override
public PointF computeScrollVectorForPosition(int targetPosition) {
final int direction = calculateScrollDirectionForPosition(targetPosition);
PointF outVector = new PointF();
if (direction == 0) {
return null;
}
if (mOrientation == HORIZONTAL) {
outVector.x = direction;
outVector.y = 0;
} else {
outVector.x = 0;
outVector.y = direction;
}
return outVector;
}
/**
* @author chensuilun
*/
class State {
/**
* Record all item view 's last position after last layout
*/
SparseArray<Rect> mItemsFrames;
/**
* RecycleView 's current scroll distance since first layout
*/
int mScrollDelta;
public State() {
mItemsFrames = new SparseArray<Rect>();
mScrollDelta = 0;
}
}
@Override
public boolean canScrollHorizontally() {
return mOrientation == HORIZONTAL;
}
@Override
public boolean canScrollVertically() {
return mOrientation == VERTICAL;
}
@Override
public int scrollHorizontallyBy(int dx, RecyclerView.Recycler recycler, RecyclerView.State state) {
// When dx is positive,finger fling from right to left(←),scrollX+
if (getChildCount() == 0 || dx == 0) {
return 0;
}
int delta = -dx;
int parentCenter = (getOrientationHelper().getEndAfterPadding() - getOrientationHelper().getStartAfterPadding()) / 2 + getOrientationHelper().getStartAfterPadding();
View child;
if (dx > 0) {
//If we've reached the last item, enforce limits
if (getPosition(getChildAt(getChildCount() - 1)) == getItemCount() - 1) {
child = getChildAt(getChildCount() - 1);
delta = -Math.max(0, Math.min(dx, (child.getRight() - child.getLeft()) / 2 + child.getLeft() - parentCenter));
}
} else {
//If we've reached the first item, enforce limits
if (mFirstVisiblePosition == 0) {
child = getChildAt(0);
delta = -Math.min(0, Math.max(dx, ((child.getRight() - child.getLeft()) / 2 + child.getLeft()) - parentCenter));
}
}
if (BuildConfig.DEBUG) {
Log.d(TAG, "scrollHorizontallyBy: dx:" + dx + ",fixed:" + delta);
}
getState().mScrollDelta = -delta;
fillCover(recycler, state, -delta);
offsetChildrenHorizontal(delta);
return -delta;
}
@Override
public int scrollVerticallyBy(int dy, RecyclerView.Recycler recycler, RecyclerView.State state) {
if (getChildCount() == 0 || dy == 0) {
return 0;
}
int delta = -dy;
int parentCenter = (getOrientationHelper().getEndAfterPadding() - getOrientationHelper().getStartAfterPadding()) / 2 + getOrientationHelper().getStartAfterPadding();
View child;
if (dy > 0) {
//If we've reached the last item, enforce limits
if (getPosition(getChildAt(getChildCount() - 1)) == getItemCount() - 1) {
child = getChildAt(getChildCount() - 1);
delta = -Math.max(0, Math.min(dy, (getDecoratedBottom(child) - getDecoratedTop(child)) / 2 + getDecoratedTop(child) - parentCenter));
}
} else {
//If we've reached the first item, enforce limits
if (mFirstVisiblePosition == 0) {
child = getChildAt(0);
delta = -Math.min(0, Math.max(dy, (getDecoratedBottom(child) - getDecoratedTop(child)) / 2 + getDecoratedTop(child) - parentCenter));
}
}
if (BuildConfig.DEBUG) {
Log.d(TAG, "scrollVerticallyBy: dy:" + dy + ",fixed:" + delta);
}
getState().mScrollDelta = -delta;
fillCover(recycler, state, -delta);
offsetChildrenVertical(delta);
return -delta;
}
public OrientationHelper getOrientationHelper() {
if (mOrientation == HORIZONTAL) {
if (mHorizontalHelper == null) {
mHorizontalHelper = OrientationHelper.createHorizontalHelper(this);
}
return mHorizontalHelper;
} else {
if (mVerticalHelper == null) {
mVerticalHelper = OrientationHelper.createVerticalHelper(this);
}
return mVerticalHelper;
}
}
/**
* @author chensuilun
*/
public static class LayoutParams extends RecyclerView.LayoutParams {
public LayoutParams(Context c, AttributeSet attrs) {
super(c, attrs);
}
public LayoutParams(int width, int height) {
super(width, height);
}
public LayoutParams(ViewGroup.MarginLayoutParams source) {
super(source);
}
public LayoutParams(ViewGroup.LayoutParams source) {
super(source);
}
public LayoutParams(RecyclerView.LayoutParams source) {
super(source);
}
}
private ItemTransformer mItemTransformer;
public void setItemTransformer(ItemTransformer itemTransformer) {
mItemTransformer = itemTransformer;
}
/**
* A ItemTransformer is invoked whenever a attached item is scrolled.
* This offers an opportunity for the application to apply a custom transformation
* to the item views using animation properties.
*/
public interface ItemTransformer {
/**
* Apply a property transformation to the given item.
*
* @param layoutManager Current LayoutManager
* @param item Apply the transformation to this item
* @param fraction of page relative to the current front-and-center position of the pager.
* 0 is front and center. 1 is one full
* page position to the right, and -1 is one page position to the left.
*/
void transformItem(GalleryLayoutManager layoutManager, View item, float fraction);
}
/**
* Listen for changes to the selected item
*
* @author chensuilun
*/
public interface OnItemSelectedListener {
/**
* @param recyclerView The RecyclerView which item view belong to.
* @param item The current selected view
* @param position The current selected view's position
*/
void onItemSelected(RecyclerView recyclerView, View item, int position, int mState);
}
private OnItemSelectedListener mOnItemSelectedListener;
public void setOnItemSelectedListener(OnItemSelectedListener onItemSelectedListener) {
mOnItemSelectedListener = onItemSelectedListener;
}
public void attach(RecyclerView recyclerView) {
this.attach(recyclerView, -1);
}
/**
* @param recyclerView
* @param selectedPosition
*/
public void attach(RecyclerView recyclerView, int selectedPosition) {
if (recyclerView == null) {
throw new IllegalArgumentException("The attach RecycleView must not null!!");
}
mRecyclerView = recyclerView;
mInitialSelectedPosition = Math.max(0, selectedPosition);
recyclerView.setLayoutManager(this);
mSnapHelper.attachToRecyclerView(recyclerView);
recyclerView.addOnScrollListener(mInnerScrollListener);
Log.d(TAG,"addOnScrollListener");
}
RecyclerView mRecyclerView;
public void setCallbackInFling(boolean callbackInFling) {
mCallbackInFling = callbackInFling;
}
/**
* Inner Listener to listen for changes to the selected item
*
* @author chensuilun
*/
private class InnerScrollListener extends RecyclerView.OnScrollListener {
int mState;
boolean mCallbackOnIdle;
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
View snap = mSnapHelper.findSnapView(recyclerView.getLayoutManager());
if (snap != null) {
int selectedPosition = recyclerView.getLayoutManager().getPosition(snap);
if (selectedPosition != mCurSelectedPosition) {
if (mCurSelectedView != null) {
mCurSelectedView.setSelected(false);
}
mCurSelectedView = snap;
mCurSelectedView.setSelected(true);
mCurSelectedPosition = selectedPosition;
if (!mCallbackInFling && mState != SCROLL_STATE_IDLE) {
if (BuildConfig.DEBUG) {
Log.v(TAG, "ignore selection change callback when fling ");
}
mCallbackOnIdle = true;
return;
}
Log.v(TAG, "ignore selection change callback when fling " +mState);
if (mOnItemSelectedListener != null&&mState == SCROLL_STATE_IDLE) {
mOnItemSelectedListener.onItemSelected(recyclerView, snap, mCurSelectedPosition,mState);
}
}
}
if (BuildConfig.DEBUG) {
Log.v(TAG, "onScrolled: dx:" + dx + ",dy:" + dy);
}
}
@Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
mState = newState;
if (BuildConfig.DEBUG) {
Log.v(TAG, "onScrollStateChanged: " + newState);
}
if (mState == SCROLL_STATE_IDLE) {
View snap = mSnapHelper.findSnapView(recyclerView.getLayoutManager());
if (snap != null) {
int selectedPosition = recyclerView.getLayoutManager().getPosition(snap);
if (selectedPosition != mCurSelectedPosition) {
if (mCurSelectedView != null) {
mCurSelectedView.setSelected(false);
}
mCurSelectedView = snap;
mCurSelectedView.setSelected(true);
mCurSelectedPosition = selectedPosition;
if (mOnItemSelectedListener != null) {
mOnItemSelectedListener.onItemSelected(recyclerView, snap, mCurSelectedPosition,mState);
}
} else if (!mCallbackInFling && mOnItemSelectedListener != null && mCallbackOnIdle) {
mCallbackOnIdle = false;
mOnItemSelectedListener.onItemSelected(recyclerView, snap, mCurSelectedPosition,mState);
}else {
Log.v(TAG, "onScrollStateChanged: " + newState+" mCallbackInFling:"+mCallbackInFling+" mCallbackOnIdle: "+mCallbackOnIdle);
if (mOnItemSelectedListener != null) {
mOnItemSelectedListener.onItemSelected(recyclerView, snap, mCurSelectedPosition,mState);
}
}
} else {
}
}
}
}
@Override
public void smoothScrollToPosition(RecyclerView recyclerView, RecyclerView.State state, int position) {
GallerySmoothScroller linearSmoothScroller = new GallerySmoothScroller(recyclerView.getContext());
linearSmoothScroller.setTargetPosition(position);
startSmoothScroll(linearSmoothScroller);
}
/**
* Implement to support {@link GalleryLayoutManager#smoothScrollToPosition(RecyclerView, RecyclerView.State, int)}
*/
private class GallerySmoothScroller extends LinearSmoothScroller {
public GallerySmoothScroller(Context context) {
super(context);
}
/**
* Calculates the horizontal scroll amount necessary to make the given view in center of the RecycleView
*
* @param view The view which we want to make in center of the RecycleView
* @return The horizontal scroll amount necessary to make the view in center of the RecycleView
*/
public int calculateDxToMakeCentral(View view) {
final RecyclerView.LayoutManager layoutManager = getLayoutManager();
if (layoutManager == null || !layoutManager.canScrollHorizontally()) {
return 0;
}
final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) view.getLayoutParams();
final int left = layoutManager.getDecoratedLeft(view) - params.leftMargin;
final int right = layoutManager.getDecoratedRight(view) + params.rightMargin;
final int start = layoutManager.getPaddingLeft();
final int end = layoutManager.getWidth() - layoutManager.getPaddingRight();
final int childCenter = left + (int) ((right - left) / 2.0f);
final int containerCenter = (int) ((end - start) / 2.f);
return containerCenter - childCenter;
}
/**
* Calculates the vertical scroll amount necessary to make the given view in center of the RecycleView
*
* @param view The view which we want to make in center of the RecycleView
* @return The vertical scroll amount necessary to make the view in center of the RecycleView
*/
public int calculateDyToMakeCentral(View view) {
final RecyclerView.LayoutManager layoutManager = getLayoutManager();
if (layoutManager == null || !layoutManager.canScrollVertically()) {
return 0;
}
final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams)
view.getLayoutParams();
final int top = layoutManager.getDecoratedTop(view) - params.topMargin;
final int bottom = layoutManager.getDecoratedBottom(view) + params.bottomMargin;
final int start = layoutManager.getPaddingTop();
final int end = layoutManager.getHeight() - layoutManager.getPaddingBottom();
final int childCenter = top + (int) ((bottom - top) / 2.0f);
final int containerCenter = (int) ((end - start) / 2.f);
return containerCenter - childCenter;
}
@Override
protected void onTargetFound(View targetView, RecyclerView.State state, Action action) {
final int dx = calculateDxToMakeCentral(targetView);
final int dy = calculateDyToMakeCentral(targetView);
final int distance = (int) Math.sqrt(dx * dx + dy * dy);
final int time = calculateTimeForDeceleration(distance);
if (time > 0) {
action.update(-dx, -dy, time, mDecelerateInterpolator);
}
}
}
}
\ No newline at end of file
package com.test.basd.smartjunkcleaner.activity.photocompress.photo
import android.content.Context
import android.provider.MediaStore
import android.text.TextUtils
import java.text.SimpleDateFormat
import java.util.Date
import java.util.Locale
object ImagesMediaUtils {
private val PHOTO_TILTLE = 1
private val PHOTO_IMAGE = 0
private fun getFirstPhotoAlbum(name: String, context: Context): String {
val list = getPhotosForFolder(name, context)
if (list.isNotEmpty()) {
return list[1].src
}
return ""
}
fun getAlbums(context: Context): List<AlbumBean> {
val folderNames = HashMap<String, Int>()
val albums: MutableList<AlbumBean> = ArrayList()
val projection =
arrayOf(MediaStore.Images.Media.BUCKET_DISPLAY_NAME, MediaStore.Images.Media._ID)
val cursor = context.contentResolver.query(
MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
projection,
null,
null,
null
)
cursor?.use {
val bucketIndex =
cursor.getColumnIndexOrThrow(MediaStore.Images.Media.BUCKET_DISPLAY_NAME)
var i = 1
while (cursor.moveToNext()) {
val folderName = cursor.getString(bucketIndex)
if (!TextUtils.isEmpty(folderName)) {
if (folderNames.containsKey(folderName)) {
folderNames[folderName] = (folderNames[folderName] ?: 0) + 1
} else {
folderNames[folderName] = 1
}
}
}
}
var mCount: Int = 0
folderNames.entries.withIndex().forEach { (index, entry) ->
val (t, u) = entry
mCount += u
albums.add(AlbumBean(t, u, getFirstPhotoAlbum(t, context)))
if (index == (folderNames.size-1)) {
albums.add(0, AlbumBean("Recently", mCount, getFirstPhotoAlbum("Recently", context)))
}
}
return albums
}
fun getPhotosForFolder(folderName: String, context: Context): ArrayList<PhotoBean> {
val photos = ArrayList<PhotoBean>()
val projection = arrayOf(
MediaStore.Images.Media.DATA,
MediaStore.Images.Media.DATE_ADDED,
MediaStore.Images.Media.SIZE,
MediaStore.Images.Media.WIDTH,
MediaStore.Images.Media.HEIGHT
)
// val selection = "${MediaStore.Images.Media.BUCKET_DISPLAY_NAME} = ?"
// val selectionArgs = arrayOf(folderName)
val sortOrder = "${MediaStore.Images.Media.DATE_ADDED} DESC" // 按时间降序排序
val cursor = if (folderName == "Recently") {
context.contentResolver.query(
MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
projection,
null,
null,
sortOrder
)
} else {
val selection = "${MediaStore.Images.Media.BUCKET_DISPLAY_NAME} = ?"
val selectionArgs = arrayOf(folderName)
context.contentResolver.query(
MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
projection,
selection,
selectionArgs,
sortOrder
)
}
cursor?.use {
val dataIndex = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA)
val dateIndex = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATE_ADDED)
val sizeIndex = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.SIZE)
val widthIndex = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.WIDTH)
val heightIndex = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.HEIGHT)
while (cursor.moveToNext()) {
val photoPath = cursor.getString(dataIndex)
val photoDate = cursor.getLong(dateIndex)
val photoSize = cursor.getLong(sizeIndex)
val photoWidth = cursor.getInt(widthIndex)
val photoHeight = cursor.getInt(heightIndex)
val date = timestampToDate(photoDate, "yyyy/MM")
if (photos.size <= 0) {
photos.add(PhotoBean(PHOTO_TILTLE, date, folderName, "", 0, "", ""))
} else {
val lastData = photos[photos.size - 1]
if (!TextUtils.equals(lastData.date, date)) {
photos.add(PhotoBean(PHOTO_TILTLE, date, folderName, "", 0, "", ""))
}
}
val entity = PhotoBean(
PHOTO_IMAGE,
date,
folderName,
photoPath,
photoSize,
formatFileSize(photoSize),
"$photoWidth x $photoHeight"
)
photos.add(entity)
}
}
return photos
}
private fun timestampToDate(timestamp: Long, dateFormat: String): String {
val date = Date(timestamp * 1000) //
val formatter = SimpleDateFormat(dateFormat, Locale.getDefault())
return formatter.format(date)
}
fun formatFileSize(size: Long): String {
if (size <= 0) return "0B"
val units = arrayOf("B", "KB", "MB", "GB", "TB")
val digitGroups = (Math.log10(size.toDouble()) / Math.log10(1024.0)).toInt()
return String.format(
"%.1f %s",
size / Math.pow(1024.0, digitGroups.toDouble()),
units[digitGroups]
)
}
}
\ No newline at end of file
package com.test.basd.smartjunkcleaner.activity.photocompress.photo
interface OnItemClickListener<T> {
fun onUpdateBtn(isChecked :Boolean , entity: T )
fun onItemClick(position:Int , entity : T)
}
\ No newline at end of file
package com.test.basd.smartjunkcleaner.activity.photocompress.photo
class PhotoBean {
var type: Int = 0
var date: String = ""
var folderName: String = ""
var src: String = ""
var size: Long = 0
var sizeText: String = ""
var whRatio: String = ""
constructor(
type: Int,
date: String,
folderName: String,
src: String,
size: Long,
sizeText: String,
whRatio: String
) {
this.type = type
this.date = date
this.folderName = folderName
this.src = src
this.size = size
this.sizeText = sizeText
this.whRatio = whRatio
}
override fun toString(): String {
return "PhotoBean(type=$type, date='$date', folderName='$folderName', src='$src', size=$size, sizeText='$sizeText', whRatio='$whRatio')"
}
}
\ No newline at end of file
package com.test.basd.smartjunkcleaner.activity.photocompress.photo;
import java.util.ArrayList;
import java.util.List;
public class PhotoCache {
private static PhotoCache sInstance;
private List<PhotoBean> mPhotoList;
private PhotoCache() {
mPhotoList = new ArrayList<>();
}
public static synchronized PhotoCache getInstance() {
if (sInstance == null) {
sInstance = new PhotoCache();
}
return sInstance;
}
public void addPhoto(PhotoBean photo) {
if (!mPhotoList.contains(photo)) {
boolean mBoolean = mPhotoList.add(photo);
}
}
public void removePhoto(PhotoBean photo) {
if (mPhotoList.contains(photo)) {
boolean mBoolean = mPhotoList.remove(photo);
}
}
public void clearCache() {
if(!mPhotoList.isEmpty()){
mPhotoList.clear();
}
}
public List<PhotoBean> getPhotoList() {
return mPhotoList;
}
public PhotoBean getPositionData(int position) {
if (position >= 0 && position < mPhotoList.size()) {
return mPhotoList.get(position);
} else {
return null;
}
}
public int getCount() {
return mPhotoList.size();
}
}
package com.test.basd.smartjunkcleaner.activity.photocompress.photo
import android.app.Activity
import android.content.Context
import android.content.Intent
import android.graphics.drawable.Drawable
import android.view.View
import android.view.View.OnClickListener
import android.widget.TextView
import androidx.core.content.ContextCompat
import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.GridLayoutManager.SpanSizeLookup
import androidx.recyclerview.widget.RecyclerView
import androidx.recyclerview.widget.SimpleItemAnimator
import com.test.basd.smartjunkcleaner.R
class PhotoListManager(context: Activity) : OnItemClickListener<PhotoBean>, SpanSizeLookup(), OnClickListener {
private var mRecycleView: RecyclerView? = null
private var mBtnContinue: TextView? = null
private val mSelectAll: TextView
private val mContext: Context = context
private var mCompressionPhotoListAdapter: CompressionPhotoListAdapter? = null
init {
mRecycleView = context.findViewById(R.id.content)
mBtnContinue = context.findViewById(R.id.btn_continue)
mSelectAll = context.findViewById(R.id.selectAll)
init()
}
private fun init() {
(mRecycleView?.itemAnimator as SimpleItemAnimator).supportsChangeAnimations = false
mCompressionPhotoListAdapter = CompressionPhotoListAdapter(this)
val layoutManager = GridLayoutManager(mContext, 3)
layoutManager.spanSizeLookup = (this)
mRecycleView?.layoutManager = layoutManager
mRecycleView?.adapter = mCompressionPhotoListAdapter
mBtnContinue?.setOnClickListener(this)
mSelectAll.setOnClickListener(this)
}
override fun getSpanSize(position: Int): Int {
val entity = mCompressionPhotoListAdapter?.getData()?.get(position)
return if ((entity?.type ?: 0) != 0) {
3
} else {
1
}
}
override fun onUpdateBtn(isChecked: Boolean, entity: PhotoBean) {
PhotoCache.getInstance().let {
if (isChecked) it.addPhoto(entity) else it.removePhoto(entity)
}
updateBtnContinue()
}
override fun onItemClick(position: Int, entity: PhotoBean) {
}
fun onSelectAlbum(album: AlbumBean) {
val list = ImagesMediaUtils.getPhotosForFolder(album.name, mContext)
if (list.isNotEmpty()) {
PhotoCache.getInstance().clearCache()
mCompressionPhotoListAdapter?.updateData(list)
updateBtnContinue()
}
}
override fun onClick(v: View?) {
when (v?.id) {
R.id.btn_continue -> {
val intent = Intent(mContext, PreviewCompressionPhotoActivity::class.java)
intent.putExtra("count", PhotoCache.getInstance().photoList.size)
mContext.startActivity(intent)
}
R.id.selectAll -> {
mCompressionPhotoListAdapter?.setNoOrYesselectAll((mCompressionPhotoListAdapter?.isSelectAll() != true))
}
}
}
private fun updateBtnContinue() {
mBtnContinue?.isEnabled = PhotoCache.getInstance().photoList.size > 0
val int = PhotoCache.getInstance().photoList.size
mBtnContinue?.text = if (mBtnContinue?.isEnabled == true) "CONTINUE ($int)" else "CONTINUE"
mCompressionPhotoListAdapter?.isSelectAll()
val drawable: Drawable? = if (mCompressionPhotoListAdapter?.isSelectAll() == true) {
ContextCompat.getDrawable(mContext, R.mipmap.quanxuan_s)
} else {
ContextCompat.getDrawable(mContext, R.mipmap.quanxuan_n)
}
mSelectAll.setCompoundDrawablesRelativeWithIntrinsicBounds(null, drawable, null, null)
}
}
\ No newline at end of file
package com.test.basd.smartjunkcleaner.activity.photocompress.photo
import android.app.Activity
import android.graphics.Bitmap
import android.util.Log
import android.view.View
import android.widget.ImageView
import android.widget.ProgressBar
import android.widget.RelativeLayout
import android.widget.SeekBar
import android.widget.SeekBar.OnSeekBarChangeListener
import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView
import com.bumptech.glide.Glide
import com.test.basd.smartjunkcleaner.R
class PreviewCompressionManager : GalleryLayoutManager.OnItemSelectedListener, CompressionImageUtils.BitmapCallback,
OnSeekBarChangeListener {
private val mContext: Activity
private var galleryRecyclerView: RecyclerView? = null
private var galleryAdapter: GalleryAdapter? = null
private val mList: List<PhotoBean>
private var mTitleView: TextView? = null
private var mPreviewView: RelativeLayout? = null
private var mLoadingView: ProgressBar? = null
private val mBeforeImage: ImageView
private val mAfterImage: ImageView
private var mBeforeSize: TextView? = null
private var mAfterSize: TextView? = null
private val mSeekBar: SeekBar
private var mIndex = 0
constructor(mContext: Activity) {
this.mContext = mContext
mList = PhotoCache.getInstance().photoList
galleryRecyclerView = mContext.findViewById(R.id.galleryRecyclerView)
mPreviewView = mContext.findViewById(R.id.preview_view)
mLoadingView = mContext.findViewById(R.id.img_loading)
mBeforeImage = mContext.findViewById(R.id.before_image)
mAfterImage = mContext.findViewById(R.id.after_image)
mBeforeSize = mContext.findViewById(R.id.before_size)
mAfterSize = mContext.findViewById(R.id.after_size)
mTitleView = mContext.findViewById(R.id.title)
mSeekBar = mContext.findViewById(R.id.seekBar)
initView()
}
private fun initView() {
galleryAdapter = GalleryAdapter(mContext, mList)
val manager = GalleryLayoutManager(GalleryLayoutManager.HORIZONTAL)
manager.setCallbackInFling(true) //
manager.setOnItemSelectedListener(this)
manager.attach(galleryRecyclerView)
manager.setItemTransformer(Transformer())
galleryRecyclerView?.adapter = galleryAdapter
mSeekBar.setOnSeekBarChangeListener(this)
}
private var lastRefreshTime = 0L
override fun onItemSelected(recyclerView: RecyclerView?, item: View?, position: Int, newState: Int) {
val currentTime = System.currentTimeMillis()
if (newState != RecyclerView.SCROLL_STATE_IDLE || currentTime - lastRefreshTime < 300) { // 只有在不滑动时才执行图片压缩操作
return
}
lastRefreshTime = currentTime
mIndex = position
Log.d("glc","")
refreshPreviewImage(true)
}
override fun onBitmapReady(bitmap: Bitmap?, size: Long, position: Int) {
if (mIndex != position) {
return
}
mContext.runOnUiThread {
val entity = mList[position]
mPreviewView?.visibility = View.VISIBLE
mLoadingView?.visibility = View.GONE
if (mBeforeImage != null) {
Glide.with(mContext).load(entity.src).into(mBeforeImage)
}
mBeforeSize?.text = entity.sizeText
mAfterSize?.text = ImagesMediaUtils.formatFileSize(size)
if (mAfterImage != null) {
Glide.with(mContext).load(bitmap).into(mAfterImage)
}
}
}
private fun loading(boolean: Boolean) {
mPreviewView?.visibility = View.GONE
mLoadingView?.visibility = View.VISIBLE
if (boolean) {
mBeforeSize?.text = "Loading..."
}
mAfterSize?.text = "Loading..."
}
override fun onProgressChanged(seekBar: SeekBar?, progress: Int, fromUser: Boolean) {
when {
progress <= 25 -> seekBar?.progress = 0
progress in 26..75 -> seekBar?.progress = 50
progress > 75 -> seekBar?.progress = 100
}
}
override fun onStartTrackingTouch(seekBar: SeekBar?) {
}
private var lastProgress = 50
override fun onStopTrackingTouch(seekBar: SeekBar?) {
val progress = seekBar?.progress ?: 0
when {
seekBar?.progress ?: 0 <= 1 -> seekBar?.progress = 0
(seekBar?.progress ?: 0) > 1 && (seekBar?.progress ?: 0) <= 50 -> seekBar?.progress =
50
seekBar?.progress ?: 0 > 50 -> seekBar?.progress = 100
}
if (progress != lastProgress) {
refreshPreviewImage(false)
lastProgress = progress
}
}
private fun refreshPreviewImage(boolean: Boolean) {
loading(boolean)
val entity = mList[mIndex]
mTitleView?.text = "Preview (" + (mIndex + 1) + "/${mList.size})"
CompressionImageUtils.compressImage(entity.src, mSeekBar.progress, mIndex, this)
}
fun getMode() = mSeekBar.progress
}
\ No newline at end of file
package com.test.basd.smartjunkcleaner.activity.photocompress.photo
import android.content.Intent
import android.graphics.Color
import android.widget.TextView
import androidx.core.view.updatePadding
import com.blankj.utilcode.util.BarUtils
import com.test.basd.smartjunkcleaner.R
import com.test.basd.smartjunkcleaner.databinding.ActivityPreviewCompressionPhotoBinding
import com.test.basd.smartjunkcleaner.helps.BaseActivity
class PreviewCompressionPhotoActivity : BaseActivity<ActivityPreviewCompressionPhotoBinding>() {
override val isLightMode = true
private var mPhotoCount: Int = 0
override val binding: ActivityPreviewCompressionPhotoBinding by lazy {
ActivityPreviewCompressionPhotoBinding.inflate(layoutInflater)
}
override fun initView() {
BarUtils.setStatusBarColor(this, Color.TRANSPARENT)
binding.root.updatePadding(top = BarUtils.getStatusBarHeight())
mPhotoCount = intent.getIntExtra("count", 0)
binding.fanhui.setOnClickListener {
finish()
}
val manager = PreviewCompressionManager(this)
findViewById<TextView>(R.id.btn_compress).setOnClickListener {
val dialog = CustomDialog(this, R.layout.custom_dialog)
dialog.setButtonClickListener(R.id.cancel) {
dialog.dismiss()
}
dialog.setButtonClickListener(R.id.btnContinue) {
CompressionPhotoListActivity.mActivity?.finish()
val intent = Intent(this, CompressioningActivity::class.java)
intent.putExtra("mode", manager.getMode())
startActivity(intent)
dialog.dismiss()
finish()
}
dialog.show()
}
}
}
\ No newline at end of file
package com.test.basd.smartjunkcleaner.activity.photocompress.photo;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Rect;
import android.view.Display;
import android.view.View;
import android.view.WindowManager;
import androidx.recyclerview.widget.RecyclerView;
public class RecyclerItemCenterDecoration extends RecyclerView.ItemDecoration {
/**
* 自定义默认的Item的边距
*/
private int mPageMargin = 5;//dp
/**
* 第一个item的左边距
*/
private int mLeftPageVisibleWidth;
@Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
// 计算一下第一个item距离屏幕左边的距离:(屏幕的宽度-item的宽度)/2。其中item的宽度=实际ImagView的宽度+margin。
view.measure(0,0);
// 默认值
int childViewWidth = dpToPx(120);
if(view.getMeasuredWidth() > 0){
childViewWidth = view.getMeasuredWidth();
}
//获取实际居左距离
mLeftPageVisibleWidth = (getScreenWidth(view.getContext()) / 2 - childViewWidth / 2);
//获取当前Item的position
int position = parent.getChildAdapterPosition(view);
//获得Item的数量
int itemCount = parent.getAdapter().getItemCount();
int leftMagin = 0;
int rightMagin = 0;
if(position == 0 && itemCount == 1){
}else if(position == 0){
if(mLeftPageVisibleWidth < dpToPx(mPageMargin)){
leftMagin = mLeftPageVisibleWidth;
rightMagin = mLeftPageVisibleWidth;
}else{
leftMagin = mLeftPageVisibleWidth;
rightMagin = dpToPx(mPageMargin);
//leftMagin = 0;
}
}else if(position == itemCount-1){
if(mLeftPageVisibleWidth < dpToPx(mPageMargin)){
rightMagin = mLeftPageVisibleWidth;
leftMagin = mLeftPageVisibleWidth;
}else{
rightMagin = mLeftPageVisibleWidth;
leftMagin = dpToPx(mPageMargin);
}
}else{
leftMagin = dpToPx(mPageMargin);
rightMagin = dpToPx(mPageMargin);
}
RecyclerView.LayoutParams layoutParams = (RecyclerView.LayoutParams) view.getLayoutParams();
//10,10分别是item到上下的margin
layoutParams.setMargins(leftMagin,10,rightMagin,10);
view.setLayoutParams(layoutParams);
super.getItemOffsets(outRect, view, parent, state);
}
/**
* d p转换成px
* @param dp:
*/
private int dpToPx(int dp){
return (int) (dp * Resources.getSystem().getDisplayMetrics().density + 0.5f);
}
/**
* 获取屏幕的宽度
* @param context:
* @return :
*/
public static int getScreenWidth(Context context) {
WindowManager manager = (WindowManager) context
.getSystemService(Context.WINDOW_SERVICE);
Display display = manager.getDefaultDisplay();
int screenWidth = display.getWidth();
return screenWidth;
}
}
\ No newline at end of file
package com.test.basd.smartjunkcleaner.activity.photocompress.photo
import android.content.res.Resources
import android.graphics.Bitmap
import android.graphics.BitmapShader
import android.graphics.Canvas
import android.graphics.Paint
import android.graphics.RectF
import android.graphics.Shader
import com.bumptech.glide.load.engine.bitmap_recycle.BitmapPool
import com.bumptech.glide.load.resource.bitmap.BitmapTransformation
import java.security.MessageDigest
class RoundedCornersTransformation(private val radiusInDp: Float) : BitmapTransformation() {
override fun updateDiskCacheKey(messageDigest: MessageDigest) {
}
private val radius: Float = Resources.getSystem().displayMetrics.density * radiusInDp
override fun transform(pool: BitmapPool, toTransform: Bitmap, outWidth: Int, outHeight: Int): Bitmap {
val bitmap = pool.get(outWidth, outHeight, Bitmap.Config.ARGB_8888)
val canvas = Canvas(bitmap)
val paint = Paint(Paint.ANTI_ALIAS_FLAG)
val shader = BitmapShader(toTransform, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP)
paint.shader = shader
val rect = RectF(0f, 0f, outWidth.toFloat(), outHeight.toFloat())
canvas.drawRoundRect(rect, radius, radius, paint)
return bitmap
}
fun getId(): String {
return "RoundedCornersTransformation(radius=$radius)"
}
}
package com.test.basd.smartjunkcleaner.activity.photocompress.photo;
import android.content.Context;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
public class SlideView extends View {
private int mLastX;
private int mMoveX;
public SlideView(Context context) {
super(context);
}
public SlideView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public SlideView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
mLastX = (int) event.getRawX();
break;
case MotionEvent.ACTION_MOVE:
int x = (int) event.getRawX();
int dx = x - mLastX;
mMoveX += dx;
mLastX = x;
layout(getLeft() + dx, getTop(), getRight() + dx, getBottom());
break;
case MotionEvent.ACTION_UP:
if (mMoveX < 0) {
animate().translationX(-mMoveX).setDuration(300).start();
} else {
animate().translationX(0).setDuration(300).start();
}
mMoveX = 0;
break;
}
return true;
}
}
\ No newline at end of file
package com.test.basd.smartjunkcleaner.activity.photocompress.photo
import android.content.Intent
import android.graphics.Color
import android.os.Handler
import androidx.activity.OnBackPressedCallback
import androidx.core.view.updatePadding
import com.blankj.utilcode.util.BarUtils
import com.blankj.utilcode.util.ToastUtils
import com.test.basd.smartjunkcleaner.databinding.ActivityStartCompressionPhotoBinding
import com.test.basd.smartjunkcleaner.helps.BaseActivity
import com.test.basd.smartjunkcleaner.helps.ads.AdmobUtils
import kotlin.random.Random
class StartCompressionPhotoActivity : BaseActivity<ActivityStartCompressionPhotoBinding>() {
override val binding: ActivityStartCompressionPhotoBinding by lazy {
ActivityStartCompressionPhotoBinding.inflate(layoutInflater)
}
override fun initView() {
BarUtils.setStatusBarColor(this, Color.TRANSPARENT)
binding.root.updatePadding(top = BarUtils.getStatusBarHeight())
onBackPressedDispatcher.addCallback(object : OnBackPressedCallback(true) {
override fun handleOnBackPressed() {
finishToMain()
}
})
}
override fun onStart() {
super.onStart()
checkPermission()
}
var isplay = 0
override fun onPermissionsResult(isGranted: Boolean) {
if (isGranted) {
isplay += 1
if (isplay == 1) {
playlottie()
}
} else {
finishToMain()
}
}
private fun playlottie(){
binding.idLottie1.imageAssetsFolder = "ya_smax_suo/images/"
binding.idLottie1.setAnimation("ya_smax_suo/data.json")
binding.idLottie1.playAnimation()
Handler().postDelayed({
AdmobUtils.showInterstitialAd(this) {
val mList = ImagesMediaUtils.getAlbums(this)
if (mList.isNotEmpty()) {
startActivity(Intent(this, CompressionPhotoListActivity::class.java))
finish()
} else {
ToastUtils.showShort("Album photos not detected, no compression required.")
}
}
},Random.nextLong(3000,4000))
}
}
\ No newline at end of file
package com.test.basd.smartjunkcleaner.activity.photocompress.photo;
import android.view.View;
public class Transformer implements GalleryLayoutManager.ItemTransformer {
@Override
public void transformItem(GalleryLayoutManager layoutManager, View item, float fraction) {
//以圆心进行缩放
item.setPivotX(item.getWidth() / 2.0f);
item.setPivotY(item.getHeight());
float scale = 1 - 0.3f * Math.abs(fraction);
item.setScaleX(scale);
item.setScaleY(scale);
}
}
\ No newline at end of file
package com.test.basd.smartjunkcleaner.activity.photocompress.photo
import android.content.Context
import android.content.res.Resources
object Utils {
fun calculateImageWidth(context: Context): Int {
val displayMetrics = context.resources.displayMetrics
val screenWidth = displayMetrics.widthPixels
return (screenWidth - 14.dpToPx() * 2 - 4.dpToPx() * 2) / 3
}
fun Int.dpToPx(): Int {
val displayMetrics = Resources.getSystem().displayMetrics
return (this * displayMetrics.density + 0.5f).toInt()
}
}
\ No newline at end of file
package com.test.basd.smartjunkcleaner.activity.speed
import android.animation.Animator
import android.content.Intent
import android.graphics.Color
import androidx.core.view.isVisible
import androidx.core.view.updatePadding
import com.blankj.utilcode.util.BarUtils
import com.test.basd.smartjunkcleaner.databinding.ActivityLayoutSpeedBinding
import com.test.basd.smartjunkcleaner.helps.BaseActivity
import com.test.basd.smartjunkcleaner.view.AFunOb.BATTERY_OPTIMIZER
class BatteryOptimizerActivity : BaseActivity<ActivityLayoutSpeedBinding>() {
override val binding: ActivityLayoutSpeedBinding by lazy {
ActivityLayoutSpeedBinding.inflate(layoutInflater)
}
override fun initView() {
BarUtils.setStatusBarColor(this, Color.TRANSPARENT)
binding.root.updatePadding(top = BarUtils.getStatusBarHeight())
binding.idOneLottie.imageAssetsFolder = "dian_smax_chi/images/"
binding.idOneLottie.setAnimation("dian_smax_chi/data.json")
binding.idOneLottie.playAnimation()
binding.root.postDelayed({
binding.idOneLottie.isVisible = false
binding.idScaning.text = "Optimizing"
playRocket()
}, 3000)
}
private fun playRocket() {
binding.idRocket.imageAssetsFolder = "battery_smax_save/images/"
binding.idRocket.setAnimation("battery_smax_save/data.json")
binding.idRocket.playAnimation()
binding.idRocket.addAnimatorListener(object : Animator.AnimatorListener {
override fun onAnimationStart(p0: Animator) {
}
override fun onAnimationEnd(p0: Animator) {
startActivity(Intent(this@BatteryOptimizerActivity, SpeedFinishActivity::class.java).putExtra("type", BATTERY_OPTIMIZER))
finish()
}
override fun onAnimationCancel(p0: Animator) {
}
override fun onAnimationRepeat(p0: Animator) {
}
})
}
}
\ No newline at end of file
package com.test.basd.smartjunkcleaner.activity.speed
import android.animation.Animator
import android.content.Intent
import android.graphics.Color
import androidx.core.view.isVisible
import androidx.core.view.updatePadding
import com.blankj.utilcode.util.AppUtils
import com.blankj.utilcode.util.BarUtils
import com.test.basd.smartjunkcleaner.databinding.ActivityLayoutSpeedBinding
import com.test.basd.smartjunkcleaner.helps.BaseActivity
import com.test.basd.smartjunkcleaner.helps.ConfigHelper
import com.test.basd.smartjunkcleaner.view.AFunOb.APP_SPEED
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.MainScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import kotlin.random.Random
class SpeedActivity : BaseActivity<ActivityLayoutSpeedBinding>() {
override val binding: ActivityLayoutSpeedBinding by lazy {
ActivityLayoutSpeedBinding.inflate(layoutInflater)
}
override fun initView() {
BarUtils.setStatusBarColor(this, Color.TRANSPARENT)
binding.root.updatePadding(top = BarUtils.getStatusBarHeight())
binding.idOneLottie.imageAssetsFolder = "guan_smax_li/images/"
binding.idOneLottie.setAnimation("guan_smax_li/data.json")
binding.idOneLottie.playAnimation()
binding.root.postDelayed({
binding.idOneLottie.isVisible = false
binding.idScaning.text = "Optimizing"
playRocket()
}, 3000)
}
private fun playRocket() {
binding.idRocket.imageAssetsFolder = "fast_smax/images/"
binding.idRocket.setAnimation("fast_smax/data.json")
binding.idRocket.playAnimation()
MainScope().launch(Dispatchers.Main) {
launch {
if (ConfigHelper.appList.isNullOrEmpty()) {
withContext(Dispatchers.IO) {
ConfigHelper.appList = AppUtils.getAppsInfo().shuffled()
}
}
val icons = ConfigHelper.appList?.filter { !it.isSystem }?.map { it.icon }
var index = 0
binding.idSpeedIcon.isVisible = true
while (binding.idRocket.isVisible) {
binding.idSpeedIcon.setImageDrawable(icons?.getOrNull(index++ % icons.size))
delay(Random.nextLong(100, 200))
}
}
}
binding.idRocket.addAnimatorListener(object : Animator.AnimatorListener {
override fun onAnimationStart(p0: Animator) {
}
override fun onAnimationEnd(p0: Animator) {
startActivity(
Intent(
this@SpeedActivity,
SpeedFinishActivity::class.java
).putExtra("type", APP_SPEED)
)
finish()
}
override fun onAnimationCancel(p0: Animator) {
}
override fun onAnimationRepeat(p0: Animator) {
}
})
}
}
\ No newline at end of file
package com.test.basd.smartjunkcleaner.activity.speed
import android.animation.Animator
import android.content.Intent
import android.graphics.Color
import androidx.core.view.updatePadding
import com.blankj.utilcode.util.BarUtils
import com.test.basd.smartjunkcleaner.activity.ResultActivity
import com.test.basd.smartjunkcleaner.databinding.ActivityLayoutSpeedFinishBinding
import com.test.basd.smartjunkcleaner.helps.BaseActivity
import com.test.basd.smartjunkcleaner.helps.ads.AdmobUtils
class SpeedFinishActivity : BaseActivity<ActivityLayoutSpeedFinishBinding>() {
override val binding: ActivityLayoutSpeedFinishBinding by lazy {
ActivityLayoutSpeedFinishBinding.inflate(layoutInflater)
}
override fun initView() {
BarUtils.setStatusBarColor(this, Color.TRANSPARENT)
val type=intent.getStringExtra("type")
binding.root.updatePadding(top = BarUtils.getStatusBarHeight())
binding.idSpeedFinish.addAnimatorListener(object : Animator.AnimatorListener {
override fun onAnimationStart(p0: Animator) {
}
override fun onAnimationEnd(p0: Animator) {
AdmobUtils.showInterstitialAd(this@SpeedFinishActivity) {
startActivity(
Intent(
this@SpeedFinishActivity,
ResultActivity::class.java
).putExtra("from", type)
)
finish()
}
}
override fun onAnimationCancel(p0: Animator) {
}
override fun onAnimationRepeat(p0: Animator) {
}
})
}
}
\ No newline at end of file
package com.test.basd.smartjunkcleaner.activity.splash
import android.Manifest
import android.annotation.SuppressLint
import android.content.Intent
import android.graphics.Color
import android.os.Build
import android.os.Handler
import android.util.Log
import androidx.activity.result.contract.ActivityResultContracts
import androidx.core.view.isVisible
import com.blankj.utilcode.util.AppUtils
import com.blankj.utilcode.util.BarUtils
import com.test.basd.smartjunkcleaner.databinding.ActivityLayoutSplashBinding
import com.test.basd.smartjunkcleaner.display.CloseNotificationReceiver
import com.test.basd.smartjunkcleaner.display.NotificationHelper
import com.test.basd.smartjunkcleaner.helps.BaseActivity
import com.test.basd.smartjunkcleaner.helps.ConfigHelper
import com.test.basd.smartjunkcleaner.helps.LogEx
import com.test.basd.smartjunkcleaner.helps.ads.AdmobUtils
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.MainScope
import kotlinx.coroutines.launch
@SuppressLint("CustomSplashScreen")
class NewSplashActivity : BaseActivity<ActivityLayoutSplashBinding>(),
PrivacyAgreementManager.onUserPrivacyAggreementListener, ProgressManager.ProgressListener {
private val TAG = "NewSplashActivity"
private fun initStatusBar() {
BarUtils.setStatusBarLightMode(this, true)
BarUtils.setStatusBarColor(this, Color.TRANSPARENT)
if (Build.VERSION.SDK_INT >= 33) {
registerForActivityResult(ActivityResultContracts.RequestPermission()) {}.launch(
Manifest.permission.POST_NOTIFICATIONS
)
}
}
override val binding: ActivityLayoutSplashBinding by lazy {
ActivityLayoutSplashBinding.inflate(layoutInflater)
}
private var mProgressManager: ProgressManager? = null
override fun initView() {
cancelNotification()
initStatusBar()
if (isDestroyed) {
return
}
mProgressManager = ProgressManager(binding, this)
if (ConfigHelper.ifAgreePrivacy) {
binding.idTvStart.isVisible = false
binding.idLlJindu.isVisible = true
binding.idLlYinsi.isVisible = false
onAgreePrivacy()
} else {
PrivacyAgreementManager(binding, this, this)
}
}
override fun onAgreePrivacy() {
MainScope().launch(Dispatchers.IO) {
ConfigHelper.appList = AppUtils.getAppsInfo().shuffled()
}
mProgressManager?.startProgress()
loadAd()
}
override fun onProgressMax() {
Handler().postDelayed({
SplashJumpUtils.jumpNextPage(this)
}, 500)
}
private fun loadAd() {
AdmobUtils.loadAppOpenAd {
if (isFinishing || isDestroyed) {
return@loadAppOpenAd
} else {
if (!isPause) {
mProgressManager?.pauseProgress()
runOnUiThread {
AdmobUtils.showAppOpenAd(this@NewSplashActivity) {
Log.d("glc", "showAppOpenAd")
mProgressManager?.maxProgress()
}
}
}
}
}
AdmobUtils.loadInterstitialAd(this)
// AdmobUtils.loadNativeAd()
}
override fun onNewIntent(intent: Intent) {
super.onNewIntent(intent)
setIntent(intent)
}
private var isPause = false
override fun onStart() {
super.onStart()
isPause = false
}
override fun onPause() {
super.onPause()
isPause = true
}
override fun onResume() {
super.onResume()
isPause = false
}
override fun onStop() {
super.onStop()
isPause = true
}
private fun cancelNotification() {
var jumpType = intent.getIntExtra("type", 0)
if (jumpType == 0) {
val uri = intent.data
val str = (uri?.getQueryParameter("type") ?: "0")
jumpType = str.toIntOrNull() ?: 0
}
LogEx.logDebug(TAG, "jumpType=$jumpType")
if (jumpType != 0) {
val cancelIntent = Intent(this, CloseNotificationReceiver::class.java)
cancelIntent.action = CloseNotificationReceiver.action
cancelIntent.putExtra("notificationId", NotificationHelper.notificationId)
sendBroadcast(cancelIntent)
}
}
}
\ No newline at end of file
package com.test.basd.smartjunkcleaner.activity.splash
import android.app.Activity
import android.content.Intent
import android.net.Uri
import android.text.SpannableString
import android.text.Spanned
import android.text.style.UnderlineSpan
import androidx.core.view.isVisible
import com.test.basd.smartjunkcleaner.MyApplication
import com.test.basd.smartjunkcleaner.databinding.ActivityLayoutSplashBinding
import com.test.basd.smartjunkcleaner.helps.ConfigHelper
import com.test.basd.smartjunkcleaner.helps.EventHelper
class PrivacyAgreementManager {
private val binding: ActivityLayoutSplashBinding
private val context: Activity
private val listener: onUserPrivacyAggreementListener
constructor(binding: ActivityLayoutSplashBinding, context: Activity, listener: onUserPrivacyAggreementListener) {
this.binding = binding
this.context = context
this.listener = listener
initView()
}
private fun initView() {
val spannableString = SpannableString("Privacy Policy")
spannableString.setSpan(
UnderlineSpan(),
0,
spannableString.length,
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
)
binding.idTvPrivacyPolicy.text = spannableString
binding.idTvPrivacyPolicy.setOnClickListener {
val intent = Intent(
Intent.ACTION_VIEW,
Uri.parse("https://sites.google.com/view/super-cleaner-max/super-cleaner-max")
)
context.startActivity(intent)
}
binding.idTvStart.setOnClickListener {
binding.idTvStart.isVisible = false
binding.idLlJindu.isVisible = true
binding.idLlYinsi.isVisible = false
ConfigHelper.ifAgreePrivacy = true
(context.application as MyApplication).initApp()
EventHelper.event("click_start_to_use")
EventHelper.event("page_${javaClass.simpleName}")
listener.onAgreePrivacy()
}
}
interface onUserPrivacyAggreementListener {
fun onAgreePrivacy()
}
}
\ No newline at end of file
package com.test.basd.smartjunkcleaner.activity.splash
import android.os.Handler
import com.blankj.utilcode.util.SPUtils
import com.test.basd.smartjunkcleaner.databinding.ActivityLayoutSplashBinding
class ProgressManager {
private val binding: ActivityLayoutSplashBinding
private var mHandler: Handler
private var mIsPaused = false
private var mProgress = 0
private val listener:ProgressListener
constructor(binding: ActivityLayoutSplashBinding, listener: ProgressListener) {
this.binding = binding
this.listener = listener
mHandler = Handler()
initView()
}
private fun initView() {
val loadTime = SPUtils.getInstance().getInt("loading_page_time", 15)
binding.pb.max = loadTime
binding.pb.progress = 0
}
fun startProgress() {
val mRunnable: Runnable = object : Runnable {
override fun run() {
if (!mIsPaused) {
mProgress++ // 计算进度
binding.pb.progress = mProgress
if (mProgress < 9) {
mHandler.postDelayed(this, 1000) // 每秒钟更新一次进度
} else {
listener.onProgressMax()
pauseProgress()
}
}
}
}
mHandler.postDelayed(mRunnable, 1000)
}
fun pauseProgress() {
if (!mIsPaused) {
mIsPaused = true
mHandler.removeCallbacksAndMessages(null)
}
}
fun maxProgress() {
binding.pb.progress = binding.pb.max
listener.onProgressMax()
}
fun resumeProgress() {
if (mIsPaused) {
mIsPaused = false
startProgress()
}
}
interface ProgressListener{
fun onProgressMax()
}
}
\ No newline at end of file
package com.test.basd.smartjunkcleaner.activity.splash
import android.app.Activity
import android.content.Intent
import com.blankj.utilcode.util.SPUtils
import com.test.basd.smartjunkcleaner.activity.GuestActivity
import com.test.basd.smartjunkcleaner.activity.AppManagerActivity
import com.test.basd.smartjunkcleaner.activity.BatteryInfoActivity
import com.test.basd.smartjunkcleaner.activity.LargeFileCleanActivity
import com.test.basd.smartjunkcleaner.activity.MainActivity
import com.test.basd.smartjunkcleaner.activity.NetWorkActivity
import com.test.basd.smartjunkcleaner.activity.RecentAppActivity
import com.test.basd.smartjunkcleaner.activity.RepeaterdPhotoActivity
import com.test.basd.smartjunkcleaner.activity.ScanJunkActivity
import com.test.basd.smartjunkcleaner.activity.SpeakerCleanerActivity
import com.test.basd.smartjunkcleaner.activity.photocompress.photo.StartCompressionPhotoActivity
import com.test.basd.smartjunkcleaner.bean.ConfigBean.Companion.ID_APP_MANAGER
//import com.test.basd.cleanmaster.bean.ConfigBean.Companion.ID_BATTERY_OPTIMIZATION
import com.test.basd.smartjunkcleaner.bean.ConfigBean.Companion.ID_CHARGE
import com.test.basd.smartjunkcleaner.bean.ConfigBean.Companion.ID_CLEAN_NOTIFICATION
import com.test.basd.smartjunkcleaner.bean.ConfigBean.Companion.ID_CLEAN_SPEAKER
import com.test.basd.smartjunkcleaner.bean.ConfigBean.Companion.ID_INSTALL_PACKAGE_PUSH
import com.test.basd.smartjunkcleaner.bean.ConfigBean.Companion.ID_JUNK_CLEAN_PUSH
import com.test.basd.smartjunkcleaner.bean.ConfigBean.Companion.ID_LARGE_FILE_PUSH
import com.test.basd.smartjunkcleaner.bean.ConfigBean.Companion.ID_LOW_BATTERY_PUSH
import com.test.basd.smartjunkcleaner.bean.ConfigBean.Companion.ID_NETWORK_TRAFFIC
//import com.test.basd.cleanmaster.bean.ConfigBean.Companion.ID_PHONE_ACCELERATE
import com.test.basd.smartjunkcleaner.bean.ConfigBean.Companion.ID_PHOTO_COMPRESS
import com.test.basd.smartjunkcleaner.bean.ConfigBean.Companion.ID_RECENT_USE_APP
import com.test.basd.smartjunkcleaner.bean.ConfigBean.Companion.ID_SIMILAR_IMAGE
import com.test.basd.smartjunkcleaner.bean.ConfigBean.Companion.ID_UNINSTALL_PACKAGE_PUSH
//import com.test.basd.cleanmaster.bean.ConfigBean.Companion.ID_VIRUS_PUSH
import com.test.basd.smartjunkcleaner.helps.ConfigHelper
import com.test.basd.smartjunkcleaner.helps.EventHelper
import com.test.basd.smartjunkcleaner.notificationclean.NotificationCleanActivity
import com.test.basd.smartjunkcleaner.notificationclean.NotificationGuestActivity
object SplashJumpUtils {
fun jumpNextPage(context: Activity) {
var jumpType = context.intent.getIntExtra("type", 0)
if (jumpType == 0) {
val uri = context.intent.data
val str = (uri?.getQueryParameter("type") ?: "0")
jumpType = str.toIntOrNull() ?: 0
if (jumpType != 0) {
EventHelper.event("notification_jump", "jumpType=$jumpType")
}
}
when (jumpType) {
//=================================主动广播=======================================
ID_JUNK_CLEAN_PUSH -> {
context.startActivity(Intent(context, ScanJunkActivity::class.java))
}
// ID_VIRUS_PUSH -> {
// context.startActivity(Intent(context, VirusActivity::class.java))
// }
ID_LARGE_FILE_PUSH -> {
context.startActivity(Intent(context, LargeFileCleanActivity::class.java))
}
ID_PHOTO_COMPRESS -> {
context.startActivity(Intent(context, StartCompressionPhotoActivity::class.java))
}
ID_APP_MANAGER -> {
context.startActivity(Intent(context, AppManagerActivity::class.java))
}
ID_NETWORK_TRAFFIC -> {
context.startActivity(Intent(context, NetWorkActivity::class.java))
}
ID_CLEAN_NOTIFICATION -> {
if (SPUtils.getInstance().getBoolean("notification_guest", false)) {
context.startActivity(Intent(context, NotificationCleanActivity::class.java))
} else {
context.startActivity(Intent(context, NotificationGuestActivity::class.java))
}
}
ID_RECENT_USE_APP -> {
context.startActivity(Intent(context, RecentAppActivity::class.java))
}
ID_SIMILAR_IMAGE -> {
context.startActivity(Intent(context, RepeaterdPhotoActivity::class.java))
}
ID_CLEAN_SPEAKER -> {
context.startActivity(Intent(context, SpeakerCleanerActivity::class.java))
}
// ID_BATTERY_OPTIMIZATION -> {
// context.startActivity(Intent(context, BatteryOptimizerActivity::class.java))
// }
//================================被动广播=========================================
ID_INSTALL_PACKAGE_PUSH -> {
context.startActivity(Intent(context, ScanJunkActivity::class.java))
}
ID_UNINSTALL_PACKAGE_PUSH -> {
context.startActivity(Intent(context, ScanJunkActivity::class.java))
}
ID_CHARGE -> {
context.startActivity(Intent(context, BatteryInfoActivity::class.java))
}
ID_LOW_BATTERY_PUSH -> {
context.startActivity(Intent(context, BatteryInfoActivity::class.java))
}
// ID_PHONE_ACCELERATE -> {
// context.startActivity(Intent(context, SpeedActivity::class.java))
// }
else -> {
val isHotLaunch = context.intent?.extras?.getBoolean("isHotLaunch", false) ?: false
if (!isHotLaunch) {
if (!ConfigHelper.ifGuest) {
context.startActivity(Intent(context, GuestActivity::class.java))
} else {
context.startActivity(Intent(context, MainActivity::class.java))
}
}
}
}
context.finish()
}
}
\ No newline at end of file
package com.test.basd.smartjunkcleaner.adapter
import android.annotation.SuppressLint
import android.view.View
import android.view.ViewGroup
import androidx.core.content.ContextCompat
import androidx.recyclerview.widget.RecyclerView
import androidx.recyclerview.widget.RecyclerView.ViewHolder
import com.blankj.utilcode.util.SPUtils
import com.test.basd.smartjunkcleaner.R
import com.test.basd.smartjunkcleaner.databinding.ItemResultFunBinding
import com.test.basd.smartjunkcleaner.view.AFunOb.APP_MANAGER
import com.test.basd.smartjunkcleaner.view.AFunOb.BATTERY_INFO
import com.test.basd.smartjunkcleaner.view.AFunOb.JUNK_CLEANER
import com.test.basd.smartjunkcleaner.view.AFunOb.LARGE_FILE_CLEANER
import com.test.basd.smartjunkcleaner.view.AFunOb.NETWORK_TRAFFIC
import com.test.basd.smartjunkcleaner.view.AFunOb.NOTIFICATION_CLEANER
import com.test.basd.smartjunkcleaner.view.AFunOb.PHOTO_COMPRESS
import com.test.basd.smartjunkcleaner.view.AFunOb.RECENT_APP_USAGE
import com.test.basd.smartjunkcleaner.view.AFunOb.SIMILAR_PHOTOS
import com.test.basd.smartjunkcleaner.view.XmlEx.inflate
import java.util.Collections
class AppFunctionAdapter(val click: (name: String) -> Unit) :
RecyclerView.Adapter<AppFunctionAdapter.JJJ>() {
val list = arrayListOf(
Fun(JUNK_CLEANER, R.mipmap.cleanjunk_jg, "Clean junk regularly to free up space", "Clean Up"),
Fun(PHOTO_COMPRESS, R.mipmap.photo_jg, "Compress photos to save space", "Compress"),
Fun(LARGE_FILE_CLEANER, R.mipmap.large_jg, "Clean large files to free up storage space", "Clean Up"),
Fun(BATTERY_INFO, R.mipmap.battery_jg, "View battery usage and details", "Check Now"),
Fun(
APP_MANAGER,
R.mipmap.appmanager_jg, "Check apps size and uninstall some apps to release storage space",
"Check Now"
),
Fun(
SIMILAR_PHOTOS,
R.mipmap.similar_jg, "Check similar photos to release more space",
"Clean Up"
),
Fun(
NOTIFICATION_CLEANER,
R.mipmap.notification_jg, "Too many annoying notifications? Block and clean",
"Check Now"
),
Fun(RECENT_APP_USAGE, R.mipmap.recent_jg, "Check and manage recently active apps", "View Now"),
Fun(
NETWORK_TRAFFIC, R.mipmap.network_jg, "View network traffic usage and stop traffic-consuming apps",
"View Now"
)
)
//修改顺序
fun updateListPostion() {
//本次进入结果页,判断使用垃圾的功能是否超过5分钟
val lastUsejunkcleaner = SPUtils.getInstance().getLong("last_use_junk_cleaner", 0)
// Log.e("MXL", "updateListPostion: " + (System.currentTimeMillis() - lastUsejunkcleaner))
if ((System.currentTimeMillis() - lastUsejunkcleaner) >= 60 * 5 * 1000) {
// Log.e("MXL", "updateListPostion: "+"超过5分钟不处理" )
} else {
// Log.e("MXL", "updateListPostion: "+"5分钟之内" )
Collections.rotate(list, list.size - 1)
}
}
class JJJ(view: View) : ViewHolder(view)
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): JJJ {
return JJJ(R.layout.item_result_fun.inflate(parent))
}
override fun getItemCount(): Int {
return list.size
}
override fun onBindViewHolder(holder: JJJ, position: Int) {
val data = list[position]
val context = holder.itemView.context
val binding = ItemResultFunBinding.bind(holder.itemView)
binding.ivIcon.setImageDrawable(ContextCompat.getDrawable(context, data.icon))
binding.tvTittle.text = data.name
binding.tvDes.text = data.des
binding.tvButton.text = data.button
binding.tvButton.setOnClickListener {
click.invoke(data.name)
}
}
@SuppressLint("NotifyDataSetChanged")
fun removeItem(name: String) {
list.removeIf { it.name == name }
notifyDataSetChanged()
}
data class Fun(
val name: String = "",
val icon: Int = 0,
val des: String = "",
val button: String = "",
)
}
\ No newline at end of file
package com.test.basd.smartjunkcleaner.adapter
import android.annotation.SuppressLint
import android.text.format.Formatter
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import androidx.recyclerview.widget.RecyclerView.ViewHolder
import com.test.basd.smartjunkcleaner.R
import com.test.basd.smartjunkcleaner.bean.AppBean
import com.test.basd.smartjunkcleaner.databinding.ItemAppListBinding
import com.test.basd.smartjunkcleaner.view.XmlEx.inflate
import java.text.SimpleDateFormat
import java.util.Date
import java.util.Locale
class AppListAdapter(
val itemClick: (data: AppBean) -> Unit, val itemSelect: ((selectList: List<AppBean>) -> Unit)? = null
) : RecyclerView.Adapter<AppListAdapter.AAAA>() {
private var list = arrayListOf<AppBean>()
inner class AAAA(view: View) : ViewHolder(view) {
val binding = ItemAppListBinding.bind(view)
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): AAAA {
return AAAA(R.layout.item_app_list.inflate(parent))
}
override fun getItemCount(): Int {
return list.size
}
@SuppressLint("SimpleDateFormat")
override fun onBindViewHolder(holder: AAAA, position: Int) {
}
@SuppressLint("SetTextI18n")
override fun onBindViewHolder(holder: AAAA, position: Int, payloads: MutableList<Any>) {
val data = list[position]
val context = holder.itemView.context
if (payloads.isEmpty()) {
holder.binding.tvAppName.text = data.appName
holder.binding.ivIcon.setImageDrawable(data.icIcon)
val dateFormat = SimpleDateFormat("MM dd", Locale.ENGLISH)
val installTimeDate = Date(data.installTime)
val installTime = String.format("%tb", installTimeDate) + " " + String.format("%te", installTimeDate)
holder.binding.tvAppInstall.text = "Installed: $installTime"
if (data.appSize == -1L) {
holder.binding.tvAppSize.text = "Size used: need permission"
} else {
val size = Formatter.formatFileSize(context, data.appSize)
holder.binding.tvAppSize.text = "Size used: $size"
}
when (data.lastUsedTime) {
-1L -> {
holder.binding.tvAppLastUse.text = "Last use: need permission"
}
-2L -> {
holder.binding.tvAppLastUse.text = "Last use: more 30 days no use"
}
else -> {
// val lastTime = dateFormat.format(data.lastUsedTime)
val lastTimeDate = Date(data.lastUsedTime)
val lastTime = String.format("%tb", lastTimeDate) + " " + String.format("%te", lastTimeDate)
holder.binding.tvAppLastUse.text = "Last use: $lastTime"
}
}
holder.binding.ivSelector.isSelected = data.isSelected
holder.binding.ivSelector.setOnClickListener {
data.isSelected = !data.isSelected
holder.binding.ivSelector.isSelected = data.isSelected
notifyItemChanged(position, "sdadd")
val selectedList = list.filter { it.isSelected }
itemSelect?.invoke(selectedList)
}
holder.binding.root.setOnClickListener {
itemClick.invoke(data)
}
} else {
holder.binding.ivSelector.isSelected = data.isSelected
super.onBindViewHolder(holder, position, payloads)
}
}
@SuppressLint("NotifyDataSetChanged")
fun setData(dataList: List<AppBean>) {
list.clear()
list.addAll(dataList)
notifyDataSetChanged()
}
@SuppressLint("NotifyDataSetChanged")
fun removeData(appBean: AppBean) {
list.remove(appBean)
notifyDataSetChanged()
}
}
\ No newline at end of file
package com.test.basd.smartjunkcleaner.adapter
import android.annotation.SuppressLint
import android.text.format.Formatter
import android.view.View
import android.view.ViewGroup
import androidx.core.view.isVisible
import androidx.recyclerview.widget.RecyclerView.Adapter
import androidx.recyclerview.widget.RecyclerView.ViewHolder
import com.bumptech.glide.Glide
import com.test.basd.smartjunkcleaner.R
import com.test.basd.smartjunkcleaner.bean.FileBean
import com.test.basd.smartjunkcleaner.databinding.ItemLargeFileBinding
import com.test.basd.smartjunkcleaner.helps.LogEx
import com.test.basd.smartjunkcleaner.view.XmlEx.inflate
class LargeFileAdapter(
val itemClick: (data: FileBean) -> Unit,
val selectClick: (selectList: List<FileBean>) -> Unit
) : Adapter<LargeFileAdapter.UUU>() {
private val TAG = "BigFileAdapter"
private val files = arrayListOf<FileBean>()
class UUU(view: View) : ViewHolder(view) {
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): UUU {
return UUU(R.layout.item_large_file.inflate(parent))
}
override fun getItemCount(): Int {
return files.size
}
override fun onBindViewHolder(holder: UUU, position: Int) {
}
override fun onBindViewHolder(holder: UUU, position: Int, payloads: MutableList<Any>) {
val binding = ItemLargeFileBinding.bind(holder.itemView)
val data = files[position]
val content = holder.itemView.context
if (payloads.isEmpty()) {
binding.ivPlay.isVisible = data.isVideo()
when {
data.isImage() -> {
Glide.with(content).load(data.path).into(binding.iv)
}
data.isVideo() -> {
Glide.with(content).load(data.path).into(binding.iv)
}
data.isAudio() -> {
binding.iv.setImageResource(R.mipmap.icon_tingyinyue)
}
data.isDoc() -> {
binding.iv.setImageResource(R.mipmap.icon_wendang)
}
data.isApk() -> {
binding.iv.setImageResource(R.mipmap.icon_apk)
}
data.isZip() -> {
binding.iv.setImageResource(R.mipmap.icon_archives)
}
else -> {
binding.iv.setImageResource(R.mipmap.icon_other_file)
}
}
binding.tvName.text = data.name
binding.tvSize.text = Formatter.formatFileSize(content, data.size)
binding.root.setOnClickListener {
itemClick.invoke(data)
}
binding.ivSelector.isSelected = data.isSelect
binding.flSelect.setOnClickListener {
data.isSelect = !data.isSelect
binding.ivSelector.isSelected = data.isSelect
notifyItemChanged(position, "dasds")
val selectList = files.filter { it.isSelect }
selectClick.invoke(selectList)
}
} else {
binding.ivSelector.isSelected = data.isSelect
super.onBindViewHolder(holder, position, payloads)
}
}
@SuppressLint("NotifyDataSetChanged")
fun setData(bigFileList: List<FileBean>) {
bigFileList.forEach {
LogEx.logDebug(TAG, "$it")
}
files.clear()
files.addAll(bigFileList)
files.sortByDescending { it.size }
notifyDataSetChanged()
}
fun getSelectData(): List<FileBean> {
return files.filter { it.isSelect }
}
@SuppressLint("NotifyDataSetChanged")
fun removeData(deleteList: ArrayList<FileBean>) {
files.removeAll(deleteList.toSet())
notifyDataSetChanged()
}
}
\ No newline at end of file
package com.test.basd.smartjunkcleaner.adapter
import android.annotation.SuppressLint
import android.view.View
import android.view.ViewGroup
import androidx.core.content.ContextCompat
import androidx.recyclerview.widget.RecyclerView
import androidx.recyclerview.widget.RecyclerView.ViewHolder
import com.test.basd.smartjunkcleaner.R
import com.test.basd.smartjunkcleaner.bean.PermissionBean
import com.test.basd.smartjunkcleaner.databinding.ItemPermissionBinding
import com.test.basd.smartjunkcleaner.view.XmlEx.inflate
class PermissionAdapter : RecyclerView.Adapter<PermissionAdapter.CCCC>() {
private val permissionBeans = arrayListOf<PermissionBean>()
inner class CCCC(view: View) : ViewHolder(view) {
val binding = ItemPermissionBinding.bind(view)
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CCCC {
return CCCC(R.layout.item_permission.inflate(parent))
}
override fun getItemCount(): Int {
return permissionBeans.size
}
override fun onBindViewHolder(holder: CCCC, position: Int) {
val context = holder.itemView.context
val data = permissionBeans[position]
holder.binding.tvTitle.text = context.getString(data.permissionTittle)
holder.binding.tvDesc.text = context.getString(data.permissionDesc)
if (data.isAuthorized) {
holder.binding.tvAuthorized.visibility = View.VISIBLE
if (data.isSensitive) {
holder.binding.tvAuthorized.background = ContextCompat.getDrawable(context, R.drawable.bg_corners_granted)
holder.binding.tvAuthorized.setTextColor(ContextCompat.getColor(context, R.color.color_fcad45))
} else {
holder.binding.tvAuthorized.background = ContextCompat.getDrawable(context, R.drawable.bg_corners_d9e2fd)
holder.binding.tvAuthorized.setTextColor(ContextCompat.getColor(context, R.color.color_4772ff))
}
} else {
holder.binding.tvAuthorized.visibility = View.GONE
}
}
@SuppressLint("NotifyDataSetChanged")
fun setData(permissions: List<PermissionBean>) {
permissionBeans.clear()
permissionBeans.addAll(permissions)
notifyDataSetChanged()
}
}
\ No newline at end of file
package com.test.basd.smartjunkcleaner.adapter
import android.annotation.SuppressLint
import android.app.Activity
import android.content.Intent
import android.net.Uri
import android.provider.Settings
import android.view.View
import android.view.ViewGroup
import androidx.core.content.ContextCompat
import androidx.recyclerview.widget.RecyclerView
import androidx.recyclerview.widget.RecyclerView.ViewHolder
import com.test.basd.smartjunkcleaner.R
import com.test.basd.smartjunkcleaner.bean.AppBean
import com.test.basd.smartjunkcleaner.databinding.ItemAppRecentBinding
import com.test.basd.smartjunkcleaner.databinding.ItemAppScreenTimeBinding
import com.test.basd.smartjunkcleaner.helps.NetworkStatsHelper
import com.test.basd.smartjunkcleaner.helps.TimeUtils
import com.test.basd.smartjunkcleaner.view.XmlEx.inflate
class RecentAppAdapter(val activity: Activity, val modeUI: String = UI_LAUNCHES_MODE) : RecyclerView.Adapter<RecentAppAdapter.DDD>() {
private var appBeans = arrayListOf<AppBean>()
private var numberMode = UI_MODE_ALL
inner class DDD(view: View) : ViewHolder(view)
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): DDD {
return if (modeUI == UI_LAUNCHES_MODE) {
DDD(R.layout.item_app_recent.inflate(parent))
} else {
DDD(R.layout.item_app_screen_time.inflate(parent))
}
}
override fun getItemCount(): Int {
return appBeans.size
}
@SuppressLint("StringFormatMatches", "SetTextI18n")
override fun onBindViewHolder(holder: DDD, position: Int) {
val context = holder.itemView.context
val data = appBeans[position]
if (modeUI == UI_LAUNCHES_MODE) {
val binding = ItemAppRecentBinding.bind(holder.itemView)
binding.ivIcon.setImageDrawable(data.icIcon)
binding.tvName.text = data.appName
val number = when (numberMode) {
UI_MODE_ALL -> data.launchTimes
UI_MODE_FOREGROUND -> data.foregroundTimes
UI_MODE_BACKGROUND -> data.backgroundTimes
else -> 0
}
binding.tvNumber.text = "$number Launches"
binding.tvStop.background = if (data.isRunning) {
ContextCompat.getDrawable(context, R.drawable.bg_corners_4772ff_2_5)
} else {
ContextCompat.getDrawable(context, R.drawable.bg_corners_bfbfbf)
}
binding.tvStop.setOnClickListener {
if (NetworkStatsHelper.canStop(activity, data.pkg)) {
// 跳转到当前应用的应用信息界面
val intent = Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS)
// 如果要跳转到其他应用的应用信息界面,需要传入那个应用的包名
intent.setData(Uri.parse("package:${data.pkg}"))
context.startActivity(intent)
}
}
val bg = if (NetworkStatsHelper.canStop(activity, data.pkg)) {
R.drawable.bg_corners_4772ff_2_5
} else {
R.drawable.bg_corners_bfbfbf
}
binding.tvStop.background = ContextCompat.getDrawable(context, bg)
}
if (modeUI == UI_SCREEN_TIME_MODE) {
val binding = ItemAppScreenTimeBinding.bind(holder.itemView)
binding.ivIcon.setImageDrawable(data.icIcon)
binding.tvName.text = data.appName
binding.tvManage.setOnClickListener {
// 跳转到当前应用的应用信息界面
val intent = Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS)
// 如果要跳转到其他应用的应用信息界面,需要传入那个应用的包名
intent.setData(Uri.parse("package:${data.pkg}"))
context.startActivity(intent)
}
binding.tvTime.text = TimeUtils.longToHoursAndMinutes(data.screenTime)
val max = appBeans.maxOf { it.screenTime }
binding.progressBar.progress = ((data.screenTime.toFloat() / max.toFloat()) * 100).toInt()
}
}
fun setNumberMode(mode: Int) {
numberMode = mode
}
@SuppressLint("NotifyDataSetChanged")
fun setData(dataList: List<AppBean>) {
appBeans.clear()
appBeans.addAll(dataList)
if (modeUI == UI_LAUNCHES_MODE) {
when (numberMode) {
UI_MODE_ALL -> appBeans.sortByDescending { it.launchTimes }
UI_MODE_FOREGROUND -> appBeans.sortByDescending { it.foregroundTimes }
UI_MODE_BACKGROUND -> appBeans.sortByDescending { it.backgroundTimes }
}
} else {
appBeans.sortByDescending { it.screenTime }
}
notifyDataSetChanged()
}
companion object {
const val UI_MODE_ALL = 5
const val UI_MODE_FOREGROUND = 25
const val UI_MODE_BACKGROUND = 35
const val UI_LAUNCHES_MODE = "LAUNCHES_UI"
const val UI_SCREEN_TIME_MODE = "SCREEN_TIME_UI"
}
}
\ No newline at end of file
package com.test.basd.smartjunkcleaner.adapter
import android.app.Activity
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import androidx.recyclerview.widget.RecyclerView.ViewHolder
import com.test.basd.smartjunkcleaner.R
import com.test.basd.smartjunkcleaner.databinding.ItemAdBinding
import com.test.basd.smartjunkcleaner.databinding.ItemToolGridBinding
import com.test.basd.smartjunkcleaner.databinding.ItemToolsGridBinding
import com.test.basd.smartjunkcleaner.helps.ads.AdmobUtils
import com.test.basd.smartjunkcleaner.view.AFunOb.APP_MANAGER
import com.test.basd.smartjunkcleaner.view.AFunOb.JUNK_CLEANER
import com.test.basd.smartjunkcleaner.view.AFunOb.LARGE_FILE_CLEANER
import com.test.basd.smartjunkcleaner.view.AFunOb.NETWORK_TRAFFIC
import com.test.basd.smartjunkcleaner.view.AFunOb.NOTIFICATION_CLEANER
import com.test.basd.smartjunkcleaner.view.AFunOb.PHOTO_COMPRESS
import com.test.basd.smartjunkcleaner.view.AFunOb.RECENT_APP_USAGE
import com.test.basd.smartjunkcleaner.view.AFunOb.SIMILAR_PHOTOS
import com.test.basd.smartjunkcleaner.view.AFunOb.SPEAK_CLEANER
import com.test.basd.smartjunkcleaner.view.XmlEx.inflate
class ToolsAdapter(
val context: Activity,
val itemClick: (kName: String) -> Unit
) : RecyclerView.Adapter<ToolsAdapter.VVV>() {
private val list = listOf(
ToolsUI(
tittle = "Popular", tools = listOf(
ToolUI(JUNK_CLEANER, context.getString(R.string.clean_junk), R.mipmap.cleanjunk_tools),
ToolUI(PHOTO_COMPRESS, context.getString(R.string.photo_compress), R.mipmap.photo_tools),
ToolUI(SIMILAR_PHOTOS, "Similar Photos", R.mipmap.similar_tools),
ToolUI(LARGE_FILE_CLEANER, context.getString(R.string.large_file_cleaner), R.mipmap.large_tools),
ToolUI(APP_MANAGER, context.getString(R.string.app_manager), R.mipmap.appmanager_tools),
ToolUI(SPEAK_CLEANER, context.getString(R.string.speaker_cleaner), R.mipmap.speaker_tools),
)
),
// ToolsUI(isAd = true),
ToolsUI(
tittle = "More",
tools = listOf(
ToolUI(NOTIFICATION_CLEANER, context.getString(R.string.notification_cleaner), R.mipmap.notification_tools),
ToolUI(NETWORK_TRAFFIC, context.getString(R.string.network_traffic), R.mipmap.network_tools),
ToolUI(RECENT_APP_USAGE, context.getString(R.string.recent_app_usage), R.mipmap.recentapp_tools),
)
),
)
override fun getItemViewType(position: Int): Int {
val data = list[position]
return if (data.isAd) 0 else 1
}
inner class VVV(view: View) : ViewHolder(view)
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): VVV {
val viewId = if (viewType == 0) R.layout.item_ad else R.layout.item_tools_grid
return VVV(viewId.inflate(parent))
}
override fun getItemCount(): Int {
return list.size
}
override fun onBindViewHolder(holder: VVV, position: Int) {
val data = list[position]
if (data.isAd) {
val binding = ItemAdBinding.bind(holder.itemView)
AdmobUtils.showNativeAd(context, binding.flAd)
} else {
val binding = ItemToolsGridBinding.bind(holder.itemView)
binding.tvTittle.text = data.tittle
data.tools.forEachIndexed { index, toolUI ->
val gItem = R.layout.item_tool_grid.inflate(binding.grid)
val toolBinding = ItemToolGridBinding.bind(gItem)
// val color = when (toolUI.kName) {
// JUNK_CLEANER -> R.color.color_f3f6ff
// RECENT_APP_USAGE -> R.color.color_fff9ed
// LARGE_FILE_CLEANER -> R.color.color_fff9ed
// NOTIFICATION_CLEANER -> R.color.color_fff1f1
// NETWORK_TRAFFIC -> R.color.color_fff1f1
// APP_MANAGER -> R.color.color_ecfcff
// BATTERY_INFO -> R.color.color_ecfcf7
// SIMILAR_PHOTOS -> R.color.color_f5f4ff
// SPEAK_CLEANER -> R.color.color_f3f6ff
// PHOTO_COMPRESS -> R.color.color_f5f4ff
// else -> R.color.white
// }
toolBinding.iv.setImageResource(toolUI.drawable)
toolBinding.tvName.text = toolUI.fName
toolBinding.root.setOnClickListener {
itemClick.invoke(toolUI.kName)
}
binding.grid.addView(gItem)
}
}
}
data class ToolsUI(
val tittle: String? = null,
val tools: List<ToolUI> = listOf(),
val isAd: Boolean = false,//是否是广告
)
data class ToolUI(
val kName: String = "",
val fName: String = "",
val drawable: Int = 0
)
}
\ No newline at end of file
package com.test.basd.smartjunkcleaner.bean
import android.app.usage.UsageEvents
import android.app.usage.UsageStats
import android.graphics.drawable.Drawable
import com.google.gson.ExclusionStrategy
import com.google.gson.FieldAttributes
import com.google.gson.GsonBuilder
data class AppBean(
val icIcon: Drawable,//Drawable会导致序列化失败
val appName: String,
val pkg: String,
var isSelected: Boolean = false,
var installTime: Long = 0,
var appSize: Long = -1L,//0 没有大小 -1没有权限
var lastUsedTime: Long = -1L, //-1没有权限 -2超过30没用
) {
var pinYin: String = ""
var usageEvents: List<UsageEvents.Event>? = null
var usageStats: List<UsageStats>? = null
var launchTimes = 0
var foregroundTimes = 0
var backgroundTimes = 0
var isRunning = false
var screenTime: Long = 0
companion object {
val appBeanGson = GsonBuilder().setExclusionStrategies(object : ExclusionStrategy {
override fun shouldSkipField(f: FieldAttributes): Boolean {
val skipField = arrayOf("icIcon")
return skipField.contains(f.name)
}
override fun shouldSkipClass(clazz: Class<*>): Boolean {
// val skipClass = arrayOf("List")
// skipClass.contains(clazz.name)
return false
}
}).create()
}
}
package com.test.basd.smartjunkcleaner.bean
data class ConfigBean(
val newuser_avoid_time: Int = 0,
val all_push_interval: Int = 0,
val loop_interval: Int = 0,
val push_interval_11001: Int = 0,
val push_interval_11002: Int = 0,
val push_interval_11003: Int = 0,
val push_interval_11004: Int = 0,
val push_interval_11005: Int = 0,
val push_interval_11006: Int = 0,
val push_interval_11007: Int = 0,
val push_interval_11008: Int = 0,
val push_interval_11009: Int = 0,
val push_interval_11010: Int = 0,
val push_interval_11011: Int = 0,
val push_interval_11012: Int = 0,
val push_interval_11013: Int = 0,
val push_interval_11014: Int = 0,
val push_interval_11015: Int = 0,
val push_interval_11016: Int = 0,
val push_interval_11017: Int = 0,
val push_interval_22001: Int = 0,
val push_interval_22002: Int = 0,
val push_interval_22003: Int = 0,
val push_interval_22004: Int = 0,
val push_interval_22005: Int = 0,
val push_interval_22006: Int = 0,
val push_interval_22007: Int = 0,
val push_interval_22011: Int = 0,
val push_circle_order: List<Int> = listOf(),
val native_show_limit: Int = 40,
val inter_show_limit: Int = 40,
val open_time_out: Int = 10,
val isNowPlayAd: Int = 0//关闭限制
) {
companion object {
//功能触发push actionId 主动发送
const val ID_JUNK_CLEAN_PUSH = 11001 //清理垃圾
const val ID_BOOST_PUSH = 11002 //性能优化
// const val ID_VIRUS_PUSH = 11003//扫描病毒,trustlook目前不加
const val ID_BATTERY_PUSH = 11004// 电量信息
const val ID_COOL_PUSH = 11005//手机降温
const val ID_LARGE_FILE_PUSH = 11006// 大文件清理
const val ID_DUPLICATE_FILE_PUSH = 11007//文件备份,重复文件,相似文件
const val ID_VIDEO_CLEAN_PUSH = 11008//清理视频缓存
const val ID_PHOTO_CLEAN_PUSH = 11009//清理相册
const val ID_PHOTO_COMPRESS = 11010//照片压缩
const val ID_APP_MANAGER = 11011//应用管理
const val ID_NETWORK_TRAFFIC = 11012//网络流量
const val ID_CLEAN_NOTIFICATION = 11013//清理通知栏
const val ID_RECENT_USE_APP = 11014//最近使用APP
const val ID_SIMILAR_IMAGE = 11015//清理相似图片
const val ID_CLEAN_SPEAKER = 11016//清理扬声器
// const val ID_BATTERY_OPTIMIZATION = 11017//电池优化,目前是虚假功能
//场景触发push actionId 被动发送
const val ID_WIFI_PUSH = 22001//连接wifi时
const val ID_INSTALL_PACKAGE_PUSH = 22002//安装应用
const val ID_UNINSTALL_PACKAGE_PUSH = 22003//卸载应用
const val ID_CHARGE = 22004//充电
const val ID_LOW_BATTERY_PUSH = 22005//电量低于阈值时
const val ID_LOW_RAM_PUSH = 22007//内存低于阈值
// const val ID_PHONE_ACCELERATE = 22011//手机加速,目前是虚假功能
fun ConfigBean.getActionPushInterval(actionId: Int): Int {
val interval = when (actionId) {
ID_JUNK_CLEAN_PUSH -> push_interval_11001
ID_BOOST_PUSH -> push_interval_11002
// ID_VIRUS_PUSH -> push_interval_11003
ID_BATTERY_PUSH -> push_interval_11004
ID_COOL_PUSH -> push_interval_11005
ID_LARGE_FILE_PUSH -> push_interval_11006
ID_DUPLICATE_FILE_PUSH -> push_interval_11007
ID_VIDEO_CLEAN_PUSH -> push_interval_11008
ID_PHOTO_CLEAN_PUSH -> push_interval_11009
ID_PHOTO_COMPRESS -> push_interval_11010
ID_APP_MANAGER -> push_interval_11011
ID_NETWORK_TRAFFIC -> push_interval_11012
ID_CLEAN_NOTIFICATION -> push_interval_11013
ID_RECENT_USE_APP -> push_interval_11014
ID_SIMILAR_IMAGE -> push_interval_11015
ID_CLEAN_SPEAKER -> push_interval_11016
// ID_BATTERY_OPTIMIZATION -> push_interval_11017
ID_WIFI_PUSH -> push_interval_22001
ID_INSTALL_PACKAGE_PUSH -> push_interval_22002
ID_UNINSTALL_PACKAGE_PUSH -> push_interval_22003
ID_CHARGE -> push_interval_22004
ID_LOW_BATTERY_PUSH -> push_interval_22005
ID_LOW_RAM_PUSH -> push_interval_22007
// ID_PHONE_ACCELERATE -> push_interval_22011
else -> 0
}
return interval
}
}
}
\ No newline at end of file
package com.test.basd.smartjunkcleaner.bean
data class FileBean(
var name: String = "",
var path: String,
val size: Long,
val isDir: Boolean = false,
val type: String = "",
val time: Long = 0L,
var isSelect: Boolean = false
) {
fun isEmptyDir() = isDir
fun isImage() = !isDir && type in listOf("png", "jpg", "gif")
fun isVideo() = !isDir && type in listOf("mp4")
fun isAudio() = !isDir && type in listOf("mp3")
fun isLargeFile() = !isDir && size >= 10 * 1024 * 1024
fun isZip() = !isDir && type in listOf("zip", "tar", "7z")
fun isApk() = !isDir && type in listOf("apk")
fun isDoc() = !isDir && type in listOf("pdf", "doc", "docx", "xls", "ppt", "txt")
fun isOther() = !isImage() && !isVideo() && !isAudio() && !isZip() && !isApk()
fun isJunk() = !isDir && type in listOf("tmp", "cache", "temp")
fun isOtherTrash() = !isDir && type in listOf("log", "bak", "old", "chk", "gid", "dmp", "thumb", "crdownload", "part", "trash", "trashes")
}
\ No newline at end of file
package com.test.basd.smartjunkcleaner.bean
data class ImageDataBean(
val path: String,
val hashCode: String,
val avgPixel: Int,
var isSelect: Boolean = false
)
\ No newline at end of file
package com.test.basd.smartjunkcleaner.bean
data class ParentBean(
val title: String,
val childItem: List<ChildBean>,
var parentSize: Long,
var isParentSelected: Boolean,
var expanded: Boolean = true,
var isfinish: Boolean = false
)
data class ChildBean(
val image: Int? = null,
val childname: String,
val chilepath: String,
val childSize: Long = 0L,
var isChildSelected: Boolean = true,
var pathList: List<String>? = null,
)
\ No newline at end of file
package com.test.basd.smartjunkcleaner.bean
import android.Manifest
import com.test.basd.smartjunkcleaner.R
data class PermissionBean(
val permissionMatch: String = Manifest.permission.CAMERA,
var permissionTittle: Int = 0,
var permissionDesc: Int = 0,
var isSensitive: Boolean = false,
var isAuthorized: Boolean = false,
) {
companion object {
fun PermissionBean.setTittle() {
permissionTittle = when (permissionMatch) {
Manifest.permission.ACCESS_BACKGROUND_LOCATION -> R.string.title_android_permission_ACCESS_BACKGROUND_LOCATION
Manifest.permission.ACCESS_COARSE_LOCATION -> R.string.title_android_permission_ACCESS_COARSE_LOCATION
Manifest.permission.ACCESS_FINE_LOCATION -> R.string.title_android_permission_ACCESS_FINE_LOCATION
Manifest.permission.ACCESS_MEDIA_LOCATION -> R.string.title_android_permission_ACCESS_MEDIA_LOCATION
Manifest.permission.ACCESS_NETWORK_STATE -> R.string.title_android_permission_ACCESS_NETWORK_STATE
Manifest.permission.ACCESS_WIFI_STATE -> R.string.title_android_permission_ACCESS_WIFI_STATE
Manifest.permission.ACTIVITY_RECOGNITION -> R.string.title_android_permission_ACTIVITY_RECOGNITION
Manifest.permission.ANSWER_PHONE_CALLS -> R.string.title_android_permission_ANSWER_PHONE_CALLS
Manifest.permission.BLUETOOTH -> R.string.title_android_permission_BLUETOOTH
Manifest.permission.BLUETOOTH_ADMIN -> R.string.title_android_permission_BLUETOOTH_ADMIN
Manifest.permission.BLUETOOTH_CONNECT -> R.string.title_android_permission_BLUETOOTH_CONNECT
Manifest.permission.BLUETOOTH_PRIVILEGED -> R.string.title_android_permission_BLUETOOTH_PRIVILEGED
Manifest.permission.BLUETOOTH_SCAN -> R.string.title_android_permission_BLUETOOTH_SCAN
Manifest.permission.BODY_SENSORS -> R.string.title_android_permission_BODY_SENSORS
Manifest.permission.CALL_PHONE -> R.string.title_android_permission_CALL_PHONE
Manifest.permission.CAMERA -> R.string.title_android_permission_CAMERA
Manifest.permission.CHANGE_NETWORK_STATE -> R.string.title_android_permission_CHANGE_NETWORK_STATE
Manifest.permission.CHANGE_WIFI_STATE -> R.string.title_android_permission_CHANGE_WIFI_STATE
Manifest.permission.CONTROL_LOCATION_UPDATES -> R.string.title_android_permission_CONTROL_LOCATION_UPDATES
Manifest.permission.DELETE_PACKAGES -> R.string.title_android_permission_DELETE_PACKAGES
Manifest.permission.GET_ACCOUNTS -> R.string.title_android_permission_GET_ACCOUNTS
Manifest.permission.INSTALL_PACKAGES -> R.string.title_android_permission_INSTALL_PACKAGES
Manifest.permission.INTERNET -> R.string.title_android_permission_INTERNET
Manifest.permission.MANAGE_EXTERNAL_STORAGE -> R.string.title_android_permission_MANAGE_EXTERNAL_STORAGE
Manifest.permission.MANAGE_MEDIA -> R.string.title_android_permission_MANAGE_MEDIA
Manifest.permission.MANAGE_WIFI_NETWORK_SELECTION -> R.string.title_android_permission_MANAGE_WIFI_NETWORK_SELECTION
Manifest.permission.MODIFY_AUDIO_SETTINGS -> R.string.title_android_permission_MODIFY_AUDIO_SETTINGS
Manifest.permission.NFC -> R.string.title_android_permission_NFC
Manifest.permission.QUERY_ALL_PACKAGES -> R.string.title_android_permission_QUERY_ALL_PACKAGES
Manifest.permission.READ_CALENDAR -> R.string.title_android_permission_READ_CALENDAR
Manifest.permission.READ_CALL_LOG -> R.string.title_android_permission_READ_CALL_LOG
Manifest.permission.READ_CONTACTS -> R.string.title_android_permission_READ_CONTACTS
Manifest.permission.READ_EXTERNAL_STORAGE -> R.string.title_android_permission_READ_EXTERNAL_STORAGE
Manifest.permission.READ_MEDIA_AUDIO -> R.string.title_android_permission_READ_MEDIA_AUDIO
Manifest.permission.READ_MEDIA_IMAGES -> R.string.title_android_permission_READ_MEDIA_IMAGES
Manifest.permission.READ_MEDIA_VIDEO -> R.string.title_android_permission_READ_MEDIA_VIDEO
Manifest.permission.READ_PHONE_NUMBERS -> R.string.title_android_permission_READ_PHONE_NUMBERS
Manifest.permission.READ_PHONE_STATE -> R.string.title_android_permission_READ_PHONE_STATE
Manifest.permission.READ_SMS -> R.string.title_android_permission_READ_SMS
Manifest.permission.READ_SYNC_SETTINGS -> R.string.title_android_permission_READ_SYNC_SETTINGS
Manifest.permission.READ_SYNC_STATS -> R.string.title_android_permission_READ_SYNC_STATS
Manifest.permission.RECEIVE_BOOT_COMPLETED -> R.string.title_android_permission_RECEIVE_BOOT_COMPLETED
Manifest.permission.RECEIVE_SMS -> R.string.title_android_permission_RECEIVE_SMS
Manifest.permission.RECORD_AUDIO -> R.string.title_android_permission_RECORD_AUDIO
Manifest.permission.REQUEST_DELETE_PACKAGES -> R.string.title_android_permission_REQUEST_DELETE_PACKAGES
Manifest.permission.SEND_SMS -> R.string.title_android_permission_SEND_SMS
Manifest.permission.UPDATE_PACKAGES_WITHOUT_USER_ACTION -> R.string.title_android_permission_UPDATE_PACKAGES_WITHOUT_USER_ACTION
Manifest.permission.USE_FINGERPRINT -> R.string.title_android_permission_USE_FINGERPRINT
Manifest.permission.WRITE_CALENDAR -> R.string.title_android_permission_WRITE_CALENDAR
Manifest.permission.WRITE_CALL_LOG -> R.string.title_android_permission_WRITE_CALL_LOG
Manifest.permission.WRITE_CONTACTS -> R.string.title_android_permission_WRITE_CONTACTS
Manifest.permission.WRITE_EXTERNAL_STORAGE -> R.string.title_android_permission_WRITE_EXTERNAL_STORAGE
Manifest.permission.WRITE_SETTINGS -> R.string.title_android_permission_WRITE_SETTINGS
Manifest.permission.WRITE_SYNC_SETTINGS -> R.string.title_android_permission_WRITE_SYNC_SETTINGS
else -> 0
}
}
fun PermissionBean.setDesc() {
permissionDesc = when (permissionMatch) {
Manifest.permission.ACCESS_BACKGROUND_LOCATION -> R.string.comment_android_permission_ACCESS_BACKGROUND_LOCATION
Manifest.permission.ACCESS_COARSE_LOCATION -> R.string.comment_android_permission_ACCESS_COARSE_LOCATION
Manifest.permission.ACCESS_FINE_LOCATION -> R.string.comment_android_permission_ACCESS_FINE_LOCATION
Manifest.permission.ACCESS_MEDIA_LOCATION -> R.string.comment_android_permission_ACCESS_MEDIA_LOCATION
Manifest.permission.ACCESS_NETWORK_STATE -> R.string.comment_android_permission_ACCESS_NETWORK_STATE
Manifest.permission.ACCESS_WIFI_STATE -> R.string.comment_android_permission_ACCESS_WIFI_STATE
Manifest.permission.ACTIVITY_RECOGNITION -> R.string.comment_android_permission_ACTIVITY_RECOGNITION
Manifest.permission.ANSWER_PHONE_CALLS -> R.string.comment_android_permission_ANSWER_PHONE_CALLS
Manifest.permission.BLUETOOTH -> R.string.comment_android_permission_BLUETOOTH
Manifest.permission.BLUETOOTH_ADMIN -> R.string.comment_android_permission_BLUETOOTH_ADMIN
Manifest.permission.BLUETOOTH_CONNECT -> R.string.comment_android_permission_BLUETOOTH_CONNECT
Manifest.permission.BLUETOOTH_PRIVILEGED -> R.string.comment_android_permission_BLUETOOTH_PRIVILEGED
Manifest.permission.BLUETOOTH_SCAN -> R.string.comment_android_permission_BLUETOOTH_SCAN
Manifest.permission.BODY_SENSORS -> R.string.comment_android_permission_BODY_SENSORS
Manifest.permission.CALL_PHONE -> R.string.comment_android_permission_CALL_PHONE
Manifest.permission.CAMERA -> R.string.comment_android_permission_CAMERA
Manifest.permission.CHANGE_NETWORK_STATE -> R.string.comment_android_permission_CHANGE_NETWORK_STATE
Manifest.permission.CHANGE_WIFI_STATE -> R.string.comment_android_permission_CHANGE_WIFI_STATE
Manifest.permission.CONTROL_LOCATION_UPDATES -> R.string.comment_android_permission_CONTROL_LOCATION_UPDATES
Manifest.permission.DELETE_PACKAGES -> R.string.comment_android_permission_DELETE_PACKAGES
Manifest.permission.GET_ACCOUNTS -> R.string.comment_android_permission_GET_ACCOUNTS
Manifest.permission.INSTALL_PACKAGES -> R.string.comment_android_permission_INSTALL_PACKAGES
Manifest.permission.INTERNET -> R.string.comment_android_permission_INTERNET
Manifest.permission.MANAGE_EXTERNAL_STORAGE -> R.string.comment_android_permission_MANAGE_EXTERNAL_STORAGE
Manifest.permission.MANAGE_MEDIA -> R.string.comment_android_permission_MANAGE_MEDIA
Manifest.permission.MANAGE_WIFI_NETWORK_SELECTION -> R.string.comment_android_permission_MANAGE_WIFI_NETWORK_SELECTION
Manifest.permission.MODIFY_AUDIO_SETTINGS -> R.string.comment_android_permission_MODIFY_AUDIO_SETTINGS
Manifest.permission.NFC -> R.string.comment_android_permission_NFC
Manifest.permission.QUERY_ALL_PACKAGES -> R.string.comment_android_permission_QUERY_ALL_PACKAGES
Manifest.permission.READ_CALENDAR -> R.string.comment_android_permission_READ_CALENDAR
Manifest.permission.READ_CALL_LOG -> R.string.comment_android_permission_READ_CALL_LOG
Manifest.permission.READ_CONTACTS -> R.string.comment_android_permission_READ_CONTACTS
Manifest.permission.READ_EXTERNAL_STORAGE -> R.string.comment_android_permission_READ_EXTERNAL_STORAGE
Manifest.permission.READ_MEDIA_AUDIO -> R.string.comment_android_permission_READ_MEDIA_AUDIO
Manifest.permission.READ_MEDIA_IMAGES -> R.string.comment_android_permission_READ_MEDIA_IMAGES
Manifest.permission.READ_MEDIA_VIDEO -> R.string.comment_android_permission_READ_MEDIA_VIDEO
Manifest.permission.READ_PHONE_NUMBERS -> R.string.comment_android_permission_READ_PHONE_NUMBERS
Manifest.permission.READ_PHONE_STATE -> R.string.comment_android_permission_READ_PHONE_STATE
Manifest.permission.READ_SMS -> R.string.comment_android_permission_READ_SMS
Manifest.permission.READ_SYNC_SETTINGS -> R.string.comment_android_permission_READ_SYNC_SETTINGS
Manifest.permission.READ_SYNC_STATS -> R.string.comment_android_permission_READ_SYNC_STATS
Manifest.permission.RECEIVE_BOOT_COMPLETED -> R.string.comment_android_permission_RECEIVE_BOOT_COMPLETED
Manifest.permission.RECEIVE_SMS -> R.string.comment_android_permission_RECEIVE_SMS
Manifest.permission.RECORD_AUDIO -> R.string.comment_android_permission_RECORD_AUDIO
Manifest.permission.REQUEST_DELETE_PACKAGES -> R.string.comment_android_permission_REQUEST_DELETE_PACKAGES
Manifest.permission.SEND_SMS -> R.string.comment_android_permission_SEND_SMS
Manifest.permission.UPDATE_PACKAGES_WITHOUT_USER_ACTION -> R.string.comment_android_permission_UPDATE_PACKAGES_WITHOUT_USER_ACTION
Manifest.permission.USE_FINGERPRINT -> R.string.comment_android_permission_USE_FINGERPRINT
Manifest.permission.WRITE_CALENDAR -> R.string.comment_android_permission_WRITE_CALENDAR
Manifest.permission.WRITE_CALL_LOG -> R.string.comment_android_permission_WRITE_CALL_LOG
Manifest.permission.WRITE_CONTACTS -> R.string.comment_android_permission_WRITE_CONTACTS
Manifest.permission.WRITE_EXTERNAL_STORAGE -> R.string.comment_android_permission_WRITE_EXTERNAL_STORAGE
Manifest.permission.WRITE_SETTINGS -> R.string.comment_android_permission_WRITE_SETTINGS
Manifest.permission.WRITE_SYNC_SETTINGS -> R.string.comment_android_permission_WRITE_SYNC_SETTINGS
else -> 0
}
}
}
}
\ No newline at end of file
package com.test.basd.smartjunkcleaner.bean
data class TrafficBean(var type: Int, val packageName: String, var wifiUsed: Long, var mobileUsed:Long)
\ No newline at end of file
package com.test.basd.smartjunkcleaner.display
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.os.Build
import android.util.Log
import com.blankj.utilcode.util.SPUtils
import com.test.basd.smartjunkcleaner.bean.ConfigBean.Companion.ID_CHARGE
import com.test.basd.smartjunkcleaner.bean.ConfigBean.Companion.ID_LOW_BATTERY_PUSH
import com.test.basd.smartjunkcleaner.display.NotificationHelper.postActionNotification
import com.test.basd.smartjunkcleaner.helps.BaseApplication
import kotlin.time.Duration.Companion.minutes
import kotlin.time.DurationUnit
class ActionBroadcast : BroadcastReceiver() {
private var mIsScreenOn = false
private var isLock = false
companion object {
private val TAG = "ActionBroadcast"
fun Context.initBroadcast() {
val filter = IntentFilter()
filter.addAction(Intent.ACTION_SCREEN_OFF)
filter.addAction(Intent.ACTION_SCREEN_ON)
filter.addAction(Intent.ACTION_USER_PRESENT)
filter.addAction(Intent.ACTION_POWER_CONNECTED)
filter.addAction(Intent.ACTION_POWER_DISCONNECTED)
filter.addAction(Intent.ACTION_BATTERY_CHANGED)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
registerReceiver(ActionBroadcast(), filter, Context.RECEIVER_EXPORTED)
} else {
registerReceiver(ActionBroadcast(), filter)
}
}
}
override fun onReceive(context: Context, intent: Intent?) {
val action = intent?.action ?: 0
when (action) {
Intent.ACTION_SCREEN_ON -> {
mIsScreenOn = true
}
Intent.ACTION_SCREEN_OFF -> {
mIsScreenOn = false
isLock = true
}
Intent.ACTION_USER_PRESENT -> {
isLock = false
Log.d("glc", "解锁")
if (mIsScreenOn && !isLock) {
// 展示主动推送
NotificationTimerTask(null).oneShotNotification()
}
}
Intent.ACTION_POWER_CONNECTED -> {
BaseApplication.context.postActionNotification(ID_CHARGE)
}
Intent.ACTION_POWER_DISCONNECTED -> {
}
Intent.ACTION_BATTERY_CHANGED -> {//电量改变
lowBattery(intent)
}
}
}
private fun lowBattery(intent: Intent?) {
val level = intent?.getIntExtra("level", 0) ?: 0
if (level < 50) {
val lastPushTime = SPUtils.getInstance().getLong(ID_LOW_BATTERY_PUSH.toString(), 0)
val flag2 = (System.currentTimeMillis() - lastPushTime) > 20.minutes.toLong(DurationUnit.MILLISECONDS)
val isPushCfg = PushStrategy.isPush(ID_LOW_BATTERY_PUSH)
if (flag2 && isPushCfg) {
BaseApplication.context.postActionNotification(ID_LOW_BATTERY_PUSH, extra = level)
}
}
}
}
\ No newline at end of file
package com.test.basd.smartjunkcleaner.display
import android.app.NotificationManager
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import com.test.basd.smartjunkcleaner.helps.EventHelper
import com.test.basd.smartjunkcleaner.helps.LogEx
class CloseNotificationReceiver : BroadcastReceiver() {
private val TAG = "CloseNotificationReceiver"
override fun onReceive(context: Context?, intent: Intent?) {
LogEx.logDebug(TAG, "onReceive intent.action=${intent?.action}")
//cancel事件,经过测试Intent.action这里无法传递参数
if (intent?.action == null) {
EventHelper.event("notification_cancel", "use cancel notification!")
}
intent?.let {
EventHelper.event("notification_userCancel", "userCancel=true")
val notificationId = it.getIntExtra("notificationId", -1)
LogEx.logDebug(TAG, "notificationId=$notificationId action=$action")
if (notificationId != -1 && it.action != "DELETE_NOTIFICATION") {
val notificationManager = context?.getSystemService(Context.NOTIFICATION_SERVICE) as? NotificationManager
notificationManager?.cancel(NotificationUtils.NOTIFICATION_TAG, notificationId)
}
}
}
companion object {
const val action = "com.file.notification.CloseNotificationReceiver"
}
}
package com.test.basd.smartjunkcleaner.display
import android.annotation.SuppressLint
import android.app.PendingIntent
import android.app.PendingIntent.FLAG_MUTABLE
import android.app.PendingIntent.FLAG_UPDATE_CURRENT
import android.content.Context
import android.content.Intent
import android.net.Uri
import android.os.Build
import android.os.Bundle
import android.util.Log
import android.widget.RemoteViews
import com.blankj.utilcode.util.SPUtils
import com.google.gson.Gson
import com.google.gson.reflect.TypeToken
import com.test.basd.smartjunkcleaner.R
import com.test.basd.smartjunkcleaner.activity.splash.NewSplashActivity
import com.test.basd.smartjunkcleaner.bean.ConfigBean.Companion.ID_APP_MANAGER
//import com.test.basd.cleanmaster.bean.ConfigBean.Companion.ID_BATTERY_OPTIMIZATION
import com.test.basd.smartjunkcleaner.bean.ConfigBean.Companion.ID_BATTERY_PUSH
import com.test.basd.smartjunkcleaner.bean.ConfigBean.Companion.ID_CHARGE
import com.test.basd.smartjunkcleaner.bean.ConfigBean.Companion.ID_CLEAN_NOTIFICATION
import com.test.basd.smartjunkcleaner.bean.ConfigBean.Companion.ID_CLEAN_SPEAKER
import com.test.basd.smartjunkcleaner.bean.ConfigBean.Companion.ID_INSTALL_PACKAGE_PUSH
import com.test.basd.smartjunkcleaner.bean.ConfigBean.Companion.ID_JUNK_CLEAN_PUSH
import com.test.basd.smartjunkcleaner.bean.ConfigBean.Companion.ID_LARGE_FILE_PUSH
import com.test.basd.smartjunkcleaner.bean.ConfigBean.Companion.ID_LOW_BATTERY_PUSH
import com.test.basd.smartjunkcleaner.bean.ConfigBean.Companion.ID_NETWORK_TRAFFIC
//import com.test.basd.cleanmaster.bean.ConfigBean.Companion.ID_PHONE_ACCELERATE
import com.test.basd.smartjunkcleaner.bean.ConfigBean.Companion.ID_PHOTO_COMPRESS
import com.test.basd.smartjunkcleaner.bean.ConfigBean.Companion.ID_RECENT_USE_APP
import com.test.basd.smartjunkcleaner.bean.ConfigBean.Companion.ID_SIMILAR_IMAGE
import com.test.basd.smartjunkcleaner.bean.ConfigBean.Companion.ID_UNINSTALL_PACKAGE_PUSH
//import com.test.basd.cleanmaster.bean.ConfigBean.Companion.ID_VIRUS_PUSH
import com.test.basd.smartjunkcleaner.helps.BaseApplication
import com.test.basd.smartjunkcleaner.helps.ComUtils
import com.test.basd.smartjunkcleaner.helps.EventHelper
import kotlin.random.Random
/**
* 推送的帮助类
*/
object NotificationHelper {
private val TAG = "NotificationHelper"
val notificationId = 19086
/**
* 当前APP支持的通知类型
*/
val supportNotification = arrayListOf(
ID_JUNK_CLEAN_PUSH,
// ID_VIRUS_PUSH,
ID_LARGE_FILE_PUSH,
ID_BATTERY_PUSH,
ID_CLEAN_NOTIFICATION,
ID_PHOTO_COMPRESS,
ID_APP_MANAGER,
ID_NETWORK_TRAFFIC,
ID_CLEAN_NOTIFICATION,
ID_RECENT_USE_APP,
ID_SIMILAR_IMAGE,
ID_CLEAN_SPEAKER,
// ID_BATTERY_OPTIMIZATION,
// ID_PHONE_ACCELERATE,//被动推送加入主动推送列表
)
/**
* 过滤可推送actionId
*/
fun filterAction(pushCircleOrder: List<Int>): ArrayList<Int> {
//过滤已有功能
val filterList = arrayListOf<Int>()
pushCircleOrder.forEach { action ->
if (supportNotification.contains(action)) {
filterList.add(action)
}
}
return filterList
}
/**
* 获取当前的推送id
*/
fun getPresentPushId(): Int {
val pushCircleOrder = ComUtils.getSpConfigBean().push_circle_order
Log.d(TAG, "push_circle_order=$pushCircleOrder")
val tempList = arrayListOf<Int>()
val json = SPUtils.getInstance().getString("push_circle_order", "")
if ((json == "") or (json == "null")) {//初始化
val filterList = filterAction(pushCircleOrder)
val initJson = Gson().toJson(filterList)
SPUtils.getInstance().getString("push_circle_order", initJson)
tempList.addAll(filterList)
} else {
val array = Gson().fromJson(json, object : TypeToken<Array<Int>>() {})
tempList.addAll(array)
if (tempList.isEmpty()) {
val filterList = filterAction(pushCircleOrder)
val saveJson = Gson().toJson(filterList)
SPUtils.getInstance().put("push_circle_order", saveJson)
tempList.addAll(filterList)
}
}
val i = tempList[0]
tempList.remove(i)
val usedJson = Gson().toJson(tempList)
SPUtils.getInstance().put("push_circle_order", usedJson)
return i
}
/**
* 发送通知对于
* @param extra 额外参数
*/
@SuppressLint("RemoteViewLayout")
fun Context.postActionNotification(actionId: Int, extra: Int? = null, pushStayTime: Long? = null) {
val remoteViews = RemoteViews(packageName, R.layout.notification_common_notify)
when (actionId) {
ID_JUNK_CLEAN_PUSH -> {
remoteViews.setImageViewResource(R.id.iv_icon, R.mipmap.cleanjunk_home)
remoteViews.setTextViewText(R.id.tv_desc, "Clean up remaining junk files")
remoteViews.setTextViewText(R.id.tv_btn, "Clean up")
}
// ID_VIRUS_PUSH -> {
// remoteViews.setImageViewResource(R.id.iv_icon, R.mipmap.virus)
// remoteViews.setTextViewText(R.id.tv_desc, "Protect your device and privacy, scan for virus threats now!")
// remoteViews.setTextViewText(R.id.tv_btn, "Scan")
// }
// ID_BATTERY_OPTIMIZATION -> {
// remoteViews.setImageViewResource(R.id.iv_icon, R.mipmap.battery)
// val random = Random.nextInt(0, 50)
// remoteViews.setTextViewText(R.id.tv_desc, "$random Apps are consuming photo power")
// remoteViews.setTextViewText(R.id.tv_btn, "Optimize")
// }
ID_LARGE_FILE_PUSH -> {
remoteViews.setImageViewResource(R.id.iv_icon, R.mipmap.large_home)
remoteViews.setTextViewText(R.id.tv_desc, "Clean big files to free up storage space")
remoteViews.setTextViewText(R.id.tv_btn, "Clean up")
}
ID_PHOTO_COMPRESS -> {
remoteViews.setImageViewResource(R.id.iv_icon, R.mipmap.photo_home)
remoteViews.setTextViewText(R.id.tv_desc, "Compress images to release more space")
remoteViews.setTextViewText(R.id.tv_btn, "Compress")
}
ID_APP_MANAGER -> {
remoteViews.setImageViewResource(R.id.iv_icon, R.mipmap.appmanager)
remoteViews.setTextViewText(R.id.tv_desc, "Check apps size and uninstall junk apps to release storage space")
remoteViews.setTextViewText(R.id.tv_btn, "Manage")
}
ID_NETWORK_TRAFFIC -> {
remoteViews.setImageViewResource(R.id.iv_icon, R.mipmap.network_tools)
remoteViews.setTextViewText(R.id.tv_desc, "View network traffic usage and stop traffic-consuming apps.")
remoteViews.setTextViewText(R.id.tv_btn, "View")
}
ID_CLEAN_NOTIFICATION -> {
remoteViews.setImageViewResource(R.id.iv_icon, R.mipmap.notification_tools)
remoteViews.setTextViewText(R.id.tv_desc, "Too many annoying notifications? Block and clean")
remoteViews.setTextViewText(R.id.tv_btn, "View")
}
ID_RECENT_USE_APP -> {
remoteViews.setImageViewResource(R.id.iv_icon, R.mipmap.recentapp_tools)
remoteViews.setTextViewText(R.id.tv_desc, "Check and manage recently active apps")
remoteViews.setTextViewText(R.id.tv_btn, "Manage")
}
ID_SIMILAR_IMAGE -> {
remoteViews.setImageViewResource(R.id.iv_icon, R.mipmap.similar_home)
remoteViews.setTextViewText(R.id.tv_desc, "Check similar photos to release more space")
remoteViews.setTextViewText(R.id.tv_btn, "Clean up")
}
ID_CLEAN_SPEAKER -> {
remoteViews.setImageViewResource(R.id.iv_icon, R.mipmap.speaker_home)
remoteViews.setTextViewText(R.id.tv_desc, "Clean the speaker dust and fix the low volume problem")
remoteViews.setTextViewText(R.id.tv_btn, "Clean up")
}
//==================================下面是被动推送的情况===============================================
ID_INSTALL_PACKAGE_PUSH -> {
remoteViews.setImageViewResource(R.id.iv_icon, R.mipmap.install)
remoteViews.setTextViewText(R.id.tv_desc, "Installation successful. Delete useless APKs")
remoteViews.setTextViewText(R.id.tv_btn, "Scan")
}
ID_UNINSTALL_PACKAGE_PUSH -> {
remoteViews.setImageViewResource(R.id.iv_icon, R.mipmap.uninstall)
remoteViews.setTextViewText(R.id.tv_desc, "Uninstalled successfully, clean residual files from your device")
remoteViews.setTextViewText(R.id.tv_btn, "Clean up")
}
ID_CHARGE -> {
remoteViews.setImageViewResource(R.id.iv_icon, R.mipmap.start)
remoteViews.setTextViewText(R.id.tv_desc, "View phone battery consumption recently")
remoteViews.setTextViewText(R.id.tv_btn, "View")
}
ID_LOW_BATTERY_PUSH -> {
remoteViews.setImageViewResource(R.id.iv_icon, R.mipmap.battery_lower)
remoteViews.setTextViewText(R.id.tv_desc, "The battery is $extra%, view the battery information")
remoteViews.setTextViewText(R.id.tv_btn, "View")
}
// ID_PHONE_ACCELERATE -> {
// remoteViews.setImageViewResource(R.id.iv_icon, R.mipmap.memory)
// remoteViews.setTextViewText(R.id.tv_desc, "Ram usage reached $extra%, optimize now!")
// remoteViews.setTextViewText(R.id.tv_btn, "Optimize")
// }
else -> {
return
}
}
val flag = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) FLAG_MUTABLE else FLAG_UPDATE_CURRENT
//cancel事件,经过测试Intent.action这里无法传递参数
val cancelIntent = Intent(this, CloseNotificationReceiver::class.java)
val cancelRequestCode = Random.nextInt(0, 1000)
val cancelPendingIntent = PendingIntent.getBroadcast(BaseApplication.context, cancelRequestCode, cancelIntent, flag)
remoteViews.setOnClickPendingIntent(R.id.fl_cancel, cancelPendingIntent)
val intent = Intent(applicationContext, NewSplashActivity::class.java)
val title: String = ""
val desc: String = ""
intent.putExtra("title", title)
intent.putExtra("desc", desc)
val customKey = "type"
intent.putExtra(customKey, actionId)
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
val bundle = Bundle()
bundle.putInt(customKey, actionId)
intent.putExtras(bundle)
val uri =
Uri.parse("cleanapp://com.test.basd.cleanmaster/com.test.basd.cleanmaster.activity.splash.NewSplashActivity?type=$actionId")
intent.data = uri
// intent.data = Uri.parse("myapp://splash?type=$actionId")
//按钮事件
val btnRequestCode = Random.nextInt(0, 1000)
val btnPendingIntent = PendingIntent.getActivity(BaseApplication.context, btnRequestCode, intent, flag)
remoteViews.setOnClickPendingIntent(R.id.fl_btn, btnPendingIntent)
//每种类型保存值
SPUtils.getInstance().put(actionId.toString(), System.currentTimeMillis())
//大间隔所需值
SPUtils.getInstance().put("all_last_push_time", System.currentTimeMillis())
EventHelper.event(
"pushCircleOrder_showNotification",
"actionId=$actionId"
)
val requestCode = Random.nextInt(0, 1000)
val pendingIntent = PendingIntent.getActivity(applicationContext, requestCode, intent, FLAG_UPDATE_CURRENT or FLAG_MUTABLE)
NotificationUtils.showNotification(
applicationContext,
notificationId,
remoteViews,
R.id.fl_cancel,
pendingIntent,
pushStayTime ?: 0
)
}
}
package com.test.basd.smartjunkcleaner.display
import android.app.Service
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.os.Build
import android.os.IBinder
class NotificationService : Service() {
companion object {
var isRunning = false
}
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
if (isRunning) {
stopSelf()
return START_NOT_STICKY
}
// isRuning = true
// Log.d("MyService", "Service is start" + Process.myPid());
// initEvenParams()
// eventParams?.let {
// startTimer()
// }
initBroadcast()
val notification = NotificationUtils.createPermanentNotification(applicationContext)
startForeground(1, notification)
return START_STICKY
}
private fun startTimer() {
// val pushManagement = ComUtils.getPushConfig()
// PeriodicTaskScheduler.getInstance().setPushParams(pushManagement)
// .setEventParam(eventParams).start(applicationContext)
}
fun Context.initBroadcast() {
val filter = IntentFilter()
filter.addAction(Intent.ACTION_SCREEN_OFF)
filter.addAction(Intent.ACTION_SCREEN_ON)
filter.addAction(Intent.ACTION_USER_PRESENT)
filter.addAction(Intent.ACTION_POWER_CONNECTED)
filter.addAction(Intent.ACTION_POWER_DISCONNECTED)
filter.addAction(Intent.ACTION_BATTERY_CHANGED)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
registerReceiver(ActionBroadcast(), filter, Context.RECEIVER_EXPORTED)
} else {
registerReceiver(ActionBroadcast(), filter)
}
}
override fun onBind(intent: Intent?): IBinder? {
return null
}
override fun onDestroy() {
isRunning = false
super.onDestroy()
// 取消订阅
}
}
\ No newline at end of file
package com.test.basd.smartjunkcleaner.display
import android.util.Log
//import com.test.basd.cleanmaster.bean.ConfigBean.Companion.ID_PHONE_ACCELERATE
import com.test.basd.smartjunkcleaner.display.NotificationHelper.postActionNotification
import com.test.basd.smartjunkcleaner.helps.BaseApplication
import com.test.basd.smartjunkcleaner.helps.EventHelper
import java.util.TimerTask
/**
* 循环推送任务
*/
class NotificationTimerTask(val eventParams: EventParams?) : TimerTask() {
private val TAG = "NotificationTimerTask"
override fun run() {
oneShotNotification()
}
fun oneShotNotification(noLimit: Boolean = false) {
val id = NotificationHelper.getPresentPushId()
val isPush = PushStrategy.isPush(id, eventParams = eventParams)
val log = "isPush=$isPush " + "id=${id} "
EventHelper.event("pushCircleOrder_isPush", log)
Log.d(TAG, log)
if (isPush || noLimit) {
val extra: Int? = null
// if (id == ID_PHONE_ACCELERATE) {
// extra = RamMemoryEx.getMemoryUsage(BaseApplication.context).toInt()
// Log.d("MyService", "ram: $extra")
// }
BaseApplication.context.postActionNotification(id, extra, 200000)
}
}
}
\ No newline at end of file
package com.test.basd.smartjunkcleaner.display
import android.app.Notification
import android.app.NotificationChannel
import android.app.NotificationManager
import android.app.PendingIntent
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.os.Build
import android.service.notification.StatusBarNotification
import android.util.Log
import android.widget.RemoteViews
import androidx.core.app.NotificationCompat
import androidx.core.app.NotificationManagerCompat
import com.test.basd.smartjunkcleaner.R
import com.test.basd.smartjunkcleaner.activity.AppManagerActivity
import com.test.basd.smartjunkcleaner.activity.BatteryInfoActivity
import com.test.basd.smartjunkcleaner.activity.MainActivity
import com.test.basd.smartjunkcleaner.activity.ScanJunkActivity
import com.test.basd.smartjunkcleaner.display.NotificationHelper.postActionNotification
import com.test.basd.smartjunkcleaner.helps.BaseApplication
import com.test.basd.smartjunkcleaner.helps.EventHelper
import com.test.basd.smartjunkcleaner.helps.KotlinExt.string
import com.test.basd.smartjunkcleaner.service.FlashlightService
object NotificationUtils {
private val TAG = "NotificationUtils"
const val NOTIFICATION_TAG = "SmartCleanNotification"
private const val CHANNEL_ID = "Misc"
fun showNotification(
context: Context,
notificationId: Int,
remoteViews: RemoteViews,
closeViewId: Int,
pendingIntent: PendingIntent,
pushStayTime: Long
) {
if (closeViewId != 0) {
val closeIntent = Intent(context, CloseNotificationReceiver::class.java).apply {
putExtra("notificationId", notificationId)
}
val closePendingIntent =
PendingIntent.getBroadcast(context, 0, closeIntent, PendingIntent.FLAG_MUTABLE)
remoteViews.setOnClickPendingIntent(closeViewId, closePendingIntent)
}
val builder = createNotificationBuilder(context, remoteViews, pendingIntent, pushStayTime)
sendNotification(context, notificationId, builder)
}
private fun createNotificationBuilder(
context: Context,
remoteViews: RemoteViews,
pendingIntent: PendingIntent,
pushStayTime: Long
): NotificationCompat.Builder {
val notificationManager =
context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val channel =
NotificationChannel(CHANNEL_ID, CHANNEL_ID, NotificationManager.IMPORTANCE_HIGH)
channel.lockscreenVisibility = NotificationCompat.VISIBILITY_PUBLIC
notificationManager.createNotificationChannel(channel)
}
val deleteIntent = Intent(context, CloseNotificationReceiver::class.java).apply {
action = "DELETE_NOTIFICATION"
}
val deletePendingIntent = PendingIntent.getBroadcast(context, 0, deleteIntent, PendingIntent.FLAG_MUTABLE)
return NotificationCompat.Builder(context, CHANNEL_ID)
.setSmallIcon(R.mipmap.logo)
.setPriority(NotificationCompat.PRIORITY_MAX)
.setCategory(NotificationCompat.CATEGORY_ALARM)
.setWhen(System.currentTimeMillis())
.setAutoCancel(true)
.setDeleteIntent(deletePendingIntent)
.setContentIntent(pendingIntent)
.setContent(remoteViews)
.setCustomHeadsUpContentView(remoteViews)
.setCustomBigContentView(remoteViews)
.setCustomContentView(remoteViews)
.setStyle(NotificationCompat.BigTextStyle())
.setFullScreenIntent(pendingIntent, true)
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
.setTimeoutAfter(pushStayTime)
.setSound(null)
.setLights(0, 0, 0)
.setVibrate(null)
.setAllowSystemGeneratedContextualActions(true)
.setBubbleMetadata(null)
}
private fun sendNotification(
context: Context,
notificationId: Int,
builder: NotificationCompat.Builder
) {
val notificationManager =
context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
val notification = builder.build()
notificationManager.notify(NOTIFICATION_TAG, notificationId, notification)
}
fun createPermanentNotification(context: Context): Notification {
Log.d(
"startForegroundService",
"Start foreground service."
)
val isOngoing = true //是否持续(为不消失的常驻通知)
val channelName = R.string.foreground_service_channel.string()
val channelId = "Service_Id"
val category = Notification.CATEGORY_SERVICE
val contentView = RemoteViews(context.packageName, R.layout.reminder_layout_notification_notify)
val expendView = RemoteViews(context.packageName, R.layout.reminder_layout_notification_big_notify)
val intent0 = Intent(context, ScanJunkActivity::class.java)
val pendingIntent0 =
PendingIntent.getActivity(context, 0, intent0, PendingIntent.FLAG_IMMUTABLE)
contentView.setOnClickPendingIntent(R.id.id_ll_clean, pendingIntent0)
expendView.setOnClickPendingIntent(R.id.id_ll_clean, pendingIntent0)
val intent2 = Intent(context, BatteryInfoActivity::class.java)
val pendingIntent2 =
PendingIntent.getActivity(context, 0, intent2, PendingIntent.FLAG_IMMUTABLE)
contentView.setOnClickPendingIntent(R.id.id_battery, pendingIntent2)
expendView.setOnClickPendingIntent(R.id.id_battery, pendingIntent2)
val intent3 = Intent()
val serviceComponent = ComponentName(context, FlashlightService::class.java)
intent3.component = serviceComponent
val pendingIntent3 =
PendingIntent.getService(context, 0, intent3, PendingIntent.FLAG_IMMUTABLE)
contentView.setOnClickPendingIntent(R.id.id_lighit, pendingIntent3)
expendView.setOnClickPendingIntent(R.id.id_lighit, pendingIntent3)
val intent4 = Intent(context, AppManagerActivity::class.java)
val pendingIntent4 =
PendingIntent.getActivity(context, 0, intent4, PendingIntent.FLAG_IMMUTABLE)
contentView.setOnClickPendingIntent(R.id.id_app_manager, pendingIntent4)
expendView.setOnClickPendingIntent(R.id.id_app_manager, pendingIntent4)
val nfIntent = Intent(context, MainActivity::class.java)
val pendingIntent =
PendingIntent.getActivity(context, 0, nfIntent, PendingIntent.FLAG_IMMUTABLE)
val builder = NotificationCompat.Builder(context, channelId)
builder.setCustomContentView(contentView)
builder.setCustomBigContentView(expendView)
builder.setContentIntent(pendingIntent) //设置PendingIntent
builder.setSmallIcon(R.mipmap.logo) //设置状态栏内的小图标
builder.setVisibility(NotificationCompat.VISIBILITY_PRIVATE) //设置通知公开可见
builder.setOngoing(isOngoing) //设置持续(不消失的常驻通知)
// builder.setCategory(category) //设置类别
builder.setAutoCancel(false)
builder.setPriority(NotificationCompat.PRIORITY_MAX) //优先级为:重要通知
builder.setWhen(System.currentTimeMillis())
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val channel =
NotificationChannel(channelId, channelName, NotificationManager.IMPORTANCE_LOW)
channel.lockscreenVisibility = 1
val notificationManager =
context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
notificationManager.createNotificationChannel(channel)
builder.setChannelId(channelId)
}
val notification = builder.build()
return notification
}
fun sendTimerPush(s: Long) {
Log.d(TAG, "sendTimerPush")
val id = NotificationHelper.getPresentPushId()
val isPush = PushStrategy.isPush(id)
val log = "isPush=$isPush " + "id=${id} "
Log.d(TAG, log)
EventHelper.event("pushCircleOrder_isPush", log)
if (isPush) {
// var extra: Int? = null
// if (id == ID_PHONE_ACCELERATE) {
// extra = RamMemoryEx.getMemoryUsage(BaseApplication.context).toInt()
// }
BaseApplication.context.postActionNotification(id, null, s)
}
}
fun isNotificationExist(context: Context, notificationId: Int): Boolean {
// 获取 NotificationManagerCompat 实例
val notificationManager = NotificationManagerCompat.from(context)
// 获取当前活动的通知列表
val notifications: MutableList<StatusBarNotification> = notificationManager.activeNotifications
// 检查指定的通知是否在列表中
for (notification in notifications) {
if (notification.id == notificationId) {
return true
}
}
return false
}
}
package com.test.basd.smartjunkcleaner.display
import android.os.Process
import com.blankj.utilcode.util.SPUtils
import com.test.basd.smartjunkcleaner.MyApplication
import com.test.basd.smartjunkcleaner.bean.ConfigBean
import com.test.basd.smartjunkcleaner.bean.ConfigBean.Companion.getActionPushInterval
import com.test.basd.smartjunkcleaner.helps.ComUtils
import com.test.basd.smartjunkcleaner.helps.LogEx
/**
* 推送的配置类,定义了是否推送的判断条件
*/
object PushStrategy {
private val TAG = "PushStrategy"
/**
* 新用户是否显示
*/
fun newUserPush(newUserAvoidTime: Int): Boolean {
val firstLaunchTime = SPUtils.getInstance().getLong("install_info", 0)
val haveFirstLaunchTime = (System.currentTimeMillis() - firstLaunchTime) / 1000
val flag = haveFirstLaunchTime > newUserAvoidTime
return flag
}
/**
* 所有推送是否推送大于间隔时间
* @param interval 秒,目前默认自然和渠道用户都配的 60s
*/
fun canPushNextTime(interval: Int): Boolean {
val lastPushTime = SPUtils.getInstance().getLong("all_last_push_time", 0)
val haveInterval = (System.currentTimeMillis() - lastPushTime) / 1000
return haveInterval > interval
}
/**
* 所有的推送条件过滤
* 根据id判断通知是否可推送
* @param actionId 功能或者场景的id
*/
fun isPush(actionId: Int, eventParams: EventParams? = null): Boolean {
val configBean: ConfigBean = ComUtils.getSpConfigBean()
// 新用户是否显示
if (!newUserPush(configBean.newuser_avoid_time)) {
LogEx.logDebug(TAG, "================================新用户推送拦截===============================")
return false
}
// 渠道用户是否推送
// if (!UserChancelEx.getUserChancelSwitch(pushManagement.push_show)) {
// LogEx.logDebug(TAG, "================================渠道用户拦截===============================")
// return false
// }
//是否上次推送间隔大于配置间隔
// val isOganic = UserChancelEx.isOrganicUser()
// val interval = if (isOganic) pushManagement.o_push_interval else pushManagement.all_push_interval
val interval = configBean.all_push_interval
if (!canPushNextTime(interval)) {
LogEx.logDebug(TAG, "================================总间隔拦截===============================")
return false
}
//当前类型通知推送间隔是否大于配置间隔
val pushInterval = configBean.getActionPushInterval(actionId)
if (!actionTypeCanPsh(actionId, pushInterval)) {
LogEx.logDebug(TAG, "================================当前通知类型间隔拦截===============================")
return false
}
//是否在前台推送
val isResumed = MyApplication.PAUSED_VALUE == 1
LogEx.logDebug(TAG, "isResumed=$isResumed")
if (isResumed) {
LogEx.logDebug(TAG, "================================app在前台拦截===============================")
return false
}
val pid = Process.myPid()
LogEx.logDebug(TAG, "可以发送通知了。。。")
return true
}
/**
* 当前类型通知推送间隔是否大于配置间隔
*/
private fun actionTypeCanPsh(actionId: Int, pushInterval: Int = 0): Boolean {
val lastTypePushTime = SPUtils.getInstance().getLong(actionId.toString(), 0L)
val actionInterval = (System.currentTimeMillis() - lastTypePushTime) / 1000
LogEx.logDebug(TAG, "actionId=$actionId actionInterval=$actionInterval pushInterval=$pushInterval")
return actionInterval > pushInterval
}
}
\ No newline at end of file
package com.test.basd.smartjunkcleaner.display
import android.app.ActivityManager
import android.content.Context
object RamMemoryEx {
fun getMemoryUsage(context: Context): Float {
val activityManager: ActivityManager = context.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
val memoryInfo: ActivityManager.MemoryInfo = ActivityManager.MemoryInfo()
activityManager.getMemoryInfo(memoryInfo)
val totalMemory: Long = memoryInfo.totalMem
val availableMemory: Long = memoryInfo.availMem
val usedMemory = (totalMemory - availableMemory).toDouble()
return ((usedMemory / totalMemory) * 100).toFloat()
}
}
package com.test.basd.smartjunkcleaner.display
import android.content.Context
import android.telephony.TelephonyManager
import com.blankj.utilcode.util.SPUtils
import com.test.basd.smartjunkcleaner.helps.BaseApplication
/**
* 判断用户渠道的方法
*/
object UserChancelEx {
fun getUserChancelSwitch(int: Int): Boolean {
val isOrganic = isOrganicUser()
val isHasSim = isSimCardAvailable()
return l(int, isHasSim, isOrganic)
}
fun l(i2: Int, isHasSim: Boolean, isOrganic: Boolean): Boolean {
return if (!isOrganic && !k(i2, 3)) {
false
} else if (isOrganic && !k(i2, 2)) {
false
} else if (!isHasSim && !k(i2, 1)) {
false
} else if (isHasSim && !k(i2, 0)) {
false
} else k(i2, 4)
}
fun k(i2: Int, i3: Int): Boolean {
return i2 shr i3 and 1 == 1
}
/**
* 是否是自然用户
*/
fun isOrganicUser(): Boolean {
val source: String = SPUtils.getInstance().getString("install_info_source","")
return !(source.contains("gclid") || source.contains("facebook"))
}
fun isSimCardAvailable(): Boolean {
val telephonyManager = BaseApplication.context.getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManager
return telephonyManager.simState != TelephonyManager.SIM_STATE_ABSENT
}
}
\ No newline at end of file
package com.test.basd.smartjunkcleaner.display
import com.blankj.utilcode.util.NetworkUtils.NetworkType
data class EventParams(
val screenHeight: Int,
val screenWidth: Int,
val mode: String,
val networkType: NetworkType,
val appVersionName: String,
val androidID: String
)
\ No newline at end of file
package com.test.basd.smartjunkcleaner.display.fcm
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.util.Log
import com.google.firebase.messaging.FirebaseMessaging
import com.test.basd.smartjunkcleaner.helps.EventHelper
class FCMCheckReceiver : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
// 检查FCM连接并重新连接(如果需要)
// 这里可以根据具体需求自行实现
val boolean = FirebaseMessaging.getInstance().isAutoInitEnabled()
if (boolean) {
EventHelper.event("fcm_autoInit_suc")
} else {
EventHelper.event("fcm_autoInit_fail")
}
Log.d("FcmHelper", "FCMCheckReceiver: $boolean")
}
}
package com.test.basd.smartjunkcleaner.display.fcm
import android.annotation.SuppressLint
import android.app.AlarmManager
import android.app.PendingIntent
import android.content.Context
import android.content.Intent
import android.util.Log
import com.google.android.gms.tasks.OnCompleteListener
import com.google.android.gms.tasks.Task
import com.google.firebase.messaging.FirebaseMessaging
import com.test.basd.smartjunkcleaner.helps.EventHelper
object FcmHelper {
private const val TAG = "FcmHelper"
fun subscribeToTopic() {
FirebaseMessaging.getInstance().subscribeToTopic("news")
.addOnCompleteListener { task: Task<Void?> ->
if (task.isSuccessful) {
EventHelper.event("fcm_subscribed_suc", "news")
Log.d(TAG, "Subscribed to topic: TOPIC_NAME")
} else {
EventHelper.event("fcm_subscribed_fail")
Log.e(TAG, "Failed to subscribe to topic: TOPIC_NAME", task.exception)
}
}
}
fun getToken() {
FirebaseMessaging.getInstance().token
.addOnCompleteListener(OnCompleteListener { task ->
if (!task.isSuccessful) {
EventHelper.event("fcm_token_fail")
Log.w(TAG, "Fetching FCM registration token failed", task.exception)
return@OnCompleteListener
}
// Get new FCM registration token
val token = task.result
EventHelper.event("fcm_token_suc", token)
Log.d(TAG, "token: $token")
})
}
@SuppressLint("UnspecifiedImmutableFlag")
fun startFCMCheckAlarm(context: Context) {
val intervalMillis = (15 * 60 * 1000).toLong() // 1分钟
// 判断是否已经在运行
val intent = Intent(context, FCMCheckReceiver::class.java)
val isRunning =
PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_IMMUTABLE) != null
if (isRunning) {
Log.d(TAG, "FCM check alarm is already running")
return
}
// 获取AlarmManager实例
val alarmManager = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager
// 创建PendingIntent,用于在指定时间触发广播
val pendingIntent = PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT)
// 设置每隔一段时间触发一次广播
val triggerAtMillis = System.currentTimeMillis() + intervalMillis
alarmManager.setRepeating(
AlarmManager.RTC_WAKEUP,
triggerAtMillis,
intervalMillis,
pendingIntent
)
Log.d(TAG, "FCM check alarm is started")
}
}
\ No newline at end of file
package com.test.basd.smartjunkcleaner.display.fcm
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.util.Log
import com.test.basd.smartjunkcleaner.helps.EventHelper
class MyFirebaseMessagingReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
val message = intent.extras?.getString("message")
Log.d(TAG, "Received FCM message$message")
EventHelper.event("fcm_receiver", message)
}
companion object {
private const val TAG = "MyFirebaseMsgReceiver"
}
}
package com.test.basd.smartjunkcleaner.display.fcm
import android.app.job.JobInfo
import android.app.job.JobScheduler
import android.content.ComponentName
import android.content.Context
import android.os.Build
import android.util.Log
import com.google.firebase.messaging.FirebaseMessagingService
import com.google.firebase.messaging.RemoteMessage
import com.test.basd.smartjunkcleaner.display.NotificationUtils
import com.test.basd.smartjunkcleaner.helps.EventHelper
class MyFirebaseMessagingService : FirebaseMessagingService() {
override fun onMessageReceived(remoteMessage: RemoteMessage) {
Log.d(TAG, "onMessageReceived: " + remoteMessage.from)
val pushStayTime = remoteMessage.data["push_stay_time"]?.toLongOrNull() ?: 0
EventHelper.event("fcm_message_received", pushStayTime.toString())
NotificationUtils.sendTimerPush(pushStayTime)
FcmHelper.startFCMCheckAlarm(this)
if (Build.VERSION.SDK_INT == Build.VERSION_CODES.S) {
return
}
initJob()
}
private fun initJob() {
val jobScheduler = getSystemService(Context.JOB_SCHEDULER_SERVICE) as JobScheduler
val job = JobInfo.Builder(0, ComponentName(this, NotificationJobService::class.java))
.setMinimumLatency(0L)
.setOverrideDeadline(0L)
.setPersisted(true)
.build()
jobScheduler.schedule(job)
}
override fun onNewToken(token: String) {
Log.d(TAG, "Refreshed token: $token")
}
companion object {
private const val TAG = "MyFirebaseMsgService"
}
}
package com.test.basd.smartjunkcleaner.display.fcm
import android.annotation.SuppressLint
import android.app.ActivityManager
import android.app.job.JobParameters
import android.app.job.JobService
import android.content.Intent
import android.os.Build
import com.test.basd.smartjunkcleaner.display.NotificationService
@SuppressLint("SpecifyJobSchedulerIdRange")
class NotificationJobService : JobService() {
private var isServiceRunning = false
override fun onStartJob(params: JobParameters): Boolean {
if (!isServiceRunning) {
// 创建一个启动服务的Intent
val intent = Intent(this, NotificationService::class.java)
// 检查服务是否已经运行
isServiceRunning = if (isServiceRunning(intent)) {
true
} else {
// 启动服务
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
startForegroundService(intent)
} else {
startService(intent)
}
// 标记服务已经运行
true
}
}
// 告诉系统任务已完成
jobFinished(params, false)
return false
}
override fun onStopJob(params: JobParameters): Boolean {
return false
}
private fun isServiceRunning(intent: Intent): Boolean {
val manager: ActivityManager = getSystemService(ACTIVITY_SERVICE) as ActivityManager
val infos: List<ActivityManager.RunningServiceInfo> = manager.getRunningServices(Int.MAX_VALUE)
for (info in infos) {
if (info.service.className == intent.component?.className) {
return true
}
}
return false
}
companion object {
private const val JOB_ID = 123
}
}
\ No newline at end of file
package com.test.basd.smartjunkcleaner.fragment
import android.annotation.SuppressLint
import android.app.AppOpsManager
import android.content.Context
import android.content.Intent
import android.net.Uri
import android.os.Build
import android.os.Bundle
import android.provider.Settings
import android.view.View
import com.test.basd.smartjunkcleaner.activity.AppManagerActivity
import com.test.basd.smartjunkcleaner.adapter.AppListAdapter
import com.test.basd.smartjunkcleaner.bean.AppBean
import com.test.basd.smartjunkcleaner.databinding.FragmentAppListBinding
import com.test.basd.smartjunkcleaner.helps.ActivityLauncher
import com.test.basd.smartjunkcleaner.helps.BaseFragment
import com.test.basd.smartjunkcleaner.helps.LogEx
import com.test.basd.smartjunkcleaner.view.AppDetailDialog.showAppDetailDialog
import net.sourceforge.pinyin4j.PinyinHelper
import net.sourceforge.pinyin4j.format.HanyuPinyinCaseType
import net.sourceforge.pinyin4j.format.HanyuPinyinOutputFormat
import net.sourceforge.pinyin4j.format.HanyuPinyinToneType
import net.sourceforge.pinyin4j.format.exception.BadHanyuPinyinOutputFormatCombination
/**
*/
class AppListFragment : BaseFragment<FragmentAppListBinding>() {
private val TAG = "AppListFragment"
private var adapter: AppListAdapter? = null
private var list = arrayListOf<AppBean>()
private var isRefreshData = false//是否需要更新数据
private lateinit var launcher: ActivityLauncher
private var type: Int = 0
var isAsc: Boolean = true
private var needPermission: Boolean = false
override val binding: FragmentAppListBinding by lazy {
FragmentAppListBinding.inflate(layoutInflater)
}
fun setInitData(
launcher: ActivityLauncher,
type: Int = 0,
isAsc: Boolean = true,
needPermission: Boolean = false
) {
this.launcher = launcher
this.type = type
this.isAsc = isAsc
this.needPermission = needPermission
}
override fun setView() {
if (needPermission && !checkUsageAccessSettings(requireContext())) {
binding.flContent.visibility = View.GONE
binding.flPermission.visibility = View.VISIBLE
} else {
if (isRefreshData) {
binding.progressbar.visibility = View.GONE
}
initRv()
initData()
}
}
override fun setListener() {
binding.tvAuthorization.setOnClickListener {
val intent = Intent(Settings.ACTION_USAGE_ACCESS_SETTINGS)
intent.addCategory("android.intent.category.DEFAULT")
intent.data = Uri.parse("package:${requireActivity().packageName}")
launcher.launch(intent) {
LogEx.logDebug(TAG, "launcher callback")
(requireActivity() as AppManagerActivity).refreshUsageAccessData()
}
}
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
}
override fun onResume() {
super.onResume()
if (checkUsageAccessSettings(requireContext())) {
binding.flPermission.visibility = View.GONE
binding.flContent.visibility = View.VISIBLE
}
}
private fun initData() {
orderList()
adapter?.setData(list)
}
/**
* 更新数据不刷洗
*/
fun setData(dataList: List<AppBean>) {
list.clear()
list.addAll(dataList)
orderList()
}
/**
* 更新数据刷新
*/
@SuppressLint("NotifyDataSetChanged")
fun setDataRefresh(dataList: List<AppBean>) {
list.clear()
list.addAll(dataList)
orderList()
if (isVisible) {
if (adapter == null) {
initRv()
}
if (isRefreshData) {
binding.progressbar.visibility = View.GONE
}
adapter?.setData(list)
}
}
/**
* 刷洗fragment数据
*/
@SuppressLint("NotifyDataSetChanged")
fun refreshFragmentData(dataList: List<AppBean>, isRefresh: Boolean = false, index: Int = 0) {
isRefreshData = true
LogEx.logDebug(TAG, "isRefresh=$isRefresh isVisible=$isVisible index=$index")
if (isRefresh && isVisible) {
binding.flPermission.visibility = View.GONE
binding.progressbar.visibility = View.GONE
setDataRefresh(dataList)
} else {
setData(dataList)
}
}
fun showContent(isRefresh: Boolean = false) {
if (isRefresh) {
binding.flContent.visibility = View.VISIBLE
}
}
private fun initRv() {
adapter = AppListAdapter(itemClick = {
if (!(requireActivity() as AppManagerActivity).animationFinish) {
return@AppListAdapter
}
requireActivity().showAppDetailDialog(it, launcher) { unInstalled ->
if (unInstalled) {
adapter?.removeData(it)
otherAppRemove(it)
}
}
}, itemSelect = { selectList ->
showUnInstall(selectList)
})
binding.rv.adapter = adapter
}
private fun showUnInstall(selectList: List<AppBean>) {
(requireActivity() as AppManagerActivity).showUnInstall(selectList)
}
private fun orderList() {
when (type) {
APP_LIST_TYPE_NAME -> {
list.forEach {
it.pinYin = getPinyin(it.appName)
}
if (isAsc) {
list.sortBy { it.pinYin }
} else {
list.sortByDescending { it.pinYin }
}
}
APP_LIST_TYPE_INSTALL -> {
if (isAsc) {
list.sortBy { it.installTime }
} else {
list.sortByDescending { it.installTime }
}
}
APP_LIST_TYPE_SIZE -> {
if (isAsc) {
list.sortBy { it.appSize }
} else {
list.sortByDescending { it.appSize }
}
}
APP_LIST_TYPE_LAST_USE -> {
if (isAsc) {
list.sortBy { it.lastUsedTime }
} else {
list.sortByDescending { it.lastUsedTime }
}
}
}
}
/**
* 翻转顺序
*/
fun reverseOrder() {
isAsc = !isAsc
orderList()
adapter?.setData(list)
}
private fun otherAppRemove(appBean: AppBean) {
(requireActivity() as AppManagerActivity).otherPageRemove(appBean, this)
}
companion object {
const val APP_LIST_TYPE_NAME = 12
const val APP_LIST_TYPE_INSTALL = 15
const val APP_LIST_TYPE_SIZE = 19
const val APP_LIST_TYPE_LAST_USE = 123
}
/**
* 汉字转为拼音
* https://cloud.tencent.com/developer/article/1731852
* @return
*/
fun getPinyin(str: String): String {
val format = HanyuPinyinOutputFormat()
format.caseType = HanyuPinyinCaseType.UPPERCASE
format.toneType = HanyuPinyinToneType.WITHOUT_TONE
val sb = StringBuilder()
val strNoSpace = str.replace("\\s".toRegex(), "").trim()
// LogEx.logDebug(TAG, "strNoSpace=$strNoSpace")
val charArray = strNoSpace.toCharArray()
for (i in charArray.indices) {
val c = charArray[i]
// 如果是空格, 跳过
if (Character.isWhitespace(c)) {
continue
}
// LogEx.logDebug(TAG, "c.code=${c.code}")
if (c.code == -127 || c.code < 128) {
// 肯定不是汉字
sb.append(c)
} else {
var s: String? = ""
try {
// LogEx.logDebug(TAG, "c=$c")
// 通过char得到拼音集合. 单 - dan, shan
val array = PinyinHelper.toHanyuPinyinStringArray(c, format)
if (array.isNotEmpty()) {
s = array.first()
sb.append(s)
}
} catch (e: BadHanyuPinyinOutputFormatCombination) {
e.printStackTrace()
sb.append(s)
}
}
}
// LogEx.logDebug(TAG, "ping str=$sb")
return sb.toString()
}
private fun checkUsageAccessSettings(context: Context): Boolean {
val appOpsManager = context.getSystemService(Context.APP_OPS_SERVICE) as AppOpsManager
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
appOpsManager.unsafeCheckOpNoThrow(
AppOpsManager.OPSTR_GET_USAGE_STATS,
android.os.Process.myUid(),
context.packageName
) == AppOpsManager.MODE_ALLOWED
} else {
appOpsManager.checkOpNoThrow(
AppOpsManager.OPSTR_GET_USAGE_STATS,
android.os.Process.myUid(),
context.packageName
) == AppOpsManager.MODE_ALLOWED
}
}
}
\ No newline at end of file
package com.test.basd.smartjunkcleaner.fragment
import android.annotation.SuppressLint
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.graphics.Color
import android.os.Bundle
import android.os.SystemClock
import com.github.mikephil.charting.components.XAxis
import com.github.mikephil.charting.components.YAxis
import com.github.mikephil.charting.data.Entry
import com.github.mikephil.charting.data.LineData
import com.github.mikephil.charting.data.LineDataSet
import com.github.mikephil.charting.formatter.IFillFormatter
import com.test.basd.smartjunkcleaner.R
import com.test.basd.smartjunkcleaner.activity.BatteryInfoActivity
import com.test.basd.smartjunkcleaner.databinding.FragmentBatteryBinding
import com.test.basd.smartjunkcleaner.helps.BaseFragment
import java.util.Calendar
import java.util.Date
class BatteryFragment : BaseFragment<FragmentBatteryBinding>() {
private lateinit var batteryReceiver: BatteryReceiver
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
batteryReceiver = BatteryReceiver()
}
override val binding: FragmentBatteryBinding by lazy {
FragmentBatteryBinding.inflate(layoutInflater)
}
override fun onResume() {
super.onResume()
val filter = IntentFilter(Intent.ACTION_BATTERY_CHANGED)
requireContext().registerReceiver(batteryReceiver, filter)
}
override fun onPause() {
super.onPause()
requireContext().unregisterReceiver(batteryReceiver)
}
override fun setView() {
binding.tvGo.setOnClickListener {
requireContext().startActivity(Intent(requireContext(), BatteryInfoActivity::class.java))
}
// testChart()
}
private inner class BatteryReceiver : BroadcastReceiver() {
@SuppressLint("SetTextI18n")
override fun onReceive(context: Context?, intent: Intent?) {
val current = intent?.extras?.getInt("level") ?: 0
val total = intent?.extras?.getInt("scale") ?: 0
val percent = current * 100 / total
binding.tvNumber.text = "$percent"
when {
percent >= 80 -> {
binding.ivBattery.setImageResource(R.mipmap.dianchitu_80)
}
percent > 50 -> {
binding.ivBattery.setImageResource(R.mipmap.dianchitu_50)
}
percent >= 30 -> {
binding.ivBattery.setImageResource(R.mipmap.dianchitu_30)
}
}
val uptime = SystemClock.elapsedRealtime()
val currantTime = Calendar.getInstance().time
val batteryUseTime = currantTime.time - uptime
val batteryDate = Date(batteryUseTime)
binding.tvTime.text = "${batteryDate.hours} H ${batteryDate.minutes} M"
}
}
fun testChart() {
binding.chart.setViewPortOffsets(0f, 0f, 0f, 0f)
binding.chart.setBackgroundColor(Color.rgb(243, 245, 249))
binding.chart.description.isEnabled = false
binding.chart.setDrawGridBackground(false)
val x: XAxis = binding.chart.xAxis
x.setDrawGridLines(false)
x.isEnabled = false
val y: YAxis = binding.chart.axisLeft
y.setLabelCount(6, false)
y.textColor = Color.WHITE
y.setPosition(YAxis.YAxisLabelPosition.INSIDE_CHART)
y.setDrawGridLines(false)
y.axisLineColor = Color.WHITE
binding.chart.axisRight.isEnabled = false
val values = ArrayList<Entry>()
for (i in 0 until 10) {
val value = (Math.random() * (50 + 1)).toFloat() + 20
values.add(Entry(i.toFloat(), value))
var set1: LineDataSet
if (binding.chart.getData() != null &&
binding.chart.getData().getDataSetCount() > 0
) {
set1 = binding.chart.data.getDataSetByIndex(0) as LineDataSet
set1.setValues(values)
binding.chart.data.notifyDataChanged()
binding.chart.notifyDataSetChanged()
} else {
// create a dataset and give it a type
set1 = LineDataSet(values, "")
set1.mode = LineDataSet.Mode.CUBIC_BEZIER
set1.setCubicIntensity(0.2f)
set1.setDrawFilled(true)
set1.setDrawCircles(false)
set1.setLineWidth(1.8f)
set1.circleRadius = 4f
set1.setCircleColor(Color.WHITE)
set1.highLightColor = Color.rgb(71, 114, 255)
set1.setColor(Color.WHITE)
set1.setFillColor(Color.WHITE)
set1.fillAlpha = 100
set1.setDrawHorizontalHighlightIndicator(false)
set1.fillFormatter = IFillFormatter { _, _ -> binding.chart.axisLeft.axisMinimum }
// create a data object with the data sets
val data = LineData(set1)
data.setValueTextSize(9f)
data.setDrawValues(false)
// set data
binding.chart.setData(data)
}
}
}
}
\ No newline at end of file
package com.test.basd.smartjunkcleaner.fragment
import android.animation.ValueAnimator
import android.annotation.SuppressLint
import android.content.Intent
import android.os.Environment
import android.os.StatFs
import android.util.Log
import android.view.animation.LinearInterpolator
import android.widget.ScrollView
import androidx.core.view.updatePadding
import com.blankj.utilcode.util.BarUtils
import com.blankj.utilcode.util.SPUtils
import com.test.basd.smartjunkcleaner.activity.AppManagerActivity
import com.test.basd.smartjunkcleaner.activity.BatteryInfoActivity
import com.test.basd.smartjunkcleaner.activity.LargeFileCleanActivity
import com.test.basd.smartjunkcleaner.activity.NetWorkActivity
import com.test.basd.smartjunkcleaner.activity.RecentAppActivity
import com.test.basd.smartjunkcleaner.activity.ScanJunkActivity
import com.test.basd.smartjunkcleaner.activity.SpeakerCleanerActivity
import com.test.basd.smartjunkcleaner.activity.photocompress.photo.StartCompressionPhotoActivity
import com.test.basd.smartjunkcleaner.databinding.FragmentLayoutHomeBinding
import com.test.basd.smartjunkcleaner.helps.BaseFragment
import com.test.basd.smartjunkcleaner.helps.KotlinExt.setOnClickListener
import com.test.basd.smartjunkcleaner.helps.KotlinExt.toFormatSize
import com.test.basd.smartjunkcleaner.helps.NetWorkSpeed
import com.test.basd.smartjunkcleaner.notificationclean.NotificationCleanActivity
import com.test.basd.smartjunkcleaner.notificationclean.NotificationGuestActivity
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.MainScope
import kotlinx.coroutines.launch
class HomeFragment : BaseFragment<FragmentLayoutHomeBinding>() {
override val binding: FragmentLayoutHomeBinding by lazy {
FragmentLayoutHomeBinding.inflate(layoutInflater)
}
override fun setView() {
binding.root.updatePadding(top = BarUtils.getStatusBarHeight())
ValueAnimator.ofFloat(0f, 360f).run {
duration = 1000
repeatMode = ValueAnimator.RESTART
repeatCount = ValueAnimator.INFINITE
interpolator = LinearInterpolator()
addUpdateListener {
runCatching {
binding.idImgXuanzhuan.rotation = it.animatedValue as Float
}
}
start()
}
initStorage()
}
@SuppressLint("SetTextI18n")
override fun setListener() {
listOf(binding.idCleanJunk, binding.idFlClean).setOnClickListener {
startActivity(Intent(requireContext(), ScanJunkActivity::class.java))
}
binding.idSpeakClean.setOnClickListener {
startActivity(Intent(requireActivity(), SpeakerCleanerActivity::class.java))
}
binding.idPhotoCompress.setOnClickListener {
startActivity(Intent(requireActivity(), StartCompressionPhotoActivity::class.java))
}
binding.idAppManager.setOnClickListener {
startActivity(Intent(requireContext(), AppManagerActivity::class.java))
}
binding.idLargeFile.setOnClickListener {
startActivity(Intent(requireContext(), LargeFileCleanActivity::class.java))
}
binding.idBatteryInfo.setOnClickListener {
startActivity(Intent(requireContext(), BatteryInfoActivity::class.java))
}
binding.idNotificationCleaner.setOnClickListener {
if (SPUtils.getInstance().getBoolean("notification_guest", false)) {
startActivity(Intent(requireActivity(), NotificationCleanActivity::class.java))
} else {
startActivity(Intent(requireActivity(), NotificationGuestActivity::class.java))
}
}
binding.idRecentApp.setOnClickListener {
startActivity(Intent(requireActivity(), RecentAppActivity::class.java))
}
binding.idNetwork.setOnClickListener {
startActivity(Intent(requireActivity(), NetWorkActivity::class.java))
}
MainScope().launch(Dispatchers.IO) {
NetWorkSpeed.startMoritor()
}
}
var totalsize = 0L
@SuppressLint("SetTextI18n")
private fun initStorage() {
val stat = StatFs(Environment.getExternalStorageDirectory().path)
val totalSize = f()
val availableSize = b()
val usedSize = totalSize - availableSize
val usedPercentage = usedSize.toFloat() / totalSize * 100
binding.idTvOccupied.text = "${usedSize.toFormatSize()}/ ${totalSize.toFormatSize()}"
}
fun f(): Long {
return if (totalsize > 0) {
totalsize
} else try {
val statFs = StatFs(Environment.getExternalStorageDirectory().path)
val statFs2 = StatFs(Environment.getRootDirectory().path)
Log.i(
"storeByteDebug",
"store1: " + statFs2.blockSizeLong * statFs2.blockCountLong + "====store2: " + statFs.blockSizeLong * statFs.blockCountLong
)
var blockSizeLong =
statFs2.blockSizeLong * statFs2.blockCountLong + statFs.blockSizeLong * statFs.blockCountLong
blockSizeLong = getClosestPowerOfTwo(blockSizeLong)
totalsize = blockSizeLong
blockSizeLong
} catch (e10: java.lang.Exception) {
0L
}
}
fun a(j10: Long): Long {
var j10 = j10
var i7 = 0
while (true) {
val j11: Long = 1000
if (j10 < j11) {
break
}
j10 /= j11
i7++
}
for (i10 in 0 until i7) {
j10 *= 1024
}
return j10
}
fun b(): Long {
try {
val statFs = StatFs(Environment.getExternalStorageDirectory().path)
val statFs2 = StatFs(Environment.getRootDirectory().path)
val blackSize =
statFs2.blockSizeLong * statFs2.availableBlocksLong + statFs.blockSizeLong * statFs.availableBlocksLong
return a(blackSize)
} catch (e10: Exception) {
return 0L
}
}
fun getClosestPowerOfTwo(size: Long): Long {
var powerOfTwo: Long = 1
while (powerOfTwo < size) {
powerOfTwo *= 2
}
return powerOfTwo
}
}
\ No newline at end of file
package com.test.basd.smartjunkcleaner.fragment
import android.annotation.SuppressLint
import android.app.usage.UsageEvents
import android.app.usage.UsageEvents.Event.ACTIVITY_PAUSED
import android.app.usage.UsageEvents.Event.ACTIVITY_RESUMED
import android.app.usage.UsageEvents.Event.FOREGROUND_SERVICE_START
import android.graphics.Typeface
import android.os.Bundle
import android.view.View
import androidx.core.content.ContextCompat
import com.test.basd.smartjunkcleaner.R
import com.test.basd.smartjunkcleaner.activity.RecentAppActivity
import com.test.basd.smartjunkcleaner.adapter.RecentAppAdapter
import com.test.basd.smartjunkcleaner.bean.AppBean
import com.test.basd.smartjunkcleaner.databinding.FragmentLaunchesBinding
import com.test.basd.smartjunkcleaner.helps.BaseFragment
import com.test.basd.smartjunkcleaner.helps.LogEx
import com.test.basd.smartjunkcleaner.helps.TimeUtils
import com.test.basd.smartjunkcleaner.helps.TimeUtils.PAST_60_MINUS_QUERY
import com.test.basd.smartjunkcleaner.helps.TimeUtils.SEVEN_DAYS_QUERY
import com.test.basd.smartjunkcleaner.helps.TimeUtils.TODAY_QUERY
import com.test.basd.smartjunkcleaner.helps.TimeUtils.YESTERDAY_QUERY
import com.test.basd.smartjunkcleaner.view.TimeSelectDialog.showTimeSelectDialog
import java.text.SimpleDateFormat
import kotlin.time.Duration.Companion.hours
import kotlin.time.DurationUnit
class LaunchesFragment : BaseFragment<FragmentLaunchesBinding>() {
private val TAG = "LaunchesFragment"
private var dataList = arrayListOf<AppBean>()
private lateinit var adapter: RecentAppAdapter
private var simpleDateFormat2 = SimpleDateFormat("(yyyy/MM/dd)")
private var UI_MODE = UI_MODE_ALL
override val binding: FragmentLaunchesBinding by lazy {
FragmentLaunchesBinding.inflate(layoutInflater)
}
override fun setView() {
setTextFont()
adapter = RecentAppAdapter(requireActivity(), RecentAppAdapter.UI_LAUNCHES_MODE)
binding.rv.adapter = adapter
}
override fun setListener() {
binding.clLaunches.setOnClickListener {
launchesMode()
}
binding.clForeground.setOnClickListener {
foregroundMode()
}
binding.clBackground.setOnClickListener {
backgroundMode()
}
binding.llDate.setOnClickListener {
requireContext().showTimeSelectDialog {
when (it) {
PAST_60_MINUS_QUERY -> {
recent60UI()
}
TODAY_QUERY -> {
todayUI()
}
YESTERDAY_QUERY -> {
yesterdayUI()
}
SEVEN_DAYS_QUERY -> {
sevenDaysUI()
}
}
}
}
todayUI(false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
}
override fun onResume() {
super.onResume()
}
@SuppressLint("SetTextI18n")
fun todayUI(isReFresh: Boolean = true) {
binding.tvDate.text = "Today" + simpleDateFormat2.format(System.currentTimeMillis())
if (isReFresh) {
(requireActivity() as RecentAppActivity).changeTimeRefresh(TODAY_QUERY, 0)
}
}
@SuppressLint("SetTextI18n")
fun recent60UI(isReFresh: Boolean = true) {
binding.tvDate.text = "Recent 60 minus"
if (isReFresh) {
(requireActivity() as RecentAppActivity).changeTimeRefresh(PAST_60_MINUS_QUERY, 0)
}
}
@SuppressLint("SetTextI18n")
fun yesterdayUI(isReFresh: Boolean = true) {
binding.tvDate.text =
"Yesterday" + simpleDateFormat2.format(System.currentTimeMillis() - 24.hours.toLong(DurationUnit.MILLISECONDS))
if (isReFresh) {
(requireActivity() as RecentAppActivity).changeTimeRefresh(YESTERDAY_QUERY, 0)
}
}
@SuppressLint("SetTextI18n")
fun sevenDaysUI(isReFresh: Boolean = true) {
binding.tvDate.text = "Last 7 days"
if (isReFresh) {
(requireActivity() as RecentAppActivity).changeTimeRefresh(SEVEN_DAYS_QUERY, 0)
}
}
private fun filterRv() {
adapter.setNumberMode(UI_MODE)
when (UI_MODE) {
UI_MODE_ALL -> {
val data = dataList.filter { it.launchTimes > 0 }
adapter.setData(data)
}
UI_MODE_FOREGROUND -> {
val data = dataList.filter { it.foregroundTimes > 0 }
adapter.setData(data)
}
UI_MODE_BACKGROUND -> {
val data = dataList.filter { it.backgroundTimes > 0 }
adapter.setData(data)
}
}
}
fun launchesMode() {
foregroundEnable(false)
backgroundEnable(false)
launchesEnable(true)
UI_MODE = UI_MODE_ALL
filterRv()
}
fun foregroundMode() {
launchesEnable(false)
backgroundEnable(false)
foregroundEnable(true)
UI_MODE = UI_MODE_FOREGROUND
filterRv()
}
fun backgroundMode() {
launchesEnable(false)
foregroundEnable(false)
backgroundEnable(true)
UI_MODE = UI_MODE_BACKGROUND
filterRv()
}
fun launchesEnable(enable: Boolean) {
if (enable) {
val enableBorder =
ContextCompat.getDrawable(requireContext(), R.drawable.bg_corners_recent)
binding.ivLaunchesBorder.background = enableBorder
binding.ivLaunchesTriangle.visibility = View.VISIBLE
binding.tvLaunchesNumber.setTextColor(
ContextCompat.getColor(
requireContext(),
R.color.white
)
)
binding.tvLaunches.setTextColor(ContextCompat.getColor(requireContext(), R.color.white))
} else {
val disableBorder =
ContextCompat.getDrawable(requireContext(), R.drawable.bg_stroke_e3e5ea)
binding.ivLaunchesBorder.background = disableBorder
binding.ivLaunchesTriangle.visibility = View.GONE
binding.tvLaunchesNumber.setTextColor(
ContextCompat.getColor(
requireContext(),
R.color.black
)
)
binding.tvLaunches.setTextColor(
ContextCompat.getColor(
requireContext(),
R.color.color_999999
)
)
}
}
fun foregroundEnable(enable: Boolean) {
if (enable) {
val enableBorder =
ContextCompat.getDrawable(requireContext(), R.drawable.bg_corners_recent)
binding.ivForegroundBorder.background = enableBorder
binding.ivForegroundTriangle.visibility = View.VISIBLE
binding.tvForegroundNumber.setTextColor(
ContextCompat.getColor(
requireContext(),
R.color.white
)
)
binding.tvForeground.setTextColor(
ContextCompat.getColor(
requireContext(),
R.color.white
)
)
} else {
val disableBorder =
ContextCompat.getDrawable(requireContext(), R.drawable.bg_stroke_e3e5ea)
binding.ivForegroundBorder.background = disableBorder
binding.ivForegroundTriangle.visibility = View.GONE
binding.tvForegroundNumber.setTextColor(
ContextCompat.getColor(
requireContext(),
R.color.black
)
)
binding.tvForeground.setTextColor(
ContextCompat.getColor(
requireContext(),
R.color.color_999999
)
)
}
}
fun backgroundEnable(enable: Boolean) {
if (enable) {
val enableBorder =
ContextCompat.getDrawable(requireContext(), R.drawable.bg_corners_recent)
binding.ivBackgroundBorder.background = enableBorder
binding.ivBackgroundTriangle.visibility = View.VISIBLE
binding.tvBackgroundNumber.setTextColor(
ContextCompat.getColor(
requireContext(),
R.color.white
)
)
binding.tvBackground.setTextColor(
ContextCompat.getColor(
requireContext(),
R.color.white
)
)
} else {
val disableBorder =
ContextCompat.getDrawable(requireContext(), R.drawable.bg_stroke_e3e5ea)
binding.ivBackgroundBorder.background = disableBorder
binding.ivBackgroundTriangle.visibility = View.GONE
binding.tvBackgroundNumber.setTextColor(
ContextCompat.getColor(
requireContext(),
R.color.black
)
)
binding.tvBackground.setTextColor(
ContextCompat.getColor(
requireContext(),
R.color.color_999999
)
)
}
}
private fun setTextFont() {
val fontMedium = "sans-serif-medium"
val fontMediumTypeface = Typeface.create(fontMedium, Typeface.NORMAL)
binding.tvDate.setTypeface(fontMediumTypeface)
}
/**
* 更新数据
*/
fun setAppUseData(dataList: List<AppBean>) {
LogEx.logDebug(TAG, "setAppUseData")
this.dataList.clear()
this.dataList.addAll(dataList)
//setLaunchNumber()
if (isVisible) {
setLaunchNumber2()
launchesMode()
}
}
private fun setLaunchNumber2() {
dataList.forEach { app ->
app.foregroundTimes = app.usageEvents?.filter { it.eventType == 1 }?.size ?: 0
app.backgroundTimes = app.usageEvents?.filter { it.eventType == 19 }?.size ?: 0
app.launchTimes = app.foregroundTimes + app.backgroundTimes
}
val totalNumber = dataList.sumOf { it.launchTimes }
val foregroundNumber = dataList.sumOf { it.foregroundTimes }
val backgroundNumber = dataList.sumOf { it.backgroundTimes }
binding.tvLaunchesNumber.text = totalNumber.toString()
binding.tvForegroundNumber.text = foregroundNumber.toString()
binding.tvBackgroundNumber.text = backgroundNumber.toString()
}
/**
* 旧的计算前台后台数量逻辑
*/
@SuppressLint("InlinedApi")
private fun setLaunchNumber() {
val tempList = ArrayList<UsageEvents.Event>()
dataList.forEach { app ->
app.usageEvents?.let { events ->
tempList.clear()
tempList.addAll(events)
tempList.forEachIndexed { index, event ->
if (app.pkg == "tv.danmaku.bili") {
LogEx.logDebug(
TAG, "$index ${event.packageName} " +
TimeUtils.simpleDateFormat.format(event.timeStamp) +
" ${event.eventType}"
)
}
//后台启动次数
if (event.eventType == FOREGROUND_SERVICE_START) {
app.backgroundTimes++
}
//上一个事件
val lastIndex = index - 1
var lastEvent: UsageEvents.Event? = null
if (lastIndex >= 0) {
lastEvent = tempList[lastIndex]
}
//上面是欧拉的前台判断逻辑 event上次 event2本次
// (event == null || !Objects.equals(event.getPackageName(), event2.getPackageName()))
// &&
//((event == null || event.getEventType() == 2) && event2.getEventType() == 1)
if (event.eventType == ACTIVITY_RESUMED) {
val flag1 =
lastEvent == null || ((lastEvent.packageName != app.pkg) && (lastEvent.eventType == ACTIVITY_PAUSED))
val flag2 = (lastEvent != null) && (lastEvent.packageName != app.pkg)
if (flag1) {
app.foregroundTimes++
if (app.pkg == "tv.danmaku.bili") {
LogEx.logDebug(TAG, "前台加一 flag1 ${app.foregroundTimes}")
}
} else if (flag2) {
app.foregroundTimes++
if (app.pkg == "tv.danmaku.bili") {
LogEx.logDebug(TAG, "前台加一 flag2 ${app.foregroundTimes}")
}
}
}
}
}
//2的阈值
if (app.foregroundTimes < 2) {
app.foregroundTimes = 0
}
app.launchTimes = app.foregroundTimes + app.backgroundTimes
if (app.pkg == "tv.danmaku.bili") {
LogEx.logDebug(
TAG, "${app.pkg} launchTimes=${app.launchTimes} " +
"foregroundTimes=${app.foregroundTimes} " +
"backgroundTimes=${app.backgroundTimes}"
)
}
}
val totalNumber = dataList.sumOf { it.launchTimes }
val foregroundNumber = dataList.sumOf { it.foregroundTimes }
val backgroundNumber = dataList.sumOf { it.backgroundTimes }
binding.tvLaunchesNumber.text = totalNumber.toString()
binding.tvForegroundNumber.text = foregroundNumber.toString()
binding.tvBackgroundNumber.text = backgroundNumber.toString()
}
companion object {
const val UI_MODE_ALL = 5
const val UI_MODE_FOREGROUND = 25
const val UI_MODE_BACKGROUND = 35
}
}
\ No newline at end of file
package com.test.basd.smartjunkcleaner.fragment
import com.test.basd.smartjunkcleaner.databinding.FragmentNetworkBinding
import com.test.basd.smartjunkcleaner.helps.BaseFragment
class NetworkFragment : BaseFragment<FragmentNetworkBinding>() {
override val binding: FragmentNetworkBinding by lazy {
FragmentNetworkBinding.inflate(layoutInflater)
}
override fun setView() {
}
}
\ No newline at end of file
package com.test.basd.smartjunkcleaner.fragment
import android.annotation.SuppressLint
import android.graphics.Typeface
import android.os.Bundle
import com.test.basd.smartjunkcleaner.activity.RecentAppActivity
import com.test.basd.smartjunkcleaner.adapter.RecentAppAdapter
import com.test.basd.smartjunkcleaner.adapter.RecentAppAdapter.Companion.UI_SCREEN_TIME_MODE
import com.test.basd.smartjunkcleaner.bean.AppBean
import com.test.basd.smartjunkcleaner.databinding.FragmentScreenTimeBinding
import com.test.basd.smartjunkcleaner.helps.BaseFragment
import com.test.basd.smartjunkcleaner.helps.TimeUtils.PAST_60_MINUS_QUERY
import com.test.basd.smartjunkcleaner.helps.TimeUtils.SEVEN_DAYS_QUERY
import com.test.basd.smartjunkcleaner.helps.TimeUtils.TODAY_QUERY
import com.test.basd.smartjunkcleaner.helps.TimeUtils.YESTERDAY_QUERY
import com.test.basd.smartjunkcleaner.view.TimeSelectDialog.showTimeSelectDialog
import java.text.SimpleDateFormat
import kotlin.time.Duration.Companion.hours
import kotlin.time.DurationUnit
/**
*/
class ScreenTimeFragment : BaseFragment<FragmentScreenTimeBinding>() {
private var simpleDateFormat2 = SimpleDateFormat("(yyyy/MM/dd)")
private lateinit var adapter: RecentAppAdapter
private val dataList = arrayListOf<AppBean>()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
}
override val binding: FragmentScreenTimeBinding by lazy {
FragmentScreenTimeBinding.inflate(layoutInflater)
}
override fun setView() {
setTextFont()
binding.llDate.setOnClickListener {
requireContext().showTimeSelectDialog {
when (it) {
PAST_60_MINUS_QUERY -> {
recent60UI()
}
TODAY_QUERY -> {
todayUI()
}
YESTERDAY_QUERY -> {
yesterdayUI()
}
SEVEN_DAYS_QUERY -> {
sevenDaysUI()
}
}
}
}
adapter = RecentAppAdapter(requireActivity(), UI_SCREEN_TIME_MODE)
binding.rv.adapter = adapter
adapter.setData(dataList)
todayUI(false)
}
private fun setTextFont() {
val fontMedium = "sans-serif-medium"
val fontMediumTypeface = Typeface.create(fontMedium, Typeface.NORMAL)
binding.tvDate.setTypeface(fontMediumTypeface)
}
@SuppressLint("SetTextI18n")
fun todayUI(isReFresh: Boolean = true) {
binding.tvDate.text = "Today" + simpleDateFormat2.format(System.currentTimeMillis())
if (isReFresh) {
(requireActivity() as RecentAppActivity).changeTimeRefresh(TODAY_QUERY, 1)
}
}
@SuppressLint("SetTextI18n")
fun recent60UI(isReFresh: Boolean = true) {
binding.tvDate.text = "Recent 60 minus"
if (isReFresh) {
(requireActivity() as RecentAppActivity).changeTimeRefresh(PAST_60_MINUS_QUERY, 1)
}
}
@SuppressLint("SetTextI18n")
fun yesterdayUI(isReFresh: Boolean = true) {
binding.tvDate.text =
"Yesterday" + simpleDateFormat2.format(
System.currentTimeMillis() - 24.hours.toLong(
DurationUnit.MILLISECONDS
)
)
if (isReFresh) {
(requireActivity() as RecentAppActivity).changeTimeRefresh(YESTERDAY_QUERY, 1)
}
}
@SuppressLint("SetTextI18n")
fun sevenDaysUI(isReFresh: Boolean = true) {
binding.tvDate.text = "Last 7 days"
if (isReFresh) {
(requireActivity() as RecentAppActivity).changeTimeRefresh(SEVEN_DAYS_QUERY, 1)
}
}
fun setScreenData(dataList: ArrayList<AppBean>) {
this.dataList.clear()
this.dataList.addAll(dataList.filter { it.screenTime > 0 })
if (isVisible) {
adapter.setData(this.dataList)
}
}
override fun onResume() {
super.onResume()
}
}
\ No newline at end of file
package com.test.basd.smartjunkcleaner.fragment
import android.content.Intent
import com.blankj.utilcode.util.SPUtils
import com.test.basd.smartjunkcleaner.activity.AppManagerActivity
import com.test.basd.smartjunkcleaner.activity.BatteryInfoActivity
import com.test.basd.smartjunkcleaner.activity.LargeFileCleanActivity
import com.test.basd.smartjunkcleaner.activity.NetWorkActivity
import com.test.basd.smartjunkcleaner.activity.RecentAppActivity
import com.test.basd.smartjunkcleaner.activity.RepeaterdPhotoActivity
import com.test.basd.smartjunkcleaner.activity.ScanJunkActivity
import com.test.basd.smartjunkcleaner.activity.SpeakerCleanerActivity
import com.test.basd.smartjunkcleaner.activity.photocompress.photo.StartCompressionPhotoActivity
import com.test.basd.smartjunkcleaner.adapter.ToolsAdapter
import com.test.basd.smartjunkcleaner.databinding.FragmentLayoutToolsBinding
import com.test.basd.smartjunkcleaner.helps.BaseFragment
import com.test.basd.smartjunkcleaner.helps.ConfigHelper
import com.test.basd.smartjunkcleaner.helps.KotlinExt.toFormatSize
import com.test.basd.smartjunkcleaner.notificationclean.NotificationCleanActivity
import com.test.basd.smartjunkcleaner.notificationclean.NotificationGuestActivity
import com.test.basd.smartjunkcleaner.view.AFunOb
import com.test.basd.smartjunkcleaner.view.AFunOb.APP_MANAGER
import com.test.basd.smartjunkcleaner.view.AFunOb.BATTERY_INFO
import com.test.basd.smartjunkcleaner.view.AFunOb.LARGE_FILE_CLEANER
import com.test.basd.smartjunkcleaner.view.AFunOb.NETWORK_TRAFFIC
import com.test.basd.smartjunkcleaner.view.AFunOb.NOTIFICATION_CLEANER
import com.test.basd.smartjunkcleaner.view.AFunOb.PHOTO_COMPRESS
import com.test.basd.smartjunkcleaner.view.AFunOb.RECENT_APP_USAGE
import com.test.basd.smartjunkcleaner.view.AFunOb.SIMILAR_PHOTOS
import com.test.basd.smartjunkcleaner.view.AFunOb.SPEAK_CLEANER
class ToolsFragment : BaseFragment<FragmentLayoutToolsBinding>() {
private lateinit var adapter: ToolsAdapter
override val binding: FragmentLayoutToolsBinding by lazy {
FragmentLayoutToolsBinding.inflate(layoutInflater)
}
override fun setView() {
adapter = ToolsAdapter(requireActivity()) { kName ->
when (kName) {
AFunOb.JUNK_CLEANER -> {
startActivity(Intent(requireContext(), ScanJunkActivity::class.java))
}
RECENT_APP_USAGE -> {
startActivity(Intent(requireActivity(), RecentAppActivity::class.java))
}
LARGE_FILE_CLEANER -> {
startActivity(Intent(requireContext(), LargeFileCleanActivity::class.java))
}
NOTIFICATION_CLEANER -> {
if (SPUtils.getInstance().getBoolean("notification_guest", false)) {
startActivity(Intent(requireActivity(), NotificationCleanActivity::class.java))
} else {
startActivity(Intent(requireActivity(), NotificationGuestActivity::class.java))
}
}
NETWORK_TRAFFIC -> {
startActivity(Intent(requireContext(), NetWorkActivity::class.java))
}
APP_MANAGER -> {
startActivity(Intent(requireContext(), AppManagerActivity::class.java))
}
SIMILAR_PHOTOS -> {
startActivity(Intent(requireContext(), RepeaterdPhotoActivity::class.java))
}
SPEAK_CLEANER -> {
startActivity(Intent(requireContext(), SpeakerCleanerActivity::class.java))
}
PHOTO_COMPRESS -> {
startActivity(Intent(requireActivity(), StartCompressionPhotoActivity::class.java))
}
BATTERY_INFO -> {
startActivity(Intent(requireActivity(), BatteryInfoActivity::class.java))
}
}
}
binding.rvTools.adapter = adapter
}
override fun onResume() {
super.onResume()
val split = ConfigHelper.junkSizeClean.toFormatSize(1).split(' ')
binding.tvSize.text = split[0]
binding.tvSizeUnit.text = split[1]
}
}
\ No newline at end of file
package com.test.basd.smartjunkcleaner.helps
import android.util.Base64
import java.security.SecureRandom
import javax.crypto.Cipher
import javax.crypto.spec.GCMParameterSpec
import javax.crypto.spec.SecretKeySpec
object AESHelper {
private const val aesKey = "ofm87b2j718gjz8t"
private val cipher by lazy {
Cipher.getInstance("AES/GCM/NoPadding")
}
fun encrypt(content: String): String {
try {
val iv = ByteArray(12).apply {
SecureRandom().nextBytes(this)
}
val contentBytes = content.toByteArray(Charsets.UTF_8)
val params = GCMParameterSpec(128, iv)
cipher.init(
Cipher.ENCRYPT_MODE,
secretKey, params
)
val encryptData = cipher.doFinal(contentBytes)
assert(encryptData.size == contentBytes.size + 16)
val message = ByteArray(12 + contentBytes.size + 16)
System.arraycopy(iv, 0, message, 0, 12)
System.arraycopy(encryptData, 0, message, 12, encryptData.size)
return String(Base64.encode(message, Base64.NO_WRAP), Charsets.UTF_8)
} catch (_: Exception) {
}
return content
}
@Synchronized
fun decrypt(content: String): String {
try {
val con = content.replace(" ".toRegex(), "+")
val contentByte = Base64.decode(con, Base64.NO_WRAP)
require(contentByte.size >= 12 + 16)
val params = GCMParameterSpec(128, contentByte, 0, 12)
cipher.init(
Cipher.DECRYPT_MODE,
secretKey, params
)
val decryptData = cipher.doFinal(contentByte, 12, contentByte.size - 12)
return String(decryptData, Charsets.UTF_8)
} catch (_: Exception) {
}
return content
}
private val secretKey by lazy {
SecretKeySpec(aesKey.toByteArray(), "AES")
}
}
\ No newline at end of file
package com.test.basd.smartjunkcleaner.helps
import android.content.Context
import android.util.AttributeSet
import com.noober.background.view.BLTextView
import com.test.basd.smartjunkcleaner.helps.KotlinExt.decode
class AESTextView @JvmOverloads constructor(
context: Context, attrs: AttributeSet? = null
) : BLTextView(context, attrs) {
override fun setText(text: CharSequence?, type: BufferType?) {
super.setText(text?.toString()?.run { decode().takeIf { it != this } } ?: text, type)
}
}
\ No newline at end of file
package com.test.basd.smartjunkcleaner.helps
import android.content.Intent
import androidx.activity.result.ActivityResult
import androidx.activity.result.ActivityResultCallback
import androidx.activity.result.ActivityResultCaller
import androidx.activity.result.contract.ActivityResultContracts
class ActivityLauncher(activityResultCaller: ActivityResultCaller) {
//region 权限
private var permissionCallback: ActivityResultCallback<Map<String, Boolean>>? = null
private val permissionLauncher =
activityResultCaller.registerForActivityResult(ActivityResultContracts.RequestMultiplePermissions()) { result: Map<String, Boolean> ->
permissionCallback?.onActivityResult(result)
}
fun launch(
permissionArray: Array<String>,
permissionCallback: ActivityResultCallback<Map<String, Boolean>>?
) {
this.permissionCallback = permissionCallback
permissionLauncher.launch(permissionArray)
}
//endregion
//region intent跳转
private var activityResultCallback: ActivityResultCallback<ActivityResult>? = null
private val intentLauncher =
activityResultCaller.registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { activityResult: ActivityResult ->
activityResultCallback?.onActivityResult(activityResult)
}
/**
* it.resultCode == Activity.RESULT_OK
*/
fun launch(
intent: Intent,
activityResultCallback: ActivityResultCallback<ActivityResult>? = null
) {
this.activityResultCallback = activityResultCallback
intentLauncher.launch(intent)
}
//endregion
}
\ No newline at end of file
package com.test.basd.smartjunkcleaner.helps
import android.app.usage.StorageStats
import android.app.usage.StorageStatsManager
import android.content.Context
import android.content.pm.IPackageStatsObserver
import android.content.pm.PackageManager
import android.content.pm.PackageStats
import android.os.Build
import android.os.storage.StorageManager
import android.text.format.Formatter
import androidx.annotation.RequiresApi
import java.io.IOException
import java.util.UUID
/**
*获取APP应用 缓存大小 数据大小 应用大小
* aidl的方式反射系统api
*/
class AppSizeUtils private constructor() {
private val TAG = "AppSizeUtils"
/**
* 打印大小
*/
@RequiresApi(Build.VERSION_CODES.O)
fun printStorageStats(context: Context, packageName: String, storageStats: StorageStats?) {
if (storageStats != null) {
val cacheBytesF = Formatter.formatFileSize(context, storageStats.cacheBytes)
val dataBytesF = Formatter.formatFileSize(context, storageStats.cacheBytes)
val appBytesF = Formatter.formatFileSize(context, storageStats.cacheBytes)
val size = storageStats.cacheBytes + storageStats.dataBytes + storageStats.appBytes
val total = Formatter.formatFileSize(context, size)
val log = "$packageName StorageStats cacheBytesF=$cacheBytesF dataBytesF=$dataBytesF appBytesF=$appBytesF total=$total"
LogEx.logDebug(TAG, log)
}
}
fun printPackageStats(context: Context, packageName: String, packageStats: PackageStats?) {
if (packageStats != null) {
val cacheSizeF = Formatter.formatFileSize(context, packageStats.cacheSize)
val dataSizeF = Formatter.formatFileSize(context, packageStats.dataSize)
val codeSizeF = Formatter.formatFileSize(context, packageStats.codeSize)
val size = packageStats.cacheSize + packageStats.dataSize + packageStats.codeSize
val total = Formatter.formatFileSize(context, size)
val log = "$packageName PackageStats cacheSizeF=$cacheSizeF dataSizeF=$dataSizeF codeSizeF=$codeSizeF total=$total"
LogEx.logDebug(TAG, log)
}
}
/**
* 获取应用的大小
*/
@RequiresApi(api = Build.VERSION_CODES.O)
fun getAppSizeO(context: Context, packageName: String): Long {
val storageStatsManager = context.getSystemService(Context.STORAGE_STATS_SERVICE) as StorageStatsManager
val storageManager = context.getSystemService(Context.STORAGE_SERVICE) as StorageManager
//获取所有应用的StorageVolume列表
val storageVolumes = storageManager.getStorageVolumes()
for (item in storageVolumes) {
val uuidStr = item.uuid
val uuid: UUID = if (uuidStr == null) {
StorageManager.UUID_DEFAULT
} else {
UUID.fromString(uuidStr)
}
val uid = getUid(context, packageName)
//通过包名获取uid
var storageStats: StorageStats? = null
try {
storageStats = storageStatsManager.queryStatsForUid(uuid, uid)
} catch (e: IOException) {
e.printStackTrace()
}
if (storageStats != null) {
val size = storageStats.cacheBytes + storageStats.dataBytes + storageStats.appBytes
printStorageStats(context, packageName, storageStats)
return size
}
}
return 0
}
/**
* 根据应用包名获取对应uid
*/
fun getUid(context: Context, pakName: String?): Int {
try {
return context.packageManager.getApplicationInfo(pakName!!, PackageManager.GET_META_DATA).uid
} catch (e: PackageManager.NameNotFoundException) {
e.printStackTrace()
}
return -1
}
/**
* 获取应用大小8.0以下
*/
fun getAppSize(context: Context, packageName: String): Long {
var size = 0L
try {
val method =
PackageManager::class.java.getMethod("getPackageSizeInfo", String::class.java, IPackageStatsObserver::class.java)
// 调用 getPackageSizeInfo 方法,需要两个参数:1、需要检测的应用包名;2、回调
method.invoke(context.packageManager, packageName, object : IPackageStatsObserver.Stub() {
override fun onGetStatsCompleted(pStats: PackageStats, succeeded: Boolean) {
size = pStats.cacheSize + pStats.dataSize + pStats.codeSize
printPackageStats(context, packageName, pStats)
}
})
} catch (e: Exception) {
e.printStackTrace()
}
return size
}
companion object {
private var mApiUrl: AppSizeUtils? = null
val instance: AppSizeUtils?
get() {
if (mApiUrl == null) {
synchronized(AppSizeUtils::class.java) {
if (mApiUrl == null) {
mApiUrl = AppSizeUtils()
}
}
}
return mApiUrl
}
}
}
package com.test.basd.smartjunkcleaner.helps
import android.app.AppOpsManager
import android.app.Dialog
import android.content.Context
import android.content.Intent
import android.net.Uri
import android.os.Build
import android.os.Bundle
import android.os.Environment
import android.provider.Settings
import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AppCompatActivity
import androidx.viewbinding.ViewBinding
import com.blankj.utilcode.constant.PermissionConstants
import com.blankj.utilcode.util.ActivityUtils
import com.blankj.utilcode.util.BarUtils
import com.blankj.utilcode.util.PermissionUtils
import com.test.basd.smartjunkcleaner.activity.MainActivity
import com.test.basd.smartjunkcleaner.activity.PermissionTripActivity
import com.test.basd.smartjunkcleaner.view.DialogViews
import org.json.JSONObject
abstract class BaseActivity<T : ViewBinding> : AppCompatActivity() {
protected abstract val binding: T
protected open val isLightMode: Boolean? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(binding.root)
isLightMode?.let {
BarUtils.setStatusBarLightMode(this, it)
}
EventHelper.event("page_${javaClass.simpleName}")
initView()
initListener()
}
protected abstract fun initView()
protected open fun initListener() {}
private val permissionLauncher =
registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
onPermissionsResult(Environment.isExternalStorageManager())
}
}
open fun onPermissionsResult(isGranted: Boolean) {
val obj = JSONObject()
obj.put("activity", javaClass.simpleName)
if (isGranted) {
EventHelper.event("permission_allow", ext = obj)
} else {
EventHelper.event("permission_deny", ext = obj)
}
}
private var isRequested = false
fun checkPermission(needcheck: Boolean = false) {
if (needcheck) {
onPermissionsResult(
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
Environment.isExternalStorageManager()
} else {
PermissionUtils.isGranted(PermissionConstants.STORAGE)
}
)
} else {
if (isRequested) {
onPermissionsResult(
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
Environment.isExternalStorageManager()
} else {
PermissionUtils.isGranted(PermissionConstants.STORAGE)
}
)
return
}
isRequested = true
// requestPermissionCount--
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
if (Environment.isExternalStorageManager()) {
onPermissionsResult(true)
} else {
if (isFinishing || isDestroyed) {
return
}
dialog = DialogViews.showGerPermission(this, {
val intent =
Intent(Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION)
intent.addCategory("android.intent.category.DEFAULT")
intent.data = Uri.parse("package:${packageName}")
permissionLauncher.launch(intent)
PermissionTripActivity.launch(this)
}, {
finishToMain()
})
}
} else {
PermissionUtils.permissionGroup(PermissionConstants.STORAGE)
.callback { isAllGranted, _, _, _ ->
if (isAllGranted) {
onPermissionsResult(true)
} else {
onPermissionsResult(false)
}
}.request()
}
}
protected fun finishToMain() {
if (this !is MainActivity && !ActivityUtils.isActivityExistsInStack(MainActivity::class.java)) {
startActivity(Intent(this, MainActivity::class.java))
}
finish()
}
private var isRequestedAccess = false
private val accessLauncher =
registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
}
fun checkAccesSettings(needcheck: Boolean = false) {
val appOpsManager = getSystemService(Context.APP_OPS_SERVICE) as AppOpsManager
if (needcheck) {
onAccesSettingsResult(
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
appOpsManager.unsafeCheckOpNoThrow(
AppOpsManager.OPSTR_GET_USAGE_STATS,
android.os.Process.myUid(),
this.packageName
) == AppOpsManager.MODE_ALLOWED
} else {
appOpsManager.checkOpNoThrow(
AppOpsManager.OPSTR_GET_USAGE_STATS,
android.os.Process.myUid(),
this.packageName
) == AppOpsManager.MODE_ALLOWED
}
)
} else {
if (isRequestedAccess) {
onAccesSettingsResult(
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
appOpsManager.unsafeCheckOpNoThrow(
AppOpsManager.OPSTR_GET_USAGE_STATS,
android.os.Process.myUid(),
this.packageName
) == AppOpsManager.MODE_ALLOWED
} else {
appOpsManager.checkOpNoThrow(
AppOpsManager.OPSTR_GET_USAGE_STATS,
android.os.Process.myUid(),
this.packageName
) == AppOpsManager.MODE_ALLOWED
}
)
return
}
isRequestedAccess = true
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
if (appOpsManager.unsafeCheckOpNoThrow(
AppOpsManager.OPSTR_GET_USAGE_STATS,
android.os.Process.myUid(),
this.packageName
) == AppOpsManager.MODE_ALLOWED
) {
onAccesSettingsResult(true)
} else {
val intent = Intent(Settings.ACTION_USAGE_ACCESS_SETTINGS)
intent.addCategory("android.intent.category.DEFAULT")
intent.data = Uri.parse("package:${this.packageName}")
accessLauncher.launch(intent)
}
} else {
if (appOpsManager.checkOpNoThrow(
AppOpsManager.OPSTR_GET_USAGE_STATS,
android.os.Process.myUid(),
this.packageName
) == AppOpsManager.MODE_ALLOWED
) {
onAccesSettingsResult(true)
} else {
val intent = Intent(Settings.ACTION_USAGE_ACCESS_SETTINGS)
intent.addCategory("android.intent.category.DEFAULT")
intent.data = Uri.parse("package:${this.packageName}")
accessLauncher.launch(intent)
}
}
}
open fun onAccesSettingsResult(isGranted: Boolean) {
val obj = JSONObject()
obj.put("activity", javaClass.simpleName)
if (isGranted) {
EventHelper.event("permission_allow", ext = obj)
} else {
EventHelper.event("permission_deny", ext = obj)
}
}
var dialog: Dialog? = null
override fun onDestroy() {
super.onDestroy()
if (dialog != null) {
dialog?.dismiss()
}
}
}
\ No newline at end of file
package com.test.basd.smartjunkcleaner.helps
import android.app.Application
abstract class BaseApplication : Application() {
companion object {
lateinit var context: BaseApplication
}
final override fun onCreate() {
super.onCreate()
context = this
init()
}
abstract fun init()
}
\ No newline at end of file
package com.test.basd.smartjunkcleaner.helps
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import androidx.viewbinding.ViewBinding
abstract class BaseFragment<T : ViewBinding> : Fragment() {
protected abstract val binding: T
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
setView()
setListener()
}
protected abstract fun setView()
protected open fun setListener() {}
}
\ No newline at end of file
package com.test.basd.smartjunkcleaner.helps
import com.blankj.utilcode.util.GsonUtils
import com.blankj.utilcode.util.SPUtils
import com.test.basd.smartjunkcleaner.BuildConfig
import com.test.basd.smartjunkcleaner.bean.ConfigBean
import com.test.basd.smartjunkcleaner.helps.ads.AdmobUtils
import okhttp3.Call
import okhttp3.Callback
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.Response
import okhttp3.logging.HttpLoggingInterceptor
import java.io.IOException
object ComUtils {
private val TAG = "ComUtils"
val localConfig =
"Fe5MEzroM+pCsdsxsLTeUuf7TT5rSM/fOu7xNx5OHfER6Ekm55d6hwrdViuDLK+9+vgB80XMIyb1opNBWRJT5RogMNTdVVkdAAiOGH0WsqZ3zRVyB4lvv4fHKspSJiYEU7bMWtufqGzYML8BMGACAZda80N/0onj7K0gKsV3qnm20QZheQDpg1DKwZQuNDUKDmYbe4QilwTq5r9bLAtXWLNJMuJxd5a0kh9Mdn7q1W1Isd7VVbHfVKtUDoCNPy5tgGgSbAR90niXnh2wwHkFzybAl0xPaEy8tuqmhN91skv27gHODzweQoLH1U5jq8Hux79Sqnr9aTXnOAy3ogFGbPijim1J1H5jl6lqcH0x2KDTrQ6jrQTq1IwKlnKih4hYZPf9G/A4MVOB4ZS+KwrSsGzDGioc25mFm43jSeqp6VgKXV7jrA3E5xK9pUPkxIkaWIxHyrtuWx8xvd0j04XWBIQ+NgyToGJ/ooelKCzT/Bz0Cyz9t7P3b3Qr6ij8G5Y594fM3v1HhHoGeWdYGSS6oWLfoyVOKFpvT78BzVmLjsTcou+i+fOlXl0DmLGk/BH2Rk0p0gzI3FYeA2rH9pUCAhMgaK3+O3N6pu8HM/vmhGkjHnQHQzOx+uhWGqjxbhZgSKVMkwaDnSSZX/ndt8Rge15ktjeKHs9mv0MgQjvda/vnnqdovGlf56lKGnTIlQ7Sk11H/0jiwmDmjge8L4bO+xeKUMRSXFJejTIutGn18Z8pB3khj5fEJQLklUA="
var configSp = ""
get() {
return SPUtils.getInstance().getString("configSp", field)
}
set(value) {
field = value
SPUtils.getInstance().put("configSp", value, true)
}
private val url by lazy {
val pkg = ConfigHelper.packageName
val url = StringBuilder(
"${ConfigHelper.apiUrl}/${
pkg.filter { it.isLowerCase() }.substring(4, 9)
}spk"
)
url.append("?pkg=$pkg")
url.toString()
}
fun requestCfg() {
val client = OkHttpClient.Builder().apply {
if (BuildConfig.DEBUG) {
addInterceptor(HttpLoggingInterceptor().apply {
level = HttpLoggingInterceptor.Level.BODY
})
}
}.build()
LogEx.logDebug(TAG, "url=$url")
val request = Request.Builder()
.url(url)
.get()
.build()
client.newCall(request).enqueue(object : Callback {
override fun onFailure(call: Call, e: IOException) {
EventHelper.event("config_api_error", value = e.toString())
}
override fun onResponse(call: Call, response: Response) {
response.body?.string()?.let {
LogEx.logDebug(TAG, "it=$it")
val i = Regex("\"data\":\"(.*?)\"").find(it)
if (i.toString() != "null") {
i!!.groupValues[1].let {
val str = AESHelper.decrypt(it)
LogEx.logDebug(TAG, "str=$str")
configSp = str
}
}
}
}
})
}
fun getLocalConfigBean(): ConfigBean {
val json = AESHelper.decrypt(localConfig)
LogEx.logDebug(TAG, "Local json=$json")
val configBean = GsonUtils.fromJson(json, ConfigBean::class.java)
LogEx.logDebug(TAG, "Local configBean=$configBean")
return configBean
}
fun getSpConfigBean(): ConfigBean {
var configBean: ConfigBean? = null
try {
configBean = GsonUtils.fromJson(configSp, ConfigBean::class.java)
} catch (e: Exception) {
e.printStackTrace()
} finally {
if (configBean == null) {
LogEx.logDebug(TAG, "Sp configBean=$configBean")
configBean = getLocalConfigBean()
}
}
AdmobUtils.adShowCount = configBean.inter_show_limit
AdmobUtils.nativeAdShowCount = configBean.native_show_limit
AdmobUtils.isNowPlayAd = configBean.isNowPlayAd
SPUtils.getInstance().put("loading_page_time", configBean.open_time_out)
LogEx.logDebug(TAG, "Sp configBean=$configBean")
return configBean
}
}
package com.test.basd.smartjunkcleaner.helps
import com.blankj.utilcode.util.AppUtils
import com.blankj.utilcode.util.SPUtils
import com.test.basd.smartjunkcleaner.activity.splash.NewSplashActivity
object ConfigHelper {
var gid = ""
var isOpenNotification = false
var appList: List<AppUtils.AppInfo>? = null
// 域名
const val eventUrl = "https://rp.denisjodion560.xyz"
const val apiUrl = "https://api.denisjodion560.xyz"
// admob广告id
const val openAdmobId = "/6499/example/app-open"
const val interAdmobId = "ca-app-pub-3940256099942544/1033173712"
const val nativeAdmobId = "ca-app-pub-3940256099942544/2247696110"
// 正式包名
const val packageName = "com.kkcc.ccsupercleanermaxaa.ackk"
val noLoadingActivities = listOf(
"full", // 过滤全屏广告
"adActivity",
NewSplashActivity::class.java.simpleName
// 返回前台时不跳转启动页的 activity
)
var junkSizeClean = 0L
get() {
return SPUtils.getInstance().getLong("junkSizeClean", field)
}
set(value) {
field = value
SPUtils.getInstance().put("junkSizeClean", value)
}
var ifAgreePrivacy = false
get() {
return SPUtils.getInstance().getBoolean("ifAgreePrivacy", field)
}
set(value) {
field = value
SPUtils.getInstance().put("ifAgreePrivacy", value, true)
}
var ifGuest = false
get() {
return SPUtils.getInstance().getBoolean("ifGuest", field)
}
set(value) {
field = value
SPUtils.getInstance().put("ifGuest", value, true)
}
}
\ No newline at end of file
package com.test.basd.smartjunkcleaner.helps
import android.os.Build
import android.util.Log
import com.blankj.utilcode.util.AppUtils
import com.blankj.utilcode.util.DeviceUtils
import com.blankj.utilcode.util.SPUtils
import com.blankj.utilcode.util.ScreenUtils
import com.test.basd.smartjunkcleaner.BuildConfig
import com.test.basd.smartjunkcleaner.helps.ConfigHelper.ifAgreePrivacy
import okhttp3.Call
import okhttp3.Callback
import okhttp3.MediaType.Companion.toMediaTypeOrNull
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.RequestBody.Companion.toRequestBody
import okhttp3.Response
import okhttp3.logging.HttpLoggingInterceptor
import org.json.JSONObject
import java.io.IOException
object EventHelper {
private val TAG = "EventHelper"
private val url by lazy {
val pkg = AppUtils.getAppPackageName()
val url = StringBuilder(
"${ConfigHelper.eventUrl}/${
pkg.filter { it.isLowerCase() }.substring(4, 9)
}sp"
)
url.append("?pkg=$pkg")
url.toString()
}
fun event(
key: String,
value: String? = null,
ext: JSONObject? = null,
isSingleEvent: Boolean = false
) {
if (!ifAgreePrivacy) {
Log.e(TAG, "ifAgreePrivacy=$ifAgreePrivacy")
return
}
if (isSingleEvent) {
val stringSet = SPUtils.getInstance().getStringSet("singleEvent")
if (stringSet.contains(key)) {
return
}
}
val pkg = AppUtils.getAppPackageName()
val s = JSONObject()
.put("action", key)
.put("value", value)
.put("ext", ext)
val s2 = JSONObject()
.put("${pkg}_1", "${ScreenUtils.getScreenHeight()}")
.put("${pkg}_2", "${ScreenUtils.getScreenWidth()}")
.put("${pkg}_3", DeviceUtils.getModel())
.put("${pkg}_4", Build.MANUFACTURER)
.put("${pkg}_5", Build.VERSION.SDK_INT)
.put("${pkg}_8", AppUtils.getAppVersionName())
.put("${pkg}_9", DeviceUtils.getAndroidID())
.put("${pkg}_10", ConfigHelper.gid)
.put("${pkg}_11", System.getProperty("http.agent"))
.put("${pkg}_13", "android")
.put("${pkg}_14", "${AppUtils.getAppVersionCode()}")
.put("${pkg}_15", "google")
.put("${pkg}_24", BuildConfig.BUILD_TYPE)
val data = JSONObject()
.put("data", s)
.put("bp", s2)
.toString()
val body = AESHelper.encrypt(data)
.toRequestBody("application/json;charset=utf-8".toMediaTypeOrNull())
val client = OkHttpClient.Builder().apply {
if (BuildConfig.DEBUG) {
addInterceptor(HttpLoggingInterceptor().apply {
level = HttpLoggingInterceptor.Level.BODY
})
}
}.build()
val request = Request.Builder()
.url(url)
.post(body)
.build()
client.newCall(request).enqueue(object : Callback {
override fun onFailure(call: Call, e: IOException) {
}
override fun onResponse(call: Call, response: Response) {
Log.d("TAG", "event: $key")
if (isSingleEvent) {
val stringSet =
SPUtils.getInstance().getStringSet("singleEvent", mutableSetOf())
stringSet.add(key)
SPUtils.getInstance().put("singleEvent", stringSet)
}
}
})
}
}
\ No newline at end of file
package com.test.basd.smartjunkcleaner.helps
import android.os.Environment
import android.provider.MediaStore
import android.text.TextUtils
import com.blankj.utilcode.util.FileUtils
import com.test.basd.smartjunkcleaner.bean.FileBean
import com.test.basd.smartjunkcleaner.bean.ImageDataBean
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.MainScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import java.io.File
import java.text.SimpleDateFormat
import java.util.Locale
import kotlin.random.Random
object FileHelps {
var usedSize = 0L //已经用的
var freeSize = 0L //剩余大小
var systemSize = 0L //系统占用
var allSize = 0L //所有大小
init {
val root = Environment.getExternalStorageDirectory()
freeSize = FileUtils.getFsAvailableSize(root.absolutePath)
allSize = FileUtils.getFsTotalSize(root.absolutePath)
usedSize = allSize - freeSize
systemSize = FileUtils.getFsTotalSize(Environment.getRootDirectory().absolutePath)
}
private val dateFormat by lazy {
SimpleDateFormat("MMMM dd,yyyy", Locale.getDefault())
}
val fileList = mutableListOf<FileBean>()
private fun initFileList() {
if (fileList.isEmpty()) {
initFileList(Environment.getExternalStorageDirectory())
}
}
fun deleteFile(fileOrDirectory: File): Boolean {
if (fileOrDirectory.isDirectory)
fileOrDirectory.listFiles()?.forEach { child -> deleteFile(child) }
return fileOrDirectory.delete()
}
fun getFileList(onUpdate: () -> Unit, onEnd: (() -> Unit)? = null) {
MainScope().launch(Dispatchers.Main) {
launch(Dispatchers.IO) {
initFileList()
}
var emptyCount = 0
var preSize = 0
while (emptyCount < 12) {
delay(Random.nextLong(400, 800))
if (fileList.isNotEmpty()) {
if (fileList.size - preSize == 0) {
emptyCount++
} else {
emptyCount = 0
onUpdate()
}
preSize = fileList.size
}
}
onEnd?.invoke()
}
}
private fun initFileList(file: File) {
if (file.isFile) {
fileList.add(file.toBean())
} else {
file.listFiles()?.let {
if (it.isEmpty()) {
fileList.add(file.toBean())
} else {
for (i in it) {
initFileList(i)
}
}
}
}
}
fun File.toBean() = FileBean(
name = name,
path = absolutePath,
size = length(),
isDir = isDirectory,
type = if (TextUtils.isEmpty(
FileUtils.getFileExtension(name).lowercase()
)
) "unKnow" else FileUtils.getFileExtension(name).lowercase(),
time = lastModified()
)
val similarImageList = mutableListOf<ImageDataBean>()
fun getImageFiles(callback: () -> Unit) {
MainScope().launch(Dispatchers.Main) {
withContext(Dispatchers.IO) {
val imageList = mutableListOf<String>()
val query = BaseApplication.context.contentResolver.query(
MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
arrayOf(MediaStore.Files.FileColumns.DATA),
null,
null,
"${MediaStore.Files.FileColumns.DATE_MODIFIED} DESC"
) ?: return@withContext
while (query.moveToNext()) {
imageList.add(query.getString(0))
}
query.close()
val imageDataList = mutableListOf<ImageDataBean>()
imageDataList.addAll(imageList.mapNotNull { ImageHelper.createImage(it) })
imageDataList.sortBy { it.hashCode }
similarImageList.clear()
var index1 = 0
var index2 = 1
val similarItem = mutableListOf<ImageDataBean>()
while (index2 < imageDataList.size) {
val first = imageDataList[index1]
val second = imageDataList[index2]
if (ImageHelper.similarCondition(first, second)) {
if (similarItem.indexOf(first) < 0) {
similarItem.add(first)
}
if (similarItem.indexOf(second) < 0) {
similarItem.add(second)
}
} else {
if (similarItem.size > 1) {
similarImageList.addAll(similarItem)
}
similarItem.clear()
}
index1++
index2++
}
if (similarItem.size > 1) {
similarImageList.addAll(similarItem)
}
}
callback()
}
}
val typeDateList = mutableListOf<Pair<String, MutableList<FileBean>>>()
fun getTypeFiles(callback: () -> Unit) {
MainScope().launch(Dispatchers.Main) {
withContext(Dispatchers.IO) {
val typeFiles = mutableListOf<String>()
val query = BaseApplication.context.contentResolver.query(
MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
arrayOf(MediaStore.Files.FileColumns.DATA),
null,
null,
"${MediaStore.Files.FileColumns.DATE_MODIFIED} DESC"
) ?: return@withContext
while (query.moveToNext()) {
typeFiles.add(query.getString(0))
}
query.close()
typeDateList.clear()
typeFiles.forEach {
val fileBean = File(it).toBean()
val dateString = dateFormat.format(fileBean.time)
if (typeDateList.lastOrNull()?.first != dateString) {
typeDateList.add(Pair(dateString, mutableListOf()))
}
typeDateList.lastOrNull()?.second?.add(fileBean)
}
callback()
}
}
}
}
\ No newline at end of file
package com.test.basd.smartjunkcleaner.helps
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.graphics.Color
import android.media.ThumbnailUtils
import com.blankj.utilcode.util.ScreenUtils
import com.test.basd.smartjunkcleaner.bean.ImageDataBean
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import kotlin.math.pow
object ImageHelper {
private val width by lazy {
ScreenUtils.getScreenWidth().coerceAtMost(1024)
}
private val height by lazy {
ScreenUtils.getScreenHeight().coerceAtMost(1280)
}
private fun loadBitmapFromFile(path: String): Bitmap? {
if (width <= 0 || height <= 0) {
return null
}
val options = BitmapFactory.Options()
options.inJustDecodeBounds = true
BitmapFactory.decodeFile(path, options)
if (options.outHeight == -1 || options.outWidth == -1) {
return null
}
var inSampleSize = (0.5f + options.outHeight.toFloat() / height.toFloat()).coerceAtLeast(0.5f + options.outWidth.toFloat() / width.toFloat()).toInt()
inSampleSize += 1
options.inSampleSize = inSampleSize.coerceAtLeast(1)
options.inJustDecodeBounds = false
return BitmapFactory.decodeFile(path, options)
}
suspend fun createImage(path: String): ImageDataBean? {
return withContext(Dispatchers.IO) {
try {
val width = 8
val height = 8
val source = loadBitmapFromFile(path)
val thumb = ThumbnailUtils.extractThumbnail(source, width, height)
val pixels = IntArray(width * height)
for (i in 0 until width) {
for (j in 0 until height) {
pixels[i * height + j] = rgbToGray(thumb.getPixel(i, j))
}
}
val avgPixel = average(pixels)
val comps = IntArray(width * height)
for (i in comps.indices) {
if (pixels[i] >= avgPixel) {
comps[i] = 1
} else {
comps[i] = 0
}
}
val hashCode = StringBuffer()
var i = 0
while (i < comps.size) {
val result = comps[i] * 2.0.pow(3.0).toInt() + (comps[i + 1] * 2.0.pow(2.0).toInt()) + (comps[i + 2] * 2.0.pow(1.0).toInt()) + comps[i + 3]
hashCode.append(binaryToHex(result))
i += 4
}
recycleBitmap(thumb)
recycleBitmap(source)
return@withContext ImageDataBean(path, hashCode.toString(), avgPixel)
} catch (_: Exception) {
return@withContext null
}
}
}
private fun rgbToGray(pixels: Int): Int {
val red = Color.red(pixels)
val green = Color.green(pixels)
val blue = Color.blue(pixels)
return (0.3 * red + 0.59 * green + 0.11 * blue).toInt()
}
private fun average(pixels: IntArray): Int {
return (pixels.sumOf { it }.toFloat() / pixels.size).toInt()
}
private fun recycleBitmap(thumb: Bitmap?) {
if (thumb?.isRecycled == false) {
thumb.recycle()
}
}
fun similarCondition(first: ImageDataBean, second: ImageDataBean): Boolean {
return hammingDistance(first.hashCode, second.hashCode) <= 6 && (first.avgPixel.toFloat() / second.avgPixel) in 0.8..1.2
}
private fun hammingDistance(sourceHashCode: String, hashCode: String): Int {
var difference = 0
for (i in sourceHashCode.indices) {
if (sourceHashCode[i] != hashCode[i]) {
difference++
}
}
return difference
}
private fun binaryToHex(binary: Int) = when (binary) {
0 -> '0'
1 -> '1'
2 -> '2'
3 -> '3'
4 -> '4'
5 -> '5'
6 -> '6'
7 -> '7'
8 -> '8'
9 -> '9'
10 -> 'a'
11 -> 'b'
12 -> 'c'
13 -> 'd'
14 -> 'e'
15 -> 'f'
else -> ' '
}
}
\ No newline at end of file
package com.test.basd.smartjunkcleaner.helps
import com.android.installreferrer.api.InstallReferrerClient
import com.android.installreferrer.api.InstallReferrerStateListener
import org.json.JSONObject
object InstallHelps {
fun init() {
val referrerClient = InstallReferrerClient.newBuilder(BaseApplication.context).build()
referrerClient.startConnection(object : InstallReferrerStateListener {
override fun onInstallReferrerSetupFinished(responseCode: Int) {
try {
when (responseCode) {
InstallReferrerClient.InstallReferrerResponse.OK -> {
val response = referrerClient.installReferrer
val installInfo = response.installReferrer
val obj = JSONObject()
obj.put("referrerUrl", response.installReferrer)
obj.put("referrerClickTime", response.referrerClickTimestampSeconds)
obj.put("appInstallTime", response.installBeginTimestampSeconds)
obj.put("instantExperienceLaunched", installInfo.toString())
EventHelper.event("install_referrer", ext = obj,isSingleEvent = true)
}
else -> {
EventHelper.event("install_referrer_error", isSingleEvent = true)
}
}
} catch (_: Exception) {
EventHelper.event("install_referrer_error", isSingleEvent = true)
}
}
override fun onInstallReferrerServiceDisconnected() {
}
})
}
}
\ No newline at end of file
package com.test.basd.smartjunkcleaner.helps
import android.view.View
import org.json.JSONObject
import java.text.SimpleDateFormat
import java.util.Locale
object KotlinExt {
private val aesMap = mutableMapOf<Int, String>()
fun Int.string(vararg arg: Any) = try {
(aesMap[this] ?: BaseApplication.context.getString(this).decode()).run {
aesMap[this@string] = this
String.format(this, *arg)
}
} catch (_: Exception) {
""
}
fun String.decode() = AESHelper.decrypt(this)
.replace("\\r", "\r")
.replace("\\n", "\n")
.replace("\\'", "'")
.replace("\\\"", "\"")
.replace("\\?", "?")
.replace("&amp;", "&")
fun Collection<View>.setOnClickListener(listener: (View) -> Unit) {
this.forEach {
it.setOnClickListener(listener)
}
}
fun View.setTrackedOnClickListener(action: (view: View) -> Unit) {
setOnClickListener {
action(this)
var view: View? = this
while (view != null) {
try {
val obj = JSONObject()
obj.put("view_id", resources.getResourceEntryName(view.id))
EventHelper.event("click_id", ext = obj)
break
} catch (_: Exception) {
view = view.parent as? View
}
}
}
}
fun Number.toFormatSize(count: Int = 1): String {
var suffix = "B"
var fSize = this.toDouble()
if (fSize > 1024) {
suffix = "KB"
fSize /= 1024.0
}
if (fSize > 1024) {
suffix = "MB"
fSize /= 1024.0
}
if (fSize > 1024) {
suffix = "GB"
fSize /= 1024.0
}
return String.format("%.${count}f %s", fSize, suffix)
}
fun Long.toFormatTime(): String {
return SimpleDateFormat("MMM/dd/yyyy", Locale.getDefault()).format(this)
}
}
\ No newline at end of file
package com.test.basd.smartjunkcleaner.helps
import android.util.Log
import com.test.basd.smartjunkcleaner.BuildConfig
object LogEx {
val isOpen = true
val filterTAG = arrayOf(
"ActionBroadcast",
// "NotificationTimerTask",
// "NotificationHelper",
// "NotificationEx",
"TimeUtils",
// "LaunchesFragment"
)
fun logDebug(tag: String, content: String, isMust: Boolean = false) {
if (isMust) {
Log.e(tag, content)
} else {
if (!isOpen) return
if (filterTAG.contains(tag)) return
if (BuildConfig.DEBUG) {
Log.e(tag, content)
}
}
}
}
\ No newline at end of file
package com.test.basd.smartjunkcleaner.helps
import android.net.TrafficStats
import android.os.SystemClock
import android.util.Log
object NetWorkSpeed {
var monitorSwitch = false
@Volatile
var preElapsedRealtime: Long = 0
@Volatile
var totalRxBytes: Long = 0
@Volatile
var totalTxBytes: Long = 0
@Volatile
var totalRxSpeed: Long = 0 //下载
@Volatile
var totalTxSpeed: Long = 0 //上传
// 开启网速监控
fun startMoritor() {
monitorSwitch = true
if (preElapsedRealtime == 0L) {
preElapsedRealtime = SystemClock.elapsedRealtime()
totalRxBytes = TrafficStats.getTotalRxBytes()
totalTxBytes = TrafficStats.getTotalTxBytes()
}
while (monitorSwitch) {
try {
val totalRxBytes = TrafficStats.getTotalRxBytes()
val totalTxBytes = TrafficStats.getTotalTxBytes()
// SystemClock.elapsedRealtime() 的计时起点是系统启动时间,即在开机后第一次调用该方法时返回0. 此处使用 SystemClock.elapsedRealtime() 来计算时间间隔
val elapsedRealtime = SystemClock.elapsedRealtime()
val j10 = (elapsedRealtime - preElapsedRealtime) / 1000
val totalTxSpeed = ((totalTxBytes - this.totalTxBytes) / j10)
val totalRxSpeed = ((totalRxBytes - this.totalRxBytes) / j10)
this.totalRxBytes = totalRxBytes
this.totalTxBytes = totalTxBytes
this.totalTxSpeed = totalTxSpeed
this.totalRxSpeed = totalRxSpeed
preElapsedRealtime = elapsedRealtime
Log.i("networkSpeedMonitor", "Tx: $totalTxSpeed, Rx: $totalRxSpeed")
} catch (e10: Exception) {
}
waitAMoment(4000L)
}
}
// 停止网速监控
fun stopMonitor() {
monitorSwitch = false
}
fun waitAMoment(j10: Long) {
try {
Thread.sleep(j10)
} catch (e10: InterruptedException) {
}
}
}
\ No newline at end of file
package com.test.basd.smartjunkcleaner.helps;
import android.annotation.SuppressLint;
import android.app.usage.NetworkStats;
import android.app.usage.NetworkStatsManager;
import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.net.ConnectivityManager;
import android.os.RemoteException;
import android.telephony.TelephonyManager;
import java.util.Calendar;
public class NetworkStatsHelper {
NetworkStatsManager networkStatsManager;
int packageUid;
public NetworkStatsHelper(NetworkStatsManager networkStatsManager) {
this.networkStatsManager = networkStatsManager;
}
/**
* 本机使用的 wifi 总流量
*/
public long getAllBytesWifi() {
NetworkStats.Bucket bucket;
try {
bucket = networkStatsManager.querySummaryForDevice(ConnectivityManager.TYPE_WIFI,
null,
getTimesMonthmorning(),
System.currentTimeMillis());
} catch (RemoteException e) {
return -1;
}
//这里可以区分发送和接收
return bucket.getTxBytes() + bucket.getRxBytes();
}
/**
* 本机使用的 mobile 总流量
*/
public long getAllBytesMobile(Context context) {
NetworkStats.Bucket bucket;
try {
bucket = networkStatsManager.querySummaryForDevice(ConnectivityManager.TYPE_MOBILE,
getSubscriberId(context, ConnectivityManager.TYPE_MOBILE),
0,
System.currentTimeMillis());
} catch (RemoteException e) {
return -1;
}
//这里可以区分发送和接收
return bucket.getTxBytes() + bucket.getRxBytes();
}
//这里要用到
@SuppressLint({"HardwareIds", "MissingPermission"})
private String getSubscriberId(Context context, int networkType) {
if (ConnectivityManager.TYPE_MOBILE == networkType) {
TelephonyManager tm = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
return tm.getSubscriberId();
}
return "";
}
/**
* 获取指定应用 wifi 发送的当天总流量
*
* @param packageUid 应用的uid
* @return
*/
public long getPackageTxDayBytesWifi(int packageUid) {
NetworkStats networkStats = null;
networkStats = networkStatsManager.queryDetailsForUid(
ConnectivityManager.TYPE_WIFI,
"",
getTimesmorning(),
System.currentTimeMillis(),
packageUid);
NetworkStats.Bucket bucket = new NetworkStats.Bucket();
networkStats.getNextBucket(bucket);
return bucket.getTxBytes();
}
/**
* 获取当天的零点时间
*
* @return
*/
public static long getTimesmorning() {
Calendar cal = Calendar.getInstance();
cal.set(Calendar.HOUR_OF_DAY, 0);
cal.set(Calendar.SECOND, 0);
cal.set(Calendar.MINUTE, 0);
cal.set(Calendar.MILLISECOND, 0);
return (cal.getTimeInMillis());
}
//获得本月第一天0点时间
public static long getTimesMonthmorning() {
Calendar cal = Calendar.getInstance();
cal.set(cal.get(Calendar.YEAR), cal.get(Calendar.MONDAY), cal.get(Calendar.DAY_OF_MONTH), 0, 0, 0);
cal.set(Calendar.DAY_OF_MONTH, cal.getActualMinimum(Calendar.DAY_OF_MONTH));
return (cal.getTimeInMillis());
}
public static Long getNeedTime(int i) {
Calendar calendar = Calendar.getInstance();
calendar.set(11, 0);
calendar.set(12, 0);
calendar.set(13, 0);
calendar.set(14, 0);
return calendar.getTimeInMillis() - ((i - 1) * 86400000L);
}
/**
* 根据包名获取uid
*
* @param context 上下文
* @param packageName 包名
*/
public static int getUidByPackageName(Context context, String packageName) {
int uid = -1;
PackageManager packageManager = context.getPackageManager();
try {
PackageInfo packageInfo = packageManager.getPackageInfo(packageName, PackageManager.GET_META_DATA);
uid = packageInfo.applicationInfo.uid;
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
}
return uid;
}
public static Boolean canStop(Context context, String packageName) {
boolean z11 = false;
if (packageName != null && !packageName.contains("com.android.vending") && !packageName.toLowerCase().contains("google") && !isSystemApp(context, packageName)) {
z11 = true;
} else {
z11 = false;
}
return z11;
}
public static boolean isSystemApp(Context context, String str) {
try {
if ((context.getApplicationContext().getPackageManager().getApplicationInfo(str, 0).flags & 129) == 0) {
return false;
}
return true;
} catch (PackageManager.NameNotFoundException unused) {
return false;
}
}
}
package com.test.basd.smartjunkcleaner.helps
import java.text.SimpleDateFormat
import java.util.Calendar
import java.util.TimeZone
object TimeUtils {
private val TAG = "TimeUtils"
val PAST_60_MINUS_QUERY = 5
val TODAY_QUERY = 15
val YESTERDAY_QUERY = 25
val SEVEN_DAYS_QUERY = 55
val THIRTY_DAYS_QUERY = 65
fun timePair(queryTime: Int): Pair<Long, Long> {
return when (queryTime) {
PAST_60_MINUS_QUERY -> recent60Minutes()
TODAY_QUERY -> todayTimestamps()
YESTERDAY_QUERY -> yesterdayTimestamps()
SEVEN_DAYS_QUERY -> sevenDaysTimestamps()
THIRTY_DAYS_QUERY -> thirtyDaysTimestamps()
else -> Pair(0, 0)
}
}
var simpleDateFormat = SimpleDateFormat("M-d-yyyy HH:mm:ss")
fun isDaysAgo(timestamp: Long, days: Int): Boolean {
// 获取当前时间戳
val currentTime = System.currentTimeMillis()
val daysAgo = currentTime - days * 24 * 60 * 60 * 1000L
val flag = timestamp < daysAgo
val f1 = simpleDateFormat.format(timestamp)
val f2 = simpleDateFormat.format(daysAgo)
LogEx.logDebug(TAG, "timestamp=$timestamp f1=$f1 daysAgo=$daysAgo f2=$f2 flag=$flag")
//几天前
return flag
}
//过去一小时
fun recent60Minutes(): Pair<Long, Long> {
// 获取当前时间的时间戳
val currentTimestamp = System.currentTimeMillis()
// 计算前60分钟的时间戳
val sixtyMinutesAgoTimestamp = currentTimestamp - (60 * 60 * 1000)
// 返回时间戳对
return Pair(sixtyMinutesAgoTimestamp, currentTimestamp)
}
//今天时间戳
fun todayTimestamps(): Pair<Long, Long> {
// 获取系统默认时区
val timeZone = TimeZone.getDefault()
// 创建 Calendar 实例,并设置时区
val calendar = Calendar.getInstance(timeZone)
// 设置 Calendar 为今天的00:00:00
calendar.set(Calendar.HOUR_OF_DAY, 0)
calendar.set(Calendar.MINUTE, 0)
calendar.set(Calendar.SECOND, 0)
calendar.set(Calendar.MILLISECOND, 0)
// 检查是否已经是新的一天,如果是,则设置为昨天的00:00:00,以便减去一天
if (calendar.before(Calendar.getInstance(timeZone))) {
calendar.add(Calendar.DAY_OF_YEAR, -1)
}
// 获取今天的起始时间戳
val startOfDayTimestamp = calendar.timeInMillis
// 获取当前时间的时间戳
val currentTimestamp = System.currentTimeMillis()
LogEx.logDebug(TAG, simpleDateFormat.format(startOfDayTimestamp))
LogEx.logDebug(TAG, simpleDateFormat.format(currentTimestamp))
// 返回时间戳对
return Pair(startOfDayTimestamp, currentTimestamp)
}
//昨天时间戳
fun yesterdayTimestamps(): Pair<Long, Long> {
// 获取系统默认时区
val timeZone = TimeZone.getDefault()
// 创建 Calendar 实例,并设置时区
val calendarStart = Calendar.getInstance(timeZone)
val calendarEnd = Calendar.getInstance(timeZone)
// 设置为昨天的日期
calendarStart.add(Calendar.DATE, -1)
calendarEnd.add(Calendar.DATE, -1)
// 设置起始时间为昨天的00:00:00
calendarStart[Calendar.HOUR_OF_DAY] = 0
calendarStart[Calendar.MINUTE] = 0
calendarStart[Calendar.SECOND] = 0
calendarStart[Calendar.MILLISECOND] = 0
// 设置结束时间为昨天的23:59:59
calendarEnd[Calendar.HOUR_OF_DAY] = 23
calendarEnd[Calendar.MINUTE] = 59
calendarEnd[Calendar.SECOND] = 59
// 获取昨天的起始和结束时间戳
val startTimestamp = calendarStart.getTimeInMillis()
val endTimestamp = calendarEnd.getTimeInMillis()
LogEx.logDebug(TAG, simpleDateFormat.format(startTimestamp))
LogEx.logDebug(TAG, simpleDateFormat.format(endTimestamp))
return Pair(startTimestamp, endTimestamp)
}
//过去7天时间戳
fun sevenDaysTimestamps(): Pair<Long, Long> {
// 获取系统默认时区
val timeZone = TimeZone.getDefault()
// 创建 Calendar 实例,并设置时区
val calendar = Calendar.getInstance(timeZone)
// 设置 Calendar 为当前时间,然后减去 7 天得到七天前的日期
calendar.timeInMillis = System.currentTimeMillis()
calendar.add(Calendar.DAY_OF_YEAR, -7)
// 设置七天前的起始时间戳为七天前的00:00:00
calendar.set(Calendar.HOUR_OF_DAY, 0)
calendar.set(Calendar.MINUTE, 0)
calendar.set(Calendar.SECOND, 0)
calendar.set(Calendar.MILLISECOND, 0)
// 当前的终止时间戳
val endTimestamp = System.currentTimeMillis()
LogEx.logDebug(TAG, simpleDateFormat.format(calendar.timeInMillis))
LogEx.logDebug(TAG, simpleDateFormat.format(endTimestamp))
// 返回时间戳对
return Pair(calendar.timeInMillis, endTimestamp)
}
fun thirtyDaysTimestamps(): Pair<Long, Long> {
// 获取当前时间的毫秒时间戳
val currentTimeMillis = System.currentTimeMillis()
// 创建 Calendar 实例以获取当前时间
val currentCalendar = Calendar.getInstance()
currentCalendar.timeInMillis = currentTimeMillis
// 将 Calendar 的小时、分钟、秒和毫秒设置为0,确保是当天的开始
currentCalendar.set(Calendar.HOUR_OF_DAY, 0)
currentCalendar.set(Calendar.MINUTE, 0)
currentCalendar.set(Calendar.SECOND, 0)
currentCalendar.set(Calendar.MILLISECOND, 0)
// 获取当前时间戳对应的00:00:00的时间戳
val startOfDayTimestamp = currentCalendar.timeInMillis
// 设置结束时间为当前时间戳减去30天的天数
val endCalendar = Calendar.getInstance()
endCalendar.add(Calendar.DAY_OF_MONTH, -30)
// 将结束时间的小时、分钟、秒和毫秒设置为0,确保是30天前的开始
endCalendar.set(Calendar.HOUR_OF_DAY, 0)
endCalendar.set(Calendar.MINUTE, 0)
endCalendar.set(Calendar.SECOND, 0)
endCalendar.set(Calendar.MILLISECOND, 0)
// 获取30天前00:00:00的时间戳
val thirtyDaysAgoTimestamp = endCalendar.timeInMillis
LogEx.logDebug(TAG, simpleDateFormat.format(thirtyDaysAgoTimestamp))
LogEx.logDebug(TAG, simpleDateFormat.format(currentTimeMillis))
return Pair(thirtyDaysAgoTimestamp, currentTimeMillis)
}
fun longToHoursAndMinutes(timestamp: Long): String {
// 将毫秒转换为秒
var totalSeconds: Double = timestamp / 1000.0
// 计算小时、分钟、秒
val hours = (totalSeconds / 3600).toLong().toFloat()
totalSeconds %= 3600.0
val minutes = (totalSeconds / 60).toLong().toFloat()
val seconds = (totalSeconds % 60).toFloat()
LogEx.logDebug(TAG, "hours=$hours minutes=$minutes seconds=$seconds")
// 根据最大的时间单位返回格式化的时间字符串
return if (hours > 0) {
// 如果有小时,则格式化为小时,保留一位小数
String.format("%.1fh", hours)
} else if (minutes > 0) {
// 如果没有小时但有分钟,则格式化为分钟,保留一位小数
String.format("%.1fm", minutes + seconds / 60.0)
} else {
// 如果既没有小时也没有分钟,则格式化为秒,保留一位小数
String.format("%.1fs", seconds)
}
}
}
\ No newline at end of file
package com.test.basd.smartjunkcleaner.helps.ads
import android.app.Activity
import android.app.Dialog
import android.os.Bundle
import android.util.Log
import android.view.ViewGroup
import androidx.core.view.isVisible
import com.blankj.utilcode.util.SPUtils
import com.google.android.gms.ads.AdError
import com.google.android.gms.ads.AdListener
import com.google.android.gms.ads.AdLoader
import com.google.android.gms.ads.AdRequest
import com.google.android.gms.ads.AdValue
import com.google.android.gms.ads.FullScreenContentCallback
import com.google.android.gms.ads.LoadAdError
import com.google.android.gms.ads.OnPaidEventListener
import com.google.android.gms.ads.ResponseInfo
import com.google.android.gms.ads.appopen.AppOpenAd
import com.google.android.gms.ads.interstitial.InterstitialAd
import com.google.android.gms.ads.interstitial.InterstitialAdLoadCallback
import com.google.android.gms.ads.nativead.NativeAd
import com.google.android.gms.ads.rewarded.RewardedAd
import com.google.firebase.analytics.FirebaseAnalytics
import com.google.firebase.analytics.ktx.analytics
import com.google.firebase.ktx.Firebase
import com.test.basd.smartjunkcleaner.R
import com.test.basd.smartjunkcleaner.activity.photocompress.photo.CustomDialog
import com.test.basd.smartjunkcleaner.helps.BaseApplication
import com.test.basd.smartjunkcleaner.helps.ConfigHelper
import com.test.basd.smartjunkcleaner.helps.EventHelper
import org.json.JSONObject
import java.util.Calendar
import java.util.UUID
object AdmobUtils {
private val mRequest = AdRequest.Builder().build()
private var openLoadTime = Long.MAX_VALUE
private var interLoadTime = Long.MAX_VALUE
private var nativeLoadTime = Long.MAX_VALUE
private var mOpenAd: AppOpenAd? = null
private const val typeShow = "Show"
var adShowCount = 80
get() {
return SPUtils.getInstance().getInt("adShowCount", field)
}
set(value) {
field = value
SPUtils.getInstance().put("adShowCount", value)
}
var nativeAdShowCount = 40
get() {
return SPUtils.getInstance().getInt("nativeAdShowCount", field)
}
set(value) {
field = value
SPUtils.getInstance().put("nativeAdShowCount", value)
}
var isNowPlayAd = 0
get() {
return SPUtils.getInstance().getInt("isNowPlayAd", field)
}
set(value) {
field = value
SPUtils.getInstance().put("isNowPlayAd", value)
}
fun isAdLimit(isNative: Boolean = false) =
getTodayCount(typeShow, isNative) % 1000 >= getAdShowCount(isNative)
private fun getAdShowCount(isNative: Boolean) = if (isNative) nativeAdShowCount else adShowCount
private fun getTodayCount(type: String, isNative: Boolean): Long {
val last = Calendar.getInstance().apply {
timeInMillis = SPUtils.getInstance()
.getLong("${if (isNative) "native" else "other"}AdToday${type}Count", 0)
}
return Calendar.getInstance().run {
if (get(Calendar.DAY_OF_YEAR) == last[Calendar.DAY_OF_YEAR]) {
last
} else {
set(Calendar.MILLISECOND, 0)
this
}
}.timeInMillis
}
private fun addTodayCount(type: String, isNative: Boolean) {
val todayCount = getTodayCount(type, isNative)
SPUtils.getInstance()
.put("${if (isNative) "native" else "other"}AdToday${type}Count", todayCount + 1)
}
var isBlack = false
get() {
return SPUtils.getInstance().getBoolean("isBlack", field)
}
set(value) {
field = value
SPUtils.getInstance().put("isBlack", value, true)
}
fun loadAppOpenAd(skip: Boolean = false, onLoad: (() -> Unit)? = null) {
if (isBlack) {
EventHelper.event("blacklist_filter")
onLoad?.invoke()
return
}
if (mOpenAd != null || skip) {
onLoad?.invoke()
return
}
val reqId = UUID.randomUUID().toString()
val obj = JSONObject()
obj.put("req_id", reqId)
obj.put("ad_type", "openAd")
EventHelper.event("ad_pull_start", ext = obj)
AppOpenAd.load(
BaseApplication.context,
ConfigHelper.openAdmobId,
mRequest,
object : AppOpenAd.AppOpenAdLoadCallback() {
override fun onAdLoaded(ad: AppOpenAd) {
openLoadTime = System.currentTimeMillis()
mOpenAd = ad
onLoad?.invoke()
pull(ad.responseInfo, "openAd", reqId = reqId)
ad.onPaidEventListener = EventOnPaidEventListener(ad)
// Log.e("MXL", "OpenonAdLoaded: ")
}
override fun onAdFailedToLoad(p0: LoadAdError) {
mOpenAd = null
onLoad?.invoke()
Log.d("glc", "开屏拉取失败:" + p0.message)
pull(p0.responseInfo, "openAd", p0.message, reqId = reqId)
//Log.e("MXL", "onAdFailedToLoad: " + p0.message)
//ToastUtils.showShort("开屏拉取失败" + p0.message)
}
})
}
fun isOpenAdLoaded() = mOpenAd != null
fun showAppOpenAd(activity: Activity, skip: Boolean = false, onHidden: (() -> Unit)? = null) {
if (isBlack) {
EventHelper.event("blacklist_filter")
onHidden?.invoke()
return
}
// if (!AdUtils.isShowAd("splash_slot")) {
// onHidden?.invoke()
// return
// }
if (activity.isFinishing || activity.isDestroyed) {
return
}
val obj = JSONObject()
obj.put("ad_unit", "openAd")
EventHelper.event("ad_prepare_show", ext = obj)
if (mOpenAd == null || skip) {
onHidden?.invoke()
return
}
if (System.currentTimeMillis() - openLoadTime > 1000 * 60 * 60) {
mOpenAd = null
loadAppOpenAd()
onHidden?.invoke()
val obj = JSONObject()
obj.put("ad_unit", "openAd")
EventHelper.event("ad_expire", ext = obj)
return
}
if (mOpenAd != null) {
var thisMOpenAd = mOpenAd
mOpenAd = null
thisMOpenAd?.fullScreenContentCallback = object : FullScreenContentCallback() {
override fun onAdClicked() {
click(thisMOpenAd?.responseInfo, "openAd")
onAdClick(thisMOpenAd)
}
override fun onAdDismissedFullScreenContent() {
mOpenAd = null
onHidden?.invoke()
loadAppOpenAd()
}
override fun onAdFailedToShowFullScreenContent(p0: AdError) {
mOpenAd = null
onHidden?.invoke()
loadAppOpenAd()
val obj = JSONObject()
obj.put("reason", p0.message)
obj.put("ad_unit", "openAd")
EventHelper.event("ad_show_error", ext = obj)
}
override fun onAdShowedFullScreenContent() {
show(thisMOpenAd?.responseInfo, "openAd", activity)
}
}
thisMOpenAd?.show(activity)
} else {
onHidden?.invoke()
loadAppOpenAd()
val obj = JSONObject()
obj.put("reason", "no_ad")
obj.put("ad_unit", "openAd")
EventHelper.event("ad_show_error", ext = obj)
}
}
private var nativeAd: NativeAd? = null
private var loadingListener: (() -> Unit)? = null
fun showNativeAd(activity: Activity?, parent: ViewGroup) {
if (isBlack) {
EventHelper.event("blacklist_filter")
return
}
// if (!AdUtils.isShowAd("native_slot")) {
// EventHelper.event("native_strategy_filter")
// return
// }
if (isAdLimit(true)) {
EventHelper.event("native_is_limit")
return
}
val obj = JSONObject()
obj.put("ad_unit", "NativeAd")
EventHelper.event("ad_prepare_show", ext = obj)
loadingListener = {
if (System.currentTimeMillis() - nativeLoadTime <= 1000 * 60 * 60) {
nativeAd?.let {
NativeView(parent.context).run {
parent.removeAllViews()
setNativeAd(it)
parent.addView(this)
parent.isVisible = true
show(nativeAd?.responseInfo, "nativeAd", activity)
}
}
}
nativeAd = null
loadingListener = null
loadNativeAd()
}
if (nativeAd == null) {
loadNativeAd()
val obj = JSONObject()
obj.put("reason", "no_ad")
obj.put("ad_unit", "nativeAd")
EventHelper.event("ad_show_error", ext = obj)
} else {
loadingListener?.invoke()
}
}
private var isLoading = false
fun loadNativeAd() {
if (isBlack) {
EventHelper.event("blacklist_filter")
return
}
if (isAdLimit()) {
EventHelper.event("native_limit")
return
}
if (nativeAd != null) {
return
}
if (isLoading) {
return
}
isLoading = true
val reqId = UUID.randomUUID().toString()
val obj = JSONObject()
obj.put("req_id", reqId)
obj.put("ad_type", "nativeAd")
EventHelper.event("ad_pull_start", ext = obj)
val adLoader = AdLoader.Builder(
BaseApplication.context, ConfigHelper.nativeAdmobId
).forNativeAd {
nativeLoadTime = System.currentTimeMillis()
nativeAd = it
isLoading = false
loadingListener?.invoke()
pull(it.responseInfo, "nativeAd", reqId = reqId)
it.setOnPaidEventListener(EventOnPaidEventListener(it))
}.withAdListener(object : AdListener() {
override fun onAdClicked() {
click(nativeAd?.responseInfo, "nativeAd")
onAdClick(nativeAd)
}
override fun onAdFailedToLoad(p0: LoadAdError) {
nativeAd = null
isLoading = false
pull(p0.responseInfo, "nativeAd", p0.message, reqId = reqId)
// Log.e("MXL", "NativeAdFailedToLoad: " + p0.message)
}
}).build()
adLoader.loadAd(mRequest)
}
private var interAd: InterstitialAd? = null
fun isInterLoaded() = interAd != null
fun loadInterstitialAd(activity: Activity, onLoad: (() -> Unit)? = null) {
if (isBlack) {
EventHelper.event("blacklist_filter")
onLoad?.invoke()
return
}
if (isAdLimit()) {
EventHelper.event("inter_load_limit")
onLoad?.invoke()
return
}
if (interAd != null) {
onLoad?.invoke()
return
}
val reqId = UUID.randomUUID().toString()
val obj = JSONObject()
obj.put("req_id", reqId)
obj.put("ad_type", "interAd")
obj.put("from", activity.javaClass.simpleName)
EventHelper.event("ad_pull_start", ext = obj)
InterstitialAd.load(
activity,
ConfigHelper.interAdmobId,
mRequest,
object : InterstitialAdLoadCallback() {
override fun onAdFailedToLoad(p0: LoadAdError) {
Log.d("glc", "广告拉取失败:" + p0.message)
interAd = null
onLoad?.invoke()
pull(p0.responseInfo, "interAd", p0.message, reqId = reqId)
// Log.e("MXL", "InterAdFailedToLoad: " + p0.message)
}
override fun onAdLoaded(ad: InterstitialAd) {
interAd = ad
onLoad?.invoke()
interLoadTime = System.currentTimeMillis()
pull(ad.responseInfo, "interAd", reqId = reqId)
ad.onPaidEventListener = EventOnPaidEventListener(ad)
// Log.e("MXL", "InteronAdLoaded: ")
}
})
}
fun showInterstitialAd(
activity: Activity,
skip: Boolean = false,
isASkip: Boolean = true,
isFirst: Boolean = true,
onHidden: (() -> Unit)? = null
) {
if (activity.isFinishing || activity.isDestroyed) {
return
}
if (isBlack) {
EventHelper.event("blacklist_filter")
onHidden?.invoke()
return
}
// if (!AdUtils.isShowAd("interstitial_slot")) {
// EventHelper.event("inter_strategy_filter")
// return
// }
if (isAdLimit()) {
EventHelper.event("inter_limit_filter")
onHidden?.invoke()
return
}
val obj = JSONObject()
obj.put("ad_unit", "interAd")
if(isASkip){
EventHelper.event("ad_prepare_show", ext = obj)
}
// if (!AdUtils.isShowAd("interstitial_slot")) {
// EventHelper.event("inter_strategy_filter")
// onHidden?.invoke()
// return
// }
if (skip) {
onHidden?.invoke()
return
}
if (System.currentTimeMillis() - interLoadTime > 1000 * 60 * 60) {
interAd = null
val obj = JSONObject()
obj.put("ad_unit", "interAd")
EventHelper.event("ad_expire", ext = obj)
loadInterstitialAd(activity)
onHidden?.invoke()
return
}
if (interAd != null) {
val thisInterAd = interAd
interAd = null
thisInterAd?.fullScreenContentCallback = object : FullScreenContentCallback() {
override fun onAdClicked() {
click(thisInterAd?.responseInfo, "interAd")
onAdClick(thisInterAd)
}
override fun onAdDismissedFullScreenContent() {
interAd = null
onHidden?.invoke()
loadInterstitialAd(activity)
}
override fun onAdFailedToShowFullScreenContent(p0: AdError) {
interAd = null
onHidden?.invoke()
loadInterstitialAd(activity)
val obj = JSONObject()
obj.put("reason", p0.message)
obj.put("ad_unit", "interAd")
EventHelper.event("ad_show_error", ext = obj)
}
override fun onAdShowedFullScreenContent() {
show(thisInterAd?.responseInfo, "interAd", activity)
}
}
thisInterAd?.show(activity)
} else {
var mDialog: Dialog? = null
mDialog = CustomDialog(activity, R.layout.dialog_ad_loading)
mDialog.show()
loadInterstitialAd(activity) {
if (isFirst) {
EventHelper.event("ad_reload_inter")
}
mDialog?.dismiss()
if (isASkip) {
showInterstitialAd(activity, false,false) {
onHidden?.invoke()
}
}
}
val obj = JSONObject()
obj.put("reason", "no_ad")
obj.put("ad_unit", "interAd")
EventHelper.event("ad_show_error", ext = obj)
if (!isASkip) {
mDialog?.dismiss()
onHidden?.invoke()
}
}
}
private fun pull(
responseInfo: ResponseInfo?,
adUnit: String,
error: String? = null,
reqId: String? = null
) {
val obj = JSONObject()
if (responseInfo != null) {
val response = responseInfo.adapterResponses.getOrNull(0)
if (response != null) {
obj.put("source", response.adSourceName)
val credentials = mapOf(
"placementid" to response.credentials.get("placementid"),
"appid" to response.credentials.get("appid"),
"pubid" to response.credentials.get("pubid")
)
obj.put("credentials", credentials.toString())
}
obj.put("session_id", responseInfo.responseId)
}
obj.put("networkname", responseInfo?.mediationAdapterClassName)
obj.put("ad_unit", adUnit)
obj.put("req_id", reqId)
if (error == null) {
obj.put("status", "1")
} else {
obj.put("errMsg", error)
obj.put("status", "2")
}
EventHelper.event("ad_pull", ext = obj)
}
private fun show(responseInfo: ResponseInfo?, adUnit: String, activity: Activity? = null) {
addTodayCount(typeShow, adUnit == "nativeAd")
val response = responseInfo?.adapterResponses?.getOrNull(0)
val obj = JSONObject()
obj.put("source", response?.adSourceName)
obj.put("ad_unit", adUnit)
obj.put("networkname", responseInfo?.mediationAdapterClassName)
val credentials = mapOf(
"placementid" to response?.credentials?.get("placementid"),
"appid" to response?.credentials?.get("appid"),
"pubid" to response?.credentials?.get("pubid")
)
obj.put("credentials", credentials.toString())
obj.put("session_id", responseInfo?.responseId)
obj.put("from", activity?.javaClass?.simpleName)
EventHelper.event("ad_show", ext = obj)
}
private var lastObj: Any? = null
private var curSingleAdClick = 0
private fun onAdClick(obj: Any?) {
if (lastObj == obj) {
curSingleAdClick++
} else {
lastObj = obj
curSingleAdClick = 1
}
}
private fun click(responseInfo: ResponseInfo?, adUnit: String) {
// addTodayCount(typeClick, adUnit == "nativeAd")
val response = responseInfo?.adapterResponses?.getOrNull(0)
val obj = JSONObject()
obj.put("source", response?.adSourceName)
obj.put("ad_unit", adUnit)
val credentials = mapOf(
"placementid" to response?.credentials?.get("placementid"),
"appid" to response?.credentials?.get("appid"),
"pubid" to response?.credentials?.get("pubid")
)
obj.put("credentials", credentials.toString())
obj.put("session_id", responseInfo?.responseId)
obj.put("networkname", responseInfo?.mediationAdapterClassName)
EventHelper.event("ad_click", ext = obj)
}
private val taichiPref by lazy {
BaseApplication.context.getSharedPreferences("TaichiTroasCache", 0)
}
private val taichiSharedPreferencesEditor by lazy {
taichiPref.edit()
}
private class EventOnPaidEventListener(private val ad: Any?) : OnPaidEventListener {
override fun onPaidEvent(adValue: AdValue) {
val valueMicros = adValue.valueMicros
val currencyCode = adValue.currencyCode
val precision = adValue.precisionType
val obj = JSONObject()
obj.put("valueMicros", valueMicros)
obj.put("currencyCode", currencyCode)
obj.put("precision", precision)
Firebase.analytics.logEvent("ad_price", Bundle().apply {
putDouble("valueMicros", valueMicros / 1000000.0)
})
val params = Bundle()
val currentImpressionRevenue = adValue.valueMicros.toDouble() / 1000000.0
params.putDouble(FirebaseAnalytics.Param.VALUE, currentImpressionRevenue)
params.putString(FirebaseAnalytics.Param.CURRENCY, "USD")
val precisionType = when (adValue.precisionType) {
0 -> "UNKNOWN"
1 -> "ESTIMATED"
2 -> "PUBLISHER_PROVIDED"
3 -> "PRECISE"
else -> "Invalid"
}
params.putString("precisionType", precisionType)
Firebase.analytics.logEvent("Ad_Impression_Revenue", params)
val previousTaichiTroasCache = taichiPref.getFloat("TaichiTroasCache", 0f)
val currentTaichiTroasCache = (previousTaichiTroasCache +
currentImpressionRevenue).toFloat()
if (currentTaichiTroasCache >= 0.01) {//如果超过0.01就触发一次tROAS taichi事件
val roasbundle = Bundle()
roasbundle.putDouble(
FirebaseAnalytics.Param.VALUE,
currentTaichiTroasCache.toDouble()
)
roasbundle.putString(FirebaseAnalytics.Param.CURRENCY, "USD")
Firebase.analytics.logEvent("Total_Ads_Revenue_001", roasbundle)
taichiSharedPreferencesEditor.putFloat("TaichiTroasCache", 0f)//重新清零,开始计算
} else {
taichiSharedPreferencesEditor.putFloat("TaichiTroasCache", currentTaichiTroasCache)
}
taichiSharedPreferencesEditor.commit()
when (ad) {
is AppOpenAd -> {
val adUnitId = ad.adUnitId
val loadedAdapterResponseInfo = ad.responseInfo.loadedAdapterResponseInfo
val adSourceName = loadedAdapterResponseInfo?.adSourceName
val adSourceId = loadedAdapterResponseInfo?.adSourceId
val adSourceInstanceName = loadedAdapterResponseInfo?.adSourceInstanceName
val adSourceInstanceId = loadedAdapterResponseInfo?.adSourceInstanceId
val sessionId = ad.responseInfo.responseId
val extras = ad.responseInfo.responseExtras
val mediationGroupName = extras.getString("mediation_group_name")
val mediationABTestName = extras.getString("mediation_ab_test_name")
val mediationABTestVariant = extras.getString("mediation_ab_test_variant")
obj.put("adUnitId", adUnitId)
obj.put("adSourceName", adSourceName)
obj.put("adSourceId", adSourceId)
obj.put("adSourceInstanceName", adSourceInstanceName)
obj.put("adSourceInstanceId", adSourceInstanceId)
obj.put("mediationGroupName", mediationGroupName)
obj.put("mediationABTestName", mediationABTestName)
obj.put("mediationABTestVariant", mediationABTestVariant)
obj.put("session_id", sessionId)
}
is InterstitialAd -> {
val adUnitId = ad.adUnitId
val loadedAdapterResponseInfo = ad.responseInfo.loadedAdapterResponseInfo
val adSourceName = loadedAdapterResponseInfo?.adSourceName
val adSourceId = loadedAdapterResponseInfo?.adSourceId
val adSourceInstanceName = loadedAdapterResponseInfo?.adSourceInstanceName
val adSourceInstanceId = loadedAdapterResponseInfo?.adSourceInstanceId
val sessionId = ad.responseInfo.responseId
val extras = ad.responseInfo.responseExtras
val mediationGroupName = extras.getString("mediation_group_name")
val mediationABTestName = extras.getString("mediation_ab_test_name")
val mediationABTestVariant = extras.getString("mediation_ab_test_variant")
obj.put("adUnitId", adUnitId)
obj.put("adSourceName", adSourceName)
obj.put("adSourceId", adSourceId)
obj.put("adSourceInstanceName", adSourceInstanceName)
obj.put("adSourceInstanceId", adSourceInstanceId)
obj.put("mediationGroupName", mediationGroupName)
obj.put("mediationABTestName", mediationABTestName)
obj.put("mediationABTestVariant", mediationABTestVariant)
obj.put("session_id", sessionId)
}
is RewardedAd -> {
val loadedAdapterResponseInfo = ad.responseInfo.loadedAdapterResponseInfo
val adSourceName = loadedAdapterResponseInfo?.adSourceName
val adSourceId = loadedAdapterResponseInfo?.adSourceId
val adSourceInstanceName = loadedAdapterResponseInfo?.adSourceInstanceName
val adSourceInstanceId = loadedAdapterResponseInfo?.adSourceInstanceId
val sessionId = ad.responseInfo.responseId
val extras = ad.responseInfo.responseExtras
val mediationGroupName = extras.getString("mediation_group_name")
val mediationABTestName = extras.getString("mediation_ab_test_name")
val mediationABTestVariant = extras.getString("mediation_ab_test_variant")
obj.put("adSourceName", adSourceName)
obj.put("adSourceId", adSourceId)
obj.put("adSourceInstanceName", adSourceInstanceName)
obj.put("adSourceInstanceId", adSourceInstanceId)
obj.put("mediationGroupName", mediationGroupName)
obj.put("mediationABTestName", mediationABTestName)
obj.put("mediationABTestVariant", mediationABTestVariant)
obj.put("session_id", sessionId)
}
is NativeAd -> {
val loadedAdapterResponseInfo = ad.responseInfo?.loadedAdapterResponseInfo
val adSourceName = loadedAdapterResponseInfo?.adSourceName
val adSourceId = loadedAdapterResponseInfo?.adSourceId
val adSourceInstanceName = loadedAdapterResponseInfo?.adSourceInstanceName
val adSourceInstanceId = loadedAdapterResponseInfo?.adSourceInstanceId
val sessionId = ad.responseInfo?.responseId
val extras = ad.responseInfo?.responseExtras
val mediationGroupName = extras?.getString("mediation_group_name")
val mediationABTestName = extras?.getString("mediation_ab_test_name")
val mediationABTestVariant = extras?.getString("mediation_ab_test_variant")
obj.put("adSourceName", adSourceName)
obj.put("adSourceId", adSourceId)
obj.put("adSourceInstanceName", adSourceInstanceName)
obj.put("adSourceInstanceId", adSourceInstanceId)
obj.put("mediationGroupName", mediationGroupName)
obj.put("mediationABTestName", mediationABTestName)
obj.put("mediationABTestVariant", mediationABTestVariant)
obj.put("session_id", sessionId)
}
}
EventHelper.event("ad_price", ext = obj)
}
}
}
package com.test.basd.smartjunkcleaner.helps.ads
import android.content.Context
import android.util.AttributeSet
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Button
import android.widget.FrameLayout
import android.widget.ImageView
import android.widget.TextView
import com.google.android.gms.ads.nativead.NativeAd
import com.google.android.gms.ads.nativead.NativeAdView
import com.test.basd.smartjunkcleaner.R
class NativeView @JvmOverloads constructor(
context: Context, attrs: AttributeSet? = null
) : FrameLayout(context, attrs) {
init {
layoutParams = LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT
)
}
fun setNativeAd(nativeAd: NativeAd?) {
nativeAd ?: return
val adView = LayoutInflater.from(context)
.inflate(R.layout.layout_native, this, false) as NativeAdView
adView.mediaView = adView.findViewById(R.id.ad_media)
adView.headlineView = adView.findViewById(R.id.ad_headline)
adView.bodyView = adView.findViewById(R.id.ad_body)
adView.callToActionView = adView.findViewById(R.id.ad_call_to_action)
adView.iconView = adView.findViewById(R.id.ad_app_icon)
(adView.headlineView as TextView?)?.text = nativeAd.headline
adView.mediaView!!.mediaContent = nativeAd.mediaContent
if (nativeAd.body == null) {
adView.bodyView!!.visibility = View.INVISIBLE
} else {
adView.bodyView!!.visibility = View.VISIBLE
(adView.bodyView as TextView?)?.text = nativeAd.body
}
if (nativeAd.callToAction == null) {
adView.callToActionView!!.visibility = View.INVISIBLE
} else {
adView.callToActionView!!.visibility = View.VISIBLE
(adView.callToActionView as Button?)?.text = nativeAd.callToAction
}
if (nativeAd.icon == null) {
adView.iconView!!.visibility = View.GONE
} else {
(adView.iconView as ImageView?)?.setImageDrawable(
nativeAd.icon!!.drawable
)
adView.iconView!!.visibility = View.VISIBLE
}
adView.setNativeAd(nativeAd)
removeAllViews()
addView(adView)
}
}
\ No newline at end of file
package com.test.basd.smartjunkcleaner.helps.recentapp
import android.app.usage.UsageEvents
import android.app.usage.UsageStatsManager
import android.content.Context
import java.util.LinkedList
class LaunchTimeStat(val beginTs: Long, val ends: Long) {
var a = 0
fun run(context: Context): HashMap<String, MutableList<UsageEvents.Event>> {
val usageStatsManager = context.getSystemService(Context.USAGE_STATS_SERVICE) as UsageStatsManager
val hashMap = HashMap<String, MutableList<UsageEvents.Event>>()
val linkedList = LinkedList<LinkedList<UsageEvents.Event>>()
val queryEvents = usageStatsManager.queryEvents(beginTs, ends)
var event: UsageEvents.Event? = null
while (queryEvents != null && queryEvents.hasNextEvent()) {
val event2 = UsageEvents.Event()
if (queryEvents.getNextEvent(event2) && event2.eventType != 23 && event2.eventType != 11) {
if (!linkedList.isEmpty() && linkedList.getLast().getLast().packageName == event2.packageName) {
linkedList.getLast().addLast(event2)
} else {
val linkedList2 = LinkedList<UsageEvents.Event>()
linkedList2.addLast(event2)
linkedList.addLast(linkedList2)
}
if ((event == null || event.packageName != event2.packageName) && (event == null || event.eventType == 2) && event2.eventType == 1) {
var list = hashMap[event2.packageName]
if (list == null) {
list = ArrayList()
}
list.add(event2)
hashMap[event2.packageName] = list
}
event = event2
}
}
return hashMap
}
companion object {
fun c(context: Context, str: String?): String? {
val packageManager = context.packageManager
return try {
packageManager.getApplicationLabel(packageManager.getApplicationInfo(str!!, 0)).toString()
} catch (e10: Exception) {
null
}
}
}
}
\ No newline at end of file
package com.test.basd.smartjunkcleaner.helps.recentapp
class RecentBean(val name: String, val packageName: String, var c: Long)
\ No newline at end of file
package com.test.basd.smartjunkcleaner.helps.recentapp
import android.app.usage.UsageEvents
import android.app.usage.UsageStatsManager
import android.content.Context
import java.util.LinkedList
class ScreenTimeStat(val beginTs: Long, val endTs: Long) {
private val TAG = "ScreenTimeStat"
fun run(context: Context): Map<String, RecentBean> {
var obj: UsageEvents.Event?
var obj2: UsageEvents.Event?
val usageStatsManager = context.getSystemService(Context.USAGE_STATS_SERVICE) as UsageStatsManager
val hashMap = HashMap<String, RecentBean>()
var j12: Long = 0
if (endTs - beginTs > 259200000) {
val queryUsageStats = usageStatsManager.queryUsageStats(0, beginTs, endTs)
if (queryUsageStats != null) {
for (usageStats in queryUsageStats) {
if (usageStats.totalTimeInForeground > j12 && usageStats.lastTimeUsed > beginTs) {
val bVar = hashMap[usageStats.packageName]
if (bVar == null) {
hashMap[usageStats.packageName] = RecentBean(
getAppName(context, usageStats.packageName),
usageStats.packageName, usageStats.totalTimeInForeground
)
} else {
bVar.c += usageStats.totalTimeInForeground
}
}
j12 = 0
}
}
} else {
val linkedList = LinkedList<LinkedList<UsageEvents.Event>>()
val queryEvents = usageStatsManager.queryEvents(beginTs, endTs)
while (queryEvents != null && queryEvents.hasNextEvent()) {
val event = UsageEvents.Event()
if (queryEvents.getNextEvent(event) && event.eventType != 23 && event.eventType != 11) {
if (!linkedList.isEmpty() && linkedList.getLast().getLast().packageName == event.packageName) {
linkedList.getLast().addLast(event)
} else {
val linkedList2 = LinkedList<UsageEvents.Event>()
linkedList2.addLast(event)
linkedList.addLast(linkedList2)
}
}
}
for (linkedList3 in linkedList) {
val packageName = linkedList3.getFirst().packageName
val it2: Iterator<UsageEvents.Event> = linkedList3.iterator()
while (true) {
obj = null
if (it2.hasNext()) {
obj2 = it2.next()
if (obj2.eventType == 1) {
break
}
} else {
obj2 = null
break
}
}
val event2 = obj2
val listIterator: ListIterator<UsageEvents.Event> = linkedList3.listIterator(linkedList3.size)
while (true) {
if (listIterator.hasPrevious()) {
val previous = listIterator.previous()
if (previous.eventType == 2) {
obj = previous
}
} else {
break
}
}
val event3 = obj
if (event2 != null && event3 != null && event2.timeStamp < event3.timeStamp) {
var bVar2 = hashMap[packageName]
if (bVar2 == null) {
bVar2 = RecentBean(getAppName(context, packageName), packageName, 0L)
}
bVar2.c += event3.timeStamp - event2.timeStamp
hashMap[packageName] = bVar2
}
}
}
// hashMap.forEach { (key, value) ->
// LogEx.logDebug(TAG, "key=$key value=$value")
// }
return hashMap
}
companion object {
fun getAppName(context: Context, str: String?): String {
val packageManager = context.packageManager
return try {
packageManager.getApplicationLabel(packageManager.getApplicationInfo(str!!, 0)).toString()
} catch (e10: Exception) {
""
}
}
}
}
\ No newline at end of file
package com.test.basd.smartjunkcleaner.notificationclean
import android.app.Notification
import android.service.notification.NotificationListenerService
import android.service.notification.StatusBarNotification
import android.util.Log
import com.test.basd.smartjunkcleaner.helps.BaseApplication
import com.test.basd.smartjunkcleaner.helps.ConfigHelper
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.MainScope
import kotlinx.coroutines.launch
class AppNotificationListenerService : NotificationListenerService() {
override fun onCreate() {
super.onCreate()
}
override fun onListenerConnected() {
super.onListenerConnected()
Log.e("MXL", "onListenerConnected: ")
ConfigHelper.isOpenNotification = true
}
override fun onNotificationPosted(sbn: StatusBarNotification) {
// 当通知被发布时调用
// 可以在这里获取通知的详细信息
val notification = sbn.notification
// 获取通知的标题和内容等
val extras = notification.extras
val title = extras.getCharSequence(Notification.EXTRA_TITLE)
val text = extras.getCharSequence(Notification.EXTRA_TEXT)
// 处理通知信息...
// Log.e("MXL", "onNotificationPosted: $extras")
// val data = NotificationData()
// data.packageName = sbn.packageName;
// data.notificationTitle = title.toString()
// data.notificationText = text.toString()
// db?.notificationDao()?.insert(data)
MainScope().launch(Dispatchers.IO) {
if (title?.isNotBlank() == true) {
HistoryDatabase.getInstance(BaseApplication.context)?.historyDao()?.insert(
NotificationData(
sbn.packageName,
title.toString(),
text.toString(),
System.currentTimeMillis()
)
)
}
}
//dbHelper.addContacts(NotifBean(null,null,"${title}","${text}",null,null))
}
override fun onNotificationRemoved(sbn: StatusBarNotification) {
// 当通知被移除时调用
}
}
\ No newline at end of file
package com.test.basd.smartjunkcleaner.notificationclean
import android.content.Context
import androidx.room.Database
import androidx.room.Room
import androidx.room.RoomDatabase
@Database(entities = [NotificationData::class], version = 1, exportSchema = false)
abstract class HistoryDatabase : RoomDatabase() {
abstract fun historyDao(): NotfiDao
companion object {
@Volatile
private var mInstance: HistoryDatabase? = null
private const val DATA_BASE_NAME = "notify_clean.db"
@JvmStatic
fun getInstance(context: Context): HistoryDatabase? {
if (mInstance == null) {
synchronized(HistoryDatabase::class.java) {
if (mInstance == null) {
mInstance = createInstance(context)
}
}
}
return mInstance
}
private fun createInstance(context: Context): HistoryDatabase {
return Room.databaseBuilder(
context.applicationContext,
HistoryDatabase::class.java,
DATA_BASE_NAME
).build()
}
}
}
\ No newline at end of file
package com.test.basd.smartjunkcleaner.notificationclean
import androidx.lifecycle.LiveData
import androidx.room.Dao
import androidx.room.Delete
import androidx.room.Insert
import androidx.room.Query
@Dao
interface NotfiDao {
@Query("SELECT * FROM Noticationt")
fun getAll(): LiveData<List<NotificationData>>
@Insert
fun insert(history: NotificationData)
@Delete
fun delete(it: NotificationData)
@Query("DELETE FROM Noticationt")
fun deleteAll()
}
\ No newline at end of file
package com.test.basd.smartjunkcleaner.notificationclean
import android.annotation.SuppressLint
import android.content.Intent
import android.graphics.Color
import android.provider.Settings
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.TextView
import androidx.activity.OnBackPressedCallback
import androidx.core.view.isVisible
import androidx.core.view.updatePadding
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.blankj.utilcode.util.BarUtils
import com.test.basd.smartjunkcleaner.R
import com.test.basd.smartjunkcleaner.databinding.ActivityLayoutTionCleanBinding
import com.test.basd.smartjunkcleaner.helps.BaseActivity
import com.test.basd.smartjunkcleaner.helps.ConfigHelper
import com.test.basd.smartjunkcleaner.helps.KotlinExt.toFormatTime
import com.test.basd.smartjunkcleaner.helps.ads.AdmobUtils
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.MainScope
import kotlinx.coroutines.launch
import kotlin.random.Random
class NotificationCleanActivity : BaseActivity<ActivityLayoutTionCleanBinding>() {
private val historyDao by lazy {
HistoryDatabase.getInstance(this)?.historyDao()
}
private val notificationList = mutableListOf<NotificationData>()
private var isRequest = 0
override val binding: ActivityLayoutTionCleanBinding by lazy {
ActivityLayoutTionCleanBinding.inflate(layoutInflater)
}
override fun initView() {
BarUtils.setStatusBarLightMode(this, true)
BarUtils.setStatusBarColor(this, Color.TRANSPARENT)
binding.root.updatePadding(top = BarUtils.getStatusBarHeight())
binding.idGant.setOnClickListener {
val intent = Intent(Settings.ACTION_NOTIFICATION_LISTENER_SETTINGS)
startActivity(intent)
}
binding.idBackNotification.setOnClickListener {
onBackPressedDispatcher.onBackPressed()
}
onBackPressedDispatcher.addCallback(object : OnBackPressedCallback(true) {
override fun handleOnBackPressed() {
AdmobUtils.showInterstitialAd(this@NotificationCleanActivity){
finishToMain()
}
}
})
binding.idTvCleanAllT.setOnClickListener {
AdmobUtils.showInterstitialAd(this) {
MainScope().launch(Dispatchers.IO) {
historyDao?.deleteAll()
}
mAdapter.notifyDataSetChanged()
showEmptydata()
}
}
historyDao?.getAll()?.observe(this) {
if (it.isNotEmpty()) {
binding.idNoNotification.isVisible = false
binding.idNotifyClean.isVisible = true
binding.idViewLine.isVisible = true
binding.idTvCleanAllT.isVisible = true
notificationList.clear()
notificationList.addAll(it)
binding.idNotifyClean.run {
layoutManager = LinearLayoutManager(this@NotificationCleanActivity)
adapter = mAdapter
mAdapter.notifyDataSetChanged()
}
} else {
showEmptydata()
}
}
}
override fun onStart() {
super.onStart()
val intent = Intent(this, AppNotificationListenerService::class.java)
startService(intent)
binding.idNeedNotifcationPremison.isVisible = !ConfigHelper.isOpenNotification
binding.idImgShezhi.isVisible = false
if (ConfigHelper.isOpenNotification) {
isRequest += 1
playAnimal()
}
}
private fun playAnimal() {
if (isRequest == 1) {
binding.idLlAnimation.isVisible = true
binding.root.postDelayed({
binding.idLlAnimation.isVisible = false
AdmobUtils.showInterstitialAd(this)
}, Random.nextLong(4000, 6000))
}
}
private fun showEmptydata() {
binding.idNotifyClean.isVisible = false
binding.idNoNotification.isVisible = true
binding.idViewLine.isVisible = false
binding.idTvCleanAllT.isVisible = false
}
private val mAdapter by lazy {
class ViewHolder(view: View) : RecyclerView.ViewHolder(view) {
val ivImage: ImageView
val tvTitle: TextView
val tvContent: TextView
val tvTime: TextView
val ivBig: ImageView
init {
ivImage = view.findViewById(R.id.id_t_icon)
tvTitle = view.findViewById(R.id.id_title)
tvContent = view.findViewById(R.id.id_content)
tvTime = view.findViewById(R.id.id_time)
ivBig = view.findViewById(R.id.id_big_img)
}
}
object : RecyclerView.Adapter<ViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val view = LayoutInflater.from(this@NotificationCleanActivity).inflate(
R.layout.item_tion_cleaner, parent, false
)
return ViewHolder(view)
}
override fun getItemCount() = notificationList.size
@SuppressLint("SetTextI18n")
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val c = ConfigHelper.appList?.firstOrNull {
it.packageName == notificationList[position].packageName
}
holder.ivImage.setImageDrawable(c?.icon)
if (notificationList[position].notificationText.isNotBlank()) {
holder.tvContent.visibility = View.VISIBLE
holder.tvContent.text = notificationList[position].notificationText
} else {
holder.tvContent.visibility = View.GONE
}
holder.tvTitle.text = notificationList[position].notificationTitle
holder.tvTime.text = notificationList[position].notificaonTime.toFormatTime()
holder.itemView.setOnClickListener {
MainScope().launch(Dispatchers.IO) {
historyDao?.delete(notificationList[position])
}
notifyDataSetChanged()
val intent =
packageManager.getLaunchIntentForPackage(notificationList[position].packageName)
if (intent != null) {
startActivity(intent)
}
}
}
override fun onBindViewHolder(
holder: ViewHolder,
position: Int,
payloads: MutableList<Any>
) {
super.onBindViewHolder(holder, position, payloads)
}
}
}
}
\ No newline at end of file
package com.test.basd.smartjunkcleaner.notificationclean
import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.PrimaryKey
@Entity(tableName = "Noticationt")
data class NotificationData(
@ColumnInfo
val packageName: String,
@ColumnInfo
var notificationTitle: String,
@ColumnInfo
val notificationText: String,
@ColumnInfo
val notificaonTime: Long,
@PrimaryKey(autoGenerate = true)
val id: Long = 0
)
\ No newline at end of file
package com.test.basd.smartjunkcleaner.notificationclean
import android.content.Intent
import android.graphics.Color
import androidx.core.view.updatePadding
import com.blankj.utilcode.util.BarUtils
import com.blankj.utilcode.util.SPUtils
import com.test.basd.smartjunkcleaner.databinding.ActivityLayoutTionGuestBinding
import com.test.basd.smartjunkcleaner.helps.BaseActivity
class NotificationGuestActivity : BaseActivity<ActivityLayoutTionGuestBinding>() {
override val binding: ActivityLayoutTionGuestBinding by lazy {
ActivityLayoutTionGuestBinding.inflate(layoutInflater)
}
override fun initView() {
BarUtils.setStatusBarLightMode(this, true)
BarUtils.setStatusBarColor(this, Color.TRANSPARENT)
binding.root.updatePadding(top = BarUtils.getStatusBarHeight())
binding.idTzGuest.imageAssetsFolder = "ying_smax_dao/images/"
binding.idTzGuest.setAnimation("ying_smax_dao/data.json")
binding.idTzGuest.playAnimation()
SPUtils.getInstance().put("notification_guest", true)
binding.idTvTryNow.setOnClickListener {
startActivity(Intent(this, NotificationCleanActivity::class.java))
finish()
}
binding.idTGuanbi.setOnClickListener {
finishToMain()
}
}
}
\ No newline at end of file
package com.test.basd.smartjunkcleaner.service
import android.app.Service
import android.content.Context
import android.content.Intent
import android.hardware.camera2.CameraAccessException
import android.hardware.camera2.CameraManager
import android.os.Build
import android.os.IBinder
import android.util.Log
class FlashlightService : Service() {
private var cameraManager: CameraManager? = null
private var flashlightOn = false
override fun onCreate() {
super.onCreate()
Log.e("MXL", "SerciveonCreate: ")
cameraManager = getSystemService(Context.CAMERA_SERVICE) as CameraManager?
}
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
try {
val cameraId = cameraManager!!.cameraIdList[0]
flashlightOn = if (flashlightOn) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
cameraManager!!.setTorchMode(cameraId, false)
}
false
} else {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
cameraManager!!.setTorchMode(cameraId, true)
}
true
}
} catch (e: CameraAccessException) {
e.printStackTrace()
}
return START_NOT_STICKY
}
override fun onBind(p0: Intent?): IBinder? {
return null
}
}
\ No newline at end of file
package com.test.basd.smartjunkcleaner.view
object AFunOb {
const val JUNK_CLEANER = "Junk Cleaner"//垃圾清理
const val RECENT_APP_USAGE = "Recent App Usage"//最近使用
const val LARGE_FILE_CLEANER = "Large File Cleaner"//大文件
const val NOTIFICATION_CLEANER = "Notification Cleaner"
const val NETWORK_TRAFFIC = "Network Traffic"
const val APP_MANAGER = "App Manager"
const val BATTERY_INFO = "Battery Info"
const val EMPTY_FILE_CLEANER = "Empty File Cleaner"
const val SIMILAR_PHOTOS = "Similar Photos"
const val SPEAK_CLEANER = "Speaker Cleaner"
const val PHOTO_COMPRESS = "Photo Compress"
const val APP_LOCK = "App Lock"
const val APP_SPEED = "App Speed"
const val BATTERY_OPTIMIZER = "Battery Optimizer"
}
\ No newline at end of file
package com.test.basd.smartjunkcleaner.view
import android.app.Activity
import android.view.Gravity
import android.view.ViewGroup
import android.widget.PopupWindow
import androidx.core.view.updatePadding
import com.blankj.utilcode.util.BarUtils
import com.test.basd.smartjunkcleaner.databinding.PopupLayoutAdPreparBinding
import kotlin.random.Random
class AdPreparationPop(private val activity: Activity, val onDismiss: () -> Unit) : PopupWindow() {
init {
width = ViewGroup.LayoutParams.MATCH_PARENT
height = ViewGroup.LayoutParams.MATCH_PARENT
isOutsideTouchable = false
isFocusable = true
isClippingEnabled = false
}
private val binding by lazy {
PopupLayoutAdPreparBinding.inflate(activity.layoutInflater)
}
fun show() {
contentView = binding.root
activity.window.decorView.post {
binding.root.updatePadding(top = BarUtils.getStatusBarHeight())
BarUtils.setStatusBarLightMode(activity, false)
showAtLocation(activity.window.decorView, Gravity.CENTER, 0, 0)
}
binding.root.postDelayed({
dismiss()
onDismiss.invoke()
}, Random.nextLong(2500, 4000))
}
override fun dismiss() {
super.dismiss()
BarUtils.setStatusBarLightMode(activity, true)
}
}
\ No newline at end of file
package com.test.basd.smartjunkcleaner.view
import android.annotation.SuppressLint
import android.app.Activity
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.net.Uri
import android.os.Build
import android.provider.Settings
import android.text.format.Formatter
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import com.blankj.utilcode.util.AppUtils
import com.google.android.material.bottomsheet.BottomSheetDialog
import com.test.basd.smartjunkcleaner.R
import com.test.basd.smartjunkcleaner.activity.PermissionManagerActivity
import com.test.basd.smartjunkcleaner.bean.AppBean
import com.test.basd.smartjunkcleaner.bean.AppBean.Companion.appBeanGson
import com.test.basd.smartjunkcleaner.databinding.DialogAppDetailBinding
import com.test.basd.smartjunkcleaner.helps.ActivityLauncher
import java.text.SimpleDateFormat
object AppDetailDialog {
private val TAG = "AppDetailDialog"
@SuppressLint("SimpleDateFormat")
fun Activity.showAppDetailDialog(
appBean: AppBean,
launcher: ActivityLauncher,
unInstallAction: ((unInstalled: Boolean) -> Unit)? = null
) {
val dialog = BottomSheetDialog(this)
val binding = DialogAppDetailBinding.inflate(LayoutInflater.from(this))
dialog.setContentView(binding.root)
dialog.setCanceledOnTouchOutside(true)
dialog.show()
binding.tvAppName.text = appBean.appName
binding.tvPkg.text = appBean.pkg
binding.ivIcon.setImageDrawable(appBean.icIcon)
binding.tvVersion.text = AppUtils.getAppVersionName(appBean.pkg)
val size = Formatter.formatFileSize(this, appBean.appSize)
binding.tvSize.text = size
val dateFormat = SimpleDateFormat("MM-dd")
val installTime = dateFormat.format(appBean.installTime)
binding.tvInstall.text = installTime
//应用来源逻辑
try {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
val installSourceInfo = packageManager.getInstallSourceInfo(appBean.pkg)
// binding.tvInstaller.text = installSourceInfo.installingPackageName
if ("com.android.vending" == installSourceInfo.installingPackageName) {
binding.tvInstaller.text = getString(R.string.google_play)
} else {
binding.llInstaller.visibility = View.GONE
}
} else {
binding.llInstaller.visibility = View.GONE
}
} catch (e: PackageManager.NameNotFoundException) {
e.printStackTrace()
}
//修改dialog的尺寸
val lp = dialog.window?.attributes
lp?.width = ViewGroup.LayoutParams.MATCH_PARENT
lp?.height = ViewGroup.LayoutParams.WRAP_CONTENT
dialog.window?.attributes = lp
dialog.window?.setBackgroundDrawableResource(android.R.color.transparent)
binding.tvDetail.setOnClickListener {
val intent = Intent()
intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS)
intent.setData(Uri.parse("package:" + appBean.pkg))
startActivity(intent)
dialog.dismiss()
}
binding.tvShare.setOnClickListener {
dialog.dismiss()
val uri = "https://play.google.com/store/apps/details?id=${appBean.pkg}&referrer=share"
//浏览器
val intentView = Intent(Intent.ACTION_VIEW)
intentView.setData(Uri.parse(uri))
// 检查是否有可以处理Intent的应用
if (intentView.resolveActivity(packageManager) != null) {
// 启动Intent,打开浏览器
startActivity(intentView)
} else {
//分享文本
val intent = Intent()
intent.setAction(Intent.ACTION_SEND)
intent.putExtra(Intent.EXTRA_TEXT, uri)
intent.setType("text/plain")
startActivity(intent)
}
}
binding.tvPermissionCheck.setOnClickListener {
dialog.dismiss()
val newIntent = Intent(this, PermissionManagerActivity::class.java)
val json = appBeanGson.toJson(appBean)
newIntent.putExtra("AppBean", json)
startActivity(newIntent)
}
binding.tvUninstall.setOnClickListener {
dialog.dismiss()
val intent = Intent(Intent.ACTION_DELETE, Uri.parse("package:${appBean.pkg}"))
launcher.launch(intent) {
unInstallAction?.invoke(!isInstalled(this, appBean.pkg))
}
}
}
private fun isInstalled(context: Context, pkg: String, error: (() -> Unit)? = null): Boolean {
return try {
context.packageManager.getPackageInfo(pkg, 0)
true
} catch (e: Exception) {
error?.invoke()
false
}
}
}
\ No newline at end of file
package com.test.basd.smartjunkcleaner.view
import android.annotation.SuppressLint
import android.content.Context
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.graphics.Canvas
import android.graphics.Paint
import android.util.AttributeSet
import android.view.View
import com.test.basd.smartjunkcleaner.R
class ArrowView : View {
private val arrowUpN: Bitmap = BitmapFactory.decodeResource(resources, R.mipmap.upper_n)
private val arrowUpS: Bitmap = BitmapFactory.decodeResource(resources, R.mipmap.l_upper)
private val arrowLoweN: Bitmap = BitmapFactory.decodeResource(resources, R.mipmap.lowe_n)
private val arrowLoweS: Bitmap = BitmapFactory.decodeResource(resources, R.mipmap.l_lower)
private val drawPaint = Paint(Paint.ANTI_ALIAS_FLAG or Paint.DITHER_FLAG)
private var upMargin = 0f
private var lowMargin = 0f
private var isAscOrder: Boolean = true
private var isOrder: Boolean = false
constructor(context: Context?) : super(context)
constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs) {}
constructor(context: Context?, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr)
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec)
upMargin = measuredHeight / 4f
lowMargin = measuredHeight / 2.3f
}
override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
super.onLayout(changed, left, top, right, bottom)
}
@SuppressLint("DrawAllocation")
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
drawOrderArrow(canvas)
}
fun drawOrderArrow(canvas: Canvas) {
if (!isOrder) {
canvas.drawBitmap(arrowUpN, 0f, upMargin, drawPaint)
canvas.drawBitmap(arrowLoweN, 0f, lowMargin, drawPaint)
} else {
if (isAscOrder) {
canvas.drawBitmap(arrowUpS, 0f, upMargin, drawPaint)
canvas.drawBitmap(arrowLoweN, 0f, lowMargin, drawPaint)
} else {
canvas.drawBitmap(arrowUpN, 0f, upMargin, drawPaint)
canvas.drawBitmap(arrowLoweS, 0f, lowMargin, drawPaint)
}
}
}
fun setAscOrder(isOrder: Boolean, isAscOrder: Boolean = false) {
this.isOrder = isOrder
this.isAscOrder = isAscOrder
invalidate()
}
}
\ No newline at end of file
package com.test.basd.smartjunkcleaner.view
import android.app.Dialog
import android.content.Context
import android.graphics.Color
import android.graphics.drawable.ColorDrawable
import android.view.LayoutInflater
import com.blankj.utilcode.util.ClickUtils
import com.blankj.utilcode.util.SpanUtils
import com.test.basd.smartjunkcleaner.R
import com.test.basd.smartjunkcleaner.databinding.DialogLayoutFlowTypeBinding
import com.test.basd.smartjunkcleaner.databinding.DialogPermissonOpenBinding
object DialogViews {
fun showGerPermission(context: Context, onClose: (view: Dialog) -> Unit, onClose2: (view: Dialog) -> Unit?):Dialog {
val dialog = Dialog(context)
val binding = DialogPermissonOpenBinding.inflate(LayoutInflater.from(context))
dialog.requestWindowFeature(1)
dialog.window!!.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT))
dialog.window!!.setLayout(-1, -1)
dialog.setCanceledOnTouchOutside(false)
dialog.setCancelable(false)
dialog.setContentView(binding.root)
val str=context.resources.getString(R.string.app_name)
SpanUtils.with(binding.idTvTt)
.append("Allow ${str} to access ")
.setFontSize(13, true)
.setForegroundColor(0xFF999999.toInt())
.append("All Files Access ")
.setBold()
.setFontSize(13, true)
.setForegroundColor(0xFF4273FF.toInt())
.append("permission to manage files of your device?")
.setFontSize(13, true)
.setForegroundColor(0xFF999999.toInt())
.create()
binding.idFullLottie.imageAssetsFolder = "quanxian_smax_full_file/images/"
binding.idFullLottie.setAnimation("quanxian_smax_full_file/data.json")
binding.idFullLottie.playAnimation()
binding.idDeny.setOnClickListener {
dialog.dismiss()
onClose2.invoke(dialog)
}
binding.idTvAllow.setOnClickListener {
dialog.dismiss()
onClose.invoke(dialog)
}
dialog.show()
return dialog
}
fun showBothTypes(context: Context, onClick: (potion: Int) -> Unit) {
val dialog = Dialog(context)
val binding = DialogLayoutFlowTypeBinding.inflate(LayoutInflater.from(context))
dialog.requestWindowFeature(1)
dialog.window!!.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT))
dialog.window!!.setLayout(-1, -1)
dialog.setCanceledOnTouchOutside(true)
dialog.setCancelable(true)
dialog.setContentView(binding.root)
ClickUtils.applyPressedBgDark(binding.idMobileNetwork)
ClickUtils.applyPressedBgDark(binding.idWifi)
ClickUtils.applyPressedBgDark(binding.idTwoTypes)
binding.idMobileNetwork.setOnClickListener {
onClick(0)
dialog.dismiss()
}
binding.idWifi.setOnClickListener {
onClick(1)
dialog.dismiss()
}
binding.idTwoTypes.setOnClickListener {
onClick(2)
dialog.dismiss()
}
dialog.show()
}
}
\ No newline at end of file
package com.test.basd.smartjunkcleaner.view
import android.content.Context
import android.text.SpannableString
import android.text.Spanned
import android.text.style.ForegroundColorSpan
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.appcompat.app.AlertDialog
import androidx.core.content.ContextCompat
import com.test.basd.smartjunkcleaner.R
import com.test.basd.smartjunkcleaner.databinding.DialogFileDeleteBinding
object FileDeleteDialog {
fun Context.showFileDeleteDialog(click: () -> Unit) {
val binding = DialogFileDeleteBinding.inflate(LayoutInflater.from(this))
val dialog = AlertDialog.Builder(this).setView(binding.root).create()
dialog.setContentView(binding.root)
dialog.setCanceledOnTouchOutside(true)
dialog.show()
//修改dialog的尺寸
val lp = dialog.window?.attributes
lp?.width = this.resources.getDimensionPixelOffset(R.dimen.dp_310)
lp?.height = ViewGroup.LayoutParams.WRAP_CONTENT
dialog.window?.attributes = lp
dialog.window?.setBackgroundDrawableResource(android.R.color.transparent)
val text = "Selected files cannot be recovered after deleting, continue anyway?"
val spannableString = SpannableString(text)
val start = text.indexOf("cannot")
if (start != -1) {
val end = start + "cannot be recovered".length
val colorSpan = ForegroundColorSpan(ContextCompat.getColor(this, R.color.color_f04949)) // 使用资源文件中的颜色
spannableString.setSpan(colorSpan, start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
}
binding.tvDesc.text = spannableString
binding.tvDelete.setOnClickListener {
dialog.dismiss()
click.invoke()
}
binding.tvCancel.setOnClickListener {
dialog.dismiss()
}
}
}
\ No newline at end of file
package com.test.basd.smartjunkcleaner.view
import android.annotation.SuppressLint
import android.content.Context
import android.text.format.Formatter
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.appcompat.app.AlertDialog
import com.test.basd.smartjunkcleaner.R
import com.test.basd.smartjunkcleaner.bean.FileBean
import com.test.basd.smartjunkcleaner.databinding.DialogFileDetailBinding
import java.text.SimpleDateFormat
import java.util.Date
object FileDetailDialog {
@SuppressLint("SetTextI18n")
fun Context.showFileDetailDialog(fileBean: FileBean) {
val binding = DialogFileDetailBinding.inflate(LayoutInflater.from(this))
val dialog = AlertDialog.Builder(this).setView(binding.root).create()
dialog.setContentView(binding.root)
dialog.setCanceledOnTouchOutside(true)
dialog.show()
//修改dialog的尺寸
val lp = dialog.window?.attributes
lp?.width = resources.getDimensionPixelOffset(R.dimen.dp_300)
lp?.height = ViewGroup.LayoutParams.WRAP_CONTENT
dialog.window?.attributes = lp
dialog.window?.setBackgroundDrawableResource(android.R.color.transparent)
binding.tvName.text = fileBean.name
val dateFormat = SimpleDateFormat("MMM dd, yyyy")
// , Locale.ENGLISH
val date = Date(fileBean.time)
// 格式化日期
val time = dateFormat.format(date)
binding.tvTime.text = "create time: $time"
val size = Formatter.formatFileSize(this, fileBean.size)
binding.tvSize.text = "size: $size"
binding.tvPath.text = "path: ${fileBean.path}"
}
}
\ No newline at end of file
package com.test.basd.smartjunkcleaner.view
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.graphics.Color
import android.media.ThumbnailUtils
import com.blankj.utilcode.util.ScreenUtils
import com.test.basd.smartjunkcleaner.bean.ImageDataBean
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import kotlin.math.pow
object ImageHelper {
private val width by lazy {
ScreenUtils.getScreenWidth().coerceAtMost(1024)
}
private val height by lazy {
ScreenUtils.getScreenHeight().coerceAtMost(1280)
}
private fun loadBitmapFromFile(path: String): Bitmap? {
if (width <= 0 || height <= 0) {
return null
}
val options = BitmapFactory.Options()
options.inJustDecodeBounds = true
BitmapFactory.decodeFile(path, options)
if (options.outHeight == -1 || options.outWidth == -1) {
return null
}
var inSampleSize = (0.5f + options.outHeight.toFloat() / height.toFloat()).coerceAtLeast(0.5f + options.outWidth.toFloat() / width.toFloat()).toInt()
inSampleSize += 1
options.inSampleSize = inSampleSize.coerceAtLeast(1)
options.inJustDecodeBounds = false
return BitmapFactory.decodeFile(path, options)
}
suspend fun createImage(path: String): ImageDataBean? {
return withContext(Dispatchers.IO) {
try {
val width = 8
val height = 8
val source = loadBitmapFromFile(path)
val thumb = ThumbnailUtils.extractThumbnail(source, width, height)
val pixels = IntArray(width * height)
for (i in 0 until width) {
for (j in 0 until height) {
pixels[i * height + j] = rgbToGray(thumb.getPixel(i, j))
}
}
val avgPixel = average(pixels)
val comps = IntArray(width * height)
for (i in comps.indices) {
if (pixels[i] >= avgPixel) {
comps[i] = 1
} else {
comps[i] = 0
}
}
val hashCode = StringBuffer()
var i = 0
while (i < comps.size) {
val result = comps[i] * 2.0.pow(3.0).toInt() + (comps[i + 1] * 2.0.pow(2.0).toInt()) + (comps[i + 2] * 2.0.pow(1.0).toInt()) + comps[i + 3]
hashCode.append(binaryToHex(result))
i += 4
}
recycleBitmap(thumb)
recycleBitmap(source)
return@withContext ImageDataBean(path, hashCode.toString(), avgPixel)
} catch (_: Exception) {
return@withContext null
}
}
}
private fun rgbToGray(pixels: Int): Int {
val red = Color.red(pixels)
val green = Color.green(pixels)
val blue = Color.blue(pixels)
return (0.3 * red + 0.59 * green + 0.11 * blue).toInt()
}
private fun average(pixels: IntArray): Int {
return (pixels.sumOf { it }.toFloat() / pixels.size).toInt()
}
private fun recycleBitmap(thumb: Bitmap?) {
if (thumb?.isRecycled == false) {
thumb.recycle()
}
}
fun similarCondition(first: ImageDataBean, second: ImageDataBean): Boolean {
return hammingDistance(first.hashCode, second.hashCode) <= 6 && (first.avgPixel.toFloat() / second.avgPixel) in 0.8..1.2
}
private fun hammingDistance(sourceHashCode: String, hashCode: String): Int {
var difference = 0
for (i in sourceHashCode.indices) {
if (sourceHashCode[i] != hashCode[i]) {
difference++
}
}
return difference
}
private fun binaryToHex(binary: Int) = when (binary) {
0 -> '0'
1 -> '1'
2 -> '2'
3 -> '3'
4 -> '4'
5 -> '5'
6 -> '6'
7 -> '7'
8 -> '8'
9 -> '9'
10 -> 'a'
11 -> 'b'
12 -> 'c'
13 -> 'd'
14 -> 'e'
15 -> 'f'
else -> ' '
}
}
\ No newline at end of file
package com.test.basd.smartjunkcleaner.view
import android.view.Gravity
import android.view.ViewGroup
import android.widget.PopupWindow
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.updatePadding
import com.blankj.utilcode.util.BarUtils
import com.test.basd.smartjunkcleaner.databinding.DialogBottomLayoutFileBinding
class PermisonPopWindow(private val activity: AppCompatActivity, val onClick: () -> Unit) :
PopupWindow() {
init {
width = ViewGroup.LayoutParams.MATCH_PARENT
height = ViewGroup.LayoutParams.MATCH_PARENT
isOutsideTouchable = false
isFocusable = true
isClippingEnabled = false
}
private val binding by lazy {
DialogBottomLayoutFileBinding.inflate(activity.layoutInflater)
}
fun show() {
contentView = binding.root
activity.window.decorView.post {
binding.root.updatePadding(top = BarUtils.getStatusBarHeight())
BarUtils.setStatusBarLightMode(activity, false)
showAtLocation(activity.window.decorView, Gravity.BOTTOM, 0, 100)
}
binding.idLottieFullFile.imageAssetsFolder = "quanxian_smax_guide/images/"
binding.idLottieFullFile.setAnimation("quanxian_smax_guide/data.json")
binding.idLottieFullFile.playAnimation()
binding.idFrameLayout.setOnClickListener {
dismiss()
onClick.invoke()
}
binding.idLlContent.setOnClickListener {
dismiss()
onClick.invoke()
}
}
override fun dismiss() {
super.dismiss()
BarUtils.setStatusBarLightMode(activity, true)
}
}
\ No newline at end of file
package com.test.basd.smartjunkcleaner.view
import android.content.Context
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.appcompat.app.AlertDialog
import com.test.basd.smartjunkcleaner.R
import com.test.basd.smartjunkcleaner.databinding.DialogPermissionFilterBinding
object PermissionFilterDialog {
fun Context.showPermissionFilterDialog(
allClick: () -> Unit,
authorizedClick: () -> Unit,
) {
val binding = DialogPermissionFilterBinding.inflate(LayoutInflater.from(this))
val dialog = AlertDialog.Builder(this).setView(binding.root).create()
dialog.setContentView(binding.root)
dialog.setCanceledOnTouchOutside(true)
dialog.show()
//修改dialog的尺寸
val lp = dialog.window?.attributes
lp?.width = resources.getDimensionPixelOffset(R.dimen.dp_360)
lp?.height = ViewGroup.LayoutParams.WRAP_CONTENT
dialog.window?.attributes = lp
dialog.window?.setBackgroundDrawableResource(android.R.color.transparent)
binding.tvAll.setOnClickListener {
dialog.dismiss()
allClick.invoke()
}
binding.tvAuthorized.setOnClickListener {
dialog.dismiss()
authorizedClick.invoke()
}
}
}
\ No newline at end of file
package com.test.basd.smartjunkcleaner.view
import android.annotation.SuppressLint
import android.app.Activity
import android.content.Intent
import android.net.Uri
import android.view.Gravity
import android.view.ViewGroup
import android.widget.PopupWindow
import androidx.core.view.updatePadding
import com.blankj.utilcode.util.BarUtils
import com.blankj.utilcode.util.SPUtils
import com.test.basd.smartjunkcleaner.R
import com.test.basd.smartjunkcleaner.databinding.PopLayoutStarBinding
class RateStarPop(private val activity: Activity) : PopupWindow() {
companion object {
fun show(activity: Activity) {
val n = SPUtils.getInstance().getInt("RateNums", 0)
SPUtils.getInstance().put("RateNums", n + 1)
if (SPUtils.getInstance().getBoolean("isRated", false)) {
return
}
if (n > 0 && n % 8 == 0) {
RateStarPop(activity).show()
} else {
return
}
}
}
init {
width = ViewGroup.LayoutParams.MATCH_PARENT
height = ViewGroup.LayoutParams.MATCH_PARENT
isOutsideTouchable = false
isFocusable = true
isClippingEnabled = false
}
private val binding by lazy {
PopLayoutStarBinding.inflate(activity.layoutInflater)
}
@SuppressLint("ClickableViewAccessibility")
fun show() {
contentView = binding.root
activity.window.decorView.post {
binding.root.updatePadding(top = BarUtils.getStatusBarHeight())
BarUtils.setStatusBarLightMode(activity, false)
showAtLocation(activity.window.decorView, Gravity.CENTER, 0, 0)
}
listOf(
binding.idStar1,
binding.idStar2,
binding.idStar3,
binding.idStar4,
binding.idStar5
).forEachIndexed { index, it ->
it.setOnClickListener {
setStar(index)
}
}
binding.idClose.setOnClickListener {
dismiss()
}
binding.idSubmit.setOnClickListener {
if (star > 0) {
if (star >= 5) {
SPUtils.getInstance().put("isRated", true)
try {
val uri =
Uri.parse("https://play.google.com/store/apps/details?id=" + activity.packageName)
val intent = Intent(Intent.ACTION_VIEW, uri)
activity.startActivity(intent)
} catch (_: Exception) {
}
}
dismiss()
}
}
}
private var star = 5
private fun setStar(star: Int) {
this.star = star
listOf(
binding.idStar1,
binding.idStar2,
binding.idStar3,
binding.idStar4,
binding.idStar5
).forEachIndexed { index, it ->
it.setImageResource(
if (index <= star) {
R.mipmap.xing_s2
} else {
R.mipmap.xing_n2
}
)
}
}
override fun dismiss() {
super.dismiss()
BarUtils.setStatusBarLightMode(activity, true)
}
}
\ No newline at end of file
package com.test.basd.smartjunkcleaner.view
import android.annotation.SuppressLint
import android.content.Context
import android.view.LayoutInflater
import android.view.ViewGroup
import com.google.android.material.bottomsheet.BottomSheetDialog
import com.test.basd.smartjunkcleaner.databinding.DialogTimeSelectBinding
import com.test.basd.smartjunkcleaner.helps.TimeUtils.PAST_60_MINUS_QUERY
import com.test.basd.smartjunkcleaner.helps.TimeUtils.SEVEN_DAYS_QUERY
import com.test.basd.smartjunkcleaner.helps.TimeUtils.TODAY_QUERY
import com.test.basd.smartjunkcleaner.helps.TimeUtils.YESTERDAY_QUERY
import java.text.SimpleDateFormat
import kotlin.time.Duration.Companion.days
import kotlin.time.DurationUnit
object TimeSelectDialog {
@SuppressLint("SimpleDateFormat", "SetTextI18n")
fun Context.showTimeSelectDialog(click: (mode: Int) -> Unit) {
val dialog = BottomSheetDialog(this)
val binding = DialogTimeSelectBinding.inflate(LayoutInflater.from(this))
dialog.setContentView(binding.root)
dialog.setCanceledOnTouchOutside(true)
dialog.show()
//修改dialog的尺寸
val lp = dialog.window?.attributes
lp?.width = ViewGroup.LayoutParams.MATCH_PARENT
lp?.height = ViewGroup.LayoutParams.WRAP_CONTENT
dialog.window?.attributes = lp
dialog.window?.setBackgroundDrawableResource(android.R.color.transparent)
val simpleDateFormat2 = SimpleDateFormat("(yyyy/MM/dd)")
binding.tvToday.text = "Today " + simpleDateFormat2.format(System.currentTimeMillis())
binding.tvYesterday.text =
"Yesterday " + simpleDateFormat2.format(System.currentTimeMillis() - 1.days.toLong(DurationUnit.MILLISECONDS))
binding.tv60.setOnClickListener {
dialog.dismiss()
click.invoke(PAST_60_MINUS_QUERY)
}
binding.tvToday.setOnClickListener {
dialog.dismiss()
click.invoke(TODAY_QUERY)
}
binding.tvYesterday.setOnClickListener {
dialog.dismiss()
click.invoke(YESTERDAY_QUERY)
}
binding.tv7Days.setOnClickListener {
dialog.dismiss()
click.invoke(SEVEN_DAYS_QUERY)
}
}
}
\ No newline at end of file
package com.test.basd.smartjunkcleaner.view
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
object XmlEx {
fun Int.inflate(parent: ViewGroup, attachToRoot: Boolean = false): View {
return LayoutInflater.from(parent.context).inflate(this, parent, attachToRoot)
}
}
\ 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