Commit 8d7b81bf authored by guest's avatar guest

混淆代码

parent 17b519ab
...@@ -10,7 +10,7 @@ android { ...@@ -10,7 +10,7 @@ android {
compileSdk 34 compileSdk 34
defaultConfig { defaultConfig {
applicationId "confine.scream" applicationId "com.cleaner.recovery.tencgog"
minSdk 24 minSdk 24
targetSdk 34 targetSdk 34
versionCode 1 versionCode 1
......
{ {
"project_info": { "project_info": {
"project_number": "135677224109", "project_number": "107894849092",
"project_id": "scream-1bc70", "project_id": "clean-junk-recovery-privacy",
"storage_bucket": "scream-1bc70.firebasestorage.app" "storage_bucket": "clean-junk-recovery-privacy.firebasestorage.app"
}, },
"client": [ "client": [
{ {
"client_info": { "client_info": {
"mobilesdk_app_id": "1:135677224109:android:5e3fd3c8cfce99d40a1224", "mobilesdk_app_id": "1:107894849092:android:09196dc899c9edd61dc611",
"android_client_info": { "android_client_info": {
"package_name": "confine.scream" "package_name": "com.cleaner.recovery.tencgog"
} }
}, },
"oauth_client": [], "oauth_client": [],
"api_key": [ "api_key": [
{ {
"current_key": "AIzaSyBIUMrPv6OOQWBheBAylXU94bkwvZgBFsg" "current_key": "AIzaSyDlzkN29SqWCYkUj1sOr9HFB27Yw5MpLNM"
} }
], ],
"services": { "services": {
......
...@@ -303,7 +303,10 @@ ...@@ -303,7 +303,10 @@
"idStar3": "potteryedto", "idStar3": "potteryedto",
"idStar4": "copeedto", "idStar4": "copeedto",
"idStar5": "favouredto", "idStar5": "favouredto",
"tvSubmit": "subjectedto" "tvSubmit": "subjectedto",
"idTvWhere": "amputateedto",
"idToken": "broughtedto",
"idCopy": "ventureedto"
}, },
"layout": { "layout": {
"item_screenshot": "curledto", "item_screenshot": "curledto",
...@@ -799,6 +802,9 @@ ...@@ -799,6 +802,9 @@
"CleanJunkActivity": "Amberedto", "CleanJunkActivity": "Amberedto",
"MessagingService": "Cooperateedto", "MessagingService": "Cooperateedto",
"CloseNotificationReceiver": "Busyedto", "CloseNotificationReceiver": "Busyedto",
"JobReceiver": "Arrogantedto",
"AlterReceiver": "Braidedto",
"AlterReceiver2": "Leftedto",
"WorkHelp": "Merchantedto", "WorkHelp": "Merchantedto",
"NotificationUtil": "Appleedto", "NotificationUtil": "Appleedto",
"RecoveryTimerManager": "Scrapeedto", "RecoveryTimerManager": "Scrapeedto",
......
This diff is collapsed.
package com.base.datarecovery.activity
import android.Manifest
import android.app.NotificationManager
import android.content.Intent
import android.graphics.Color
import android.graphics.Typeface
import android.os.Build
import android.provider.Settings
import android.view.View
import androidx.activity.addCallback
import androidx.activity.result.contract.ActivityResultContracts
import androidx.core.view.updatePadding
import androidx.fragment.app.Fragment
import androidx.lifecycle.lifecycleScope
import androidx.viewpager2.adapter.FragmentStateAdapter
import androidx.viewpager2.widget.ViewPager2
import com.base.datarecovery.ads.admob.AdmobBannerUtils
import com.base.datarecovery.databinding.ActivityMainBinding
import com.base.datarecovery.fragment.HomeFragment
import com.base.datarecovery.fragment.RecoveryFragment
import com.base.datarecovery.help.BaseActivity
import com.base.datarecovery.help.PermissionHelp.checkStorePermission
import com.base.datarecovery.utils.AppPreferences
import com.base.datarecovery.utils.BarUtils
import com.base.datarecovery.view.DialogViews.showExitDialog
import com.base.datarecovery.view.NotifPermisonPop
import com.base.datarecovery.view.PermissionDialog.showPermissionBottomSheet
import com.base.datarecovery.view.RateStarPop.showRateStarPopDialog
import com.facebook.appevents.AppEventsLogger
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
class MainActivity : BaseActivity<ActivityMainBinding>() {
private val requestPermission =
registerForActivityResult(ActivityResultContracts.RequestPermission()) {
if (!it) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val intent = Intent()
intent.action = Settings.ACTION_APP_NOTIFICATION_SETTINGS
intent.putExtra(Settings.EXTRA_APP_PACKAGE, this.packageName)
startActivity(intent)
}
}
}
override val binding: ActivityMainBinding by lazy {
ActivityMainBinding.inflate(layoutInflater)
}
private val homeFragment by lazy {
HomeFragment()
}
private val recoveryFragment by lazy {
RecoveryFragment()
}
private val fragments by lazy {
mutableListOf(homeFragment, recoveryFragment)
}
override fun initView() {
BarUtils.setStatusBarLightMode(this, true)
BarUtils.setStatusBarColor(this, Color.TRANSPARENT)
binding.root.updatePadding(top = BarUtils.getStatusBarHeight())
if (!checkStorePermission()) {
if (dialog == null) {
dialog = showPermissionBottomSheet(launcher) {
binding.flBanner.visibility = View.VISIBLE
AdmobBannerUtils.showCollapsibleBannerAd(this, binding.flBanner)
}
}
} else {
binding.flBanner.visibility = View.VISIBLE
AdmobBannerUtils.showCollapsibleBannerAd(this, binding.flBanner)
}
binding.viewpager2.run {
adapter = object : FragmentStateAdapter(this@MainActivity) {
override fun getItemCount(): Int {
return fragments.size
}
override fun createFragment(position: Int): Fragment {
return fragments[position]
}
}
}
binding.viewpager2.registerOnPageChangeCallback(object :
ViewPager2.OnPageChangeCallback() {
override fun onPageSelected(position: Int) {
setPageIndicate(position)
}
})
val fbhome = AppPreferences.getInstance().getInt("fb_home", 0)
if (fbhome == 0) {
val logger = AppEventsLogger.newLogger(this)
logger.logEvent("home")
AppPreferences.getInstance().put("fb_home", 1)
}
if (!requestNotifPermisson()) {
NotifPermisonPop(this) {
if (Build.VERSION.SDK_INT >= 33) {
requestPermission.launch(Manifest.permission.POST_NOTIFICATIONS)
} else {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val intent = Intent()
intent.action = Settings.ACTION_APP_NOTIFICATION_SETTINGS
intent.putExtra(Settings.EXTRA_APP_PACKAGE, this.packageName)
startActivity(intent)
}
}
}.show()
}
}
private fun setPageIndicate(p: Int) {
binding.ll1.isSelected = false
binding.ll2.isSelected = false
binding.tv1.typeface = Typeface.DEFAULT
binding.tv2.typeface = Typeface.DEFAULT
when (p) {
0 -> {
binding.ll1.isSelected = true
binding.tv1.typeface = Typeface.DEFAULT_BOLD
}
1 -> {
binding.ll2.isSelected = true
binding.tv2.typeface = Typeface.DEFAULT_BOLD
}
}
}
override fun initListener() {
super.initListener()
onBackPressedDispatcher.addCallback {
showExitDialog()
}
binding.ll1.setOnClickListener {
// BarUtils.setStatusBarColor(this, Color.parseColor("#F4F5F9"))
binding.viewpager2.currentItem = 0
}
binding.ll2.setOnClickListener {
// BarUtils.setStatusBarColor(this, Color.parseColor("#F4F5F9"))
binding.viewpager2.currentItem = 1
}
}
override fun onResume() {
super.onResume()
lifecycleScope.launch(Dispatchers.Main) {
delay(1000)
showRateStarPopDialog()
}
}
private fun requestNotifPermisson(): Boolean {
val mNotificationManager =
this.getSystemService(NOTIFICATION_SERVICE) as NotificationManager
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
return mNotificationManager.importance != NotificationManager.IMPORTANCE_NONE
} else {
return true
}
}
}
\ No newline at end of file
package com.base.datarecovery.activity
import android.annotation.SuppressLint
import android.content.Intent
import android.graphics.Color
import androidx.activity.OnBackPressedCallback
import androidx.core.view.updatePadding
import com.base.datarecovery.R
import com.base.datarecovery.activity.appmanager.AppManagerAnimationActivity
import com.base.datarecovery.activity.appprocess.AppProcessAnimationActivity
import com.base.datarecovery.activity.battery.BatteryInfoAnimationActivity
import com.base.datarecovery.activity.junkclean.ScanJunkActivity
import com.base.datarecovery.activity.largefile.LargeFileAnimationActivity
import com.base.datarecovery.activity.privacyspace.PrivacyPinOneActivity
import com.base.datarecovery.activity.privacyspace.PrivacySpaceActivity
import com.base.datarecovery.activity.recovery.FileScanResultActivity
import com.base.datarecovery.activity.repeat.RepeatAnimationActivity
import com.base.datarecovery.activity.screenshot.ScreenShotAnimationActivity
import com.base.datarecovery.activity.whatsapp.WhatsAppCleanerAnimationActivity
import com.base.datarecovery.adapter.AppFunctionAdapter
import com.base.datarecovery.ads.AdmobMaxHelper
import com.base.datarecovery.bean.ConstObject.APP_MANAGER
import com.base.datarecovery.bean.ConstObject.APP_PROCESS
import com.base.datarecovery.bean.ConstObject.BATTERY_INFO
import com.base.datarecovery.bean.ConstObject.JUNK_CLEANER
import com.base.datarecovery.bean.ConstObject.LARGE_FILE
import com.base.datarecovery.bean.ConstObject.PRIVACY_SPACE
import com.base.datarecovery.bean.ConstObject.RECOVERY_AUDIOS
import com.base.datarecovery.bean.ConstObject.RECOVERY_DOCUMENTS
import com.base.datarecovery.bean.ConstObject.RECOVERY_PHOTOS
import com.base.datarecovery.bean.ConstObject.RECOVERY_VIDEOS
import com.base.datarecovery.bean.ConstObject.REPEAT_PHOTOS
import com.base.datarecovery.bean.ConstObject.SCAN_AUDIOS
import com.base.datarecovery.bean.ConstObject.SCAN_DOCUMENTS
import com.base.datarecovery.bean.ConstObject.SCAN_PHOTOS
import com.base.datarecovery.bean.ConstObject.SCAN_VIDEOS
import com.base.datarecovery.bean.ConstObject.SCREENSHOT_CLEANER
import com.base.datarecovery.bean.ConstObject.WHATSAPP_CLEANER
import com.base.datarecovery.bean.ConstObject.privacyPinPassword
import com.base.datarecovery.databinding.ActivityLayoutResultBinding
import com.base.datarecovery.help.BaseActivity
import com.base.datarecovery.help.KotlinExt.toFormatSize
import com.base.datarecovery.utils.BarUtils
import com.base.datarecovery.utils.AppPreferences
import com.base.datarecovery.utils.LogEx
class ResultActivity : BaseActivity<ActivityLayoutResultBinding>() {
private val TAG = "ResultActivity"
override val binding: ActivityLayoutResultBinding by lazy {
ActivityLayoutResultBinding.inflate(layoutInflater)
}
private lateinit var adapter: AppFunctionAdapter
@SuppressLint("SetTextI18n", "NotifyDataSetChanged")
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))
}
REPEAT_PHOTOS -> {
startActivity(Intent(this, RepeatAnimationActivity::class.java))
}
SCREENSHOT_CLEANER -> {
startActivity(Intent(this, ScreenShotAnimationActivity::class.java))
}
RECOVERY_PHOTOS -> {
startActivity(Intent(this, FileScanResultActivity::class.java).apply {
putExtra("ScanType", SCAN_PHOTOS)
})
}
RECOVERY_VIDEOS -> {
startActivity(Intent(this, FileScanResultActivity::class.java).apply {
putExtra("ScanType", SCAN_VIDEOS)
})
}
RECOVERY_DOCUMENTS -> {
startActivity(Intent(this, FileScanResultActivity::class.java).apply {
putExtra("ScanType", SCAN_DOCUMENTS)
})
}
RECOVERY_AUDIOS -> {
startActivity(Intent(this, FileScanResultActivity::class.java).apply {
putExtra("ScanType", SCAN_AUDIOS)
})
}
PRIVACY_SPACE -> {
if (privacyPinPassword.isNotEmpty()) {
startActivity(Intent(this, PrivacyPinOneActivity::class.java))
} else {
startActivity(Intent(this, PrivacySpaceActivity::class.java).apply {
})
}
}
WHATSAPP_CLEANER -> {
startActivity(Intent(this, WhatsAppCleanerAnimationActivity::class.java))
}
LARGE_FILE -> {
startActivity(Intent(this, LargeFileAnimationActivity::class.java))
}
APP_PROCESS -> {
startActivity(Intent(this, AppProcessAnimationActivity::class.java))
}
APP_MANAGER -> {
startActivity(Intent(this, AppManagerAnimationActivity::class.java))
}
BATTERY_INFO -> {
startActivity(Intent(this, BatteryInfoAnimationActivity::class.java))
}
}
finish()
}
binding.rvFun.adapter = adapter
adapter.updateListPosition()
adapter.notifyDataSetChanged()
val from = intent.extras?.getString("from")
LogEx.logDebug(TAG, "from=$from")
when (from) {
JUNK_CLEANER -> {
if (intent.getLongExtra("clean_size", 0L) > 0) {
val size = intent.getLongExtra("clean_size", 0L).toFormatSize(1)
binding.tvInfo.text = "Cleaned up $size"
} else {
binding.tvInfo.text = "No junk files found."
}
AppPreferences.getInstance()
.put("last_use_junk_cleaner", System.currentTimeMillis())
}
else -> {
binding.tvInfo.text = "Completed!"
}
}
from?.let {
binding.tvTitle.text = it
adapter.removeItem(it)
}
onBackPressedDispatcher.addCallback(object : OnBackPressedCallback(true) {
override fun handleOnBackPressed() {
val s = AppPreferences.getInstance().getString("isShowBackIntAd", "0").toIntOrNull()
?: 0
if (s == 1) {
AdmobMaxHelper.admobMaxShowInterstitialAd(this@ResultActivity,false) {
finishToMain()
}
} else {
finishToMain()
}
}
})
binding.ivBack.setOnClickListener {
onBackPressedDispatcher.onBackPressed()
}
AdmobMaxHelper.admobMaxShowNativeAd(this@ResultActivity, binding.flAd)
}
override fun onDestroy() {
super.onDestroy()
binding.icSuccess.clearAnimation()
}
}
\ No newline at end of file
package com.base.datarecovery.activity
import android.content.ClipData
import android.content.ClipboardManager
import android.content.Context
import android.content.Intent
import android.graphics.Color
import android.net.Uri
import androidx.activity.addCallback
import com.base.datarecovery.bean.ConstObject.fcmNotification
import com.base.datarecovery.bean.ConstObject.stayNotification
import com.base.datarecovery.databinding.ActivitySettingBinding
import com.base.datarecovery.fcm.FCMManager.subscribeToTopic
import com.base.datarecovery.fcm.FCMManager.unsubscribeFromTopic
import com.base.datarecovery.help.BaseActivity
import com.base.datarecovery.help.ConfigHelper
import com.base.datarecovery.service.StayJobService
import com.base.datarecovery.service.StayJobService.Companion.startJob
import com.base.datarecovery.utils.BarUtils
import com.base.datarecovery.view.RateStarPop.showRateStarPopDialog
class SettingActivity : BaseActivity<ActivitySettingBinding>() {
override val binding: ActivitySettingBinding by lazy {
ActivitySettingBinding.inflate(layoutInflater)
}
override fun initView() {
BarUtils.setStatusBarLightMode(this, true)
BarUtils.setStatusBarColor(this, Color.TRANSPARENT)
binding.switchStayNotification.isChecked = stayNotification
binding.switchFcmNotification.isChecked = fcmNotification
}
override fun initListener() {
onBackPressedDispatcher.addCallback {
finishToMain()
}
binding.flBack.setOnClickListener {
onBackPressedDispatcher.onBackPressed()
}
binding.switchStayNotification.setOnCheckedChangeListener { buttonView, isChecked ->
stayNotification = isChecked
if (isChecked) {
// startStayNotification()
startJob()
} else {
// val serviceIntent = Intent(this, StayNotificationService::class.java)
// stopService(serviceIntent)
StayJobService.isCancel = true
}
}
binding.switchFcmNotification.setOnCheckedChangeListener { buttonView, isChecked ->
fcmNotification = isChecked
val topic = "${ConfigHelper.packageName}_push"
if (isChecked) {
subscribeToTopic(topic)
} else {
unsubscribeFromTopic(topic)
}
}
binding.llPrivacy.setOnClickListener {
val intent = Intent(
Intent.ACTION_VIEW,
Uri.parse(ConfigHelper.privacyPolicy)
)
startActivity(intent)
}
binding.llScore.setOnClickListener {
showRateStarPopDialog(mustShow = true)
}
// binding.idToken.text= AppPreferences.getInstance().getString("token","")
// binding.idCopy.setOnClickListener {
// copyText(this,binding.idToken.text.toString())
// }
}
fun copyText(context: Context, text: String?) {
val clipboardManager: ClipboardManager =
context.getSystemService(CLIPBOARD_SERVICE) as ClipboardManager
val clip = ClipData.newPlainText("simple text", text)
clipboardManager.setPrimaryClip(clip)
}
}
\ No newline at end of file
package com.base.datarecovery.activity.appmanager
import android.annotation.SuppressLint
import android.content.Context
import android.content.Intent
import android.content.pm.ApplicationInfo
import android.content.pm.PackageInfo
import android.graphics.Color
import android.net.Uri
import android.view.View
import androidx.activity.addCallback
import androidx.core.view.updatePadding
import androidx.lifecycle.lifecycleScope
import com.base.datarecovery.adapter.AppManagerAdapter
import com.base.datarecovery.ads.AdmobMaxHelper
import com.base.datarecovery.bean.AppBean
import com.base.datarecovery.databinding.ActivityAppManagerBinding
import com.base.datarecovery.help.BaseActivity
import com.base.datarecovery.utils.BarUtils
import com.base.datarecovery.utils.RamUtils.ramPair
import com.base.datarecovery.view.DialogViews.showExitFunctionDialog
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
class AppManagerActivity : BaseActivity<ActivityAppManagerBinding>() {
var uninstallTimes = 0
override val binding: ActivityAppManagerBinding by lazy {
ActivityAppManagerBinding.inflate(layoutInflater)
}
private lateinit var adapter: AppManagerAdapter
private var ePercent = 0
@SuppressLint("SetTextI18n")
override fun initView() {
BarUtils.setStatusBarLightMode(this, true)
BarUtils.setStatusBarColor(this, Color.TRANSPARENT)
binding.root.updatePadding(top = BarUtils.getStatusBarHeight())
val ramPair = ramPair()
val percent = ((ramPair.first.toFloat() / ramPair.second.toFloat()) * 100).toInt()
ePercent = percent
adapter = AppManagerAdapter(true) { pkg ->
if (uninstallTimes % 3 == 0 && uninstallTimes > 0) {
AdmobMaxHelper.admobMaxShowInterstitialAd(this,false) { unInstall(pkg) }
} else {
unInstall(pkg)
}
}
binding.rv.adapter = adapter
initData()
}
private fun unInstall(pkg: String) {
val intent = Intent(Intent.ACTION_DELETE, Uri.parse("package:${pkg}"))
launcher.launch(intent) {
if (!isInstalled(this, pkg)) {
adapter.removeBean(pkg)
uninstallTimes++
}
}
}
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
}
}
override fun initListener() {
super.initListener()
onBackPressedDispatcher.addCallback {
showExitFunctionDialog{showAd ->
if (showAd){
AdmobMaxHelper.admobMaxShowInterstitialAd(this@AppManagerActivity, isLoading = false) {
finishToMain()
}
}else{
finishToMain()
}
}
}
binding.flFanhui.setOnClickListener {
onBackPressedDispatcher.onBackPressed()
}
}
@SuppressLint("QueryPermissionsNeeded")
private fun initData() = lifecycleScope.launch(Dispatchers.IO) {
val pm = packageManager
val packages = pm.getInstalledPackages(0)
val list = arrayListOf<AppBean>()
packages.forEach { app ->
if (isLaunchApp(this@AppManagerActivity, app)) {
val appBean = AppBean(
app.applicationInfo.loadIcon(pm),
app.applicationInfo.loadLabel(pm).toString(),
app.applicationInfo.packageName
)
list.add(appBean)
}
}
launch(Dispatchers.Main) {
binding.pbLoading.visibility = View.GONE
adapter.setData(list)
}
}
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
}
}
\ No newline at end of file
package com.base.datarecovery.activity.appmanager
import android.annotation.SuppressLint
import android.content.Intent
import android.view.View
import android.widget.Toast
import androidx.activity.addCallback
import androidx.lifecycle.lifecycleScope
import com.base.datarecovery.ads.AdmobMaxHelper
import com.base.datarecovery.databinding.ActivityAppProcessAnimationBinding
import com.base.datarecovery.help.BaseActivity
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlin.random.Random
class AppManagerAnimationActivity : BaseActivity<ActivityAppProcessAnimationBinding>() {
override val binding: ActivityAppProcessAnimationBinding by lazy {
ActivityAppProcessAnimationBinding.inflate(layoutInflater)
}
private var job: Job? = null
override fun initView() {
playLottie()
}
override fun initListener() {
onBackPressedDispatcher.addCallback {
Toast.makeText(this@AppManagerAnimationActivity, "wait a moment", Toast.LENGTH_SHORT).show()
}
}
@SuppressLint("SetTextI18n")
private fun playLottie() {
binding.lottie.imageAssetsFolder = "app_manager/images/"
binding.lottie.setAnimation("app_manager/data.json")
binding.lottie.playAnimation()
}
@SuppressLint("SetTextI18n")
fun jumpJob() = lifecycleScope.launch(Dispatchers.Main) {
delay(Random.nextLong(4000, 6000))
binding.lottie.visibility = View.GONE
binding.lottieCompleted.visibility = View.VISIBLE
binding.lottieCompleted.playAnimation()
binding.tv.text = "Completed!"
delay(1000)
AdmobMaxHelper.admobMaxShowInterstitialAd(this@AppManagerAnimationActivity) {
startActivity(Intent(this@AppManagerAnimationActivity, AppManagerActivity::class.java))
finish()
}
}
override fun onResume() {
super.onResume()
if (job?.isActive == false || job == null) {
job = jumpJob()
}
}
override fun onPause() {
super.onPause()
job?.cancel()
job = null
}
}
\ No newline at end of file
package com.base.datarecovery.activity.appprocess
import android.annotation.SuppressLint
import android.content.Context
import android.content.Intent
import android.content.pm.ApplicationInfo
import android.content.pm.PackageInfo
import android.graphics.Color
import android.net.Uri
import android.provider.Settings
import android.view.View
import androidx.activity.addCallback
import androidx.core.view.updatePadding
import androidx.lifecycle.lifecycleScope
import com.base.datarecovery.adapter.AppManagerAdapter
import com.base.datarecovery.ads.AdmobMaxHelper
import com.base.datarecovery.ads.AdmobMaxHelper.showBackInterAdSp
import com.base.datarecovery.bean.AppBean
import com.base.datarecovery.databinding.ActivityAppProcessBinding
import com.base.datarecovery.help.BaseActivity
import com.base.datarecovery.help.KotlinExt.toFormatSize
import com.base.datarecovery.utils.BarUtils
import com.base.datarecovery.utils.RamUtils.ramPair
import com.base.datarecovery.view.DialogViews.showAppProcessExitDialog
import com.base.datarecovery.view.DialogViews.showExitFunctionDialog
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
class AppProcessActivity : BaseActivity<ActivityAppProcessBinding>() {
override val binding: ActivityAppProcessBinding by lazy {
ActivityAppProcessBinding.inflate(layoutInflater)
}
private lateinit var adapter: AppManagerAdapter
private var ePercent = 0
@SuppressLint("SetTextI18n")
override fun initView() {
BarUtils.setStatusBarLightMode(this, true)
BarUtils.setStatusBarColor(this, Color.TRANSPARENT)
binding.root.updatePadding(top = BarUtils.getStatusBarHeight())
val ramPair = ramPair()
val percent = ((ramPair.first.toFloat() / ramPair.second.toFloat()) * 100).toInt()
ePercent = percent
binding.tvPercent.text = percent.toString()
binding.pbRam.progress = percent
binding.tvSize.text = "${ramPair.first.toFormatSize()}/${ramPair.second.toFormatSize()}"
adapter = AppManagerAdapter { pkg ->
val intent = Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS)
val uri = Uri.fromParts("package", pkg, null) // 替换为需要跳转的应用包名
intent.setData(uri)
startActivity(intent)
}
binding.rv.adapter = adapter
initData()
}
override fun initListener() {
super.initListener()
onBackPressedDispatcher.addCallback {
showAppProcessExitDialog(ePercent) {
if (showBackInterAdSp()) {
AdmobMaxHelper.admobMaxShowInterstitialAd(this@AppProcessActivity, false) {
finishToMain()
}
} else {
finishToMain()
}
}
}
binding.flFanhui.setOnClickListener {
onBackPressedDispatcher.onBackPressed()
}
}
@SuppressLint("QueryPermissionsNeeded")
private fun initData() = lifecycleScope.launch(Dispatchers.IO) {
val pm = packageManager
val packages = pm.getInstalledPackages(0)
val list = arrayListOf<AppBean>()
packages.forEach { app ->
if (isLaunchApp(this@AppProcessActivity, app)) {
val appBean = AppBean(
app.applicationInfo.loadIcon(pm),
app.applicationInfo.loadLabel(pm).toString(),
app.applicationInfo.packageName
)
list.add(appBean)
}
}
launch(Dispatchers.Main) {
binding.pbLoading.visibility = View.GONE
adapter.setData(list)
}
}
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
}
}
\ No newline at end of file
package com.base.datarecovery.activity.appprocess
import android.annotation.SuppressLint
import android.content.Intent
import android.view.View
import android.widget.Toast
import androidx.activity.addCallback
import androidx.lifecycle.lifecycleScope
import com.base.datarecovery.ads.AdmobMaxHelper
import com.base.datarecovery.databinding.ActivityAppManagerAnimationBinding
import com.base.datarecovery.help.BaseActivity
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlin.random.Random
class AppProcessAnimationActivity : BaseActivity<ActivityAppManagerAnimationBinding>() {
override val binding: ActivityAppManagerAnimationBinding by lazy {
ActivityAppManagerAnimationBinding.inflate(layoutInflater)
}
private var job: Job? = null
override fun initView() {
playLottie()
}
override fun initListener() {
onBackPressedDispatcher.addCallback {
Toast.makeText(this@AppProcessAnimationActivity, "wait a moment", Toast.LENGTH_SHORT).show()
}
}
@SuppressLint("SetTextI18n")
private fun playLottie() {
binding.lottie.imageAssetsFolder = "app_manager/images/"
binding.lottie.setAnimation("app_manager/data.json")
binding.lottie.playAnimation()
}
@SuppressLint("SetTextI18n")
private fun jumpJob() = lifecycleScope.launch(Dispatchers.Main) {
delay(Random.nextLong(4000, 6000))
binding.lottie.visibility = View.GONE
binding.lottieCompleted.visibility = View.VISIBLE
binding.lottieCompleted.playAnimation()
binding.tv.text = "Completed!"
delay(1000)
AdmobMaxHelper.admobMaxShowInterstitialAd(this@AppProcessAnimationActivity) {
startActivity(Intent(this@AppProcessAnimationActivity, AppProcessActivity::class.java))
finish()
}
}
override fun onResume() {
super.onResume()
if (job?.isActive == false || job == null) {
job = jumpJob()
}
}
override fun onPause() {
super.onPause()
job?.cancel()
job = null
}
}
\ No newline at end of file
package com.base.datarecovery.activity.battery
import android.annotation.SuppressLint
import android.content.Intent
import android.content.IntentFilter
import android.graphics.Color
import android.os.BatteryManager
import androidx.activity.addCallback
import androidx.core.view.updatePadding
import com.base.datarecovery.activity.ResultActivity
import com.base.datarecovery.ads.AdmobMaxHelper
import com.base.datarecovery.bean.ConstObject.BATTERY_INFO
import com.base.datarecovery.databinding.ActivityBatteryBinding
import com.base.datarecovery.help.BaseActivity
import com.base.datarecovery.receiver.BatteryReceiver
import com.base.datarecovery.utils.BarUtils
import com.base.datarecovery.view.DialogViews.showExitFunctionDialog
import kotlin.math.abs
class BatteryActivity : BaseActivity<ActivityBatteryBinding>() {
private val TAG = "BatteryActivity"
override val binding: ActivityBatteryBinding by lazy {
ActivityBatteryBinding.inflate(layoutInflater)
}
private lateinit var batteryReceiver: BatteryReceiver
private lateinit var mBatteryManager: BatteryManager
@SuppressLint("SetTextI18n")
override fun initView() {
BarUtils.setStatusBarLightMode(this, true)
BarUtils.setStatusBarColor(this, Color.parseColor("#FAFAFA"))
binding.root.updatePadding(top = BarUtils.getStatusBarHeight())
batteryReceiver = BatteryReceiver {
updateUi()
}
registerReceiver(batteryReceiver, IntentFilter().apply {
addAction("android.intent.action.BATTERY_CHANGED")
})
mBatteryManager = getSystemService(BATTERY_SERVICE) as BatteryManager
// getAverageCurrent()
//
// lifecycleScope.launch(Dispatchers.Main) {
// currentFlow.collectLatest {
// binding.tvElectric.text = "${it}mA"
// }
// }
// lifecycleScope.launch(Dispatchers.Main) {
// averageFlow.collectLatest {
// binding.tvCurrentAverage.text = "${it}mA"
// }
// }
AdmobMaxHelper.admobMaxShowNativeAd(this, binding.flAd, 2)
}
override fun initListener() {
super.initListener()
onBackPressedDispatcher.addCallback {
showExitFunctionDialog { showAd ->
if (showAd) {
AdmobMaxHelper.admobMaxShowInterstitialAd(this@BatteryActivity) {
finishToMain()
}
} else {
finishToMain()
}
}
}
binding.flFanhui.setOnClickListener {
onBackPressedDispatcher.onBackPressed()
}
binding.tvFinish.setOnClickListener {
startActivity(Intent(this, ResultActivity::class.java).apply {
putExtra("from", BATTERY_INFO)
})
finish()
}
}
@SuppressLint("SetTextI18n")
private fun updateUi() {
val temperature = BatteryReceiver.temperature / 10f
binding.tvTemperature.text = format(temperature) + " °C"
binding.tvVoltage.text = "${format((BatteryReceiver.voltage / 1000f))} mV"
binding.tvTechnology.text = BatteryReceiver.technology
val capacity = BatteryReceiver.mAh.toInt()
binding.tvCapacity.text = "$capacity mAh"
val currentNow = mBatteryManager.getIntProperty(BatteryManager.BATTERY_PROPERTY_CURRENT_NOW)
binding.tvElectric.text = "${getFirstThreeDigits(currentNow * 100)} mA"
val currentAverage: Int = mBatteryManager.getIntProperty(BatteryManager.BATTERY_PROPERTY_CURRENT_AVERAGE)
binding.tvCurrentAverage.text = "${getFirstThreeDigits(currentAverage * 100)} mA"
val hm = (capacity.toFloat() / getFirstThreeDigits(currentNow * 100).toFloat())
binding.tvH.text = abs(hm.toInt()).toString()
binding.tvM.text = abs(((hm - hm.toInt()) * 60).toInt()).toString()
}
private fun getFirstThreeDigits(number: Int): String {
if (number == 0) {
return "0"
}
return number.toString().take(3)
}
private fun format(float: Float): String {
return "%.1f".format(float)
}
override fun onDestroy() {
super.onDestroy()
unregisterReceiver(batteryReceiver)
}
// @SuppressLint("PrivateApi")
// fun getBatteryFilePath(): String {
// val systemProperties = Class.forName("android.os.SystemProperties")
// val getMethod = systemProperties.getDeclaredMethod("get", String::class.java)
// val platName = getMethod.invoke(null, "ro.hardware") as String
//
// LogEx.logDebug(TAG, "platName=$platName")
// val filePath: String = if (platName.startsWith("mt") || platName.startsWith("MT")) {
// "/sys/class/power_supply/battery/device/FG_Battery_CurrentConsumption"
// // MTK平台该值不区分充放电,都为负数
// } else if (platName.startsWith("qcom")) {
// "/sys/class/power_supply/battery/current_now"
// } else if (platName.startsWith("exynos"))
// "/sys/class/power_supply/battery/BatteryAverageCurrent"
// else {
// ""
// }
// LogEx.logDebug(TAG, "filePath=$filePath")
// return filePath
// }
// var mAverageFlow = MutableSharedFlow<Float>()
// val averageFlow: SharedFlow<Float> = mAverageFlow
//
// var mCurrentFlow = MutableSharedFlow<Int>()
// var currentFlow: SharedFlow<Int> = mCurrentFlow
// private fun getAverageCurrent(filePath: String = getBatteryFilePath()) {
// var sum = 0
// var loopCount = 0
// lifecycleScope.launch(Dispatchers.IO) {
// while (isActive) {
// val currentValue = readFileValue(filePath)
// mCurrentFlow.emit(currentValue)
// sum += currentValue
// loopCount++
// val average = sum / loopCount.toFloat()
// mAverageFlow.emit(average)
// delay(150)
// }
// }
// }
// private fun readFileValue(filePath: String): Int {
// var value = 0
// var br: BufferedReader? = null
// try {
// br = BufferedReader(FileReader(filePath))
// val line = br.readLine()
// value = line?.toInt() ?: 0
// LogEx.logDebug(TAG, "readFileValue value=$value")
// } catch (e: Exception) {
// e.printStackTrace()
// } finally {
// br?.close()
// }
// return value
// }
}
\ No newline at end of file
package com.base.datarecovery.activity.battery
import android.annotation.SuppressLint
import android.content.Intent
import android.view.View
import android.widget.Toast
import androidx.activity.addCallback
import androidx.lifecycle.lifecycleScope
import com.base.datarecovery.ads.AdmobMaxHelper
import com.base.datarecovery.databinding.ActivityBatteryInfoAnimationBinding
import com.base.datarecovery.help.BaseActivity
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlin.random.Random
class BatteryInfoAnimationActivity : BaseActivity<ActivityBatteryInfoAnimationBinding>() {
override val binding: ActivityBatteryInfoAnimationBinding by lazy {
ActivityBatteryInfoAnimationBinding.inflate(layoutInflater)
}
private var job: Job? = null
override fun initView() {
resumeLottie()
}
override fun initListener() {
onBackPressedDispatcher.addCallback {
Toast.makeText(this@BatteryInfoAnimationActivity, "wait a moment", Toast.LENGTH_SHORT).show()
}
}
override fun onPause() {
super.onPause()
binding.lottie.pauseAnimation()
job?.cancel()
job = null
}
override fun onResume() {
super.onResume()
resumeLottie()
if (job?.isActive == false || job == null) {
job = waitJob()
}
}
private fun resumeLottie() {
binding.lottie.imageAssetsFolder = "battery_scan/images/"
binding.lottie.setAnimation("battery_scan/data.json")
binding.lottie.playAnimation()
}
@SuppressLint("SetTextI18n")
private fun waitJob() = lifecycleScope.launch(Dispatchers.Main) {
delay(Random.nextLong(3000, 4500))
binding.lottie.visibility = View.GONE
binding.lottieCompleted.visibility = View.VISIBLE
binding.lottieCompleted.playAnimation()
binding.tv.text = "Completed!"
delay(1000)
AdmobMaxHelper.admobMaxShowInterstitialAd(this@BatteryInfoAnimationActivity) {
binding.lottie.clearAnimation()
startActivity(Intent(this@BatteryInfoAnimationActivity, BatteryActivity::class.java))
finish()
}
}
}
\ No newline at end of file
package com.base.datarecovery.activity.guide
import android.graphics.Color
import androidx.core.content.ContextCompat
import androidx.fragment.app.Fragment
import androidx.viewpager2.adapter.FragmentStateAdapter
import androidx.viewpager2.widget.ViewPager2
import com.base.datarecovery.R
import com.base.datarecovery.databinding.ActivityGuideBinding
import com.base.datarecovery.fragment.GuideFragment
import com.base.datarecovery.help.BaseActivity
import com.base.datarecovery.utils.BarUtils
import com.zhpan.indicator.enums.IndicatorSlideMode
import com.zhpan.indicator.enums.IndicatorStyle
class GuideActivity : BaseActivity<ActivityGuideBinding>() {
private val page1: GuideFragment by lazy {
GuideFragment().apply { page = 1 }
}
private val page2: GuideFragment by lazy {
GuideFragment().apply { page = 2 }
}
private val fragments by lazy {
mutableListOf(page1, page2)
}
override val binding: ActivityGuideBinding by lazy {
ActivityGuideBinding.inflate(layoutInflater)
}
fun setPosition(position: Int) {
binding.viewpager2.currentItem = position
}
override fun initView() {
BarUtils.setStatusBarLightMode(this, true)
BarUtils.setStatusBarColor(this, Color.TRANSPARENT)
binding.viewpager2.run {
adapter = object : FragmentStateAdapter(this@GuideActivity) {
override fun getItemCount(): Int {
return fragments.size
}
override fun createFragment(position: Int): Fragment {
return fragments[position]
}
}
}
binding.viewpager2.registerOnPageChangeCallback(object :
ViewPager2.OnPageChangeCallback() {
override fun onPageSelected(position: Int) {
}
})
val normalColor = ContextCompat.getColor(this, R.color.color_cccccc)
val selectedColor = ContextCompat.getColor(this, R.color.color_4f86eb)
binding.indicatorView
.setSliderColor(normalColor, selectedColor)
.setSliderWidth(resources.getDimension(R.dimen.dp_9))
.setSliderHeight(resources.getDimension(R.dimen.dp_9))
.setSlideMode(IndicatorSlideMode.WORM)
.setIndicatorStyle(IndicatorStyle.CIRCLE)
.setupWithViewPager(binding.viewpager2)
}
}
\ No newline at end of file
package com.base.datarecovery.activity.junkclean
import android.animation.ValueAnimator
import android.content.Intent
import android.graphics.Color
import android.view.animation.LinearInterpolator
import android.widget.Toast
import androidx.activity.addCallback
import androidx.core.animation.doOnEnd
import androidx.core.view.updatePadding
import com.base.datarecovery.activity.ResultActivity
import com.base.datarecovery.ads.AdmobMaxHelper
import com.base.datarecovery.ads.admob.AdmobInterstitialUtils
import com.base.datarecovery.bean.ConstObject.JUNK_CLEANER
import com.base.datarecovery.databinding.ActivityLayoutCleanupingBinding
import com.base.datarecovery.help.BaseActivity
import com.base.datarecovery.utils.BarUtils
import com.base.datarecovery.utils.MediaStoreUtils
import com.base.datarecovery.utils.NewFileUtils
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.MainScope
import kotlinx.coroutines.launch
import kotlin.random.Random
/**
* 实际清理页面
*/
class CleaningActivity : BaseActivity<ActivityLayoutCleanupingBinding>() {
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()
}
override fun initView() {
BarUtils.setStatusBarColor(this, Color.TRANSPARENT)
binding.root.updatePadding(top = BarUtils.getStatusBarHeight())
MainScope().launch(Dispatchers.IO) {
try {
listPath.forEach {
runCatching { NewFileUtils.delete(it) }
}
MediaStoreUtils.updateMediaStore(this@CleaningActivity, listPath)
} catch (_: Exception) {
} finally {
}
}
playAnm()
}
override fun initListener() {
onBackPressedDispatcher.addCallback {
Toast.makeText(this@CleaningActivity, "wait a moment", Toast.LENGTH_SHORT).show()
}
}
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 {
AdmobMaxHelper.admobMaxShowInterstitialAd(this@CleaningActivity,false) {
startActivity(
Intent(this@CleaningActivity, ResultActivity::class.java).putExtra("from", JUNK_CLEANER)
.putExtra("clean_size", intentSize)
)
finish()
}
}
start()
}
}
}
\ No newline at end of file
package com.base.datarecovery.activity.junkclean
import android.animation.ValueAnimator
import android.annotation.SuppressLint
import android.content.Intent
import android.graphics.Color
import android.view.View
import android.view.animation.LinearInterpolator
import android.widget.Toast
import androidx.activity.addCallback
import androidx.core.view.updatePadding
import androidx.lifecycle.lifecycleScope
import com.base.datarecovery.ads.AdmobMaxHelper
import com.base.datarecovery.databinding.ActivityLayoutScanJunkBinding
import com.base.datarecovery.help.BaseActivity
import com.base.datarecovery.help.PermissionHelp.checkStorePermission
import com.base.datarecovery.help.PermissionHelp.requestStorePermission
import com.base.datarecovery.utils.BarUtils
import com.base.datarecovery.utils.LogEx
import com.base.datarecovery.view.DialogViews.showGerPermission
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
class ScanJunk2Activity : BaseActivity<ActivityLayoutScanJunkBinding>() {
private val TAG = "ScanJunkActivity"
override val binding: ActivityLayoutScanJunkBinding by lazy {
ActivityLayoutScanJunkBinding.inflate(layoutInflater)
}
private var job: Job? = null
override fun initView() {
LogEx.logDebug(TAG,"ScanJunkActivity initView")
BarUtils.setStatusBarColor(this, Color.TRANSPARENT)
binding.root.updatePadding(top = BarUtils.getStatusBarHeight())
}
override fun initListener() {
onBackPressedDispatcher.addCallback {
Toast.makeText(this@ScanJunk2Activity, "wait a moment", Toast.LENGTH_SHORT).show()
}
}
override fun onResume() {
super.onResume()
resumeLottie()
job = waitJob()
}
override fun onDestroy() {
super.onDestroy()
animator1?.cancel()
animator2?.cancel()
animator3?.cancel()
binding.lottie.cancelAnimation()
}
override fun onPause() {
super.onPause()
animator1?.pause()
animator2?.pause()
animator3?.pause()
binding.lottie.pauseAnimation()
job?.cancel()
}
@SuppressLint("SetTextI18n")
private fun resumeLottie() {
if (animator1 == null) {
animator1 = ValueAnimator.ofFloat(0f, 360f)
animator1?.run {
duration = 1000
repeatMode = ValueAnimator.RESTART
repeatCount = ValueAnimator.INFINITE
interpolator = LinearInterpolator()
addUpdateListener {
binding.idView1.rotation = it.animatedValue as Float
}
}
}
animator1?.start()
if (animator2 == null) {
animator2 = ValueAnimator.ofFloat(0f, 360f)
animator2?.run {
duration = 1000
repeatMode = ValueAnimator.RESTART
repeatCount = ValueAnimator.INFINITE
interpolator = LinearInterpolator()
addUpdateListener {
binding.idView2.rotation = it.animatedValue as Float
}
}
}
animator2?.start()
if (animator3 == null) {
animator3 = ValueAnimator.ofFloat(0f, 360f)
animator3?.run {
duration = 1000
repeatMode = ValueAnimator.RESTART
repeatCount = ValueAnimator.INFINITE
interpolator = LinearInterpolator()
addUpdateListener {
binding.idView3.rotation = it.animatedValue as Float
}
}
}
animator3?.start()
binding.lottie.imageAssetsFolder = "junk_scan/images/"
binding.lottie.setAnimation("junk_scan/data.json")
binding.lottie.playAnimation()
}
private fun waitJob() = lifecycleScope.launch(Dispatchers.Main) {
delay(5000)
binding.lottie.visibility = View.GONE
binding.lottieCompleted.visibility = View.VISIBLE
binding.lottieCompleted.playAnimation()
binding.tv.text = "Completed!"
animator1?.cancel()
animator2?.cancel()
animator3?.cancel()
delay(1000)
AdmobMaxHelper.admobMaxShowInterstitialAd(this@ScanJunk2Activity) {
startActivity(Intent(this@ScanJunk2Activity, CleanJunkActivity::class.java))
finish()
}
}
private var animator1: ValueAnimator? = null
private var animator2: ValueAnimator? = null
private var animator3: ValueAnimator? = null
}
\ No newline at end of file
package com.base.datarecovery.activity.junkclean
import android.animation.ValueAnimator
import android.annotation.SuppressLint
import android.content.Intent
import android.graphics.Color
import android.view.View
import android.view.animation.LinearInterpolator
import android.widget.Toast
import androidx.activity.addCallback
import androidx.core.view.updatePadding
import androidx.lifecycle.lifecycleScope
import com.base.datarecovery.ads.AdmobMaxHelper.admobMaxShowInterstitialAd
import com.base.datarecovery.databinding.ActivityLayoutScanJunkBinding
import com.base.datarecovery.help.BaseActivity
import com.base.datarecovery.help.PermissionHelp.checkStorePermission
import com.base.datarecovery.help.PermissionHelp.requestStorePermission
import com.base.datarecovery.utils.BarUtils
import com.base.datarecovery.utils.LogEx
import com.base.datarecovery.view.DialogViews.showGerPermission
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
/**
* 清理动画页面
*/
class ScanJunkActivity : BaseActivity<ActivityLayoutScanJunkBinding>() {
private val TAG = "ScanJunkActivity"
override val binding: ActivityLayoutScanJunkBinding by lazy {
ActivityLayoutScanJunkBinding.inflate(layoutInflater)
}
private var job: Job? = null
override fun initView() {
LogEx.logDebug(TAG,"ScanJunkActivity initView")
BarUtils.setStatusBarColor(this, Color.TRANSPARENT)
binding.root.updatePadding(top = BarUtils.getStatusBarHeight())
if (!checkStorePermission()) {
showGerPermission(tittle = "Storage Permission Required",
desc = "This feature requires access to your storage to scan your files and clean up junk files and unused APK files. We will not transmit your data to any third-party service. Please grant permission so that we can provide you with better service.",
deny = { finishToMain() },
allow = {
requestStorePermission(launcher, result = {
if (it) {
} else {
finishToMain()
}
})
})
}
}
override fun initListener() {
onBackPressedDispatcher.addCallback {
Toast.makeText(this@ScanJunkActivity, "wait a moment", Toast.LENGTH_SHORT).show()
}
}
override fun onResume() {
super.onResume()
if (checkStorePermission()) {
resumeLottie()
job = waitJob()
}
}
override fun onDestroy() {
super.onDestroy()
animator1?.cancel()
animator2?.cancel()
animator3?.cancel()
binding.lottie.cancelAnimation()
}
override fun onPause() {
super.onPause()
animator1?.pause()
animator2?.pause()
animator3?.pause()
binding.lottie.pauseAnimation()
job?.cancel()
}
@SuppressLint("SetTextI18n")
private fun resumeLottie() {
if (animator1 == null) {
animator1 = ValueAnimator.ofFloat(0f, 360f)
animator1?.run {
duration = 1000
repeatMode = ValueAnimator.RESTART
repeatCount = ValueAnimator.INFINITE
interpolator = LinearInterpolator()
addUpdateListener {
binding.idView1.rotation = it.animatedValue as Float
}
}
}
animator1?.start()
if (animator2 == null) {
animator2 = ValueAnimator.ofFloat(0f, 360f)
animator2?.run {
duration = 1000
repeatMode = ValueAnimator.RESTART
repeatCount = ValueAnimator.INFINITE
interpolator = LinearInterpolator()
addUpdateListener {
binding.idView2.rotation = it.animatedValue as Float
}
}
}
animator2?.start()
if (animator3 == null) {
animator3 = ValueAnimator.ofFloat(0f, 360f)
animator3?.run {
duration = 1000
repeatMode = ValueAnimator.RESTART
repeatCount = ValueAnimator.INFINITE
interpolator = LinearInterpolator()
addUpdateListener {
binding.idView3.rotation = it.animatedValue as Float
}
}
}
animator3?.start()
binding.lottie.imageAssetsFolder = "junk_scan/images/"
binding.lottie.setAnimation("junk_scan/data.json")
binding.lottie.playAnimation()
}
private fun waitJob() = lifecycleScope.launch(Dispatchers.Main) {
delay(5000)
binding.lottie.visibility = View.GONE
binding.lottieCompleted.visibility = View.VISIBLE
binding.lottieCompleted.playAnimation()
binding.tv.text = "Completed!"
animator1?.cancel()
animator2?.cancel()
animator3?.cancel()
delay(1000)
admobMaxShowInterstitialAd(this@ScanJunkActivity) {
startActivity(Intent(this@ScanJunkActivity, CleanJunkActivity::class.java))
finish()
}
}
private var animator1: ValueAnimator? = null
private var animator2: ValueAnimator? = null
private var animator3: ValueAnimator? = null
}
\ No newline at end of file
package com.base.datarecovery.activity.largefile
import android.annotation.SuppressLint
import android.content.Intent
import androidx.activity.addCallback
import androidx.core.view.isVisible
import androidx.lifecycle.lifecycleScope
import com.base.datarecovery.activity.ResultActivity
import com.base.datarecovery.adapter.LargeFileAdapter
import com.base.datarecovery.ads.AdmobMaxHelper
import com.base.datarecovery.bean.ConstObject
import com.base.datarecovery.bean.ConstObject.SELECT_FILE_ALL
import com.base.datarecovery.bean.ConstObject.SELECT_FILE_AUDIO
import com.base.datarecovery.bean.ConstObject.SELECT_FILE_DOCUMENT
import com.base.datarecovery.bean.ConstObject.SELECT_FILE_OTHER
import com.base.datarecovery.bean.ConstObject.SELECT_FILE_PICTURE
import com.base.datarecovery.bean.ConstObject.SELECT_FILE_VIDEO
import com.base.datarecovery.bean.MediaBean
import com.base.datarecovery.databinding.ActivityLargeFileBinding
import com.base.datarecovery.help.BaseActivity
import com.base.datarecovery.help.KotlinExt.toFormatSize
import com.base.datarecovery.help.MediaStoreHelp.getAllMedia
import com.base.datarecovery.help.PermissionHelp.checkStorePermission
import com.base.datarecovery.help.PermissionHelp.requestStorePermission
import com.base.datarecovery.view.DialogViews.showDeletePermanentlyDialog
import com.base.datarecovery.view.DialogViews.showExitFunctionDialog
import com.base.datarecovery.view.DialogViews.showGerPermission
import com.base.datarecovery.view.DialogViews.showLargeFileTypeDialog
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import java.io.File
class LargeFileActivity : BaseActivity<ActivityLargeFileBinding>() {
private lateinit var largeFileAdapter: LargeFileAdapter
private var largeData = listOf<MediaBean>()
override val binding: ActivityLargeFileBinding by lazy {
ActivityLargeFileBinding.inflate(layoutInflater)
}
var selectType = SELECT_FILE_ALL
@SuppressLint("SetTextI18n")
override fun initView() {
largeFileAdapter = LargeFileAdapter(itemClick = {}, selectClick = {
if (it.isNotEmpty()) {
binding.tvDelete.isEnabled = true
binding.tvDelete.text = "Delete(${it.sumOf { bean -> bean.size }.toFormatSize()})"
} else {
binding.tvDelete.isEnabled = false
}
})
binding.rv.adapter = largeFileAdapter
if (checkStorePermission()) {
initData()
} else {
showGerPermission(
desc = "This feature requires access to your storage to scan your large files and clean up large files." +
"We will not transmit your data to any third-party service. Please grant permission so that we can provide you with better service.",
deny = { finishToMain() },
allow = {
requestStorePermission(launcher) {
if (it) initData() else finishToMain()
}
})
}
}
override fun initListener() {
super.initListener()
onBackPressedDispatcher.addCallback {
showExitFunctionDialog { show ->
if (show) {
AdmobMaxHelper.admobMaxShowInterstitialAd(this@LargeFileActivity, false) {
finishToMain()
}
} else {
finishToMain()
}
}
}
binding.flBack.setOnClickListener {
onBackPressedDispatcher.onBackPressed()
}
binding.tvDelete.setOnClickListener {
showDeletePermanentlyDialog {
deleteFiles()
}
}
binding.flFilter.setOnClickListener {
showLargeFileTypeDialog(selectType) { type ->
selectType = type
binding.tvType.text = selectType
largeFileAdapter.setData(largeData.filter { selectFilter(it) })
}
}
}
private fun selectFilter(mediaBean: MediaBean): Boolean {
when (selectType) {
SELECT_FILE_ALL -> return true
SELECT_FILE_PICTURE -> {
return mediaBean.mimeType.contains("image")
}
SELECT_FILE_VIDEO -> {
return mediaBean.mimeType.contains("video")
}
SELECT_FILE_AUDIO -> {
return mediaBean.mimeType.contains("audio")
}
SELECT_FILE_DOCUMENT -> {
return mediaBean.mimeType.contains("pdf")
|| mediaBean.mimeType.contains("msword")
|| mediaBean.mimeType.contains("document")
|| mediaBean.mimeType.contains("excel")
|| mediaBean.mimeType.contains("sheet")
|| mediaBean.mimeType.contains("powerpoint")
|| mediaBean.mimeType.contains("presentation")
}
SELECT_FILE_OTHER -> {
return mediaBean.mimeType.contains("zip")
|| mediaBean.mimeType.contains("rar")
|| mediaBean.mimeType.contains("7z")
|| mediaBean.mimeType.contains("tar")
|| mediaBean.mimeType.contains("archive")
|| mediaBean.mimeType.contains("stream")
}
}
return false
}
private fun deleteFiles() = lifecycleScope.launch(Dispatchers.IO) {
val list = largeFileAdapter.getSelectData()
runCatching {
list.forEach { bean ->
File(bean.path).delete()
}
}
launch(Dispatchers.Main) {
largeFileAdapter.removeData(list)
delay(1000)
startActivity(Intent(this@LargeFileActivity, ResultActivity::class.java).apply {
putExtra("from", ConstObject.LARGE_FILE)
})
finish()
}
}
private fun initData() = lifecycleScope.launch(Dispatchers.IO) {
val beanList = getAllMedia { size -> size > 1024 * 1024 * 10 }
launch(Dispatchers.Main) {
largeData = beanList
binding.llEmpty.isVisible = beanList.isEmpty()
largeFileAdapter.setData(beanList)
}
}
}
\ No newline at end of file
package com.base.datarecovery.activity.largefile
import android.annotation.SuppressLint
import android.content.Intent
import android.view.View
import android.widget.Toast
import androidx.activity.addCallback
import androidx.lifecycle.lifecycleScope
import com.base.datarecovery.ads.AdmobMaxHelper
import com.base.datarecovery.databinding.ActivityLargeFileAnimationBinding
import com.base.datarecovery.help.BaseActivity
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
class LargeFileAnimationActivity : BaseActivity<ActivityLargeFileAnimationBinding>() {
override val binding: ActivityLargeFileAnimationBinding by lazy {
ActivityLargeFileAnimationBinding.inflate(layoutInflater)
}
private var job: Job? = null
override fun initView() {
}
override fun initListener() {
onBackPressedDispatcher.addCallback {
Toast.makeText(this@LargeFileAnimationActivity, "wait a moment", Toast.LENGTH_SHORT).show()
}
}
private fun jumpJob() = lifecycleScope.launch(Dispatchers.Main) {
delay(5000)
binding.lottie.visibility = View.GONE
binding.lottie.clearAnimation()
binding.lottieCompleted.visibility = View.VISIBLE
binding.lottieCompleted.playAnimation()
binding.tv.text = "Completed!"
delay(1000)
AdmobMaxHelper.admobMaxShowInterstitialAd(this@LargeFileAnimationActivity) {
startActivity(Intent(this@LargeFileAnimationActivity, LargeFileActivity::class.java))
finish()
}
}
override fun onResume() {
super.onResume()
if (job?.isActive == false || job == null) {
job = jumpJob()
}
}
override fun onPause() {
super.onPause()
job?.cancel()
job = null
}
}
\ No newline at end of file
package com.base.datarecovery.activity.photomanager
import android.content.Intent
import android.graphics.Color
import android.os.Environment
import androidx.activity.addCallback
import androidx.core.view.updatePadding
import androidx.lifecycle.lifecycleScope
import com.base.datarecovery.activity.repeat.RepeatActivity
import com.base.datarecovery.activity.screenshot.ScreenShotActivity
import com.base.datarecovery.databinding.ActivityPhotoManagerBinding
import com.base.datarecovery.help.BaseActivity
import com.base.datarecovery.help.FileHelp.getDirFiles
import com.base.datarecovery.help.KotlinExt.toFormatSize
import com.base.datarecovery.help.PermissionHelp.checkStorePermission
import com.base.datarecovery.help.PermissionHelp.requestStorePermission
import com.base.datarecovery.utils.BarUtils
import com.base.datarecovery.utils.FileHexEx.isImage
import com.base.datarecovery.view.DialogViews.showGerPermission
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import java.io.File
class PhotoManagerActivity : BaseActivity<ActivityPhotoManagerBinding>() {
override val binding: ActivityPhotoManagerBinding by lazy {
ActivityPhotoManagerBinding.inflate(layoutInflater)
}
override fun initView() {
BarUtils.setStatusBarLightMode(this, true)
BarUtils.setStatusBarColor(this, Color.WHITE)
binding.root.updatePadding(top = BarUtils.getStatusBarHeight())
val tittle = intent.extras?.getString("tittle")
tittle?.let { binding.tvTittle.text = it }
if (checkStorePermission()) {
initDataSize()
} else {
showGerPermission(desc = "This feature requires access to your storage to scan your files and clean up screenshots. We will not transmit your data to any third-party service. Please grant permission so that we can provide you with better service.",
deny = { finishToMain() }, allow = {
requestStorePermission(launcher) {
if (it) {
initDataSize()
} else {
finishToMain()
}
}
})
}
}
override fun initListener() {
super.initListener()
onBackPressedDispatcher.addCallback {
finishToMain()
}
binding.flBack.setOnClickListener {
onBackPressedDispatcher.onBackPressed()
}
binding.cardScreenshot.setOnClickListener {
startActivity(Intent(this@PhotoManagerActivity, ScreenShotActivity::class.java))
}
binding.cardRepeatPhotos.setOnClickListener {
startActivity(Intent(this@PhotoManagerActivity, RepeatActivity::class.java))
}
}
private fun initDataSize() = lifecycleScope.launch(Dispatchers.IO) {
val dcimSize =
getDirFiles(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM)).filter { isImage(it) }
.sumOf { it.length() }.toFormatSize()
RepeatActivity.beanList.addAll(RepeatActivity.getSimilarList(this@PhotoManagerActivity))
val photoSize = RepeatActivity.beanList.sumOf { it.beans.sumOf { s -> File(s.path).length() } }.toFormatSize()
launch(Dispatchers.Main) {
binding.tvScreenshotSize.text = dcimSize
binding.tvDuplicateSize.text = photoSize
}
}
}
\ No newline at end of file
package com.base.datarecovery.activity.photomanager
import android.content.Intent
import android.view.View
import android.widget.Toast
import androidx.activity.addCallback
import androidx.lifecycle.lifecycleScope
import com.base.datarecovery.ads.AdmobMaxHelper
import com.base.datarecovery.databinding.ActivityScreenShotAnimationBinding
import com.base.datarecovery.help.BaseActivity
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlin.random.Random
class PhotoManagerAnimationActivity : BaseActivity<ActivityScreenShotAnimationBinding>() {
override val binding: ActivityScreenShotAnimationBinding by lazy {
ActivityScreenShotAnimationBinding.inflate(layoutInflater)
}
;
private var job: Job? = null
override fun initView() {
}
private fun jumpJob() = lifecycleScope.launch(Dispatchers.Main) {
delay(Random.nextLong(4000, 4500))
binding.lottie.visibility = View.INVISIBLE
binding.lottieCompleted.visibility = View.VISIBLE
binding.lottieCompleted.playAnimation()
delay(1500)
val tittle = this@PhotoManagerAnimationActivity.intent?.extras?.getString("tittle")
AdmobMaxHelper.admobMaxShowInterstitialAd(this@PhotoManagerAnimationActivity) {
startActivity(Intent(this@PhotoManagerAnimationActivity, PhotoManagerActivity::class.java).apply {
tittle?.let { putExtra("tittle", it) }
})
finish()
}
}
override fun initListener() {
super.initListener()
onBackPressedDispatcher.addCallback {
Toast.makeText(this@PhotoManagerAnimationActivity, "wait a moment", Toast.LENGTH_SHORT).show()
}
}
override fun onResume() {
super.onResume()
if (job?.isActive == false || job == null) {
job = jumpJob()
}
}
override fun onPause() {
super.onPause()
job?.cancel()
job = null
}
}
\ No newline at end of file
package com.base.datarecovery.activity.privacyspace
import android.annotation.SuppressLint
import android.content.Intent
import android.graphics.Color
import android.view.View
import androidx.activity.addCallback
import androidx.core.view.isVisible
import androidx.fragment.app.Fragment
import androidx.lifecycle.lifecycleScope
import androidx.viewpager2.adapter.FragmentStateAdapter
import androidx.viewpager2.widget.ViewPager2
import com.base.datarecovery.bean.ConstObject.rememberOption
import com.base.datarecovery.bean.ConstObject.rememberRemove
import com.base.datarecovery.bean.MediaBean
import com.base.datarecovery.databinding.ActivityPrivacyImportBinding
import com.base.datarecovery.fragment.PrivacyFileImportFragment
import com.base.datarecovery.help.BaseActivity
import com.base.datarecovery.help.KotlinExt.toFormatSize
import com.base.datarecovery.help.MediaStoreHelp.updateMediaStore
import com.base.datarecovery.utils.BarUtils
import com.base.datarecovery.utils.LogEx
import com.base.datarecovery.view.DialogViews.showRemoveOriginalTip
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import java.io.File
class PrivacyImportActivity : BaseActivity<ActivityPrivacyImportBinding>() {
private val TAG = "PrivacyImportActivity"
private val photosImportFragment: PrivacyFileImportFragment by lazy {
PrivacyFileImportFragment("Photos")
}
private val videosImportFragment: PrivacyFileImportFragment by lazy {
PrivacyFileImportFragment("Videos")
}
private val fragments by lazy {
mutableListOf(photosImportFragment, videosImportFragment)
}
override val binding: ActivityPrivacyImportBinding by lazy {
ActivityPrivacyImportBinding.inflate(layoutInflater)
}
override fun initView() {
BarUtils.setStatusBarLightMode(this, true)
BarUtils.setStatusBarColor(this, Color.TRANSPARENT)
binding.viewpager2.run {
adapter = object : FragmentStateAdapter(this@PrivacyImportActivity) {
override fun getItemCount(): Int {
return fragments.size
}
override fun createFragment(position: Int): Fragment {
return fragments[position]
}
}
}
binding.viewpager2.registerOnPageChangeCallback(object :
ViewPager2.OnPageChangeCallback() {
override fun onPageSelected(position: Int) {
if (position == 0) {
binding.llPhotos.isSelected = true
binding.llVideos.isSelected = false
} else {
binding.llVideos.isSelected = true
binding.llPhotos.isSelected = false
}
}
})
}
override fun initListener() {
super.initListener()
binding.llPhotos.setOnClickListener {
binding.viewpager2.currentItem = 0
}
binding.llVideos.setOnClickListener {
binding.viewpager2.currentItem = 1
}
binding.flFanhui.setOnClickListener {
onBackPressedDispatcher.onBackPressed()
}
onBackPressedDispatcher.addCallback {
finish()
}
}
private val photosPath = arrayListOf<MediaBean>()
private val videosPath = arrayListOf<MediaBean>()
@SuppressLint("SetTextI18n")
fun showCardImport(type: String, list: List<MediaBean>) {
if (type == "Photos") {
photosPath.clear()
photosPath.addAll(list)
} else {
videosPath.clear()
videosPath.addAll(list)
}
val allSelect = arrayListOf<MediaBean>()
allSelect.addAll(photosPath)
allSelect.addAll(videosPath)
if (allSelect.isNotEmpty()) {
binding.cardImport.isVisible = true
binding.tvSelectNumber.text = "${allSelect.size} Selected"
binding.tvSize.text = allSelect.sumOf { File(it.path).length() }.toFormatSize()
binding.tvImport.setOnClickListener {
if (!rememberOption) {
showRemoveOriginalTip { isRememberOption, isRemove ->
rememberOption = isRememberOption
rememberRemove = isRemove
copyMediaToPrivacySpace(allSelect)
}
} else {
copyMediaToPrivacySpace(allSelect)
}
}
} else {
binding.tvImport.setOnClickListener {}
binding.cardImport.isVisible = false
}
}
private fun copyMediaToPrivacySpace(allSelect: ArrayList<MediaBean>) {
binding.flProgress.setOnClickListener { }
binding.flProgress.visibility = View.VISIBLE
lifecycleScope.launch(Dispatchers.IO) {
allSelect.forEach {
LogEx.logDebug(TAG, it.mimeType)
val dir = filesDir
val file = File(it.path)
runCatching {
if (it.mimeType.contains("image")) {
val imageDir = File(dir, "photos")
file.copyTo(File(imageDir, file.name), true)
} else {
val videoDir = File(dir, "videos")
file.copyTo(File(videoDir, file.name), true)
}
if (rememberRemove) {
file.delete()
}
}
}
updateMediaStore()
launch(Dispatchers.Main) {
binding.flProgress.visibility = View.GONE
setResult(0, Intent().apply { putExtra("Result", "Success") })
finish()
}
}
}
}
\ No newline at end of file
package com.base.datarecovery.activity.privacyspace
import android.annotation.SuppressLint
import android.graphics.Color
import android.os.Environment
import android.view.View
import android.widget.Toast
import androidx.core.view.updatePadding
import androidx.lifecycle.lifecycleScope
import com.base.datarecovery.adapter.MediaSubAdapter
import com.base.datarecovery.ads.AdmobMaxHelper
import com.base.datarecovery.ads.admob.AdmobInterstitialUtils
import com.base.datarecovery.bean.MediaBean
import com.base.datarecovery.databinding.ActivityPrivacyManageBinding
import com.base.datarecovery.help.BaseActivity
import com.base.datarecovery.help.FileHelp.loadFileByFilter
import com.base.datarecovery.help.MediaStoreHelp.updateMediaStore
import com.base.datarecovery.utils.BarUtils
import com.base.datarecovery.utils.LogEx
import com.base.datarecovery.view.DialogViews.showDeletePermanentlyDialog
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import java.io.File
class PrivacyManageActivity : BaseActivity<ActivityPrivacyManageBinding>() {
private val TAG = "PrivacyManageActivity"
private var mediaType: String = ""
private lateinit var mediaSubAdapter: MediaSubAdapter
override val binding: ActivityPrivacyManageBinding by lazy {
ActivityPrivacyManageBinding.inflate(layoutInflater)
}
override fun initView() {
BarUtils.setStatusBarLightMode(this, true)
BarUtils.setStatusBarColor(this, Color.TRANSPARENT)
binding.root.updatePadding(top = BarUtils.getStatusBarHeight())
mediaType = intent?.extras?.getString("mediaType") ?: ""
mediaSubAdapter = MediaSubAdapter()
binding.rv.adapter = mediaSubAdapter
initData()
}
@SuppressLint("SetTextI18n")
override fun initListener() {
binding.llDownload.setOnClickListener {
downloadCopy()
}
binding.llDelete.setOnClickListener {
deleteFilesDirMedia()
}
binding.tvSelectAll.setOnClickListener {
binding.tvSelectAll.isSelected = !binding.tvSelectAll.isSelected
mediaSubAdapter.toggleSelect(binding.tvSelectAll.isSelected)
if (binding.tvSelectAll.isSelected) {
binding.tvSelectAll.text = "Unselect All"
} else {
binding.tvSelectAll.text = "Select All"
}
}
binding.flGuanbi.setOnClickListener {
finish()
}
}
private fun deleteFilesDirMedia() {
showDeletePermanentlyDialog {
AdmobMaxHelper.admobMaxShowInterstitialAd(this,false){
lifecycleScope.launch(Dispatchers.IO) {
val list = mediaSubAdapter.getSelectData()
list.forEach {
runCatching { File(it.path).delete() }
}
launch(Dispatchers.Main) {
Toast.makeText(this@PrivacyManageActivity, "success", Toast.LENGTH_SHORT).show()
mediaSubAdapter.removeData(list)
}
}
}
}
}
private fun downloadCopy() = lifecycleScope.launch(Dispatchers.IO) {
mediaSubAdapter.getSelectData().forEach { bean ->
runCatching {
val file = File(bean.path)
val dir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS)
file.copyTo(File(dir, file.name))
}
}
this@PrivacyManageActivity.updateMediaStore()
launch(Dispatchers.Main) {
Toast.makeText(this@PrivacyManageActivity, "success", Toast.LENGTH_SHORT).show()
mediaSubAdapter.toggleSelect(false)
}
}
private fun initData() {
val dir = getFilesMediaDir()
if (!dir.exists()) {
dir.mkdirs()
}
val list = arrayListOf<MediaBean>()
lifecycleScope.loadFileByFilter(folder = dir, filter = { true }, onDo = {
LogEx.logDebug(TAG, "$it")
list.add(MediaBean(it.absolutePath))
}, onFinish = {
lifecycleScope.launch(Dispatchers.Main) {
if (list.isNotEmpty()) {
binding.rv.visibility = View.VISIBLE
mediaSubAdapter.setData(list)
}
}
})
}
private fun getFilesMediaDir(): File {
return if (mediaType == "Photos") {
File(filesDir, "photos")
} else {
File(filesDir, "videos")
}
}
}
\ No newline at end of file
package com.base.datarecovery.activity.privacyspace
import android.content.Intent
import android.graphics.Color
import android.os.Build
import android.view.KeyEvent
import androidx.activity.addCallback
import androidx.annotation.RequiresApi
import androidx.core.content.ContextCompat
import androidx.core.widget.addTextChangedListener
import androidx.lifecycle.lifecycleScope
import com.base.datarecovery.R
import com.base.datarecovery.bean.ConstObject.privacyPinPassword
import com.base.datarecovery.databinding.ActivityPrivacyPinOneBinding
import com.base.datarecovery.help.BaseActivity
import com.base.datarecovery.help.KotlinExt.array2String
import com.base.datarecovery.utils.BarUtils
import com.base.datarecovery.utils.LogEx
import com.base.datarecovery.view.AsteriskPasswordTransformationMethod
import com.base.datarecovery.view.DialogViews.showPinReEnterDialog
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
class PrivacyPinOneActivity : BaseActivity<ActivityPrivacyPinOneBinding>() {
private val TAG = "PrivacyPinActivity"
private val firstInput = arrayOf("", "", "", "")
override val binding: ActivityPrivacyPinOneBinding by lazy {
ActivityPrivacyPinOneBinding.inflate(layoutInflater)
}
override fun initView() {
BarUtils.setStatusBarLightMode(this, true)
BarUtils.setStatusBarColor(this, Color.TRANSPARENT)
}
@RequiresApi(Build.VERSION_CODES.O)
override fun initListener() {
binding.flFanhui.setOnClickListener {
onBackPressedDispatcher.onBackPressed()
}
onBackPressedDispatcher.addCallback {
finishToMain()
}
setInput()
setDeleteKey()
}
private fun setDeleteKey() {
arrayOf(binding.edit1, binding.edit2, binding.edit3, binding.edit4).forEach {
it.setOnKeyListener { v, actionId, event ->
if ((event.action == KeyEvent.ACTION_DOWN) && (event.keyCode == KeyEvent.KEYCODE_DEL)) {
if (binding.edit1.hasFocus()) {
binding.edit1.setText("")
}
if (binding.edit2.hasFocus()) {
binding.edit2.setText("")
binding.edit1.requestFocus()
}
if (binding.edit3.hasFocus()) {
binding.edit3.setText("")
binding.edit2.requestFocus()
}
if (binding.edit4.hasFocus()) {
binding.edit4.setText("")
binding.edit3.requestFocus()
}
true
} else {
false
}
}
}
}
private fun startJump() {
if (privacyPinPassword.isNotEmpty()) {
if (privacyPinPassword == firstInput.array2String()) {
startActivity(Intent(this, PrivacySpaceActivity::class.java))
finish()
} else {
warmPassword()
}
} else {
launcher.launch(
Intent(this, PrivacyPinTwoActivity::class.java).apply {
addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION)
putExtra("FirstInput", firstInput.array2String())
}
) {
startActivity(Intent(this, PrivacySpaceActivity::class.java))
finish()
}
}
}
private fun setInput() {
binding.edit1.setOnFocusChangeListener { v, hasFocus ->
cancelWarmPassword()
binding.fl1.isSelected = hasFocus
}
binding.edit2.setOnFocusChangeListener { v, hasFocus ->
cancelWarmPassword()
binding.fl2.isSelected = hasFocus
}
binding.edit3.setOnFocusChangeListener { v, hasFocus ->
cancelWarmPassword()
binding.fl3.isSelected = hasFocus
}
binding.edit4.setOnFocusChangeListener { v, hasFocus ->
cancelWarmPassword()
binding.fl4.isSelected = hasFocus
}
binding.edit1.transformationMethod = AsteriskPasswordTransformationMethod()
binding.edit2.transformationMethod = AsteriskPasswordTransformationMethod()
binding.edit3.transformationMethod = AsteriskPasswordTransformationMethod()
binding.edit4.transformationMethod = AsteriskPasswordTransformationMethod()
binding.edit1.requestFocus()
binding.edit1.addTextChangedListener {
val text = it.toString()
LogEx.logDebug(TAG, "text=$text")
if (text.isNotEmpty()) {
firstInput[0] = text
binding.edit2.requestFocus()
}
}
binding.edit2.addTextChangedListener {
val text = it.toString()
LogEx.logDebug(TAG, "text=$text")
if (text.isNotEmpty()) {
firstInput[1] = text
binding.edit3.requestFocus()
}
}
binding.edit3.addTextChangedListener {
val text = it.toString()
LogEx.logDebug(TAG, "text=$text")
if (text.isNotEmpty()) {
firstInput[2] = text
binding.edit4.requestFocus()
}
}
binding.edit4.addTextChangedListener {
val text = it.toString()
LogEx.logDebug(TAG, "text=$text")
if (text.isNotEmpty()) {
firstInput[3] = text
startJump()
}
}
}
private fun warmPassword() {
binding.fl1.tag = true
binding.fl1.background = ContextCompat.getDrawable(this, R.drawable.bg_border_pin_warm)
binding.fl2.background = ContextCompat.getDrawable(this, R.drawable.bg_border_pin_warm)
binding.fl3.background = ContextCompat.getDrawable(this, R.drawable.bg_border_pin_warm)
binding.fl4.background = ContextCompat.getDrawable(this, R.drawable.bg_border_pin_warm)
binding.edit1.setTextColor(ContextCompat.getColor(this, R.color.color_fa020b))
binding.edit2.setTextColor(ContextCompat.getColor(this, R.color.color_fa020b))
binding.edit3.setTextColor(ContextCompat.getColor(this, R.color.color_fa020b))
binding.edit4.setTextColor(ContextCompat.getColor(this, R.color.color_fa020b))
val dialog = showPinReEnterDialog()
lifecycleScope.launch {
delay(2000)
dialog.dismiss()
}
}
private fun cancelWarmPassword() {
if (binding.fl1.tag == true) {
binding.fl1.background = ContextCompat.getDrawable(this, R.drawable.bg_border_pin)
binding.fl2.background = ContextCompat.getDrawable(this, R.drawable.bg_border_pin)
binding.fl3.background = ContextCompat.getDrawable(this, R.drawable.bg_border_pin)
binding.fl4.background = ContextCompat.getDrawable(this, R.drawable.bg_border_pin)
binding.edit1.setTextColor(ContextCompat.getColor(this, R.color.black))
binding.edit2.setTextColor(ContextCompat.getColor(this, R.color.black))
binding.edit3.setTextColor(ContextCompat.getColor(this, R.color.black))
binding.edit4.setTextColor(ContextCompat.getColor(this, R.color.black))
binding.fl1.tag = false
}
}
}
\ No newline at end of file
package com.base.datarecovery.activity.privacyspace
import android.graphics.Color
import android.view.KeyEvent
import android.view.View
import android.view.inputmethod.InputMethodManager
import androidx.activity.addCallback
import androidx.core.content.ContextCompat
import androidx.core.widget.addTextChangedListener
import androidx.lifecycle.lifecycleScope
import com.base.datarecovery.R
import com.base.datarecovery.bean.ConstObject.privacyPinPassword
import com.base.datarecovery.databinding.ActivityPrivacyPinTwoBinding
import com.base.datarecovery.help.BaseActivity
import com.base.datarecovery.help.KotlinExt.array2String
import com.base.datarecovery.utils.BarUtils
import com.base.datarecovery.utils.LogEx
import com.base.datarecovery.view.AsteriskPasswordTransformationMethod
import com.base.datarecovery.view.DialogViews.showPinReEnterDialog
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
class PrivacyPinTwoActivity : BaseActivity<ActivityPrivacyPinTwoBinding>() {
private var firstInput = "null"
private val secondInput = arrayOf("", "", "", "")
private val TAG = "PrivacyPinTwoActivity"
override val binding: ActivityPrivacyPinTwoBinding by lazy {
ActivityPrivacyPinTwoBinding.inflate(layoutInflater)
}
override fun initView() {
BarUtils.setStatusBarLightMode(this, true)
BarUtils.setStatusBarColor(this, Color.TRANSPARENT)
firstInput = intent.extras?.getString("FirstInput") ?: "null"
LogEx.logDebug(TAG, "firstInput=$firstInput")
}
override fun initListener() {
binding.flFanhui.setOnClickListener {
onBackPressedDispatcher.onBackPressed()
}
onBackPressedDispatcher.addCallback {
finish()
}
setInput()
setDeleteKey()
}
private fun hideInput() {
val imm = getSystemService(INPUT_METHOD_SERVICE) as InputMethodManager
imm.hideSoftInputFromWindow(binding.edit4.windowToken, 0)
binding.edit4.clearFocus()
}
private fun warmPassword() {
binding.fl1.tag = true
binding.fl1.background = ContextCompat.getDrawable(this, R.drawable.bg_border_pin_warm)
binding.fl2.background = ContextCompat.getDrawable(this, R.drawable.bg_border_pin_warm)
binding.fl3.background = ContextCompat.getDrawable(this, R.drawable.bg_border_pin_warm)
binding.fl4.background = ContextCompat.getDrawable(this, R.drawable.bg_border_pin_warm)
binding.edit1.setTextColor(ContextCompat.getColor(this, R.color.color_fa020b))
binding.edit2.setTextColor(ContextCompat.getColor(this, R.color.color_fa020b))
binding.edit3.setTextColor(ContextCompat.getColor(this, R.color.color_fa020b))
binding.edit4.setTextColor(ContextCompat.getColor(this, R.color.color_fa020b))
val dialog = showPinReEnterDialog()
lifecycleScope.launch {
delay(2000)
dialog.dismiss()
}
}
private fun cancelWarmPassword() {
if (binding.fl1.tag == true) {
binding.fl1.background = ContextCompat.getDrawable(this, R.drawable.bg_border_pin)
binding.fl2.background = ContextCompat.getDrawable(this, R.drawable.bg_border_pin)
binding.fl3.background = ContextCompat.getDrawable(this, R.drawable.bg_border_pin)
binding.fl4.background = ContextCompat.getDrawable(this, R.drawable.bg_border_pin)
binding.edit1.setTextColor(ContextCompat.getColor(this, R.color.black))
binding.edit2.setTextColor(ContextCompat.getColor(this, R.color.black))
binding.edit3.setTextColor(ContextCompat.getColor(this, R.color.black))
binding.edit4.setTextColor(ContextCompat.getColor(this, R.color.black))
binding.fl1.tag = false
}
}
private fun startJump() {
binding.llInput.visibility = View.GONE
binding.llRememberTip.visibility = View.VISIBLE
hideInput()
onBackPressedDispatcher.addCallback {
setResult(0, intent)
finish()
}
binding.flFanhui.setOnClickListener {
onBackPressedDispatcher.onBackPressed()
}
}
private fun setInput() {
binding.edit1.setOnFocusChangeListener { v, hasFocus ->
cancelWarmPassword()
binding.fl1.isSelected = hasFocus
}
binding.edit2.setOnFocusChangeListener { v, hasFocus ->
cancelWarmPassword()
binding.fl2.isSelected = hasFocus
}
binding.edit3.setOnFocusChangeListener { v, hasFocus ->
cancelWarmPassword()
binding.fl3.isSelected = hasFocus
}
binding.edit4.setOnFocusChangeListener { v, hasFocus ->
cancelWarmPassword()
binding.fl4.isSelected = hasFocus
}
binding.edit1.transformationMethod = AsteriskPasswordTransformationMethod()
binding.edit2.transformationMethod = AsteriskPasswordTransformationMethod()
binding.edit3.transformationMethod = AsteriskPasswordTransformationMethod()
binding.edit4.transformationMethod = AsteriskPasswordTransformationMethod()
binding.edit1.requestFocus()
binding.edit1.addTextChangedListener {
val text = it.toString()
LogEx.logDebug(TAG, "text=$text")
if (text.isNotEmpty()) {
secondInput[0] = text
binding.edit2.requestFocus()
}
}
binding.edit2.addTextChangedListener {
val text = it.toString()
LogEx.logDebug(TAG, "text=$text")
if (text.isNotEmpty()) {
secondInput[1] = text
binding.edit3.requestFocus()
}
}
binding.edit3.addTextChangedListener {
val text = it.toString()
LogEx.logDebug(TAG, "text=$text")
if (text.isNotEmpty()) {
secondInput[2] = text
binding.edit4.requestFocus()
}
}
binding.edit4.addTextChangedListener {
val text = it.toString()
LogEx.logDebug(TAG, "text=$text")
if (text.isNotEmpty()) {
secondInput[3] = text
if (firstInput == secondInput.array2String()) {
privacyPinPassword = firstInput
startJump()
} else {
warmPassword()
}
}
}
}
private fun setDeleteKey() {
arrayOf(binding.edit1, binding.edit2, binding.edit3, binding.edit4).forEach {
it.setOnKeyListener { v, actionId, event ->
if ((event.action == KeyEvent.ACTION_DOWN) && (event.keyCode == KeyEvent.KEYCODE_DEL)) {
if (binding.edit1.hasFocus()) {
binding.edit1.setText("")
}
if (binding.edit2.hasFocus()) {
binding.edit2.setText("")
binding.edit1.requestFocus()
}
if (binding.edit3.hasFocus()) {
binding.edit3.setText("")
binding.edit2.requestFocus()
}
if (binding.edit4.hasFocus()) {
binding.edit4.setText("")
binding.edit3.requestFocus()
}
true
} else {
false
}
}
}
}
}
\ No newline at end of file
package com.base.datarecovery.activity.privacyspace
import android.annotation.SuppressLint
import android.content.Intent
import android.graphics.Color
import android.view.View
import androidx.activity.addCallback
import androidx.fragment.app.Fragment
import androidx.viewpager2.adapter.FragmentStateAdapter
import androidx.viewpager2.widget.ViewPager2
import com.base.datarecovery.ads.AdmobMaxHelper
import com.base.datarecovery.ads.admob.AdmobInterstitialUtils
import com.base.datarecovery.bean.ConstObject.privacyPinPassword
import com.base.datarecovery.databinding.ActivityPrivacySpaceBinding
import com.base.datarecovery.fragment.PrivacyPageFragment
import com.base.datarecovery.help.BaseActivity
import com.base.datarecovery.utils.BarUtils
import com.base.datarecovery.view.DialogViews.showExitFunctionDialog
/**
* 隐私空间
*/
class PrivacySpaceActivity : BaseActivity<ActivityPrivacySpaceBinding>() {
override val binding: ActivityPrivacySpaceBinding by lazy {
ActivityPrivacySpaceBinding.inflate(layoutInflater)
}
private val photosFragment: PrivacyPageFragment by lazy {
PrivacyPageFragment("Photos")
}
private val videosFragment: PrivacyPageFragment by lazy {
PrivacyPageFragment("Videos")
}
private val fragments by lazy {
mutableListOf(photosFragment, videosFragment)
}
@SuppressLint("SetTextI18n")
override fun initView() {
BarUtils.setStatusBarLightMode(this, true)
BarUtils.setStatusBarColor(this, Color.TRANSPARENT)
binding.viewpager2.run {
adapter = object : FragmentStateAdapter(this@PrivacySpaceActivity) {
override fun getItemCount(): Int {
return fragments.size
}
override fun createFragment(position: Int): Fragment {
return fragments[position]
}
}
}
binding.viewpager2.registerOnPageChangeCallback(object :
ViewPager2.OnPageChangeCallback() {
override fun onPageSelected(position: Int) {
if (position == 0) {
binding.llPhotos.isSelected = true
binding.llVideos.isSelected = false
} else {
binding.llVideos.isSelected = true
binding.llPhotos.isSelected = false
}
}
})
if (privacyPinPassword.isEmpty()) {
binding.tvUninstallTip.setTextColor(Color.parseColor("#FAB44B"))
binding.tvGoOrSetting.text = "Setting"
} else {
binding.tvPinTip.visibility = View.GONE
binding.tvGoOrSetting.text = "Got it"
}
}
override fun initListener() {
super.initListener()
binding.tvGoOrSetting.setOnClickListener {
if (privacyPinPassword.isEmpty()) {
startActivity(Intent(this, PrivacyPinOneActivity::class.java))
} else {
binding.llTip.visibility = View.GONE
}
}
binding.llPhotos.setOnClickListener {
binding.viewpager2.currentItem = 0
}
binding.llVideos.setOnClickListener {
binding.viewpager2.currentItem = 1
}
onBackPressedDispatcher.addCallback {
AdmobMaxHelper.admobMaxShowInterstitialAd(this@PrivacySpaceActivity,false) {
finishToMain()
}
}
binding.flFanhui.setOnClickListener {
onBackPressedDispatcher.onBackPressed()
}
}
}
\ No newline at end of file
package com.base.datarecovery.activity.recovery
import android.annotation.SuppressLint
import android.graphics.Color
import android.os.Environment
import androidx.activity.addCallback
import com.base.datarecovery.R
import com.base.datarecovery.ads.AdmobMaxHelper
import com.base.datarecovery.bean.ConstObject.SCAN_AUDIOS
import com.base.datarecovery.bean.ConstObject.SCAN_DOCUMENTS
import com.base.datarecovery.bean.ConstObject.SCAN_PHOTOS
import com.base.datarecovery.bean.ConstObject.SCAN_VIDEOS
import com.base.datarecovery.databinding.ActivityFileRecoveredBinding
import com.base.datarecovery.help.BaseActivity
import com.base.datarecovery.utils.BarUtils
import java.io.File
class FileRecoveredActivity : BaseActivity<ActivityFileRecoveredBinding>() {
override val binding: ActivityFileRecoveredBinding by lazy {
ActivityFileRecoveredBinding.inflate(layoutInflater)
}
@SuppressLint("SetTextI18n")
override fun initView() {
BarUtils.setStatusBarLightMode(this, true)
BarUtils.setStatusBarColor(this, Color.TRANSPARENT)
val number = intent.extras?.getInt("Number") ?: 0
val scanType = intent.extras?.getInt("ScanType")
binding.tvNumber.text = number.toString()
val type = when (scanType) {
SCAN_PHOTOS -> if (number == 1) "Photo" else "Photos"
SCAN_VIDEOS -> if (number == 1) "Video" else "Videos"
SCAN_DOCUMENTS -> if (number == 1) "Document" else "Documents"
SCAN_AUDIOS -> if (number == 1) "Audio" else "Audios"
else -> ""
}
binding.tvType.text = type
val appName = this.resources.getString(R.string.app_name)
val appDir = File(Environment.getExternalStorageDirectory(), appName)
binding.tvDir.text = "Recovered in $appDir"
}
override fun initListener() {
binding.flFanhui.setOnClickListener {
onBackPressedDispatcher.onBackPressed()
}
onBackPressedDispatcher.addCallback {
AdmobMaxHelper.admobMaxShowInterstitialAd(this@FileRecoveredActivity,false) {
finishToMain()
}
}
binding.tvContinue.setOnClickListener {
AdmobMaxHelper.admobMaxShowInterstitialAd(this,false) {
finish()
}
}
binding.tvFinish.setOnClickListener {
onBackPressedDispatcher.onBackPressed()
}
}
}
\ No newline at end of file
package com.base.datarecovery.activity.recovery
import android.annotation.SuppressLint
import android.content.Intent
import android.graphics.Color
import android.os.Environment
import android.view.View
import androidx.activity.addCallback
import androidx.core.view.isVisible
import androidx.lifecycle.lifecycleScope
import com.base.datarecovery.adapter.FileFolderAdapter
import com.base.datarecovery.ads.AdmobMaxHelper
import com.base.datarecovery.bean.ConstObject.SCAN_AUDIOS
import com.base.datarecovery.bean.ConstObject.SCAN_DOCUMENTS
import com.base.datarecovery.bean.ConstObject.SCAN_PHOTOS
import com.base.datarecovery.bean.ConstObject.SCAN_VIDEOS
import com.base.datarecovery.bean.FolderBean
import com.base.datarecovery.bean.RecoveryBean
import com.base.datarecovery.bean.RecoveryBean.Companion.setAudioType
import com.base.datarecovery.bean.RecoveryBean.Companion.setType
import com.base.datarecovery.databinding.ActivityFileScanResultBinding
import com.base.datarecovery.help.BaseActivity
import com.base.datarecovery.help.FileHelp.loadFileByFilter
import com.base.datarecovery.help.PermissionHelp.checkStorePermission
import com.base.datarecovery.help.PermissionHelp.requestStorePermission
import com.base.datarecovery.utils.BarUtils
import com.base.datarecovery.utils.FileHexEx
import com.base.datarecovery.utils.LogEx
import com.base.datarecovery.view.DialogViews.showExitFunctionDialog
import com.base.datarecovery.view.DialogViews.showGerPermission
import com.base.datarecovery.view.FileScanDialog
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.channels.BufferOverflow
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.SharedFlow
import kotlinx.coroutines.launch
import java.io.File
class FileScanResultActivity : BaseActivity<ActivityFileScanResultBinding>() {
private val TAG = "FileScanResultActivity"
private var scanOnce: Boolean = false
private var scanType = 0
override val binding: ActivityFileScanResultBinding by lazy {
ActivityFileScanResultBinding.inflate(layoutInflater)
}
private lateinit var fileFolderAdapter: FileFolderAdapter
@SuppressLint("SetTextI18n")
override fun initView() {
BarUtils.setStatusBarLightMode(this, true)
BarUtils.setStatusBarColor(this, Color.TRANSPARENT)
scanType = intent.extras?.getInt("ScanType") ?: 0
LogEx.logDebug(TAG, "scanType=$scanType")
when (scanType) {
SCAN_PHOTOS -> {
binding.tvTittle.text = "Photo Recovery"
binding.tvFileType.text = "Photos"
}
SCAN_DOCUMENTS -> {
binding.tvTittle.text = "Document Recovery"
binding.tvFileType.text = "Documents"
}
SCAN_VIDEOS -> {
binding.tvTittle.text = "Video Recovery"
binding.tvFileType.text = "videos"
}
SCAN_AUDIOS -> {
binding.tvTittle.text = "Audio Recovery"
binding.tvFileType.text = "Audios"
}
}
fileFolderAdapter = FileFolderAdapter(scanType) { folderBean ->
FileRecoveryActivity.folderBean = null
FileRecoveryActivity.folderBean = folderBean
startActivity(Intent(this, FileRecoveryActivity::class.java).apply {
putExtra("ScanType", scanType)
})
}
binding.rv.adapter = fileFolderAdapter
if (checkStorePermission()) {
beginScan()
} else {
showGerPermission(
desc = "In order to scan and recover your lost photos, videos, and documents, we need to access your device storage. This permission is essential for our app to locate and restore your files.",
deny = { finishToMain() }) {
requestStorePermission(launcher, result = {
if (it) beginScan() else finishToMain()
})
}
}
}
override fun initListener() {
binding.flFanhui.setOnClickListener {
onBackPressedDispatcher.onBackPressed()
}
onBackPressedDispatcher.addCallback {
showExitFunctionDialog {
if (it) {
AdmobMaxHelper.admobMaxShowInterstitialAd(this@FileScanResultActivity,false) {
finishToMain()
}
} else {
finishToMain()
}
}
}
}
@SuppressLint("SetTextI18n")
private fun beginScan() {
if (scanOnce)
return
scanOnce = true
//变量路径
val mPathFlow = MutableSharedFlow<String>(
replay = 5,//当新的订阅者Collect时,发送几个已经发送过的数据给它
extraBufferCapacity = 5,//减去replay,MutableSharedFlow还缓存多少数据,缓冲池容量 = replay + extraBufferCapacity
onBufferOverflow = BufferOverflow.DROP_OLDEST//缓存策略,三种 丢掉最新值、丢掉最旧值和挂起
)
val pathFlow: SharedFlow<String> = mPathFlow
//符合条件Flow
val mFoundFlow = MutableSharedFlow<Pair<Int, String>>(
replay = 5,
extraBufferCapacity = 5,
onBufferOverflow = BufferOverflow.SUSPEND
)
val foundFlow: SharedFlow<Pair<Int, String>> = mFoundFlow
val dialogClass = FileScanDialog(this)
val scanDialog = dialogClass.showFileScanDialog(pathFlow, foundFlow) {
pathList.add(it)
binding.tvFileNumber.text = pathList.size.toString()
setAdapterData(it)
}
val filter = when (scanType) {
SCAN_PHOTOS -> FileHexEx::isImage
SCAN_DOCUMENTS -> FileHexEx::isDocument
SCAN_VIDEOS -> FileHexEx::isVideo
SCAN_AUDIOS -> FileHexEx::isAudio
else -> FileHexEx::isImage
}
val root = Environment.getExternalStorageDirectory()
lifecycleScope.loadFileByFilter(
mPathFlow,
mFoundFlow,
root, filter = filter,
onDo = { file ->
LogEx.logDebug(TAG, "file =${file.absolutePath}")
},
onFinish = {
lifecycleScope.launch(Dispatchers.Main) {
binding.ivWancheng.visibility = View.VISIBLE
binding.tvScanning.text = "Completed"
lifecycleScope.launchWhenResumed {
AdmobMaxHelper.admobMaxShowInterstitialAd(this@FileScanResultActivity,false)
}
binding.flEmpty.isVisible = pathList.isEmpty()
setAdapterData(null)
dialogClass.finishScan {
scanDialog.dismiss()
}
}
}
)
}
private val pathList = ArrayList<String>()
private val hashMap = HashMap<String, ArrayList<RecoveryBean>>()
private fun setAdapterData(path: String? = null) {
val flag = path?.let { addMapFolder(it) } ?: true
if (flag) {
binding.tvFolderNumber.text = hashMap.keys.size.toString()
val mapList = hashMap.map {
FolderBean(it.key, recoveryList = it.value)
}
fileFolderAdapter.setData(mapList)
}
}
private fun addMapFolder(path: String): Boolean {
var isNewFolder = false
val folder = File(path).parent?.split("/")?.last() ?: ""
if (hashMap[folder] == null) {
hashMap[folder] = arrayListOf()
isNewFolder = true
}
val recoveryBean = RecoveryBean(path)
if (scanType == SCAN_DOCUMENTS) {
recoveryBean.setType()
}
if (scanType == SCAN_AUDIOS) {
recoveryBean.setAudioType()
}
hashMap[folder]?.add(recoveryBean)
return isNewFolder
}
}
\ No newline at end of file
package com.base.datarecovery.activity.repeat
import android.content.Context
import android.graphics.Color
import android.view.View
import androidx.activity.addCallback
import androidx.core.view.isVisible
import androidx.core.view.updatePadding
import androidx.lifecycle.lifecycleScope
import com.base.datarecovery.adapter.MediaAdapter
import com.base.datarecovery.ads.AdmobMaxHelper
import com.base.datarecovery.ads.AdmobMaxHelper.showDeleteAdSp
import com.base.datarecovery.bean.MediaBean
import com.base.datarecovery.bean.MediaTimeBean
import com.base.datarecovery.databinding.ActivityRepeatBinding
import com.base.datarecovery.help.BaseActivity
import com.base.datarecovery.help.ConfigHelper
import com.base.datarecovery.help.KotlinExt.toFormatSize
import com.base.datarecovery.help.KotlinExt.toFormatTime
import com.base.datarecovery.help.MediaStoreHelp.getImageMedia
import com.base.datarecovery.help.PermissionHelp.checkStorePermission
import com.base.datarecovery.help.PermissionHelp.requestStorePermission
import com.base.datarecovery.utils.BarUtils
import com.base.datarecovery.utils.SimilarHelper.calculateSimilar
import com.base.datarecovery.view.DialogViews.showDeletePermanentlyDialog
import com.base.datarecovery.view.DialogViews.showExitFunctionDialog
import com.base.datarecovery.view.DialogViews.showGerPermission
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import java.io.File
class RepeatActivity : BaseActivity<ActivityRepeatBinding>() {
override val binding: ActivityRepeatBinding by lazy {
ActivityRepeatBinding.inflate(layoutInflater)
}
private lateinit var mediaAdapter: MediaAdapter
override fun initView() {
BarUtils.setStatusBarLightMode(this, true)
BarUtils.setStatusBarColor(this, Color.TRANSPARENT)
binding.root.updatePadding(top = BarUtils.getStatusBarHeight())
mediaAdapter = MediaAdapter {
binding.ivSelectAll.isSelected = it.first
binding.tvClean.isEnabled = it.second.isNotEmpty()
runCatching {
val split = it.second.sumOf { bean -> File(bean.path).length() }.toFormatSize().split(" ")
binding.tvSize.text = split[0]
binding.tvUnit.text = split[1]
}
}
binding.rv.adapter = mediaAdapter
if (beanList.isNotEmpty()) {
binding.progressBar.visibility = View.GONE
binding.tvScanning.visibility = View.GONE
binding.flScanning.visibility = View.GONE
mediaAdapter.setData(beanList)
}
if (checkStorePermission()) {
initData()
} else {
showGerPermission("Storage Permission Required",
"This feature requires access to your storage to scan your files and clean up repeat photos. We will not transmit your data to any third-party service. Please grant permission so that we can provide you with better service.",
deny = { finishToMain() },
allow = {
requestStorePermission(launcher, result = {
if (it) {
initData()
} else {
finishToMain()
}
})
})
}
}
override fun initListener() {
onBackPressedDispatcher.addCallback {
showExitFunctionDialog {
if (it) {
AdmobMaxHelper.admobMaxShowInterstitialAd(this@RepeatActivity,false) {
finishToMain()
}
} else {
finishToMain()
}
}
}
binding.flBack.setOnClickListener {
onBackPressedDispatcher.onBackPressed()
}
binding.llSelectAll.setOnClickListener {
binding.ivSelectAll.isSelected = !binding.ivSelectAll.isSelected
mediaAdapter.toggleAllSelect(binding.ivSelectAll.isSelected)
}
binding.tvClean.setOnClickListener {
showDeletePermanentlyDialog {
if (showDeleteAdSp()) {
AdmobMaxHelper.admobMaxShowInterstitialAd(this@RepeatActivity,false) {
cleanFile()
}
} else {
cleanFile()
}
}
}
}
private fun cleanFile() {
lifecycleScope.launch(Dispatchers.IO) {
val cleanList = mediaAdapter.getSelectData().second
cleanList.forEach {
runCatching {
val file = File(it.path)
file.delete()
}
}
launch(Dispatchers.Main) {
mediaAdapter.removeData(cleanList)
if (mediaAdapter.isEmptyData()) {
binding.llEmpty.isVisible = true
binding.tvSize.text = "0.0"
binding.tvUnit.text = "B"
}
}
}
}
private fun initData() {
if (beanList.isNotEmpty()) {
return
}
lifecycleScope.launch(Dispatchers.IO) {
val beanList = getSimilarList(this@RepeatActivity)
launch(Dispatchers.Main) {
binding.progressBar.visibility = View.GONE
binding.tvScanning.visibility = View.GONE
binding.flScanning.visibility = View.GONE
mediaAdapter.setData(beanList)
if (beanList.sumOf { it.beans.size } > 6 || ConfigHelper.mustShowNativeAd) {
AdmobMaxHelper.admobMaxShowNativeAd(this@RepeatActivity, binding.flAd)
}
binding.llEmpty.isVisible = beanList.isEmpty()
}
}
}
companion object {
var beanList: ArrayList<MediaTimeBean> = ArrayList()
fun getSimilarList(context: Context): List<MediaTimeBean> {
val list = arrayListOf<MediaBean>()
context.getImageMedia(list)
val similarList = calculateSimilar(list)
val beanList = similarList.map { map ->
val time = File(map.key).lastModified().toFormatTime()
MediaTimeBean(time = time, beans = map.value)
}
return beanList
}
}
override fun onDestroy() {
super.onDestroy()
beanList.clear()
}
}
\ No newline at end of file
package com.base.datarecovery.activity.repeat
import android.content.Intent
import android.widget.Toast
import androidx.activity.addCallback
import androidx.lifecycle.lifecycleScope
import com.base.datarecovery.ads.AdmobMaxHelper
import com.base.datarecovery.databinding.ActivityRepeatAnimationBinding
import com.base.datarecovery.help.BaseActivity
import kotlinx.coroutines.Job
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlin.random.Random
class RepeatAnimationActivity : BaseActivity<ActivityRepeatAnimationBinding>() {
override val binding: ActivityRepeatAnimationBinding by lazy {
ActivityRepeatAnimationBinding.inflate(layoutInflater)
}
private var job: Job? = null
override fun initView() {
}
override fun initListener() {
onBackPressedDispatcher.addCallback {
Toast.makeText(this@RepeatAnimationActivity, "wait a moment", Toast.LENGTH_SHORT).show()
}
}
fun jumpJob() = lifecycleScope.launch {
delay(Random.nextLong(3000, 4500))
AdmobMaxHelper.admobMaxShowInterstitialAd(this@RepeatAnimationActivity) {
startActivity(Intent(this@RepeatAnimationActivity, RepeatActivity::class.java))
finish()
}
}
override fun onResume() {
super.onResume()
if (job?.isActive == false || job == null) {
job = jumpJob()
}
}
override fun onPause() {
super.onPause()
job?.cancel()
job = null
}
}
\ No newline at end of file
package com.base.datarecovery.activity.screenshot
import android.content.Intent
import android.widget.Toast
import androidx.activity.addCallback
import androidx.lifecycle.lifecycleScope
import com.base.datarecovery.ads.AdmobMaxHelper
import com.base.datarecovery.ads.admob.AdmobInterstitialUtils
import com.base.datarecovery.databinding.ActivityScreenShotAnimationBinding
import com.base.datarecovery.help.BaseActivity
import com.base.datarecovery.utils.LogEx
import kotlinx.coroutines.Job
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlin.random.Random
class ScreenShotAnimationActivity : BaseActivity<ActivityScreenShotAnimationBinding>() {
override val binding: ActivityScreenShotAnimationBinding by lazy {
ActivityScreenShotAnimationBinding.inflate(layoutInflater)
}
private var job: Job? = null
override fun initView() {
}
fun jumpJob() = lifecycleScope.launch {
delay(Random.nextLong(3000, 4500))
AdmobMaxHelper.admobMaxShowInterstitialAd(this@ScreenShotAnimationActivity) {
startActivity(Intent(this@ScreenShotAnimationActivity, ScreenShotActivity::class.java))
finish()
}
}
override fun initListener() {
super.initListener()
onBackPressedDispatcher.addCallback {
Toast.makeText(this@ScreenShotAnimationActivity, "wait a moment", Toast.LENGTH_SHORT).show()
}
}
override fun onResume() {
super.onResume()
if (job?.isActive == false || job == null) {
job = jumpJob()
}
}
override fun onPause() {
super.onPause()
job?.cancel()
job = null
}
}
\ No newline at end of file
package com.base.datarecovery.adapter
import android.annotation.SuppressLint
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import com.base.datarecovery.R
import com.base.datarecovery.bean.AppBean
import com.base.datarecovery.databinding.ItemAppManagerBinding
import com.base.datarecovery.view.XmlEx.inflate
class AppManagerAdapter(val isUninstall: Boolean = false, val click: (packageName: String) -> Unit) :
RecyclerView.Adapter<AppManagerAdapter.AAA>() {
private val beanList = arrayListOf<AppBean>()
class AAA(view: View) : RecyclerView.ViewHolder(view)
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): AAA {
return AAA(R.layout.item_app_manager.inflate(parent))
}
override fun getItemCount(): Int {
return beanList.size
}
@SuppressLint("SetTextI18n")
override fun onBindViewHolder(holder: AAA, position: Int) {
val binding = ItemAppManagerBinding.bind(holder.itemView)
val bean = beanList[position]
binding.iv.setImageDrawable(bean.icon)
binding.tvName.text = bean.name
binding.tvStop.setOnClickListener {
click.invoke(bean.packageName)
}
if (isUninstall) {
binding.tvStop.text = "UnInstall"
}
}
@SuppressLint("NotifyDataSetChanged")
fun setData(list: List<AppBean>) {
beanList.clear()
beanList.addAll(list)
notifyDataSetChanged()
}
@SuppressLint("NotifyDataSetChanged")
fun removeBean(pkg: String) {
beanList.removeIf { it.packageName == pkg }
notifyDataSetChanged()
}
}
\ No newline at end of file
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
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