Commit 1f4fded5 authored by wanglei's avatar wanglei

[修改]改为单模块代码

parent c4bbfecc
/build
\ No newline at end of file
plugins {
alias(libs.plugins.android.library)
alias(libs.plugins.kotlin.android)
}
android {
namespace = "com.koko.batteryinfo"
compileSdk = 34
defaultConfig {
minSdk = 24
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
consumerProguardFiles("consumer-rules.pro")
}
buildTypes {
release {
isMinifyEnabled = false
proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro")
}
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = "1.8"
}
buildFeatures {
viewBinding = true
buildConfig = true
}
}
dependencies {
implementation(libs.androidx.core.ktx)
implementation(libs.androidx.appcompat)
implementation(libs.material)
testImplementation(libs.junit)
androidTestImplementation(libs.androidx.junit)
androidTestImplementation(libs.androidx.espresso.core)
implementation(project(":Drouter"))
}
\ No newline at end of file
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile
\ No newline at end of file
package com.koko.batteryinfo
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.ext.junit.runners.AndroidJUnit4
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.Assert.*
/**
* Instrumented test, which will execute on an Android device.
*
* See [testing documentation](http://d.android.com/tools/testing).
*/
@RunWith(AndroidJUnit4::class)
class ExampleInstrumentedTest {
@Test
fun useAppContext() {
// Context of the app under test.
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
assertEquals("com.koko.batteryinfo.test", appContext.packageName)
}
}
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<application>
<activity
android:name=".BatteryInfoActivity"
android:screenOrientation="portrait"
tools:ignore="DiscouragedApi" />
</application>
</manifest>
\ No newline at end of file
package com.koko.batteryinfo
import android.animation.ValueAnimator
import android.content.Intent
import android.content.IntentFilter
import android.graphics.Color
import android.os.Bundle
import android.view.animation.LinearInterpolator
import androidx.activity.SystemBarStyle
import androidx.activity.addCallback
import androidx.activity.enableEdgeToEdge
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import com.didi.drouter.api.DRouter
import com.koko.batteryinfo.databinding.ActivityBatteryInfoBinding
import com.koko.drouter.ad.InterAdService
import com.koko.drouter.ad.NativeBatteryType
import com.koko.drouter.ad.NativeAdService
import com.koko.drouter.ad.interAdService
import com.koko.drouter.app.AppService
import com.koko.drouter.app.appService
import com.koko.drouter.base.BaseActivity
import java.math.BigDecimal
import java.math.RoundingMode
class BatteryInfoActivity : BaseActivity<ActivityBatteryInfoBinding>(ActivityBatteryInfoBinding::inflate) {
private var batteryReceiver: BatteryReceiver? = null
override fun initView() {
useDefaultImmersive()
registerReceiver()
showNative()
showInter()
}
override fun initListener() {
super.initListener()
onBackPressedDispatcher.addCallback {
backMainShowAd()
}
binding.flBack.setOnClickListener { onBackPressedDispatcher.onBackPressed() }
}
private fun showInter() {
interAdService.showInterAd(this) {
startProgressAnimation(binding.tvBattery.text.toString().toInt())
}
}
private fun backMainShowAd() {
interAdService.showInterAdBack(this) {
appService.finishToMainTop(this)
}
}
private fun showNative() {
val nativeService = DRouter.build(NativeAdService::class.java).getService()
nativeService.showNative(this, binding.flAd, NativeBatteryType)
}
override fun useDefaultImmersive() {
enableEdgeToEdge(SystemBarStyle.light(Color.TRANSPARENT, Color.TRANSPARENT))
ViewCompat.setOnApplyWindowInsetsListener(binding.root) { v, insets ->
val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars())
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom)
insets
}
}
override fun onDestroy() {
super.onDestroy()
batteryReceiver?.let { unregisterReceiver(batteryReceiver) }
}
private fun registerReceiver() {
val intentFilter = IntentFilter(Intent.ACTION_BATTERY_CHANGED)
batteryReceiver = BatteryReceiver { setBatteryInfo() }
registerReceiver(batteryReceiver, intentFilter)
}
fun formatTime(totalMinutes: Int): String {
val hours = totalMinutes / 60
val minutes = totalMinutes % 60
return "${hours}h${minutes}m"
}
private fun setBatteryInfo() {
val percent = BatteryReceiver.level / BatteryReceiver.scale.toFloat()
val remainingCapacity = BatteryReceiver.mAh * percent
val wTime = (remainingCapacity / 6.0f).toInt()
val battery = (percent * 100).toInt()
val temperature = calculate(BatteryReceiver.temperature, 10)
val voltage = calculate(BatteryReceiver.voltage, 1000)
binding.tvBattery.text = "$battery"
binding.tvEstimated.text = formatTime(wTime)
binding.tvTemperature.text = "$temperature℃"
binding.tvVoltage.text = "${voltage}V"
binding.tvPower.text = "${(BatteryReceiver.mAh * percent).toInt()}mAh"
binding.tvBatteryType.text = "${BatteryReceiver.technology}"
binding.tvBatteryCapacity.text = "${BatteryReceiver.mAh.toInt()}mAh"
binding.tvStatus.text = if (BatteryReceiver.isCharging) "Charging" else "Normal"
}
private fun startProgressAnimation(endProgress: Int) {
ValueAnimator.ofInt(0, endProgress).apply {
duration = 700
interpolator = LinearInterpolator()
addUpdateListener { animation ->
val progress = animation.animatedValue as Int
binding.pbBattery.setProgress(progress / 100f)
}
}.start()
}
private 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.koko.batteryinfo
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.os.BatteryManager
import android.os.Build
/**
* 作者:马海钊
* 时间:2025/2/11 11:59
* 功能:
*/
class BatteryReceiver(
private val block: (() -> Unit?)? = null
) : BroadcastReceiver() {
companion object {
var level = 0
var scale = 0
var voltage = 0
var temperature = 0
var technology = ""
var health = 0
var status = 0
var mAh = 0f
var isCharging = false
@JvmStatic
fun register(context: Context) {
val intentFilter = IntentFilter().apply {
addAction(Intent.ACTION_BATTERY_CHANGED)
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
context.registerReceiver(BatteryReceiver(), intentFilter, Context.RECEIVER_EXPORTED)
} else {
context.registerReceiver(BatteryReceiver(), intentFilter)
}
}
}
override fun onReceive(context: Context, intent: Intent) {
if (Intent.ACTION_BATTERY_CHANGED != intent.action) return
val batteryManager = context.getSystemService(Context.BATTERY_SERVICE) as BatteryManager
level = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, 0)
scale = intent.getIntExtra(BatteryManager.EXTRA_SCALE, 0)
voltage = intent.getIntExtra(BatteryManager.EXTRA_VOLTAGE, 0)
temperature = intent.getIntExtra(BatteryManager.EXTRA_TEMPERATURE, 0)
technology = intent.getStringExtra(BatteryManager.EXTRA_TECHNOLOGY).toString()
health = intent.getIntExtra(BatteryManager.EXTRA_HEALTH, 0)
status = intent.getIntExtra(BatteryManager.EXTRA_STATUS, BatteryManager.BATTERY_STATUS_UNKNOWN)
isCharging = status == BatteryManager.BATTERY_STATUS_CHARGING || status == BatteryManager.BATTERY_STATUS_FULL
val chargeCounter = batteryManager.getIntProperty(BatteryManager.BATTERY_PROPERTY_CHARGE_COUNTER)
val propertyCapacity = batteryManager.getIntProperty(BatteryManager.BATTERY_PROPERTY_CAPACITY)
if (chargeCounter != Int.MIN_VALUE && propertyCapacity != Int.MIN_VALUE) {
mAh = (chargeCounter / (propertyCapacity.toFloat() / 100f)) / 1000f
}
block?.invoke()
}
}
\ No newline at end of file
package com.base.pdfreaderpro.ui.battery
import android.content.Context
import android.graphics.Canvas
import android.graphics.Color
import android.graphics.LinearGradient
import android.graphics.Paint
import android.graphics.Path
import android.graphics.RectF
import android.graphics.Shader
import android.util.AttributeSet
import android.view.View
class CircleProgressBar @JvmOverloads constructor(
context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
) : View(context, attrs, defStyleAttr) {
private val arcPaint: Paint
private val bgPaint: Paint
private val path: Path
private val bounds: RectF
private var progress = 1f // 进度
private var colorArray = intArrayOf()
init {
arcPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
style = Paint.Style.STROKE
strokeWidth = dpToPx(6f)
strokeCap = Paint.Cap.ROUND
}
bgPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
color = Color.TRANSPARENT
style = Paint.Style.STROKE
strokeWidth = dpToPx(6f)
strokeCap = Paint.Cap.ROUND
}
path = Path().apply {
}
bounds = RectF()
colorArray = intArrayOf(
Color.parseColor("#00a8ce"),
Color.parseColor("#00a7fa"),
Color.parseColor("#8afbff"),
)
}
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
// 考虑Paint的strokeWidth,调整RectF的边界
val strokeWidth: Float = bgPaint.getStrokeWidth()
val halfStrokeWidth = strokeWidth / 2
// 绘制完整圆的背景
path.reset()
path.addCircle(
width / 2f,
height / 2f,
(Math.max(width, height) - strokeWidth) / 2,
Path.Direction.CW
)
canvas.drawPath(path, bgPaint)
// 绘制完整圆的进度
path.reset()
val sweepAngleForProgress: Float = 360 * progress
// 设置渐变色
// 计算渐变的终点X坐标,基于进度
val endX = width / 2f + (sweepAngleForProgress / 360) * (width / 2f - halfStrokeWidth)
// 设置渐变色
// 设置渐变色
val shader = LinearGradient(
width / 2f, 0f, // 起点坐标
width / 2f, height.toFloat(), // 终点坐标
colorArray, // 渐变颜色
null, // 颜色位置(null表示均匀分布)
Shader.TileMode.CLAMP // 填充模式
)
arcPaint.shader = shader
// 从圆的顶部开始绘制
bounds.set(halfStrokeWidth, halfStrokeWidth, width - halfStrokeWidth, height - halfStrokeWidth)
path.addArc(bounds, 270f, sweepAngleForProgress)
canvas.drawPath(path, arcPaint)
}
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
val desiredWidth = dpToPx(144f)
val desiredHeight = dpToPx(144f)
setMeasuredDimension(resolveSize(desiredWidth, widthMeasureSpec), resolveSize(desiredHeight, heightMeasureSpec))
}
fun set(strokeWidth: Float, colorArray: IntArray) {
val width = dpToPx(strokeWidth)
arcPaint.strokeWidth = width
bgPaint.strokeWidth = width
this.colorArray = colorArray
}
fun setProgress(progress: Float) {
this.progress = progress
invalidate()
}
private fun dpToPx(dp: Float): Float {
return dp * resources.displayMetrics.density
}
private fun resolveSize(desiredSize: Float, measureSpec: Int): Int {
val mode = MeasureSpec.getMode(measureSpec)
val size = MeasureSpec.getSize(measureSpec)
return when (mode) {
MeasureSpec.EXACTLY -> size
MeasureSpec.AT_MOST -> Math.min(desiredSize.toInt(), size)
else -> desiredSize.toInt()
}
}
}
\ 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="#F2FAF8" />
<corners android:radius="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="#ffffff" />
<corners android:radius="12dp" />
</shape>
\ No newline at end of file
package com.koko.batteryinfo
import org.junit.Test
import org.junit.Assert.*
/**
* Example local unit test, which will execute on the development machine (host).
*
* See [testing documentation](http://d.android.com/tools/testing).
*/
class ExampleUnitTest {
@Test
fun addition_isCorrect() {
assertEquals(4, 2 + 2)
}
}
\ No newline at end of file
/build
\ No newline at end of file
plugins {
alias(libs.plugins.android.library)
alias(libs.plugins.kotlin.android)
}
android {
namespace = "com.koko.drouter"
compileSdk = 34
defaultConfig {
minSdk = 24
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
consumerProguardFiles("consumer-rules.pro")
}
buildTypes {
release {
isMinifyEnabled = false
proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro")
}
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = "1.8"
}
buildFeatures {
viewBinding = true
buildConfig = true
}
}
dependencies {
implementation(libs.androidx.core.ktx)
implementation(libs.androidx.appcompat)
implementation(libs.material)
testImplementation(libs.junit)
androidTestImplementation(libs.androidx.junit)
androidTestImplementation(libs.androidx.espresso.core)
api(libs.drouter.api)
api(libs.drouter.api.page)
api(libs.drouter.api.process)
// 语种切换框架:https://github.com/getActivity/MultiLanguages
api("com.github.getActivity:MultiLanguages:9.3")
}
\ No newline at end of file
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile
\ No newline at end of file
package com.koko.drouter
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.ext.junit.runners.AndroidJUnit4
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.Assert.*
/**
* Instrumented test, which will execute on an Android device.
*
* See [testing documentation](http://d.android.com/tools/testing).
*/
@RunWith(AndroidJUnit4::class)
class ExampleInstrumentedTest {
@Test
fun useAppContext() {
// Context of the app under test.
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
assertEquals("com.koko.router.test", appContext.packageName)
}
}
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
</manifest>
\ No newline at end of file
package com.koko.drouter.ad
import android.app.Activity
import com.didi.drouter.api.DRouter
const val INTER_BATTERY_INFO = "INTER_BATTERY_INFO"
val interAdService by lazy {
DRouter.build(InterAdService::class.java).getService()
}
interface InterAdService {
fun showInterAd(activity: Activity, interWhere: String = "", callBack: () -> Unit) = Unit
fun showInterAdBack(activity: Activity, interWhere: String = "", callBack: () -> Unit) = Unit
}
\ No newline at end of file
package com.koko.drouter.ad
import android.app.Activity
import android.widget.FrameLayout
import com.didi.drouter.api.DRouter
const val NativeBatteryType: String = "NativeBatteryType"
val nativeAdService by lazy {
DRouter.build(NativeAdService::class.java).getService()
}
interface NativeAdService {
fun showNative(activity: Activity, flAd: FrameLayout, nativeType: String)
}
\ No newline at end of file
package com.koko.drouter.aes
import com.didi.drouter.api.DRouter
val aesService by lazy {
DRouter.build(AesService::class.java).getService()
}
interface AesService {
fun decrypt(aes: String): String
}
\ No newline at end of file
package com.koko.drouter.app
import android.app.Activity
import com.didi.drouter.api.DRouter
import java.util.Locale
val appService by lazy {
DRouter.build(AppService::class.java).getService()
}
interface AppService {
fun getCurrentSpLocal(): Locale
fun changeActivityLanguage(
activity: Activity,
currentLocale: Locale,
changeAction: () -> Unit
): Boolean
fun finishToMain() = Unit
fun finishToMainTop(activity: Activity) = Unit
}
\ No newline at end of file
package com.koko.drouter.base
import android.content.Intent
import androidx.activity.result.ActivityResult
import androidx.activity.result.ActivityResultCallback
import androidx.activity.result.ActivityResultCaller
import androidx.activity.result.contract.ActivityResultContracts
class ActivityLauncher(activityResultCaller: ActivityResultCaller) {
//region 权限
private var permissionCallback: ActivityResultCallback<Map<String, Boolean>>? = null
private val permissionLauncher =
activityResultCaller.registerForActivityResult(ActivityResultContracts.RequestMultiplePermissions()) { result: Map<String, Boolean> ->
permissionCallback?.onActivityResult(result)
}
fun launch(
permissionArray: Array<String>,
permissionCallback: ActivityResultCallback<Map<String, Boolean>>?
) {
this.permissionCallback = permissionCallback
permissionLauncher.launch(permissionArray)
}
//endregion
//region intent跳转
private var activityResultCallback: ActivityResultCallback<ActivityResult>? = null
private val intentLauncher =
activityResultCaller.registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { activityResult: ActivityResult ->
activityResultCallback?.onActivityResult(activityResult)
}
/**
* it.resultCode == Activity.RESULT_OK
*/
fun launch(
intent: Intent,
activityResultCallback: ActivityResultCallback<ActivityResult>? = null
) {
this.activityResultCallback = activityResultCallback
intentLauncher.launch(intent)
}
//endregion
//region saf
// private var safResultCallback: ActivityResultCallback<Uri?>? = null
// private val safLauncher =
// activityResultCaller.registerForActivityResult(
// ActivityResultContracts.OpenDocument(),
// ) { uri ->
// safResultCallback?.onActivityResult(uri)
// }
//
// fun launch(array: Array<String>, safResultCallback: ActivityResultCallback<Uri?>?) {
// this.safResultCallback = safResultCallback
// safLauncher.launch(array)
// }
//end region
}
\ No newline at end of file
package com.koko.drouter.event
import android.app.Activity
import com.didi.drouter.api.DRouter
val eventService by lazy {
DRouter.build(EventService::class.java).getService()
}
interface EventService {
fun eventActivity(activity: Activity) = Unit
}
\ No newline at end of file
package com.koko.drouter.gobalConfig
import com.didi.drouter.api.DRouter
val globalConfigService by lazy {
DRouter.build(GlobalConfigService::class.java).getService()
}
interface GlobalConfigService {
fun getPackageName(): String
fun getApiUri(): String
fun getAppName(): String
}
\ No newline at end of file
package com.koko.drouter.sp
import com.didi.drouter.api.DRouter
val spService by lazy {
DRouter.build(SpService::class.java).getService()
}
interface SpService {
fun getBoolean(key: String): Boolean
fun putBoolean(key: String, value: Boolean)
fun getLong(key: String): Long
fun putLong(key: String, value: Long)
fun getInt(key: String): Int
fun putInt(key: String, value: Int)
fun getString(key: String): String
fun putString(key: String, value: String)
}
\ No newline at end of file
package com.koko.drouter
import org.junit.Test
import org.junit.Assert.*
/**
* Example local unit test, which will execute on the development machine (host).
*
* See [testing documentation](http://d.android.com/tools/testing).
*/
class ExampleUnitTest {
@Test
fun addition_isCorrect() {
assertEquals(4, 2 + 2)
}
}
\ No newline at end of file
......@@ -6,7 +6,6 @@ plugins {
alias(libs.plugins.kotlin.android)
id("com.google.gms.google-services")
id("com.google.firebase.crashlytics")
id("com.didi.drouter")
id("com.google.devtools.ksp")
}
......@@ -42,10 +41,6 @@ android {
release {
isMinifyEnabled = true
proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro")
// 设置是否要自动上传
firebaseCrashlytics {
mappingFileUploadEnabled = true
}
signingConfig = signingConfigs.getByName("release")
}
}
......@@ -85,8 +80,6 @@ dependencies {
testImplementation(libs.junit)
androidTestImplementation(libs.androidx.junit)
androidTestImplementation(libs.androidx.espresso.core)
implementation(project(":Drouter"))
implementation(project(":BatteryInfo"))
//网络请求
implementation("com.google.code.gson:gson:2.11.0")
......@@ -140,4 +133,8 @@ dependencies {
//work
implementation("androidx.work:work-runtime-ktx:2.7.1") // 请使用最新版本
val billing_version = "7.1.1"
implementation("com.android.billingclient:billing:$billing_version")
implementation("com.android.billingclient:billing-ktx:$billing_version")
}
\ No newline at end of file
......@@ -104,7 +104,7 @@
</receiver>
<service
android:name=".service.StayJobService"
android:name=".business.service.StayJobService"
android:exported="false"
android:foregroundServiceType="dataSync"
android:permission="android.permission.BIND_JOB_SERVICE" />
......
......@@ -20,10 +20,10 @@ import com.base.appzxhy.fcm.receiver.BatteryStatusReceiver
import com.base.appzxhy.fcm.receiver.PackageStatusReceiver
import com.base.appzxhy.fcm.receiver.ScreenStatusReceiver
import com.base.appzxhy.fcm.work.RepeatingWorker.Companion.schedulePeriodicWork
import com.base.appzxhy.helper.EventUtils
import com.base.appzxhy.helper.InstallHelps
import com.base.appzxhy.helper.NewComUtils
import com.base.appzxhy.service.StayJobService.Companion.startJob
import com.base.appzxhy.business.helper.EventUtils
import com.base.appzxhy.business.helper.InstallHelps
import com.base.appzxhy.business.helper.NewComUtils
import com.base.appzxhy.business.service.StayJobService.Companion.startJob
import com.base.appzxhy.ui.start.StartActivity
import com.base.appzxhy.utils.ActivityManagerUtils
import com.base.appzxhy.utils.AppPreferences
......
@file:Suppress("unused")
package com.koko.drouter.base
package com.base.appzxhy.base
import android.app.Activity
import android.content.Intent
......@@ -10,7 +10,6 @@ import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import androidx.viewbinding.ViewBinding
import kotlin.random.Random
/**
*
......@@ -23,7 +22,6 @@ abstract class BaseFragment<VB : ViewBinding>(
private var _binding: VB? = null
private var fragmentInit = false
open val TAG = javaClass.simpleName
private val tagNo = Random.nextInt(500)
//重写binding get()方法不直接暴露真实的可空_binding数据
@Suppress("MemberVisibilityCanBePrivate")
......@@ -39,13 +37,10 @@ abstract class BaseFragment<VB : ViewBinding>(
return binding.root
}
private var onViewCreatedI = 0
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
initView()
initListener()
onViewCreatedI++
// LogEx.logDebug(TAG, "lifecycle $tagNo onViewCreated ${javaClass.simpleName} $onViewCreatedI")
}
protected open fun initView() {}
......@@ -54,50 +49,18 @@ abstract class BaseFragment<VB : ViewBinding>(
open fun onResumeOneShoot() = Unit
private var onResumeOneShootI = 0
private var onResumeI = 0
override fun onResume() {
onResumeI++
// LogEx.logDebug(TAG, "lifecycle $tagNo onResume ${javaClass.simpleName} $onResumeI")
if (!fragmentInit) {
onResumeOneShoot()
onResumeOneShootI++
// LogEx.logDebug(TAG, "lifecycle $tagNo onResumeOneShoot ${javaClass.simpleName} $onResumeOneShootI")
fragmentInit = true
}
super.onResume()
}
private var onDestroyViewI = 0
override fun onDestroyView() {
super.onDestroyView()
//要手动置null防止内存泄漏
_binding = null
onDestroyViewI++
// LogEx.logDebug(TAG, "lifecycle $tagNo onDestroyView ${javaClass.simpleName} $onDestroyViewI")
}
private var onPauseI = 0
override fun onPause() {
super.onPause()
onPauseI++
// LogEx.logDebug(TAG, "lifecycle $tagNo onPause ${javaClass.simpleName} $onPauseI")
}
private var onStopI = 0
override fun onStop() {
super.onStop()
onStopI++
// LogEx.logDebug(TAG, "lifecycle $tagNo onStop ${javaClass.simpleName} $onStopI")
fragmentInit = false
}
private var onDestroyI = 0
override fun onDestroy() {
super.onDestroy()
onDestroyI++
// LogEx.logDebug(TAG, "lifecycle $tagNo onDestroy ${javaClass.simpleName} $onDestroyI")
}
}
......
......@@ -13,8 +13,10 @@ class AdConfigBean(
var numClickLimit: Int = -1,//点击次数限制
var timeInterval: Int = 1,//广告间隔
var openAdLoading: Int = 15,//开屏广告拉取时间
var functionBackShowAd: Boolean = true,//功能退出显示广告
var functionInShowAd: Boolean = true,//功能进入显示广告
var rvEmptyShowNative: Boolean = true,//列表空的显示原生不
) {
......
package com.base.appzxhy.bean.config
/**
* 后台配置
*/
data class ConfigBean(
var isInBlackList: Boolean = false,
val ut: Int = 0,
var vpnCanUse: Boolean = true,
val adConfigBean: AdConfigBean = AdConfigBean(),
val popupConfigBean: PopupConfigBean = PopupConfigBean(),
val vipConfigBean: VipConfigBean = VipConfigBean(),
val noEventKey: List<String> = listOf(),
val getConfigInterval: Int = 4,//请求配间隔小时
) {
companion object {
var configBean: ConfigBean = ConfigBean()
}
......
......@@ -2,38 +2,25 @@ package com.base.appzxhy.bean.config
class PopupConfigBean(
var popupForegroundCanPush: Boolean = false,
var popupStatus: Boolean = true,
var popupCount: Int = 24,
var popupStart: Int = 0,
var popupEnd: Int = 24,
var popupInterval: Int = 1,
var popupForegroundCanPush: Boolean = true,//前台是否可以推送
var popupStatus: Boolean = true,//推送总开关
var popupCount: Int = 200,//推送总次数
var isDifferNotificationId: Boolean = true,//推送使用不同的通知ID
var popupLimitRangeTime: List<Int> = listOf(),//推送限制时间
var popupTimerInterval: Int = 1,
var popupFcmInterval: Int = 1,
var popupHoverStatus: Boolean = true,
var popupInterval: Int = 1,//推送总时间间隔分钟
var popupHoverStatus: Boolean = true,//推送悬停触开关
var popupHoverCount: Int = 5,
var popupHoverDelay: Int = 5000,
//定时器
var timerS: Boolean = true,
var timerDelay: Int = 1,
var timerInterval: Int = 1,
//解锁
var screenS: Boolean = true,
var popupScreenCount: Int = 20,
var popupScreenInterval: Int = 1,
//电量
var batteryS: Boolean = true,
var popupBatteryValue: Int = 20,
var popupBatteryInterval: Int = 1,
var timerS: Boolean = true,//定时器触发推送
var timerDelay: Int = 60,//定时器执行延迟 秒
var timerInterval: Int = 7,//定时器执行间隔 分钟
//安装卸载
var packageS: Boolean = true,
var popupPackageCount: Int = 20,
var popupPackageInterval: Int = 1
val screenS: Boolean = true,//解锁触发推送
val alarmS: Boolean = true,//是否闹钟触发推送
val workS: Boolean = true,//workManager触发推送
) {
companion object {
var popupConfigBean: PopupConfigBean = PopupConfigBean()
......
package com.base.appzxhy.bean.config
class VipConfigBean(
val productIdYear: String = "vip_year",
val basePlanIdYear: String = "vip-year",
val offerIdYear: String = "year-free-3",
val productIdMonth: String = "vip_month",
val basePlanIdMonth: String = "vip-month",
val offerIdMonth: String = "",
val productIdWeek: String = "vip_week",
val basePlanIdWeek: String = "vip-week",
val offerIdWeek: String = ""
) {
companion object {
var vipConfigBean = VipConfigBean()
}
override fun toString(): String {
return "VipConfigBean(productIdYear='$productIdYear', basePlanIdYear='$basePlanIdYear', offerIdYear='$offerIdYear', productIdMonth='$productIdMonth', basePlanMonth='$basePlanIdMonth', offerIdMonth='$offerIdMonth', productIdWeek='$productIdWeek', basePlanWeek='$basePlanIdWeek', offIdWeek='$offerIdWeek')"
}
}
package com.base.appzxhy.ads
package com.base.appzxhy.business.ads
import android.animation.ObjectAnimator
import android.animation.ValueAnimator.INFINITE
......
package com.base.appzxhy.ads
package com.base.appzxhy.business.ads
import com.base.appzxhy.MyApplication
import com.base.appzxhy.helper.EventUtils
import com.base.appzxhy.business.helper.EventUtils
import com.base.appzxhy.utils.LogEx
import org.json.JSONObject
import java.util.UUID
......
package com.base.appzxhy.ads
package com.base.appzxhy.business.ads
import android.app.Activity
import android.app.Dialog
......
package com.base.appzxhy.ads
package com.base.appzxhy.business.ads
import android.app.Activity
import android.content.Context
......@@ -21,7 +21,7 @@ import com.base.appzxhy.ads.applovin.MaxOpenMgr
import com.base.appzxhy.bean.config.AdConfigBean
import com.base.appzxhy.bean.config.AdConfigBean.Companion.adsConfigBean
import com.base.appzxhy.bean.config.ConfigBean.Companion.configBean
import com.base.appzxhy.helper.EventUtils
import com.base.appzxhy.business.helper.EventUtils
import com.base.appzxhy.utils.AppPreferences
import com.base.appzxhy.utils.LogEx
import com.base.appzxhy.utils.ToastUtils.toast
......
package com.base.appzxhy.ads
package com.base.appzxhy.business.ads
abstract class AdsShowCallBack {
open fun show() {}
......
package com.base.appzxhy.ads
package com.base.appzxhy.business.ads
/**
* 广告类型
......
package com.base.appzxhy.ads
package com.base.appzxhy.business.ads
import com.base.appzxhy.BuildConfig
import com.base.appzxhy.bean.config.AdConfigBean
......
package com.base.appzxhy.ads
package com.base.appzxhy.business.ads
import android.annotation.SuppressLint
import android.content.Context
......
package com.base.appzxhy.ads
package com.base.appzxhy.business.ads
import com.base.appzxhy.GlobalConfig
import com.google.android.gms.ads.appopen.AppOpenAd
......
package com.base.appzxhy.ads.admob
package com.base.appzxhy.business.ads.admob
import android.os.Bundle
import android.view.ViewGroup
......
package com.base.appzxhy.ads.admob
package com.base.appzxhy.business.ads.admob
import android.app.Activity
import android.content.Context
......
package com.base.appzxhy.ads.admob
package com.base.appzxhy.business.ads.admob
import android.content.Context
import android.util.Log
......
package com.base.appzxhy.ads.admob
package com.base.appzxhy.business.ads.admob
import android.app.Activity
import android.content.Context
......
package com.base.appzxhy.ads.admob
package com.base.appzxhy.business.ads.admob
import android.app.Activity
import android.os.Bundle
......@@ -8,7 +8,7 @@ import com.base.appzxhy.ads.SolarEngineEvent
import com.base.appzxhy.ads.taichiPref
import com.base.appzxhy.ads.taichiSharedPreferencesEditor
import com.base.appzxhy.bean.config.AdConfigBean
import com.base.appzxhy.helper.EventUtils
import com.base.appzxhy.business.helper.EventUtils
import com.base.appzxhy.utils.LogEx
import com.facebook.appevents.AppEventsConstants
import com.facebook.appevents.AppEventsLogger
......
package com.base.appzxhy.ads.applovin
package com.base.appzxhy.business.ads.applovin
import android.os.Bundle
import com.applovin.mediation.MaxAd
......@@ -9,7 +9,7 @@ import com.base.appzxhy.MyApplication
import com.base.appzxhy.ads.AdEvent
import com.base.appzxhy.ads.taichiPref
import com.base.appzxhy.ads.taichiSharedPreferencesEditor
import com.base.appzxhy.helper.EventUtils
import com.base.appzxhy.business.helper.EventUtils
import com.base.appzxhy.utils.LogEx.logDebug
import com.facebook.appevents.AppEventsConstants
import com.facebook.appevents.AppEventsLogger
......
package com.base.appzxhy.ads.applovin
package com.base.appzxhy.business.ads.applovin
import android.app.Activity
import android.content.Context
......
package com.base.appzxhy.ads.applovin
package com.base.appzxhy.business.ads.applovin
import androidx.annotation.LayoutRes
import com.applovin.mediation.MaxAd
......@@ -10,7 +10,7 @@ import com.base.appzxhy.GlobalConfig
import com.base.appzxhy.ads.AdsType
import com.base.appzxhy.ads.LimitUtils
import com.base.appzxhy.ads.NativeParentView
import com.base.appzxhy.helper.EventUtils
import com.base.appzxhy.business.helper.EventUtils
import org.json.JSONObject
import java.util.UUID
......
package com.base.appzxhy.ads.applovin
package com.base.appzxhy.business.ads.applovin
import android.app.Activity
import android.content.Context
......
package com.base.appzxhy.helper
package com.base.appzxhy.business.helper
import android.util.Base64
import com.base.appzxhy.GlobalConfig
......
package com.base.appzxhy.helper
package com.base.appzxhy.business.helper
import android.os.Build
import com.base.appzxhy.BuildConfig
import com.base.appzxhy.GlobalConfig
import com.base.appzxhy.helper.ReportUtils.doPost
import com.base.appzxhy.business.helper.ReportUtils.doPost
import com.base.appzxhy.utils.AppPreferences
import com.base.appzxhy.utils.LogEx
import org.json.JSONException
......
package com.base.appzxhy.helper
package com.base.appzxhy.business.helper
import com.android.installreferrer.api.InstallReferrerClient
import com.android.installreferrer.api.InstallReferrerStateListener
......
package com.base.appzxhy.helper
package com.base.appzxhy.business.helper
import android.util.Base64
import android.util.Log
import com.base.appzxhy.BuildConfig
import com.base.appzxhy.GlobalConfig
......@@ -25,9 +26,9 @@ object NewComUtils {
val packageName = GlobalConfig.PACKAGE_NAME
val appCode = packageName.substringAfter(PACKAGE_NAME_PREFIX).take(5).toLowerCase(Locale.getDefault())
val bRefer = android.util.Base64.encodeToString(
val bRefer = Base64.encodeToString(
AppPreferences.getInstance().getString("install_referrer", "").toByteArray(),
android.util.Base64.DEFAULT
Base64.DEFAULT
)
var s = "$API_URL/${appCode}spk?pkg=$packageName" +
......
package com.base.appzxhy.helper;
package com.base.appzxhy.business.helper;
import android.text.TextUtils;
......
package com.base.appzxhy.fcm
package com.base.appzxhy.push.fcm
import android.content.Context
import android.util.Log
import com.base.appzxhy.helper.EventUtils.event
import com.base.appzxhy.business.helper.EventUtils.event
import com.base.appzxhy.utils.AppPreferences
import com.base.appzxhy.utils.LogEx
import com.google.android.gms.tasks.OnCompleteListener
......
package com.base.appzxhy.fcm
package com.base.appzxhy.push.fcm
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import com.base.appzxhy.bean.NotificationSendBean
import com.base.appzxhy.bean.NotificationSendBean.Companion.POPUP_WHERE_FCM
import com.base.appzxhy.fcm.notification.MyNotificationManager
import com.base.appzxhy.helper.EventUtils.event
import com.base.appzxhy.push.notification.MyNotificationManager
import com.base.appzxhy.business.helper.EventUtils.event
class FcmReceiver : BroadcastReceiver() {
......@@ -14,7 +14,6 @@ class FcmReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
event("FCM_Received", "FcmReceiver", null)
val sendBean = NotificationSendBean(context, POPUP_WHERE_FCM, canSend = { true }, sendSuccess = {})
MyNotificationManager.submitSendBean(sendBean)
MyNotificationManager.submitSendBean(NotificationSendBean(context, POPUP_WHERE_FCM))
}
}
package com.base.appzxhy.fcm
package com.base.appzxhy.push.fcm
import android.annotation.SuppressLint
import com.base.appzxhy.bean.NotificationSendBean
import com.base.appzxhy.bean.NotificationSendBean.Companion.POPUP_WHERE_FCM
import com.base.appzxhy.fcm.notification.MyNotificationManager
import com.base.appzxhy.helper.EventUtils.event
import com.base.appzxhy.push.notification.MyNotificationManager
import com.base.appzxhy.business.helper.EventUtils.event
import com.google.firebase.messaging.FirebaseMessagingService
import com.google.firebase.messaging.RemoteMessage
import org.json.JSONObject
......@@ -15,7 +15,6 @@ class MessagingService : FirebaseMessagingService() {
super.onMessageReceived(remoteMessage)
val data = JSONObject(remoteMessage.data.toString())
event("FCM_Received", "MessagingService", data)
val sendBean = NotificationSendBean(this, POPUP_WHERE_FCM, canSend = { true }, sendSuccess = {})
MyNotificationManager.submitSendBean(sendBean)
MyNotificationManager.submitSendBean(NotificationSendBean(this, POPUP_WHERE_FCM))
}
}
\ No newline at end of file
package com.base.appzxhy.fcm.notification
package com.base.appzxhy.push.notification
import android.content.Context
import com.base.appzxhy.BuildConfig
import com.base.appzxhy.MyApplication
import com.base.appzxhy.bean.NotificationSendBean
import com.base.appzxhy.bean.NotificationSendBean.Companion.ACTION_ID_SCAN
import com.base.appzxhy.bean.NotificationSendBean.Companion.POPUP_WHERE_TIMER
import com.base.appzxhy.bean.NotificationSendBean.Companion.ACTION_ID_BEAN_MEDIA
import com.base.appzxhy.bean.NotificationSendBean.Companion.POPUP_WHERE_WORK_MANAGER
import com.base.appzxhy.bean.config.PopupConfigBean.Companion.popupConfigBean
import com.base.appzxhy.helper.EventUtils
import com.base.appzxhy.push.notification.NotificationBeanUtils.nextNotificationInfoBean
import com.base.appzxhy.business.helper.EventUtils
import com.base.appzxhy.utils.AppPreferences
import com.base.appzxhy.utils.KotlinExt.currentDate
import com.base.appzxhy.utils.LogEx
......@@ -19,6 +20,7 @@ import kotlinx.coroutines.isActive
import kotlinx.coroutines.launch
import java.util.Calendar
import java.util.concurrent.ArrayBlockingQueue
import java.util.concurrent.TimeUnit
import java.util.concurrent.atomic.AtomicBoolean
/**
......@@ -26,7 +28,7 @@ import java.util.concurrent.atomic.AtomicBoolean
*/
object MyNotificationManager {
private val TAG = "NotificationManager"
private val TAG = "MyNotificationManager"
private var sendBeanBlockingQueue = ArrayBlockingQueue<NotificationSendBean>(10)
......@@ -49,53 +51,66 @@ object MyNotificationManager {
private var isStartSendQueue = AtomicBoolean(false)
private var isUnLimit: Boolean = if (BuildConfig.DEBUG) false else false
private var isUnLimit: Boolean = if (BuildConfig.DEBUG) true else false
fun startNotificationQueue() {
if (isStartSendQueue.get()) return
isStartSendQueue.set(true)
MainScope().launch(Dispatchers.IO) {
while (isActive) {
val bean = sendBeanBlockingQueue.take()
//测试哪些位置触发
if (BuildConfig.DEBUG) {
if (!testWhere.contains(bean.where)) continue
}
LogEx.logDebug(TAG, "sendNotificationIfCan where=${bean.where}")
EventUtils.event("Notification_Popup_Start", "where=${bean.where}")
// 尝试从队列中获取元素,最多等待 5 秒
var bean = sendBeanBlockingQueue.poll(5, TimeUnit.SECONDS)
// Log.e(TAG, "bean=$bean")
if (bean != null) {
LogEx.logDebug(TAG, "sendNotificationIfCan where=${bean.where}")
// EventUtils.event("Notification_Popup_Start", "where=${bean.where}")
if (!isUnLimit) {
//应用在前台不推
if (MyApplication.PAUSED_VALUE == 1 && !popupConfigBean.popupForegroundCanPush) {
LogEx.logDebug(TAG, "app Foreground")
continue
}
//总的限制条件
if (!canSendNotification(bean.context)) continue
//其他条推送是否可以推送
if (!bean.canSend.invoke()) continue
}
if (!isUnLimit) {
//应用在前台不推
if (MyApplication.PAUSED_VALUE == 1 && !popupConfigBean.popupForegroundCanPush) {
LogEx.logDebug(TAG, "app Foreground")
continue
//提前准备数据
if (bean.actionId == ACTION_ID_BEAN_MEDIA) {
val uiBean = nextNotificationInfoBean()
if (uiBean == null) {
LogEx.logDebug(TAG, "sendNotification no bean")
continue
}
bean.valueMap["bean"] = uiBean
}
//总的限制条件
if (!canSendNotification(bean.context)) continue
//其他条推送是否可以推送
if (!bean.canSend.invoke()) continue
bean = async(Dispatchers.Main) {
NotificationUiUtil.setNotification(bean)
}.await()
LogEx.logDebug(TAG, "sendNotificationCan where=${bean.where}")
//上报通知
EventUtils.event("Notification_Popup_${bean.where}", "actionId=${bean.actionId}")
//当天次数加一
dayPopupCount += 1
//推送时间
lastPopupTime = System.currentTimeMillis()
//这条推送回调
bean.sendSuccess?.invoke()
async(Dispatchers.Main) {
//发送悬停
NotificationHoverUtils.sendHoverNotification(bean)
}.await()
}
async(Dispatchers.Main) {
NotificationUiUtil.setNotification(bean)
}.await()
//上报通知
EventUtils.event("Notification_Popup", "where=${bean.where} actionId=${bean.actionId}")
//当天次数加一
dayPopupCount += 1
//推送时间
lastPopupTime = System.currentTimeMillis()
//这条推送回调
bean.sendSuccess?.invoke()
async(Dispatchers.Main) {
//发送悬停
NotificationHoverUtils.sendHoverNotification(bean)
}.await()
}
isStartSendQueue.set(false)
}
......@@ -146,14 +161,13 @@ object MyNotificationManager {
return false
}
//判断是否在时间区域
val start = popupConfigBean.popupStart
val end = popupConfigBean.popupEnd
//判断是否在限制时间区域
val calendar = Calendar.getInstance()
val currentHour = calendar.get(Calendar.HOUR_OF_DAY)
if (currentHour !in start until end) {
LogEx.logDebug("canSendNotification", "start-end currentHour=$currentHour start=$start end=$end")
EventUtils.event("Notification_Error", "start=$start end=$end currentHour=$currentHour")
val limitRangeTime = popupConfigBean.popupLimitRangeTime
if (limitRangeTime.contains(currentHour)) {
LogEx.logDebug("canSendNotification", "currentHour=$currentHour popupLimitRangeTime=$limitRangeTime")
EventUtils.event("Notification_Error", "currentHour=$currentHour popupLimitRangeTime=$limitRangeTime")
return false
}
......@@ -161,7 +175,7 @@ object MyNotificationManager {
val interval = popupConfigBean.popupInterval
val passedTime = System.currentTimeMillis() - lastPopupTime
if (passedTime < interval * 60 * 1000L) {
EventUtils.event("Notification_Error", "interval=$interval passedTime=$passedTime")
// EventUtils.event("Notification_Error", "interval=$interval passedTime=$passedTime")
LogEx.logDebug("canSendNotification", "interval=$interval passedTime=$passedTime")
return false
}
......@@ -172,21 +186,26 @@ object MyNotificationManager {
* 值测某些类型
*/
private var testWhere = listOf(
POPUP_WHERE_TIMER
// POPUP_WHERE_TIMER,
// POPUP_WHERE_LOCK,
// POPUP_WHERE_ALARM,
POPUP_WHERE_WORK_MANAGER
)
private val looper_actionId = listOf(
ACTION_ID_SCAN,
ACTION_ID_BEAN_MEDIA,
)
private var actionIdList = arrayListOf<String>()
private fun getNextActionId(): String {
if (BuildConfig.DEBUG) {
return ACTION_ID_BEAN_MEDIA
}
if (actionIdList.isEmpty()) {
actionIdList.addAll(looper_actionId)
}
val next = actionIdList[0]
actionIdList.removeAt(0)
if (BuildConfig.DEBUG) {
return ACTION_ID_SCAN
}
return next
}
}
\ No newline at end of file
package com.base.appzxhy.push.notification
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.graphics.BitmapShader
import android.graphics.Canvas
import android.graphics.Paint
import android.graphics.Path
import android.graphics.RectF
import android.graphics.Shader
import androidx.core.graphics.createBitmap
import com.base.appzxhy.bean.NotificationInfoBean
import com.base.appzxhy.helper.PushApiUtils
import com.google.gson.Gson
import com.google.gson.reflect.TypeToken
import com.mbridge.msdk.thrid.okhttp.OkHttpClient
import com.mbridge.msdk.thrid.okhttp.Request
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import java.io.InputStream
import java.util.LinkedList
/**
* 处理推送要到bean类
*/
object NotificationBeanUtils {
var notificationQueue = LinkedList<NotificationInfoBean>()
private fun getNotificationInfoBeanList() {
try {
val gson = Gson()
val type = object : TypeToken<List<NotificationInfoBean>>() {}.type
val list = gson.fromJson<List<NotificationInfoBean>>(PushApiUtils.notificationInfoJson, type)
notificationQueue.addAll(list)
} catch (e: Exception) {
}
}
suspend fun nextNotificationInfoBean(): NotificationInfoBean? {
if (notificationQueue.isEmpty()) {
getNotificationInfoBeanList()
}
val bean = notificationQueue.poll()
bean?.bitmap = downloadAndApplyRoundedCorners(bean.imageUrl)
return bean
}
suspend fun downloadAndApplyRoundedCorners(url: String): Bitmap? {
return withContext(Dispatchers.IO) {
try {
val client = OkHttpClient()
val request = Request.Builder().url(url).build()
val response = client.newCall(request).execute()
if (!response.isSuccessful) {
return@withContext null
}
val inputStream: InputStream = response.body()!!.byteStream()
val bitmap = BitmapFactory.decodeStream(inputStream)
val roundedBitmap = getRoundedCornersBitmap(bitmap, 8f)
roundedBitmap
} catch (e: Exception) {
e.printStackTrace()
null
}
}
}
fun getRoundedCornersBitmap(source: Bitmap, radius: Float): Bitmap {
val width = source.width
val height = source.height
val bitmap = createBitmap(width, height)
val canvas = Canvas(bitmap)
val paint = Paint().apply {
isAntiAlias = true
setShader(BitmapShader(source, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP))
}
val path = Path()
val rect = RectF(0f, 0f, width.toFloat(), height.toFloat())
path.addRoundRect(rect, radius, radius, Path.Direction.CW)
path.close()
canvas.drawPath(path, paint)
return bitmap
}
}
\ No newline at end of file
package com.base.appzxhy.fcm.notification
package com.base.appzxhy.push.notification
import android.os.Handler
import android.os.HandlerThread
import com.base.appzxhy.MyApplication
import com.base.appzxhy.bean.NotificationSendBean
import com.base.appzxhy.bean.config.PopupConfigBean.Companion.popupConfigBean
import com.base.appzxhy.utils.LogEx
/**
* 悬停
* 通知悬停
*/
object NotificationHoverUtils {
......@@ -46,7 +47,7 @@ object NotificationHoverUtils {
if (MyApplication.PAUSED_VALUE == 1) {
handler?.removeCallbacksAndMessages(null)
} else {
NotificationUiUtil.setNotification(sendBean)
NotificationUiUtil.sendCustomNotification(sendBean)
}
}, time)
}
......
package com.base.appzxhy.fcm.notification
package com.base.appzxhy.push.notification
import android.app.NotificationChannel
import android.app.NotificationManager
......@@ -11,8 +11,11 @@ import android.widget.RemoteViews
import androidx.core.app.NotificationCompat
import androidx.core.graphics.drawable.IconCompat
import com.base.appzxhy.R
import com.base.appzxhy.bean.NotificationInfoBean
import com.base.appzxhy.bean.NotificationSendBean
import com.base.appzxhy.ui.start.StartActivity
import com.base.appzxhy.bean.NotificationSendBean.Companion.ACTION_ID_BEAN_MEDIA
import com.base.appzxhy.bean.config.PopupConfigBean
import com.base.appzxhy.ui.splash.SplashActivity
import kotlin.random.Random
/**
......@@ -20,25 +23,49 @@ import kotlin.random.Random
*/
object NotificationUiUtil {
private val CHANNEL_ID = "video downloader saver Channel"
private val CHANNEL_NAME = "video downloader saver Channel NAME"
private val CHANNEL_ID = "San Qr Channel"
private val CHANNEL_NAME = "San Qr Channel NAME"
fun setNotification(sendBean: NotificationSendBean) {
/**
* 这部分准备UI的代码不能放入IO
*/
fun setNotification(sendBean: NotificationSendBean): NotificationSendBean {
val context = sendBean.context
when (sendBean.actionId) {
ACTION_ID_BEAN_MEDIA -> {
val bean = sendBean.valueMap.get("bean") as NotificationInfoBean
val smallRemoteViews = RemoteViews(context.packageName, R.layout.notification_small).apply {
setTextViewText(R.id.tvTitle, bean.title)
setTextViewText(R.id.tvContent, bean.content)
setImageViewBitmap(R.id.ivImage, bean.bitmap)
}
val bigRemoteViews =
RemoteViews(context.packageName, R.layout.notification_big).apply {
setTextViewText(R.id.tvTitle, bean.title)
setTextViewText(R.id.tvContent, bean.content)
setImageViewBitmap(R.id.ivImage, bean.bitmap)
}
sendBean.smallRemoteViews = smallRemoteViews
sendBean.bigRemoteViews = bigRemoteViews
}
}
val intent = Intent(sendBean.context, StartActivity::class.java)
if (PopupConfigBean.popupConfigBean.isDifferNotificationId) {
sendBean.notificationId = Random.nextInt(1000, 2000)
}
val intent = Intent(sendBean.context, SplashActivity::class.java)
val bean = sendBean.valueMap["bean"] as NotificationInfoBean
intent.putExtra("actionId", sendBean.actionId)
intent.putExtra("linkUrl", bean.linkUrl)
// intent.putExtra("notificationId", sendBean.notificationId)
sendBean.intent = intent
sendCustomNotification(sendBean)
return sendBean
}
private fun sendCustomNotification(
fun sendCustomNotification(
sendBean: NotificationSendBean
) {
val notificationManager = sendBean.context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
val context = sendBean.context
......@@ -60,7 +87,7 @@ object NotificationUiUtil {
//设置状态栏内的小图标
val smallIcon = IconCompat.createFromIcon(
context, Icon.createWithResource(
context, R.drawable.log_svg
context, R.mipmap.logo_r
)
)
smallIcon?.let {
......
package com.base.appzxhy.push.receiver
import android.annotation.SuppressLint
import android.app.AlarmManager
import android.app.PendingIntent
import android.app.job.JobService
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import com.base.appzxhy.BuildConfig
import com.base.appzxhy.bean.NotificationSendBean
import com.base.appzxhy.bean.config.PopupConfigBean
import com.base.appzxhy.business.helper.EventUtils
import com.base.appzxhy.push.notification.MyNotificationManager
import com.base.appzxhy.push.work.RepeatingWorker
import com.base.appzxhy.utils.LogEx
import java.util.Calendar
/**
*
*/
class AlarmReceiver : BroadcastReceiver() {
private val TAG = "AlarmJobReceiver"
companion object {
fun startAlarm(context: Context, hour: Int) {
// context.startJob()
//闹钟定时任务
val alarmManager = context.getSystemService(JobService.ALARM_SERVICE) as AlarmManager
val pendingIntent = PendingIntent.getBroadcast(
context, hour, Intent(context, AlarmReceiver::class.java),
PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT
)
val calendar = Calendar.getInstance()
calendar.set(Calendar.HOUR_OF_DAY, 6)
calendar.set(Calendar.MINUTE, 0)
calendar.set(Calendar.SECOND, 0)
val oneHour = 60 * 60 * 1000L
var delay = oneHour * hour
if (BuildConfig.DEBUG) {
delay = 1 * 60 * 1000L
}
alarmManager.setRepeating(
AlarmManager.RTC_WAKEUP, calendar.timeInMillis, delay, pendingIntent
)
RepeatingWorker.Companion.schedulePeriodicWork(context)
}
}
@SuppressLint("UnsafeProtectedBroadcastReceiver")
override fun onReceive(context: Context, intent: Intent?) {
LogEx.logDebug(TAG, "AlarmJobReceiver onReceive")
EventUtils.event("alarm_push")
if (PopupConfigBean.popupConfigBean.alarmS) {
MyNotificationManager.submitSendBean(NotificationSendBean(context, NotificationSendBean.Companion.POPUP_WHERE_ALARM))
}
RepeatingWorker.Companion.schedulePeriodicWork(context)
}
}
\ No newline at end of file
package com.base.appzxhy.fcm.receiver
package com.base.appzxhy.push.receiver
import android.content.BroadcastReceiver
import android.content.Context
......@@ -8,11 +8,7 @@ import android.os.Build
import com.base.appzxhy.bean.NotificationSendBean
import com.base.appzxhy.bean.NotificationSendBean.Companion.POPUP_WHERE_LOCK
import com.base.appzxhy.bean.config.PopupConfigBean.Companion.popupConfigBean
import com.base.appzxhy.fcm.notification.MyNotificationManager
import com.base.appzxhy.helper.EventUtils
import com.base.appzxhy.utils.AppPreferences
import com.base.appzxhy.utils.KotlinExt.currentDate
import com.base.appzxhy.utils.LogEx
import com.base.appzxhy.push.notification.MyNotificationManager
import java.util.Objects
class ScreenStatusReceiver : BroadcastReceiver() {
......@@ -31,12 +27,7 @@ class ScreenStatusReceiver : BroadcastReceiver() {
isSecureLockActive = false
if (isDeviceInteractive && !isSecureLockActive) {
if (popupConfigBean.screenS) {
val sendBean = NotificationSendBean(
context,
POPUP_WHERE_LOCK,
canSend = { canScreenStatusReceiverPush() },
sendSuccess = { saveScreenPushedData() })
MyNotificationManager.submitSendBean(sendBean)
MyNotificationManager.submitSendBean(NotificationSendBean(context, POPUP_WHERE_LOCK))
}
}
}
......@@ -64,70 +55,5 @@ class ScreenStatusReceiver : BroadcastReceiver() {
}
}
/**
* 当前天推送数量
*/
private var todayScreenPush = 0
get() {
return AppPreferences.getInstance().getInt("todayScreenPush_${currentDate()}", field)
}
set(value) {
field = value
AppPreferences.getInstance().put("todayScreenPush_${currentDate()}", value, true)
}
/**
* 上次成功推送
*/
private var screenLastPushTime = 0L
get() {
return AppPreferences.getInstance().getLong("screenLastPushTime", field)
}
set(value) {
field = value
AppPreferences.getInstance().put("screenLastPushTime", value, true)
}
/**
* 解锁是否可推送
* 总的限制条件判断后才判断
*/
fun canScreenStatusReceiverPush(): Boolean {
if (!popupConfigBean.popupStatus) return false
val popupScreenCount = popupConfigBean.popupScreenCount
val flag1 = todayScreenPush <= popupScreenCount
val minute = 60 * 1000L
val interval = popupConfigBean.popupScreenInterval
val passTime = System.currentTimeMillis() - screenLastPushTime
val flag2 = screenLastPushTime == 0L || passTime > interval * minute
val flag = flag1 && flag2
if (!flag) {
EventUtils.event(
"Notification_Error", "todayScreenPush=$todayScreenPush " +
"popupScreenCount=$popupScreenCount where=$POPUP_WHERE_LOCK"
)
LogEx.logDebug(
"canSendNotification",
"Notification_Error todayScreenPush=$todayScreenPush " +
"popupScreenCount=$popupScreenCount where=$POPUP_WHERE_LOCK"
)
}
return flag
}
/**
* 推送成功后保存值
*/
fun saveScreenPushedData() {
todayScreenPush += 1
screenLastPushTime = System.currentTimeMillis()
}
}
}
\ No newline at end of file
package com.base.appzxhy.fcm.timer
package com.base.appzxhy.push.timer
import android.util.Log
import com.base.appzxhy.MyApplication
import com.base.appzxhy.bean.NotificationSendBean
import com.base.appzxhy.bean.NotificationSendBean.Companion.POPUP_WHERE_TIMER
import com.base.appzxhy.bean.config.PopupConfigBean.Companion.popupConfigBean
import com.base.appzxhy.fcm.notification.MyNotificationManager
import com.base.appzxhy.fcm.receiver.ScreenStatusReceiver
import com.base.appzxhy.utils.LogEx.logDebug
import com.base.appzxhy.push.notification.MyNotificationManager
import com.base.appzxhy.push.receiver.ScreenStatusReceiver
import java.util.Timer
import java.util.TimerTask
......@@ -17,27 +17,28 @@ class TimerManager private constructor() {
private var taskTimer: Timer? = null
private var isTaskTimerActive: Boolean = false
fun createNewTask(): TimerTask {
val task: TimerTask = object : TimerTask() {
override fun run() {
Log.e(TAG, "TimerTask run")
// 确保设备处于交互状态,未锁定,且应用未暂停
val deviceRunning = (ScreenStatusReceiver.isDeviceInteractive &&
!ScreenStatusReceiver.isSecureLockActive &&
MyApplication.PAUSED_VALUE != 1)
if (deviceRunning || popupConfigBean.popupForegroundCanPush) {
MyNotificationManager.submitSendBean(NotificationSendBean(MyApplication.appContext, POPUP_WHERE_TIMER))
}
}
}
return task
}
fun scheduleTask(delay: Long, period: Long) {
logDebug(TAG, "scheduleTask")
synchronized(TimerManager::class.java) {
ensureTimerIsStopped() // 确保定时器未运行
taskTimer = Timer() // 创建新的 Timer 实例
val task: TimerTask = object : TimerTask() {
override fun run() {
logDebug(TAG, "scheduleTask run")
// 确保设备处于交互状态,未锁定,且应用未暂停
if (ScreenStatusReceiver.isDeviceInteractive
&& !ScreenStatusReceiver.isSecureLockActive
&& MyApplication.PAUSED_VALUE != 1
) {
logDebug(TAG, "scheduleTask send")
val sendBean =
NotificationSendBean(MyApplication.appContext, POPUP_WHERE_TIMER, canSend = { true }, sendSuccess = {})
MyNotificationManager.submitSendBean(sendBean)
}
}
}
taskTimer?.schedule(task, delay, period) // 调度任务
taskTimer?.schedule(createNewTask(), delay, period) // 调度任务
isTaskTimerActive = true // 设置定时器状态为活跃
}
}
......@@ -62,7 +63,6 @@ class TimerManager private constructor() {
private val instance: TimerManager by lazy((LazyThreadSafetyMode.SYNCHRONIZED)) { TimerManager() }
fun changeTimer() {
if (!popupConfigBean.timerS) {
......@@ -74,7 +74,7 @@ class TimerManager private constructor() {
val isTaskTimerActive = instance.isTaskTimerActive
if (!isTaskTimerActive) {
instance.scheduleTask(
(timerDelay * 60 * 1000).toLong(),
(timerDelay * 1000).toLong(),
(timerInterval * 60 * 1000).toLong()
)
}
......
package com.base.appzxhy.fcm.work
package com.base.appzxhy.push.work
import android.content.Context
import androidx.work.Constraints
......@@ -10,8 +10,9 @@ import androidx.work.Worker
import androidx.work.WorkerParameters
import com.base.appzxhy.bean.NotificationSendBean
import com.base.appzxhy.bean.NotificationSendBean.Companion.POPUP_WHERE_WORK_MANAGER
import com.base.appzxhy.fcm.notification.MyNotificationManager
import com.base.appzxhy.helper.EventUtils
import com.base.appzxhy.bean.config.PopupConfigBean
import com.base.appzxhy.push.notification.MyNotificationManager
import com.base.appzxhy.business.helper.EventUtils
import java.util.concurrent.TimeUnit
class RepeatingWorker(val appContext: Context, workerParams: WorkerParameters) : Worker(appContext, workerParams) {
......@@ -21,8 +22,9 @@ class RepeatingWorker(val appContext: Context, workerParams: WorkerParameters) :
// 例如,更新UI,发送网络请求等
EventUtils.event("workManager_live")
try {
val sendBean = NotificationSendBean(appContext, POPUP_WHERE_WORK_MANAGER, canSend = { true }, sendSuccess = {})
MyNotificationManager.submitSendBean(sendBean)
if (PopupConfigBean.popupConfigBean.workS) {
MyNotificationManager.submitSendBean(NotificationSendBean(appContext, POPUP_WHERE_WORK_MANAGER))
}
} catch (e: Exception) {
EventUtils.event("WorkManager Error")
}
......
package com.base.appzxhy.service
package com.base.appzxhy.business.service
import android.app.NotificationManager
import android.app.job.JobInfo
......@@ -13,8 +13,8 @@ import android.os.Build
import android.os.CountDownTimer
import androidx.work.Configuration
import com.base.appzxhy.BuildConfig
import com.base.appzxhy.helper.EventUtils
import com.base.appzxhy.service.StayNotification.createPermanentNotification
import com.base.appzxhy.business.helper.EventUtils
import com.base.appzxhy.business.service.StayNotification.createPermanentNotification
import com.base.appzxhy.utils.LogEx
......@@ -81,7 +81,7 @@ class StayJobService : JobService() {
private fun notifyForeground() {
val notificationManager =
getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
getSystemService(NOTIFICATION_SERVICE) as NotificationManager
notificationManager.notify(
NOTIFICATION_STAY_ID,
......
package com.base.appzxhy.service
package com.base.appzxhy.business.service
import android.app.Notification
import android.app.NotificationChannel
......@@ -13,7 +13,7 @@ import android.widget.RemoteViews
import androidx.core.app.NotificationCompat
import androidx.core.graphics.drawable.IconCompat
import com.base.appzxhy.R
import com.base.appzxhy.service.StayJobService.Companion.NOTIFICATION_STAY_ID
import com.base.appzxhy.business.service.StayJobService.Companion.NOTIFICATION_STAY_ID
import com.base.appzxhy.ui.main.MainActivity
object StayNotification {
......
package com.base.appzxhy.drouterimp
import android.app.Activity
import android.content.Intent
import com.base.appzxhy.ui.main.MainActivity
import com.didi.drouter.annotation.Service
import com.koko.drouter.app.AppService
import java.util.Locale
@Service(function = [AppService::class])
class AppServiceImp : AppService {
override fun getCurrentSpLocal(): Locale {
return Locale.getDefault()
}
override fun changeActivityLanguage(activity: Activity, currentLocale: Locale, changeAction: () -> Unit): Boolean {
return false
}
override fun finishToMainTop(activity: Activity) {
val intent = Intent(activity, MainActivity::class.java)
intent.putExtra("where", "finishToMainTop")
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
activity.startActivity(intent)
activity.finish()
}
}
\ No newline at end of file
package com.base.appzxhy.drouterimp
import android.app.Activity
import com.base.appzxhy.helper.EventUtils
import com.didi.drouter.annotation.Service
import com.koko.drouter.app.AppService
import com.koko.drouter.event.EventService
@Service(function = [EventService::class])
class EventServiceImp : EventService {
override fun eventActivity(activity: Activity) {
super.eventActivity(activity)
EventUtils.event("page_${javaClass.simpleName}")
}
}
\ No newline at end of file
package com.base.appzxhy.drouterimp
import android.app.Activity
import com.base.appzxhy.ads.AdsMgr
import com.base.appzxhy.ads.AdsShowCallBack
import com.base.appzxhy.bean.config.AdConfigBean
import com.didi.drouter.annotation.Service
import com.koko.drouter.ad.InterAdService
@Service(function = [InterAdService::class])
class InterAdServiceImp : InterAdService {
override fun showInterAd(activity: Activity, interWhere: String, callBack: () -> Unit) {
if (AdConfigBean.adsConfigBean.functionInShowAd) {
AdsMgr.showInsert(activity, object : AdsShowCallBack() {
override fun next() {
callBack.invoke()
}
})
} else {
callBack.invoke()
}
}
override fun showInterAdBack(activity: Activity, interWhere: String, callBack: () -> Unit) {
if (AdConfigBean.adsConfigBean.functionBackShowAd) {
AdsMgr.showInsert(activity, object : AdsShowCallBack() {
override fun next() {
callBack.invoke()
}
})
} else {
callBack.invoke()
}
}
}
\ No newline at end of file
package com.base.appzxhy.drouterimp
import android.app.Activity
import android.widget.FrameLayout
import com.base.appzxhy.R
import com.base.appzxhy.ads.AdsMgr
import com.base.appzxhy.utils.LogEx
import com.didi.drouter.annotation.Service
import com.koko.drouter.ad.NativeBatteryType
import com.koko.drouter.ad.NativeAdService
@Service(function = [NativeAdService::class])
class NativeAdServiceImp : NativeAdService {
private val TAG = "NativeServiceImp"
override fun showNative(activity: Activity, flAd: FrameLayout, nativeType: String) {
LogEx.logDebug(TAG, "activity=${activity::class.java.simpleName}")
val admobLayout = when (nativeType) {
NativeBatteryType -> R.layout.layout_admob_native_custom
else -> R.layout.layout_admob_native_custom
}
val maxLayout = R.layout.layout_max_native_custom
// AdsMgr.showNative(activity, flAd, admobLayout, maxLayout)
}
}
\ No newline at end of file
package com.base.appzxhy.fcm.alarm
import android.annotation.SuppressLint
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import com.base.appzxhy.bean.NotificationSendBean
import com.base.appzxhy.bean.NotificationSendBean.Companion.POPUP_WHERE_ALARM
import com.base.appzxhy.fcm.notification.MyNotificationManager
import com.base.appzxhy.fcm.work.RepeatingWorker.Companion.schedulePeriodicWork
import com.base.appzxhy.helper.EventUtils
import com.base.appzxhy.utils.LogEx
/**
*
*/
class AlarmReceiver : BroadcastReceiver() {
private val TAG = "AlarmJobReceiver"
@SuppressLint("UnsafeProtectedBroadcastReceiver")
override fun onReceive(context: Context, intent: Intent?) {
LogEx.logDebug(TAG, "AlarmJobReceiver onReceive")
EventUtils.event("alarm_push")
val sendBean = NotificationSendBean(context, POPUP_WHERE_ALARM, canSend = { true }, sendSuccess = {})
MyNotificationManager.submitSendBean(sendBean)
schedulePeriodicWork(context)
return
}
}
\ No newline at end of file
import android.app.AlarmManager
import android.app.PendingIntent
import android.app.job.JobService
import android.content.Context
import android.content.Intent
import com.base.appzxhy.BuildConfig
import com.base.appzxhy.fcm.alarm.AlarmReceiver
import com.base.appzxhy.fcm.work.RepeatingWorker.Companion.schedulePeriodicWork
import java.util.Calendar
object AlarmUtils {
fun startAlarm(context: Context, hour: Int) {
// context.startJob()
//闹钟定时任务
val alarmManager = context.getSystemService(JobService.ALARM_SERVICE) as AlarmManager
val pendingIntent = PendingIntent.getBroadcast(
context, hour, Intent(context, AlarmReceiver::class.java),
PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT
)
val calendar = Calendar.getInstance()
calendar.set(Calendar.HOUR_OF_DAY, 6)
calendar.set(Calendar.MINUTE, 0)
calendar.set(Calendar.SECOND, 0)
val oneHour = 60 * 60 * 1000L
var delay = oneHour * hour
if (BuildConfig.DEBUG) {
delay = 1 * 60 * 1000L
}
alarmManager.setRepeating(
AlarmManager.RTC_WAKEUP, calendar.timeInMillis, delay, pendingIntent
)
schedulePeriodicWork(context)
}
}
\ No newline at end of file
package com.base.appzxhy.fcm.receiver
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.os.BatteryManager
import android.os.Build
import com.base.appzxhy.bean.NotificationSendBean
import com.base.appzxhy.bean.NotificationSendBean.Companion.POPUP_WHERE_BATTERY
import com.base.appzxhy.bean.config.PopupConfigBean.Companion.popupConfigBean
import com.base.appzxhy.fcm.notification.MyNotificationManager
import com.base.appzxhy.helper.EventUtils
import com.base.appzxhy.utils.AppPreferences
import com.base.appzxhy.utils.KotlinExt.currentDate
import com.base.appzxhy.utils.LogEx
import kotlin.math.absoluteValue
/**
*电量监听
*/
class BatteryStatusReceiver() : BroadcastReceiver() {
companion object {
private val TAG = "BatteryStatusReceiver"
fun registerBatteryStatusReceiver(context: Context) {
val intentFilter = IntentFilter().apply {
addAction(Intent.ACTION_BATTERY_CHANGED)
addAction(Intent.ACTION_POWER_CONNECTED)
addAction(Intent.ACTION_POWER_DISCONNECTED)
}
val applicationContext = context.applicationContext
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
applicationContext.registerReceiver(
BatteryStatusReceiver(),
intentFilter,
Context.RECEIVER_EXPORTED
)
} else {
applicationContext.registerReceiver(BatteryStatusReceiver(), intentFilter)
}
}
/**
* 当前天推送数量
*/
private var todayBatteryPush = 0
get() {
return AppPreferences.getInstance().getInt("todayBatteryPush_${currentDate()}", field)
}
set(value) {
field = value
AppPreferences.getInstance().put("todayBatteryPush_${currentDate()}", value, true)
}
/**
* 上次成功推送
*/
private var batteryLastPushTime = 0L
get() {
return AppPreferences.getInstance().getLong("batteryLastPushTime", field)
}
set(value) {
field = value
AppPreferences.getInstance().put("batteryLastPushTime", value, true)
}
/**
* 电池电量是否可推送
* 总的限制条件判断后才判断
*/
fun canBatteryStatusReceiverPush(): Boolean {
if (!popupConfigBean.batteryS) return false
val popupBatteryCount = popupConfigBean.popupBatteryValue
val flag1 = todayBatteryPush <= popupBatteryCount
val minute = 60 * 1000L
val interval = popupConfigBean.popupBatteryInterval
val passTime = System.currentTimeMillis() - batteryLastPushTime
val flag2 = batteryLastPushTime == 0L || passTime > interval * minute
val flag = flag1 && flag2
if (!flag) {
EventUtils.event(
"Notification_Error", "todayBatteryPush=$todayBatteryPush " +
"popupBatteryCount=$popupBatteryCount where=$POPUP_WHERE_BATTERY"
)
LogEx.logDebug(
"canSendNotification", "Notification_Error todayBatteryPush=$todayBatteryPush " +
"popupBatteryCount=$popupBatteryCount where=$POPUP_WHERE_BATTERY"
)
}
return flag
}
/**
* 推送成功后保存值
*/
fun saveBatteryPushedData() {
todayBatteryPush += 1
batteryLastPushTime = System.currentTimeMillis()
}
}
private var currentBatteryPercentage = 0f
override fun onReceive(context: Context, intent: Intent?) {
val action = intent?.action
when (action) {
Intent.ACTION_BATTERY_CHANGED -> {
val batteryLevel = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, -1)
val batteryScale = intent.getIntExtra(BatteryManager.EXTRA_SCALE, -1)
val batteryPercentage = (batteryLevel / batteryScale.toFloat()) * 100
//避免频繁触发
val changeValue = currentBatteryPercentage - batteryPercentage
if (batteryPercentage < popupConfigBean.popupBatteryValue && changeValue.absoluteValue >= 1f) {
//推送次数没有达到限制并且展示的最小时间间隔大于配置时间(分钟)
LogEx.logDebug(TAG, "onReceive changed")
val sendBean = NotificationSendBean(context, POPUP_WHERE_BATTERY,
canSend = {
canBatteryStatusReceiverPush()
},
sendSuccess = {
saveBatteryPushedData()
})
MyNotificationManager.submitSendBean(sendBean)
}
currentBatteryPercentage = batteryPercentage
}
Intent.ACTION_POWER_CONNECTED -> {
val sendBean = NotificationSendBean(context, POPUP_WHERE_BATTERY,
canSend = {
canBatteryStatusReceiverPush()
},
sendSuccess = {
saveBatteryPushedData()
})
MyNotificationManager.submitSendBean(sendBean)
}
}
}
}
\ No newline at end of file
package com.base.appzxhy.fcm.receiver
import android.annotation.SuppressLint
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import com.base.appzxhy.bean.NotificationSendBean
import com.base.appzxhy.bean.NotificationSendBean.Companion.POPUP_WHERE_FILE_JOB
import com.base.appzxhy.fcm.notification.MyNotificationManager
class FileJobReceiver : BroadcastReceiver() {
@SuppressLint("UnsafeProtectedBroadcastReceiver")
override fun onReceive(context: Context?, intent: Intent?) {
// context?.startJob()
context?.let {
val sendBean = NotificationSendBean(context, POPUP_WHERE_FILE_JOB, canSend = { true }, sendSuccess = {})
MyNotificationManager.submitSendBean(sendBean)
}
}
}
\ No newline at end of file
package com.base.appzxhy.fcm.receiver
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.os.Build
import com.base.appzxhy.bean.NotificationSendBean
import com.base.appzxhy.bean.NotificationSendBean.Companion.POPUP_WHERE_PACKAGE
import com.base.appzxhy.bean.config.PopupConfigBean.Companion.popupConfigBean
import com.base.appzxhy.fcm.notification.MyNotificationManager
import com.base.appzxhy.helper.EventUtils
import com.base.appzxhy.utils.AppPreferences
import com.base.appzxhy.utils.KotlinExt.currentDate
import com.base.appzxhy.utils.LogEx
class PackageStatusReceiver() : BroadcastReceiver() {
companion object {
private val TAG = "PackageStatusReceiver"
fun registerPackageStatusReceiver(context: Context) {
LogEx.logDebug(TAG, "registerPackageStatusReceiver")
val intentFilter = IntentFilter().apply {
addAction(Intent.ACTION_PACKAGE_ADDED)
addAction(Intent.ACTION_PACKAGE_REMOVED)
addDataScheme("package")
}
val applicationContext = context.applicationContext
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
applicationContext.registerReceiver(
PackageStatusReceiver(),
intentFilter,
Context.RECEIVER_EXPORTED
)
} else {
applicationContext.registerReceiver(PackageStatusReceiver(), intentFilter)
}
}
/**
* 当前天推送数量
*/
private var todayPackagePush = 0
get() {
return AppPreferences.getInstance().getInt("todayPackagePush_${currentDate()}", field)
}
set(value) {
field = value
AppPreferences.getInstance().put("todayPackagePush_${currentDate()}", value, true)
}
/**
* 上次成功推送
*/
private var packageLastPushTime = 0L
get() {
return AppPreferences.getInstance().getLong("packageLastPushTime", field)
}
set(value) {
field = value
AppPreferences.getInstance().put("packageLastPushTime", value, true)
}
/**
* 安装卸载是否可推送
* 总的限制条件判断后才判断
*/
fun canPackageStatusReceiverPush(): Boolean {
if (!popupConfigBean.packageS) return false
val popupPackageCount = popupConfigBean.popupPackageCount
val flag1 = todayPackagePush <= popupPackageCount
val minute = 60 * 1000L
val interval = popupConfigBean.popupPackageInterval
val passTime = System.currentTimeMillis() - packageLastPushTime
val flag2 = packageLastPushTime == 0L || passTime > interval * minute
val flag = flag1 && flag2
if (!flag) {
EventUtils.event(
"Notification_Error", "todayPackagePush=$todayPackagePush " +
"popupPackageCount=$popupPackageCount where=$POPUP_WHERE_PACKAGE"
)
LogEx.logDebug(
"canSendNotification", "Notification_Error todayPackagePush=$todayPackagePush " +
"popupPackageCount=$popupPackageCount where=$POPUP_WHERE_PACKAGE"
)
}
return flag
}
/**
* 推送成功后保存值
*/
fun savePackagePushedData() {
todayPackagePush += 1
packageLastPushTime = System.currentTimeMillis()
}
}
override fun onReceive(context: Context, intent: Intent?) {
val action = intent?.action
LogEx.logDebug(TAG, "onReceive action=$action")
if (action == Intent.ACTION_PACKAGE_ADDED || action == Intent.ACTION_PACKAGE_REMOVED) {
val sendBean = NotificationSendBean(context, POPUP_WHERE_PACKAGE, canSend = {
canPackageStatusReceiverPush()
}, sendSuccess = {
savePackagePushedData()
})
MyNotificationManager.submitSendBean(sendBean)
}
}
}
\ No newline at end of file
package com.base.appzxhy.utils
import android.content.Context
import android.net.ConnectivityManager
import android.net.Network
import android.net.NetworkCapabilities
import android.net.NetworkRequest
import android.util.Log
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.update
class VpnStatusListener(context: Context) {
private val TAG = "VpnStatusListener"
private val connectivityManager = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
private var isListening = false
private var currentVpnStatus: Boolean = false
var actionVpn: ((isVip: Boolean) -> Unit)? = null
companion object {
var vpnFlow: MutableStateFlow<Boolean> = MutableStateFlow(false)
}
private val networkCallback = object : ConnectivityManager.NetworkCallback() {
override fun onAvailable(network: Network) {
super.onAvailable(network)
val caps = connectivityManager.getNetworkCapabilities(network)
checkVpnStatus(caps)
}
override fun onLost(network: Network) {
super.onLost(network)
val caps = connectivityManager.getNetworkCapabilities(network)
checkVpnStatus(caps)
}
override fun onCapabilitiesChanged(network: Network, networkCapabilities: NetworkCapabilities) {
super.onCapabilitiesChanged(network, networkCapabilities)
checkVpnStatus(networkCapabilities)
}
}
fun startListen() {
if (!isListening) {
val networkRequest = NetworkRequest.Builder()
.addTransportType(NetworkCapabilities.TRANSPORT_VPN)
.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN)
.build()
connectivityManager.registerNetworkCallback(networkRequest, networkCallback)
isListening = true
}
}
fun stopListen() {
if (isListening) {
connectivityManager.unregisterNetworkCallback(networkCallback)
isListening = false
}
}
private fun checkVpnStatus(networkCapabilities: NetworkCapabilities?) {
val isVpn = networkCapabilities?.hasTransport(NetworkCapabilities.TRANSPORT_VPN) == true
// 这里可以根据isVpn的值进行相应处理,比如更新UI等
if (currentVpnStatus != isVpn) {
currentVpnStatus = isVpn
Log.e(TAG, "VpnStatusListener =$isVpn")
actionVpn?.invoke(isVpn)
vpnFlow.update { isVpn }
}
}
}
\ No newline at end of file
......@@ -9,9 +9,3 @@ plugins {
id("com.google.devtools.ksp") version "1.9.24-1.0.20" apply false
}
buildscript {
dependencies {
classpath(libs.drouter.plugin)
}
}
\ No newline at end of file
......@@ -54,12 +54,6 @@ applovin_facebook = { group = "com.applovin.mediation", name = "facebook-adapter
applovin_mintegral = { group = "com.applovin.mediation", name = "mintegral-adapter", version.ref = "mintegral" }
applovin_pangle = { group = "com.applovin.mediation", name = "bytedance-adapter", version.ref = "pangle" }
drouter-api = { module = "io.github.didi:drouter-api", version.ref = "drouterApi" }
drouter-plugin = { module = "io.github.didi:drouter-plugin", version.ref = "drouterPlugin" }
drouter-api-page = { module = "io.github.didi:drouter-api-page", version.ref = "drouterApiPage" }
drouter-api-process = { module = "io.github.didi:drouter-api-process", version.ref = "drouterApiProcess" }
[plugins]
android-application = { id = "com.android.application", version.ref = "agp" }
......
......@@ -28,5 +28,3 @@ dependencyResolutionManagement {
rootProject.name = "appzxhy"
include(":app")
include(":Drouter")
include(":BatteryInfo")
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