app 固定时间节点通知
This commit is contained in:
parent
5e5ba292bd
commit
8f038ca3ea
|
|
@ -227,6 +227,7 @@ class MainActivity : AppViewsActivity<ViewBinding, UiState, ViewModel>(), OnTabS
|
|||
"Notific_Type" to when (notifyFrom) {
|
||||
NotificationCheckController.NotificationType.UNLOCK.string -> 1
|
||||
NotificationCheckController.NotificationType.BACKGROUND.string -> 1
|
||||
NotificationCheckController.NotificationType.FIXTIMEPOINT.string -> 1
|
||||
NotificationCheckController.NotificationType.KEEPALIVE.string -> 1
|
||||
NotificationCheckController.NotificationType.FCM.string -> 3
|
||||
NotificationCheckController.NotificationType.RESIDENT.string -> 4
|
||||
|
|
@ -256,6 +257,7 @@ class MainActivity : AppViewsActivity<ViewBinding, UiState, ViewModel>(), OnTabS
|
|||
"Notific_Type" to when (intent.getStringExtra(LANDING_NOTIFICATION_FROM).orEmpty()) {
|
||||
NotificationCheckController.NotificationType.UNLOCK.string -> 1
|
||||
NotificationCheckController.NotificationType.BACKGROUND.string -> 1
|
||||
NotificationCheckController.NotificationType.FIXTIMEPOINT.string -> 1
|
||||
NotificationCheckController.NotificationType.KEEPALIVE.string -> 1
|
||||
NotificationCheckController.NotificationType.FCM.string -> 3
|
||||
NotificationCheckController.NotificationType.RESIDENT.string -> 4
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
"unlock_push_interval": "10",
|
||||
"background_push_interval": "1",
|
||||
"hover_duration_strategy_switch": 1,
|
||||
"hover_duration_loop_count": 8888888,
|
||||
"hover_duration_loop_count": 88888888,
|
||||
"new_user_cooldown": 0,
|
||||
"do_not_disturb_start": "02:00",
|
||||
"do_not_disturb_end": "08:00",
|
||||
|
|
@ -16,7 +16,7 @@
|
|||
"unlock_push_interval": "10",
|
||||
"background_push_interval": "1",
|
||||
"hover_duration_strategy_switch": 1,
|
||||
"hover_duration_loop_count": 8888888,
|
||||
"hover_duration_loop_count": 88888888,
|
||||
"new_user_cooldown": "24",
|
||||
"do_not_disturb_start": "02:00",
|
||||
"do_not_disturb_end": "08:00",
|
||||
|
|
|
|||
|
|
@ -0,0 +1,8 @@
|
|||
package com.remax.notification.beans
|
||||
|
||||
import com.ama.core.architecture.util.DateUtil
|
||||
|
||||
data class FixTimeRecord (
|
||||
private val mDayStartTime: Long = DateUtil.getTodayStartTimeMs(),
|
||||
private val mHasShowTimePointList: MutableList<Int> = mutableListOf()
|
||||
)
|
||||
|
|
@ -282,6 +282,87 @@ class GeneralModelManager() {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
class FixTimeModelManager() {
|
||||
|
||||
fun getModel(context: Context,type:NotificationCheckController.NotificationType): GeneralNotificationData {
|
||||
val notificationId = type2notificationId[NotificationType.GENERAL] ?: 0
|
||||
val data = PushContentController.getNextPushContent()!!
|
||||
val title = data.title
|
||||
val content = data.desc
|
||||
val pendingIntent = entryPointPendingIntent(context, notificationId) {
|
||||
it.putExtra(LANDING_NOTIFICATION_ACTION, data.actionType)
|
||||
it.putExtra(LANDING_NOTIFICATION_FROM, type.string)
|
||||
it.putExtra(LANDING_NOTIFICATION_TITLE, title)
|
||||
it.putExtra(LANDING_NOTIFICATION_CONTENT, content)
|
||||
}
|
||||
|
||||
val badgeCount = Random.nextInt(1, 100).toString()
|
||||
val contentView = NotificationRemoteViewsBuilder(
|
||||
context.packageName,
|
||||
R.layout.layout_notification_beauty_12,
|
||||
R.layout.layout_notification_beauty
|
||||
)
|
||||
.setImageViewResource(
|
||||
R.id.iv, when (data.iconType) {
|
||||
PushContent.ICON_TYPE_PHOTO -> R.drawable.ic_noti_photo
|
||||
PushContent.ICON_TYPE_VIDEO -> R.drawable.ic_noti_video
|
||||
PushContent.ICON_TYPE_DOCUMENT -> R.drawable.ic_noti_document
|
||||
PushContent.ICON_TYPE_AUDIO -> R.drawable.ic_noti_audio
|
||||
PushContent.ICON_TYPE_SCREENSHOT -> R.drawable.ic_noti_shot
|
||||
PushContent.ICON_TYPE_RECOVERED -> R.drawable.ic_noti_recover
|
||||
else -> R.drawable.ic_noti_photo
|
||||
}
|
||||
)
|
||||
.setTextViewText(R.id.tvCount, badgeCount)
|
||||
.setTextViewText(R.id.tvTitle, title)
|
||||
.setTextViewText(R.id.tvDesc, content)
|
||||
.setTextViewText(
|
||||
R.id.tvAction, when (data.iconType) {
|
||||
PushContent.ICON_TYPE_SCREENSHOT -> StringUtils.getString(R.string.noti_clean)
|
||||
else -> StringUtils.getString(R.string.noti_recovery)
|
||||
}
|
||||
)
|
||||
.build()
|
||||
|
||||
val bigContentView = NotificationRemoteViewsBuilder(
|
||||
context.packageName,
|
||||
R.layout.layout_notification_beauty_12,
|
||||
R.layout.layout_notification_beauty
|
||||
)
|
||||
.setImageViewResource(
|
||||
R.id.iv, when (data.iconType) {
|
||||
PushContent.ICON_TYPE_PHOTO -> R.drawable.ic_noti_photo
|
||||
PushContent.ICON_TYPE_VIDEO -> R.drawable.ic_noti_video
|
||||
PushContent.ICON_TYPE_DOCUMENT -> R.drawable.ic_noti_document
|
||||
PushContent.ICON_TYPE_AUDIO -> R.drawable.ic_noti_audio
|
||||
PushContent.ICON_TYPE_SCREENSHOT -> R.drawable.ic_noti_shot
|
||||
PushContent.ICON_TYPE_RECOVERED -> R.drawable.ic_noti_recover
|
||||
else -> R.drawable.ic_noti_photo
|
||||
}
|
||||
)
|
||||
.setTextViewText(R.id.tvCount, badgeCount)
|
||||
.setTextViewText(R.id.tvTitle, title)
|
||||
.setTextViewText(R.id.tvDesc, content)
|
||||
.setTextViewText(
|
||||
R.id.tvAction, when (data.iconType) {
|
||||
PushContent.ICON_TYPE_SCREENSHOT -> StringUtils.getString(R.string.noti_clean)
|
||||
else -> StringUtils.getString(R.string.noti_recovery)
|
||||
}
|
||||
)
|
||||
.build()
|
||||
|
||||
return GeneralNotificationData(
|
||||
notificationId = notificationId,
|
||||
contentTitle = title,
|
||||
contentContent = content,
|
||||
contentIntent = pendingIntent,
|
||||
contentView = contentView,
|
||||
bigContentView = bigContentView
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
class ResidentModelManger {
|
||||
|
||||
fun getModel(context: Context): GeneralNotificationData {
|
||||
|
|
|
|||
|
|
@ -47,7 +47,8 @@ class NotificationCheckController private constructor() {
|
|||
*/
|
||||
enum class NotificationType(val string: String) {
|
||||
UNLOCK("local_push"), // 解锁通知
|
||||
BACKGROUND("local_push"), // 后台通知
|
||||
BACKGROUND("local_push"), // app后台 - for withdraw
|
||||
FIXTIMEPOINT("fix_time_point_push"), // 每日固定时间点 "9:10""12:10""13:10""19:10""21:00""22:30""23:30"
|
||||
KEEPALIVE("local_push"), // 保活通知(无间隔限制)
|
||||
FCM("firebase_push"), // FCM 推送通知(无间隔限制)
|
||||
RESIDENT("top_notification") // 常驻(无间隔限制)
|
||||
|
|
|
|||
|
|
@ -13,25 +13,32 @@ import android.os.Looper
|
|||
import androidx.core.app.NotificationCompat
|
||||
import androidx.core.app.NotificationManagerCompat
|
||||
import androidx.core.content.ContextCompat
|
||||
import com.ama.core.architecture.util.DateUtil
|
||||
import com.ama.core.architecture.util.SpUtil
|
||||
import com.remax.base.ext.canSendNotification
|
||||
import com.remax.base.report.DataReportManager
|
||||
import com.remax.notification.NotifyConst
|
||||
import com.remax.notification.R
|
||||
import com.remax.notification.builder.FixTimeModelManager
|
||||
import com.remax.notification.builder.GeneralModelManager
|
||||
import com.remax.notification.builder.GeneralNotificationData
|
||||
import com.remax.notification.builder.NotificationType
|
||||
import com.remax.notification.builder.ResidentModelManger
|
||||
import com.remax.notification.builder.type2notificationId
|
||||
import com.remax.notification.config.NotificationConfigController
|
||||
import com.remax.notification.check.NotificationCheckController
|
||||
import com.remax.notification.utils.NotiLogger
|
||||
import com.remax.notification.config.NotificationConfigController
|
||||
import com.remax.notification.newUtil.NotificationRecorder
|
||||
import com.remax.notification.receiver.NotificationDeleteReceiver
|
||||
import com.remax.notification.timing.NotificationTimingController
|
||||
import com.remax.notification.utils.NotiLogger
|
||||
import com.remax.notification.utils.TimeCheckUtil
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.SupervisorJob
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
|
||||
/**
|
||||
* 通知触发控制器
|
||||
* 提供常驻通知和普通通知的触发功能
|
||||
|
|
@ -42,10 +49,13 @@ object NotificationTriggerController {
|
|||
const val CHANNEL_ID_RESIDENT = "resident_notification"
|
||||
const val CHANNEL_ID_GENERAL = "general_notification"
|
||||
const val CHANNEL_ID_GENERAL_SILENT = "general_silent_notification"
|
||||
const val CHANNEL_ID_BEAUTY = "general_beauty"
|
||||
const val CHANNEL_NAME_RESIDENT = "recovery_resident"
|
||||
const val CHANNEL_NAME_GENERAL = "recovery_single"
|
||||
const val CHANNEL_NAME_GENERAL_SILENT = "recovery_loop"
|
||||
|
||||
const val CHANNEL_NAME_BEAUTY = "beauty_single"
|
||||
|
||||
private var notificationManager: NotificationManagerCompat? = null
|
||||
private var context: Context? = null
|
||||
|
||||
|
|
@ -58,10 +68,14 @@ object NotificationTriggerController {
|
|||
private var repeatCount: Int = 0
|
||||
private var currentNotificationId: Int = 0
|
||||
|
||||
private var mFixTimeHandler: Handler? = null
|
||||
private var mFixTimeRunnable: Runnable? = null
|
||||
|
||||
/**
|
||||
* 初始化通知通道
|
||||
* @param context 上下文
|
||||
*/
|
||||
@SuppressLint("NewApi")
|
||||
fun initializeChannels(context: Context) {
|
||||
this.context = context
|
||||
this.notificationManager = NotificationManagerCompat.from(context)
|
||||
|
|
@ -86,6 +100,16 @@ object NotificationTriggerController {
|
|||
enableVibration(false)
|
||||
}
|
||||
|
||||
// beauty
|
||||
val beautyChannel = NotificationChannel(
|
||||
CHANNEL_ID_BEAUTY, CHANNEL_NAME_BEAUTY, NotificationManager.IMPORTANCE_HIGH
|
||||
).apply {
|
||||
description = "for beauty notification"
|
||||
setShowBadge(true)
|
||||
enableLights(false)
|
||||
enableVibration(false)
|
||||
}
|
||||
|
||||
// 静音通知通道
|
||||
val silentChannel = NotificationChannel(
|
||||
CHANNEL_ID_GENERAL_SILENT,
|
||||
|
|
@ -101,7 +125,7 @@ object NotificationTriggerController {
|
|||
|
||||
notificationManager?.createNotificationChannels(
|
||||
listOf(
|
||||
residentChannel, generalChannel, silentChannel
|
||||
residentChannel, generalChannel, silentChannel, beautyChannel
|
||||
)
|
||||
)
|
||||
NotiLogger.d("通知通道创建完成")
|
||||
|
|
@ -207,6 +231,7 @@ object NotificationTriggerController {
|
|||
// 创建 Handler
|
||||
repeatHandler = Handler(Looper.getMainLooper())
|
||||
|
||||
|
||||
// 创建重复任务
|
||||
repeatRunnable = object : Runnable {
|
||||
override fun run() {
|
||||
|
|
@ -237,6 +262,30 @@ object NotificationTriggerController {
|
|||
repeatHandler?.postDelayed(repeatRunnable!!, 4000)
|
||||
}
|
||||
|
||||
|
||||
private fun checkAndStartFixTimeNotification() {
|
||||
|
||||
stopFixTimeNotification()
|
||||
|
||||
mFixTimeHandler = Handler(Looper.getMainLooper())
|
||||
|
||||
mFixTimeRunnable = object : Runnable {
|
||||
override fun run() {
|
||||
val lastFixNotifiShowMs = NotificationRecorder.getLastWithdrawShowTime()
|
||||
if (/*TimeCheckUtil.isTargetTime() && */(DateUtil.getCurTimeMs() - lastFixNotifiShowMs > 60000)) {
|
||||
NotificationTimingController.getInstance().triggerNotificationIfAllowed(NotificationCheckController.NotificationType.FIXTIMEPOINT)
|
||||
NotificationRecorder.saveLastWithdrawShowTime(DateUtil.getCurTimeMs())
|
||||
NotiLogger.d("固定时间节点通知,第${repeatCount + 1}次")
|
||||
}
|
||||
|
||||
mFixTimeHandler?.postDelayed(this, 1000)
|
||||
}
|
||||
}
|
||||
|
||||
mFixTimeHandler?.postDelayed(mFixTimeRunnable!!, 1000)
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 停止重复通知
|
||||
*/
|
||||
|
|
@ -245,11 +294,17 @@ object NotificationTriggerController {
|
|||
repeatHandler = null
|
||||
repeatRunnable = null
|
||||
repeatCount = 0
|
||||
currentNotificationId = 0
|
||||
|
||||
NotiLogger.d("停止重复通知")
|
||||
}
|
||||
|
||||
fun stopFixTimeNotification() {
|
||||
mFixTimeHandler?.removeCallbacks(mFixTimeRunnable ?: return)
|
||||
mFixTimeHandler = null
|
||||
mFixTimeRunnable = null
|
||||
NotiLogger.d("停止时间节点通知")
|
||||
}
|
||||
|
||||
fun triggerResidentNotification() {
|
||||
if (context?.canSendNotification() == true) {
|
||||
triggerNotification(
|
||||
|
|
@ -274,11 +329,15 @@ object NotificationTriggerController {
|
|||
onNotificationSent?.invoke()
|
||||
generalTrack(type, notificationData)
|
||||
// 检查是否需要重复通知
|
||||
checkAndStartRepeatNotification(notificationData, notification,type)
|
||||
checkAndStartRepeatNotification(notificationData, notification, type)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fun triggerFixTimeNotification() {
|
||||
checkAndStartFixTimeNotification()
|
||||
}
|
||||
|
||||
private fun resident(model: GeneralNotificationData) =
|
||||
NotificationCompat.Builder(context!!, CHANNEL_ID_RESIDENT)
|
||||
.setColor(ContextCompat.getColor(context!!, R.color.noti_color))
|
||||
|
|
@ -330,6 +389,39 @@ object NotificationTriggerController {
|
|||
}.build()
|
||||
}
|
||||
|
||||
private fun fixTime(model: GeneralNotificationData): Notification {
|
||||
// 根据重复通知配置选择通道
|
||||
val channelId = CHANNEL_ID_BEAUTY
|
||||
|
||||
// 创建删除监听 Intent
|
||||
val deleteIntent = Intent(context, NotificationDeleteReceiver::class.java).apply {
|
||||
action = "com.remax.notification.ACTION_NOTIFICATION_DELETE"
|
||||
}
|
||||
val deletePendingIntent = PendingIntent.getBroadcast(
|
||||
context,
|
||||
System.currentTimeMillis().toInt(),
|
||||
deleteIntent,
|
||||
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
|
||||
)
|
||||
|
||||
val builder = NotificationCompat.Builder(context!!, channelId)
|
||||
.setPriority(NotificationCompat.PRIORITY_MAX)
|
||||
.setColor(ContextCompat.getColor(context!!, R.color.noti_color))
|
||||
.setSmallIcon(R.drawable.ic_noti_icon).setAutoCancel(true)
|
||||
.setGroup("push_" + System.currentTimeMillis()).setContentText(model.contentTitle)
|
||||
.setContentIntent(model.contentIntent).setDeleteIntent(deletePendingIntent) // 设置删除监听
|
||||
.setCustomContentView(model.contentView).setCustomBigContentView(model.bigContentView)
|
||||
|
||||
// 记录使用的通道
|
||||
NotiLogger.d("使用普通通道发送通知: $channelId")
|
||||
|
||||
return builder.apply {
|
||||
if (Build.VERSION.SDK_INT >= 31) {
|
||||
setCustomHeadsUpContentView(model.contentView)
|
||||
}
|
||||
}.build()
|
||||
}
|
||||
|
||||
/**
|
||||
* 取消通知
|
||||
* @param notificationId 通知ID
|
||||
|
|
@ -428,6 +520,30 @@ object NotificationTriggerController {
|
|||
"Notific_Type" to when (type) {
|
||||
NotificationCheckController.NotificationType.UNLOCK -> 1
|
||||
NotificationCheckController.NotificationType.BACKGROUND -> 1
|
||||
NotificationCheckController.NotificationType.FIXTIMEPOINT -> 1
|
||||
NotificationCheckController.NotificationType.KEEPALIVE -> 1
|
||||
NotificationCheckController.NotificationType.FCM -> 3
|
||||
else -> 4
|
||||
},
|
||||
"Notific_Position" to 1,
|
||||
"Notific_Priority" to "PRIORITY_MAX",
|
||||
"event_id" to "customer_general_style",
|
||||
"title" to notificationData.contentTitle,
|
||||
"text" to notificationData.contentContent,
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
private fun fixTimeTrack(
|
||||
type: NotificationCheckController.NotificationType,
|
||||
notificationData: GeneralNotificationData
|
||||
) {
|
||||
DataReportManager.reportData(
|
||||
"Notific_Show", mapOf(
|
||||
"Notific_Type" to when (type) {
|
||||
NotificationCheckController.NotificationType.UNLOCK -> 1
|
||||
NotificationCheckController.NotificationType.BACKGROUND -> 1
|
||||
NotificationCheckController.NotificationType.FIXTIMEPOINT -> 1
|
||||
NotificationCheckController.NotificationType.KEEPALIVE -> 1
|
||||
NotificationCheckController.NotificationType.FCM -> 3
|
||||
else -> 4
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
package com.remax.notification.newUtil
|
||||
|
||||
import android.app.PendingIntent
|
||||
import com.google.gson.annotations.SerializedName
|
||||
|
||||
class NotificationConfig(val notificationId: Int = System.currentTimeMillis().toInt(),
|
||||
val channelId: String,
|
||||
|
|
@ -12,3 +13,4 @@ class NotificationConfig(val notificationId: Int = System.currentTimeMillis().to
|
|||
val useFullScreenIntent: Boolean = false,
|
||||
) {
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,10 +1,9 @@
|
|||
package com.remax.notification.newUtil
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.PendingIntent
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import com.ama.core.architecture.BaseApp
|
||||
import com.ama.core.architecture.util.AndroidUtil
|
||||
import com.ama.core.architecture.util.ResUtil
|
||||
import com.remax.notification.R
|
||||
import com.remax.notification.builder.LANDING_NOTIFICATION_ACTION
|
||||
|
|
@ -17,8 +16,10 @@ import com.remax.notification.config.PushContent
|
|||
import java.util.concurrent.ConcurrentHashMap
|
||||
|
||||
|
||||
|
||||
@SuppressLint("StaticFieldLeak")
|
||||
object NotificationDatas {
|
||||
private var mCurRandomIndex = 0
|
||||
const val GAP_FOR_WITHDRAW_NOTIFY = 30 * 1000
|
||||
|
||||
var context: Context = BaseApp.appContext()
|
||||
|
|
@ -28,37 +29,62 @@ object NotificationDatas {
|
|||
const val NOTI_TYPE_BG_RANDOM = "NOTI_TYPE_BG_RANDOM"
|
||||
const val NOTI_TYPE_RESIDENT = "NOTI_TYPE_RESIDENT"
|
||||
const val NOTI_TYPE_UNLOCK = "NOTI_TYPE_UNLOCK"
|
||||
const val NOTI_TYPE_FXITIME = "NOTI_TYPE_FXITIME"
|
||||
|
||||
// notifi id
|
||||
const val NOTI_ID_TYPE_BG_WITHDRAW = 666
|
||||
const val NOTI_ID_TYPE_BG_RANDOM = 667
|
||||
const val NOTI_ID_TYPE_RESIDENT = 668
|
||||
const val NOTI_ID_TYPE_UNLOCK = 669
|
||||
const val NOTI_ID_TYPE_FXITIME = 670
|
||||
|
||||
const val NOTI_ID_TYPE_RANDOM_1 = 670
|
||||
const val NOTI_ID_TYPE_RANDOM_2 = 671
|
||||
const val NOTI_ID_TYPE_RANDOM_3 = 672
|
||||
const val NOTI_ID_TYPE_RANDOM_4 = 673
|
||||
const val NOTI_ID_TYPE_RANDOM_5 = 674
|
||||
const val NOTI_ID_TYPE_RANDOM_6 = 675
|
||||
const val NOTI_ID_TYPE_RANDOM_7 = 676
|
||||
const val NOTI_ID_TYPE_RANDOM_8 = 677
|
||||
const val NOTI_ID_TYPE_RANDOM_9 = 678
|
||||
const val NOTI_ID_TYPE_RANDOM_10 = 679
|
||||
const val NOTI_ID_TYPE_RANDOM_11 = 680
|
||||
|
||||
// channelId type
|
||||
const val CHANNEL_TYPE_BG_WITHDRAW = "CHANNEL_TYPE_BG_WITHDRAW"
|
||||
const val CHANNEL_TYPE_BG_RANDOM = "CHANNEL_TYPE_BG_RANDOM"
|
||||
const val CHANNEL_TYPE_RESIDENT = "CHANNEL_TYPE_RESIDENT"
|
||||
const val CHANNEL_TYPE_UNLOCK = "CHANNEL_TYPE_UNLOCK"
|
||||
const val CHANNEL_TYPE_RANDOM = "CHANNEL_TYPE_RANDOM"
|
||||
|
||||
// channelId name
|
||||
const val CHANNEL_TYPE_BG_WITHDRAW_NAME = "CHANNEL_TYPE_BG_WITHDRAW_NAME"
|
||||
const val CHANNEL_TYPE_BG_RANDOM_NAME = "CHANNEL_TYPE_BG_RANDOM_NAME"
|
||||
const val CHANNEL_TYPE_RESIDENT_NAME = "CHANNEL_TYPE_RESIDENT_NAME"
|
||||
const val CHANNEL_TYPE_UNLOCK_NAME = "CHANNEL_TYPE_UNLOCK_NAME"
|
||||
const val CHANNEL_TYPE_RANDOM_NAME = "CHANNEL_TYPE_RANDOM_NAME"
|
||||
|
||||
|
||||
private val mConfigList: ConcurrentHashMap<String, NotificationConfig> = ConcurrentHashMap()
|
||||
private val mConfigMap: ConcurrentHashMap<String, NotificationConfig> = ConcurrentHashMap()
|
||||
private val mRandomConfigList: MutableList<NotificationConfig> = mutableListOf()
|
||||
|
||||
|
||||
|
||||
init {
|
||||
|
||||
initRandomConfigs()
|
||||
|
||||
|
||||
|
||||
|
||||
// # 切换后台 - 提现
|
||||
val bgWithdrawIntent = entryPointPendingIntent(context,
|
||||
type2notificationId[NotificationType.RESIDENT_WITHDRAW] ?: 0
|
||||
) {
|
||||
it.putExtra(LANDING_NOTIFICATION_ACTION, PushContent.ACTION_TYPE_WITHDRAW)
|
||||
it.putExtra(LANDING_NOTIFICATION_FROM, NotificationCheckController.NotificationType.RESIDENT.string)
|
||||
}
|
||||
mConfigList.put(NOTI_TYPE_BG_WITHDRAW, NotificationConfig(
|
||||
mConfigMap.put(NOTI_TYPE_BG_WITHDRAW, NotificationConfig(
|
||||
NOTI_ID_TYPE_BG_WITHDRAW,
|
||||
CHANNEL_TYPE_BG_WITHDRAW,
|
||||
CHANNEL_TYPE_BG_WITHDRAW_NAME,
|
||||
|
|
@ -68,12 +94,144 @@ object NotificationDatas {
|
|||
bgWithdrawIntent,
|
||||
true,
|
||||
))
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
private fun initRandomConfigs() {
|
||||
try {
|
||||
mRandomConfigList.add(NotificationConfig(
|
||||
NOTI_ID_TYPE_RANDOM_1,
|
||||
CHANNEL_TYPE_RANDOM,
|
||||
CHANNEL_TYPE_RANDOM_NAME,
|
||||
"Time to claim coins!",
|
||||
"You have free coins today!Open to claim!",
|
||||
R.mipmap.task_cash,
|
||||
entryPointPendingIntent(context, NOTI_ID_TYPE_RANDOM_1))
|
||||
)
|
||||
|
||||
mRandomConfigList.add(NotificationConfig(
|
||||
NOTI_ID_TYPE_RANDOM_2,
|
||||
CHANNEL_TYPE_RANDOM,
|
||||
CHANNEL_TYPE_RANDOM_NAME,
|
||||
"Time to claim cash!",
|
||||
"You have free cash today!Open to claim!",
|
||||
R.mipmap.task_cash,
|
||||
entryPointPendingIntent(context, NOTI_ID_TYPE_RANDOM_2))
|
||||
)
|
||||
|
||||
mRandomConfigList.add(NotificationConfig(
|
||||
NOTI_ID_TYPE_RANDOM_3,
|
||||
CHANNEL_TYPE_RANDOM,
|
||||
CHANNEL_TYPE_RANDOM_NAME,
|
||||
"Hey!",
|
||||
"You received R$10, click to withdraw immediately!",
|
||||
R.mipmap.task_cash,
|
||||
entryPointPendingIntent(context, NOTI_ID_TYPE_RANDOM_3))
|
||||
)
|
||||
|
||||
mRandomConfigList.add(NotificationConfig(
|
||||
NOTI_ID_TYPE_RANDOM_4,
|
||||
CHANNEL_TYPE_RANDOM,
|
||||
CHANNEL_TYPE_RANDOM_NAME,
|
||||
"\uD83D\uDCDE\uD83D\uDCDE\uD83D\uDCDE Do you miss me? ",
|
||||
"Come play the game and withdraw now!",
|
||||
R.mipmap.task_cash,
|
||||
entryPointPendingIntent(context, NOTI_ID_TYPE_RANDOM_4))
|
||||
)
|
||||
|
||||
mRandomConfigList.add(NotificationConfig(
|
||||
NOTI_ID_TYPE_RANDOM_5,
|
||||
CHANNEL_TYPE_RANDOM,
|
||||
CHANNEL_TYPE_RANDOM_NAME,
|
||||
"\uD83D\uDCDE\uD83D\uDCDE\uD83D\uDCDE Do you miss me? ",
|
||||
"Come play the game and withdraw now!",
|
||||
R.mipmap.task_cash,
|
||||
entryPointPendingIntent(context, NOTI_ID_TYPE_RANDOM_5))
|
||||
)
|
||||
|
||||
mRandomConfigList.add(NotificationConfig(
|
||||
NOTI_ID_TYPE_RANDOM_6,
|
||||
CHANNEL_TYPE_RANDOM,
|
||||
CHANNEL_TYPE_RANDOM_NAME,
|
||||
"\uD83D\uDCDE\uD83D\uDCDE\uD83D\uDCDE Do you miss me? ",
|
||||
"Come play the game and withdraw now!",
|
||||
R.mipmap.task_cash,
|
||||
entryPointPendingIntent(context, NOTI_ID_TYPE_RANDOM_6))
|
||||
)
|
||||
|
||||
mRandomConfigList.add(NotificationConfig(
|
||||
NOTI_ID_TYPE_RANDOM_7,
|
||||
CHANNEL_TYPE_RANDOM,
|
||||
CHANNEL_TYPE_RANDOM_NAME,
|
||||
"\uD83D\uDCDE\uD83D\uDCDE\uD83D\uDCDE Do you miss me? ",
|
||||
"Come play the game and withdraw now!",
|
||||
R.mipmap.task_cash,
|
||||
entryPointPendingIntent(context, NOTI_ID_TYPE_RANDOM_7))
|
||||
)
|
||||
|
||||
mRandomConfigList.add(NotificationConfig(
|
||||
NOTI_ID_TYPE_RANDOM_8,
|
||||
CHANNEL_TYPE_RANDOM,
|
||||
CHANNEL_TYPE_RANDOM_NAME,
|
||||
"Today’s 1,024th Cash-Out Champion is HERE!",
|
||||
"You’re the missing piece! Join now!",
|
||||
R.mipmap.task_cash,
|
||||
entryPointPendingIntent(context, NOTI_ID_TYPE_RANDOM_8))
|
||||
)
|
||||
|
||||
mRandomConfigList.add(NotificationConfig(
|
||||
NOTI_ID_TYPE_RANDOM_9,
|
||||
CHANNEL_TYPE_RANDOM,
|
||||
CHANNEL_TYPE_RANDOM_NAME,
|
||||
"User #888 Just Cashed Out!",
|
||||
"Don’t be left behind! Tap to claim yours!",
|
||||
R.mipmap.task_cash,
|
||||
entryPointPendingIntent(context, NOTI_ID_TYPE_RANDOM_9))
|
||||
)
|
||||
|
||||
mRandomConfigList.add(NotificationConfig(
|
||||
NOTI_ID_TYPE_RANDOM_10,
|
||||
CHANNEL_TYPE_RANDOM,
|
||||
CHANNEL_TYPE_RANDOM_NAME,
|
||||
"You missed(2) calls",
|
||||
"+86-12345, +86-666999",
|
||||
R.mipmap.task_cash,
|
||||
entryPointPendingIntent(context, NOTI_ID_TYPE_RANDOM_10))
|
||||
)
|
||||
|
||||
mRandomConfigList.add(NotificationConfig(
|
||||
NOTI_ID_TYPE_RANDOM_11,
|
||||
CHANNEL_TYPE_RANDOM,
|
||||
CHANNEL_TYPE_RANDOM_NAME,
|
||||
"Gmail",
|
||||
"support.gov@gmail.com",
|
||||
R.mipmap.task_cash,
|
||||
entryPointPendingIntent(context, NOTI_ID_TYPE_RANDOM_11))
|
||||
)
|
||||
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fun getConfigForType(notifiType: String): NotificationConfig? {
|
||||
return mConfigList[notifiType]
|
||||
return mConfigMap[notifiType]
|
||||
}
|
||||
|
||||
fun getRandomConfig(): NotificationConfig {
|
||||
var randomIndex = AndroidUtil.randomInt(0, mRandomConfigList.size - 1)
|
||||
if (mCurRandomIndex == randomIndex) {
|
||||
randomIndex++
|
||||
if (randomIndex >= mRandomConfigList.size) {
|
||||
randomIndex = 0
|
||||
}
|
||||
}
|
||||
mCurRandomIndex = randomIndex
|
||||
return mRandomConfigList[mCurRandomIndex]
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -49,7 +49,28 @@ class NotificationUtil private constructor() {
|
|||
/**
|
||||
* 1. 基本通知
|
||||
*/
|
||||
fun showBasicNotification(
|
||||
fun showBasicNotification(notifiConfig: NotificationConfig) {
|
||||
val notificationId = notifiConfig.notificationId
|
||||
val channelId = notifiConfig.channelId
|
||||
val channelName = notifiConfig.channelName
|
||||
val title = notifiConfig.title
|
||||
val content = notifiConfig.content
|
||||
val smallIcon = notifiConfig.smallIcon
|
||||
val pendingIntent = notifiConfig.intent
|
||||
|
||||
val notification = NotificationCompat.Builder(mContext, channelId)
|
||||
.setContentTitle(title)
|
||||
.setContentText(content)
|
||||
.setSmallIcon(smallIcon)
|
||||
.setContentIntent(pendingIntent)
|
||||
.setAutoCancel(true)
|
||||
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
|
||||
.build()
|
||||
|
||||
showNotification(notificationId, notification, channelName)
|
||||
}
|
||||
|
||||
fun showBasicNotification_old(
|
||||
channelId: String,
|
||||
title: String,
|
||||
content: String,
|
||||
|
|
@ -413,7 +434,7 @@ class NotificationUtil private constructor() {
|
|||
|
||||
@RequiresPermission(Manifest.permission.POST_NOTIFICATIONS)
|
||||
private fun doShowNotification(notificationId: Int = System.currentTimeMillis().toInt(), notification: Notification, channelName: String) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && notificationManager.getNotificationChannel(notification.channelId) == null) {
|
||||
createNotificationChannel(notification.channelId, channelName, NotificationCompat.PRIORITY_HIGH)
|
||||
}
|
||||
NotificationManagerCompat.from(mContext).notify(notificationId, notification)
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ import com.remax.base.ext.canSendNotification
|
|||
import com.remax.base.report.DataReportManager
|
||||
import com.remax.notification.check.NotificationCheckController
|
||||
import com.remax.notification.controller.NotificationTriggerController
|
||||
import com.remax.notification.controller.NotificationTriggerController.triggerFixTimeNotification
|
||||
import com.remax.notification.timing.NotificationTimingController
|
||||
import com.remax.notification.utils.NotiLogger
|
||||
import com.remax.notification.utils.Topic
|
||||
|
|
@ -153,6 +154,7 @@ class NotificationKeepAliveService : Service() {
|
|||
NotiLogger.d("保活服务已在运行中,刷新通知,间隔: ${intervalSeconds}秒")
|
||||
// 服务已运行,只刷新通知
|
||||
updateForegroundNotification()
|
||||
triggerFixTimeNotification()
|
||||
return
|
||||
}
|
||||
|
||||
|
|
@ -219,6 +221,7 @@ class NotificationKeepAliveService : Service() {
|
|||
NotiLogger.d("执行保活任务")
|
||||
DataReportManager.reportData("Notific_Pull", mapOf("topic" to "timer"))
|
||||
updateForegroundNotification()
|
||||
triggerFixTimeNotification()
|
||||
// 尝试触发保活通知
|
||||
NotificationTimingController.getInstance().triggerNotificationIfAllowed(
|
||||
NotificationCheckController.NotificationType.KEEPALIVE
|
||||
|
|
@ -237,6 +240,7 @@ class NotificationKeepAliveService : Service() {
|
|||
|
||||
// 延迟执行首次任务
|
||||
handler?.postDelayed(keepAliveRunnable!!, intervalSeconds * 1000)
|
||||
triggerFixTimeNotification()
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -1,16 +1,13 @@
|
|||
package com.remax.notification.timing
|
||||
|
||||
import android.app.ActivityManager
|
||||
import android.content.BroadcastReceiver
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.IntentFilter
|
||||
import android.os.Build
|
||||
import android.os.Handler
|
||||
import android.os.Looper
|
||||
import androidx.lifecycle.Lifecycle
|
||||
import androidx.lifecycle.LifecycleObserver
|
||||
import androidx.lifecycle.LifecycleOwner
|
||||
import androidx.lifecycle.OnLifecycleEvent
|
||||
import androidx.lifecycle.ProcessLifecycleOwner
|
||||
import androidx.work.PeriodicWorkRequestBuilder
|
||||
|
|
@ -31,14 +28,10 @@ import com.remax.notification.utils.NotiLogger
|
|||
import com.remax.notification.utils.FCMTopicManager
|
||||
import com.remax.notification.service.FCMService
|
||||
import com.remax.notification.service.NotificationKeepAliveServiceManager
|
||||
import com.remax.notification.utils.DateUtil
|
||||
import com.remax.notification.utils.Topic
|
||||
import com.remax.notification.worker.NotificationKeepAliveWorker
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.SupervisorJob
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
import java.util.concurrent.TimeUnit
|
||||
import java.util.concurrent.atomic.AtomicBoolean
|
||||
|
||||
|
|
@ -284,7 +277,8 @@ class NotificationTimingController private constructor() : LifecycleObserver {
|
|||
NotificationCheckController.NotificationType.BACKGROUND -> "后台通知"
|
||||
NotificationCheckController.NotificationType.KEEPALIVE -> "保活通知"
|
||||
NotificationCheckController.NotificationType.FCM -> "FCM推送通知"
|
||||
NotificationCheckController.NotificationType.RESIDENT -> ""
|
||||
NotificationCheckController.NotificationType.RESIDENT -> "常驻通知"
|
||||
NotificationCheckController.NotificationType.FIXTIMEPOINT -> "固定时间点通知"
|
||||
}
|
||||
DataReportManager.reportData("Notific_Pull", mapOf("topic" to "localPush"))
|
||||
|
||||
|
|
@ -333,13 +327,13 @@ class NotificationTimingController private constructor() : LifecycleObserver {
|
|||
NotificationCheckController.NotificationType.RESIDENT -> {
|
||||
|
||||
}
|
||||
|
||||
NotificationCheckController.NotificationType.FIXTIMEPOINT -> {
|
||||
showNotifyRandom()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -356,5 +350,9 @@ class NotificationTimingController private constructor() : LifecycleObserver {
|
|||
}
|
||||
}
|
||||
|
||||
private fun showNotifyRandom() {
|
||||
NotificationUtil.getInstance().showBasicNotification(NotificationDatas.getRandomConfig())
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,54 @@
|
|||
package com.remax.notification.utils
|
||||
|
||||
import java.util.Calendar
|
||||
import kotlin.arrayOf
|
||||
import kotlin.math.abs
|
||||
|
||||
|
||||
|
||||
object TimeCheckUtil {
|
||||
|
||||
private val TARGET_TIMES = arrayOf<IntArray>(
|
||||
intArrayOf(9, 10),
|
||||
intArrayOf(12, 10),
|
||||
intArrayOf(13, 10),
|
||||
intArrayOf(19, 10),
|
||||
intArrayOf(21, 0),
|
||||
intArrayOf(22, 30),
|
||||
intArrayOf(23, 30),
|
||||
|
||||
intArrayOf(14, 18),
|
||||
)
|
||||
|
||||
fun isTargetTime(): Boolean {
|
||||
val calendar = Calendar.getInstance()
|
||||
val currentHour = calendar.get(Calendar.HOUR_OF_DAY) // 24小时制
|
||||
val currentMinute = calendar.get(Calendar.MINUTE)
|
||||
|
||||
for (time in TARGET_TIMES) {
|
||||
val targetHour = time[0]
|
||||
val targetMinute = time[1]
|
||||
if (currentHour == targetHour && currentMinute == targetMinute) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
|
||||
fun isTargetTimeWithTolerance(tolerance: Int): Boolean {
|
||||
val calendar = Calendar.getInstance()
|
||||
val currentHour = calendar.get(Calendar.HOUR_OF_DAY)
|
||||
val currentMinute = calendar.get(Calendar.MINUTE)
|
||||
|
||||
for (time in TARGET_TIMES) {
|
||||
val targetHour = time[0]
|
||||
val targetMinute = time[1]
|
||||
val diff = abs((currentHour * 60 + currentMinute) - (targetHour * 60 + targetMinute))
|
||||
if (diff <= tolerance) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,61 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@drawable/noti_bg_r16_white"
|
||||
android:gravity="center_horizontal"
|
||||
android:orientation="vertical"
|
||||
tools:ignore="ResourceName"
|
||||
android:padding="12dp" >
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:gravity="center_vertical">
|
||||
<LinearLayout
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:layout_weight="1"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvTitle"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:ellipsize="end"
|
||||
android:lines="1"
|
||||
android:textColor="#333333"
|
||||
android:textSize="14sp"
|
||||
tools:text="title" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvDesc"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="2dp"
|
||||
android:ellipsize="end"
|
||||
android:lines="1"
|
||||
android:textColor="#666666"
|
||||
android:textSize="12sp"
|
||||
tools:text="desc" />
|
||||
</LinearLayout>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvAction"
|
||||
android:layout_width="80dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@drawable/noti_bg_button_primary"
|
||||
android:paddingHorizontal="12dp"
|
||||
android:paddingVertical="4dp"
|
||||
android:gravity="center"
|
||||
android:text="@string/vidi_go"
|
||||
android:textColor="#fff"
|
||||
android:textSize="14sp" />
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
|
|
@ -0,0 +1,60 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@drawable/noti_bg_r16_white"
|
||||
android:gravity="center_horizontal"
|
||||
android:orientation="vertical"
|
||||
tools:ignore="ResourceName" >
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:gravity="center_vertical">
|
||||
<LinearLayout
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:layout_weight="1"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvTitle"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:ellipsize="end"
|
||||
android:lines="1"
|
||||
android:textColor="#333333"
|
||||
android:textSize="14sp"
|
||||
tools:text="title" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvDesc"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="2dp"
|
||||
android:ellipsize="end"
|
||||
android:lines="1"
|
||||
android:textColor="#666666"
|
||||
android:textSize="12sp"
|
||||
tools:text="desc" />
|
||||
</LinearLayout>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvAction"
|
||||
android:layout_width="80dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@drawable/noti_bg_button_primary"
|
||||
android:paddingHorizontal="12dp"
|
||||
android:paddingVertical="4dp"
|
||||
android:gravity="center"
|
||||
android:text="@string/vidi_go"
|
||||
android:textColor="#fff"
|
||||
android:textSize="14sp" />
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
Loading…
Reference in New Issue