Commit 679da567 authored by wanglei's avatar wanglei

..

parent 940128af
......@@ -156,5 +156,9 @@ dependencies {
implementation(libs.lottie)
implementation(libs.expandablerecyclerview)
implementation(libs.okhttp)
implementation(libs.logging.interceptor)
}
package com.base.scanqrclear.luma
import android.util.Base64
import com.base.scanqrclear.GlobalConfig.KEY_AES
import java.nio.charset.Charset
import java.security.SecureRandom
import javax.crypto.Cipher
import javax.crypto.spec.GCMParameterSpec
import javax.crypto.spec.SecretKeySpec
object AESHelps {
private val AES_CIPHER_TRANSFORMATION = "AES/GCM/NoPadding" // 明确指定加密算法
// 使用单例模式初始化Cipher,避免每次加密解密都重新创建
private val cipher: Cipher by lazy {
Cipher.getInstance(AES_CIPHER_TRANSFORMATION)
}
// 使用单例模式初始化SecretKeySpec,避免每次加密解密都重新创建
private val secretKey: SecretKeySpec by lazy {
SecretKeySpec(KEY_AES.toByteArray(Charset.defaultCharset()), "AES")
}
fun encrypt(content: String): String {
try {
// 初始化向量IV,确保每次加密都是随机的
val iv = generateIv()
// 初始化GCM参数规范
val gcmParameterSpec = GCMParameterSpec(128, iv)
// 初始化Cipher为加密模式
cipher.init(Cipher.ENCRYPT_MODE, secretKey, gcmParameterSpec)
// 加密内容
val encryptedBytes = cipher.doFinal(content.toByteArray(Charset.defaultCharset()))
// 将IV和加密数据合并,并进行Base64编码
val message = iv + encryptedBytes
return Base64.encodeToString(message, Base64.NO_WRAP)
} catch (_: Exception) {
return content
}
}
fun decrypt(content: String): String {
try {
// 将Base64编码的字符串解码为字节数组
val contentBytes = Base64.decode(content, Base64.NO_WRAP)
// 提取IV
val iv = contentBytes.copyOfRange(0, 12)
// 提取加密数据
val encryptedBytes = contentBytes.copyOfRange(12, contentBytes.size)
// 初始化GCM参数规范
val gcmParameterSpec = GCMParameterSpec(128, iv)
// 初始化Cipher为解密模式
cipher.init(Cipher.DECRYPT_MODE, secretKey, gcmParameterSpec)
// 解密内容
val decryptedBytes = cipher.doFinal(encryptedBytes)
// 将解密后的字节数组转换为字符串
return String(decryptedBytes, Charset.defaultCharset())
} catch (_: Exception) {
return content
}
}
// 生成随机的初始化向量IV
private fun generateIv(): ByteArray {
val iv = ByteArray(12)
SecureRandom().nextBytes(iv)
return iv
}
}
\ No newline at end of file
package com.base.scanqrclear.luma
import android.util.Base64
import com.base.scanqrclear.BuildConfig
import com.base.scanqrclear.GlobalConfig.PACKAGE_NAME
import com.base.scanqrclear.GlobalConfig.URL_API
import com.base.scanqrclear.helper.EventUtils
import com.base.scanqrclear.luma.AdmobHelps.KEY_GOOGLE_ADVERTISER_ID
import com.base.scanqrclear.luma.AdmobHelps.KEY_REFERRER
import com.base.scanqrclear.luma.AdmobHelps.KEY_UUID
import com.google.gson.Gson
import com.google.gson.reflect.TypeToken
import okhttp3.Call
import okhttp3.Callback
import okhttp3.MediaType.Companion.toMediaTypeOrNull
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.RequestBody.Companion.toRequestBody
import okhttp3.Response
import okhttp3.logging.HttpLoggingInterceptor
import org.json.JSONObject
import java.io.IOException
object APIUtils {
private val configUrl by lazy {
val url = StringBuilder("${URL_API}/${PACKAGE_NAME.filter { it.isLowerCase() }.substring(4, 9)}spk")
url.append("?pkg=$PACKAGE_NAME")
val referrer = SpUtils.getInstance().getString(KEY_REFERRER)
url.append("&referrer=${Base64.encodeToString(referrer.toByteArray(), Base64.DEFAULT)}")
url.append("&vn=${BuildConfig.VERSION_NAME}")
url.append("&vc=${BuildConfig.VERSION_CODE}")
url.append("&device=${SpUtils.getInstance().getString(KEY_GOOGLE_ADVERTISER_ID)}")
url.append("&aid=${SpUtils.getInstance().getString(KEY_UUID)}")
url.append("&mode=4")
url.toString()
}
private val blacklistUrl by lazy {
val url = StringBuilder("${URL_API}/${PACKAGE_NAME.filter { it.isLowerCase() }.substring(4, 9)}cl")
url.append("?pkg=${PACKAGE_NAME}")
url.toString()
}
fun requestConfig(block: () -> Unit) {
val request = Request.Builder()
.url(configUrl)
.get()
.build()
val client = createOkHttpClient()
client.newCall(request).enqueue(object : Callback {
override fun onFailure(call: Call, e: IOException) {
EventUtils.event("api_config_error", value = e.toString())
}
override fun onResponse(call: Call, response: Response) {
response.body?.string()?.let {
val result = Regex("\"data\":\"(.*?)\"").find(it)
if (result != null && result.toString() != "null") {
result.groupValues[1].let {
try {
val str = AESHelps.decrypt(it)
val gson = Gson()
val type = object : TypeToken<Map<String, Int>>() {}.type
val configMap = gson.fromJson<Map<String, Int>>(str, type)
configMap.forEach { (t, u) ->
SpUtils.getInstance().put(t, u)
}
block.invoke()
val jsonObject = JSONObject()
jsonObject.put("ut", SpUtils.getInstance().getInt("ut"))
EventUtils.event("user_type", ext = jsonObject)
} catch (_: Exception) {
}
}
}
}
}
})
}
fun requestBlacklist() {
val data = JSONObject()
.put("bp", DeviceHelps.getConfigParams())
.toString()
val body = AESHelps.encrypt(data)
.toRequestBody("application/json;charset=utf-8".toMediaTypeOrNull())
val request = Request.Builder()
.url(blacklistUrl)
.post(body)
.build()
val client = createOkHttpClient()
client.newCall(request).enqueue(object : Callback {
override fun onFailure(call: Call, e: IOException) {
EventUtils.event("api_blacklist_error", value = e.toString())
}
override fun onResponse(call: Call, response: Response) {
response.body?.string()?.let {
val result = Regex("\"data\":\"(.*?)\"").find(it)
if (result != null && result.toString() != "null") {
result.groupValues[1].let {
val str = AESHelps.decrypt(it)
val blacklist = if (str == "true") 0 else 1
// GlobalConfig.blacklist = blacklist
}
}
}
}
})
}
private fun createOkHttpClient(): OkHttpClient {
val client = OkHttpClient.Builder().apply {
if (BuildConfig.DEBUG) {
addInterceptor(HttpLoggingInterceptor().apply {
level = HttpLoggingInterceptor.Level.BODY
})
}
}.build()
return client
}
}
\ No newline at end of file
package com.base.scanqrclear.luma
import android.app.Activity
import android.content.Context
import android.content.Intent
import com.base.scanqrclear.luma.AdmobHelps.ID_CLEAN_JUNK
import com.base.scanqrclear.luma.AdmobHelps.ID_LARGE_FILE_CLEANER
import com.base.scanqrclear.luma.AdmobHelps.ID_PHOTO_COMPRESSION
import com.base.scanqrclear.luma.AdmobHelps.ID_SCREENSHOT_CLEAN
import com.base.scanqrclear.luma.AdmobHelps.ID_WHATSAPP
import com.base.scanqrclear.utils.ActivityLauncher
object ActivityJumpHelps {
private var isShowStoragePermission = false
fun start(
context: Context,
launcher: ActivityLauncher,
nameId: Int
) {
when(nameId) {
// R.string.junk_scan -> requestPermission(context, launcher) { CleanJunkActivity.start(context) }
// R.string.battery_info -> BatteryInfoActivity.start(context)
// R.string.app_process -> AppProcessActivity.start(context)
// R.string.whatsapp_clean -> requestPermission(context, launcher) { WhatsappCleanActivity.start(context) }
// R.string.screenshot_clean -> requestPermission(context, launcher) { ScreenshotCleanActivity.start(context) }
// R.string.app_manager -> AppManagerActivity.start(context)
// R.string.large_files -> requestPermission(context, launcher) { LargeFileCleanActivity.start(context) }
// R.string.image_compressor -> requestPermission(context, launcher) { PhotoCompressionActivity.start(context) }
// R.string.similar_photos -> requestPermission(context, launcher) { SimilarPhotosActivity.start(context) }
}
}
fun startFunction(
context: Context,
launcher: ActivityLauncher,
notificationId: Int
) {
when (notificationId) {
// ID_CLEAN_JUNK -> requestPermission(context, launcher) { CleanJunkActivity.start(context) }
// ID_WHATSAPP -> requestPermission(context, launcher) { WhatsappCleanActivity.start(context) }
// ID_LARGE_FILE_CLEANER -> requestPermission(context, launcher) { LargeFileCleanActivity.start(context) }
// ID_PHOTO_COMPRESSION -> requestPermission(context, launcher) { PhotoCompressionActivity.start(context) }
// ID_SCREENSHOT_CLEAN -> requestPermission(context, launcher) { ScreenshotCleanActivity.start(context) }
}
}
fun toFunction(context: Activity, notificationId: Int) {
if (!PermissionHelps.checkFilesAccessPermission(context)
&& (notificationId == ID_CLEAN_JUNK
|| notificationId == ID_LARGE_FILE_CLEANER
|| notificationId == ID_PHOTO_COMPRESSION
|| notificationId == ID_WHATSAPP
|| notificationId == ID_SCREENSHOT_CLEAN)) {
// context.startActivity(Intent(context, MainActivity::class.java).apply { putExtra(KEY_NOTIFICATION_ID, notificationId) })
return
}
when (notificationId) {
// ID_CLEAN_JUNK -> CleanJunkActivity.start(context)
// ID_WHATSAPP -> WhatsappCleanActivity.start(context)
// ID_BATTERY_INFO -> BatteryInfoActivity.start(context)
// ID_LARGE_FILE_CLEANER -> LargeFileCleanActivity.start(context)
// ID_PHOTO_COMPRESSION -> PhotoCompressionActivity.start(context)
// ID_SCREENSHOT_CLEAN -> ScreenshotCleanActivity.start(context)
// ID_APP_INSTALL -> AppManagerActivity.start(context)
// ID_APP_UNINSTALL -> AppManagerActivity.start(context)
// ID_NOT_CLEANED -> CleanJunkActivity.start(context)
// ID_BATTERY_LEVEL -> BatteryInfoActivity.start(context)
// ID_APP_MANAGER -> AppManagerActivity.start(context)
// ID_HOME -> MainActivity.start(context)
}
}
fun requestPermission(
context: Context,
launcher: ActivityLauncher,
block: (() -> Unit?)? = null
) {
if (PermissionHelps.checkFilesAccessPermission(context)) {
block?.invoke()
} else {
if (isShowStoragePermission) return
isShowStoragePermission = true
DialogHelps.showStoragePermissionDialog(
context,
{
PermissionHelps.requestFilesAccessPermission(context, launcher, result = {
if (it) block?.invoke()
isShowStoragePermission = false
})
},
{
isShowStoragePermission = false
}
)
}
}
}
\ No newline at end of file
package com.base.scanqrclear.luma
import android.app.Activity
import java.util.Stack
object ActivityManagerHelps {
private val activityStack = Stack<Activity>()
/**
* 添加Activity到堆栈
*/
fun add(activity: Activity) {
activityStack.add(activity)
}
/**
* 移除Activity从堆栈
*/
fun remove(activity: Activity) {
activityStack.remove(activity)
}
/**
* 结束指定的Activity
*/
fun finish(activity: Activity?) {
if (activity != null) {
activityStack.remove(activity)
activity.finish()
}
}
/**
* 结束所有Activity
*/
fun finishAll() {
for (activity in activityStack) {
activity?.finish()
}
activityStack.clear()
}
fun finishAll(excludeCls: Class<*>?) {
val iterator = activityStack.iterator()
while (iterator.hasNext()) {
val item = iterator.next()
if (excludeCls != item.javaClass) {
item.finish()
iterator.remove()
}
}
}
/**
* 检查Activity是否存在于堆栈中
*/
fun isActivityInStack(cls: Class<*>?): Boolean {
var isExist = false
if (cls != null) {
for (activity in activityStack) {
if (cls == activity.javaClass) {
isExist = true
break
}
}
}
return isExist
}
val all: List<Activity>
get() = activityStack
val topActivity: Activity?
get() = if (activityStack.isEmpty()) {
null
} else {
activityStack.peek()
}
}
\ No newline at end of file
package com.base.scanqrclear.luma
import android.app.Activity
import android.content.Context
import android.os.Bundle
import android.util.Log
import android.view.ViewGroup
import android.view.ViewTreeObserver
import com.applovin.mediation.MaxAd
import com.applovin.mediation.MaxAdListener
import com.applovin.mediation.MaxAdViewAdListener
import com.applovin.mediation.MaxError
import com.applovin.mediation.ads.MaxAdView
import com.applovin.mediation.ads.MaxAppOpenAd
import com.applovin.mediation.ads.MaxInterstitialAd
import com.applovin.mediation.nativeAds.MaxNativeAdListener
import com.applovin.mediation.nativeAds.MaxNativeAdLoader
import com.applovin.mediation.nativeAds.MaxNativeAdView
import com.applovin.sdk.AppLovinMediationProvider
import com.applovin.sdk.AppLovinSdk
import com.applovin.sdk.AppLovinSdkInitializationConfiguration
import com.base.scanqrclear.GlobalConfig.ID_ADMOB_BANNER
import com.base.scanqrclear.GlobalConfig.ID_ADMOB_INTER
import com.base.scanqrclear.GlobalConfig.ID_ADMOB_NATIVE
import com.base.scanqrclear.GlobalConfig.ID_ADMOB_OPEN
import com.base.scanqrclear.GlobalConfig.ID_MAX_BANNER
import com.base.scanqrclear.GlobalConfig.ID_MAX_INTER
import com.base.scanqrclear.GlobalConfig.ID_MAX_NATIVE
import com.base.scanqrclear.GlobalConfig.ID_MAX_OPEN
import com.base.scanqrclear.GlobalConfig.KEY_MAX
import com.google.ads.mediation.admob.AdMobAdapter
import com.google.android.gms.ads.AdError
import com.google.android.gms.ads.AdListener
import com.google.android.gms.ads.AdLoader
import com.google.android.gms.ads.AdRequest
import com.google.android.gms.ads.AdSize
import com.google.android.gms.ads.AdView
import com.google.android.gms.ads.FullScreenContentCallback
import com.google.android.gms.ads.LoadAdError
import com.google.android.gms.ads.appopen.AppOpenAd
import com.google.android.gms.ads.interstitial.InterstitialAd
import com.google.android.gms.ads.interstitial.InterstitialAdLoadCallback
import com.google.android.gms.ads.nativead.NativeAd
import com.google.android.gms.ads.nativead.NativeAdOptions
import java.text.SimpleDateFormat
import java.util.Calendar
import java.util.Locale
import java.util.UUID
object AdmobHelps {
//
const val KEY_INIT = "key_init"
const val KEY_AD = "sAd"
const val KEY_BLACKLIST = "blacklist"
const val KEY_REFERRER = "referrer"
const val KEY_UUID = "key_uuid"
const val KEY_GOOGLE_ADVERTISER_ID = "key_google_advertiser_id"
const val KEY_DEVICE_NAME = "key_device_name"
const val KEY_INSTALL_TIME = "key_install_time"
const val KEY_CLEAN_LAST_TIME = "key_clean_last_time"
const val KEY_CLEANUP_SIZE = "key_cleanup_size"
const val KEY_NOT_CLEAN_TIME = "key_not_clean_time"
const val KEY_NOTIFICATION_ID = "key_notification_id"
const val KEY_APP_BACKGROUND = "key_app_background"
const val KEY_HOME_COUNT = "key_home_count"
const val KEY_DEVICE_SCAN = "key_device_scan"
const val KEY_SCORE = "key_score"
const val ID_CLEAN_JUNK = 31000
const val ID_WHATSAPP = 31010
const val ID_BATTERY_INFO = 31020
const val ID_LARGE_FILE_CLEANER = 31030
const val ID_PHOTO_COMPRESSION = 31040
const val ID_SCREENSHOT_CLEAN = 31050
const val ID_APP_INSTALL = 31060
const val ID_APP_UNINSTALL = 31070
const val ID_NOT_CLEANED = 31080
const val ID_BATTERY_LEVEL = 31090
const val ID_APP_MANAGER = 31100
const val ID_HOME = 31200
private const val APP_OPEN_AD_UNIT = "openAd"
private const val INTERSTITIAL_AD_UNIT = "interAd"
private const val NATIVE_AD_UNIT = "nativeAd"
private const val NO_AD = "no_ad"
private const val KEY_AD_INTERVAL = "adInterval"
private const val KEY_AD_REQUEST = "adRequest"
private const val KEY_AD_CLICK = "adClick"
private const val KEY_AD_SHOW = "adShow"
private const val KEY_IS_SHOW_AD_BANNER = "isShowAdBanner"
private const val KEY_IS_SHOW_AD_NATIVE = "isShowAdNative"
private const val KEY_IS_SHOW_AD_INTER = "isShowAdInter"
private const val KEY_IS_SHOW_AD_BACK_INTER = "isShowAdBackInter"
private const val KEY_IS_SHOW_AD_RESULT_BACK_INTER = "isShowAdResultBackInter"
private const val DEFAULT_AD_INTERVAL = 0
private const val DEFAULT_AD_REQUEST = 100
private const val DEFAULT_AD_CLICK = 10
private const val DEFAULT_AD_SHOW = 45
private const val KEY_DISPLAY_NUMBER = "key_display_number"
private const val KEY_REQUEST_NUMBER = "key_request_number"
private const val KEY_CLICK_NUMBER = "key_click_number"
private const val KEY_LAST_DISPLAY_TIME = "key_last_display_time"
private const val EVENT_BLACKLIST = "blacklist"
private const val EVENT_SHOW_DISPLAYS = "showDisPlays"
private const val EVENT_SHOW_REQUESTS = "showRequests"
private const val EVENT_SHOW_CLICKS = "showClicks"
private const val EVENT_SHOW_INTERVAL_TIME = "showIntervalTime"
//
private const val TAG = "AdmobHelps"
private var appOpenAd: AppOpenAd? = null
private var appOpenMaxAd: MaxAppOpenAd? = null
private var appOpenAdLoadTime: Long = 0
private var interstitialAd: InterstitialAd? = null
private var interstitialMaxAd: MaxInterstitialAd? = null
private var interstitialAdLoadTime: Long = 0
var isLoadingAppOpenAd = false
var isLoadingInterstitialAd = false
private var lastShowTime: Long = 0
var blacklist = 1
private var showTime
get() = SpUtils.getInstance()
.getLong(KEY_LAST_DISPLAY_TIME, System.currentTimeMillis())
set(value) = SpUtils.getInstance().putLong(KEY_LAST_DISPLAY_TIME, value)
inline var useAdmob
get() = SpUtils.getInstance().getBoolean("sAd", true)
set(value) = SpUtils.getInstance().putBoolean("sAd", value)
fun init(
context: Context,
loaded: (Int) -> Unit,
failed: () -> Unit,
) {
if (isBlacklist()) {
failed.invoke()
return
}
if (useAdmob) {
if (isAppOpenAdAvailable()) {
loaded.invoke(0)
return
}
if (isInterstitialAdAvailable()) {
loaded.invoke(1)
loadAppOpenAd(context)
return
}
loadAppOpenAd(context, { loaded.invoke(0) }, { failed.invoke() })
} else {
val initConfig = AppLovinSdkInitializationConfiguration.builder(
KEY_MAX, context
).setMediationProvider(AppLovinMediationProvider.MAX).build()
AppLovinSdk.getInstance(context).initialize(initConfig) {
if (isAppOpenAdAvailable()) {
loaded.invoke(0)
return@initialize
}
if (isInterstitialAdAvailable()) {
loaded.invoke(1)
loadAppOpenAd(context)
return@initialize
}
loadAppOpenAd(context, { loaded.invoke(0) }, { failed.invoke() })
}
}
}
fun loadAppOpenAd(
context: Context,
loaded: (() -> Unit?)? = null,
failed: (() -> Unit?)? = null,
) {
if (!checkAvailable(APP_OPEN_AD_UNIT)) {
failed?.invoke()
return
}
if (isLoadingAppOpenAd) {
// failed?.invoke()
return
}
isLoadingAppOpenAd = true
Log.d(TAG, "app open Ad load.")
val reqId = UUID.randomUUID().toString()
ReportAdUtils.pullStartAd(reqId, APP_OPEN_AD_UNIT, (context as Activity).javaClass.simpleName)
if (useAdmob) {
val adRequest = AdRequest.Builder().build()
AppOpenAd.load(
context,
ID_ADMOB_OPEN,
adRequest,
object : AppOpenAd.AppOpenAdLoadCallback() {
override fun onAdFailedToLoad(loadAdError: LoadAdError) {
Log.d(TAG, "app open failed to load: ${loadAdError.message}")
isLoadingAppOpenAd = false
failed?.invoke()
ReportAdUtils.pullAd(
loadAdError.responseInfo,
APP_OPEN_AD_UNIT,
loadAdError.message,
reqId
)
}
override fun onAdLoaded(ad: AppOpenAd) {
Log.d(TAG, "app open Ad was loaded.")
appOpenAd = ad
appOpenAdLoadTime = System.currentTimeMillis()
isLoadingAppOpenAd = false
loaded?.invoke()
calculate(KEY_REQUEST_NUMBER)
ReportAdUtils.pullAd(ad.responseInfo, APP_OPEN_AD_UNIT, reqId = reqId)
ad.onPaidEventListener = ReportAdUtils.EventOnPaidEventListener(ad)
}
})
} else {
appOpenMaxAd = MaxAppOpenAd(ID_MAX_OPEN, context)
appOpenMaxAd?.setListener(object : MaxAdListener {
override fun onAdLoaded(ad: MaxAd) {
Log.d(TAG, "app open Ad Max was loaded.")
appOpenAdLoadTime = System.currentTimeMillis()
loaded?.invoke()
calculate(KEY_REQUEST_NUMBER)
isLoadingAppOpenAd = false
}
override fun onAdDisplayed(p0: MaxAd) {
}
override fun onAdHidden(p0: MaxAd) {
}
override fun onAdClicked(p0: MaxAd) {
}
override fun onAdLoadFailed(p0: String, p1: MaxError) {
Log.d(TAG, "app open Max failed to load: $p0 ${p1.message}")
isLoadingAppOpenAd = false
failed?.invoke()
}
override fun onAdDisplayFailed(p0: MaxAd, p1: MaxError) {
}
})
appOpenMaxAd?.loadAd()
}
}
fun loadInterstitialAd(
context: Context,
loaded: (() -> Unit?)? = null,
failed: (() -> Unit?)? = null,
) {
if (!checkAvailable(INTERSTITIAL_AD_UNIT)) {
failed?.invoke()
return
}
if (isLoadingInterstitialAd) {
failed?.invoke()
return
}
isLoadingInterstitialAd = true
Log.d(TAG, "interstitial Ad load.")
val reqId = UUID.randomUUID().toString()
ReportAdUtils.pullStartAd(reqId, INTERSTITIAL_AD_UNIT, (context as Activity).javaClass.simpleName)
if (useAdmob) {
val adRequest = AdRequest.Builder().build()
InterstitialAd.load(
context,
ID_ADMOB_INTER,
adRequest,
object : InterstitialAdLoadCallback() {
override fun onAdFailedToLoad(loadAdError: LoadAdError) {
Log.d(TAG, "interstitial Ad failed to load: ${loadAdError.message}")
interstitialAd = null
isLoadingInterstitialAd = false
failed?.invoke()
ReportAdUtils.pullAd(
loadAdError.responseInfo,
INTERSTITIAL_AD_UNIT,
loadAdError.message,
reqId
)
}
override fun onAdLoaded(ad: InterstitialAd) {
Log.d(TAG, "interstitial Ad was loaded.")
interstitialAd = ad
interstitialAdLoadTime = System.currentTimeMillis()
isLoadingInterstitialAd = false
loaded?.invoke()
calculate(KEY_REQUEST_NUMBER)
ReportAdUtils.pullAd(ad.responseInfo, INTERSTITIAL_AD_UNIT, reqId = reqId)
ad.onPaidEventListener = ReportAdUtils.EventOnPaidEventListener(ad)
}
})
} else {
interstitialMaxAd = MaxInterstitialAd(ID_MAX_INTER, context)
interstitialMaxAd?.setListener(object : MaxAdListener {
override fun onAdLoaded(p0: MaxAd) {
Log.d(TAG, "interstitial Ad Max was loaded.")
interstitialAdLoadTime = System.currentTimeMillis()
isLoadingInterstitialAd = false
loaded?.invoke()
calculate(KEY_REQUEST_NUMBER)
}
override fun onAdDisplayed(p0: MaxAd) {
}
override fun onAdHidden(p0: MaxAd) {
}
override fun onAdClicked(p0: MaxAd) {
}
override fun onAdLoadFailed(p0: String, p1: MaxError) {
Log.d(TAG, "interstitial Ad failed to load: ${p1.message}")
interstitialMaxAd = null
isLoadingInterstitialAd = false
failed?.invoke()
}
override fun onAdDisplayFailed(p0: MaxAd, p1: MaxError) {
}
})
interstitialMaxAd?.loadAd()
}
}
fun showAppOpenAd(
activity: Activity,
dismissed: (() -> Unit?)? = null,
completed: (() -> Unit)? = null,
failed: (() -> Unit?)? = null,
isRetry: Boolean = false
) {
if (activity.isFinishing || activity.isDestroyed) return
if (!checkAvailable(APP_OPEN_AD_UNIT, true)) {
failed?.invoke()
return
}
if (!isRetry) ReportAdUtils.showPrepareAd(APP_OPEN_AD_UNIT)
if (!isAppOpenAdAvailable()) {
Log.d(TAG, "The app open ad is not ready yet.")
ReportAdUtils.limitErrorAd(NO_AD, APP_OPEN_AD_UNIT)
loadAppOpenAd(activity, {
showAppOpenAd(activity, dismissed, completed, failed, true)
}, {
failed?.invoke()
})
return
}
if (useAdmob) {
appOpenAd?.fullScreenContentCallback = object : FullScreenContentCallback() {
override fun onAdClicked() {
Log.d(TAG, "app open Ad was clicked")
calculate(KEY_CLICK_NUMBER)
ReportAdUtils.clickAd(appOpenAd?.responseInfo, APP_OPEN_AD_UNIT)
}
override fun onAdDismissedFullScreenContent() {
Log.d(TAG, "app open Ad dismissed fullscreen content.")
dismissed?.invoke()
}
override fun onAdFailedToShowFullScreenContent(adError: AdError) {
Log.d(TAG, "app open failed to show: ${adError.message}")
appOpenAd = null
failed?.invoke()
ReportAdUtils.showErrorAd(adError.message, APP_OPEN_AD_UNIT)
loadAppOpenAd(activity)
}
override fun onAdShowedFullScreenContent() {
Log.d(TAG, "app open Ad showed fullscreen content.")
lastShowTime = System.currentTimeMillis()
calculate(KEY_DISPLAY_NUMBER)
completed?.invoke()
ReportAdUtils.showAd(appOpenAd?.responseInfo, APP_OPEN_AD_UNIT, activity)
appOpenAd = null
loadAppOpenAd(activity)
}
}
appOpenAd?.show(activity)
} else {
appOpenMaxAd?.setListener(object : MaxAdListener {
override fun onAdLoaded(ad: MaxAd) {
}
override fun onAdDisplayed(p0: MaxAd) {
Log.d(TAG, "app open Ad Max showed fullscreen content.")
appOpenMaxAd = null
calculate(KEY_DISPLAY_NUMBER)
completed?.invoke()
loadAppOpenAd(activity)
}
override fun onAdHidden(p0: MaxAd) {
Log.d(TAG, "app open Ad Max dismissed fullscreen content.")
dismissed?.invoke()
}
override fun onAdClicked(p0: MaxAd) {
Log.d(TAG, "app open Ad Max was clicked")
calculate(KEY_CLICK_NUMBER)
}
override fun onAdLoadFailed(p0: String, p1: MaxError) {
}
override fun onAdDisplayFailed(p0: MaxAd, p1: MaxError) {
Log.d(TAG, "app open Max failed to show: ${p1.message}")
appOpenMaxAd = null
lastShowTime = System.currentTimeMillis()
failed?.invoke()
ReportAdUtils.showErrorAd(p1.message, APP_OPEN_AD_UNIT)
loadAppOpenAd(activity)
}
})
appOpenMaxAd?.showAd()
}
}
fun showInterstitialAd(
activity: Activity,
dismissed: (() -> Unit?)? = null,
completed: (() -> Unit)? = null,
failed: (() -> Unit?)? = null,
isRetry: Boolean = false
) {
if (activity.isFinishing || activity.isDestroyed) return
if (!checkAvailable(INTERSTITIAL_AD_UNIT, true)) {
failed?.invoke()
return
}
if (!isRetry) ReportAdUtils.showPrepareAd(INTERSTITIAL_AD_UNIT)
if (!isInterstitialAdAvailable()) {
Log.d(TAG, "The interstitial ad is not ready yet.")
ReportAdUtils.limitErrorAd(NO_AD, INTERSTITIAL_AD_UNIT)
loadInterstitialAd(activity, {
showInterstitialAd(activity, dismissed, completed, failed, true)
}, {
failed?.invoke()
})
return
}
if (useAdmob) {
interstitialAd?.fullScreenContentCallback = object : FullScreenContentCallback() {
override fun onAdClicked() {
Log.d(TAG, "interstitial Ad was clicked")
calculate(KEY_CLICK_NUMBER)
ReportAdUtils.clickAd(interstitialAd?.responseInfo, INTERSTITIAL_AD_UNIT)
}
override fun onAdDismissedFullScreenContent() {
Log.d(TAG, "interstitial Ad was dismissed.")
dismissed?.invoke()
}
override fun onAdFailedToShowFullScreenContent(adError: AdError) {
Log.d(TAG, "interstitial Ad failed to show: ${adError.message}")
interstitialAd = null
failed?.invoke()
ReportAdUtils.showErrorAd(adError.message, INTERSTITIAL_AD_UNIT)
loadInterstitialAd(activity)
}
override fun onAdShowedFullScreenContent() {
Log.d(TAG, "interstitial Ad showed fullscreen content.")
lastShowTime = System.currentTimeMillis()
calculate(KEY_DISPLAY_NUMBER)
completed?.invoke()
ReportAdUtils.showAd(
interstitialAd?.responseInfo,
INTERSTITIAL_AD_UNIT,
activity
)
interstitialAd = null
loadInterstitialAd(activity)
}
}
interstitialAd?.show(activity)
} else {
interstitialMaxAd?.setListener(object : MaxAdListener {
override fun onAdLoaded(p0: MaxAd) {
}
override fun onAdDisplayed(p0: MaxAd) {
Log.d(TAG, "interstitial Ad Max showed fullscreen content.")
calculate(KEY_DISPLAY_NUMBER)
completed?.invoke()
interstitialMaxAd = null
loadInterstitialAd(activity)
}
override fun onAdHidden(p0: MaxAd) {
Log.d(TAG, "interstitial Ad Max was dismissed.")
dismissed?.invoke()
}
override fun onAdClicked(p0: MaxAd) {
Log.d(TAG, "interstitial Ad Max was clicked")
calculate(KEY_CLICK_NUMBER)
}
override fun onAdLoadFailed(p0: String, p1: MaxError) {
}
override fun onAdDisplayFailed(p0: MaxAd, p1: MaxError) {
Log.d(TAG, "interstitial Ad MaX failed to show: ${p1.message}")
interstitialMaxAd = null
lastShowTime = System.currentTimeMillis()
failed?.invoke()
ReportAdUtils.showErrorAd(p1.message, INTERSTITIAL_AD_UNIT)
loadInterstitialAd(activity)
}
})
interstitialMaxAd?.showAd(activity)
}
}
fun showNativeAd(
activity: Activity,
completed: ((nativeAd: NativeAd) -> Unit)? = null,
maxCompleted: ((nativeAdLoader: MaxNativeAdLoader, nativeMaxAd: MaxAd) -> Unit)? = null,
failed: (() -> Unit?)? = null
) {
if (!isShowAdNative() || isBlacklist()) {
failed?.invoke()
return
}
Log.d(TAG, "native Ad load.")
ReportAdUtils.showPrepareAd(NATIVE_AD_UNIT)
if (useAdmob) {
var nativeAd: NativeAd? = null
val adLoader = AdLoader.Builder(activity, ID_ADMOB_NATIVE)
.forNativeAd { ad: NativeAd ->
completed?.invoke(ad)
ReportAdUtils.showAd(ad.responseInfo, NATIVE_AD_UNIT, activity)
ad.setOnPaidEventListener(ReportAdUtils.EventOnPaidEventListener(ad))
nativeAd = ad
}
.withAdListener(object : AdListener() {
override fun onAdClicked() {
Log.d(TAG, "native Ad clicked.")
ReportAdUtils.clickAd(nativeAd?.responseInfo, NATIVE_AD_UNIT)
}
override fun onAdFailedToLoad(loadAdError: LoadAdError) {
Log.d(TAG, "native Ad failed to load. ${loadAdError.message}")
failed?.invoke()
}
override fun onAdLoaded() {
Log.d(TAG, "native Ad loaded.")
}
})
.withNativeAdOptions(NativeAdOptions.Builder().build())
.build()
adLoader.loadAd(AdRequest.Builder().build())
} else {
val nativeAdLoader = MaxNativeAdLoader(ID_MAX_NATIVE, activity)
nativeAdLoader.setNativeAdListener(object : MaxNativeAdListener() {
override fun onNativeAdLoaded(nativeAdView: MaxNativeAdView?, ad: MaxAd) {
Log.d(TAG, "native Ad Max loaded.")
maxCompleted?.invoke(nativeAdLoader, ad)
}
override fun onNativeAdLoadFailed(adUnitId: String, error: MaxError) {
Log.d(TAG, "native Ad Max failed to load. ${error.message}")
failed?.invoke()
}
override fun onNativeAdClicked(ad: MaxAd) {
Log.d(TAG, "native Ad Max clicked.")
}
})
nativeAdLoader.loadAd()
}
}
fun showBannerAd(
context: Context,
adViewContainer: ViewGroup,
collapsible: String? = "bottom",
completed: (() -> Unit?)? = null,
failed: (() -> Unit?)? = null
) {
adViewContainer.viewTreeObserver.addOnGlobalLayoutListener(object :
ViewTreeObserver.OnGlobalLayoutListener {
override fun onGlobalLayout() {
val screenPixelDensity = context.resources.displayMetrics.density
val adWidth = (adViewContainer.width / screenPixelDensity).toInt()
val adSize =
AdSize.getCurrentOrientationAnchoredAdaptiveBannerAdSize(context, adWidth)
showBannerAd(context, adViewContainer, adSize, collapsible, completed, failed)
adViewContainer.viewTreeObserver.removeOnGlobalLayoutListener(this)
}
})
}
fun showBannerAd(
context: Context,
adViewContainer: ViewGroup,
adSize: AdSize,
collapsible: String? = "bottom",
completed: (() -> Unit?)? = null,
failed: (() -> Unit?)? = null
) {
if (!isShowAdBanner() || isBlacklist()) {
failed?.invoke()
return
}
Log.d(TAG, "banner Ad load.")
val adView = AdView(context)
adView.adUnitId = ID_ADMOB_BANNER
adView.setAdSize(adSize)
val extras = Bundle()
extras.putString("collapsible", collapsible)
val adRequest = AdRequest.Builder()
.addNetworkExtrasBundle(AdMobAdapter::class.java, extras)
.build()
adView.loadAd(adRequest)
adView.adListener = object : AdListener() {
override fun onAdClicked() {
Log.d(TAG, "banner Ad clicked.")
}
override fun onAdFailedToLoad(loadAdError: LoadAdError) {
Log.d(TAG, "banner Ad failed to load: ${loadAdError.message}")
failed?.invoke()
}
override fun onAdLoaded() {
Log.d(TAG, "banner Ad loaded.")
completed?.invoke()
}
}
adViewContainer.removeAllViews()
adViewContainer.addView(adView)
}
private fun showBannerAd(
context: Context,
adViewContainer: ViewGroup,
completed: (() -> Unit?)? = null,
failed: (() -> Unit?)? = null
) {
if (!isShowAdBanner() || isBlacklist()) {
failed?.invoke()
return
}
Log.d(TAG, "banner Ad Max load.")
val adView = MaxAdView(ID_MAX_BANNER, context)
adView.setListener(object : MaxAdViewAdListener {
override fun onAdLoaded(p0: MaxAd) {
Log.d(TAG, "banner Ad Max loaded.")
completed?.invoke()
}
override fun onAdDisplayed(p0: MaxAd) {
}
override fun onAdHidden(p0: MaxAd) {
}
override fun onAdClicked(p0: MaxAd) {
Log.d(TAG, "banner Ad Max clicked.")
}
override fun onAdLoadFailed(p0: String, p1: MaxError) {
Log.d(TAG, "banner Ad Max failed to load: ${p1.message}")
failed?.invoke()
}
override fun onAdDisplayFailed(p0: MaxAd, p1: MaxError) {
}
override fun onAdExpanded(p0: MaxAd) {
}
override fun onAdCollapsed(p0: MaxAd) {
}
})
adViewContainer.removeAllViews()
adViewContainer.addView(adView)
adView.loadAd()
}
fun isShowAdBanner(): Boolean {
val isShowAdBanner = SpUtils.getInstance().getInt(KEY_IS_SHOW_AD_BANNER)
return isShowAdBanner == 1
}
fun isShowAdNative(): Boolean {
val isShowAdNative = SpUtils.getInstance().getInt(KEY_IS_SHOW_AD_NATIVE)
return isShowAdNative == 1
}
fun isShowAdInter(): Boolean {
val isShowAdInter = SpUtils.getInstance().getInt(KEY_IS_SHOW_AD_INTER)
return isShowAdInter == 1
}
fun isShowAdBackInter(): Boolean {
val isShowAdBackInter = SpUtils.getInstance().getInt(KEY_IS_SHOW_AD_BACK_INTER)
return isShowAdBackInter == 1
}
fun isShowAdResultBackInter(): Boolean {
val isShowAdInter = SpUtils.getInstance().getInt(KEY_IS_SHOW_AD_RESULT_BACK_INTER)
return isShowAdInter == 1
}
fun isAppOpenAdAvailable(): Boolean {
return (if (useAdmob) appOpenAd != null else appOpenMaxAd?.isReady == true) && wasLoadTimeLessThanNHoursAgo(
1,
appOpenAdLoadTime
)
}
fun isInterstitialAdAvailable(): Boolean {
return (if (useAdmob) interstitialAd != null else interstitialMaxAd?.isReady == true) && wasLoadTimeLessThanNHoursAgo(
1,
interstitialAdLoadTime
)
}
private fun wasLoadTimeLessThanNHoursAgo(numHours: Long, time: Long): Boolean {
val dateDifference: Long = System.currentTimeMillis() - time
val numMilliSecondsPerHour: Long = 3600000
return dateDifference < numMilliSecondsPerHour * numHours
}
private fun isBlacklist(): Boolean {
return blacklist != 1
}
private fun checkAvailable(adUnit: String, checkIntervalTime: Boolean = false): Boolean {
if (isBlacklist()) {
ReportAdUtils.limitErrorAd(EVENT_BLACKLIST, adUnit)
return false
}
reset()
val displayNumber = SpUtils.getInstance().getInt(KEY_DISPLAY_NUMBER)
val requestNumber = SpUtils.getInstance().getInt(KEY_REQUEST_NUMBER)
val clickNumber = SpUtils.getInstance().getInt(KEY_CLICK_NUMBER)
val adShow = SpUtils.getInstance().getInt(KEY_AD_SHOW, DEFAULT_AD_SHOW)
val adRequest = SpUtils.getInstance().getInt(KEY_AD_REQUEST, DEFAULT_AD_REQUEST)
val adClick = SpUtils.getInstance().getInt(KEY_AD_CLICK, DEFAULT_AD_CLICK)
if (displayNumber >= adShow) {
ReportAdUtils.limitErrorAd(EVENT_SHOW_DISPLAYS, adUnit)
return false
}
if (requestNumber >= adRequest) {
ReportAdUtils.limitErrorAd(EVENT_SHOW_REQUESTS, adUnit)
return false
}
if (clickNumber >= adClick) {
ReportAdUtils.limitErrorAd(EVENT_SHOW_CLICKS, adUnit)
return false
}
if (!checkIntervalTime) return true
val interval = SpUtils.getInstance().getInt(KEY_AD_INTERVAL, DEFAULT_AD_INTERVAL) * 60 * 1000
val flag = System.currentTimeMillis() - lastShowTime > interval
if (!flag) {
ReportAdUtils.limitErrorAd(EVENT_SHOW_INTERVAL_TIME, adUnit)
}
return flag
}
fun Long.toFormatTime4(): String {
return SimpleDateFormat("yyyy-MM-dd", Locale.ENGLISH).format(this)
}
fun areTimestampsSameDay(timestamp1: Long, timestamp2: Long): Boolean {
val calendar1 = Calendar.getInstance().apply { timeInMillis = timestamp1 }
val calendar2 = Calendar.getInstance().apply { timeInMillis = timestamp2 }
// 比较年、月、日是否相同
return calendar1.get(Calendar.YEAR) == calendar2.get(Calendar.YEAR) &&
calendar1.get(Calendar.MONTH) == calendar2.get(Calendar.MONTH) &&
calendar1.get(Calendar.DAY_OF_MONTH) == calendar2.get(Calendar.DAY_OF_MONTH)
}
fun calculate(key: String) {
reset()
val value = SpUtils.getInstance().getInt(key)
SpUtils.getInstance().putInt(key, value + 1)
}
private fun reset() {
val currentTime = System.currentTimeMillis()
if (showTime.toInt() == 0 || showTime.toFormatTime4() != currentTime.toFormatTime4()) {
showTime = currentTime
SpUtils.getInstance().putInt(KEY_DISPLAY_NUMBER, 0)
SpUtils.getInstance().putInt(KEY_REQUEST_NUMBER, 0)
SpUtils.getInstance().putInt(KEY_CLICK_NUMBER, 0)
}
}
}
\ No newline at end of file
package com.base.scanqrclear.luma
import android.animation.Animator
import android.animation.AnimatorListenerAdapter
import android.animation.ValueAnimator
import android.app.Activity
import android.app.Dialog
import android.os.Bundle
import android.view.View
import android.view.animation.LinearInterpolator
import android.widget.Toast
import androidx.activity.addCallback
import androidx.appcompat.app.AppCompatActivity
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.lifecycle.lifecycleScope
import com.airbnb.lottie.LottieAnimationView
import com.base.scanqrclear.R
import com.base.scanqrclear.helper.EventUtils
import com.base.scanqrclear.luma.AdmobHelps.KEY_CLEANUP_SIZE
import com.base.scanqrclear.ui.main.MainActivity
import com.base.scanqrclear.utils.ActivityLauncher
import com.base.scanqrclear.utils.ExitType
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import kotlin.random.Random
open class BaseActivity2 : AppCompatActivity() {
protected lateinit var launcher: ActivityLauncher
protected var loadingDialog: Dialog? = null
protected var isDisableBack = false
protected var isExit = false
private var isShowAdInterstitial = false
private var isPause = false
private var lottieType: LottieType? = null
private var complete: (() -> Unit?)? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
launcher = ActivityLauncher(this)
immersive()
//todo 返回
// addBackPressedCallback()
ActivityManagerHelps.add(this)
EventUtils.event("${javaClass.simpleName}_show")
}
override fun onResume() {
super.onResume()
isPause = false
// if (isShowAdInterstitial) {
// lifecycleScope.launch {
// delay(1000)
// startAdInterstitial()
// }
// }
}
override fun onPause() {
super.onPause()
isPause = true
}
override fun onDestroy() {
super.onDestroy()
ActivityManagerHelps.remove(this)
isExit = false
lottieType = null
complete = null
}
protected open fun immersive() {
StatusBarHelps.immersive(this, getColor(R.color.color_ffb764))
}
protected fun showAnimationAd(
lottieType: LottieType = LottieType.CLEAN,
isShowAnimation: Boolean = true,
complete: (() -> Unit?)? = null
) {
this.lottieType = lottieType
this.complete = complete
showAdInterstitial(AdmobHelps.isShowAdInter()) {
val animationView = findViewById<ConstraintLayout>(R.id.animation)
animationView.setOnClickListener { }
if (isShowAnimation) {
StatusBarHelps.immersive(this, getColor(R.color.color_ffb764))
showAnimation(animationView, lottieType, complete)
} else {
complete?.invoke()
}
}
// if (isShowAnimation) {
// StatusBarHelps.immersive(this, getColor(R.color.color_ffb764))
// val animationView = findViewById<ConstraintLayout>(R.id.animation)
// showAnimation(animationView, lottieType) { startAdInterstitial() }
// } else {
// startAdInterstitial()
// }
}
// protected fun startAdInterstitial() {
// if (this is SplashActivity || this is MainActivity) return
// isShowAdInterstitial = true
// if (!isPause) {
// val animationView = findViewById<ConstraintLayout>(R.id.animation)
// showAdInterstitial(animationView, complete)
// }
// }
protected fun showAdInterstitial(isShow: Boolean, complete: (() -> Unit?)? = null) {
if (this.isFinishing || this.isDestroyed || isShowAdInterstitial) return
isShowAdInterstitial = true
if (isShow) {
AdmobHelps.showInterstitialAd(
this,
dismissed = {
clearLoading()
immersive()
complete?.invoke()
},
completed = {
StatusBarHelps.immersive(this, getColor(R.color.black))
loadingDialog?.dismiss()
},
failed = {
clearLoading()
immersive()
complete?.invoke()
}
)
} else {
isShowAdInterstitial = false
complete?.invoke()
}
}
protected fun showAnimation(
view: View,
lottieType: LottieType,
complete: (() -> Unit?)? = null
) {
isDisableBack = true
view.visibility = View.VISIBLE
val progress = view.findViewById<androidx.appcompat.widget.AppCompatTextView>(R.id.tv_progress)
progress.text = "0%"
val lottie = view.findViewById<LottieAnimationView>(R.id.lottie_animation)
lottie.setAnimation(lottieType.data)
lottie.loop(isLoop(lottieType))
lottie.playAnimation()
if (!lottieType.images.isNullOrEmpty())
lottie.imageAssetsFolder = lottieType.images
if (!isLoop(lottieType)) {
lottie.addAnimatorUpdateListener { animation ->
val value = animation.animatedValue as Float * 100
progress.text = "${value.toInt()}%"
if (value.toInt() == 100) {
view.visibility = View.GONE
complete?.invoke()
isDisableBack = false
}
}
return
}
lifecycleScope.launch(Dispatchers.Main) {
val durationTime = Random.Default.nextLong(3000, 3500)
ValueAnimator.ofInt(0, 100).apply {
duration = durationTime
interpolator = LinearInterpolator()
addUpdateListener { animation ->
val value = animation.animatedValue as Int
progress.text = "${value}%"
}
addListener(object : AnimatorListenerAdapter() {
override fun onAnimationEnd(animation: Animator) {
view.visibility = View.GONE
complete?.invoke()
isDisableBack = false
lottie.cancelAnimation()
}
})
}.start()
}
}
private fun isLoop(lottieType: LottieType): Boolean {
return lottieType != LottieType.CLEAN
}
private fun clearLoading() {
loadingDialog?.dismiss()
loadingDialog = null
isShowAdInterstitial = false
}
protected fun cleanFiles(list: List<FileBean>) {
lifecycleScope.launch(Dispatchers.IO) {
val paths = list.filter { it.isSelected }.map { it.path }.toTypedArray()
paths.forEach { FileHelps.deleteFile(it) }
withContext(Dispatchers.Main) {
MediaHelps.updateMedia(applicationContext, paths)
}
}
}
protected fun toCleanResult(size: Long, titleName: String) {
val cleanupSize = SpUtils.getInstance().getLong(KEY_CLEANUP_SIZE)
SpUtils.getInstance().putLong(KEY_CLEANUP_SIZE, cleanupSize + size)
CleanResultActivity.start(this, size, titleName)
ActivityManagerHelps.finishAll(MainActivity::class.java)
}
protected fun backPressed() {
onBackPressedDispatcher.onBackPressed()
}
protected open fun handleBackPressed(activity: Activity) {
when (activity) {
is MainActivity -> logout()
// is AppManagerActivity -> exit(ExitType.APP_MANAGER)
// is BatteryInfoActivity -> exit(ExitType.BATTERY_INFO)
// is CleanJunkActivity -> exit(ExitType.CLEAN)
// is LargeFileCleanActivity -> exit(ExitType.LARGE_FILE)
// is ScreenshotCleanActivity -> exit(ExitType.SCREENSHOT)
// is SimilarPhotosActivity -> exit(ExitType.SIMILAR_PHOTOS)
// is WhatsappCleanActivity -> exit(ExitType.WHATSAPP)
// is PhotoCompressionActivity -> exit(ExitType.PHOTO_COMPRESSION)
// else -> finishOrToMain()
}
}
private fun logout() {
DialogHelps.showLogoutDialog(this, {
ActivityManagerHelps.finishAll()
// exitProcess(0)
}, {
ActivityJumpHelps.start(this, launcher, R.string.junk_scan)
})
}
protected fun showExitDialog(title: String, content: CharSequence) {
if (isExit) return
isExit = true
DialogHelps.showExitDialog(
this,
title,
content,
{ showAdInterstitial(AdmobHelps.isShowAdBackInter()) { finishOrToMain() } },
{ isExit = false }
)
}
private fun exit(exit: ExitType) {
showExitDialog(getString(exit.title), getString(exit.content))
}
private fun addBackPressedCallback(activity: Activity) {
onBackPressedDispatcher.addCallback {
if (isDisableBack) {
Toast.makeText(applicationContext, getString(R.string.wait_a_moment), Toast.LENGTH_SHORT).show()
} else {
if (isShowAdInterstitial) return@addCallback
handleBackPressed(activity)
}
}
}
protected fun finishOrToMain() {
// if (this !is MainActivity
// && !ActivityManagerHelps.isActivityInStack(MainActivity::class.java)
// ) {
// MainActivity.start(this)
// }
// finish()
}
}
\ No newline at end of file
package com.base.scanqrclear.luma
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import androidx.viewbinding.ViewBinding
abstract class BaseAdapter<T, VB : ViewBinding>(
private var items: List<T> = emptyList()
) : RecyclerView.Adapter<BaseViewHolder<VB>>() {
interface OnClickCallback<T> {
fun onClicked(view: View, position: Int, item: T)
}
var callback: OnClickCallback<T>? = null
fun submitList(data: List<T>) {
items = data
notifyDataSetChanged()
}
fun add(item: T) {
items = items.toMutableList().apply { add(item) }
notifyItemInserted(items.size - 1)
}
val list: MutableList<T> get() = items.toMutableList()
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BaseViewHolder<VB> {
return BaseViewHolder(getViewBinding(LayoutInflater.from(parent.context), parent))
}
override fun onBindViewHolder(holder: BaseViewHolder<VB>, position: Int) {
bind(holder, position, items[position])
}
override fun getItemCount(): Int = items.size
abstract fun getViewBinding(layoutInflater: LayoutInflater, parent: ViewGroup): VB
abstract fun bind(holder: BaseViewHolder<VB>, position: Int, item: T)
}
\ No newline at end of file
package com.base.scanqrclear.luma
import androidx.recyclerview.widget.RecyclerView
import androidx.viewbinding.ViewBinding
class BaseViewHolder<VB : ViewBinding>(val binding: VB) : RecyclerView.ViewHolder(binding.root)
\ No newline at end of file
package com.base.scanqrclear.luma
import android.content.Context
import android.content.Intent
import android.os.Bundle
import android.view.View
import androidx.recyclerview.widget.LinearLayoutManager
import com.base.scanqrclear.R
import com.base.scanqrclear.databinding.ActivityCleanResultBinding
class CleanResultActivity : BaseActivity2() {
companion object {
var total = 0L
var titleName = ""
fun start(context: Context, total: Long, titleName: String) {
Companion.total = total
Companion.titleName = titleName
val intent = Intent(context, CleanResultActivity::class.java)
context.startActivity(intent)
}
}
private val binding by lazy {
ActivityCleanResultBinding.inflate(layoutInflater)
}
private lateinit var adapter: CleanResultAdapter
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(binding.root)
initView()
}
//todo
// override fun handleBackPressed() {
// showAdInterstitial(AdmobHelps.isShowAdResultBackInter()) { finishOrToMain() }
// }
private fun initView() {
adapter = CleanResultAdapter(getData())
adapter.callback = object : BaseAdapter.OnClickCallback<FunctionBean> {
override fun onClicked(view: View, position: Int, item: FunctionBean) {
ActivityJumpHelps.start(this@CleanResultActivity, launcher, item.name)
finish()
}
}
binding.rvResult.adapter = adapter
binding.rvResult.layoutManager = LinearLayoutManager(this)
if (titleName.isNotEmpty()) binding.tvTitle.text = titleName
binding.tvCleanedUp.text = "${getString(R.string.cleaned_up)} ${Utils.getSize(total)}"
binding.flBack.setOnClickListener {
onBackPressedDispatcher.onBackPressed()
}
}
private fun getData(): List<FunctionBean> {
val list = mutableListOf<FunctionBean>().apply {
add(FunctionBean(R.mipmap.icon_junk_scan1, R.string.junk_scan, R.string.result_clean_junk, text = R.string.clean_now))
add(FunctionBean(R.mipmap.icon_battery_1, R.string.battery_info, R.string.result_battery_info, text = R.string.view))
add(FunctionBean(R.mipmap.icon_app_process_1, R.string.app_process, R.string.result_app_processes, text = R.string.view))
add(FunctionBean(R.mipmap.icon_large_1, R.string.large_files, R.string.result_large_file, text = R.string.clean_now))
add(FunctionBean(R.mipmap.icon_image, R.string.image_compressor, R.string.result_photo_compression, text = R.string.compress))
add(FunctionBean(R.mipmap.icon_screenshot_1, R.string.screenshot_clean, R.string.result_screenshot_clean, text = R.string.clean_now))
add(FunctionBean(R.mipmap.icon_similar_1, R.string.similar_photos, R.string.result_similar_photos, text = R.string.clean_now))
add(FunctionBean(R.mipmap.icon_app_1, R.string.app_manager, R.string.result_app_manager, text = R.string.view))
add(FunctionBean(R.mipmap.icon_whatsapp_1, R.string.whatsapp_clean, R.string.result_whatsapp_clean, text = R.string.clean_now))
}
val filteredList = list.filter { getString(it.name) != titleName }
val randomIndices = filteredList.indices.shuffled().take(4).toList()
val data = randomIndices.map { filteredList[it] }
return data
}
}
\ No newline at end of file
package com.base.scanqrclear.luma
import android.animation.ObjectAnimator
import android.view.LayoutInflater
import android.view.ViewGroup
import com.base.scanqrclear.databinding.ItemCleanResultBinding
class CleanResultAdapter(
val data: List<FunctionBean>
) : BaseAdapter<FunctionBean, ItemCleanResultBinding>(data) {
override fun getViewBinding(
layoutInflater: LayoutInflater,
parent: ViewGroup
): ItemCleanResultBinding {
return ItemCleanResultBinding.inflate(layoutInflater, parent, false)
}
override fun bind(
holder: BaseViewHolder<ItemCleanResultBinding>,
position: Int,
item: FunctionBean
) {
holder.binding.ivImage.setImageResource(item.icon)
holder.binding.tvName.setText(item.name)
holder.binding.tvContent.setText(item.content)
holder.binding.tvCleanNow.setText(item.text)
holder.binding.tvCleanNow.setOnClickListener {
callback?.onClicked(it, position, item)
}
val translationX = holder.itemView.context.resources.displayMetrics.widthPixels.toFloat()
holder.itemView.translationX = -translationX
ObjectAnimator.ofFloat(holder.itemView, "translationX", -translationX, 0f).apply {
duration = 1000
startDelay = (position + 1) * 300L
start()
}
}
}
\ No newline at end of file
package com.base.scanqrclear.luma
import android.os.Build
import android.util.Log
import com.base.scanqrclear.BuildConfig
import com.base.scanqrclear.GlobalConfig.PACKAGE_NAME
import com.base.scanqrclear.MyApplication
import com.base.scanqrclear.luma.AdmobHelps.KEY_DEVICE_NAME
import com.base.scanqrclear.luma.AdmobHelps.KEY_GOOGLE_ADVERTISER_ID
import com.base.scanqrclear.luma.AdmobHelps.KEY_UUID
import com.google.android.gms.ads.identifier.AdvertisingIdClient
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.MainScope
import kotlinx.coroutines.launch
import org.json.JSONObject
import java.util.Locale
import java.util.TimeZone
object DeviceHelps {
fun init() {
MainScope().launch(Dispatchers.IO) {
getGoogleAdvertiserId()
getDeviceName()
}
}
private fun getGoogleAdvertiserId() {
try {
val googleAdvertiserId = AdvertisingIdClient.getAdvertisingIdInfo(MyApplication.appContext).id ?: ""
if (googleAdvertiserId.isNotEmpty())
SpUtils.getInstance().putString(KEY_GOOGLE_ADVERTISER_ID, googleAdvertiserId)
} catch (e: Exception) {
Log.d("TAG", "getGoogleAdvertiserId error: ${e.localizedMessage}")
}
}
private fun getDeviceName() {
val manufacturer = Build.MANUFACTURER
val model = Build.MODEL
val flag = model.lowercase(Locale.getDefault()).startsWith(manufacturer.lowercase(Locale.getDefault()))
val deviceName = if (flag) {
if (model.isNullOrEmpty()) {
model
} else {
model.substring(0, 1)
.uppercase(Locale.getDefault()) + model.substring(1)
.lowercase(Locale.getDefault())
}
} else {
val ss = if (manufacturer.isNullOrEmpty()) {
manufacturer
} else {
manufacturer.substring(0, 1)
.uppercase(Locale.getDefault()) + manufacturer.substring(1)
.lowercase(Locale.getDefault())
}
"$ss $model"
}
SpUtils.getInstance().putString(KEY_DEVICE_NAME, deviceName)
}
// 获取配置参数
fun getConfigParams(): JSONObject {
val packageName = PACKAGE_NAME
val jsonObject = JSONObject()
jsonObject.put("${packageName}_3", SpUtils.getInstance().getString(KEY_DEVICE_NAME))//手机型号
jsonObject.put("${packageName}_4", Build.MANUFACTURER)//手机厂商
jsonObject.put("${packageName}_5", Build.VERSION.SDK_INT)//android系统版本号
jsonObject.put("${packageName}_8", BuildConfig.VERSION_NAME)//APP版本号,如:1.1.2
jsonObject.put("${packageName}_9", SpUtils.getInstance().getString(KEY_UUID))//Android id
jsonObject.put("${packageName}_10", SpUtils.getInstance().getString(KEY_GOOGLE_ADVERTISER_ID))//Google advertiser id
jsonObject.put("${packageName}_13", "android")//platform 默认android
jsonObject.put("${packageName}_14", BuildConfig.VERSION_CODE)//android版本,如:13
jsonObject.put("${packageName}_15", "google")
jsonObject.put("${packageName}_24", BuildConfig.BUILD_TYPE)//环境
val timeZone: TimeZone = TimeZone.getDefault()
jsonObject.put("${packageName}_34", timeZone.id) // 手机本地时区,值如下格式:America/New_York
return jsonObject
}
}
\ No newline at end of file
package com.base.scanqrclear.luma
import android.animation.ObjectAnimator
import android.app.Activity
import android.app.Dialog
import android.content.Context
import android.content.Intent
import android.net.Uri
import android.view.LayoutInflater
import android.view.View
import android.view.animation.LinearInterpolator
import com.base.scanqrclear.R
import com.base.scanqrclear.databinding.DialogDeleteBinding
import com.base.scanqrclear.databinding.DialogExitBinding
import com.base.scanqrclear.databinding.DialogLoadingBinding
import com.base.scanqrclear.databinding.DialogLogoutBinding
import com.base.scanqrclear.databinding.DialogNotificationBinding
import com.base.scanqrclear.databinding.DialogPermissionBinding
import com.base.scanqrclear.databinding.DialogScoreBinding
import com.base.scanqrclear.luma.AdmobHelps.KEY_SCORE
import com.base.scanqrclear.utils.ExitType
import com.google.android.material.bottomsheet.BottomSheetDialog
object DialogHelps {
fun showStoragePermissionDialog(context: Context, open: () -> Unit, dismiss: () -> Unit) {
val binding = DialogPermissionBinding.inflate(LayoutInflater.from(context))
val dialog = BottomSheetDialog(context, R.style.BottomSheetDialog).apply {
setContentView(binding.root)
show()
}
dialog.setOnDismissListener { dismiss.invoke() }
binding.tvOpenSettings.setOnClickListener {
it.isEnabled = false
open.invoke()
it.postDelayed({ dialog.dismiss() }, 500)
}
}
fun showNotificationDialog(context: Context, turnOn: () -> Unit, dismiss: () -> Unit) {
val binding = DialogNotificationBinding.inflate(LayoutInflater.from(context))
val dialog = BottomSheetDialog(context, R.style.BottomSheetDialog).apply {
setCancelable(false)
setCanceledOnTouchOutside(false)
setContentView(binding.root)
show()
}
binding.ivClose.setOnClickListener {
dismiss.invoke()
dialog.dismiss()
}
binding.tvTurnOn.setOnClickListener {
it.isEnabled = false
turnOn.invoke()
it.postDelayed({ dialog.dismiss() }, 500)
}
}
fun showScoreDialog(context: Context, view: View? = null): Dialog {
view?.isEnabled = false
val binding = DialogScoreBinding.inflate(LayoutInflater.from(context))
val dialog = Dialog(context, R.style.CustomDialogStyle).apply {
setCancelable(false)
setCanceledOnTouchOutside(false)
setContentView(binding.root)
show()
}
val starList = listOf(
binding.ivStar1,
binding.ivStar2,
binding.ivStar3,
binding.ivStar4,
binding.ivStar5,
)
var score = 0
starList.forEachIndexed { index, view ->
view.setOnClickListener {
starList.forEachIndexed { index2, view2 ->
view2.setImageResource(if (index2 <= index) R.mipmap.star_s else R.mipmap.star_n)
if (index == index2) scoreAnimation(view2)
}
score = index + 1
binding.lottieFace.cancelAnimation()
binding.lottieFace.setAnimation(if (score != 5) "top_rate_sad.json" else "top_rate_happy.json")
binding.lottieFace.playAnimation()
}
}
binding.ivClose.setOnClickListener {
view?.isEnabled = true
dialog.dismiss()
}
binding.tvSubmit.setOnClickListener {
if (score == 5) {
SpUtils.getInstance().putInt(KEY_SCORE, score)
}
try {
val url = "https://play.google.com/store/apps/details?id=${context.packageName}"
context.startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(url)))
} catch (_: Exception) {
}
view?.isEnabled = true
dialog.dismiss()
}
return dialog
}
private fun scoreAnimation(view: View) {
ObjectAnimator.ofFloat(
view,
View.SCALE_X,
0.75f,
1.2f,
1f
).apply {
duration = 500
interpolator = LinearInterpolator()
start()
}
ObjectAnimator.ofFloat(
view,
View.SCALE_Y,
0.75f,
1.2f,
1f
).apply {
duration = 500
interpolator = LinearInterpolator()
start()
}
}
fun showDeleteDialog(context: Context, view: View, confirm: (() -> Unit?)?): DialogDeleteBinding {
view.isEnabled = false
val binding = DialogDeleteBinding.inflate(LayoutInflater.from(context))
val dialog = Dialog(context, R.style.CustomDialogStyle).apply {
setCancelable(false)
setCanceledOnTouchOutside(false)
setContentView(binding.root)
show()
}
binding.tvCancel.setOnClickListener {
dialog.dismiss()
view.isEnabled = true
}
binding.tvDelete.setOnClickListener {
it.isEnabled = false
dialog.dismiss()
confirm?.invoke()
view.isEnabled = true
}
return binding
}
fun showConfirmDialog(context: Context, view: View, confirm: (() -> Unit?)?) {
val binding = showDeleteDialog(context, view, confirm)
binding.tvTitle.text = context.getString(R.string.confirm)
binding.tvContent.text = context.getString(R.string.confirm_content)
binding.tvDelete.text = context.getString(R.string.compress)
}
fun showExitDialog(
context: Activity,
exit: ExitType,
confirm: () -> Unit,
dismiss: () -> Unit
) {
showExitDialog(context, context.getString(exit.title), context.getString(exit.content), confirm, dismiss)
}
fun showExitDialog(
context: Activity,
title: String,
content: CharSequence,
confirm: () -> Unit,
dismiss: () -> Unit
) {
val binding = DialogExitBinding.inflate(LayoutInflater.from(context))
val dialog = Dialog(context, R.style.CustomDialogStyle).apply {
setCancelable(false)
setCanceledOnTouchOutside(false)
setContentView(binding.root)
show()
}
binding.tvTitle.text = title
binding.tvContent.text = content
binding.ivCancel.setOnClickListener {
dialog.dismiss()
dismiss.invoke()
}
binding.tvCancel.setOnClickListener {
dialog.dismiss()
dismiss.invoke()
}
binding.tvSure.setOnClickListener {
it.isEnabled = false
dialog.dismiss()
confirm.invoke()
}
AdmobHelps.showNativeAd(context, completed = {
binding.adNative.setExitNativeAd(it)
}, maxCompleted = { nativeAdLoader, nativeMaxAd ->
binding.adNative.setExitNativeAd(nativeAdLoader, nativeMaxAd)
})
}
fun showLogoutDialog(context: Activity, confirm: () -> Unit, check: () -> Unit) {
val binding = DialogLogoutBinding.inflate(LayoutInflater.from(context))
val dialog = Dialog(context, R.style.CustomDialogStyle).apply {
setCancelable(false)
setCanceledOnTouchOutside(false)
setContentView(binding.root)
show()
}
binding.ivCancel.setOnClickListener { dialog.dismiss() }
binding.tvCancel.setOnClickListener {
it.isEnabled = false
dialog.dismiss()
check.invoke()
}
binding.tvExit.setOnClickListener {
it.isEnabled = false
dialog.dismiss()
confirm.invoke()
}
AdmobHelps.showNativeAd(context, completed = {
binding.adNative.setExitNativeAd(it)
}, maxCompleted = { nativeAdLoader, nativeMaxAd ->
binding.adNative.setExitNativeAd(nativeAdLoader, nativeMaxAd)
})
}
fun showLoadingDialog(context: Context): Dialog {
val binding = DialogLoadingBinding.inflate(LayoutInflater.from(context))
val dialog = Dialog(context, R.style.CustomDialogStyle).apply {
setCancelable(false)
setCanceledOnTouchOutside(false)
setContentView(binding.root)
show()
}
return dialog
}
}
\ No newline at end of file
package com.base.scanqrclear.luma
import android.os.Environment
import java.io.File
object FileHelps {
private const val WHATS_APP_MEDIA_DIR = "Android/media/com.whatsapp/WhatsApp/Media/"
fun findWhatsappFiles(): MutableList<FileBean> {
val rootDir = File(getAppSpecificDirPath(WHATS_APP_MEDIA_DIR))
// val rootDir = File(getAppSpecificDirPath("Pictures/"))
val list = findFilesRecursive(rootDir, arrayOf())
list.sortByDescending { it.lastModified }
return list
}
fun findEmptyFolders(): MutableList<FileBean> {
val externalStorageDir = Environment.getExternalStorageDirectory()
val filters = arrayOf("/storage/emulated/0/Android/data", "/storage/emulated/0/Android/obb")
val emptyFolders = getAllEmptyFolders(externalStorageDir, filters)
return emptyFolders.map { FileBean(it.name, it.path, length = 4 * 1024) }.toMutableList()
}
fun findTempFiles(): MutableList<FileBean> {
val rootDir = Environment.getExternalStorageDirectory()
return findFilesRecursive(rootDir, arrayOf(".temp"))
}
fun findApkFiles(): MutableList<FileBean> {
val rootDir = Environment.getExternalStorageDirectory()
return findFilesRecursive(rootDir, arrayOf(".apk", ".aab"))
}
fun findLogFiles(): MutableList<FileBean> {
val rootDir = Environment.getExternalStorageDirectory()
return findFilesRecursive(rootDir, arrayOf(".log"))
}
fun deleteFile(filePath: String) {
File(filePath).deleteIfExists()
}
fun File?.deleteIfExists(): Boolean {
if (this?.exists() == true) {
return this.delete()
}
return false
}
private fun findFilesRecursive(dir: File, suffixes: Array<String>): MutableList<FileBean> {
return dir.walk()
.filter { it.isFile && it.endsWith(suffixes) }
.map { FileBean(it.name, it.path, length = it.length(), lastModified = it.lastModified()) }
.toMutableList()
}
private fun File.endsWith(suffixes: Array<String>): Boolean {
return suffixes.isEmpty() || suffixes.any { name.lowercase().endsWith(it.lowercase()) }
}
private fun getAppSpecificDirPath(relativePath: String): String {
return "${Environment.getExternalStorageDirectory()}/$relativePath"
}
private fun getAllEmptyFolders(root: File, filters: Array<String>): List<File> {
return root.walk()
.filter {
it.isDirectory && !filters.contains(it.path) && !it.isHidden && it.list()?.isEmpty() == true
}
.toList()
}
}
\ No newline at end of file
package com.base.scanqrclear.luma
class FunctionBean(
val icon: Int = 0,
val name: Int,
val content: Int = 0,
val text: Int = 0,
val backgroundId: Int = 0
)
\ No newline at end of file
package com.base.scanqrclear.luma
enum class LottieType(val data: String, val images: String? = null) {
APP_MANAGER("app_manager/scan/data.json", "app_manager/scan/images"),
APP_PROCESS("app_process/scan/data.json", "app_process/scan/images"),
BATTERY_INFO("battery_info/scan/data.json", "battery_info/scan/images"),
LARGE_FILE("large_files/scan/data.json", "large_files/scan/images"),
PHOTO_COMPRESSION("photo_compression/scan/data.json", "photo_compression/scan/images"),
PHOTO_COMPRESSION_COMPRESS("photo_compression/compress/data.json", "photo_compression/compress/images"),
SCREENSHOT("screenshot/scan/data.json", "screenshot/scan/images"),
SIMILAR_PHOTOS("similar_photos/scan/data.json", "similar_photos/scan/images"),
WHATSAPP("whatsapp/scan/data.json", "whatsapp/scan/images"),
CLEAN("clean.json")
}
\ No newline at end of file
package com.base.scanqrclear.luma
import android.annotation.SuppressLint
import android.content.Context
import android.database.Cursor
import android.media.MediaScannerConnection
import android.net.Uri
import android.provider.MediaStore
import java.io.File
import java.text.SimpleDateFormat
import java.util.Locale
object MediaHelps {
private val projection = arrayOf(
MediaStore.Files.FileColumns.DISPLAY_NAME,
MediaStore.Files.FileColumns.DATA,
MediaStore.Files.FileColumns.SIZE,
MediaStore.Files.FileColumns.DATE_MODIFIED,
MediaStore.Files.FileColumns.MIME_TYPE
)
private const val EXTERNAL = "external"
fun updateMedia(context:Context, paths: Array<String>) {
MediaScannerConnection.scanFile(context, paths, null, null)
}
fun findFiles(context: Context, size: Long = 0): List<FileBean> {
return queryFilesWithSize(context, MediaStore.Files.getContentUri(EXTERNAL), size)
}
fun findSpecifiedFiles(context: Context): List<FileBean> {
val suffixes = arrayOf("%.log", "%.apk", "%.aab", "%.temp", "%.LOG", "%.APK", "%.AAB", "%.TEMP")
return queryFilesWithSuffixes(context, MediaStore.Files.getContentUri(EXTERNAL), suffixes)
}
fun findScreenshotsFiles(context: Context): Map<String, MutableList<FileBean>> {
val selection = "${MediaStore.Images.Media.RELATIVE_PATH} LIKE ?"
val selectionArgs = arrayOf("%Screenshots%")
val list = query(context, MediaStore.Images.Media.EXTERNAL_CONTENT_URI, projection, selection, selectionArgs, projection[3])
val map = mutableMapOf<String, MutableList<FileBean>>()
val dateFormat = SimpleDateFormat("yyyy/MM", Locale.getDefault())
list.forEach { map.getOrPut(dateFormat.format(it.lastModified)) { mutableListOf() }.add(it) }
return map
}
fun findImageFiles(context: Context, size: Long = 0): List<FileBean> {
return queryFilesWithSize(context, MediaStore.Images.Media.EXTERNAL_CONTENT_URI, size)
}
fun findVideoFiles(context: Context, size: Long = 0): List<FileBean> {
return queryFilesWithSize(context, MediaStore.Video.Media.EXTERNAL_CONTENT_URI, size)
}
fun findAudioFiles(context: Context, size: Long = 0): List<FileBean> {
return queryFilesWithSize(context, MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, size)
}
fun findDocFiles(context: Context, size: Long = 0): List<FileBean> {
val mimeTypes = arrayOf(
"text/plain",
"application/msword",
"application/vnd.openxmlformats-officedocument.wordprocessingml.document",
"application/vnd.ms-excel",
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
"application/pdf",
)
return queryFilesWithMimeTypes(context, MediaStore.Files.getContentUri(EXTERNAL), mimeTypes, size)
}
private fun queryFilesWithSuffixes(context: Context, uri: Uri, suffixes: Array<String>): List<FileBean> {
val selection = suffixes.joinToString(separator = " OR ") { "${MediaStore.Files.FileColumns.DISPLAY_NAME} LIKE ?" }
return query(context, uri, projection, selection, suffixes)
}
private fun queryFilesWithSize(context: Context, uri: Uri, size: Long): List<FileBean> {
var selection: String? = null
var selectionArgs: Array<String>? = null
if (size > 0) {
selection = "${MediaStore.Files.FileColumns.SIZE} >= ?"
selectionArgs = arrayOf(size.toString())
}
return query(context, uri, projection, selection, selectionArgs)
}
private fun queryFilesWithMimeTypes(context: Context, uri: Uri, mimeTypes: Array<String>, size: Long): List<FileBean> {
val (selection, selectionArgs) = buildSelectionWithMimeTypes(mimeTypes, size)
return query(context, uri, projection, selection, selectionArgs)
}
private fun buildSelectionWithMimeTypes(mimeTypes: Array<String>, size: Long): Pair<String, Array<String>> {
val mimeTypeConditions = mimeTypes.joinToString(separator = " OR ") { "${MediaStore.Files.FileColumns.MIME_TYPE}=?" }
val sizeCondition = "${MediaStore.Files.FileColumns.SIZE} >=?"
val selection = "($mimeTypeConditions) AND ($sizeCondition)"
val selectionArgs = mimeTypes + size.toString()
return selection to selectionArgs
}
private fun buildSelectionWithMimeTypes(mimeTypes: Array<String>): Pair<String, Array<String>> {
val selection = mimeTypes.joinToString(prefix = "${MediaStore.Files.FileColumns.MIME_TYPE}=?", separator = " OR ") { "'$it'" }
val selectionArgs = mimeTypes
return Pair(selection, selectionArgs)
}
@SuppressLint("Range")
private fun query(
context: Context,
uri: Uri,
projection: Array<String>,
selection: String? = null,
selectionArgs: Array<String>? = null,
sortOrder: String = MediaStore.Files.FileColumns.DATE_MODIFIED
): List<FileBean> {
val list = mutableListOf<FileBean>()
var cursor: Cursor? = null
try {
val contentResolver = context.contentResolver
cursor = contentResolver.query(
uri,
projection,
selection,
selectionArgs,
"${sortOrder} DESC"
)
cursor?.let {
while (it.moveToNext()) {
val filePath = it.getString(it.getColumnIndex(projection[1]))
if (!File(filePath).exists()) continue
val displayName = it.getString(it.getColumnIndex(projection[0]))
val size = it.getLong(it.getColumnIndex(projection[2]))
val dateModified = it.getLong(it.getColumnIndex(projection[3])) * 1000
val mineType = it.getString(it.getColumnIndex(projection[4]))
list.add(FileBean(displayName, filePath, 0, size, dateModified, mineType))
}
}
} catch (e: Exception) {
e.printStackTrace()
} finally {
cursor?.close()
}
return list
}
}
\ No newline at end of file
package com.base.scanqrclear.luma
import android.content.Context
import android.util.AttributeSet
import android.view.LayoutInflater
import android.widget.FrameLayout
import android.widget.ImageView
import android.widget.TextView
import androidx.annotation.LayoutRes
import com.applovin.mediation.MaxAd
import com.applovin.mediation.nativeAds.MaxNativeAdLoader
import com.applovin.mediation.nativeAds.MaxNativeAdView
import com.applovin.mediation.nativeAds.MaxNativeAdViewBinder
import com.base.scanqrclear.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) {
fun setExitNativeAd(nativeAd: NativeAd) {
setNativeAd(nativeAd, R.layout.layout_ad_native_exit)
}
fun setExitNativeAd(
nativeAdLoader: MaxNativeAdLoader,
nativeAd: MaxAd,
@LayoutRes resource: Int = R.layout.layout_ad_native_exit
) {
setNativeAd(nativeAdLoader, nativeAd, resource)
}
fun setNativeAd(
nativeAdLoader: MaxNativeAdLoader,
nativeAd: MaxAd,
@LayoutRes resource: Int = R.layout.layout_ad_native
) {
val builder = MaxNativeAdViewBinder.Builder(resource)
.setTitleTextViewId(R.id.ad_headline)
.setIconImageViewId(R.id.ad_icon)
.setMediaContentViewGroupId(R.id.ad_media)
.setCallToActionButtonId(R.id.ad_call_to_action)
if (resource != R.layout.layout_ad_native)
builder.setBodyTextViewId(R.id.ad_body)
visibility = VISIBLE
val adView = MaxNativeAdView(builder.build(), context)
nativeAdLoader.render(adView, nativeAd)
removeAllViews()
addView(adView)
}
fun setNativeAd(nativeAd: NativeAd, @LayoutRes resource: Int ?= R.layout.layout_ad_native) {
visibility = VISIBLE
val adView = resource?.let { LayoutInflater.from(context).inflate(it, null) } as NativeAdView
adView.mediaView = adView.findViewById(R.id.ad_media)
adView.headlineView = adView.findViewById(R.id.ad_headline)
adView.callToActionView = adView.findViewById(R.id.ad_call_to_action)
adView.iconView = adView.findViewById(R.id.ad_icon)
(adView.headlineView as TextView?)?.text = nativeAd.headline
adView.mediaView!!.mediaContent = nativeAd.mediaContent
if (nativeAd.callToAction != null) {
(adView.callToActionView as TextView?)?.text = nativeAd.callToAction
}
if (nativeAd.icon != null) {
(adView.iconView as ImageView?)?.setImageDrawable(nativeAd.icon!!.drawable)
}
val body = adView.findViewById<TextView>(R.id.ad_body)
if (body != null) {
adView.bodyView = body
if (nativeAd.body == null) {
adView.bodyView?.visibility = GONE
} else {
adView.bodyView?.visibility = VISIBLE
(adView.bodyView as TextView?)?.text = nativeAd.body
}
}
adView.setNativeAd(nativeAd)
removeAllViews()
addView(adView)
}
}
\ No newline at end of file
package com.base.scanqrclear.luma
import android.Manifest
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.net.Uri
import android.os.Build
import android.os.Environment
import android.provider.Settings
import androidx.core.app.NotificationManagerCompat
import androidx.core.content.ContextCompat
import com.base.scanqrclear.utils.ActivityLauncher
object PermissionHelps {
fun requestFilesAccessPermission(
context: Context,
launcher: ActivityLauncher,
result: (Boolean) -> Unit
) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
try {
val intent = Intent(Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION)
intent.addCategory("android.intent.category.DEFAULT")
intent.data = Uri.parse("package:${context.packageName}")
launcher.launch(intent) {
result.invoke(checkFilesAccessPermission(context))
}
} catch (_: Exception) { }
} else {
val array = arrayOf(
Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.WRITE_EXTERNAL_STORAGE
)
launcher.launch(array) { permissions ->
result(permissions.values.all { it })
}
}
}
fun requestNotificationPermission(
context: Context,
launcher: ActivityLauncher,
result: (Boolean) -> Unit
) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
val array = arrayOf(Manifest.permission.POST_NOTIFICATIONS)
launcher.launch(array) { permissions ->
val flag = permissions.values.all { it }
if (!flag) notificationSettings(context, launcher, result)
}
} else {
notificationSettings(context, launcher, result)
}
}
fun notificationSettings(
context: Context,
launcher: ActivityLauncher,
result: (Boolean) -> Unit
) {
val intent = Intent()
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
intent.action = Settings.ACTION_APP_NOTIFICATION_SETTINGS
intent.putExtra(Settings.EXTRA_APP_PACKAGE, context.packageName)
} else {
intent.action = "android.settings.APP_NOTIFICATION_SETTINGS"
intent.putExtra("app_package", context.packageName)
}
launcher.launch(intent) {
result.invoke(checkNotificationPermission(context))
}
}
fun checkNotificationPermission(context: Context): Boolean {
return NotificationManagerCompat.from(context).areNotificationsEnabled()
}
fun checkFilesAccessPermission(context: Context): Boolean {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
return Environment.isExternalStorageManager()
} else {
val readPermission = ContextCompat.checkSelfPermission(context, Manifest.permission.READ_EXTERNAL_STORAGE)
val writePermission = ContextCompat.checkSelfPermission(context, Manifest.permission.WRITE_EXTERNAL_STORAGE)
return readPermission == PackageManager.PERMISSION_GRANTED && writePermission == PackageManager.PERMISSION_GRANTED
}
}
}
\ No newline at end of file
package com.base.scanqrclear.luma
import android.app.Activity
import android.os.Bundle
import com.base.scanqrclear.MyApplication
import com.base.scanqrclear.helper.EventUtils
import com.facebook.appevents.AppEventsConstants
import com.facebook.appevents.AppEventsLogger
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
import kotlin.random.Random
object ReportAdUtils {
private val taichiPref by lazy {
MyApplication.appContext.getSharedPreferences("TaichiTroasCache", 0)
}
private val taichiSharedPreferencesEditor by lazy {
taichiPref.edit()
}
fun pullStartAd(
reqId: String,
adUnit: String,
className: String,
) {
val obj = JSONObject()
obj.put("req_id", reqId)
obj.put("ad_unit", adUnit)
obj.put("from", className)
EventUtils.event("ad_pull_start", "", ext = obj)
}
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)
obj.put("mediation", "admob")
if (error == null) {
obj.put("status", "1")
} else {
obj.put("errMsg", error)
obj.put("status", "2")
}
EventUtils.event("ad_pull", "", ext = obj)
}
class EventOnPaidEventListener(private val ad: Any?) : OnPaidEventListener {
override fun onPaidEvent(adValue: AdValue) {
val adRatio = SpUtils.getInstance().getInt("adRatio", 100)
val random = Random.Default.nextInt(1, 100)
if (random > adRatio) {
taichiSharedPreferencesEditor.putFloat("TaichiTroasCache", 0f)
taichiSharedPreferencesEditor.commit()
EventUtils.event("ad_price_limit")
return
}
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)
val adValue = SpUtils.getInstance().getInt("adValue", 1) / 100f
if (currentTaichiTroasCache >= adValue) {//如果超过0.01就触发一次tROAS taichi事件
val roasbundle = Bundle()
roasbundle.putDouble(
FirebaseAnalytics.Param.VALUE,
currentTaichiTroasCache
)
roasbundle.putString(FirebaseAnalytics.Param.CURRENCY, "USD")
Firebase.analytics.logEvent("Total_Ads_Revenue_001", roasbundle)
taichiSharedPreferencesEditor.putFloat("TaichiTroasCache", 0f)//重新清零,开始计算
val logger = AppEventsLogger.newLogger(MyApplication.appContext)
val parameters = Bundle()
parameters.putString(AppEventsConstants.EVENT_PARAM_CURRENCY, "USD")
logger.logEvent("ad_value", currentTaichiTroasCache, parameters)
} else {
taichiSharedPreferencesEditor.putFloat("TaichiTroasCache", currentTaichiTroasCache.toFloat())
}
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)
obj.put("adUnitId", "nativeAd")
}
}
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)
obj.put("mediation", "admob")
EventUtils.event("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)
obj.put("mediation", "admob")
EventUtils.event("ad_show", "", ext = obj)
}
fun showErrorAd(reason: String, adUnit: String) {
val obj = JSONObject()
obj.put("reason", reason)
obj.put("ad_unit", adUnit)
EventUtils.event("ad_show_error", "", ext = obj)
}
fun showPrepareAd(adUnit: String) {
val obj = JSONObject()
obj.put("ad_unit", adUnit)
EventUtils.event("ad_prepare_show", "", ext = obj)
}
fun limitErrorAd(reason: String, adUnit: String) {
val obj = JSONObject()
obj.put("reason", reason)
obj.put("ad_unit", adUnit)
EventUtils.event("ad_limit_error", "", ext = obj)
}
}
\ No newline at end of file
package com.base.scanqrclear.luma
import android.content.Context
import android.content.SharedPreferences
import com.base.scanqrclear.MyApplication
import org.json.JSONObject
class SpUtils private constructor(context: Context) {
private val sharedPreferences: SharedPreferences = context.getSharedPreferences("app_preferences", Context.MODE_PRIVATE)
// 通用的保存数据方法
fun <T> put(key: String, value: T) {
when (value) {
is String -> putString(key, value)
is Int -> putInt(key, value)
is Long -> putLong(key, value)
is Boolean -> putBoolean(key, value)
is Float -> putFloat(key, value)
else -> throw IllegalArgumentException("Unsupported type")
}
}
// 通用的获取数据方法
fun <T> get(key: String, defaultValue: T): T {
return when (defaultValue) {
is String -> getString(key, defaultValue)
is Int -> getInt(key, defaultValue)
is Long -> getLong(key, defaultValue)
is Boolean -> getBoolean(key, defaultValue)
is Float -> getFloat(key, defaultValue)
else -> throw IllegalArgumentException("Unsupported type")
} as T
}
// 保存字符串
fun putString(key: String, value: String) {
sharedPreferences.edit().putString(getKey(key), value).apply()
}
// 获取字符串
fun getString(key: String, defaultValue: String = ""): String {
return sharedPreferences.getString(getKey(key), defaultValue) ?: defaultValue
}
// 保存整数
fun putInt(key: String, value: Int) {
sharedPreferences.edit().putInt(getKey(key), value).apply()
}
// 获取整数
fun getInt(key: String, defaultValue: Int = 0): Int {
return sharedPreferences.getInt(getKey(key), defaultValue)
}
// 保存长整数
fun putLong(key: String, value: Long) {
sharedPreferences.edit().putLong(getKey(key), value).apply()
}
// 获取长整数
fun getLong(key: String, defaultValue: Long = 0): Long {
return sharedPreferences.getLong(getKey(key), defaultValue)
}
// 保存布尔值
fun putBoolean(key: String, value: Boolean) {
sharedPreferences.edit().putBoolean(getKey(key), value).apply()
}
// 获取布尔值
fun getBoolean(key: String, defaultValue: Boolean = false): Boolean {
return sharedPreferences.getBoolean(getKey(key), defaultValue)
}
// 保存浮点数
fun putFloat(key: String, value: Float) {
sharedPreferences.edit().putFloat(getKey(key), value).apply()
}
// 获取浮点数
fun getFloat(key: String, defaultValue: Float = 0f): Float {
return sharedPreferences.getFloat(getKey(key), defaultValue)
}
// 删除键值对
fun remove(key: String) {
sharedPreferences.edit().remove(getKey(key)).apply()
}
// 清除所有数据
fun clear() {
sharedPreferences.edit().clear().apply()
}
// 检查键是否存在
fun contains(key: String): Boolean {
return sharedPreferences.contains(getKey(key))
}
// 保存 JSON 对象
fun saveJsonObjectToSp(json: String) {
try {
if (json.isEmpty()) return
val jsonObject = JSONObject(json)
sharedPreferences.edit().apply {
jsonObject.keys().forEachRemaining { key ->
val value = jsonObject.get(key)
val newKey = getKey(key)
when (value) {
is String -> putString(newKey, value)
is Int -> putInt(newKey, value)
is Long -> putLong(newKey, value)
is Boolean -> putBoolean(newKey, value)
else -> putString(newKey, value.toString())
}
}
apply()
}
} catch (_:Exception) { }
}
fun <T> getValue(key: String, default: T): T {
val newKey = getKey(key)
val value = get(newKey, default)
return value
}
private fun getKey(key: String): String = "${MyApplication.appContext.packageName}_$key"
companion object {
@Volatile
private var instance: SpUtils? = null
// 单例模式获取实例
fun getInstance(context: Context = MyApplication.appContext): SpUtils {
return instance ?: synchronized(this) {
instance ?: SpUtils(context).also {
instance = it
}
}
}
}
}
\ No newline at end of file
package com.base.scanqrclear.luma
import android.app.Activity
import android.graphics.Color
import android.view.View
import android.view.WindowManager
import androidx.annotation.ColorInt
import androidx.core.view.WindowCompat
object StatusBarHelps {
fun immersiveStatusBar(activity: Activity, @ColorInt statusBarColor: Int, isDark: Boolean) {
activity.window.apply {
// 透明状态栏
addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS)
this.statusBarColor = statusBarColor
if (isDark) {
decorView.systemUiVisibility =
decorView.systemUiVisibility or View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR
} else{
decorView.systemUiVisibility =
decorView.systemUiVisibility and View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR.inv()
}
}
}
fun setDark(activity: Activity, isDark: Boolean) {
activity.window.apply {
if (isDark) {
decorView.systemUiVisibility =
decorView.systemUiVisibility or View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR
} else {
decorView.systemUiVisibility =
decorView.systemUiVisibility and View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR.inv()
}
}
}
fun immersive(activity: Activity, @ColorInt statusBarColor: Int) {
immersiveStatusBar(activity, statusBarColor, false)
}
fun immersive(activity: Activity) {
// 设置状态栏透明
activity.window.statusBarColor = Color.TRANSPARENT
WindowCompat.setDecorFitsSystemWindows(activity.window, false)
}
}
\ No newline at end of file
package com.base.scanqrclear.luma
import android.content.Context
import java.math.BigDecimal
import java.math.RoundingMode
import java.text.DecimalFormat
import java.text.SimpleDateFormat
import java.util.Calendar
import java.util.Date
import java.util.Locale
object Utils {
fun getFormatSize(bytes: Number): String {
val value: Double
val unit: String
if (bytes.toDouble() < 1024) {
value = bytes.toDouble()
unit = " B"
} else if (bytes.toDouble() < 1024 * 1024) {
value = bytes.toDouble() / 1024
unit = " KB"
} else if (bytes.toDouble() < 1024 * 1024 * 1024) {
value = bytes.toDouble() / (1024 * 1024)
unit = " MB"
} else {
value = bytes.toDouble() / (1024 * 1024 * 1024)
unit = " GB"
}
val formattedNumber = String.format("%.2f", value)
return formattedNumber + unit
}
fun getSize(bytes: Long): String {
if (bytes == 0.toLong()) return "0 B"
val value: Double
val unit: String
if (bytes.toDouble() < 1024) {
value = bytes.toDouble()
unit = " B"
} else if (bytes.toDouble() < 1024 * 1024) {
value = bytes.toDouble() / 1024
unit = " KB"
} else if (bytes.toDouble() < 1024 * 1024 * 1024) {
value = bytes.toDouble() / (1024 * 1024)
unit = " MB"
} else {
value = bytes.toDouble() / (1024 * 1024 * 1024)
unit = " GB"
}
val formattedNumber = String.format("%.1f", value)
return formattedNumber + unit
}
fun getSizeArray(bytes: Long): Array<String> {
val value = getSize(bytes)
val size = value.substring(0, value.indexOf(" "))
val unit = value.substring(value.indexOf(" ") + 1)
return arrayOf(size, unit)
}
fun Long.toDateString(): String {
val formatter = SimpleDateFormat("yyyy-MM-dd", Locale.getDefault())
return formatter.format(Date(this))
}
fun Context.toDate(): String {
val formatter = SimpleDateFormat("yyyy-MM-dd", Locale.getDefault())
return formatter.format(Date())
}
fun getCurrentMinute(): Int {
val calendar = Calendar.getInstance()
return calendar.get(Calendar.MINUTE)
}
fun calculate(dividend: Int, divisor: Int): String {
val bigDecimalDividend = BigDecimal(dividend)
val bigDecimalDivisor = BigDecimal(divisor)
// 执行除法操作,并保留一位小数,不四舍五入
val result = bigDecimalDividend.divide(bigDecimalDivisor, 1, RoundingMode.DOWN)
// 将结果转换回Double
return result.toString()
}
}
\ No newline at end of file
package com.base.scanqrclear.utils
import com.base.scanqrclear.R
import com.tool.luma.smart.cleaner.R
enum class ExitType(val title: Int, val content: Int) {
APP_MANAGER(R.string.exit_app_manager, R.string.exit_app_manager_content),
BATTERY_INFO(R.string.exit_battery_info, R.string.exit_battery_info_content),
LARGE_FILE(R.string.exit_large_file_clean, R.string.exit_large_file_clean_content),
PHOTO_COMPRESSION(R.string.exit_photo_compression, R.string.exit_photo_compression_content),
SCREENSHOT(R.string.exit_screenshot_cleaner, R.string.exit_screenshot_cleaner_content),
SIMILAR_PHOTOS(R.string.exit_similar_photos, R.string.exit_similar_photos_content),
WHATSAPP(R.string.exit_whatsapp_clean, R.string.exit_whatsapp_clean_content),
CLEAN(R.string.exit_clean_junk, R.string.exit_clean_junk_content),
}
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="@color/color_f0f0f0" />
<corners android:radius="16dp" />
</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>
<solid android:color="@color/color_ffcda0" />
<corners android:radius="8dp" />
</shape>
</item>
<item android:id="@android:id/progress">
<scale android:scaleWidth="100%">
<shape>
<solid android:color="@color/white" />
<corners android:radius="8dp" />
</shape>
</scale>
</item>
</layer-list>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<corners android:radius="4dp" />
<solid android:color="@color/white" />
<stroke android:width="1dp" android:color="@color/color_aeb4bd" />
</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>
<solid android:color="@color/colorPrimary" />
</shape>
</item>
<item android:id="@android:id/progress">
<scale android:scaleWidth="100%">
<shape>
<solid android:color="@color/white" />
</shape>
</scale>
</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"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/white_background">
<com.airbnb.lottie.LottieAnimationView
android:id="@+id/lottie_face"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="24dp"
app:lottie_fileName="top_rate_nor.json"
app:lottie_loop="true"
app:lottie_autoPlay="true"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent" />
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/iv_close"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:layout_marginEnd="16dp"
android:src="@mipmap/icon_close_pingfen"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintEnd_toEndOf="parent" />
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/tv_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:layout_marginHorizontal="16dp"
android:text="@string/score_title"
android:textAlignment="center"
android:textColor="@color/color_181b1f"
android:textSize="16sp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@id/lottie_face" />
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/tv_content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="4dp"
android:layout_marginHorizontal="16dp"
android:text="@string/score_content"
android:textAlignment="center"
android:textColor="@color/color_4f4f4f"
android:textSize="12sp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@id/tv_title" />
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/cl_star"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@id/tv_content">
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/ll_star"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent">
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/iv_star1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="24dp"
android:layout_marginEnd="6dp"
android:src="@mipmap/star_n"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintEnd_toStartOf="@id/iv_star2" />
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/iv_star2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="24dp"
android:layout_marginEnd="6dp"
android:src="@mipmap/star_n"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintEnd_toStartOf="@id/iv_star3" />
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/iv_star3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="24dp"
android:src="@mipmap/star_n"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent" />
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/iv_star4"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="24dp"
android:layout_marginStart="6dp"
android:src="@mipmap/star_n"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toEndOf="@id/iv_star3" />
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/iv_star5"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="24dp"
android:layout_marginStart="6dp"
android:src="@mipmap/star_n"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toEndOf="@id/iv_star4" />
<androidx.appcompat.widget.LinearLayoutCompat
android:id="@+id/ll_star_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:layout_marginEnd="10dp"
app:layout_constraintEnd_toEndOf="@id/iv_star5"
app:layout_constraintTop_toBottomOf="@id/iv_star5">
<androidx.appcompat.widget.AppCompatTextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="4dp"
android:text="@string/the_best_we_can_get"
android:textColor="@color/colorPrimary"
android:textSize="14sp" />
<androidx.appcompat.widget.AppCompatImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="6dp"
android:src="@mipmap/icon_zhixiang" />
</androidx.appcompat.widget.LinearLayoutCompat>
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/tv_submit"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:layout_marginBottom="48dp"
android:layout_marginHorizontal="28dp"
android:paddingTop="10dp"
android:paddingBottom="10dp"
android:text="@string/confirm"
android:textAllCaps="true"
android:textColor="@color/white"
android:textSize="16sp"
android:gravity="center"
android:background="@drawable/gradient"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toBottomOf="@id/cl_star" />
</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<gradient
android:startColor="@color/color_ff6400"
android:endColor="@color/color_ff8200"
android:angle="0"
android:centerX="0.5"
android:centerY="0.5" />
<corners android:radius="50dp" />
</shape>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<gradient
android:startColor="@color/color_ff6400"
android:endColor="@color/color_ff8200"
android:angle="0"
android:centerX="0.5"
android:centerY="0.5" />
<corners android:radius="37dp" />
</shape>
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<gradient
android:startColor="@color/color_ff6400"
android:endColor="@color/color_ff8200"
android:angle="0"
android:centerX="0.5"
android:centerY="0.5" />
<corners android:radius="37dp" />
</shape>
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<gradient
android:startColor="@color/color_ff6400"
android:endColor="@color/color_ff8200"
android:angle="0"
android:centerX="0.5"
android:centerY="0.5" />
<corners android:radius="4dp" />
</shape>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<gradient
android:startColor="@color/color_ffb764"
android:endColor="@color/color_ffae52"
android:angle="270"
android:centerX="0.5"
android:centerY="0.5" />
</shape>
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<gradient
android:startColor="@color/color_ffb462"
android:endColor="@color/color_ffc889"
android:angle="0"
android:centerX="0.5"
android:centerY="0.5" />
<corners android:radius="50dp" />
</shape>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<gradient
android:startColor="@color/color_ffb462"
android:endColor="@color/color_ffc889"
android:angle="0"
android:centerX="0.5"
android:centerY="0.5" />
<corners android:radius="4dp" />
</shape>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<gradient
android:startColor="@color/color_ffe14a"
android:endColor="@color/color_ffcd58"
android:angle="0"
android:centerX="0.5"
android:centerY="0.5" />
<corners android:radius="50dp" />
</shape>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<gradient
android:startColor="@color/color_ffe8ce"
android:endColor="@color/color_f8f8fb"
android:angle="270"
android:centerX="0.5"
android:centerY="0.5" />
</shape>
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<corners android:radius="37dp" />
<solid android:color="@android:color/transparent" />
<stroke android:width="1dp" android:color="@color/color_ebebeb" />
</shape>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="28dp"
android:height="28dp"
android:viewportWidth="28"
android:viewportHeight="28">
<!-- <path-->
<!-- android:pathData="M3.281,0L24.719,0A3.281,3.281 0,0 1,28 3.281L28,24.719A3.281,3.281 0,0 1,24.719 28L3.281,28A3.281,3.281 0,0 1,0 24.719L0,3.281A3.281,3.281 0,0 1,3.281 0z"-->
<!-- android:fillColor="#A0A0A0"/>-->
<path
android:pathData="M24.502,19.893C24.604,19.694 24.888,19.694 24.99,19.893L25.699,21.287C25.725,21.337 25.766,21.379 25.817,21.405L27.204,22.125C27.401,22.226 27.401,22.508 27.204,22.61L25.817,23.329C25.766,23.355 25.725,23.397 25.699,23.448L24.99,24.842C24.888,25.041 24.604,25.041 24.502,24.842L23.793,23.448C23.767,23.397 23.726,23.355 23.675,23.329L22.288,22.61C22.092,22.508 22.092,22.226 22.288,22.125L23.675,21.405C23.726,21.379 23.767,21.337 23.793,21.287L24.502,19.893Z"
android:fillColor="#E2E2E2"/>
<path
android:pathData="M8.704,1.59C8.719,1.341 9.032,1.241 9.189,1.435L10.106,2.564C10.154,2.623 10.224,2.659 10.3,2.665L11.749,2.765C11.995,2.782 12.095,3.092 11.904,3.249L10.782,4.172C10.723,4.22 10.687,4.291 10.683,4.366L10.591,5.818C10.576,6.067 10.263,6.167 10.106,5.974L9.189,4.844C9.141,4.785 9.071,4.749 8.995,4.744L7.546,4.643C7.299,4.626 7.2,4.316 7.391,4.159L8.513,3.236C8.572,3.188 8.607,3.118 8.612,3.042L8.704,1.59Z"
android:fillColor="#E2E2E2"/>
<path
android:pathData="M3.28,8.232C3.292,8.099 3.449,8.035 3.55,8.122L3.982,8.494C4.008,8.517 4.04,8.53 4.075,8.534L4.642,8.584C4.775,8.596 4.839,8.754 4.752,8.855L4.38,9.287C4.358,9.313 4.344,9.345 4.341,9.379L4.29,9.947C4.278,10.08 4.121,10.144 4.02,10.057L3.588,9.685C3.562,9.662 3.529,9.649 3.495,9.646L2.927,9.595C2.794,9.583 2.731,9.425 2.818,9.324L3.19,8.892C3.212,8.866 3.226,8.834 3.229,8.8L3.28,8.232Z"
android:fillColor="#E2E2E2"/>
<group>
<clip-path
android:pathData="M5,5h15.85v18.9h-15.85z"/>
<path
android:pathData="M8.66,13.11C8.66,13.11 5.31,19.56 5.04,19.41C4.77,19.26 6.1,20.5 8.91,21.38L9.99,21.55L9.91,21.83C9.91,21.83 13.45,23.26 14.92,23.19L14.99,22.95C14.99,22.95 16.22,23.99 20.04,23.9C20.04,23.9 19.7,17.98 20.27,16.48L8.66,13.12V13.11Z"
android:fillColor="#DDDDDD"/>
<path
android:pathData="M17.535,5.196L17.016,5.046C16.358,4.855 15.671,5.234 15.48,5.892L14.035,10.877C13.845,11.535 14.224,12.222 14.881,12.413L15.4,12.563C16.058,12.754 16.746,12.375 16.936,11.717L18.381,6.732C18.571,6.075 18.192,5.387 17.535,5.196Z"
android:fillColor="#DDDDDD"/>
<path
android:pathData="M20.85,14.48C21.02,13.9 18.56,12.67 15.35,11.74C12.14,10.81 9.41,10.53 9.24,11.11C9.24,11.12 9.24,11.14 9.24,11.15L8.67,13.1L20.28,16.46L20.85,14.5C20.85,14.5 20.86,14.47 20.87,14.46L20.85,14.48Z"
android:fillColor="#BCBCBC"/>
<path
android:pathData="M16.385,10.348L15.232,10.014C14.707,9.862 14.158,10.165 14.006,10.69L13.889,11.093C13.737,11.618 14.039,12.167 14.564,12.319L15.717,12.653C16.242,12.806 16.791,12.503 16.943,11.978L17.06,11.575C17.212,11.05 16.91,10.5 16.385,10.348Z"
android:fillColor="#BCBCBC"/>
<path
android:pathData="M9.98,21.55L10.99,18.05C10.99,18.05 9.57,21.55 8.9,21.38C8.23,21.21 9.58,21.55 9.98,21.55Z"
android:fillColor="#BFBFBF"/>
<path
android:pathData="M14.98,22.95L16.36,18.18C16.36,18.18 15.24,23.06 15.74,23.32C15.65,23.28 15.09,23.06 14.97,22.95H14.98Z"
android:fillColor="#BFBFBF"/>
</group>
</vector>
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<corners android:radius="8dp" />
<stroke android:width="1dp" android:color="@color/colorPrimary" />
</shape>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="@color/color_66000000" />
<corners
android:bottomLeftRadius="8dp"
android:bottomRightRadius="8dp" />
</shape>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="@color/color_66000000" />
<corners android:radius="8dp" />
</shape>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="@color/color_f8fbff" />
<corners android:radius="8dp" />
</shape>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<gradient
android:startColor="@color/color_2b91f1"
android:endColor="@color/color_b5e0ffc"
android:angle="270"
android:centerX="0.5"
android:centerY="0.5" />
<corners android:radius="50dp" />
</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>
<solid android:color="@color/color_e6e6e6" />
<corners android:radius="5dp" />
</shape>
</item>
<item android:id="@android:id/progress">
<clip>
<shape>
<solid android:color="@color/color_2b91f1" />
<corners android:radius="5dp" />
</shape>
</clip>
</item>
</layer-list>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@mipmap/tob_home" android:state_checked="true"/>
<item android:drawable="@mipmap/tob_home_n" />
</selector>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@mipmap/tob_tools_s" android:state_checked="true"/>
<item android:drawable="@mipmap/tob_tools_n" />
</selector>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="@color/white" />
<corners android:radius="12dp" />
</shape>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="@color/white" />
<corners android:radius="16dp" />
</shape>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="@color/white" />
<corners android:radius="8dp" />
</shape>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="@color/white" />
<corners
android:bottomLeftRadius="15dp"
android:bottomRightRadius="15dp" />
</shape>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="@color/white" />
<corners
android:topLeftRadius="15dp"
android:topRightRadius="15dp" />
</shape>
\ 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"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/color_f7fafa">
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/cl_top"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/gradient_background"
app:layout_constraintTop_toTopOf="parent">
<androidx.appcompat.widget.LinearLayoutCompat
android:id="@+id/ll_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingTop="6dp"
android:paddingBottom="6dp"
android:gravity="center_vertical"
android:orientation="horizontal"
app:layout_constraintTop_toTopOf="parent">
<FrameLayout
android:id="@+id/fl_back"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="11dp"
android:padding="4dp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent">
<androidx.appcompat.widget.AppCompatImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@mipmap/icon_return_bar_nor" />
</FrameLayout>
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/tv_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/app_name"
android:textColor="@color/white"
android:textSize="20sp"
android:textStyle="bold"
app:layout_constraintTop_toTopOf="@id/fl_back"
app:layout_constraintBottom_toBottomOf="@id/fl_back"
app:layout_constraintStart_toEndOf="@id/fl_back" />
</androidx.appcompat.widget.LinearLayoutCompat>
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/tv_cleaned_up"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="84dp"
android:text="@string/cleaned_up"
android:textColor="@color/white"
android:textSize="20sp"
android:textStyle="bold"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@id/ll_title" />
<androidx.appcompat.widget.AppCompatTextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="2dp"
android:layout_marginBottom="46dp"
android:text="@string/cleaned_up_content"
android:textColor="@color/white"
android:textSize="14sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@id/tv_cleaned_up" />
</androidx.constraintlayout.widget.ConstraintLayout>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rv_result"
android:layout_width="match_parent"
android:layout_height="0dp"
android:paddingTop="12dp"
android:paddingBottom="12dp"
android:scrollbars="none"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toBottomOf="@id/cl_top" />
<com.airbnb.lottie.LottieAnimationView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
app:lottie_fileName="ok.json"
app:lottie_autoPlay="true"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
\ 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"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/color_f7fafa">
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/cl_top"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/gradient_background"
app:layout_constraintTop_toTopOf="parent">
<androidx.appcompat.widget.LinearLayoutCompat
android:id="@+id/ll_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingTop="6dp"
android:paddingBottom="6dp"
android:gravity="center_vertical"
android:orientation="horizontal"
app:layout_constraintTop_toTopOf="parent">
<FrameLayout
android:id="@+id/fl_back"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="11dp"
android:padding="4dp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent">
<androidx.appcompat.widget.AppCompatImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@mipmap/icon_return_bar_nor" />
</FrameLayout>
<androidx.appcompat.widget.AppCompatTextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/whatsapp_clean"
android:textColor="@color/white"
android:textSize="20sp"
android:textStyle="bold"
app:layout_constraintTop_toTopOf="@id/fl_back"
app:layout_constraintBottom_toBottomOf="@id/fl_back"
app:layout_constraintStart_toEndOf="@id/fl_back" />
</androidx.appcompat.widget.LinearLayoutCompat>
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/cl_size"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="34dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@id/ll_title">
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/tv_size"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="0"
android:textColor="@color/white"
android:textSize="32sp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent" />
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/tv_unit"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="B"
android:textColor="@color/white"
android:textSize="12sp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toEndOf="@id/tv_size" />
</androidx.constraintlayout.widget.ConstraintLayout>
<androidx.appcompat.widget.AppCompatTextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="4dp"
android:layout_marginBottom="67dp"
android:text="@string/cleanable"
android:textColor="@color/white"
android:textSize="16sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@id/cl_size" />
</androidx.constraintlayout.widget.ConstraintLayout>
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/cl_video_message"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="24dp"
android:layout_marginStart="15dp"
android:layout_marginEnd="15dp"
android:paddingTop="12dp"
android:paddingBottom="12dp"
android:paddingStart="16dp"
android:paddingEnd="16dp"
android:gravity="center"
android:background="@drawable/white_background"
android:orientation="vertical"
app:layout_constraintTop_toBottomOf="@id/cl_top">
<androidx.appcompat.widget.LinearLayoutCompat
android:id="@+id/ll_video"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:orientation="horizontal"
app:layout_constraintTop_toTopOf="parent">
<androidx.appcompat.widget.AppCompatImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@mipmap/icon_video_junk_nor" />
<androidx.appcompat.widget.LinearLayoutCompat
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:layout_marginStart="8dp"
android:layout_marginEnd="8dp"
android:orientation="vertical">
<androidx.appcompat.widget.AppCompatTextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/video_messages"
android:textColor="@color/color_181b1f"
android:textSize="16sp"
android:textStyle="bold" />
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/tv_no_video"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="2dp"
android:text="@string/no_video_message_found"
android:textColor="@color/color_aeb4bd"
android:textSize="12sp" />
</androidx.appcompat.widget.LinearLayoutCompat>
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/tv_video_size"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="4dp"
android:text="0KB"
android:textColor="@color/color_aeb4bd"
android:textSize="12sp" />
<androidx.appcompat.widget.AppCompatImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@mipmap/icon_jiantou" />
</androidx.appcompat.widget.LinearLayoutCompat>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rv_video"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="48dp"
android:scrollbars="none"
app:layout_constraintTop_toBottomOf="@id/ll_video" />
<View
android:id="@+id/view_video"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintTop_toTopOf="@id/rv_video"
app:layout_constraintBottom_toBottomOf="@id/rv_video"
app:layout_constraintStart_toStartOf="@id/rv_video"
app:layout_constraintEnd_toEndOf="@id/rv_video" />
</androidx.constraintlayout.widget.ConstraintLayout>
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/cl_image_message"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:layout_marginStart="15dp"
android:layout_marginEnd="15dp"
android:paddingTop="12dp"
android:paddingBottom="12dp"
android:paddingStart="16dp"
android:paddingEnd="16dp"
android:gravity="center"
android:background="@drawable/white_background"
android:orientation="vertical"
app:layout_constraintTop_toBottomOf="@id/cl_video_message">
<androidx.appcompat.widget.LinearLayoutCompat
android:id="@+id/ll_image"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:orientation="horizontal"
app:layout_constraintTop_toTopOf="parent">
<androidx.appcompat.widget.AppCompatImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@mipmap/icon_image_junk_nor" />
<androidx.appcompat.widget.LinearLayoutCompat
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:layout_marginStart="8dp"
android:layout_marginEnd="8dp"
android:orientation="vertical">
<androidx.appcompat.widget.AppCompatTextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/image_messages"
android:textColor="@color/color_181b1f"
android:textSize="16sp"
android:textStyle="bold" />
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/tv_no_image"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="2dp"
android:text="@string/no_image_message_found"
android:textColor="@color/color_aeb4bd"
android:textSize="12sp" />
</androidx.appcompat.widget.LinearLayoutCompat>
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/tv_image_size"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="4dp"
android:text="0KB"
android:textColor="@color/color_aeb4bd"
android:textSize="12sp" />
<androidx.appcompat.widget.AppCompatImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@mipmap/icon_jiantou" />
</androidx.appcompat.widget.LinearLayoutCompat>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rv_image"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="48dp"
android:scrollbars="none"
app:layout_constraintTop_toBottomOf="@id/ll_image" />
<View
android:id="@+id/view_image"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintTop_toTopOf="@id/rv_image"
app:layout_constraintBottom_toBottomOf="@id/rv_image"
app:layout_constraintStart_toStartOf="@id/rv_image"
app:layout_constraintEnd_toEndOf="@id/rv_image" />
</androidx.constraintlayout.widget.ConstraintLayout>
<androidx.appcompat.widget.LinearLayoutCompat
android:id="@+id/ll_audio_message"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:layout_marginStart="15dp"
android:layout_marginEnd="15dp"
android:paddingTop="12dp"
android:paddingBottom="12dp"
android:paddingStart="16dp"
android:paddingEnd="16dp"
android:gravity="center"
android:background="@drawable/white_background"
android:orientation="horizontal"
app:layout_constraintTop_toBottomOf="@id/cl_image_message">
<androidx.appcompat.widget.AppCompatImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@mipmap/icon_audio_junk_nor" />
<androidx.appcompat.widget.LinearLayoutCompat
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:layout_marginStart="8dp"
android:layout_marginEnd="8dp"
android:orientation="vertical">
<androidx.appcompat.widget.AppCompatTextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/audio_messages"
android:textColor="@color/color_181b1f"
android:textSize="16sp"
android:textStyle="bold" />
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/tv_no_audio"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="2dp"
android:text="@string/no_audio_message_found"
android:textColor="@color/color_aeb4bd"
android:textSize="12sp" />
</androidx.appcompat.widget.LinearLayoutCompat>
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/tv_audio_size"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="4dp"
android:text="0KB"
android:textColor="@color/color_aeb4bd"
android:textSize="12sp" />
<androidx.appcompat.widget.AppCompatImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@mipmap/icon_jiantou" />
</androidx.appcompat.widget.LinearLayoutCompat>
<include
android:id="@+id/animation"
android:visibility="gone"
layout="@layout/custom_animation" />
</androidx.constraintlayout.widget.ConstraintLayout>
\ 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"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/gradient_background">
<androidx.constraintlayout.widget.Guideline
android:id="@+id/gl_line"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintGuide_percent="0" />
<com.airbnb.lottie.LottieAnimationView
android:id="@+id/lottie_animation"
android:layout_width="wrap_content"
android:layout_height="290dp"
android:layout_marginTop="50dp"
android:adjustViewBounds="true"
app:lottie_loop="true"
app:lottie_autoPlay="true"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@id/gl_line" />
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/tv_progress"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingVertical="2dp"
android:gravity="center"
android:text="0%"
android:textAlignment="center"
android:textColor="@color/white"
android:textSize="30sp"
android:textStyle="bold"
app:layout_constraintTop_toBottomOf="@id/lottie_animation" />
<androidx.appcompat.widget.AppCompatTextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:text="@string/please_wait"
android:textAlignment="center"
android:textColor="@color/white"
android:textSize="14sp"
app:layout_constraintTop_toBottomOf="@id/tv_progress" />
</androidx.constraintlayout.widget.ConstraintLayout>
\ 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"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/white_background">
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/tv_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="24dp"
android:layout_marginStart="24dp"
android:layout_marginEnd="24dp"
android:includeFontPadding="false"
android:lineSpacingExtra="4dp"
android:text="@string/delete_title"
android:textAlignment="center"
android:textColor="@color/color_181b1f"
android:textSize="18sp"
android:textStyle="bold"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/tv_content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="4dp"
android:layout_marginStart="24dp"
android:layout_marginEnd="24dp"
android:lineSpacingExtra="4dp"
android:includeFontPadding="false"
android:text="@string/delete_content"
android:textAlignment="center"
android:textColor="@color/color_666666"
android:textSize="16sp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@id/tv_title" />
<androidx.appcompat.widget.LinearLayoutCompat
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:layout_marginBottom="24dp"
android:paddingStart="32dp"
android:paddingEnd="32dp"
android:orientation="horizontal"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toBottomOf="@id/tv_content">
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/tv_cancel"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:layout_marginEnd="8dp"
android:paddingVertical="8dp"
android:gravity="center"
android:background="@drawable/button_border_background"
android:text="@string/cancel"
android:textColor="@color/color_aeb4bd"
android:textSize="14sp" />
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/tv_delete"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:layout_marginStart="8dp"
android:paddingVertical="8dp"
android:gravity="center"
android:background="@drawable/gradient_4"
android:text="@string/delete"
android:textColor="@color/white"
android:textSize="14sp" />
</androidx.appcompat.widget.LinearLayoutCompat>
</androidx.constraintlayout.widget.ConstraintLayout>
\ 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"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/cl_exit"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/white_background_16"
android:paddingBottom="30dp"
app:layout_constraintTop_toTopOf="parent">
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/iv_cancel"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:layout_marginEnd="20dp"
android:src="@mipmap/icon_close_pingfen"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/tv_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="48dp"
android:layout_marginTop="30dp"
android:layout_marginEnd="48dp"
android:includeFontPadding="false"
android:lineSpacingExtra="4dp"
android:text="@string/app_name"
android:textAlignment="center"
android:textColor="@color/color_181b1f"
android:textSize="20sp"
android:textStyle="bold"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/tv_content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="24dp"
android:layout_marginTop="6dp"
android:layout_marginEnd="24dp"
android:includeFontPadding="false"
android:lineSpacingExtra="4dp"
android:text="@string/storage_permission_content"
android:textAlignment="center"
android:textColor="@color/color_666666"
android:textSize="16sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/tv_title" />
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/tv_cancel"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="32dp"
android:layout_marginTop="32dp"
android:layout_marginEnd="8dp"
android:background="@drawable/gradient_not_clickable"
android:paddingTop="10dp"
android:paddingBottom="10dp"
android:text="@string/cancel"
android:textAlignment="center"
android:textColor="@color/white"
android:textSize="18sp"
android:textStyle="bold"
app:layout_constraintEnd_toStartOf="@id/tv_sure"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/tv_content" />
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/tv_sure"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginTop="32dp"
android:layout_marginEnd="32dp"
android:background="@drawable/gradient"
android:paddingTop="10dp"
android:paddingBottom="10dp"
android:text="@string/sure"
android:textAlignment="center"
android:textColor="@color/white"
android:textSize="18sp"
android:textStyle="bold"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/tv_cancel"
app:layout_constraintTop_toBottomOf="@id/tv_content" />
</androidx.constraintlayout.widget.ConstraintLayout>
<com.base.scanqrclear.luma.NativeView
android:id="@+id/ad_native"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:background="@drawable/white_background_16"
android:padding="11dp"
android:visibility="gone"
app:layout_constraintTop_toBottomOf="@id/cl_exit" />
</androidx.constraintlayout.widget.ConstraintLayout>
\ 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"
android:layout_width="match_parent"
android:layout_height="wrap_content"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:background="@drawable/white_background">
<com.airbnb.lottie.LottieAnimationView
android:id="@+id/lottie_loading"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="4dp"
app:lottie_fileName="ad_loading.json"
app:lottie_loop="true"
app:lottie_autoPlay="true"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent" />
<androidx.appcompat.widget.AppCompatTextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:layout_marginBottom="24dp"
android:text="@string/loading"
android:textAlignment="center"
android:textColor="@color/color_181b1f"
android:textSize="16sp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toBottomOf="@id/lottie_loading" />
</androidx.constraintlayout.widget.ConstraintLayout>
\ 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"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/cl_top"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@mipmap/img_bj_pop"
android:paddingBottom="30dp"
app:layout_constraintTop_toTopOf="parent">
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/iv_logo"
android:layout_width="66dp"
android:layout_height="66dp"
android:layout_marginTop="30dp"
android:src="@mipmap/logo"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/iv_cancel"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:layout_marginEnd="20dp"
android:src="@mipmap/icon_close_pop"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/tv_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="37dp"
android:layout_marginTop="12dp"
android:layout_marginEnd="37dp"
android:text="@string/please_wait_a_moment"
android:textAlignment="center"
android:textColor="@color/color_181b1f"
android:textSize="22sp"
android:textStyle="bold"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/iv_logo" />
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/tv_content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="37dp"
android:layout_marginTop="6dp"
android:layout_marginEnd="37dp"
android:text="@string/logout_content"
android:textAlignment="center"
android:textColor="@color/color_666666"
android:textSize="18sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/tv_title" />
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/tv_exit"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="32dp"
android:layout_marginTop="32dp"
android:layout_marginEnd="4dp"
android:background="@drawable/gradient_not_clickable"
android:paddingTop="10dp"
android:paddingBottom="10dp"
android:text="@string/exit"
android:textAlignment="center"
android:textColor="@color/white"
android:textSize="18sp"
android:textStyle="bold"
app:layout_constraintEnd_toStartOf="@id/tv_cancel"
app:layout_constraintHorizontal_weight="1"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/tv_content" />
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/tv_cancel"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="4dp"
android:layout_marginTop="32dp"
android:layout_marginEnd="32dp"
android:background="@drawable/gradient"
android:paddingTop="10dp"
android:paddingBottom="10dp"
android:text="@string/clean_now"
android:textAlignment="center"
android:textColor="@color/white"
android:textSize="18sp"
android:textStyle="bold"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_weight="1.4"
app:layout_constraintStart_toEndOf="@id/tv_exit"
app:layout_constraintTop_toBottomOf="@id/tv_content" />
</androidx.constraintlayout.widget.ConstraintLayout>
<com.base.scanqrclear.luma.NativeView
android:id="@+id/ad_native"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:background="@drawable/white_background_16"
android:padding="11dp"
android:visibility="gone"
app:layout_constraintTop_toBottomOf="@id/cl_top" />
</androidx.constraintlayout.widget.ConstraintLayout>
\ 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"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@mipmap/img_tongzhi_pop_bj">
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/iv_close"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="24dp"
android:layout_marginEnd="20dp"
android:src="@mipmap/icon_close_pop"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintEnd_toEndOf="parent" />
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/tv_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="168dp"
android:text="@string/notification_title"
android:textAlignment="center"
android:textColor="@color/color_181b1f"
android:textSize="22sp"
android:textStyle="bold"
app:layout_constraintTop_toTopOf="parent" />
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/tv_content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="12dp"
android:layout_marginStart="42dp"
android:layout_marginEnd="42dp"
android:text="@string/notification_content"
android:textAlignment="center"
android:textColor="@color/color_666666"
android:textSize="16sp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/tv_title" />
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/tv_turn_on"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="24dp"
android:layout_marginBottom="44dp"
android:layout_marginStart="58dp"
android:layout_marginEnd="58dp"
android:paddingTop="10dp"
android:paddingBottom="10dp"
android:text="@string/turn_on"
android:textColor="@color/white"
android:textSize="20sp"
android:textStyle="bold"
android:gravity="center"
android:background="@drawable/gradient"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toBottomOf="@id/tv_content" />
</androidx.constraintlayout.widget.ConstraintLayout>
\ 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"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingTop="24dp"
android:paddingBottom="40dp"
android:background="@drawable/white_background_top">
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/tv_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="24dp"
android:layout_marginEnd="24dp"
android:text="@string/storage_permission_title"
android:textColor="@color/black"
android:textSize="18sp"
android:textStyle="bold"
app:layout_constraintTop_toTopOf="parent" />
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/tv_content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:layout_marginStart="24dp"
android:layout_marginEnd="24dp"
android:text="@string/storage_permission_content"
android:textColor="@color/color_666666"
android:textSize="16sp"
app:layout_constraintTop_toBottomOf="@id/tv_title" />
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/iv_permission"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="17dp"
android:layout_marginHorizontal="15dp"
android:adjustViewBounds="true"
android:src="@mipmap/img_quanxian_pop"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@id/tv_content" />
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/tv_open_settings"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="30dp"
android:layout_marginStart="30dp"
android:layout_marginEnd="30dp"
android:paddingTop="12dp"
android:paddingBottom="12dp"
android:text="@string/open_settings"
android:textColor="@color/white"
android:textSize="18sp"
android:textStyle="bold"
android:gravity="center"
android:background="@drawable/gradient"
app:layout_constraintTop_toBottomOf="@id/iv_permission" />
</androidx.constraintlayout.widget.ConstraintLayout>
\ 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"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/white_background">
<com.airbnb.lottie.LottieAnimationView
android:id="@+id/lottie_face"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="24dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:lottie_autoPlay="true"
app:lottie_fileName="top_rate_nor.json"
app:lottie_loop="true" />
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/iv_close"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:layout_marginEnd="16dp"
android:src="@mipmap/icon_close_pingfen"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/tv_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="16dp"
android:layout_marginTop="8dp"
android:text="@string/score_title"
android:textAlignment="center"
android:textColor="@color/color_181b1f"
android:textSize="16sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/lottie_face" />
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/tv_content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="16dp"
android:layout_marginTop="4dp"
android:text="@string/score_content"
android:textAlignment="center"
android:textColor="@color/color_4f4f4f"
android:textSize="12sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/tv_title" />
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/cl_star"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/tv_content">
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/ll_star"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/iv_star1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="24dp"
android:layout_marginEnd="6dp"
android:src="@mipmap/star_n"
app:layout_constraintEnd_toStartOf="@id/iv_star2"
app:layout_constraintTop_toTopOf="parent" />
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/iv_star2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="24dp"
android:layout_marginEnd="6dp"
android:src="@mipmap/star_n"
app:layout_constraintEnd_toStartOf="@id/iv_star3"
app:layout_constraintTop_toTopOf="parent" />
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/iv_star3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="24dp"
android:src="@mipmap/star_n"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/iv_star4"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="6dp"
android:layout_marginTop="24dp"
android:src="@mipmap/star_n"
app:layout_constraintStart_toEndOf="@id/iv_star3"
app:layout_constraintTop_toTopOf="parent" />
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/iv_star5"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="6dp"
android:layout_marginTop="24dp"
android:src="@mipmap/star_n"
app:layout_constraintStart_toEndOf="@id/iv_star4"
app:layout_constraintTop_toTopOf="parent" />
<androidx.appcompat.widget.LinearLayoutCompat
android:id="@+id/ll_star_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:layout_marginEnd="10dp"
app:layout_constraintEnd_toEndOf="@id/iv_star5"
app:layout_constraintTop_toBottomOf="@id/iv_star5">
<androidx.appcompat.widget.AppCompatTextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="4dp"
android:text="@string/the_best_we_can_get"
android:textColor="@color/colorPrimary"
android:textSize="14sp" />
<androidx.appcompat.widget.AppCompatImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="6dp"
android:src="@mipmap/icon_zhixiang" />
</androidx.appcompat.widget.LinearLayoutCompat>
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/tv_submit"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginHorizontal="28dp"
android:layout_marginTop="16dp"
android:layout_marginBottom="48dp"
android:background="@drawable/gradient"
android:gravity="center"
android:paddingTop="10dp"
android:paddingBottom="10dp"
android:text="@string/confirm"
android:textAllCaps="true"
android:textColor="@color/white"
android:textSize="16sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/cl_star" />
</androidx.constraintlayout.widget.ConstraintLayout>
\ 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"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="12dp"
android:layout_marginStart="15dp"
android:layout_marginEnd="15dp"
android:padding="16dp"
android:background="@drawable/white_background">
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/iv_image"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@mipmap/icon_clean_home"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent" />
<androidx.appcompat.widget.LinearLayoutCompat
android:id="@+id/ll_content"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="5dp"
android:orientation="vertical"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/iv_image">
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/tv_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/clean_junk"
android:textColor="@color/color_181b1f"
android:textSize="16sp"
android:textStyle="bold" />
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/tv_content"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/result_clean_junk"
android:textColor="@color/color_666666"
android:textSize="16sp" />
</androidx.appcompat.widget.LinearLayoutCompat>
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/tv_clean_now"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:paddingTop="8dp"
android:paddingBottom="8dp"
android:gravity="center"
android:background="@drawable/gradient_4"
android:text="@string/clean_now"
android:textColor="@color/white"
android:textSize="14sp"
android:textStyle="bold"
app:layout_constraintTop_toBottomOf="@id/ll_content" />
</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<com.google.android.gms.ads.nativead.NativeAdView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/ad_native_background">
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/ad_small_icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="4dp"
android:layout_marginStart="6dp"
android:src="@mipmap/icon_ad_pop"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent" />
<com.google.android.gms.ads.nativead.MediaView
android:id="@+id/ad_media"
android:layout_width="0dp"
android:layout_height="130dp"
android:layout_marginStart="16dp"
android:layout_marginEnd="42dp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/ad_small_icon" />
<ImageView
android:id="@+id/ad_icon"
android:layout_width="33dp"
android:layout_height="33dp"
android:layout_marginStart="14dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@id/ad_call_to_action"
app:layout_constraintBottom_toBottomOf="@id/ad_call_to_action" />
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/ad_headline"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_marginStart="12dp"
android:layout_marginEnd="12dp"
android:ellipsize="end"
android:maxLines="2"
android:text="@string/app_name"
android:textColor="@color/black"
android:textSize="16sp"
app:layout_constraintTop_toTopOf="@id/ad_call_to_action"
app:layout_constraintBottom_toBottomOf="@id/ad_call_to_action"
app:layout_constraintStart_toEndOf="@id/ad_icon"
app:layout_constraintEnd_toStartOf="@id/ad_call_to_action" />
<Button
android:id="@+id/ad_call_to_action"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:layout_marginBottom="10dp"
android:layout_marginEnd="6dp"
android:paddingStart="20dp"
android:paddingEnd="20dp"
android:text="@string/open"
android:textColor="@color/white"
android:textSize="15sp"
android:textStyle="bold"
android:background="@drawable/gradient_26"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toEndOf="@id/ad_headline"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@id/ad_media" />
</androidx.constraintlayout.widget.ConstraintLayout>
</com.google.android.gms.ads.nativead.NativeAdView>
<?xml version="1.0" encoding="utf-8"?>
<com.google.android.gms.ads.nativead.NativeAdView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="8dp"
android:background="@drawable/ad_native_background">
<com.google.android.gms.ads.nativead.MediaView
android:id="@+id/ad_media"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginEnd="8dp"
app:layout_constraintHorizontal_weight="1"
app:layout_constraintTop_toTopOf="@id/ll_content"
app:layout_constraintBottom_toBottomOf="@id/ll_content"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toStartOf="@id/ll_content" />
<androidx.appcompat.widget.LinearLayoutCompat
android:id="@+id/ll_content"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_marginEnd="4dp"
android:orientation="vertical"
app:layout_constraintHorizontal_weight="1.3"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/ad_media">
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/ad_headline"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:ellipsize="end"
android:maxLines="1"
android:text="@string/app_name"
android:textColor="@color/color_181b1f"
android:textSize="12sp" />
<androidx.appcompat.widget.LinearLayoutCompat
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="4dp"
android:orientation="horizontal">
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/ad_small_icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@mipmap/icon_ad_pop"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent" />
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/ad_body"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginEnd="10dp"
android:text="@string/storage_permission_content"
android:ellipsize="end"
android:maxLines="2"
android:textColor="@color/color_666666"
android:textSize="10sp" />
</androidx.appcompat.widget.LinearLayoutCompat>
<androidx.appcompat.widget.LinearLayoutCompat
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="4dp"
android:orientation="horizontal">
<androidx.appcompat.widget.LinearLayoutCompat
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1">
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/ad_icon"
android:layout_width="33dp"
android:layout_height="33dp" />
</androidx.appcompat.widget.LinearLayoutCompat>
<Button
android:id="@+id/ad_call_to_action"
android:layout_width="wrap_content"
android:layout_height="38dp"
android:background="@drawable/gradient_26"
android:text="@string/open"
android:textAllCaps="false"
android:textColor="@color/white"
android:textSize="14sp"
android:textStyle="bold" />
</androidx.appcompat.widget.LinearLayoutCompat>
</androidx.appcompat.widget.LinearLayoutCompat>
</androidx.constraintlayout.widget.ConstraintLayout>
</com.google.android.gms.ads.nativead.NativeAdView>
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="@color/white" />
<corners android:radius="12dp" />
</shape>
\ No newline at end of file
<resources xmlns:tools="http://schemas.android.com/tools">
<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation">
<string name="app_name" tools:ignore="MissingTranslation">Clear Scan QR &amp; Barcode</string>
<string name="facebook_app_id" tools:ignore="MissingTranslation">593803576925183</string>
......@@ -117,4 +117,168 @@
<string name="continue_">Continue</string>
<string name="complete">Complete</string>
<string name="junk_scan">Junk Scan</string>
<string name="clean_junk">Clean Junk</string>
<string name="battery_info">Battery Info</string>
<string name="app_process">App Process</string>
<string name="whatsapp_clean">WhatsApp Clean</string>
<string name="screenshot_clean">Screenshot Clean</string>
<string name="app_manager">App Manager</string>
<string name="large_file_clean">Large File Clean</string>
<string name="image_compressor">Image Compression</string>
<string name="storage_used">Storage Used</string>
<string name="memory_used">Memory Used</string>
<string name="systematic">Systematic</string>
<string name="read_terms_and_conditions">Read terms and conditions</string>
<string name="rate_us">Rate us</string>
<string name="rate_our_app">Rate our app</string>
<string name="share_app_with_friends">Share app with friends</string>
<string name="found">Found</string>
<string name="normal">Normal</string>
<string name="charging">Charging</string>
<string name="battery_status">Battery status</string>
<string name="temperature">Temperature</string>
<string name="voltage">Voltage</string>
<string name="power">Power</string>
<string name="battery_type">Battery Type</string>
<string name="battery_capacity">Battery Capacity</string>
<string name="finish">Finish</string>
<string name="storage_permission_title">Need to obtain storage permissions</string>
<string name="storage_permission_content">To ensure proper functionality,please allow Fast Phone Cleaner to access all files on your device.</string>
<string name="notification_title">Turn on notification</string>
<string name="notification_content">Never miss important phone cleaning notification reminders</string>
<string name="open_settings">Open Settings</string>
<string name="score_title">Thank you for using Fast Phone Cleaner!</string>
<string name="score_content">Five medal express great satisfaction</string>
<string name="sure">Sure</string>
<string name="clean_now">Clean Now</string>
<string name="exit">Exit</string>
<string name="please_wait_a_moment">Please wait a moment</string>
<string name="logout_content">Are you sure you want to quit without trying to clean up the garbage again?</string>
<string name="already_occupied">Already Occupied</string>
<string name="running_program">Running Program</string>
<string name="skip">Skip</string>
<string name="stop">Stop</string>
<string name="cleanable">Cleanable</string>
<string name="video_messages">Video Messages</string>
<string name="image_messages">Image Messages</string>
<string name="audio_messages">Audio Messages</string>
<string name="no_video_message_found">No video message found.</string>
<string name="no_image_message_found">No image message found.</string>
<string name="no_audio_message_found">No audio message found.</string>
<string name="found_voice_message">Found 3 audio message(s),unable to listen once cleaned.</string>
<string name="select_all">Select all</string>
<string name="delete">Delete</string>
<string name="selected">Selected</string>
<string name="delete_title">Sure to delete?</string>
<string name="delete_content">Selected files cannot be recoverd after deleting, continue anyway?</string>
<string name="large_file">Large File</string>
<string name="large_files">Large Files</string>
<string name="battery">Battery</string>
<string name="low">Low</string>
<string name="low_lower">low</string>
<string name="charging_lower">charging</string>
<string name="exit_clean_junk">Exit Junk Scan</string>
<string name="exit_clean_junk_content">Exit Junk Scan? Uncleared junk files might be taking up space.</string>
<string name="exit_app_manager">Exit App Manager</string>
<string name="exit_app_manager_content">Exit App manager? Unused applications may occupy phone memory.</string>
<string name="exit_battery_info">Exit Battery Info</string>
<string name="exit_battery_info_content">Exit Battery Info? Continuing to use it can help you better understand the battery condition.</string>
<string name="exit_whatsapp_clean">Exit WhatsApp Clean</string>
<string name="exit_whatsapp_clean_content">Exit WhatsApp Clean? Possible WhatsApp junk files may be using space.</string>
<string name="exit_large_file_clean">Exit Large Files</string>
<string name="exit_large_file_clean_content">Exit Large Files? Undeleted large files may be occupying valuable space.</string>
<string name="exit_app_process">Exit App Process</string>
<string name="exit_app_process_content1">Exit App Process?</string>
<string name="exit_app_process_content2">background processes are still running.</string>
<string name="exit_photo_compression">Exit Image Compression</string>
<string name="exit_photo_compression_content">Exit Image Compression? Uncompressed photos may be occupying space.</string>
<string name="exit_screenshot_cleaner">Exit Screenshot Clean</string>
<string name="exit_screenshot_cleaner_content">Exit Screenshot Clean? Unremoved screenshots may be taking up space.</string>
<string name="exit_similar_photos">Exit Similar Photos</string>
<string name="exit_similar_photos_content">Exit Similar Photos? Unremoved similar photos might be occupying space.</string>
<string name="notification_clean_junk">Clean out junk fles to free up space on your phone!</string>
<string name="notification_whatsapp_clean">Free up space by cleaning up WhatsApp junk files!</string>
<string name="notification_battery_info">View phone battery consumption recently!</string>
<string name="notification_large_file">Clean out large files to free up your storage space!</string>
<string name="notification_photo_compression">Compress photos to free up phone storage space!</string>
<string name="notification_screenshot_clean">Clear screenshot clutter to free up space!</string>
<string name="notification_installed">Installed successfully \nClean up files from your device!</string>
<string name="notification_uninstalled">Uninstalled successfully \nClean up files from your device!</string>
<string name="notification_not_clean">Not cleaned in</string>
<string name="notification_not_clean_days">day(s)</string>
<string name="result_clean_junk">Clean up unnecessary junk files!</string>
<string name="result_battery_info">View battery usage and details</string>
<string name="result_app_processes">Manage backend processes</string>
<string name="result_large_file">Clean large fles to free up storage space</string>
<string name="result_screenshot_clean">Check Screenshot Clean to release more space</string>
<string name="result_photo_compression">Compress photos to optimize storage usage</string>
<string name="result_similar_photos">Clean up similar photos to free up space</string>
<string name="result_app_manager">Clear out apps you no longer need</string>
<string name="result_whatsapp_clean">Free up space by cleaning up WhatsApp junkfiles</string>
<string name="cleaned_up">Cleaned up</string>
<string name="cleaned_up_content">Cleaned up other data to free up more space</string>
<string name="free_up_phone_space">Free Up Phone Space</string>
<string name="apps">apps</string>
<string name="uninstall">Uninstall</string>
<string name="screenshots_totally">Screenshots totally</string>
<string name="clean_tips">Clean Up doesn\'t Delete Your Personal Data</string>
<string name="empty_folder">Empty Folder</string>
<string name="apk_files">Apk Files</string>
<string name="temp_files">Temp Files</string>
<string name="logs_files">Logs Files</string>
<string name="start">Start</string>
<string name="agree">By continuing you are agreeing to the</string>
<string name="go_it">Go it</string>
<string name="other_than">Other than</string>
<string name="all_types">All types</string>
<string name="image">Image</string>
<string name="video">Video</string>
<string name="audio">Audio</string>
<string name="document">Document</string>
<string name="apk">Apk</string>
<string name="other_types">Other types</string>
<string name="mb_10">10 MB</string>
<string name="mb_20">20 MB</string>
<string name="mb_50">50 MB</string>
<string name="mb_100">100 MB</string>
<string name="mb_500">500 MB</string>
<string name="all_time">All time</string>
<string name="week_1">1 week</string>
<string name="month_1">1 month</string>
<string name="month_3">3 month</string>
<string name="month_6">6 month</string>
<string name="year_1">1 year</string>
<string name="please_wait">Please wait...</string>
<string name="wait_a_moment">Wait a moment</string>
<string name="compress">Compress</string>
<string name="best_quality_photo">Best quality photo</string>
<string name="most_space_saved">Most space saved</string>
<string name="compress_all">Compress all</string>
<string name="confirm_content">The original photos will be replaced by the compressed ones</string>
<string name="process_ad">This process may contain ads</string>
<string name="app">App</string>
<string name="larger_than">Larger than</string>
<string name="junk_files">Junk Files</string>
<string name="hot">Hot</string>
<string name="hot_tools">Hot Tools</string>
<string name="similar_photos">Similar Photos</string>
<string name="release_space">Release Space</string>
<string name="cleaned">Cleaned</string>
<string name="auto_select">Auto select</string>
<string name="occupies">Occupies</string>
<string name="have_been_with_you_for">Have been with you for</string>
<string name="days">days</string>
<string name="the_best_we_can_get">The best we can get</string>
<string name="whatsapp">WhatsApp</string>
<string name="view">View</string>
<string name="equipment">Equipment</string>
<string name="device_manufacturer">Device Manufacturer</string>
<string name="google_advertiser_id">Google Advertiser Id</string>
<string name="android_version">Android Version</string>
<string name="initialize_engine">Initialize engine</string>
<string name="device_ready">Device Ready</string>
<string name="loading_point">loading...</string>
</resources>
\ No newline at end of file
......@@ -79,4 +79,6 @@
<style name="CustomDialogStyle" parent="android:Theme.Material.Dialog">
<item name="android:windowBackground">@android:color/transparent</item>
</style>
</resources>
\ No newline at end of file
......@@ -22,6 +22,8 @@ pangle = "6.3.0.4.0"
protoliteWellKnownTypes = "18.0.0"
lottie = "6.5.2"
expandablerecyclerview = "0.9.3"
okhttp = "4.12.0"
......@@ -55,6 +57,9 @@ applovin_pangle = { group = "com.applovin.mediation", name = "bytedance-adapter"
protolite-well-known-types = { group = "com.google.firebase", name = "protolite-well-known-types", version.ref = "protoliteWellKnownTypes" }
lottie = { group = "com.airbnb.android", name = "lottie", version.ref = "lottie" }
expandablerecyclerview = { group = "com.github.pokercc", name = "ExpandableRecyclerView", version.ref = "expandablerecyclerview" }
okhttp = { group = "com.squareup.okhttp3", name = "okhttp", version.ref = "okhttp" }
logging-interceptor = { group = "com.squareup.okhttp3", name = "logging-interceptor", version.ref = "okhttp" }
......
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