Commit 239af647 authored by wanglei's avatar wanglei

...ui

parent 801e9303
plugins {
alias(libs.plugins.androidApplication)
alias(libs.plugins.jetbrainsKotlinAndroid)
id 'com.google.gms.google-services'
id 'com.google.firebase.crashlytics'
}
android {
......@@ -30,6 +32,10 @@ android {
shrinkResources true
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
signingConfig signingConfigs.realse
// 设置是否要自动上传
firebaseCrashlytics {
mappingFileUploadEnabled true
}
}
}
compileOptions {
......@@ -45,6 +51,13 @@ android {
aidl true
}
}
gradle.taskGraph.whenReady {
tasks.each { task ->
if (task.name.contains("uploadCrashlyticsMappingFile")) {
task.enabled = false
}
}
}
dependencies {
......@@ -66,4 +79,19 @@ dependencies {
//网络
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'
//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")
}
\ No newline at end of file
{
"project_info": {
"project_number": "1032027169037",
"project_id": "easycleanerjunk",
"storage_bucket": "easycleanerjunk.appspot.com"
},
"client": [
{
"client_info": {
"mobilesdk_app_id": "1:1032027169037:android:c81c9c37c0534a2b2a9044",
"android_client_info": {
"package_name": "com.base.datarecovery"
}
},
"oauth_client": [],
"api_key": [
{
"current_key": "AIzaSyCqV4IqJ0QYV7TCMBMBEPepvP0JD6Lj_5k"
}
],
"services": {
"appinvite_service": {
"other_platform_oauth_client": []
}
}
}
],
"configuration_version": "1"
}
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" >
xmlns:tools="http://schemas.android.com/tools">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
......@@ -8,6 +8,7 @@
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />
<application
android:name=".MyApplication"
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"
......@@ -16,25 +17,65 @@
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.DataRecovery"
tools:targetApi="31" >
tools:targetApi="31">
<activity
android:name=".activity.FileScanResultActivity"
android:exported="false" />
<activity
android:name=".activity.FileRecoveryActivity"
android:exported="false" />
<activity
android:name=".activity.FileScanActivity"
android:exported="false" />
<activity
android:name=".MainActivity"
android:exported="true" >
android:name=".activity.SplashActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name=".activity.MainActivity"
android:exported="false"
android:launchMode="singleTop"
android:screenOrientation="portrait"
tools:ignore="DiscouragedApi,LockedOrientationActivity" />
<activity
android:name=".activity.FileRecoveredActivity"
android:exported="false"
android:launchMode="singleTop"
android:screenOrientation="portrait"
tools:ignore="DiscouragedApi,LockedOrientationActivity" />
<activity
android:name=".activity.FileScanResultActivity"
android:exported="false"
android:launchMode="singleTop"
android:screenOrientation="portrait"
tools:ignore="DiscouragedApi,LockedOrientationActivity" />
<activity
android:name=".activity.FileRecoveryActivity"
android:exported="false"
android:launchMode="singleTop"
android:screenOrientation="portrait"
tools:ignore="DiscouragedApi,LockedOrientationActivity" />
<activity
android:name=".activity.FileScanActivity"
android:exported="false"
android:launchMode="singleTop"
android:screenOrientation="portrait"
tools:ignore="DiscouragedApi,LockedOrientationActivity" />
<meta-data
android:name="com.google.android.gms.ads.flag.OPTIMIZE_INITIALIZATION"
android:value="true" />
<meta-data
android:name="com.google.android.gms.ads.flag.OPTIMIZE_AD_LOADING"
android:value="true" />
<meta-data
android:name="com.google.android.gms.ads.flag.NATIVE_AD_DEBUGGER_ENABLED"
android:value="false" />
<meta-data
android:name="com.google.android.gms.ads.APPLICATION_ID"
android:value="ca-app-pub-3940256099942544~3347511713" />
<meta-data
android:name="com.facebook.sdk.ApplicationId"
android:value="@string/facebook_app_id" />
</application>
</manifest>
\ No newline at end of file
package com.base.datarecovery
import android.annotation.SuppressLint
import android.app.Activity
import android.os.Bundle
import com.base.datarecovery.bean.ConstObject.ifAgreePrivacy
import com.base.datarecovery.help.BaseApplication
import com.base.datarecovery.help.ConfigHelper
import com.base.datarecovery.utils.ActivityManagerUtils
import com.google.android.gms.ads.MobileAds
class MyApplication : BaseApplication() {
companion object {
@JvmField
var PAUSED_VALUE = 0
}
override fun init() {
initApp()
}
private fun initApp() {
if (ifAgreePrivacy) {
MobileAds.initialize(this) { initializationStatus ->
}
}
}
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) }
}
if (flag) {
// if (AdmobUtils.isOpenAdLoaded()) {
// AdmobUtils.showAppOpenAd(activity)
// } else {
// topActivity?.startActivity(
// Intent(
// topActivity,
// NewSplashActivity::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.datarecovery.activity
import android.os.Bundle
import androidx.activity.enableEdgeToEdge
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import com.base.datarecovery.R
import com.base.datarecovery.databinding.ActivityFileRecoveredBinding
import com.base.datarecovery.help.BaseActivity
class FileRecoveredActivity : BaseActivity<ActivityFileRecoveredBinding>() {
override val binding: ActivityFileRecoveredBinding by lazy {
ActivityFileRecoveredBinding.inflate(layoutInflater)
}
override fun initView() {
}
}
\ No newline at end of file
package com.base.datarecovery.activity
import android.annotation.SuppressLint
import android.content.Intent
import android.graphics.BitmapFactory
import android.graphics.Color
import android.os.Environment
import android.view.View
import android.widget.Toast
import androidx.activity.addCallback
import androidx.core.content.ContextCompat
import androidx.core.view.isVisible
import androidx.lifecycle.lifecycleScope
import com.base.datarecovery.R
import com.base.datarecovery.adapter.FileMediaColumnsAdapter
import com.base.datarecovery.adapter.RecoveryFilterAdapter
import com.base.datarecovery.bean.ConstObject
import com.base.datarecovery.bean.ConstObject.SCAN_DOCUMENTS
import com.base.datarecovery.bean.ConstObject.SCAN_PHOTOS
import com.base.datarecovery.bean.ConstObject.SCAN_VIDEOS
import com.base.datarecovery.bean.FolderBean
import com.base.datarecovery.bean.RecoveryBean
import com.base.datarecovery.bean.RecoveryFilterBean
......@@ -21,6 +27,7 @@ import com.base.datarecovery.utils.BarUtils
import com.base.datarecovery.utils.TimeUtils.isWithinOneMonth
import com.base.datarecovery.utils.TimeUtils.isWithinSixMonths
import com.base.datarecovery.utils.TimeUtils.isWithinTwentyFourMonths
import com.base.datarecovery.view.DialogViews.showRecoveringDialog
import com.google.gson.Gson
import java.io.File
......@@ -66,7 +73,7 @@ class FileRecoveryActivity : BaseActivity<ActivityFileRecoveryBinding>() {
folderBean = Gson().fromJson(json, FolderBean::class.java)
when (scanType) {
ConstObject.SCAN_PHOTOS -> {
SCAN_PHOTOS -> {
var size = 0
folderBean?.recoveryList?.forEach {
val bitmap = BitmapFactory.decodeFile(it.path)
......@@ -78,11 +85,11 @@ class FileRecoveryActivity : BaseActivity<ActivityFileRecoveryBinding>() {
binding.tvThumbnails.text = "Hide thumbnails (${size})"
}
ConstObject.SCAN_DOCUMENTS -> {
SCAN_DOCUMENTS -> {
binding.clThumbnails.visibility = View.GONE
}
ConstObject.SCAN_VIDEOS -> {
SCAN_VIDEOS -> {
binding.clThumbnails.visibility = View.GONE
}
......@@ -131,7 +138,31 @@ class FileRecoveryActivity : BaseActivity<ActivityFileRecoveryBinding>() {
binding.llLayout.setOnClickListener {
showFilter(it.id)
}
binding.tvRecover.setOnClickListener {
var dirType = ""
when (scanType) {
SCAN_PHOTOS -> dirType = "Photo"
SCAN_DOCUMENTS -> dirType = "Document"
SCAN_VIDEOS -> dirType = "Video"
}
val appName = this.resources.getString(R.string.app_name)
val appDir = File(Environment.getExternalStorageDirectory(), appName)
val dir = File(appDir, dirType)
if (!dir.exists()) {
dir.mkdirs()
}
beginRecover(dir)
}
}
private fun beginRecover(dir: File) {
val list = adapter.getSelectData().map { it.path }
showRecoveringDialog(lifecycleScope, list, dir, copyProgressAction = {
}, finish = {
adapter.toggleAllSelect(false)
startActivity(Intent(this, FileRecoveredActivity::class.java))
})
}
private fun showFilter(filter: Int) {
......
package com.base.datarecovery
package com.base.datarecovery.activity
import android.graphics.Color
import android.os.Bundle
......
package com.base.datarecovery.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.datarecovery.ads.AdmobNativeUtils
import com.base.datarecovery.databinding.ActivitySplashBinding
import com.base.datarecovery.help.BaseActivity
import com.base.datarecovery.help.ConfigHelper
import com.base.datarecovery.utils.BarUtils
import com.base.datarecovery.utils.SPUtils
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 var job: Job? = null
private val progress = MutableSharedFlow<Int>()
private val progressFlow: SharedFlow<Int> = progress
private var oneClickStart: Boolean = false
var ifAgreePrivacy = false
get() {
return SPUtils.getInstance().getBoolean("ifAgreePrivacy", field)
}
set(value) {
field = value
SPUtils.getInstance().put("ifAgreePrivacy", value, true)
}
override val binding: ActivitySplashBinding by lazy {
ActivitySplashBinding.inflate(layoutInflater)
}
override fun initView() {
BarUtils.setStatusBarLightMode(this, true)
BarUtils.setStatusBarColor(this, Color.TRANSPARENT)
jumpNext()
if (ifAgreePrivacy) {
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)
}
AdmobNativeUtils.loadNativeAd()
}
private fun jumpNext() {
lifecycleScope.launch {
progressFlow.collectLatest {
if (it >= 100) {
job?.cancel()
startActivity(Intent(this@SplashActivity, MainActivity::class.java))
finish()
}
}
}
}
override fun initListener() {
binding.idTvStart.setOnClickListener {
if (oneClickStart) {
return@setOnClickListener
}
oneClickStart = true
ifAgreePrivacy = true
binding.llStart.visibility = View.GONE
binding.llProgress.visibility = View.VISIBLE
startProgress()
}
}
fun startProgress() = lifecycleScope.launch {
while (isActive) {
delay(Random.nextLong(50, 150))
val value = binding.pb.progress + Random.nextInt(3, 5)
binding.pb.setProgress(value, true)
progress.emit(value)
}
}
override fun onResume() {
super.onResume()
if (ifAgreePrivacy) {
job = startProgress()
}
}
override fun onPause() {
super.onPause()
job?.cancel()
}
}
\ No newline at end of file
......@@ -49,28 +49,27 @@ class FileFolderAdapter(
when (scanType) {
SCAN_PHOTOS, SCAN_VIDEOS -> {
LogEx.logDebug(TAG, "scanType=SCAN_PHOTOS or 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.drawable.videotu)
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.drawable.videotu)
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.drawable.videotu)
request3.error(R.mipmap.videotu)
}
request3.into(binding.iv3)
}
......
......@@ -175,10 +175,18 @@ class FileMediaColumnsAdapter(
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.datarecovery.ads;
import android.content.SharedPreferences;
import com.base.datarecovery.help.BaseApplication;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.Locale;
public class AdDisplayUtils {
private static final int DEFAULT_MAX_AD_DISPLAY_COUNT = 45; // 总广告展示次数限制默认值
private static final int DEFAULT_MAX_AD_CLICK_COUNT = 10; // 单个广告点击次数限制默认值
private static final String AD_PREFS_NAME = "ad_prefs"; // SharedPreferences 名称
private static final String AD_DISPLAY_COUNT_KEY = "ad_display_count"; // 广告展示次数的键
private static final String AD_CLICK_COUNT_KEY = "ad_click_count"; // 广告点击次数的键
private static final String MAX_AD_DISPLAY_COUNT_KEY = "max_ad_display_count"; // 总广告展示次数限制的键
private static final String MAX_AD_CLICK_COUNT_KEY = "max_ad_click_count"; // 单个广告点击次数限制的键
private static AdDisplayUtils instance; // 单例对象
private int adDisplayCount = 0; // 当前广告展示次数
private int adClickCount = 0; // 当前广告点击次数
private int maxAdDisplayCount; // 总广告展示次数限制
private int maxAdClickCount; // 单个广告点击次数限制
private String currentDate; // 当前日期
private AdDisplayUtils() {
currentDate = getCurrentDate();
SharedPreferences prefs = BaseApplication.context.getSharedPreferences(AD_PREFS_NAME, 0);
maxAdDisplayCount = prefs.getInt(MAX_AD_DISPLAY_COUNT_KEY, DEFAULT_MAX_AD_DISPLAY_COUNT);
maxAdClickCount = prefs.getInt(MAX_AD_CLICK_COUNT_KEY, DEFAULT_MAX_AD_CLICK_COUNT);
adDisplayCount = prefs.getInt(getAdDisplayCountKey(), 0);
adClickCount = prefs.getInt(getAdClickCountKey(), 0);
}
public static synchronized AdDisplayUtils getInstance() {
if (instance == null) {
instance = new AdDisplayUtils();
}
return instance;
}
public boolean shouldDisplayAd() {
return adDisplayCount < getMaxAdDisplayCount();
}
public boolean shouldIncrementClickCount() {
return adClickCount < getMaxAdClickCount();
}
public boolean shouldShowAd() {
return shouldDisplayAd() || shouldIncrementClickCount();
}
public void incrementAdDisplayCount() {
if (!currentDate.equals(getCurrentDate())) {
currentDate = getCurrentDate();
adDisplayCount = 0;
}
adDisplayCount++;
saveAdDisplayCount();
}
public void incrementAdClickCount() {
if (!currentDate.equals(getCurrentDate())) {
currentDate = getCurrentDate();
adClickCount = 0;
}
adClickCount++;
saveAdClickCount();
}
public void setAdClickCount(int s) {
if (!currentDate.equals(getCurrentDate())) {
currentDate = getCurrentDate();
adClickCount = 0;
}
adClickCount = s;
saveAdClickCount();
}
private String getCurrentDate() {
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd", Locale.getDefault());
Date currentDate = Calendar.getInstance().getTime();
return dateFormat.format(currentDate);
}
private String getAdDisplayCountKey() {
return AD_DISPLAY_COUNT_KEY + "_" + currentDate;
}
private String getAdClickCountKey() {
return AD_CLICK_COUNT_KEY + "_" + currentDate;
}
private void saveAdDisplayCount() {
SharedPreferences prefs = BaseApplication.context.getSharedPreferences(AD_PREFS_NAME, 0);
SharedPreferences.Editor editor = prefs.edit();
editor.putInt(getAdDisplayCountKey(), adDisplayCount);
editor.apply();
}
private void saveAdClickCount() {
SharedPreferences prefs = BaseApplication.context.getSharedPreferences(AD_PREFS_NAME, 0);
SharedPreferences.Editor editor = prefs.edit();
editor.putInt(getAdClickCountKey(), adClickCount);
editor.apply();
}
private int getMaxAdDisplayCount() {
return maxAdDisplayCount;
}
public void setMaxAdDisplayCount(int maxAdDisplayCount) {
this.maxAdDisplayCount = maxAdDisplayCount;
saveMaxAdDisplayCount();
}
public int getMaxAdClickCount() {
return maxAdClickCount;
}
public void setMaxAdClickCount(int maxAdClickCount) {
this.maxAdClickCount = maxAdClickCount;
saveMaxAdClickCount();
}
private void saveMaxAdDisplayCount() {
SharedPreferences prefs = BaseApplication.context.getSharedPreferences(AD_PREFS_NAME, 0);
SharedPreferences.Editor editor = prefs.edit();
editor.putInt(MAX_AD_DISPLAY_COUNT_KEY, maxAdDisplayCount);
editor.apply();
}
private void saveMaxAdClickCount() {
SharedPreferences prefs = BaseApplication.context.getSharedPreferences(AD_PREFS_NAME, 0);
SharedPreferences.Editor editor = prefs.edit();
editor.putInt(MAX_AD_CLICK_COUNT_KEY, maxAdClickCount);
editor.apply();
}
}
package com.base.datarecovery.ads
import com.base.datarecovery.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
package com.base.datarecovery.ads
import android.app.Activity
import android.os.Bundle
import com.base.datarecovery.help.BaseApplication
import com.google.android.gms.ads.AdValue
import com.google.android.gms.ads.OnPaidEventListener
import com.google.android.gms.ads.ResponseInfo
import com.google.android.gms.ads.appopen.AppOpenAd
import com.google.android.gms.ads.interstitial.InterstitialAd
import com.google.android.gms.ads.nativead.NativeAd
import com.google.android.gms.ads.rewarded.RewardedAd
import com.google.firebase.analytics.FirebaseAnalytics
import com.google.firebase.analytics.ktx.analytics
import com.google.firebase.ktx.Firebase
import org.json.JSONObject
object AdmobEvent {
fun pullAd(
responseInfo: ResponseInfo?,
adUnit: String,
error: String? = null,
reqId: String? = null
) {
val obj = JSONObject()
if (responseInfo != null) {
val response = responseInfo.adapterResponses.getOrNull(0)
if (response != null) {
obj.put("source", response.adSourceName)
val credentials = mapOf(
"placementid" to response.credentials.get("placementid"),
"appid" to response.credentials.get("appid"),
"pubid" to response.credentials.get("pubid")
)
obj.put("credentials", credentials.toString())
}
obj.put("session_id", responseInfo.responseId)
}
obj.put("networkname", responseInfo?.mediationAdapterClassName)
obj.put("ad_unit", adUnit)
obj.put("req_id", reqId)
if (error == null) {
obj.put("status", "1")
} else {
obj.put("errMsg", error)
obj.put("status", "2")
}
// EventUtils.event("ad_pull", ext = obj)
}
private val taichiPref by lazy {
BaseApplication.context.getSharedPreferences("TaichiTroasCache", 0)
}
private val taichiSharedPreferencesEditor by lazy {
taichiPref.edit()
}
class EventOnPaidEventListener(private val ad: Any?) : OnPaidEventListener {
override fun onPaidEvent(adValue: AdValue) {
val valueMicros = adValue.valueMicros
val currencyCode = adValue.currencyCode
val precision = adValue.precisionType
val obj = JSONObject()
obj.put("valueMicros", valueMicros)
obj.put("currencyCode", currencyCode)
obj.put("precision", precision)
Firebase.analytics.logEvent("ad_price", Bundle().apply {
putDouble("valueMicros", valueMicros / 1000000.0)
})
val params = Bundle()
val currentImpressionRevenue = adValue.valueMicros.toDouble() / 1000000.0
params.putDouble(FirebaseAnalytics.Param.VALUE, currentImpressionRevenue)
params.putString(FirebaseAnalytics.Param.CURRENCY, "USD")
val precisionType = when (adValue.precisionType) {
0 -> "UNKNOWN"
1 -> "ESTIMATED"
2 -> "PUBLISHER_PROVIDED"
3 -> "PRECISE"
else -> "Invalid"
}
params.putString("precisionType", precisionType)
Firebase.analytics.logEvent("Ad_Impression_Revenue", params)
val previousTaichiTroasCache = taichiPref.getFloat("TaichiTroasCache", 0f)
val currentTaichiTroasCache = (previousTaichiTroasCache +
currentImpressionRevenue).toFloat()
if (currentTaichiTroasCache >= 0.01) {//如果超过0.01就触发一次tROAS taichi事件
val roasbundle = Bundle()
roasbundle.putDouble(
FirebaseAnalytics.Param.VALUE,
currentTaichiTroasCache.toDouble()
)
roasbundle.putString(FirebaseAnalytics.Param.CURRENCY, "USD")
Firebase.analytics.logEvent("Total_Ads_Revenue_001", roasbundle)
taichiSharedPreferencesEditor.putFloat("TaichiTroasCache", 0f)//重新清零,开始计算
} else {
taichiSharedPreferencesEditor.putFloat("TaichiTroasCache", currentTaichiTroasCache)
}
taichiSharedPreferencesEditor.commit()
when (ad) {
is AppOpenAd -> {
val adUnitId = ad.adUnitId
val loadedAdapterResponseInfo = ad.responseInfo.loadedAdapterResponseInfo
val adSourceName = loadedAdapterResponseInfo?.adSourceName
val adSourceId = loadedAdapterResponseInfo?.adSourceId
val adSourceInstanceName = loadedAdapterResponseInfo?.adSourceInstanceName
val adSourceInstanceId = loadedAdapterResponseInfo?.adSourceInstanceId
val sessionId = ad.responseInfo.responseId
val extras = ad.responseInfo.responseExtras
val mediationGroupName = extras.getString("mediation_group_name")
val mediationABTestName = extras.getString("mediation_ab_test_name")
val mediationABTestVariant = extras.getString("mediation_ab_test_variant")
obj.put("adUnitId", adUnitId)
obj.put("adSourceName", adSourceName)
obj.put("adSourceId", adSourceId)
obj.put("adSourceInstanceName", adSourceInstanceName)
obj.put("adSourceInstanceId", adSourceInstanceId)
obj.put("mediationGroupName", mediationGroupName)
obj.put("mediationABTestName", mediationABTestName)
obj.put("mediationABTestVariant", mediationABTestVariant)
obj.put("session_id", sessionId)
}
is InterstitialAd -> {
val adUnitId = ad.adUnitId
val loadedAdapterResponseInfo = ad.responseInfo.loadedAdapterResponseInfo
val adSourceName = loadedAdapterResponseInfo?.adSourceName
val adSourceId = loadedAdapterResponseInfo?.adSourceId
val adSourceInstanceName = loadedAdapterResponseInfo?.adSourceInstanceName
val adSourceInstanceId = loadedAdapterResponseInfo?.adSourceInstanceId
val sessionId = ad.responseInfo.responseId
val extras = ad.responseInfo.responseExtras
val mediationGroupName = extras.getString("mediation_group_name")
val mediationABTestName = extras.getString("mediation_ab_test_name")
val mediationABTestVariant = extras.getString("mediation_ab_test_variant")
obj.put("adUnitId", adUnitId)
obj.put("adSourceName", adSourceName)
obj.put("adSourceId", adSourceId)
obj.put("adSourceInstanceName", adSourceInstanceName)
obj.put("adSourceInstanceId", adSourceInstanceId)
obj.put("mediationGroupName", mediationGroupName)
obj.put("mediationABTestName", mediationABTestName)
obj.put("mediationABTestVariant", mediationABTestVariant)
obj.put("session_id", sessionId)
}
is RewardedAd -> {
val loadedAdapterResponseInfo = ad.responseInfo.loadedAdapterResponseInfo
val adSourceName = loadedAdapterResponseInfo?.adSourceName
val adSourceId = loadedAdapterResponseInfo?.adSourceId
val adSourceInstanceName = loadedAdapterResponseInfo?.adSourceInstanceName
val adSourceInstanceId = loadedAdapterResponseInfo?.adSourceInstanceId
val sessionId = ad.responseInfo.responseId
val extras = ad.responseInfo.responseExtras
val mediationGroupName = extras.getString("mediation_group_name")
val mediationABTestName = extras.getString("mediation_ab_test_name")
val mediationABTestVariant = extras.getString("mediation_ab_test_variant")
obj.put("adSourceName", adSourceName)
obj.put("adSourceId", adSourceId)
obj.put("adSourceInstanceName", adSourceInstanceName)
obj.put("adSourceInstanceId", adSourceInstanceId)
obj.put("mediationGroupName", mediationGroupName)
obj.put("mediationABTestName", mediationABTestName)
obj.put("mediationABTestVariant", mediationABTestVariant)
obj.put("session_id", sessionId)
}
is NativeAd -> {
val loadedAdapterResponseInfo = ad.responseInfo?.loadedAdapterResponseInfo
val adSourceName = loadedAdapterResponseInfo?.adSourceName
val adSourceId = loadedAdapterResponseInfo?.adSourceId
val adSourceInstanceName = loadedAdapterResponseInfo?.adSourceInstanceName
val adSourceInstanceId = loadedAdapterResponseInfo?.adSourceInstanceId
val sessionId = ad.responseInfo?.responseId
val extras = ad.responseInfo?.responseExtras
val mediationGroupName = extras?.getString("mediation_group_name")
val mediationABTestName = extras?.getString("mediation_ab_test_name")
val mediationABTestVariant = extras?.getString("mediation_ab_test_variant")
obj.put("adSourceName", adSourceName)
obj.put("adSourceId", adSourceId)
obj.put("adSourceInstanceName", adSourceInstanceName)
obj.put("adSourceInstanceId", adSourceInstanceId)
obj.put("mediationGroupName", mediationGroupName)
obj.put("mediationABTestName", mediationABTestName)
obj.put("mediationABTestVariant", mediationABTestVariant)
obj.put("session_id", sessionId)
}
}
// EventUtils.event("ad_price", ext = obj)
}
}
fun clickAd(responseInfo: ResponseInfo?, adUnit: String) {
val response = responseInfo?.adapterResponses?.getOrNull(0)
val obj = JSONObject()
obj.put("source", response?.adSourceName)
obj.put("ad_unit", adUnit)
val credentials = mapOf(
"placementid" to response?.credentials?.get("placementid"),
"appid" to response?.credentials?.get("appid"),
"pubid" to response?.credentials?.get("pubid")
)
obj.put("credentials", credentials.toString())
obj.put("session_id", responseInfo?.responseId)
obj.put("networkname", responseInfo?.mediationAdapterClassName)
if (adUnit != "nativeAd") {
// EventUtils.event("ad_click", ext = obj)
} else {
// EventUtils.event("bigimage_ad_click", ext = obj)
}
}
fun showAd(responseInfo: ResponseInfo?, adUnit: String, activity: Activity? = null) {
val response = responseInfo?.adapterResponses?.getOrNull(0)
val obj = JSONObject()
obj.put("source", response?.adSourceName)
obj.put("ad_unit", adUnit)
obj.put("networkname", responseInfo?.mediationAdapterClassName)
val credentials = mapOf(
"placementid" to response?.credentials?.get("placementid"),
"appid" to response?.credentials?.get("appid"),
"pubid" to response?.credentials?.get("pubid")
)
obj.put("credentials", credentials.toString())
obj.put("session_id", responseInfo?.responseId)
obj.put("from", activity?.javaClass?.simpleName)
if (adUnit != "nativeAd") {
// EventUtils.event("ad_show", ext = obj)
} else {
// EventUtils.event("bigimage_ad_show", ext = obj)
}
}
}
\ No newline at end of file
package com.base.datarecovery.ads
import android.app.Activity
import android.view.ViewGroup
import androidx.core.view.isVisible
import com.base.datarecovery.ads.AdmobCommonUtils.isMultiClick
import com.base.datarecovery.ads.AdmobEvent.clickAd
import com.base.datarecovery.ads.AdmobEvent.pullAd
import com.base.datarecovery.ads.AdmobEvent.showAd
import com.base.datarecovery.help.BaseApplication
import com.base.datarecovery.help.ConfigHelper
import com.google.android.gms.ads.AdListener
import com.google.android.gms.ads.AdLoader
import com.google.android.gms.ads.AdRequest
import com.google.android.gms.ads.LoadAdError
import com.google.android.gms.ads.nativead.NativeAd
import org.json.JSONObject
import java.util.UUID
object AdmobNativeUtils {
private var nativeAd: NativeAd? = null
private var isLoading = false
private var nativeLoadTime = Long.MAX_VALUE
private var loadingListener: (() -> Unit)? = null
private val mRequest = AdRequest.Builder().build()
fun loadNativeAd() {
if (nativeAd != null) {
return
}
if (isLoading) {
return
}
isLoading = true
if (!AdDisplayUtils.getInstance().shouldShowAd()) {
return
}
val reqId = UUID.randomUUID().toString()
val obj = JSONObject()
obj.put("req_id", reqId)
obj.put("ad_type", "nativeAd")
val adLoader = AdLoader.Builder(
BaseApplication.context, ConfigHelper.nativeAdmobId
).forNativeAd {
nativeLoadTime = System.currentTimeMillis()
nativeAd = it
isLoading = false
loadingListener?.invoke()
pullAd(it.responseInfo, "nativeAd", reqId = reqId)
it.setOnPaidEventListener(AdmobEvent.EventOnPaidEventListener(it))
}.withAdListener(object : AdListener() {
override fun onAdClicked() {
clickAd(nativeAd?.responseInfo, "nativeAd")
isMultiClick(nativeAd)
}
override fun onAdFailedToLoad(p0: LoadAdError) {
nativeAd = null
isLoading = false
pullAd(p0.responseInfo, "nativeAd", p0.message, reqId = reqId)
// Log.e("MXL", "NativeAdFailedToLoad: " + p0.message)
}
}).build()
adLoader.loadAd(mRequest)
}
fun showNativeAd(activity: Activity?, parent: ViewGroup) {
val obj = JSONObject()
obj.put("ad_unit", "NativeAd")
// EventUtils.event("ad_prepare_show", ext = obj)
if (!AdDisplayUtils.getInstance().shouldShowAd()) {
return
}
loadingListener = {
if (System.currentTimeMillis() - nativeLoadTime <= 1000 * 60 * 60) {
nativeAd?.let {
NativeView(parent.context).run {
parent.removeAllViews()
setNativeAd(it)
parent.addView(this)
parent.isVisible = true
showAd(nativeAd?.responseInfo, "nativeAd", activity)
}
}
}
nativeAd = null
loadingListener = null
loadNativeAd()
}
if (nativeAd == null) {
loadNativeAd()
val obj = JSONObject()
obj.put("reason", "no_ad")
obj.put("ad_unit", "nativeAd")
// EventUtils.event("ad_show_error", ext = obj)
} else {
loadingListener?.invoke()
}
}
}
\ No newline at end of file
package com.base.datarecovery.ads
import android.content.Context
import android.util.AttributeSet
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Button
import android.widget.FrameLayout
import android.widget.ImageView
import android.widget.TextView
import com.base.datarecovery.R
import com.google.android.gms.ads.nativead.NativeAd
import com.google.android.gms.ads.nativead.NativeAdView
class NativeView @JvmOverloads constructor(
context: Context, attrs: AttributeSet? = null
) : FrameLayout(context, attrs) {
init {
layoutParams = LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT
)
}
fun setNativeAd(nativeAd: NativeAd?) {
nativeAd ?: return
val adView = LayoutInflater.from(context)
.inflate(R.layout.layout_native, this, false) as NativeAdView
adView.mediaView = adView.findViewById(R.id.ad_media)
adView.headlineView = adView.findViewById(R.id.ad_headline)
adView.bodyView = adView.findViewById(R.id.ad_body)
adView.callToActionView = adView.findViewById(R.id.ad_call_to_action)
adView.iconView = adView.findViewById(R.id.ad_app_icon)
(adView.headlineView as TextView?)?.text = nativeAd.headline
adView.mediaView!!.mediaContent = nativeAd.mediaContent
if (nativeAd.body == null) {
adView.bodyView!!.visibility = View.INVISIBLE
} else {
adView.bodyView!!.visibility = View.VISIBLE
(adView.bodyView as TextView?)?.text = nativeAd.body
}
if (nativeAd.callToAction == null) {
adView.callToActionView!!.visibility = View.INVISIBLE
} else {
adView.callToActionView!!.visibility = View.VISIBLE
(adView.callToActionView as Button?)?.text = nativeAd.callToAction
}
if (nativeAd.icon == null) {
adView.iconView!!.visibility = View.GONE
} else {
(adView.iconView as ImageView?)?.setImageDrawable(
nativeAd.icon!!.drawable
)
adView.iconView!!.visibility = View.VISIBLE
}
adView.setNativeAd(nativeAd)
removeAllViews()
addView(adView)
}
}
\ No newline at end of file
package com.base.datarecovery.bean
import com.base.datarecovery.utils.SPUtils
object ConstObject {
const val SCAN_PHOTOS = 1
const val SCAN_DOCUMENTS = 2
const val SCAN_VIDEOS = 3
var ifAgreePrivacy = false
get() {
return SPUtils.getInstance().getBoolean("ifAgreePrivacy", field)
}
set(value) {
field = value
SPUtils.getInstance().put("ifAgreePrivacy", value, true)
}
}
\ No newline at end of file
package com.base.datarecovery.fragment
import android.animation.AnimatorSet
import android.animation.ObjectAnimator
import android.annotation.SuppressLint
import android.content.Intent
import android.view.View
import android.view.animation.AccelerateDecelerateInterpolator
import com.base.datarecovery.activity.FileScanActivity
import com.base.datarecovery.ads.AdmobNativeUtils
import com.base.datarecovery.bean.ConstObject.SCAN_DOCUMENTS
import com.base.datarecovery.bean.ConstObject.SCAN_PHOTOS
import com.base.datarecovery.bean.ConstObject.SCAN_VIDEOS
......@@ -13,28 +19,68 @@ class HomeFragment : BaseFragment<FragmentHomeBinding>() {
override val binding: FragmentHomeBinding by lazy {
FragmentHomeBinding.inflate(layoutInflater)
}
private lateinit var animatorSet: AnimatorSet
override fun setView() {
animatorSet = createHeartbeatAnimation(binding.flScan)
AdmobNativeUtils.showNativeAd(requireActivity(), binding.flAd)
}
override fun setListener() {
binding.llRyPhotos.setOnClickListener {
binding.flRyPhoto.setOnClickListener {
startActivity(Intent(requireContext(), FileScanActivity::class.java).apply {
putExtra("Type", SCAN_PHOTOS)
})
}
binding.llRyDocuments.setOnClickListener {
binding.flRyVideo.setOnClickListener {
startActivity(Intent(requireContext(), FileScanActivity::class.java).apply {
putExtra("Type", SCAN_DOCUMENTS)
putExtra("Type", SCAN_VIDEOS)
})
}
binding.llRyVideos.setOnClickListener {
binding.cardRyDocument.setOnClickListener {
startActivity(Intent(requireContext(), FileScanActivity::class.java).apply {
putExtra("Type", SCAN_VIDEOS)
putExtra("Type", SCAN_DOCUMENTS)
})
}
}
@SuppressLint("Recycle")
fun createHeartbeatAnimation(view: View): AnimatorSet {
// 创建放大动画
val scaleUpAnimator = ObjectAnimator.ofFloat(view, "scaleX", 1f, 1.1f)
scaleUpAnimator.setDuration(500)
scaleUpAnimator.interpolator = AccelerateDecelerateInterpolator()
scaleUpAnimator.repeatCount = ObjectAnimator.INFINITE
scaleUpAnimator.repeatMode = ObjectAnimator.REVERSE
// 创建缩小动画
val scaleDownAnimator = ObjectAnimator.ofFloat(view, "scaleY", 1.1f, 1f)
scaleDownAnimator.setDuration(500)
scaleDownAnimator.interpolator = AccelerateDecelerateInterpolator()
scaleDownAnimator.setStartDelay(500) // 延迟开始,以便与放大动画同步结束
scaleDownAnimator.repeatCount = ObjectAnimator.INFINITE
scaleDownAnimator.repeatMode = ObjectAnimator.REVERSE
// 组合放大和缩小动画
val animatorSet = AnimatorSet()
animatorSet.play(scaleUpAnimator).with(scaleDownAnimator)
// 启动动画
animatorSet.start()
return animatorSet
}
override fun onPause() {
super.onPause()
animatorSet.pause()
}
override fun onResume() {
super.onResume()
animatorSet.resume()
}
}
\ No newline at end of file
......@@ -5,9 +5,8 @@ import android.content.Intent
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import androidx.viewbinding.ViewBinding
import com.base.datarecovery.MainActivity
import com.base.datarecovery.activity.MainActivity
import com.base.datarecovery.utils.ActivityManagerUtils
import com.base.datarecovery.utils.BarUtils
abstract class BaseActivity<T : ViewBinding> : AppCompatActivity() {
......
package com.base.datarecovery.help
import android.app.Application
abstract class BaseApplication : Application() {
companion object {
lateinit var context: BaseApplication
}
final override fun onCreate() {
super.onCreate()
context = this
init()
}
abstract fun init()
}
\ No newline at end of file
package com.base.datarecovery.help
import com.base.datarecovery.activity.SplashActivity
import com.base.datarecovery.utils.SPUtils
object ConfigHelper {
const val privacyPolicy = "https://sites.google.com/view/easy-cleannow/easy-clean"
// 域名
const val eventUrl = "https://rp.easyfilemanager.xyz"
// const val apiUrl = "https://api.easyfilemanager.xyz"
// admob广告id
const val openAdmobId = "/6499/example/app-open"
const val interAdmobId = "ca-app-pub-3940256099942544/1033173712"
const val nativeAdmobId = "ca-app-pub-3940256099942544/2247696110"
// 正式包名
const val packageName = "com.kk.junkcleaner.easy.zxkk"
val noLoadingActivities = listOf(
"full", // 过滤全屏广告
"adActivity",
SplashActivity::class.java.simpleName
// 返回前台时不跳转启动页的 activity
)
}
\ No newline at end of file
package com.base.datarecovery.utils;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.SharedPreferences;
import androidx.annotation.NonNull;
import com.base.datarecovery.help.BaseApplication;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
@SuppressLint("ApplySharedPref")
public final class SPUtils {
private static final Map<String, SPUtils> SP_UTILS_MAP = new HashMap<>();
private SharedPreferences sp;
/**
* Return the single {@link SPUtils} instance
*
* @return the single {@link SPUtils} instance
*/
public static SPUtils getInstance() {
return getInstance("", Context.MODE_PRIVATE);
}
/**
* Return the single {@link SPUtils} instance
*
* @param mode Operating mode.
* @return the single {@link SPUtils} instance
*/
public static SPUtils getInstance(final int mode) {
return getInstance("", mode);
}
/**
* Return the single {@link SPUtils} instance
*
* @param spName The name of sp.
* @return the single {@link SPUtils} instance
*/
public static SPUtils getInstance(String spName) {
return getInstance(spName, Context.MODE_PRIVATE);
}
/**
* Return the single {@link SPUtils} instance
*
* @param spName The name of sp.
* @param mode Operating mode.
* @return the single {@link SPUtils} instance
*/
public static SPUtils getInstance(String spName, final int mode) {
if (isSpace(spName)) spName = "spUtils";
SPUtils spUtils = SP_UTILS_MAP.get(spName);
if (spUtils == null) {
synchronized (SPUtils.class) {
spUtils = SP_UTILS_MAP.get(spName);
if (spUtils == null) {
spUtils = new SPUtils(spName, mode);
SP_UTILS_MAP.put(spName, spUtils);
}
}
}
return spUtils;
}
private SPUtils(final String spName) {
sp = BaseApplication.context.getSharedPreferences(spName, Context.MODE_PRIVATE);
}
private SPUtils(final String spName, final int mode) {
sp = BaseApplication.context.getSharedPreferences(spName, mode);
}
/**
* Put the string value in sp.
*
* @param key The key of sp.
* @param value The value of sp.
*/
public void put(@NonNull final String key, final String value) {
put(key, value, false);
}
/**
* Put the string value in sp.
*
* @param key The key of sp.
* @param value The value of sp.
* @param isCommit True to use {@link SharedPreferences.Editor#commit()},
* false to use {@link SharedPreferences.Editor#apply()}
*/
public void put(@NonNull final String key, final String value, final boolean isCommit) {
if (isCommit) {
sp.edit().putString(key, value).commit();
} else {
sp.edit().putString(key, value).apply();
}
}
/**
* Return the string value in sp.
*
* @param key The key of sp.
* @return the string value if sp exists or {@code ""} otherwise
*/
public String getString(@NonNull final String key) {
return getString(key, "");
}
/**
* Return the string value in sp.
*
* @param key The key of sp.
* @param defaultValue The default value if the sp doesn't exist.
* @return the string value if sp exists or {@code defaultValue} otherwise
*/
public String getString(@NonNull final String key, final String defaultValue) {
return sp.getString(key, defaultValue);
}
/**
* Put the int value in sp.
*
* @param key The key of sp.
* @param value The value of sp.
*/
public void put(@NonNull final String key, final int value) {
put(key, value, false);
}
/**
* Put the int value in sp.
*
* @param key The key of sp.
* @param value The value of sp.
* @param isCommit True to use {@link SharedPreferences.Editor#commit()},
* false to use {@link SharedPreferences.Editor#apply()}
*/
public void put(@NonNull final String key, final int value, final boolean isCommit) {
if (isCommit) {
sp.edit().putInt(key, value).commit();
} else {
sp.edit().putInt(key, value).apply();
}
}
/**
* Return the int value in sp.
*
* @param key The key of sp.
* @return the int value if sp exists or {@code -1} otherwise
*/
public int getInt(@NonNull final String key) {
return getInt(key, -1);
}
/**
* Return the int value in sp.
*
* @param key The key of sp.
* @param defaultValue The default value if the sp doesn't exist.
* @return the int value if sp exists or {@code defaultValue} otherwise
*/
public int getInt(@NonNull final String key, final int defaultValue) {
return sp.getInt(key, defaultValue);
}
/**
* Put the long value in sp.
*
* @param key The key of sp.
* @param value The value of sp.
*/
public void put(@NonNull final String key, final long value) {
put(key, value, false);
}
/**
* Put the long value in sp.
*
* @param key The key of sp.
* @param value The value of sp.
* @param isCommit True to use {@link SharedPreferences.Editor#commit()},
* false to use {@link SharedPreferences.Editor#apply()}
*/
public void put(@NonNull final String key, final long value, final boolean isCommit) {
if (isCommit) {
sp.edit().putLong(key, value).commit();
} else {
sp.edit().putLong(key, value).apply();
}
}
/**
* Return the long value in sp.
*
* @param key The key of sp.
* @return the long value if sp exists or {@code -1} otherwise
*/
public long getLong(@NonNull final String key) {
return getLong(key, -1L);
}
/**
* Return the long value in sp.
*
* @param key The key of sp.
* @param defaultValue The default value if the sp doesn't exist.
* @return the long value if sp exists or {@code defaultValue} otherwise
*/
public long getLong(@NonNull final String key, final long defaultValue) {
return sp.getLong(key, defaultValue);
}
/**
* Put the float value in sp.
*
* @param key The key of sp.
* @param value The value of sp.
*/
public void put(@NonNull final String key, final float value) {
put(key, value, false);
}
/**
* Put the float value in sp.
*
* @param key The key of sp.
* @param value The value of sp.
* @param isCommit True to use {@link SharedPreferences.Editor#commit()},
* false to use {@link SharedPreferences.Editor#apply()}
*/
public void put(@NonNull final String key, final float value, final boolean isCommit) {
if (isCommit) {
sp.edit().putFloat(key, value).commit();
} else {
sp.edit().putFloat(key, value).apply();
}
}
/**
* Return the float value in sp.
*
* @param key The key of sp.
* @return the float value if sp exists or {@code -1f} otherwise
*/
public float getFloat(@NonNull final String key) {
return getFloat(key, -1f);
}
/**
* Return the float value in sp.
*
* @param key The key of sp.
* @param defaultValue The default value if the sp doesn't exist.
* @return the float value if sp exists or {@code defaultValue} otherwise
*/
public float getFloat(@NonNull final String key, final float defaultValue) {
return sp.getFloat(key, defaultValue);
}
/**
* Put the boolean value in sp.
*
* @param key The key of sp.
* @param value The value of sp.
*/
public void put(@NonNull final String key, final boolean value) {
put(key, value, false);
}
/**
* Put the boolean value in sp.
*
* @param key The key of sp.
* @param value The value of sp.
* @param isCommit True to use {@link SharedPreferences.Editor#commit()},
* false to use {@link SharedPreferences.Editor#apply()}
*/
public void put(@NonNull final String key, final boolean value, final boolean isCommit) {
if (isCommit) {
sp.edit().putBoolean(key, value).commit();
} else {
sp.edit().putBoolean(key, value).apply();
}
}
/**
* Return the boolean value in sp.
*
* @param key The key of sp.
* @return the boolean value if sp exists or {@code false} otherwise
*/
public boolean getBoolean(@NonNull final String key) {
return getBoolean(key, false);
}
/**
* Return the boolean value in sp.
*
* @param key The key of sp.
* @param defaultValue The default value if the sp doesn't exist.
* @return the boolean value if sp exists or {@code defaultValue} otherwise
*/
public boolean getBoolean(@NonNull final String key, final boolean defaultValue) {
return sp.getBoolean(key, defaultValue);
}
/**
* Put the set of string value in sp.
*
* @param key The key of sp.
* @param value The value of sp.
*/
public void put(@NonNull final String key, final Set<String> value) {
put(key, value, false);
}
/**
* Put the set of string value in sp.
*
* @param key The key of sp.
* @param value The value of sp.
* @param isCommit True to use {@link SharedPreferences.Editor#commit()},
* false to use {@link SharedPreferences.Editor#apply()}
*/
public void put(@NonNull final String key,
final Set<String> value,
final boolean isCommit) {
if (isCommit) {
sp.edit().putStringSet(key, value).commit();
} else {
sp.edit().putStringSet(key, value).apply();
}
}
/**
* Return the set of string value in sp.
*
* @param key The key of sp.
* @return the set of string value if sp exists
* or {@code Collections.<String>emptySet()} otherwise
*/
public Set<String> getStringSet(@NonNull final String key) {
return getStringSet(key, Collections.<String>emptySet());
}
/**
* Return the set of string value in sp.
*
* @param key The key of sp.
* @param defaultValue The default value if the sp doesn't exist.
* @return the set of string value if sp exists or {@code defaultValue} otherwise
*/
public Set<String> getStringSet(@NonNull final String key,
final Set<String> defaultValue) {
return sp.getStringSet(key, defaultValue);
}
/**
* Return all values in sp.
*
* @return all values in sp
*/
public Map<String, ?> getAll() {
return sp.getAll();
}
/**
* Return whether the sp contains the preference.
*
* @param key The key of sp.
* @return {@code true}: yes<br>{@code false}: no
*/
public boolean contains(@NonNull final String key) {
return sp.contains(key);
}
/**
* Remove the preference in sp.
*
* @param key The key of sp.
*/
public void remove(@NonNull final String key) {
remove(key, false);
}
/**
* Remove the preference in sp.
*
* @param key The key of sp.
* @param isCommit True to use {@link SharedPreferences.Editor#commit()},
* false to use {@link SharedPreferences.Editor#apply()}
*/
public void remove(@NonNull final String key, final boolean isCommit) {
if (isCommit) {
sp.edit().remove(key).commit();
} else {
sp.edit().remove(key).apply();
}
}
/**
* Remove all preferences in sp.
*/
public void clear() {
clear(false);
}
/**
* Remove all preferences in sp.
*
* @param isCommit True to use {@link SharedPreferences.Editor#commit()},
* false to use {@link SharedPreferences.Editor#apply()}
*/
public void clear(final boolean isCommit) {
if (isCommit) {
sp.edit().clear().commit();
} else {
sp.edit().clear().apply();
}
}
private static boolean isSpace(final String s) {
if (s == null) return true;
for (int i = 0, len = s.length(); i < len; ++i) {
if (!Character.isWhitespace(s.charAt(i))) {
return false;
}
}
return true;
}
}
......@@ -6,11 +6,29 @@ import android.content.Context
import android.graphics.Color
import android.graphics.drawable.ColorDrawable
import android.view.LayoutInflater
import android.view.View
import androidx.lifecycle.LifecycleCoroutineScope
import com.base.datarecovery.R
import com.base.datarecovery.databinding.DialogPermissonOpenBinding
import com.base.datarecovery.databinding.DialogRecoveringBinding
import com.base.datarecovery.utils.LogEx
import com.google.android.material.bottomsheet.BottomSheetBehavior
import com.google.android.material.bottomsheet.BottomSheetDialog
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.cancel
import kotlinx.coroutines.channels.BufferOverflow
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.SharedFlow
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.launch
import java.io.File
import kotlin.random.Random
object DialogViews {
private val TAG = "DialogViews"
@SuppressLint("SetTextI18n")
fun Context.showGerPermission(
tittle: String? = null,
......@@ -42,4 +60,79 @@ object DialogViews {
dialog.show()
return dialog
}
@SuppressLint("SetTextI18n")
fun Context.showRecoveringDialog(
lifecycleScope: LifecycleCoroutineScope,
list: List<String>,
dir: File,
copyProgressAction: () -> Unit,
finish: () -> Unit
) {
val dialog = BottomSheetDialog(this)
val binding = DialogRecoveringBinding.inflate(LayoutInflater.from(this))
dialog.setContentView(binding.root)
dialog.setCanceledOnTouchOutside(false)
val mutableSharedFlow = MutableSharedFlow<Int>(
replay = 5,//当新的订阅者Collect时,发送几个已经发送过的数据给它
extraBufferCapacity = 5,//减去replay,MutableSharedFlow还缓存多少数据,缓冲池容量 = replay + extraBufferCapacity
onBufferOverflow = BufferOverflow.SUSPEND//缓存策略,三种 丢掉最新值、丢掉最旧值和挂起
)
val sharedFlow: SharedFlow<Int> = mutableSharedFlow
val parentView = binding.root.parent as View
val behavior = BottomSheetBehavior.from(parentView)
//展开
behavior.state = BottomSheetBehavior.STATE_EXPANDED
// 设置禁止通过拖动来隐藏
behavior.isHideable = false
// 禁止点击外部区域关闭
dialog.setOnCancelListener { dialogInterface ->
dialogInterface.cancel() // 这里可以处理点击外部区域的逻辑
}
lifecycleScope.launch(Dispatchers.IO) {
val arrayList = arrayListOf<String>().apply {
addAll(list)
}
var index = 0
while (arrayList.isNotEmpty()) {
val path = arrayList[0]
arrayList.removeAt(0)
val file = File(path)
runCatching {
val recoveryFile = File(dir, file.name)
file.copyTo(recoveryFile, true)
}
mutableSharedFlow.emit(index + 1)
index++
delay(Random.nextLong(500, 1500))
}
mutableSharedFlow.emit(-1)
}
lifecycleScope.launch(Dispatchers.Main) {
sharedFlow.collectLatest {
LogEx.logDebug(TAG, "Flow $it")
if (it == -1) {
finish.invoke()
dialog.dismiss()
cancel()
} else {
val process = it.toFloat() / list.size.toFloat()
if (process > 0.3) {
copyProgressAction.invoke()
}
binding.tvNumber.text = "${it + 1}/${list.size}"
}
}
}
dialog.show()
}
}
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="#577DFD" />
<corners
android:topLeftRadius="20dp"
android:topRightRadius="20dp" />
</shape>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="@android:id/background">
<shape>
<stroke
android:width="1px"
android:color="#FF3835" />
<corners android:radius="6dp" />
</shape>
</item>
<item
android:id="@android:id/progress"
android:bottom="3dp"
android:end="3dp"
android:start="3dp"
android:top="3dp">
<scale android:scaleWidth="100%">
<shape>
<corners android:radius="5dp" />
<solid android:color="#FF3835" />
</shape>
</scale>
</item>
</layer-list>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@color/white"/>
<!-- <item android:drawable="@drawable/splash_bg" />-->
<item
android:top="130dp"
android:gravity="top|center_horizontal">
<bitmap
android:src="@mipmap/qdylogo" />
</item>
</layer-list>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/main"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".activity.FileRecoveredActivity">
</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
......@@ -178,7 +178,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:src="@drawable/b_circle_selector"
android:src="@drawable/bg_circle_selector"
tools:ignore="ContentDescription" />
<TextView
......
......@@ -5,7 +5,7 @@
android:id="@+id/main"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
tools:context=".activity.MainActivity">
<fragment
android:id="@+id/fragment"
......
<?xml version="1.0" encoding="utf-8"?>
<androidx.appcompat.widget.LinearLayoutCompat xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/splash_bp"
android:gravity="center_horizontal"
android:orientation="vertical">
<View
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_weight="1" />
<View
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_weight="2" />
<LinearLayout
android:id="@+id/ll_progress"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="49dp"
android:gravity="center_horizontal"
android:orientation="vertical"
android:visibility="gone">
<ProgressBar
android:id="@+id/pb"
style="@style/Widget.AppCompat.ProgressBar.Horizontal"
android:layout_width="match_parent"
android:layout_height="15dp"
android:layout_marginHorizontal="32dp"
android:layout_marginTop="5dp"
android:max="100"
android:progressDrawable="@drawable/shape_splash_s"
tools:progress="50" />
<TextView
android:id="@+id/tv_load"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="18dp"
android:text="Loading..."
android:textColor="#000000"
android:textSize="15sp"
tools:ignore="HardcodedText" />
<TextView
android:id="@+id/tv_ad_des"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="42dp"
android:text="This process may involve ad."
android:textColor="#000000"
android:textSize="15sp"
tools:ignore="HardcodedText" />
</LinearLayout>
<LinearLayout
android:id="@+id/ll_start"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="20dp"
android:gravity="center">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginHorizontal="4dp"
android:text="By continuing you are agreeing to the"
android:textColor="#676767"
android:textSize="14sp"
tools:ignore="HardcodedText" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text=" &amp; "
android:visibility="gone"
tools:ignore="HardcodedText" />
<TextView
android:id="@+id/id_tv_privacy_policy"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Privacy Policy"
android:textColor="#676767"
android:textSize="14sp"
tools:ignore="HardcodedText" />
</LinearLayout>
<TextView
android:id="@+id/id_tv_start"
android:layout_width="match_parent"
android:layout_height="48dp"
android:layout_marginHorizontal="40dp"
android:layout_marginBottom="49dp"
android:background="#577CFB"
android:gravity="center"
android:text="START TO USE"
android:textColor="@color/white"
android:textSize="18sp"
android:textStyle="bold"
android:visibility="visible"
tools:ignore="HardcodedText" />
</LinearLayout>
</androidx.appcompat.widget.LinearLayoutCompat>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/bg_ss"
android:orientation="vertical">
<TextView
android:id="@+id/tv_number"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="18dp"
android:layout_marginTop="40dp"
android:textColor="@color/white"
android:textSize="45sp"
android:textStyle="bold"
tools:text="0/1" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="18dp"
android:layout_marginTop="25dp"
android:text="Recovering..."
android:textColor="@color/white"
android:textSize="25sp"
tools:ignore="HardcodedText" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginHorizontal="18dp"
android:layout_marginTop="15dp"
android:text="It may take a few seconds to recover the file(s), please wait.."
android:textColor="@color/white"
android:textSize="15sp"
tools:ignore="HardcodedText" />
<FrameLayout
android:id="@+id/fl_ad"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="26dp" />
</LinearLayout>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#F6F6F6"
android:orientation="vertical"
tools:context=".fragment.HomeFragment">
<View
android:id="@+id/v_top"
android:layout_width="match_parent"
android:layout_height="40dp"
app:layout_constraintTop_toTopOf="parent" />
<ImageView
<androidx.core.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="232dp"
android:background="@mipmap/bg"
app:layout_constraintTop_toTopOf="parent"
tools:ignore="ContentDescription" />
android:layout_height="wrap_content">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintTop_toBottomOf="@id/v_top">
<ImageView
android:layout_width="wrap_content"
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="26dp"
android:layout_marginTop="24dp"
android:src="@mipmap/icon"
tools:ignore="ContentDescription" />
android:orientation="vertical">
<androidx.cardview.widget.CardView
android:layout_width="match_parent"
android:layout_height="131dp"
android:layout_marginHorizontal="16sp"
android:layout_marginTop="36dp"
app:cardCornerRadius="10dp"
app:cardElevation="5dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:text="@string/app_name"
android:textSize="19sp"
android:textStyle="bold" />
<FrameLayout
android:id="@+id/fl_scan"
android:layout_width="214dp"
android:layout_height="214dp"
android:layout_gravity="center_horizontal"
android:layout_marginTop="30dp"
android:background="@mipmap/yuan">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:text="Scan"
android:textSize="34sp"
android:textStyle="bold"
tools:ignore="HardcodedText" />
</FrameLayout>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="15dp"
android:layout_marginTop="23dp"
android:text="Core Services"
android:textColor="@color/black"
android:textSize="19sp"
android:textStyle="bold"
tools:ignore="HardcodedText" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="8dp"
android:layout_marginTop="18dp"
android:orientation="horizontal"
tools:ignore="DisableBaselineAlignment">
<LinearLayout
android:id="@+id/ll_ry_photos"
<FrameLayout
android:id="@+id/fl_ry_photo"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginStart="12dp"
android:layout_height="80dp"
android:layout_marginHorizontal="8dp"
android:layout_weight="1"
android:orientation="vertical">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:src="@mipmap/recovery_photo"
tools:ignore="ContentDescription" />
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginTop="2dp"
android:src="@mipmap/triangle_1"
tools:ignore="ContentDescription" />
android:background="@mipmap/photo">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginTop="2dp"
android:gravity="center"
android:layout_gravity="center_vertical"
android:layout_marginStart="12dp"
android:text="Recovery\nPhotos"
android:textColor="#333333"
android:textSize="12sp"
android:textColor="@color/white"
android:textSize="16sp"
tools:ignore="HardcodedText" />
</LinearLayout>
</FrameLayout>
<LinearLayout
android:id="@+id/ll_ry_documents"
<FrameLayout
android:id="@+id/fl_ry_video"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_height="80dp"
android:layout_marginHorizontal="8dp"
android:layout_weight="1"
android:orientation="vertical">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:src="@mipmap/recovery_documents"
tools:ignore="ContentDescription" />
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginTop="2dp"
android:src="@mipmap/triangle_2"
tools:ignore="ContentDescription" />
android:background="@mipmap/videos">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginTop="2dp"
android:gravity="center"
android:text="Recovery\nDocuments"
android:textColor="#333333"
android:textSize="12sp"
android:layout_gravity="center_vertical"
android:layout_marginStart="12dp"
android:text="Recovery\nVideos"
android:textColor="@color/white"
android:textSize="16sp"
tools:ignore="HardcodedText" />
</FrameLayout>
</LinearLayout>
</LinearLayout>
<LinearLayout
android:id="@+id/ll_ry_videos"
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="15dp"
android:layout_marginTop="20dp"
android:text="Comprehensive functions"
android:textColor="@color/black"
android:textSize="19sp"
android:textStyle="bold"
tools:ignore="HardcodedText" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="8dp"
android:orientation="horizontal">
<androidx.cardview.widget.CardView
android:id="@+id/card_ry_document"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginEnd="12dp"
android:layout_height="70dp"
android:layout_marginHorizontal="8dp"
android:layout_weight="1"
android:orientation="vertical">
app:cardBackgroundColor="#F8F8F8"
app:cardCornerRadius="10dp"
app:cardElevation="0dp">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:src="@mipmap/recovery_videos"
tools:ignore="ContentDescription" />
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginTop="2dp"
android:src="@mipmap/triangle_3"
tools:ignore="ContentDescription" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal"
tools:ignore="UseCompoundDrawables">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginTop="2dp"
android:gravity="center"
android:text="Recovery\nVideos"
android:textColor="#333333"
android:textSize="12sp"
tools:ignore="HardcodedText" />
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginStart="8dp"
android:src="@mipmap/recovery"
tools:ignore="ContentDescription" />
</LinearLayout>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginStart="8dp"
android:text="Recovery\nDocuments"
android:textColor="@color/black"
android:textSize="15sp"
tools:ignore="HardcodedText" />
</LinearLayout>
</LinearLayout>
</androidx.cardview.widget.CardView>
<FrameLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginHorizontal="16dp">
</androidx.cardview.widget.CardView>
<FrameLayout
android:layout_width="match_parent"
android:layout_height="130dp"
android:layout_marginTop="30dp"
android:background="@mipmap/bg_1">
<androidx.cardview.widget.CardView
android:layout_width="0dp"
android:layout_height="70dp"
android:layout_marginHorizontal="8dp"
android:layout_weight="1"
app:cardBackgroundColor="#F8F8F8"
app:cardCornerRadius="10dp"
app:cardElevation="0dp">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginStart="20dp"
android:orientation="vertical"
tools:ignore="UselessParent">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Duplicate file scanning"
android:textColor="#333333"
android:textSize="16sp"
tools:ignore="HardcodedText" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal"
tools:ignore="UseCompoundDrawables">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:text="Clear a large number of duplicate files"
android:textColor="#333333"
android:textSize="12sp"
tools:ignore="HardcodedText" />
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginStart="8dp"
android:src="@mipmap/space"
tools:ignore="ContentDescription" />
<TextView
android:id="@+id/tv_scan_duplicate"
android:layout_width="111dp"
android:layout_height="30dp"
android:layout_marginTop="12dp"
android:background="@mipmap/button_blue"
android:gravity="center"
android:text="scan"
android:textColor="@color/white"
android:textSize="16sp"
tools:ignore="HardcodedText" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginStart="8dp"
android:text="Privacy\nSpace"
android:textColor="@color/black"
android:textSize="15sp"
tools:ignore="HardcodedText" />
</LinearLayout>
</LinearLayout>
</FrameLayout>
</androidx.cardview.widget.CardView>
<ImageView
android:layout_width="wrap_content"
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="end"
android:layout_marginEnd="10dp"
android:src="@mipmap/tu"
tools:ignore="ContentDescription" />
android:layout_marginHorizontal="8dp"
android:layout_marginTop="12dp"
android:orientation="horizontal">
</FrameLayout>
<androidx.cardview.widget.CardView
android:layout_width="0dp"
android:layout_height="70dp"
android:layout_marginHorizontal="8dp"
android:layout_weight="1"
app:cardBackgroundColor="#F8F8F8"
app:cardCornerRadius="10dp"
app:cardElevation="0dp">
<FrameLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginHorizontal="16dp">
<FrameLayout
android:layout_width="match_parent"
android:layout_height="130dp"
android:layout_marginTop="30dp"
android:background="@mipmap/bg_2">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal"
tools:ignore="UseCompoundDrawables">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginStart="20dp"
android:orientation="vertical"
tools:ignore="UselessParent">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginStart="8dp"
android:src="@mipmap/repeatedphotos"
tools:ignore="ContentDescription" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Recycle bin"
android:textColor="#333333"
android:textSize="16sp"
tools:ignore="HardcodedText" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginStart="8dp"
android:text="Repeated\nPhotos"
android:textColor="@color/black"
android:textSize="15sp"
tools:ignore="HardcodedText" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:textColor="#333333"
android:textSize="12sp"
tools:ignore="HardcodedText" />
</LinearLayout>
<TextView
android:id="@+id/tv_clean_bin"
android:layout_width="111dp"
android:layout_height="30dp"
android:layout_marginTop="12dp"
android:background="@mipmap/button_blue"
android:gravity="center"
android:text="scan"
android:textColor="@color/white"
android:textSize="16sp"
tools:ignore="HardcodedText" />
</LinearLayout>
</androidx.cardview.widget.CardView>
</FrameLayout>
<androidx.cardview.widget.CardView
android:layout_width="0dp"
android:layout_height="70dp"
android:layout_marginHorizontal="8dp"
android:layout_weight="1"
app:cardBackgroundColor="#F8F8F8"
app:cardCornerRadius="10dp"
app:cardElevation="0dp">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="end"
android:layout_marginEnd="10dp"
android:src="@mipmap/tu_1"
tools:ignore="ContentDescription" />
</FrameLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal"
tools:ignore="UseCompoundDrawables">
<FrameLayout
android:id="@+id/fl_recovered"
android:layout_width="match_parent"
android:layout_height="69dp"
android:layout_marginHorizontal="16dp"
android:layout_marginTop="24dp"
android:background="@drawable/bg_gradient_3e7df3_10">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginStart="8dp"
android:src="@mipmap/screenshots"
tools:ignore="ContentDescription" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="45dp"
android:layout_gravity="center"
android:layout_marginHorizontal="12dp"
android:background="@drawable/bg_80ffffff_10"
android:orientation="horizontal">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginStart="8dp"
android:text="Clean\nScreenshots"
android:textColor="@color/black"
android:textSize="15sp"
tools:ignore="HardcodedText" />
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginStart="5dp"
android:src="@mipmap/icon_recovered"
tools:ignore="ContentDescription" />
</LinearLayout>
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginStart="12dp"
android:layout_weight="1"
android:text="Recovered"
android:textColor="@color/white"
android:textSize="16sp"
android:textStyle="bold"
tools:ignore="HardcodedText" />
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginEnd="15dp"
android:src="@mipmap/triangle_4"
tools:ignore="ContentDescription" />
</androidx.cardview.widget.CardView>
</LinearLayout>
</FrameLayout>
<FrameLayout
android:id="@+id/fl_ad"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<FrameLayout
android:layout_marginTop="20dp"
android:layout_marginHorizontal="16dp"
android:id="@+id/fl_ad"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</LinearLayout>
</androidx.core.widget.NestedScrollView>
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
</LinearLayout>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#F6F6F6"
tools:context=".fragment.HomeFragment">
<View
android:id="@+id/v_top"
android:layout_width="match_parent"
android:layout_height="40dp"
app:layout_constraintTop_toTopOf="parent" />
<ImageView
android:layout_width="match_parent"
android:layout_height="232dp"
android:background="@mipmap/bg"
app:layout_constraintTop_toTopOf="parent"
tools:ignore="ContentDescription" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintTop_toBottomOf="@id/v_top">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="26dp"
android:layout_marginTop="24dp"
android:src="@mipmap/icon"
tools:ignore="ContentDescription" />
<androidx.cardview.widget.CardView
android:layout_width="match_parent"
android:layout_height="131dp"
android:layout_marginHorizontal="16sp"
android:layout_marginTop="36dp"
app:cardCornerRadius="10dp"
app:cardElevation="5dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal"
tools:ignore="DisableBaselineAlignment">
<LinearLayout
android:id="@+id/ll_ry_photos"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginStart="12dp"
android:layout_weight="1"
android:orientation="vertical">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:src="@mipmap/recovery_photo"
tools:ignore="ContentDescription" />
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginTop="2dp"
android:src="@mipmap/triangle_1"
tools:ignore="ContentDescription" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginTop="2dp"
android:gravity="center"
android:text="Recovery\nPhotos"
android:textColor="#333333"
android:textSize="12sp"
tools:ignore="HardcodedText" />
</LinearLayout>
<LinearLayout
android:id="@+id/ll_ry_documents"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_weight="1"
android:orientation="vertical">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:src="@mipmap/recovery_documents"
tools:ignore="ContentDescription" />
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginTop="2dp"
android:src="@mipmap/triangle_2"
tools:ignore="ContentDescription" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginTop="2dp"
android:gravity="center"
android:text="Recovery\nDocuments"
android:textColor="#333333"
android:textSize="12sp"
tools:ignore="HardcodedText" />
</LinearLayout>
<LinearLayout
android:id="@+id/ll_ry_videos"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginEnd="12dp"
android:layout_weight="1"
android:orientation="vertical">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:src="@mipmap/recovery_videos"
tools:ignore="ContentDescription" />
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginTop="2dp"
android:src="@mipmap/triangle_3"
tools:ignore="ContentDescription" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginTop="2dp"
android:gravity="center"
android:text="Recovery\nVideos"
android:textColor="#333333"
android:textSize="12sp"
tools:ignore="HardcodedText" />
</LinearLayout>
</LinearLayout>
</androidx.cardview.widget.CardView>
<FrameLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginHorizontal="16dp">
<FrameLayout
android:layout_width="match_parent"
android:layout_height="130dp"
android:layout_marginTop="30dp"
android:background="@mipmap/bg_1">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginStart="20dp"
android:orientation="vertical"
tools:ignore="UselessParent">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Duplicate file scanning"
android:textColor="#333333"
android:textSize="16sp"
tools:ignore="HardcodedText" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:text="Clear a large number of duplicate files"
android:textColor="#333333"
android:textSize="12sp"
tools:ignore="HardcodedText" />
<TextView
android:id="@+id/tv_scan_duplicate"
android:layout_width="111dp"
android:layout_height="30dp"
android:layout_marginTop="12dp"
android:background="@mipmap/button_blue"
android:gravity="center"
android:text="scan"
android:textColor="@color/white"
android:textSize="16sp"
tools:ignore="HardcodedText" />
</LinearLayout>
</FrameLayout>
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="end"
android:layout_marginEnd="10dp"
android:src="@mipmap/tu"
tools:ignore="ContentDescription" />
</FrameLayout>
<FrameLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginHorizontal="16dp">
<FrameLayout
android:layout_width="match_parent"
android:layout_height="130dp"
android:layout_marginTop="30dp"
android:background="@mipmap/bg_2">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginStart="20dp"
android:orientation="vertical"
tools:ignore="UselessParent">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Recycle bin"
android:textColor="#333333"
android:textSize="16sp"
tools:ignore="HardcodedText" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:textColor="#333333"
android:textSize="12sp"
tools:ignore="HardcodedText" />
<TextView
android:id="@+id/tv_clean_bin"
android:layout_width="111dp"
android:layout_height="30dp"
android:layout_marginTop="12dp"
android:background="@mipmap/button_blue"
android:gravity="center"
android:text="scan"
android:textColor="@color/white"
android:textSize="16sp"
tools:ignore="HardcodedText" />
</LinearLayout>
</FrameLayout>
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="end"
android:layout_marginEnd="10dp"
android:src="@mipmap/tu_1"
tools:ignore="ContentDescription" />
</FrameLayout>
<FrameLayout
android:id="@+id/fl_recovered"
android:layout_width="match_parent"
android:layout_height="69dp"
android:layout_marginHorizontal="16dp"
android:layout_marginTop="24dp"
android:background="@drawable/bg_gradient_3e7df3_10">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="45dp"
android:layout_gravity="center"
android:layout_marginHorizontal="12dp"
android:background="@drawable/bg_80ffffff_10"
android:orientation="horizontal">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginStart="5dp"
android:src="@mipmap/icon_recovered"
tools:ignore="ContentDescription" />
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginStart="12dp"
android:layout_weight="1"
android:text="Recovered"
android:textColor="@color/white"
android:textSize="16sp"
android:textStyle="bold"
tools:ignore="HardcodedText" />
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginEnd="15dp"
android:src="@mipmap/triangle_4"
tools:ignore="ContentDescription" />
</LinearLayout>
</FrameLayout>
<FrameLayout
android:layout_marginTop="20dp"
android:layout_marginHorizontal="16dp"
android:id="@+id/fl_ad"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
......@@ -38,7 +38,7 @@
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginEnd="5dp"
android:src="@drawable/b_circle_selector"
android:src="@drawable/bg_circle_selector"
tools:ignore="ContentDescription" />
<TextView
......
......@@ -23,7 +23,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:src="@drawable/b_circle_border_selector"
android:src="@drawable/bg_circle_border_selector"
tools:ignore="ContentDescription" />
</FrameLayout>
......
......@@ -23,7 +23,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="5dp"
android:src="@drawable/b_circle_border_selector"
android:src="@drawable/bg_circle_border_selector"
tools:ignore="ContentDescription" />
</FrameLayout>
......
......@@ -23,7 +23,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="5dp"
android:src="@drawable/b_circle_border_selector"
android:src="@drawable/bg_circle_border_selector"
tools:ignore="ContentDescription" />
</FrameLayout>
......
<com.google.android.gms.ads.nativead.NativeAdView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="10dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:background="#D9D9D9"
android:minHeight="50dp"
android:paddingHorizontal="15dp"
android:paddingVertical="10dp"
tools:ignore="DisableBaselineAlignment">
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginEnd="10dp"
android:layout_weight="1"
android:orientation="vertical">
<TextView
android:id="@+id/ad_headline"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ellipsize="end"
android:lines="2"
android:textColor="@color/black"
android:textSize="16sp"
android:textStyle="bold" />
<TextView
android:id="@+id/ad_body"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="10dp"
android:layout_marginRight="10dp"
android:ellipsize="end"
android:lines="2"
android:textColor="@color/black"
android:textSize="12sp"
tools:ignore="RtlHardcoded" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ImageView
android:id="@+id/ad_app_icon"
android:layout_width="40dp"
android:layout_height="40dp"
android:layout_gravity="center_vertical"
tools:ignore="ContentDescription" />
<Button
android:id="@+id/ad_call_to_action"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginHorizontal="5dp"
android:backgroundTint="@color/black"
android:gravity="center"
android:textColor="@color/white"
android:textSize="12sp" />
</LinearLayout>
</LinearLayout>
<FrameLayout
android:layout_width="wrap_content"
android:layout_height="match_parent">
<com.google.android.gms.ads.nativead.MediaView
android:id="@+id/ad_media"
android:layout_width="160dp"
android:layout_height="match_parent"
android:layout_gravity="center_horizontal" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="end"
android:background="@color/black"
android:paddingStart="3dp"
android:paddingTop="2dp"
android:paddingEnd="3dp"
android:paddingBottom="2dp"
android:text="Ad"
android:textColor="@color/white"
android:textSize="12sp"
tools:ignore="HardcodedText" />
</FrameLayout>
</LinearLayout>
</com.google.android.gms.ads.nativead.NativeAdView>
\ No newline at end of file
......@@ -2,4 +2,5 @@
<string name="app_name">Data Recovery</string>
<!-- TODO: Remove or change this placeholder text -->
<string name="hello_blank_fragment">Hello blank fragment</string>
<string name="facebook_app_id">11</string>
</resources>
\ No newline at end of file
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
dependencies {
classpath 'com.google.gms:google-services:4.3.15'
classpath 'com.google.firebase:firebase-crashlytics-gradle:2.9.5'
}
}
plugins {
alias(libs.plugins.androidApplication) apply false
alias(libs.plugins.androidApplication) apply false
alias(libs.plugins.jetbrainsKotlinAndroid) apply false
}
\ No newline at end of file
......@@ -16,6 +16,11 @@ dependencyResolutionManagement {
repositories {
google()
mavenCentral()
maven { url 'https://jitpack.io' }
maven { url "https://android-sdk.is.com" }
maven { url "https://dl-maven-android.mintegral.com/repository/mbridge_android_sdk_oversea" }
maven { url "https://artifact.bytedance.com/repository/pangle" }
maven { url "https://s01.oss.sonatype.org/content/groups/public" }
}
}
......
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