Commit 227c7d67 authored by wanglei's avatar wanglei

初始化

parent 9169f94b
Pipeline #1202 failed with stages
*.iml
.gradle
/local.properties
.idea
/.idea/caches
/.idea/libraries
/.idea/modules.xml
/.idea/workspace.xml
/.idea/navEditor.xml
/.idea/assetWizardSettings.xml
.DS_Store
/build
/captures
.externalNativeBuild
.cxx
local.properties
# File Recovery RecycleBin
# Data Recovery RecycleBin
文件恢复包结合回收站功能
\ No newline at end of file
文件恢复类型应用+回收站功能
\ No newline at end of file
/build
\ No newline at end of file
plugins {
alias(libs.plugins.androidApplication)
alias(libs.plugins.jetbrainsKotlinAndroid)
id 'com.google.gms.google-services'
id 'com.google.firebase.crashlytics'
}
android {
namespace 'com.base.filerecoveryrecyclebin'
compileSdk 34
defaultConfig {
applicationId "com.base.filerecoveryrecyclebin"
minSdk 24
targetSdk 34
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
setProperty("archivesBaseName", "FileRecovery-v${versionName}")
}
signingConfigs {
realse {
storeFile file('../smartcl.jks')
storePassword '123456'
keyAlias 'key0'
keyPassword '123456'
}
}
buildTypes {
release {
minifyEnabled true
shrinkResources true
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
signingConfig signingConfigs.realse
// 设置是否要自动上传
firebaseCrashlytics {
mappingFileUploadEnabled true
}
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = '1.8'
}
buildFeatures {
viewBinding true
buildConfig true
aidl true
}
}
gradle.taskGraph.whenReady {
tasks.each { task ->
if (task.name.contains("uploadCrashlyticsMappingFile")) {
task.enabled = false
}
}
}
dependencies {
implementation libs.androidx.core.ktx
implementation libs.androidx.appcompat
implementation libs.material
implementation libs.androidx.activity
implementation libs.androidx.constraintlayout
testImplementation libs.junit
androidTestImplementation libs.androidx.junit
androidTestImplementation libs.androidx.espresso.core
def lifecycle_version = "2.7.0"
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version"
//第三方UI
implementation("com.airbnb.android:lottie:6.4.0")
implementation("com.github.bumptech.glide:glide:4.16.0")
implementation("com.github.pokercc:ExpandableRecyclerView:0.9.3")
//网络
implementation group: 'com.google.code.gson', name: 'gson', version: '2.10.1'
//facebook
implementation("com.facebook.android:facebook-android-sdk:[8,9)")
//广告
implementation("com.google.android.gms:play-services-ads:23.1.0")
implementation 'com.google.ads.mediation:applovin:12.4.3.0'
implementation 'com.google.ads.mediation:facebook:6.17.0.0'
implementation 'com.google.ads.mediation:mintegral:16.7.21.0'
implementation 'com.google.ads.mediation:pangle:5.9.0.4.0'
//Google UMP
//https://docs.tradplusad.com/docs/tradplussdk_android_doc_v6/privacy_policy/google_ump/
implementation 'com.google.android.ump:user-messaging-platform:2.1.0'
//firebase
implementation platform('com.google.firebase:firebase-bom:32.3.1')
implementation 'com.google.firebase:firebase-analytics:21.6.2'
implementation("com.google.firebase:firebase-messaging")
//max adjust 归因
implementation 'com.adjust.sdk:adjust-android:4.28.7'
}
\ No newline at end of file
{
"project_info": {
"project_number": "286059626470",
"project_id": "datarecovery-1311d",
"storage_bucket": "datarecovery-1311d.appspot.com"
},
"client": [
{
"client_info": {
"mobilesdk_app_id": "1:286059626470:android:561dad747cfdbdaac0e027",
"android_client_info": {
"package_name": "com.base.filerecoveryrecyclebin"
}
},
"oauth_client": [],
"api_key": [
{
"current_key": "AIzaSyDNejLop3k-9tsbSwYe7vhhkq8AW_L2T20"
}
],
"services": {
"appinvite_service": {
"other_platform_oauth_client": []
}
}
}
],
"configuration_version": "1"
}
\ No newline at end of file
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile
-dontwarn javax.annotation.Nullable
-keep class com.base.filerecoveryrecyclebin.bean.** {*;}
-dontoptimize
-keepattributes Signature
-keep class com.squareup.okhttp.** { *; }
-keep class com.google.gson.stream.** { *; }
-keep class com.google.gson.** { *; }
-keep class com.alibaba.idst.** { *; }
-keep class * extends com.google.gson.reflect.TypeToken
-keepattributes AnnotationDefault, RuntimeVisibleAnnotations
-keep class com.bytedance.sdk.** { *; }
package com.base.filerecoveryrecyclebin
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.ext.junit.runners.AndroidJUnit4
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.Assert.*
/**
* Instrumented test, which will execute on an Android device.
*
* See [testing documentation](http://d.android.com/tools/testing).
*/
@RunWith(AndroidJUnit4::class)
class ExampleInstrumentedTest {
@Test
fun useAppContext() {
// Context of the app under test.
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
assertEquals("com.base.datarecovery", appContext.packageName)
}
}
\ No newline at end of file
This diff is collapsed.
{"v":"5.9.2","fr":30,"ip":0,"op":60,"w":1020,"h":1020,"nm":"合成 1","ddd":0,"assets":[{"id":"image_0","w":270,"h":270,"u":"images/","p":"img_0.png","e":0},{"id":"image_1","w":729,"h":729,"u":"images/","p":"img_1.png","e":0},{"id":"comp_0","nm":"外圆","fr":30,"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"形状图层 6","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[10],"e":[0]},{"t":40}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[510,510,0],"ix":2,"l":2},"a":{"a":0,"k":[-23.227,28.773,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":0,"s":[100,100,100],"e":[140,140,100]},{"t":40}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[729,729],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"椭圆路径 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[0.16470600203,0.431372997808,0.96470600203,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"描边 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[-23.227,28.773],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"变换"}],"nm":"椭圆 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":40,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"形状图层 7","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":20,"s":[10],"e":[0]},{"t":60}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[510,510,0],"ix":2,"l":2},"a":{"a":0,"k":[-23.227,28.773,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":20,"s":[100,100,100],"e":[140,140,100]},{"t":60}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[729,729],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"椭圆路径 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[0.16470600203,0.431372997808,0.96470600203,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"描边 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[-23.227,28.773],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"变换"}],"nm":"椭圆 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":20,"op":60,"st":20,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"形状图层 8","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":40,"s":[10],"e":[0]},{"t":80}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[510,510,0],"ix":2,"l":2},"a":{"a":0,"k":[-23.227,28.773,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":40,"s":[100,100,100],"e":[140,140,100]},{"t":80}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[729,729],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"椭圆路径 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[0.16470600203,0.431372997808,0.96470600203,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"描边 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[-23.227,28.773],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"变换"}],"nm":"椭圆 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":40,"op":80,"st":40,"bm":0}]}],"layers":[{"ddd":0,"ind":1,"ty":2,"nm":"Group 153.png","cl":"png","refId":"image_0","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[510,510,0],"ix":2,"l":2},"a":{"a":0,"k":[135,135,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"ip":0,"op":60,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":2,"nm":"Group 159.png","cl":"png","refId":"image_1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[0],"e":[360]},{"t":60}],"ix":10},"p":{"a":0,"k":[510,510,0],"ix":2,"l":2},"a":{"a":0,"k":[364.5,364.5,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"ip":0,"op":60,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":0,"nm":"外圆","refId":"comp_0","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[510,510,0],"ix":2,"l":2},"a":{"a":0,"k":[510,510,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"w":1020,"h":1020,"ip":0,"op":20,"st":-20,"bm":0},{"ddd":0,"ind":4,"ty":0,"nm":"外圆","refId":"comp_0","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[510,510,0],"ix":2,"l":2},"a":{"a":0,"k":[510,510,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"w":1020,"h":1020,"ip":20,"op":40,"st":0,"bm":0},{"ddd":0,"ind":5,"ty":0,"nm":"外圆","refId":"comp_0","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[510,510,0],"ix":2,"l":2},"a":{"a":0,"k":[510,510,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"w":1020,"h":1020,"ip":40,"op":60,"st":20,"bm":0}],"markers":[]}
\ No newline at end of file
{"v":"5.5.7","fr":60,"ip":0,"op":60,"w":640,"h":140,"nm":"合成 1","ddd":0,"assets":[{"id":"image_0","w":157,"h":175,"u":"images/","p":"img_0.png","e":0},{"id":"image_1","w":86,"h":50,"u":"images/","p":"img_1.png","e":0},{"id":"image_2","w":540,"h":114,"u":"images/","p":"img_2.png","e":0}],"layers":[{"ddd":0,"ind":1,"ty":2,"nm":"bu.png","cl":"png","refId":"image_0","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":0,"s":[518,94.5,0],"to":[5.917,0,0],"ti":[-5.917,0,0]},{"t":30,"s":[553.5,94.5,0]}],"ix":2},"a":{"a":0,"k":[78.5,87.5,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"ip":0,"op":60,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":2,"nm":"on.png","cl":"png","refId":"image_1","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":22,"s":[0]},{"t":31,"s":[100]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[502.25,57.5,0],"ix":2},"a":{"a":0,"k":[43,25,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"ip":0,"op":60,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":2,"nm":"qx2.png","cl":"png","refId":"image_2","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[320,57,0],"ix":2},"a":{"a":0,"k":[270,57,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"ip":0,"op":60,"st":0,"bm":0}],"markers":[]}
\ No newline at end of file
package com.base.filerecoveryrecyclebin
import android.annotation.SuppressLint
import android.app.Activity
import android.content.Intent
import android.os.Bundle
import com.base.filerecoveryrecyclebin.activity.SplashActivity
import com.base.filerecoveryrecyclebin.ads.admob.AdmobOpenUtils
import com.base.filerecoveryrecyclebin.bean.ConstObject.ifAgreePrivacy
import com.base.filerecoveryrecyclebin.fcm.FCMManager
import com.base.filerecoveryrecyclebin.fcm.RecoveryTimerManager
import com.base.filerecoveryrecyclebin.fcm.ScreenStatusReceiver
import com.base.filerecoveryrecyclebin.help.BaseApplication
import com.base.filerecoveryrecyclebin.help.ConfigHelper
import com.base.filerecoveryrecyclebin.utils.ActivityManagerUtils
import com.base.filerecoveryrecyclebin.utils.AppPreferences
import com.base.filerecoveryrecyclebin.utils.InstallHelps
import com.base.filerecoveryrecyclebin.utils.LogEx
import com.base.filerecoveryrecyclebin.utils.NewComUtils
import com.google.android.gms.ads.MobileAds
class MyApplication : BaseApplication() {
private val TAG = "MyApplication"
companion object {
@JvmField
var PAUSED_VALUE = 0
}
override fun init() {
initApp()
}
fun initApp() {
val topic = ConfigHelper.packageName + "_push"
FCMManager.initFirebase(this)
FCMManager.subscribeToTopic(topic)
LogEx.logDebug(TAG, "topic=${topic}")
NewComUtils.requestCfg {
ScreenStatusReceiver.setupScreenStatusListener(this)
val timerStatus: Int = AppPreferences.getInstance().getString("timerS", "1").toIntOrNull() ?: 1
if (timerStatus == 0) {
RecoveryTimerManager.getInstance().stopTaskTimer()
} else {
val timerDelay: Int = AppPreferences.getInstance().getString("timerDelay", "1").toIntOrNull() ?: 1
val timerInterval: Int = AppPreferences.getInstance().getString("timerInterval", "7").toIntOrNull() ?: 7
if (!RecoveryTimerManager.getInstance().isTaskTimerActive) {
RecoveryTimerManager.getInstance().scheduleTask(
(timerDelay * 60 * 1000).toLong(),
(timerInterval * 60 * 1000).toLong()
)
}
}
}
if (ifAgreePrivacy) {
InstallHelps.init()
MobileAds.initialize(this) { initializationStatus ->
}
}
initLifeListener()
}
private var lastTimePause = 0L
private var lastTimeResume = 0L
private fun isHotLaunch(): Boolean {
if ((lastTimeResume - lastTimePause) > 1000) {
return true
}
return false
}
@SuppressLint("UnspecifiedRegisterReceiverFlag")
private fun initLifeListener() {
registerActivityLifecycleCallbacks(object : ActivityLifecycleCallbacks {
private var count = 0
override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) {}
override fun onActivityStarted(activity: Activity) {
count++
lastTimeResume = System.currentTimeMillis()
if (count == 1 && isHotLaunch()) {
val topActivity: Activity? = ActivityManagerUtils.getInstance().topActivity
val flag = if (topActivity == null) {
true
} else {
ConfigHelper.noLoadingActivities.all { !topActivity.localClassName.contains(it, true) }
}
LogEx.logDebug(TAG, "flag=$flag")
if (flag) {
if (AdmobOpenUtils.isOpenAdLoaded()) {
AdmobOpenUtils.showAppOpenAd(activity)
} else {
topActivity?.startActivity(
Intent(
topActivity,
SplashActivity::class.java
).apply {
putExtra("isHotLaunch", true)
putExtra("type", -1)
})
}
}
}
}
override fun onActivityResumed(activity: Activity) {
PAUSED_VALUE = 1
}
override fun onActivityPaused(activity: Activity) {
PAUSED_VALUE = 2
lastTimePause = System.currentTimeMillis()
}
override fun onActivityStopped(activity: Activity) {
count--
}
override fun onActivitySaveInstanceState(activity: Activity, outState: Bundle) {}
override fun onActivityDestroyed(activity: Activity) {}
})
}
}
\ No newline at end of file
package com.base.filerecoveryrecyclebin.activity
import android.graphics.Color
import androidx.lifecycle.lifecycleScope
import com.base.filerecoveryrecyclebin.databinding.ActivityMainBinding
import com.base.filerecoveryrecyclebin.help.BaseActivity
import com.base.filerecoveryrecyclebin.help.PermissionHelp.checkStorePermission
import com.base.filerecoveryrecyclebin.utils.BarUtils
import com.base.filerecoveryrecyclebin.view.PermissionDialog.showPermissionBottomSheet
import com.base.filerecoveryrecyclebin.view.RateStarPop.showRateStarPopDialog
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
class MainActivity : BaseActivity<ActivityMainBinding>() {
override val binding: ActivityMainBinding by lazy {
ActivityMainBinding.inflate(layoutInflater)
}
override fun initView() {
BarUtils.setStatusBarLightMode(this, true)
BarUtils.setStatusBarColor(this, Color.TRANSPARENT)
// binding.root.updatePadding(top = BarUtils.getStatusBarHeight())
// showRateStarPopDialog()
// showExitFunctionDialog()
}
override fun onResume() {
super.onResume()
if (!checkStorePermission()) {
if (dialog == null) {
dialog = showPermissionBottomSheet(launcher)
}
}
lifecycleScope.launch(Dispatchers.Main) {
delay(1000)
showRateStarPopDialog()
}
}
}
\ No newline at end of file
package com.base.filerecoveryrecyclebin.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.filerecoveryrecyclebin.R
import com.base.filerecoveryrecyclebin.activity.junkclean.ScanJunkActivity
import com.base.filerecoveryrecyclebin.activity.privacyspace.PrivacyPinOneActivity
import com.base.filerecoveryrecyclebin.activity.privacyspace.PrivacySpaceActivity
import com.base.filerecoveryrecyclebin.activity.recovery.FileScanResultActivity
import com.base.filerecoveryrecyclebin.activity.repeat.RepeatAnimationActivity
import com.base.filerecoveryrecyclebin.activity.screenshot.ScreenShotAnimationActivity
import com.base.filerecoveryrecyclebin.adapter.AppFunctionAdapter
import com.base.filerecoveryrecyclebin.ads.admob.AdmobInterstitialUtils
import com.base.filerecoveryrecyclebin.ads.admob.AdmobNativeUtils
import com.base.filerecoveryrecyclebin.bean.ConstObject.JUNK_CLEANER
import com.base.filerecoveryrecyclebin.bean.ConstObject.PRIVACY_SPACE
import com.base.filerecoveryrecyclebin.bean.ConstObject.RECOVERY_DOCUMENTS
import com.base.filerecoveryrecyclebin.bean.ConstObject.RECOVERY_PHOTOS
import com.base.filerecoveryrecyclebin.bean.ConstObject.RECOVERY_VIDEOS
import com.base.filerecoveryrecyclebin.bean.ConstObject.REPEAT_PHOTOS
import com.base.filerecoveryrecyclebin.bean.ConstObject.SCAN_DOCUMENTS
import com.base.filerecoveryrecyclebin.bean.ConstObject.SCAN_PHOTOS
import com.base.filerecoveryrecyclebin.bean.ConstObject.SCAN_VIDEOS
import com.base.filerecoveryrecyclebin.bean.ConstObject.SCREENSHOT_CLEANER
import com.base.filerecoveryrecyclebin.bean.ConstObject.privacyPinPassword
import com.base.filerecoveryrecyclebin.databinding.ActivityLayoutResultBinding
import com.base.filerecoveryrecyclebin.help.BaseActivity
import com.base.filerecoveryrecyclebin.help.KotlinExt.toFormatSize
import com.base.filerecoveryrecyclebin.utils.BarUtils
import com.base.filerecoveryrecyclebin.utils.AppPreferences
class ResultActivity : BaseActivity<ActivityLayoutResultBinding>() {
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)
})
}
PRIVACY_SPACE -> {
if (privacyPinPassword.isNotEmpty()) {
startActivity(Intent(this, PrivacyPinOneActivity::class.java))
} else {
startActivity(Intent(this, PrivacySpaceActivity::class.java).apply {
})
}
}
}
finish()
}
binding.rvFun.adapter = adapter
adapter.updateListPosition()
adapter.notifyDataSetChanged()
val from = intent.getStringExtra("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 -> {}
}
from?.let {
binding.tvTitle.text = it
adapter.removeItem(it)
}
onBackPressedDispatcher.addCallback(object : OnBackPressedCallback(true) {
override fun handleOnBackPressed() {
AdmobInterstitialUtils.showInterstitialAd(this@ResultActivity) {
finishToMain()
}
}
})
binding.ivBack.setOnClickListener {
onBackPressedDispatcher.onBackPressed()
}
AdmobNativeUtils.showNativeAd(this@ResultActivity, binding.flAd, R.layout.layout_native_custom_white)
}
override fun onDestroy() {
super.onDestroy()
binding.icSuccess.clearAnimation()
}
}
\ No newline at end of file
package com.base.filerecoveryrecyclebin.activity
import android.content.Intent
import android.graphics.Color
import android.net.Uri
import androidx.activity.addCallback
import com.base.filerecoveryrecyclebin.bean.ConstObject.fcmNotification
import com.base.filerecoveryrecyclebin.bean.ConstObject.stayNotification
import com.base.filerecoveryrecyclebin.databinding.ActivitySettingBinding
import com.base.filerecoveryrecyclebin.fcm.FCMManager.subscribeToTopic
import com.base.filerecoveryrecyclebin.fcm.FCMManager.unsubscribeFromTopic
import com.base.filerecoveryrecyclebin.help.BaseActivity
import com.base.filerecoveryrecyclebin.help.ConfigHelper
import com.base.filerecoveryrecyclebin.service.StayNotificationService
import com.base.filerecoveryrecyclebin.service.StayNotificationService.Companion.startStayNotification
import com.base.filerecoveryrecyclebin.utils.BarUtils
import com.base.filerecoveryrecyclebin.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()
} else {
val serviceIntent = Intent(this, StayNotificationService::class.java)
stopService(serviceIntent)
}
}
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)
}
}
}
\ No newline at end of file
package com.base.filerecoveryrecyclebin.activity
import android.annotation.SuppressLint
import android.content.Intent
import android.graphics.Color
import android.net.Uri
import android.text.SpannableString
import android.text.Spanned
import android.text.style.UnderlineSpan
import android.view.View
import androidx.lifecycle.lifecycleScope
import com.base.filerecoveryrecyclebin.MyApplication
import com.base.filerecoveryrecyclebin.activity.junkclean.ScanJunkActivity
import com.base.filerecoveryrecyclebin.activity.privacyspace.PrivacyPinOneActivity
import com.base.filerecoveryrecyclebin.activity.privacyspace.PrivacySpaceActivity
import com.base.filerecoveryrecyclebin.activity.recovery.FileScanResultActivity
import com.base.filerecoveryrecyclebin.activity.repeat.RepeatActivity
import com.base.filerecoveryrecyclebin.activity.screenshot.ScreenShotActivity
import com.base.filerecoveryrecyclebin.ads.admob.AdmobInterstitialUtils
import com.base.filerecoveryrecyclebin.ads.admob.AdmobNativeUtils
import com.base.filerecoveryrecyclebin.ads.admob.AdmobOpenUtils
import com.base.filerecoveryrecyclebin.bean.ConstObject
import com.base.filerecoveryrecyclebin.bean.ConstObject.ID_JUNK_CLEAN_PUSH
import com.base.filerecoveryrecyclebin.bean.ConstObject.ID_PRIVACY_SPACE
import com.base.filerecoveryrecyclebin.bean.ConstObject.ID_RECOVERY_DOCUMENTS
import com.base.filerecoveryrecyclebin.bean.ConstObject.ID_RECOVERY_PHOTOS
import com.base.filerecoveryrecyclebin.bean.ConstObject.ID_RECOVERY_VIDEOS
import com.base.filerecoveryrecyclebin.bean.ConstObject.ID_SCREENSHOT_CLEAN
import com.base.filerecoveryrecyclebin.bean.ConstObject.ID_SIMILAR_IMAGE
import com.base.filerecoveryrecyclebin.bean.ConstObject.ifAgreePrivacy
import com.base.filerecoveryrecyclebin.bean.ConstObject.privacyPinPassword
import com.base.filerecoveryrecyclebin.databinding.ActivitySplashBinding
import com.base.filerecoveryrecyclebin.fcm.CloseNotificationReceiver
import com.base.filerecoveryrecyclebin.fcm.CloseNotificationReceiver.Companion.NotificationId
import com.base.filerecoveryrecyclebin.fcm.NotificationUtil
import com.base.filerecoveryrecyclebin.help.BaseActivity
import com.base.filerecoveryrecyclebin.help.ConfigHelper
import com.base.filerecoveryrecyclebin.service.StayNotificationService.Companion.startStayNotification
import com.base.filerecoveryrecyclebin.utils.BarUtils
import com.base.filerecoveryrecyclebin.utils.LogEx
import kotlinx.coroutines.Job
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.SharedFlow
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.isActive
import kotlinx.coroutines.launch
import kotlin.random.Random
@SuppressLint("CustomSplashScreen")
class SplashActivity : BaseActivity<ActivitySplashBinding>() {
private val TAG = "SplashActivity"
private var job: Job? = null
private val progress = MutableSharedFlow<Int>()
private val progressFlow: SharedFlow<Int> = progress
private var oneClickStart: Boolean = false
private var actionId = -1
override val binding: ActivitySplashBinding by lazy {
ActivitySplashBinding.inflate(layoutInflater)
}
override fun initView() {
BarUtils.setStatusBarLightMode(this, true)
BarUtils.setStatusBarColor(this, Color.TRANSPARENT)
startStayNotification()
NotificationUtil.stopNotificationHandler()
actionId = intent?.extras?.getInt("actionId") ?: -1
closeNotification()
AdmobNativeUtils.loadNativeAd()
AdmobInterstitialUtils.loadInterstitialAd(this)
progressCollect()
if (ifAgreePrivacy) {
job = startProgress()
binding.llStart.visibility = View.GONE
binding.llProgress.visibility = View.VISIBLE
} else {
binding.llStart.visibility = View.VISIBLE
binding.llProgress.visibility = View.GONE
}
val spannableString = SpannableString("Privacy Policy")
spannableString.setSpan(
UnderlineSpan(),
0,
spannableString.length,
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
)
binding.idTvPrivacyPolicy.text = spannableString
binding.idTvPrivacyPolicy.setOnClickListener {
val intent = Intent(
Intent.ACTION_VIEW,
Uri.parse(ConfigHelper.privacyPolicy)
)
startActivity(intent)
}
}
private fun progressCollect() {
lifecycleScope.launch {
progressFlow.collectLatest {
if (it >= 100) {
job?.cancel()
jumpNext()
}
}
}
}
private fun jumpNext() {
when (actionId) {
ID_JUNK_CLEAN_PUSH -> {
startActivity(Intent(this, ScanJunkActivity::class.java))
}
ID_SIMILAR_IMAGE -> {
startActivity(Intent(this, RepeatActivity::class.java))
}
ID_SCREENSHOT_CLEAN -> {
startActivity(Intent(this, ScreenShotActivity::class.java))
}
ID_RECOVERY_PHOTOS -> {
startActivity(Intent(this, FileScanResultActivity::class.java).apply {
putExtra("ScanType", ConstObject.SCAN_PHOTOS)
})
}
ID_RECOVERY_VIDEOS -> {
startActivity(Intent(this, FileScanResultActivity::class.java).apply {
putExtra("ScanType", ConstObject.SCAN_VIDEOS)
})
}
ID_RECOVERY_DOCUMENTS -> {
startActivity(Intent(this, FileScanResultActivity::class.java).apply {
putExtra("ScanType", ConstObject.SCAN_DOCUMENTS)
})
}
ID_PRIVACY_SPACE -> {
if (privacyPinPassword.isNotEmpty()) {
startActivity(Intent(this, PrivacyPinOneActivity::class.java))
} else {
startActivity(Intent(this, PrivacySpaceActivity::class.java).apply {
})
}
}
else -> {
val isHotLaunch = intent?.extras?.getBoolean("isHotLaunch", false) ?: false
if (!isHotLaunch) {
startActivity(Intent(this@SplashActivity, MainActivity::class.java))
}
}
}
finish()
}
private fun closeNotification() {
sendBroadcast(Intent(this, CloseNotificationReceiver::class.java).apply {
this.action = CloseNotificationReceiver.Action
this.putExtra(NotificationId, actionId)
})
}
override fun initListener() {
binding.idTvStart.setOnClickListener {
if (oneClickStart) {
return@setOnClickListener
}
(application as MyApplication).initApp()
oneClickStart = true
ifAgreePrivacy = true
binding.llStart.visibility = View.GONE
binding.llProgress.visibility = View.VISIBLE
job = startProgress()
}
}
private var processTime = 0L
private var outTimeAdStart = false
private fun startProgress() = lifecycleScope.launch {
LogEx.logDebug(TAG,"startProgress")
while (isActive) {
val value = binding.pb.progress + 2
binding.pb.setProgress(value, true)
progress.emit(value)
val delayTime = 300L
delay(delayTime)
processTime += delayTime
if (processTime >= Random.nextLong(3000, 4000)) {
if (!outTimeAdStart) {
outTimeAdStart = true
outTimeAd()
}
}
}
}
private fun outTimeAd() {
LogEx.logDebug(TAG,"outTimeAd")
AdmobOpenUtils.loadAppOpenAd {
LogEx.logDebug(TAG, "load where=$it")
job?.cancel()
AdmobOpenUtils.showAppOpenAd(this@SplashActivity) {
LogEx.logDebug(TAG, "adCallBack=$it")
binding.pb.progress = 100
jumpNext()
}
}
}
override fun onResume() {
super.onResume()
LogEx.logDebug(TAG,"onResume")
if (ifAgreePrivacy && job?.isActive == false) {
job = startProgress()
}
}
override fun onPause() {
super.onPause()
job?.cancel()
LogEx.logDebug(TAG,"onPause")
}
}
\ No newline at end of file
package com.base.filerecoveryrecyclebin.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.filerecoveryrecyclebin.activity.ResultActivity
import com.base.filerecoveryrecyclebin.ads.admob.AdmobInterstitialUtils
import com.base.filerecoveryrecyclebin.bean.ConstObject.JUNK_CLEANER
import com.base.filerecoveryrecyclebin.databinding.ActivityLayoutCleanupingBinding
import com.base.filerecoveryrecyclebin.help.BaseActivity
import com.base.filerecoveryrecyclebin.utils.BarUtils
import com.base.filerecoveryrecyclebin.utils.MediaStoreUtils
import com.base.filerecoveryrecyclebin.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 { 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 {
AdmobInterstitialUtils.showInterstitialAd(this@CleaningActivity) {
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.filerecoveryrecyclebin.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.view.updatePadding
import com.base.filerecoveryrecyclebin.ads.admob.AdmobInterstitialUtils
import com.base.filerecoveryrecyclebin.databinding.ActivityLayoutScanJunkBinding
import com.base.filerecoveryrecyclebin.help.BaseActivity
import com.base.filerecoveryrecyclebin.help.PermissionHelp.checkStorePermission
import com.base.filerecoveryrecyclebin.help.PermissionHelp.requestStorePermission
import com.base.filerecoveryrecyclebin.utils.BarUtils
import com.base.filerecoveryrecyclebin.view.DialogViews.showGerPermission
/**
* 清理动画页面
*/
class ScanJunkActivity : BaseActivity<ActivityLayoutScanJunkBinding>() {
override val binding: ActivityLayoutScanJunkBinding by lazy {
ActivityLayoutScanJunkBinding.inflate(layoutInflater)
}
override fun initView() {
BarUtils.setStatusBarColor(this, Color.TRANSPARENT)
binding.root.updatePadding(top = BarUtils.getStatusBarHeight())
if (checkStorePermission()) {
playLottie()
} else {
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) {
playLottie(false)
} else {
finishToMain()
}
})
})
}
}
override fun initListener() {
onBackPressedDispatcher.addCallback {
Toast.makeText(this@ScanJunkActivity, "wait a moment", Toast.LENGTH_SHORT).show()
}
}
private var isPause = false
override fun onResume() {
super.onResume()
if (isPause) {
binding.idJunkScan.resumeAnimation()
}
isPause = false
}
override fun onDestroy() {
super.onDestroy()
animator1?.cancel()
animator2?.cancel()
animator3?.cancel()
binding.idJunkScan.cancelAnimation()
}
override fun onPause() {
super.onPause()
isPause = true
animator1?.pause()
animator2?.pause()
animator3?.pause()
binding.idJunkScan.pauseAnimation()
}
private fun playLottie(showAd: Boolean = true) {
val a1 = ValueAnimator.ofFloat(0f, 360f)
a1.run {
duration = 1000
repeatMode = ValueAnimator.RESTART
repeatCount = ValueAnimator.INFINITE
interpolator = LinearInterpolator()
addUpdateListener {
binding.idView1.rotation = it.animatedValue as Float
}
start()
}
val a2 = ValueAnimator.ofFloat(0f, 360f)
a2.run {
duration = 1000
repeatMode = ValueAnimator.RESTART
repeatCount = ValueAnimator.INFINITE
interpolator = LinearInterpolator()
addUpdateListener {
binding.idView2.rotation = it.animatedValue as Float
}
start()
}
val a3 = ValueAnimator.ofFloat(0f, 360f)
a3.run {
duration = 1000
repeatMode = ValueAnimator.RESTART
repeatCount = ValueAnimator.INFINITE
interpolator = LinearInterpolator()
addUpdateListener {
binding.idView3.rotation = it.animatedValue as Float
}
start()
}
binding.idJunkScan.imageAssetsFolder = "junk_scan/images/"
binding.idJunkScan.setAnimation("junk_scan/data.json")
binding.idJunkScan.playAnimation()
binding.root.postDelayed({
if (showAd) {
AdmobInterstitialUtils.showInterstitialAd(this) {
a1.cancel()
a2.cancel()
a3.cancel()
startActivity(Intent(this, CleanJunkActivity::class.java))
finish()
}
} else {
a1.cancel()
a2.cancel()
a3.cancel()
startActivity(Intent(this, CleanJunkActivity::class.java))
finish()
}
}, 5000)
}
private var animator1: ValueAnimator? = null
private var animator2: ValueAnimator? = null
private var animator3: ValueAnimator? = null
}
\ No newline at end of file
package com.base.filerecoveryrecyclebin.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.filerecoveryrecyclebin.bean.ConstObject.rememberOption
import com.base.filerecoveryrecyclebin.bean.ConstObject.rememberRemove
import com.base.filerecoveryrecyclebin.bean.MediaBean
import com.base.filerecoveryrecyclebin.databinding.ActivityPrivacyImportBinding
import com.base.filerecoveryrecyclebin.fragment.PrivacyFileImportFragment
import com.base.filerecoveryrecyclebin.help.BaseActivity
import com.base.filerecoveryrecyclebin.help.KotlinExt.toFormatSize
import com.base.filerecoveryrecyclebin.help.MediaStoreHelp.updateMediaStore
import com.base.filerecoveryrecyclebin.utils.BarUtils
import com.base.filerecoveryrecyclebin.utils.LogEx
import com.base.filerecoveryrecyclebin.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.filerecoveryrecyclebin.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.filerecoveryrecyclebin.adapter.MediaSubAdapter
import com.base.filerecoveryrecyclebin.ads.admob.AdmobInterstitialUtils
import com.base.filerecoveryrecyclebin.bean.MediaBean
import com.base.filerecoveryrecyclebin.databinding.ActivityPrivacyManageBinding
import com.base.filerecoveryrecyclebin.help.BaseActivity
import com.base.filerecoveryrecyclebin.help.FileHelp.loadFileByFilter
import com.base.filerecoveryrecyclebin.help.MediaStoreHelp.updateMediaStore
import com.base.filerecoveryrecyclebin.utils.BarUtils
import com.base.filerecoveryrecyclebin.utils.LogEx
import com.base.filerecoveryrecyclebin.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 {
AdmobInterstitialUtils.showInterstitialAd(this) {
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.filerecoveryrecyclebin.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.filerecoveryrecyclebin.R
import com.base.filerecoveryrecyclebin.bean.ConstObject.privacyPinPassword
import com.base.filerecoveryrecyclebin.databinding.ActivityPrivacyPinOneBinding
import com.base.filerecoveryrecyclebin.help.BaseActivity
import com.base.filerecoveryrecyclebin.help.KotlinExt.array2String
import com.base.filerecoveryrecyclebin.utils.BarUtils
import com.base.filerecoveryrecyclebin.utils.LogEx
import com.base.filerecoveryrecyclebin.view.AsteriskPasswordTransformationMethod
import com.base.filerecoveryrecyclebin.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.filerecoveryrecyclebin.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.filerecoveryrecyclebin.R
import com.base.filerecoveryrecyclebin.bean.ConstObject.privacyPinPassword
import com.base.filerecoveryrecyclebin.databinding.ActivityPrivacyPinTwoBinding
import com.base.filerecoveryrecyclebin.help.BaseActivity
import com.base.filerecoveryrecyclebin.help.KotlinExt.array2String
import com.base.filerecoveryrecyclebin.utils.BarUtils
import com.base.filerecoveryrecyclebin.utils.LogEx
import com.base.filerecoveryrecyclebin.view.AsteriskPasswordTransformationMethod
import com.base.filerecoveryrecyclebin.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.filerecoveryrecyclebin.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.filerecoveryrecyclebin.ads.admob.AdmobInterstitialUtils
import com.base.filerecoveryrecyclebin.bean.ConstObject.privacyPinPassword
import com.base.filerecoveryrecyclebin.databinding.ActivityPrivacySpaceBinding
import com.base.filerecoveryrecyclebin.fragment.PrivacyPageFragment
import com.base.filerecoveryrecyclebin.help.BaseActivity
import com.base.filerecoveryrecyclebin.utils.BarUtils
import com.base.filerecoveryrecyclebin.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 {
showExitFunctionDialog(true) {
if (it) {
AdmobInterstitialUtils.showInterstitialAd(this@PrivacySpaceActivity) {
finishToMain()
}
} else {
finishToMain()
}
}
}
binding.flFanhui.setOnClickListener {
onBackPressedDispatcher.onBackPressed()
}
}
}
\ No newline at end of file
package com.base.filerecoveryrecyclebin.activity.recovery
import android.annotation.SuppressLint
import android.graphics.Color
import android.os.Environment
import androidx.activity.addCallback
import com.base.filerecoveryrecyclebin.R
import com.base.filerecoveryrecyclebin.bean.ConstObject.SCAN_DOCUMENTS
import com.base.filerecoveryrecyclebin.bean.ConstObject.SCAN_PHOTOS
import com.base.filerecoveryrecyclebin.bean.ConstObject.SCAN_VIDEOS
import com.base.filerecoveryrecyclebin.databinding.ActivityFileRecoveredBinding
import com.base.filerecoveryrecyclebin.help.BaseActivity
import com.base.filerecoveryrecyclebin.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"
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 {
finishToMain()
}
binding.tvContinue.setOnClickListener {
finish()
}
binding.tvFinish.setOnClickListener {
onBackPressedDispatcher.onBackPressed()
}
}
}
\ No newline at end of file
package com.base.filerecoveryrecyclebin.activity.recovery
import android.annotation.SuppressLint
import android.graphics.Color
import androidx.activity.addCallback
import com.base.filerecoveryrecyclebin.R
import com.base.filerecoveryrecyclebin.ads.admob.AdmobInterstitialUtils
import com.base.filerecoveryrecyclebin.bean.ConstObject.SCAN_DOCUMENTS
import com.base.filerecoveryrecyclebin.bean.ConstObject.SCAN_PHOTOS
import com.base.filerecoveryrecyclebin.bean.ConstObject.SCAN_VIDEOS
import com.base.filerecoveryrecyclebin.databinding.ActivityFileScanBinding
import com.base.filerecoveryrecyclebin.help.BaseActivity
import com.base.filerecoveryrecyclebin.utils.BarUtils
import com.base.filerecoveryrecyclebin.view.DialogViews.showExitFunctionDialog
/**
* 文件扫描,遍历文件夹的方式进行
*/
class FileScanActivity : BaseActivity<ActivityFileScanBinding>() {
private val TAG = "FileScanActivity"
private var scanOnce: Boolean = false
private var scanType = 0
override val binding: ActivityFileScanBinding by lazy {
ActivityFileScanBinding.inflate(layoutInflater)
}
@SuppressLint("SetTextI18n")
override fun initView() {
BarUtils.setStatusBarLightMode(this, false)
BarUtils.setStatusBarColor(this, Color.TRANSPARENT)
scanType = intent.extras?.getInt("Type") ?: 0
when (scanType) {
SCAN_PHOTOS -> {
binding.tvTittle.text = "Photos Recovery"
binding.ivIcon.setImageResource(R.mipmap.tu_photos_scan)
}
SCAN_DOCUMENTS -> {
binding.tvTittle.text = "Documents Recovery"
binding.ivIcon.setImageResource(R.mipmap.tu_documents_scan)
}
SCAN_VIDEOS -> {
binding.tvTittle.text = "Videos Recovery"
binding.ivIcon.setImageResource(R.mipmap.tu_videos_scan)
}
}
}
override fun initListener() {
onBackPressedDispatcher.addCallback {
showExitFunctionDialog {
if (it) {
AdmobInterstitialUtils.showInterstitialAd(this@FileScanActivity) {
finishToMain()
}
} else {
finishToMain()
}
}
}
binding.flFanhui.setOnClickListener {
onBackPressedDispatcher.onBackPressed()
}
}
private fun requestPermission() {
}
}
\ No newline at end of file
package com.base.filerecoveryrecyclebin.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.filerecoveryrecyclebin.adapter.FileFolderAdapter
import com.base.filerecoveryrecyclebin.ads.admob.AdmobInterstitialUtils
import com.base.filerecoveryrecyclebin.bean.ConstObject.SCAN_DOCUMENTS
import com.base.filerecoveryrecyclebin.bean.ConstObject.SCAN_PHOTOS
import com.base.filerecoveryrecyclebin.bean.ConstObject.SCAN_VIDEOS
import com.base.filerecoveryrecyclebin.bean.FolderBean
import com.base.filerecoveryrecyclebin.bean.RecoveryBean
import com.base.filerecoveryrecyclebin.bean.RecoveryBean.Companion.setType
import com.base.filerecoveryrecyclebin.databinding.ActivityFileScanResultBinding
import com.base.filerecoveryrecyclebin.help.BaseActivity
import com.base.filerecoveryrecyclebin.help.FileHelp.loadFileByFilter
import com.base.filerecoveryrecyclebin.help.PermissionHelp.checkStorePermission
import com.base.filerecoveryrecyclebin.help.PermissionHelp.requestStorePermission
import com.base.filerecoveryrecyclebin.utils.BarUtils
import com.base.filerecoveryrecyclebin.utils.FileHexEx
import com.base.filerecoveryrecyclebin.utils.LogEx
import com.base.filerecoveryrecyclebin.view.DialogViews.showExitFunctionDialog
import com.base.filerecoveryrecyclebin.view.FileScanDialog
import com.google.gson.Gson
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"
}
}
fileFolderAdapter = FileFolderAdapter(scanType) { folderBean ->
startActivity(Intent(this, FileRecoveryActivity::class.java).apply {
putExtra("ScanType", scanType)
putExtra("Data", Gson().toJson(folderBean))
})
}
binding.rv.adapter = fileFolderAdapter
if (checkStorePermission()) {
beginScan()
} else {
requestStorePermission(launcher, result = {
if (it) beginScan() else finishToMain()
})
}
}
override fun initListener() {
binding.flFanhui.setOnClickListener {
onBackPressedDispatcher.onBackPressed()
}
onBackPressedDispatcher.addCallback {
showExitFunctionDialog {
if (it) {
AdmobInterstitialUtils.showInterstitialAd(this@FileScanResultActivity) {
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
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"
AdmobInterstitialUtils.showInterstitialAd(this@FileScanResultActivity, isShowDialog = 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()
}
hashMap[folder]?.add(recoveryBean)
return isNewFolder
}
}
\ No newline at end of file
package com.base.filerecoveryrecyclebin.activity.recyclebin
import android.annotation.SuppressLint
import android.content.Context
import android.database.ContentObserver
import android.database.Cursor
import android.net.Uri
import android.os.Handler
import android.provider.MediaStore
import com.base.filerecoveryrecyclebin.bean.RecycleBinBean
import com.base.filerecoveryrecyclebin.utils.LogEx
import com.google.gson.Gson
import java.io.File
class MediaContentObserver(val context: Context, val handler: Handler? = null) : ContentObserver(null) {
private val TAG = "MediaContentObserver"
override fun onChange(selfChange: Boolean, uri: Uri?) {
super.onChange(selfChange, uri)
uri?.let { queryNewMediaFiles(it) }
}
@SuppressLint("Range")
private fun queryNewMediaFiles(uri: Uri) {
// 查询新添加的媒体文件
val projection = arrayOf(
MediaStore.MediaColumns.DISPLAY_NAME,
MediaStore.MediaColumns.DATA,
MediaStore.MediaColumns.MIME_TYPE,
)
val cursor: Cursor? = context.contentResolver.query(uri, projection, null, null, null)
if (cursor != null) {
try {
while (cursor.moveToNext()) {
val name = cursor.getString(cursor.getColumnIndex(MediaStore.MediaColumns.DISPLAY_NAME))
val path = cursor.getString(cursor.getColumnIndex(MediaStore.MediaColumns.DATA))
val mimeType = cursor.getString(cursor.getColumnIndex(MediaStore.MediaColumns.MIME_TYPE))
// 处理新文件路径
backupRecycleBinFile(name, path, mimeType)
}
} catch (e: Exception) {
} finally {
cursor.close()
}
}
}
private fun backupRecycleBinFile(name: String, path: String, mimeType: String) {
LogEx.logDebug(TAG, "name=$name path=$path mimeType=$mimeType")
val file = context.filesDir
val recycleBinDir = File(file, "RecycleBin")
if (!recycleBinDir.exists()) {
recycleBinDir.mkdirs()
}
val src = File(path)
val recycleBinFile = File(recycleBinDir, ".$name")
if (!recycleBinFile.exists()) {
src.copyTo(recycleBinFile, true)
val binBean = RecycleBinBean(src.path, 0, src.length(), mimeType)
val binFile = File(recycleBinDir, ".$name.bin")
binFile.createNewFile()
binFile.writeText(Gson().toJson(binBean))
LogEx.logDebug(TAG, "回收站备份成功")
handler?.sendEmptyMessage(99)
}
}
}
package com.base.filerecoveryrecyclebin.activity.recyclebin
import android.os.Build
import android.os.FileObserver
import android.os.Handler
import androidx.annotation.RequiresApi
import com.base.filerecoveryrecyclebin.bean.RecycleBinBean
import com.base.filerecoveryrecyclebin.help.BaseApplication
import com.base.filerecoveryrecyclebin.utils.LogEx
import com.google.gson.Gson
import com.google.gson.reflect.TypeToken
import java.io.File
import java.lang.reflect.Type
@RequiresApi(Build.VERSION_CODES.Q)
class MyFileObserver(val handler: Handler? = null, files: List<File>) : FileObserver(files, ALL_EVENTS) {
private val TAG = "MyFileObserver"
override fun onEvent(event: Int, path: String?) {
if (path != null) {
// LogEx.logDebug(TAG, "onEvent=$event path=$path")
val filteredEvent = event and ALL_EVENTS
LogEx.logDebug(TAG, "filteredEvent=$filteredEvent path=$path")
when (filteredEvent) {
CLOSE_NOWRITE -> {
}
OPEN -> {}
MOVED_FROM -> {
recordRecycleBinFile(path)
}
MOVED_TO -> {
}
CREATE -> {
}
DELETE -> {
recordRecycleBinFile(path)
}
DELETE_SELF -> {
recordRecycleBinFile(path)
}
}
}
}
private fun recordRecycleBinFile(path: String) {
val file = BaseApplication.context.filesDir
val recycleBinDir = File(file, "RecycleBin")
if (!recycleBinDir.exists()) {
recycleBinDir.mkdirs()
}
val recycleFile = recycleBinDir.listFiles()?.find { it.name.contains(path) }
val recycleBinFile = recycleBinDir.listFiles()?.find { it.name.contains(".$path.bin") }
LogEx.logDebug(TAG, "recycleFile=$recycleFile")
LogEx.logDebug(TAG, "recycleBinFile=$recycleBinFile")
if (recycleFile != null && recycleBinFile != null) {
runCatching {
val rJson = recycleBinFile.readText()
val bean = Gson().fromJson(rJson, RecycleBinBean::class.java)
bean.deleted = true
bean.deleteTime = System.currentTimeMillis()
val wJson = Gson().toJson(bean)
recycleBinFile.writeText(wJson)
handler?.sendEmptyMessage(100)
}
}
}
}
\ No newline at end of file
package com.base.filerecoveryrecyclebin.activity.recyclebin
import android.content.Intent
import android.os.Build
import android.os.Environment
import android.os.Handler
import androidx.annotation.RequiresApi
import androidx.lifecycle.lifecycleScope
import com.base.filerecoveryrecyclebin.adapter.RecycleBinAdapter
import com.base.filerecoveryrecyclebin.bean.RecycleBinBean
import com.base.filerecoveryrecyclebin.databinding.ActivityRecycleBinBinding
import com.base.filerecoveryrecyclebin.help.BaseActivity
import com.base.filerecoveryrecyclebin.help.PermissionHelp.checkStorePermission
import com.base.filerecoveryrecyclebin.help.PermissionHelp.requestStorePermission
import com.base.filerecoveryrecyclebin.service.StayNotificationService
import com.base.filerecoveryrecyclebin.service.StayNotificationService.Companion.FILE_OBSERVER_ACTION
import com.base.filerecoveryrecyclebin.service.StayNotificationService.Companion.MEDIA_OBSERVER_ACTION
import com.base.filerecoveryrecyclebin.utils.LogEx
import com.base.filerecoveryrecyclebin.view.DialogViews.showGerPermission
import com.google.gson.Gson
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import java.io.File
@RequiresApi(Build.VERSION_CODES.Q)
class RecycleBinActivity : BaseActivity<ActivityRecycleBinBinding>() {
private val TAG = "RecycleBinActivity"
private lateinit var handler: Handler
private lateinit var recycleBinAdapter: RecycleBinAdapter
override val binding: ActivityRecycleBinBinding by lazy {
ActivityRecycleBinBinding.inflate(layoutInflater)
}
override fun initView() {
handler = Handler(mainLooper) { message ->
if (message.what == 99) {
LogEx.logDebug(TAG, "媒体库更新了")
}
if (message.what == 100) {
LogEx.logDebug(TAG, "回收站数据更新了")
}
true
}
StayNotificationService.mainHandler = handler
startService(Intent(this, StayNotificationService::class.java).apply {
this.action = MEDIA_OBSERVER_ACTION
})
startService(Intent(this, StayNotificationService::class.java).apply {
this.action = FILE_OBSERVER_ACTION
putExtra("isStart", true)
})
recycleBinAdapter = RecycleBinAdapter()
binding.rv.adapter = recycleBinAdapter
if (checkStorePermission()) {
initData()
} else {
showGerPermission(allow = {
requestStorePermission(launcher, result = { flag ->
if (flag) {
initData()
}
})
})
}
}
override fun initListener() {
super.initListener()
binding.swipeRefreshLayout.setOnRefreshListener {
binding.swipeRefreshLayout.isRefreshing = true
initData()
lifecycleScope.launch {
delay(1500)
binding.swipeRefreshLayout.isRefreshing = false
}
}
}
override fun onDestroy() {
super.onDestroy()
startService(Intent(FILE_OBSERVER_ACTION).apply {
putExtra("isStart", false)
})
}
private fun initData() {
lifecycleScope.launch(Dispatchers.IO) {
val file = this@RecycleBinActivity.filesDir
val recycleBinDir = File(file, "RecycleBin")
if (!recycleBinDir.exists()) {
recycleBinDir.mkdirs()
}
val binList = recycleBinDir.listFiles()?.filter { it.name.contains(".bin") }
val beanList = arrayListOf<RecycleBinBean>()
binList?.forEach {
val bean = Gson().fromJson(it.readText(), RecycleBinBean::class.java)
beanList.add(bean)
}
launch(Dispatchers.Main) {
recycleBinAdapter.setData(beanList)
}
}
}
}
\ No newline at end of file
package com.base.filerecoveryrecyclebin.activity.repeat
import android.graphics.Color
import android.view.View
import androidx.activity.addCallback
import androidx.core.view.updatePadding
import androidx.lifecycle.lifecycleScope
import com.base.filerecoveryrecyclebin.adapter.MediaAdapter
import com.base.filerecoveryrecyclebin.ads.admob.AdmobInterstitialUtils
import com.base.filerecoveryrecyclebin.ads.admob.AdmobInterstitialUtils.showInterAdSp
import com.base.filerecoveryrecyclebin.ads.admob.AdmobNativeUtils
import com.base.filerecoveryrecyclebin.bean.MediaBean
import com.base.filerecoveryrecyclebin.bean.MediaTimeBean
import com.base.filerecoveryrecyclebin.databinding.ActivityRepeatBinding
import com.base.filerecoveryrecyclebin.help.BaseActivity
import com.base.filerecoveryrecyclebin.help.ConfigHelper
import com.base.filerecoveryrecyclebin.help.KotlinExt.toFormatSize
import com.base.filerecoveryrecyclebin.help.KotlinExt.toFormatTime
import com.base.filerecoveryrecyclebin.help.MediaStoreHelp.getImageMedia
import com.base.filerecoveryrecyclebin.help.PermissionHelp.checkStorePermission
import com.base.filerecoveryrecyclebin.help.PermissionHelp.requestStorePermission
import com.base.filerecoveryrecyclebin.utils.BarUtils
import com.base.filerecoveryrecyclebin.view.DialogViews.showDeletePermanentlyDialog
import com.base.filerecoveryrecyclebin.view.DialogViews.showExitFunctionDialog
import com.base.filerecoveryrecyclebin.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 (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) {
AdmobInterstitialUtils.showInterstitialAd(this@RepeatActivity) {
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 (showInterAdSp()) {
AdmobInterstitialUtils.showInterstitialAd(this) {
cleanFile()
}
} else {
cleanFile()
}
}
}
}
private fun cleanFile() {
lifecycleScope.launch(Dispatchers.IO) {
mediaAdapter.getSelectData().second.forEach {
runCatching {
val file = File(it.path)
file.delete()
}
}
initData()
}
}
private fun initData() {
lifecycleScope.launch(Dispatchers.IO) {
val list = arrayListOf<MediaBean>()
getImageMedia(list)
val hashMap = HashMap<String, ArrayList<MediaBean>>()
list.forEach {
val time = it.time.toFormatTime()
if (hashMap[time] == null) {
hashMap[time] = arrayListOf()
}
hashMap[time]?.add(it)
}
val beanList = hashMap.map { MediaTimeBean(it.key, it.value) }
launch(Dispatchers.Main) {
binding.progressBar.visibility = View.GONE
mediaAdapter.setData(beanList)
if (beanList.sumOf { it.beans.size } > 6 || ConfigHelper.mustShowNativeAd) {
AdmobNativeUtils.showNativeAd(this@RepeatActivity, binding.flAd)
}
}
}
}
}
\ No newline at end of file
package com.base.filerecoveryrecyclebin.activity.repeat
import android.content.Intent
import android.widget.Toast
import androidx.activity.addCallback
import com.base.filerecoveryrecyclebin.ads.admob.AdmobInterstitialUtils
import com.base.filerecoveryrecyclebin.databinding.ActivityRepeatAnimationBinding
import com.base.filerecoveryrecyclebin.help.BaseActivity
import kotlin.random.Random
class RepeatAnimationActivity : BaseActivity<ActivityRepeatAnimationBinding>() {
override val binding: ActivityRepeatAnimationBinding by lazy {
ActivityRepeatAnimationBinding.inflate(layoutInflater)
}
override fun initView() {
binding.root.postDelayed({
AdmobInterstitialUtils.showInterstitialAd(this) {
startActivity(Intent(this, RepeatActivity::class.java))
finish()
}
}, Random.nextLong(3000, 4500))
}
override fun initListener() {
onBackPressedDispatcher.addCallback {
Toast.makeText(this@RepeatAnimationActivity, "wait a moment", Toast.LENGTH_SHORT).show()
}
}
}
\ No newline at end of file
package com.base.filerecoveryrecyclebin.activity.screenshot
import android.annotation.SuppressLint
import android.graphics.Color
import android.os.Environment
import androidx.activity.addCallback
import androidx.core.view.updatePadding
import androidx.lifecycle.lifecycleScope
import com.base.filerecoveryrecyclebin.adapter.ScreenShotAdapter
import com.base.filerecoveryrecyclebin.ads.admob.AdmobInterstitialUtils
import com.base.filerecoveryrecyclebin.ads.admob.AdmobInterstitialUtils.showInterAdSp
import com.base.filerecoveryrecyclebin.ads.admob.AdmobNativeUtils
import com.base.filerecoveryrecyclebin.bean.ScreenPhotoBean
import com.base.filerecoveryrecyclebin.bean.ScreenshotBean
import com.base.filerecoveryrecyclebin.databinding.ActivityScreenShotBinding
import com.base.filerecoveryrecyclebin.help.BaseActivity
import com.base.filerecoveryrecyclebin.help.ConfigHelper
import com.base.filerecoveryrecyclebin.help.FileHelp
import com.base.filerecoveryrecyclebin.help.KotlinExt.toFormatSize
import com.base.filerecoveryrecyclebin.help.PermissionHelp.checkStorePermission
import com.base.filerecoveryrecyclebin.help.PermissionHelp.requestStorePermission
import com.base.filerecoveryrecyclebin.utils.BarUtils
import com.base.filerecoveryrecyclebin.view.DialogViews.showDeletePermanentlyDialog
import com.base.filerecoveryrecyclebin.view.DialogViews.showExitFunctionDialog
import com.base.filerecoveryrecyclebin.view.DialogViews.showGerPermission
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.async
import kotlinx.coroutines.launch
import java.io.File
import java.text.SimpleDateFormat
import java.util.Locale
import kotlin.collections.HashSet
class ScreenShotActivity : BaseActivity<ActivityScreenShotBinding>() {
private lateinit var screenShotAdapter: ScreenShotAdapter
override val binding: ActivityScreenShotBinding by lazy {
ActivityScreenShotBinding.inflate(layoutInflater)
}
override fun initView() {
BarUtils.setStatusBarLightMode(this, true)
BarUtils.setStatusBarColor(this, Color.TRANSPARENT)
binding.root.updatePadding(top = BarUtils.getStatusBarHeight())
screenShotAdapter = ScreenShotAdapter { size ->
setCleanUpUi(size)
}
binding.rv.adapter = screenShotAdapter
if (checkStorePermission()) {
initData()
} else {
dialog = showGerPermission("Storage Permission Required",
"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, result = { flag ->
if (flag) initData() else finishToMain()
})
})
}
}
@SuppressLint("SetTextI18n")
private fun setCleanUpUi(size: Int) {
if (size > 0) {
binding.tvClean.isEnabled = true
binding.tvClean.text = "CLEAN(${size})"
} else {
binding.tvClean.isEnabled = false
binding.tvClean.text = "CLEAN"
}
binding.llSelectAll.isSelected = screenShotAdapter.isAllSelect()
}
private fun initData() = lifecycleScope.launch(Dispatchers.IO) {
val list = getScreenshotBean()
launch(Dispatchers.Main) {
screenShotAdapter.setData(list)
val size = list.sumOf { it.screenPhotoBean.sumOf { ss -> ss.file.length() } }
val sizeF = size.toFormatSize().split(" ")
binding.tvSize.text = sizeF[0]
binding.tvUnit.text = sizeF[1]
if (list.sumOf { it.screenPhotoBean.size } > 6 || ConfigHelper.mustShowNativeAd) {
AdmobNativeUtils.showNativeAd(this@ScreenShotActivity, binding.flAd)
}
}
}
@SuppressLint("SetTextI18n")
override fun initListener() {
binding.flBack.setOnClickListener {
onBackPressedDispatcher.onBackPressed()
}
onBackPressedDispatcher.addCallback {
showExitFunctionDialog {
if (it) {
AdmobInterstitialUtils.showInterstitialAd(this@ScreenShotActivity) {
finishToMain()
}
} else {
finishToMain()
}
}
}
binding.llSelectAll.setOnClickListener {
binding.llSelectAll.isSelected = !binding.llSelectAll.isSelected
screenShotAdapter.toggleSelect(binding.llSelectAll.isSelected)
val size = screenShotAdapter.getSelectDataSize()
setCleanUpUi(size)
}
binding.tvClean.setOnClickListener {
showDeletePermanentlyDialog {
if (showInterAdSp()) {
AdmobInterstitialUtils.showInterstitialAd(this) {
cleanFile()
}
} else {
cleanFile()
}
}
}
}
private fun cleanFile() {
val files = screenShotAdapter.getSelectData()
lifecycleScope.launch(Dispatchers.IO) {
async {
files.forEach { screenFile ->
runCatching {
val file = screenFile.file
if (file.exists()) file.delete()
}
}
}.await()
launch(Dispatchers.Main) {
binding.tvClean.text = "CLEAN"
screenShotAdapter.removeData(files)
val split = screenShotAdapter.getAllData().sumOf { it.file.length() }.toFormatSize().split(" ")
binding.tvSize.text = split[0]
binding.tvUnit.text = split[1]
setCleanUpUi(0)
}
}
}
private fun getScreenshotBean(): ArrayList<ScreenshotBean> {
val dcim = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM)
val screenFiles = FileHelp.getDirFiles(dcim)
val hash = HashMap<String, HashSet<File>>()
val formatter = SimpleDateFormat("yyyy/MM", Locale.getDefault())
screenFiles.forEach { file ->
val time = formatter.format(file.lastModified())
if (hash[time] == null) {
hash[time] = HashSet()
}
val set = hash[time]
set?.add(file)
}
val beans = arrayListOf<ScreenshotBean>()
hash.forEach { (t, u) ->
val arrayList = arrayListOf<ScreenPhotoBean>()
val mapList = u.map { file -> ScreenPhotoBean(file) }
arrayList.addAll(mapList)
beans.add(ScreenshotBean(t, arrayList))
}
return beans
}
}
\ No newline at end of file
package com.base.filerecoveryrecyclebin.activity.screenshot
import android.content.Intent
import android.widget.Toast
import androidx.activity.addCallback
import com.base.filerecoveryrecyclebin.ads.admob.AdmobInterstitialUtils
import com.base.filerecoveryrecyclebin.databinding.ActivityScreenShotAnimationBinding
import com.base.filerecoveryrecyclebin.help.BaseActivity
import kotlin.random.Random
class ScreenShotAnimationActivity : BaseActivity<ActivityScreenShotAnimationBinding>() {
override val binding: ActivityScreenShotAnimationBinding by lazy {
ActivityScreenShotAnimationBinding.inflate(layoutInflater)
}
override fun initView() {
binding.root.postDelayed({
AdmobInterstitialUtils.showInterstitialAd(this) {
startActivity(Intent(this, ScreenShotActivity::class.java))
finish()
}
}, Random.nextLong(3000, 4500))
}
override fun initListener() {
super.initListener()
onBackPressedDispatcher.addCallback {
Toast.makeText(this@ScreenShotAnimationActivity, "wait a moment", Toast.LENGTH_SHORT).show()
}
}
}
\ No newline at end of file
//package com.base.datarecovery.activity.whatsapp
//
//import android.content.Intent
//import android.graphics.Color
//import android.os.Environment
//import androidx.activity.addCallback
//import androidx.lifecycle.lifecycleScope
//import com.base.datarecovery.R
//import com.base.datarecovery.adapter.WhatsAppCleanerAdapter
//import com.base.datarecovery.ads.AdmobInterstitialUtils
//import com.base.datarecovery.ads.AdmobNativeUtils
//import com.base.datarecovery.bean.WhatsAppCleanerBean
//import com.base.datarecovery.databinding.ActivityWhatsAppCleanerBinding
//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.utils.FileHexEx.isVideo
//import com.base.datarecovery.utils.LogEx
//import com.base.datarecovery.view.DialogViews.showGerPermission
//import com.google.gson.Gson
//import kotlinx.coroutines.Dispatchers
//import kotlinx.coroutines.launch
//import java.io.File
//
//class WhatsAppCleanerActivity : BaseActivity<ActivityWhatsAppCleanerBinding>() {
//
// private val TAG = "WhatsAppCleanerActivity"
// private lateinit var adapter: WhatsAppCleanerAdapter
//
// override val binding: ActivityWhatsAppCleanerBinding by lazy {
// ActivityWhatsAppCleanerBinding.inflate(layoutInflater)
// }
//
//
// override fun initView() {
// BarUtils.setStatusBarLightMode(this, true)
// BarUtils.setStatusBarColor(this, Color.TRANSPARENT)
//
// adapter = WhatsAppCleanerAdapter {
// startActivity(Intent(this, WhatsAppMessageCleanActivity::class.java).apply {
// putExtra("PathList", Gson().toJson(it.subFile.toTypedArray()))
// putExtra("Tittle", it.tittle)
// })
// finish()
// }
// binding.rv.adapter = adapter
//
// if (checkStorePermission()) {
// initData()
// } else {
// showGerPermission(null, deny = {
// finishToMain()
// }, allow = {
// requestStorePermission(
// launcher,
// result = { flag ->
// if (flag) {
// initData()
// } else {
// finishToMain()
// }
// },
// )
// })
// }
// AdmobNativeUtils.showNativeAd(this, binding.flAd)
// }
//
// override fun initListener() {
// binding.flBack.setOnClickListener {
// onBackPressedDispatcher.onBackPressed()
// }
// onBackPressedDispatcher.addCallback {
// AdmobInterstitialUtils.showInterstitialAd(this@WhatsAppCleanerActivity) {
// finishToMain()
// }
// }
// binding.tvTvGotIt.setOnClickListener {
//
// }
//
// }
//
//
// private fun initData() = lifecycleScope.launch(Dispatchers.IO) {
//
// val whatsAppRoot = File(Environment.getExternalStorageDirectory(), "Android/media/com.whatsapp/WhatsApp/Media/")
// LogEx.logDebug(TAG, whatsAppRoot.absolutePath)
// val list = arrayListOf<WhatsAppCleanerBean>()
// list.add(WhatsAppCleanerBean(R.mipmap.videomessages, "Video Messages"))
// list.add(WhatsAppCleanerBean(R.mipmap.imagemessages, "Image Messages"))
// list.add(WhatsAppCleanerBean(R.mipmap.audiomessages, "Audio Messages"))
// var totalSize = 0L
// whatsAppRoot.listFiles()?.forEach { file ->
//
// if (file.name == "WhatsApp Video") {
// LogEx.logDebug(TAG, "whatsapp file=${file.absolutePath}")
// val bean = list.find { it.tittle == "Video Messages" }
// val subFile = getDirFiles(file).filter { isVideo(it) }
// val size = subFile.sumOf { it.length() }
// bean?.size = size
// totalSize += size
// bean?.subFile = subFile.map { it.absolutePath }
//// subFile.forEach { path ->
//// LogEx.logDebug(TAG, "path=$path")
//// }
// }
// if (file.name == "WhatsApp Images") {
// val bean = list.find { it.tittle == "Image Messages" }
// val subFile = getDirFiles(file).filter { isImage(it) }
// val size = subFile.sumOf { it.length() }
// bean?.size = size
// totalSize += size
// bean?.subFile = subFile.map { it.absolutePath }
// }
// if (file.name == "WhatsApp Audio") {
// val bean = list.find { it.tittle == "Audio Messages" }
// val subFile = getDirFiles(file).filter { it.name.contains(".mp3") }
// val size = subFile.sumOf { it.length() }
// bean?.size = size
// totalSize += size
// bean?.subFile = subFile.map { it.absolutePath }
// }
// }
// launch(Dispatchers.Main) {
// adapter.setData(list)
// val split = totalSize.toFormatSize().split(" ")
// binding.tvSize.text = split[0]
// binding.tvUnit.text = split[1]
// }
// }
//
//}
\ No newline at end of file
//package com.base.datarecovery.activity.whatsapp
//
//import android.content.Intent
//import android.graphics.Color
//import androidx.activity.addCallback
//import androidx.recyclerview.widget.GridLayoutManager
//import androidx.recyclerview.widget.LinearLayoutManager
//import com.base.datarecovery.adapter.WhatsAppMediaAdapter
//import com.base.datarecovery.ads.AdmobInterstitialUtils
//import com.base.datarecovery.ads.AdmobNativeUtils
//import com.base.datarecovery.bean.MediaBean
//import com.base.datarecovery.databinding.ActivityWhatsAppMessageCleanBinding
//import com.base.datarecovery.help.BaseActivity
//import com.base.datarecovery.help.KotlinExt.toFormatSize
//import com.base.datarecovery.utils.BarUtils
//import com.google.gson.Gson
//import java.io.File
//
//class WhatsAppMessageCleanActivity : BaseActivity<ActivityWhatsAppMessageCleanBinding>() {
//
// private val pathList by lazy {
// val json = intent.extras?.getString("PathList")
// val list = Gson().fromJson(json, Array<String>::class.java)
// list.map { MediaBean(it) }
// }
// private lateinit var adapter: WhatsAppMediaAdapter
//
// override val binding: ActivityWhatsAppMessageCleanBinding by lazy {
// ActivityWhatsAppMessageCleanBinding.inflate(layoutInflater)
// }
//
//
// override fun initView() {
// BarUtils.setStatusBarLightMode(this, true)
// BarUtils.setStatusBarColor(this, Color.TRANSPARENT)
//
// val tittle = intent.extras?.getString("Tittle") ?: ""
// binding.tvTitle.text = tittle
//
// var isList = false
// when (tittle) {
// "Audio Messages" -> {
// binding.rv.layoutManager = LinearLayoutManager(this)
// isList = true
// }
//
// else -> {
// binding.rv.layoutManager = GridLayoutManager(this, 3)
// }
// }
//
// adapter = WhatsAppMediaAdapter(isList) {
// binding.llSelectAll.isSelected = it
// binding.tvClean.isEnabled = adapter.getSelectData().isNotEmpty()
// val split = adapter.getSelectDataSize().toFormatSize().split(" ")
// binding.tvSize.text = split[0]
// binding.tvUnit.text = split[1]
// }
// binding.rv.adapter = adapter
// adapter.setData(pathList)
//
// AdmobNativeUtils.showNativeAd(this, binding.flAd)
// }
//
// override fun initListener() {
// binding.flBack.setOnClickListener {
// onBackPressedDispatcher.onBackPressed()
// }
// onBackPressedDispatcher.addCallback {
// AdmobInterstitialUtils.showInterstitialAd(this@WhatsAppMessageCleanActivity, isShowInterVal = false) {
// finishToMain()
// }
// }
// binding.llSelectAll.setOnClickListener {
// it.isSelected = !it.isSelected
// adapter.toggleSelect(it.isSelected)
// binding.tvClean.isEnabled = it.isSelected
// val split = adapter.getSelectDataSize().toFormatSize().split(" ")
// binding.tvSize.text = split[0]
// binding.tvUnit.text = split[1]
// }
//
// binding.tvClean.setOnClickListener {
//
// AdmobInterstitialUtils.showInterstitialAd(this) {
// val list = adapter.getSelectData()
// runCatching {
// list.forEach { File(it).delete() }
// }
// finish()
// }
// }
// }
//
//
//}
\ No newline at end of file
package com.base.filerecoveryrecyclebin.adapter
import android.annotation.SuppressLint
import android.view.View
import android.view.ViewGroup
import androidx.core.content.ContextCompat
import androidx.recyclerview.widget.RecyclerView
import androidx.recyclerview.widget.RecyclerView.ViewHolder
import com.base.filerecoveryrecyclebin.R
import com.base.filerecoveryrecyclebin.bean.ConstObject.JUNK_CLEANER
import com.base.filerecoveryrecyclebin.bean.ConstObject.PRIVACY_SPACE
import com.base.filerecoveryrecyclebin.bean.ConstObject.RECOVERY_DOCUMENTS
import com.base.filerecoveryrecyclebin.bean.ConstObject.RECOVERY_PHOTOS
import com.base.filerecoveryrecyclebin.bean.ConstObject.RECOVERY_VIDEOS
import com.base.filerecoveryrecyclebin.bean.ConstObject.REPEAT_PHOTOS
import com.base.filerecoveryrecyclebin.bean.ConstObject.SCREENSHOT_CLEANER
import com.base.filerecoveryrecyclebin.databinding.ItemResultFun2Binding
import com.base.filerecoveryrecyclebin.utils.AppPreferences
import com.base.filerecoveryrecyclebin.view.XmlEx.inflate
import java.util.Collections
class AppFunctionAdapter(val click: (name: String) -> Unit) :
RecyclerView.Adapter<AppFunctionAdapter.JJJ>() {
val list = arrayListOf(
Fun(JUNK_CLEANER, R.mipmap.clean),
Fun(RECOVERY_PHOTOS, R.mipmap.photos_ss),
Fun(RECOVERY_VIDEOS, R.mipmap.videos_ss),
Fun(RECOVERY_DOCUMENTS, R.mipmap.documents_ss),
Fun(PRIVACY_SPACE, R.mipmap.space),
Fun(REPEAT_PHOTOS, R.mipmap.repeatedphotos),
Fun(SCREENSHOT_CLEANER, R.mipmap.screenshots),
)
class JJJ(view: View) : ViewHolder(view)
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): JJJ {
return JJJ(R.layout.item_result_fun_2.inflate(parent))
}
override fun getItemCount(): Int {
return list.size
}
override fun onBindViewHolder(holder: JJJ, position: Int) {
val data = list[position]
val context = holder.itemView.context
val binding = ItemResultFun2Binding.bind(holder.itemView)
binding.ivIcon.setImageDrawable(ContextCompat.getDrawable(context, data.icon))
binding.tvTittle.text = data.name
// binding.tvDes.text = data.des
// binding.tvButton.text = data.button
binding.root.setOnClickListener {
click.invoke(data.name)
}
}
@SuppressLint("NotifyDataSetChanged")
fun removeItem(name: String) {
list.removeIf { it.name == name }
notifyDataSetChanged()
}
data class Fun(
val name: String = "",
val icon: Int = 0,
val des: String = "",
val button: String = "",
)
fun updateListPosition() {
//本次进入结果页,判断使用垃圾的功能是否超过5分钟
val lastUseJunkCleaner = AppPreferences.getInstance().getLong("last_use_junk_cleaner", 0)
if ((System.currentTimeMillis() - lastUseJunkCleaner) >= 60 * 5 * 1000) {
} else {
Collections.rotate(list, list.size - 1)
}
}
}
\ No newline at end of file
package com.base.filerecoveryrecyclebin.adapter
import android.annotation.SuppressLint
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import androidx.recyclerview.widget.RecyclerView.ViewHolder
import com.base.filerecoveryrecyclebin.R
import com.base.filerecoveryrecyclebin.bean.ConstObject.SCAN_PHOTOS
import com.base.filerecoveryrecyclebin.bean.ConstObject.SCAN_VIDEOS
import com.base.filerecoveryrecyclebin.bean.FolderBean
import com.base.filerecoveryrecyclebin.databinding.ItemFolderBinding
import com.base.filerecoveryrecyclebin.databinding.ItemFolderRecoveryBinding
import com.base.filerecoveryrecyclebin.utils.LogEx
import com.base.filerecoveryrecyclebin.utils.ScreenUtil.dpToPx
import com.base.filerecoveryrecyclebin.view.XmlEx.inflate
import com.bumptech.glide.Glide
import com.bumptech.glide.load.resource.bitmap.CenterCrop
import com.bumptech.glide.load.resource.bitmap.RoundedCorners
import com.bumptech.glide.request.RequestOptions
class FileFolderAdapter(
private val scanType: Int, private val click: (bean: FolderBean) -> Unit
) : RecyclerView.Adapter<FileFolderAdapter.FF>() {
private val TAG = "FileFolderAdapter"
private val beanList = arrayListOf<FolderBean>()
class FF(view: View) : ViewHolder(view)
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): FF {
val layout = when (scanType) {
SCAN_PHOTOS, SCAN_VIDEOS -> R.layout.item_folder_recovery
else -> R.layout.item_folder
}
return FF(layout.inflate(parent))
}
override fun getItemCount(): Int {
return beanList.size
}
@SuppressLint("SetTextI18n", "CheckResult")
override fun onBindViewHolder(holder: FF, position: Int) {
val bean = beanList[position]
val context = holder.itemView.context
when (scanType) {
SCAN_PHOTOS, SCAN_VIDEOS -> {
val binding = ItemFolderRecoveryBinding.bind(holder.itemView)
runCatching {
val options = RequestOptions().transform(CenterCrop(), RoundedCorners(context.dpToPx(10)))
val image1 = bean.recoveryList[0].path
val request1 = Glide.with(context).load(image1).apply(options)
if (scanType == SCAN_VIDEOS) {
request1.error(R.mipmap.videotu)
}
request1.into(binding.iv1)
val image2 = bean.recoveryList[1].path
val request2 = Glide.with(context).load(image2).apply(options)
if (scanType == SCAN_VIDEOS) {
request2.error(R.mipmap.videotu)
}
request2.into(binding.iv2)
val image3 = bean.recoveryList[2].path
val request3 = Glide.with(context).load(image3).apply(options)
if (scanType == SCAN_VIDEOS) {
request3.error(R.mipmap.videotu)
}
request3.into(binding.iv3)
}
binding.tvFolder.text = bean.folder
binding.tvFolderNumber.text = "(${bean.recoveryList.size})"
binding.root.setOnClickListener {
click.invoke(bean)
}
}
else -> {
LogEx.logDebug(TAG, "scanType=$scanType")
val binding = ItemFolderBinding.bind(holder.itemView)
binding.tvFolder.text = bean.folder
binding.tvFileNumber.text = "${bean.recoveryList.size}"
binding.root.setOnClickListener {
click.invoke(bean)
}
}
}
}
@SuppressLint("NotifyDataSetChanged")
fun setData(list: List<FolderBean>) {
beanList.clear()
beanList.addAll(list)
notifyDataSetChanged()
}
@SuppressLint("NotifyDataSetChanged")
fun addData(list: List<FolderBean>) {
beanList.addAll(list)
notifyDataSetChanged()
}
}
\ No newline at end of file
package com.base.filerecoveryrecyclebin.adapter
import android.annotation.SuppressLint
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import androidx.recyclerview.widget.RecyclerView.ViewHolder
import com.base.filerecoveryrecyclebin.R
import com.base.filerecoveryrecyclebin.databinding.ItemMediaGridBinding
import com.base.filerecoveryrecyclebin.utils.LogEx
import com.base.filerecoveryrecyclebin.view.XmlEx.inflate
import com.bumptech.glide.Glide
class FileGridAdapter : RecyclerView.Adapter<FileGridAdapter.FFF>() {
private val beanList = arrayListOf<String>()
private val TAG = "FileGridAdapter"
inner class FFF(view: View) : ViewHolder(view)
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): FFF {
return FFF(R.layout.item_media_grid.inflate(parent))
}
override fun getItemCount(): Int {
return beanList.size
}
override fun onBindViewHolder(holder: FFF, position: Int) {
val binding = ItemMediaGridBinding.bind(holder.itemView)
val context = holder.itemView.context
val data = beanList[position]
LogEx.logDebug(TAG, data)
Glide.with(context).load(data).centerCrop().into(binding.iv)
}
@SuppressLint("NotifyDataSetChanged")
fun setData(list: List<String>) {
beanList.clear()
beanList.addAll(list)
notifyDataSetChanged()
}
}
\ No newline at end of file
package com.base.filerecoveryrecyclebin.adapter
import android.annotation.SuppressLint
import android.view.Gravity
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.LinearLayout
import android.widget.TextView
import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import androidx.recyclerview.widget.RecyclerView.ViewHolder
import com.base.filerecoveryrecyclebin.R
import com.base.filerecoveryrecyclebin.bean.ConstObject.SCAN_DOCUMENTS
import com.base.filerecoveryrecyclebin.bean.FolderBean
import com.base.filerecoveryrecyclebin.bean.RecoveryBean
import com.base.filerecoveryrecyclebin.databinding.ItemDocumentBinding
import com.base.filerecoveryrecyclebin.databinding.ItemTimeMediaBinding
import com.base.filerecoveryrecyclebin.help.KotlinExt.toFormatSize
import com.base.filerecoveryrecyclebin.view.XmlEx.inflate
import com.bumptech.glide.Glide
import java.io.File
import java.text.SimpleDateFormat
import java.util.Locale
class FileTimeColumnsAdapter(
val scanType: Int,
val select: (all: Boolean, size: Int) -> Unit,
) : RecyclerView.Adapter<FileTimeColumnsAdapter.MCV>() {
private val beanList = arrayListOf<FolderBean>()
private var hideThumbnails: Boolean = false
private var columns: Int = 2
class MCV(view: View) : ViewHolder(view)
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MCV {
return MCV(R.layout.item_time_media.inflate(parent))
}
override fun getItemCount(): Int {
return beanList.size
}
override fun onBindViewHolder(holder: MCV, position: Int) {
val binding = ItemTimeMediaBinding.bind(holder.itemView)
val context = holder.itemView.context
val bean = beanList[position]
val file = File(bean.recoveryList.first().path)
val time = SimpleDateFormat("yyyy-MMM-dd", Locale.getDefault()).format(file.lastModified())
binding.tvTime.text = time
binding.ivSelector.isSelected = bean.isSelect
val subAdapter = MediaColumnsAdapter()
if (scanType == SCAN_DOCUMENTS) {
binding.rvMediaColumns.layoutManager = LinearLayoutManager(context)
} else {
binding.rvMediaColumns.layoutManager = GridLayoutManager(context, columns)
binding.rvMediaColumns.layoutParams =
LinearLayout.LayoutParams(RecyclerView.LayoutParams.WRAP_CONTENT, RecyclerView.LayoutParams.WRAP_CONTENT).apply {
topMargin = context.resources.getDimensionPixelOffset(R.dimen.dp_8)
gravity = Gravity.CENTER_HORIZONTAL
}
}
binding.rvMediaColumns.adapter = subAdapter
val list = if (hideThumbnails) bean.recoveryList.filter { !it.isThumbnails } else bean.recoveryList
if (list.isEmpty()) {
binding.flTittle.visibility = View.GONE
} else {
binding.flTittle.visibility = View.VISIBLE
subAdapter.setData(list)
}
binding.ivSelector.setOnClickListener {
bean.isSelect = !bean.isSelect
binding.ivSelector.isSelected = bean.isSelect
notifyItemChanged(position)
itemClick(bean.isSelect, subAdapter)
}
}
@SuppressLint("NotifyDataSetChanged")
fun setData(data: List<FolderBean>) {
beanList.clear()
beanList.addAll(data)
notifyDataSetChanged()
}
inner class MediaColumnsAdapter : RecyclerView.Adapter<MediaColumnsAdapter.CS>() {
private val list = arrayListOf<RecoveryBean>()
inner class CS(view: View) : ViewHolder(view) {
val iv = view.findViewById<ImageView>(R.id.iv)
val ivSelector = view.findViewById<ImageView>(R.id.iv_selector)
val tvSize = view.findViewById<TextView>(R.id.tv_size)
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CS {
var layout = when (columns) {
2 -> R.layout.item_media_columns2
3 -> R.layout.item_media_columns3
4 -> R.layout.item_media_columns4
else -> R.layout.item_media_columns2
}
if (scanType == SCAN_DOCUMENTS) {
layout = R.layout.item_document
}
return CS(layout.inflate(parent))
}
override fun getItemCount(): Int {
return list.size
}
override fun onBindViewHolder(holder: CS, position: Int) {
}
override fun onBindViewHolder(holder: CS, position: Int, payloads: MutableList<Any>) {
val context = holder.itemView.context
val data = list[position]
if (scanType == SCAN_DOCUMENTS) {
if (payloads.isEmpty()) {
val binding = ItemDocumentBinding.bind(holder.itemView)
binding.ivSelector.isSelected = data.isSelect
val icon = when (data.type) {
"PDF" -> R.mipmap.pdf
"DOC" -> R.mipmap.doc
"XLS" -> R.mipmap.xls
"PPT" -> R.mipmap.ppt
else -> 0
}
binding.ivType.setImageResource(icon)
val file = File(data.path)
binding.tvName.text = file.name
val time = SimpleDateFormat("MMM-dd-yyyy", Locale.getDefault()).format(file.lastModified())
val size = file.length().toFormatSize()
val info = "$time $size"
binding.tvInfo.text = info
binding.root.setOnClickListener {
data.isSelect = !data.isSelect
holder.ivSelector.isSelected = data.isSelect
notifyItemChanged(position, "asdada")
subItemClick(data)
}
} else {
holder.ivSelector.isSelected = data.isSelect
super.onBindViewHolder(holder, position, payloads)
}
} else {
if (payloads.isEmpty()) {
Glide.with(context).asBitmap().load(data.path).centerCrop().into(holder.iv)
holder.ivSelector.isSelected = data.isSelect
holder.itemView.setOnClickListener {
data.isSelect = !data.isSelect
holder.ivSelector.isSelected = data.isSelect
notifyItemChanged(position, "changeIv")
subItemClick(data)
}
val file = File(data.path)
holder.tvSize.text = file.length().toFormatSize()
} else {
holder.ivSelector.isSelected = data.isSelect
super.onBindViewHolder(holder, position, payloads)
}
}
}
@SuppressLint("NotifyDataSetChanged")
fun setData(bean: List<RecoveryBean>) {
list.clear()
list.addAll(bean)
notifyDataSetChanged()
}
@SuppressLint("NotifyDataSetChanged")
fun toggleSelect(select: Boolean) {
list.forEach {
it.isSelect = select
}
notifyDataSetChanged()
}
}
@SuppressLint("NotifyDataSetChanged")
fun subItemClick(data: RecoveryBean) {
val folderBean = beanList.find { it.recoveryList.contains(data) }
folderBean?.let { folder ->
val select = folder.recoveryList.all { it.isSelect }
if (folder.isSelect != select) {
folder.isSelect = select
notifyDataSetChanged()
}
}
val all = beanList.all { it.isSelect }
val size = beanList.sumOf { it.recoveryList.filter { bean -> bean.isSelect }.size }
select.invoke(all, size)
}
private fun itemClick(isSelect: Boolean, subAdapter: MediaColumnsAdapter) {
subAdapter.toggleSelect(isSelect)
val all = beanList.all { it.isSelect }
val size = beanList.sumOf { it.recoveryList.filter { bean -> bean.isSelect }.size }
select.invoke(all, size)
}
@SuppressLint("NotifyDataSetChanged")
fun toggleThumbnails(hideThumbnails: Boolean) {
this.hideThumbnails = hideThumbnails
notifyDataSetChanged()
}
@SuppressLint("NotifyDataSetChanged")
fun toggleAllSelect(selected: Boolean) {
beanList.forEach {
it.isSelect = selected
it.recoveryList.forEach { bean -> bean.isSelect = selected }
}
notifyDataSetChanged()
val all = beanList.all { it.isSelect }
val size = beanList.sumOf { it.recoveryList.filter { bean -> bean.isSelect }.size }
select.invoke(all, size)
}
@SuppressLint("NotifyDataSetChanged")
fun changeColumns(columns: Int) {
this.columns = columns
notifyDataSetChanged()
}
fun getSelectData(): List<RecoveryBean> {
return beanList.flatMap { it.recoveryList }.filter { it.isSelect }
}
}
\ No newline at end of file
package com.base.filerecoveryrecyclebin.adapter
import android.annotation.SuppressLint
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import androidx.recyclerview.widget.RecyclerView.ViewHolder
import com.base.filerecoveryrecyclebin.R
import com.base.filerecoveryrecyclebin.bean.MediaBean
import com.base.filerecoveryrecyclebin.bean.MediaTimeBean
import com.base.filerecoveryrecyclebin.databinding.ItemTimeMedia2Binding
import com.base.filerecoveryrecyclebin.view.XmlEx.inflate
class MediaAdapter(private val clickAction: (Pair<Boolean, List<MediaBean>>) -> Unit) : RecyclerView.Adapter<MediaAdapter.NNN>() {
private val beanList = arrayListOf<MediaTimeBean>()
inner class NNN(view: View) : ViewHolder(view) {
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): NNN {
return NNN(R.layout.item_time_media2.inflate(parent))
}
override fun getItemCount(): Int {
return beanList.size
}
override fun onBindViewHolder(holder: NNN, position: Int) {}
@SuppressLint("SetTextI18n")
override fun onBindViewHolder(holder: NNN, position: Int, payloads: MutableList<Any>) {
val binding = ItemTimeMedia2Binding.bind(holder.itemView)
// val context = holder.itemView.context
val bean = beanList[position]
if (payloads.isEmpty()) {
binding.tvTime.text = bean.time + " (${bean.beans.size})"
binding.ivSelector.isSelected = bean.isSelect
val subAdapter = MediaSubAdapter().apply {
subClickAction = { clickAction.invoke(this@MediaAdapter.getSelectData()) }
}
binding.rvMediaColumns.adapter = subAdapter
subAdapter.setData(bean.beans)
binding.flSelect.setOnClickListener {
bean.isSelect = !bean.isSelect
binding.ivSelector.isSelected = bean.isSelect
notifyItemChanged(position, "222")
toggleSubList(bean, subAdapter)
}
} else {
binding.ivSelector.isSelected = bean.isSelect
super.onBindViewHolder(holder, position, payloads)
}
}
@SuppressLint("NotifyDataSetChanged")
private fun toggleSubList(bean: MediaTimeBean, subAdapter: MediaSubAdapter) {
bean.beans.forEach { it.isSelect = bean.isSelect }
subAdapter.notifyDataSetChanged()
clickAction.invoke(getSelectData())
}
fun getSelectData(): Pair<Boolean, List<MediaBean>> {
val flatMap = beanList.flatMap { it.beans }
val allSelect = flatMap.all { it.isSelect }
val pathList = flatMap.filter { it.isSelect }
return Pair(allSelect, pathList)
}
@SuppressLint("NotifyDataSetChanged")
fun toggleAllSelect(select: Boolean) {
beanList.forEach { it.isSelect = select }
beanList.flatMap { it.beans }.forEach { it.isSelect = select }
notifyDataSetChanged()
clickAction.invoke(getSelectData())
}
@SuppressLint("NotifyDataSetChanged")
fun setData(list: List<MediaTimeBean>) {
beanList.clear()
beanList.addAll(list)
notifyDataSetChanged()
clickAction.invoke(getSelectData())
}
}
\ No newline at end of file
package com.base.filerecoveryrecyclebin.adapter
import android.annotation.SuppressLint
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import androidx.recyclerview.widget.RecyclerView
import com.base.filerecoveryrecyclebin.R
import com.base.filerecoveryrecyclebin.bean.MediaBean
import com.base.filerecoveryrecyclebin.view.XmlEx.inflate
import com.bumptech.glide.Glide
class MediaSubAdapter : RecyclerView.Adapter<MediaSubAdapter.Sub>() {
private val subList: ArrayList<MediaBean> = arrayListOf()
var subClickAction: (() -> Unit)? = null
inner class Sub(view: View) : RecyclerView.ViewHolder(view) {
val iv = view.findViewById<ImageView>(R.id.iv)
val ivSelector = view.findViewById<ImageView>(R.id.iv_selector)
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): Sub {
return Sub(R.layout.item_media_grid_select.inflate(parent))
}
override fun getItemCount(): Int {
return subList.size
}
override fun onBindViewHolder(holder: Sub, position: Int) {
}
override fun onBindViewHolder(holder: Sub, position: Int, payloads: MutableList<Any>) {
val context = holder.itemView.context
val data = subList[position]
if (payloads.isEmpty()) {
Glide.with(context).load(data.path).centerCrop().into(holder.iv)
holder.ivSelector.isSelected = data.isSelect
holder.itemView.setOnClickListener {
data.isSelect = !data.isSelect
holder.ivSelector.isSelected = data.isSelect
this.notifyItemChanged(position, "hh")
subClickAction?.invoke()
}
} else {
holder.ivSelector.isSelected = data.isSelect
super.onBindViewHolder(holder, position, payloads)
}
}
@SuppressLint("NotifyDataSetChanged")
fun setData(list: List<MediaBean>) {
subList.clear()
subList.addAll(list)
notifyDataSetChanged()
}
fun getSelectData(): List<MediaBean> {
return subList.filter { it.isSelect }
}
@SuppressLint("NotifyDataSetChanged")
fun removeData(list: List<MediaBean>) {
subList.removeAll(list.toSet())
notifyDataSetChanged()
}
@SuppressLint("NotifyDataSetChanged")
fun toggleSelect(b: Boolean) {
subList.forEach { it.isSelect=b }
notifyDataSetChanged()
}
}
\ No newline at end of file
package com.base.filerecoveryrecyclebin.adapter
import android.annotation.SuppressLint
import android.view.View
import android.view.ViewGroup
import androidx.annotation.Dimension
import androidx.core.content.ContextCompat
import androidx.core.view.isVisible
import androidx.recyclerview.widget.RecyclerView
import androidx.recyclerview.widget.RecyclerView.ViewHolder
import com.base.filerecoveryrecyclebin.R
import com.base.filerecoveryrecyclebin.bean.RecoveryFilterBean
import com.base.filerecoveryrecyclebin.databinding.ItemRecoveryFilterBinding
import com.base.filerecoveryrecyclebin.view.XmlEx.inflate
class RecoveryFilterAdapter(
val click: (bean: RecoveryFilterBean) -> Unit
) : RecyclerView.Adapter<RecoveryFilterAdapter.YYDS>() {
private val beanList = arrayListOf<RecoveryFilterBean>()
inner class YYDS(view: View) : ViewHolder(view)
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): YYDS {
return YYDS(R.layout.item_recovery_filter.inflate(parent))
}
override fun getItemCount(): Int {
return beanList.size
}
override fun onBindViewHolder(holder: YYDS, position: Int) {
val binding = ItemRecoveryFilterBinding.bind(holder.itemView)
val bean = beanList[position]
val context = holder.itemView.context
binding.tv.text = bean.tittle
binding.iv.isVisible = bean.isSelect
if (bean.isSelect) {
binding.tv.setTextColor(ContextCompat.getColor(context, R.color.black))
binding.tv.setTextSize(Dimension.SP, 18f)
} else {
binding.tv.setTextColor(ContextCompat.getColor(context, R.color.color_747474))
binding.tv.setTextSize(Dimension.SP, 16f)
}
binding.root.setOnClickListener {
click.invoke(bean)
}
}
@SuppressLint("NotifyDataSetChanged")
fun setData(list: List<RecoveryFilterBean>) {
beanList.clear()
beanList.addAll(list)
notifyDataSetChanged()
}
}
\ No newline at end of file
package com.base.filerecoveryrecyclebin.adapter
import android.annotation.SuppressLint
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import androidx.recyclerview.widget.RecyclerView.ViewHolder
import com.base.filerecoveryrecyclebin.R
import com.base.filerecoveryrecyclebin.bean.RecycleBinBean
import com.base.filerecoveryrecyclebin.databinding.ItemRecycleBinBeanBinding
import com.base.filerecoveryrecyclebin.view.XmlEx.inflate
class RecycleBinAdapter : RecyclerView.Adapter<RecycleBinAdapter.RBAV>() {
private val beanList = arrayListOf<RecycleBinBean>()
inner class RBAV(view: View) : ViewHolder(view) {
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RBAV {
return RBAV(R.layout.item_recycle_bin_bean.inflate(parent))
}
override fun getItemCount(): Int {
return beanList.size
}
@SuppressLint("SetTextI18n")
override fun onBindViewHolder(holder: RBAV, position: Int) {
val binding = ItemRecycleBinBeanBinding.bind(holder.itemView)
val bean = beanList[position]
binding.tvPath.text = bean.path
binding.tvDeleted.text = "deleted=${bean.deleted}"
binding.tvDeleteTime.text = "time=" + bean.deleteTime.toString()
binding.tvSize.text = "size=${bean.size}"
binding.tvMimetype.text = "mimetype=${bean.mimeType}"
}
@SuppressLint("NotifyDataSetChanged")
fun setData(list: List<RecycleBinBean>) {
beanList.clear()
beanList.addAll(list)
notifyDataSetChanged()
}
}
\ No newline at end of file
//package com.base.datarecovery.adapter
//
//import android.annotation.SuppressLint
//import android.graphics.Color
//import android.view.View
//import android.view.ViewGroup
//import androidx.core.content.ContextCompat
//import androidx.recyclerview.widget.RecyclerView
//import androidx.recyclerview.widget.RecyclerView.ViewHolder
//import com.base.datarecovery.R
//import com.base.datarecovery.bean.WhatsAppCleanerBean
//import com.base.datarecovery.databinding.ItemWhatsappBinding
//import com.base.datarecovery.help.KotlinExt.toFormatSize
//import com.base.datarecovery.utils.ScreenUtil.dpToPx
//import com.base.datarecovery.view.XmlEx.inflate
//import com.bumptech.glide.Glide
//import com.bumptech.glide.load.resource.bitmap.CenterCrop
//import com.bumptech.glide.load.resource.bitmap.RoundedCorners
//import com.bumptech.glide.request.RequestOptions
//
//class WhatsAppCleanerAdapter(val click: (bean: WhatsAppCleanerBean) -> Unit) : RecyclerView.Adapter<WhatsAppCleanerAdapter.Whats>() {
//
// private val beanList = arrayListOf<WhatsAppCleanerBean>()
//
// class Whats(view: View) : ViewHolder(view) {
//
// }
//
// override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): Whats {
// return Whats(R.layout.item_whatsapp.inflate(parent))
// }
//
// override fun getItemCount(): Int {
// return beanList.size
// }
//
// override fun onBindViewHolder(holder: Whats, position: Int) {
// val binding = ItemWhatsappBinding.bind(holder.itemView)
// val context = holder.itemView.context
// val bean = beanList[position]
// binding.iv.setImageDrawable(ContextCompat.getDrawable(context, bean.icon))
// binding.tvTitle.text = bean.tittle
// if (bean.subFile.isEmpty()) {
// binding.llIv.visibility = View.GONE
// binding.tvSize.setTextColor(Color.parseColor("#999999"))
// binding.ivArrow.setImageDrawable(ContextCompat.getDrawable(context, R.mipmap.jianotu_whatsapp))
// binding.tvNoMessage.visibility = View.VISIBLE
// } else {
// binding.llIv.visibility = View.VISIBLE
// runCatching {
// val options = RequestOptions().transform(CenterCrop(), RoundedCorners(context.dpToPx(10)))
// val image1 = bean.subFile[0]
// Glide.with(context).load(image1).apply(options).into(binding.iv1)
//
// val image2 = bean.subFile[1]
// Glide.with(context).load(image2).apply(options).into(binding.iv2)
//
// val image3 = bean.subFile[2]
// Glide.with(context).load(image3).apply(options).into(binding.iv3)
// }
// binding.tvSize.setTextColor(Color.parseColor("#355BEA"))
// binding.tvSize.text = bean.size.toFormatSize()
// binding.ivArrow.setImageDrawable(ContextCompat.getDrawable(context, R.mipmap.jianotul_whatsapp))
// binding.tvNoMessage.visibility = View.GONE
// }
// binding.root.setOnClickListener {
// if (bean.subFile.isEmpty()) {
// return@setOnClickListener
// }
// click.invoke(bean)
// }
// }
//
// @SuppressLint("NotifyDataSetChanged")
// fun setData(list: List<WhatsAppCleanerBean>) {
// beanList.clear()
// beanList.addAll(list)
// notifyDataSetChanged()
// }
//
//
//}
\ No newline at end of file
package com.base.filerecoveryrecyclebin.ads
import com.base.filerecoveryrecyclebin.utils.ActivityManagerUtils
object AdmobCommonUtils {
private var lastAd: Any? = null
private var maxMultiClick = 4
private var multiClick = 0
fun isMultiClick(currentAd: Any?) {
if (currentAd == null) {
return
}
if (lastAd == currentAd) {
multiClick++
if (multiClick >= maxMultiClick) {
AdDisplayUtils.getInstance()
.setAdClickCount(AdDisplayUtils.getInstance().maxAdClickCount)
ActivityManagerUtils.getInstance().finishAllActivity()
return
}
} else {
multiClick = 0
}
lastAd = currentAd
}
}
\ 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.
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