Commit 239af647 authored by wanglei's avatar wanglei

...ui

parent 801e9303
plugins { plugins {
alias(libs.plugins.androidApplication) alias(libs.plugins.androidApplication)
alias(libs.plugins.jetbrainsKotlinAndroid) alias(libs.plugins.jetbrainsKotlinAndroid)
id 'com.google.gms.google-services'
id 'com.google.firebase.crashlytics'
} }
android { android {
...@@ -30,6 +32,10 @@ android { ...@@ -30,6 +32,10 @@ android {
shrinkResources true shrinkResources true
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
signingConfig signingConfigs.realse signingConfig signingConfigs.realse
// 设置是否要自动上传
firebaseCrashlytics {
mappingFileUploadEnabled true
}
} }
} }
compileOptions { compileOptions {
...@@ -45,6 +51,13 @@ android { ...@@ -45,6 +51,13 @@ android {
aidl true aidl true
} }
} }
gradle.taskGraph.whenReady {
tasks.each { task ->
if (task.name.contains("uploadCrashlyticsMappingFile")) {
task.enabled = false
}
}
}
dependencies { dependencies {
...@@ -66,4 +79,19 @@ dependencies { ...@@ -66,4 +79,19 @@ dependencies {
//网络 //网络
implementation group: 'com.google.code.gson', name: 'gson', version: '2.10.1' 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"?> <?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" <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.INTERNET" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />
<application <application
android:name=".MyApplication"
android:allowBackup="true" android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules" android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules" android:fullBackupContent="@xml/backup_rules"
...@@ -16,25 +17,65 @@ ...@@ -16,25 +17,65 @@
android:roundIcon="@mipmap/ic_launcher_round" android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true" android:supportsRtl="true"
android:theme="@style/Theme.DataRecovery" android:theme="@style/Theme.DataRecovery"
tools:targetApi="31" > tools:targetApi="31">
<activity <activity
android:name=".activity.FileScanResultActivity" android:name=".activity.SplashActivity"
android:exported="false" /> android:exported="true">
<activity
android:name=".activity.FileRecoveryActivity"
android:exported="false" />
<activity
android:name=".activity.FileScanActivity"
android:exported="false" />
<activity
android:name=".MainActivity"
android:exported="true" >
<intent-filter> <intent-filter>
<action android:name="android.intent.action.MAIN" /> <action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" /> <category android:name="android.intent.category.LAUNCHER" />
</intent-filter> </intent-filter>
</activity> </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> </application>
</manifest> </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 package com.base.datarecovery.activity
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.content.Intent
import android.graphics.BitmapFactory import android.graphics.BitmapFactory
import android.graphics.Color import android.graphics.Color
import android.os.Environment
import android.view.View import android.view.View
import android.widget.Toast
import androidx.activity.addCallback import androidx.activity.addCallback
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.core.view.isVisible import androidx.core.view.isVisible
import androidx.lifecycle.lifecycleScope
import com.base.datarecovery.R import com.base.datarecovery.R
import com.base.datarecovery.adapter.FileMediaColumnsAdapter import com.base.datarecovery.adapter.FileMediaColumnsAdapter
import com.base.datarecovery.adapter.RecoveryFilterAdapter 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.FolderBean
import com.base.datarecovery.bean.RecoveryBean import com.base.datarecovery.bean.RecoveryBean
import com.base.datarecovery.bean.RecoveryFilterBean import com.base.datarecovery.bean.RecoveryFilterBean
...@@ -21,6 +27,7 @@ import com.base.datarecovery.utils.BarUtils ...@@ -21,6 +27,7 @@ import com.base.datarecovery.utils.BarUtils
import com.base.datarecovery.utils.TimeUtils.isWithinOneMonth import com.base.datarecovery.utils.TimeUtils.isWithinOneMonth
import com.base.datarecovery.utils.TimeUtils.isWithinSixMonths import com.base.datarecovery.utils.TimeUtils.isWithinSixMonths
import com.base.datarecovery.utils.TimeUtils.isWithinTwentyFourMonths import com.base.datarecovery.utils.TimeUtils.isWithinTwentyFourMonths
import com.base.datarecovery.view.DialogViews.showRecoveringDialog
import com.google.gson.Gson import com.google.gson.Gson
import java.io.File import java.io.File
...@@ -66,7 +73,7 @@ class FileRecoveryActivity : BaseActivity<ActivityFileRecoveryBinding>() { ...@@ -66,7 +73,7 @@ class FileRecoveryActivity : BaseActivity<ActivityFileRecoveryBinding>() {
folderBean = Gson().fromJson(json, FolderBean::class.java) folderBean = Gson().fromJson(json, FolderBean::class.java)
when (scanType) { when (scanType) {
ConstObject.SCAN_PHOTOS -> { SCAN_PHOTOS -> {
var size = 0 var size = 0
folderBean?.recoveryList?.forEach { folderBean?.recoveryList?.forEach {
val bitmap = BitmapFactory.decodeFile(it.path) val bitmap = BitmapFactory.decodeFile(it.path)
...@@ -78,11 +85,11 @@ class FileRecoveryActivity : BaseActivity<ActivityFileRecoveryBinding>() { ...@@ -78,11 +85,11 @@ class FileRecoveryActivity : BaseActivity<ActivityFileRecoveryBinding>() {
binding.tvThumbnails.text = "Hide thumbnails (${size})" binding.tvThumbnails.text = "Hide thumbnails (${size})"
} }
ConstObject.SCAN_DOCUMENTS -> { SCAN_DOCUMENTS -> {
binding.clThumbnails.visibility = View.GONE binding.clThumbnails.visibility = View.GONE
} }
ConstObject.SCAN_VIDEOS -> { SCAN_VIDEOS -> {
binding.clThumbnails.visibility = View.GONE binding.clThumbnails.visibility = View.GONE
} }
...@@ -131,7 +138,31 @@ class FileRecoveryActivity : BaseActivity<ActivityFileRecoveryBinding>() { ...@@ -131,7 +138,31 @@ class FileRecoveryActivity : BaseActivity<ActivityFileRecoveryBinding>() {
binding.llLayout.setOnClickListener { binding.llLayout.setOnClickListener {
showFilter(it.id) 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) { private fun showFilter(filter: Int) {
......
package com.base.datarecovery package com.base.datarecovery.activity
import android.graphics.Color import android.graphics.Color
import android.os.Bundle 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( ...@@ -49,28 +49,27 @@ class FileFolderAdapter(
when (scanType) { when (scanType) {
SCAN_PHOTOS, SCAN_VIDEOS -> { SCAN_PHOTOS, SCAN_VIDEOS -> {
LogEx.logDebug(TAG, "scanType=SCAN_PHOTOS or SCAN_VIDEOS")
val binding = ItemFolderRecoveryBinding.bind(holder.itemView) val binding = ItemFolderRecoveryBinding.bind(holder.itemView)
runCatching { runCatching {
val options = RequestOptions().transform(CenterCrop(), RoundedCorners(context.dpToPx(10))) val options = RequestOptions().transform(CenterCrop(), RoundedCorners(context.dpToPx(10)))
val image1 = bean.recoveryList[0].path val image1 = bean.recoveryList[0].path
val request1 = Glide.with(context).load(image1).apply(options) val request1 = Glide.with(context).load(image1).apply(options)
if (scanType == SCAN_VIDEOS) { if (scanType == SCAN_VIDEOS) {
request1.error(R.drawable.videotu) request1.error(R.mipmap.videotu)
} }
request1.into(binding.iv1) request1.into(binding.iv1)
val image2 = bean.recoveryList[1].path val image2 = bean.recoveryList[1].path
val request2 = Glide.with(context).load(image2).apply(options) val request2 = Glide.with(context).load(image2).apply(options)
if (scanType == SCAN_VIDEOS) { if (scanType == SCAN_VIDEOS) {
request2.error(R.drawable.videotu) request2.error(R.mipmap.videotu)
} }
request2.into(binding.iv2) request2.into(binding.iv2)
val image3 = bean.recoveryList[2].path val image3 = bean.recoveryList[2].path
val request3 = Glide.with(context).load(image3).apply(options) val request3 = Glide.with(context).load(image3).apply(options)
if (scanType == SCAN_VIDEOS) { if (scanType == SCAN_VIDEOS) {
request3.error(R.drawable.videotu) request3.error(R.mipmap.videotu)
} }
request3.into(binding.iv3) request3.into(binding.iv3)
} }
......
...@@ -175,10 +175,18 @@ class FileMediaColumnsAdapter( ...@@ -175,10 +175,18 @@ class FileMediaColumnsAdapter(
it.recoveryList.forEach { bean -> bean.isSelect = selected } it.recoveryList.forEach { bean -> bean.isSelect = selected }
} }
notifyDataSetChanged() 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) { fun changeColumns(columns: Int) {
this.columns = columns this.columns = columns
notifyDataSetChanged() 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
This diff is collapsed.
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 package com.base.datarecovery.bean
import com.base.datarecovery.utils.SPUtils
object ConstObject { object ConstObject {
const val SCAN_PHOTOS = 1 const val SCAN_PHOTOS = 1
const val SCAN_DOCUMENTS = 2 const val SCAN_DOCUMENTS = 2
const val SCAN_VIDEOS = 3 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 package com.base.datarecovery.fragment
import android.animation.AnimatorSet
import android.animation.ObjectAnimator
import android.annotation.SuppressLint
import android.content.Intent import android.content.Intent
import android.view.View
import android.view.animation.AccelerateDecelerateInterpolator
import com.base.datarecovery.activity.FileScanActivity 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_DOCUMENTS
import com.base.datarecovery.bean.ConstObject.SCAN_PHOTOS import com.base.datarecovery.bean.ConstObject.SCAN_PHOTOS
import com.base.datarecovery.bean.ConstObject.SCAN_VIDEOS import com.base.datarecovery.bean.ConstObject.SCAN_VIDEOS
...@@ -13,28 +19,68 @@ class HomeFragment : BaseFragment<FragmentHomeBinding>() { ...@@ -13,28 +19,68 @@ class HomeFragment : BaseFragment<FragmentHomeBinding>() {
override val binding: FragmentHomeBinding by lazy { override val binding: FragmentHomeBinding by lazy {
FragmentHomeBinding.inflate(layoutInflater) FragmentHomeBinding.inflate(layoutInflater)
} }
private lateinit var animatorSet: AnimatorSet
override fun setView() { override fun setView() {
animatorSet = createHeartbeatAnimation(binding.flScan)
AdmobNativeUtils.showNativeAd(requireActivity(), binding.flAd)
} }
override fun setListener() { override fun setListener() {
binding.llRyPhotos.setOnClickListener { binding.flRyPhoto.setOnClickListener {
startActivity(Intent(requireContext(), FileScanActivity::class.java).apply { startActivity(Intent(requireContext(), FileScanActivity::class.java).apply {
putExtra("Type", SCAN_PHOTOS) putExtra("Type", SCAN_PHOTOS)
}) })
} }
binding.llRyDocuments.setOnClickListener { binding.flRyVideo.setOnClickListener {
startActivity(Intent(requireContext(), FileScanActivity::class.java).apply { 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 { 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 ...@@ -5,9 +5,8 @@ import android.content.Intent
import android.os.Bundle import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.viewbinding.ViewBinding 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.ActivityManagerUtils
import com.base.datarecovery.utils.BarUtils
abstract class BaseActivity<T : ViewBinding> : AppCompatActivity() { 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
This diff is collapsed.
...@@ -6,11 +6,29 @@ import android.content.Context ...@@ -6,11 +6,29 @@ import android.content.Context
import android.graphics.Color import android.graphics.Color
import android.graphics.drawable.ColorDrawable import android.graphics.drawable.ColorDrawable
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View
import androidx.lifecycle.LifecycleCoroutineScope
import com.base.datarecovery.R import com.base.datarecovery.R
import com.base.datarecovery.databinding.DialogPermissonOpenBinding 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 { object DialogViews {
private val TAG = "DialogViews"
@SuppressLint("SetTextI18n") @SuppressLint("SetTextI18n")
fun Context.showGerPermission( fun Context.showGerPermission(
tittle: String? = null, tittle: String? = null,
...@@ -42,4 +60,79 @@ object DialogViews { ...@@ -42,4 +60,79 @@ object DialogViews {
dialog.show() dialog.show()
return dialog 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 @@ ...@@ -178,7 +178,7 @@
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="center_vertical" android:layout_gravity="center_vertical"
android:src="@drawable/b_circle_selector" android:src="@drawable/bg_circle_selector"
tools:ignore="ContentDescription" /> tools:ignore="ContentDescription" />
<TextView <TextView
......
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
android:id="@+id/main" android:id="@+id/main"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
tools:context=".MainActivity"> tools:context=".activity.MainActivity">
<fragment <fragment
android:id="@+id/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
This diff is collapsed.
This diff is collapsed.
...@@ -38,7 +38,7 @@ ...@@ -38,7 +38,7 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="center_vertical" android:layout_gravity="center_vertical"
android:layout_marginEnd="5dp" android:layout_marginEnd="5dp"
android:src="@drawable/b_circle_selector" android:src="@drawable/bg_circle_selector"
tools:ignore="ContentDescription" /> tools:ignore="ContentDescription" />
<TextView <TextView
......
...@@ -23,7 +23,7 @@ ...@@ -23,7 +23,7 @@
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_margin="10dp" android:layout_margin="10dp"
android:src="@drawable/b_circle_border_selector" android:src="@drawable/bg_circle_border_selector"
tools:ignore="ContentDescription" /> tools:ignore="ContentDescription" />
</FrameLayout> </FrameLayout>
......
...@@ -23,7 +23,7 @@ ...@@ -23,7 +23,7 @@
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_margin="5dp" android:layout_margin="5dp"
android:src="@drawable/b_circle_border_selector" android:src="@drawable/bg_circle_border_selector"
tools:ignore="ContentDescription" /> tools:ignore="ContentDescription" />
</FrameLayout> </FrameLayout>
......
...@@ -23,7 +23,7 @@ ...@@ -23,7 +23,7 @@
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_margin="5dp" android:layout_margin="5dp"
android:src="@drawable/b_circle_border_selector" android:src="@drawable/bg_circle_border_selector"
tools:ignore="ContentDescription" /> tools:ignore="ContentDescription" />
</FrameLayout> </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 @@ ...@@ -2,4 +2,5 @@
<string name="app_name">Data Recovery</string> <string name="app_name">Data Recovery</string>
<!-- TODO: Remove or change this placeholder text --> <!-- TODO: Remove or change this placeholder text -->
<string name="hello_blank_fragment">Hello blank fragment</string> <string name="hello_blank_fragment">Hello blank fragment</string>
<string name="facebook_app_id">11</string>
</resources> </resources>
\ No newline at end of file
// Top-level build file where you can add configuration options common to all sub-projects/modules. // 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 { plugins {
alias(libs.plugins.androidApplication) apply false alias(libs.plugins.androidApplication) apply false
alias(libs.plugins.jetbrainsKotlinAndroid) apply false alias(libs.plugins.jetbrainsKotlinAndroid) apply false
} }
\ No newline at end of file
...@@ -16,6 +16,11 @@ dependencyResolutionManagement { ...@@ -16,6 +16,11 @@ dependencyResolutionManagement {
repositories { repositories {
google() google()
mavenCentral() 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