定时消息提醒的实现

543 阅读2分钟

定时消息提醒的实现

前言

项目开发的待办模块需要实现待办提醒,也就是定时弹出通知消息。一开始的思路是AlarmManager+BoardcastReceiver+Notification,也就是设置闹钟管理定时通知广播来弹出通知框。最后的实现方法是通过定时器+Notification的方式。具体实现如下:

1、创建接口管理通知任务

/**
 * 添加一条待办通知
 */
fun insertOrUpdateTodo(todoInfo: TodoInfo)

/**
 * 添加待办通知
 */
fun inserts(todoInfo: List<TodoInfo>)

/**
 * 删除一条待办通知
 */
fun remove(todoInfoId: Long)

/**
 * 删除所有待办通知
 */
fun removeAll()

2、实现接口,将任务加入定时器中

private val todoMap = mutableMapOf<Long, TimerTask>()

override fun insertOrUpdateTodo(todoInfo: TodoInfo) {
    SpeedyLog.i(TAG, "insertOrUpdateTodo todoInfo=$todoInfo")
    //移除已存在的任务
    todoMap.remove(todoInfo.id)?.cancel()
    //过滤不是今天的
    if (!todoInfo.isToday){
        SpeedyLog.i(TAG, "任务不是今天的,不加入定时器")
        return
    }
    //过滤已完成的
    if (todoInfo.isOver()){
        SpeedyLog.i(TAG, "任务已完成,不加入定时器")
        return
    }

    var delayTime = todoInfo.getTodoDelayTime()
    SpeedyLog.i(TAG, "delayTime is $delayTime")

    if (delayTime > 0){
        mTimer.schedule(SafeTask {
            SpeedyLog.i(TAG, "${todoInfo.id}定时器触发,消息弹窗提醒")
            //移除保存map
            todoMap.remove(todoInfo.id)
            //消息弹窗提醒
            TodoNotification().showNotify(todoInfo)
        }.also {
            todoMap[todoInfo.id] = it
        }, delayTime)
    }else{
        SpeedyLog.i(TAG, "delayTime < 0,不加入定时器")
    }

}

override fun inserts(todoInfos: List<TodoInfo>) {
     todoInfos.forEach{
         insertOrUpdateTodo(it)
     }
}

override fun remove(todoInfoId: Long) {
    SpeedyLog.i(TAG, "移除任务 todoInfoId=$todoInfoId")
    todoMap.remove(todoInfoId)?.cancel()
}

override fun removeAll() {
    SpeedyLog.i(TAG, "移除所有任务")
    todoMap.forEach {
        it.value.cancel()
    }
    todoMap.clear()
}

3、显示到通知栏

fun showNotify(todoInfo: TodoInfo) {
    binding = LayoutTodoNotifyItemBinding.inflate(context.layoutInflater())

    val notificationLayout =
        RemoteViews(context.packageName, R.layout.layout_todo_notify_item)
    val notifyAfterI = Intent().setAction(TodoNotifyReceiver.TODO_CHANGE_ACTION)
    val alreadyKnowI = Intent().setAction(TodoNotifyReceiver.TODO_ALREADY_KNOW_ACTION)
    notifyAfterI.putExtra("todoInfo", todoInfo)
    alreadyKnowI.putExtra("todoInfo", todoInfo)
    val intent = Intent(context, MarketActivity::class.java)
    val pendingIntent =
        PendingIntent.getActivity(
            context,
            todoInfo.id.toInt(),
            intent,
            PendingIntent.FLAG_CANCEL_CURRENT
        )
    var notifyAfterPI =
        PendingIntent.getBroadcast(
            context,
            todoInfo.id.toInt(),
            notifyAfterI,
            PendingIntent.FLAG_CANCEL_CURRENT
        )
    var alreadyKnowPI =
        PendingIntent.getBroadcast(
            context,
            todoInfo.id.toInt(),
            alreadyKnowI,
            PendingIntent.FLAG_CANCEL_CURRENT
        )

    notificationLayout.setOnClickPendingIntent(R.id.notify_after, notifyAfterPI)
    notificationLayout.setOnClickPendingIntent(R.id.already_know, alreadyKnowPI)

    notificationLayout.setTextViewText(R.id.notify_content, todoInfo.content)
    notificationLayout.setTextViewText(
        R.id.notify_date,
        "${todoInfo.year}-${todoInfo.month}-${todoInfo.day}    ${todoInfo.time}"
    )
    var notifyBuild: NotificationCompat.Builder? = null

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
        var notificationChannel =
            NotificationChannel(
                context.packageName,
                "todoNotify",
                NotificationManager.IMPORTANCE_HIGH
            )
        notificationChannel.lockscreenVisibility = Notification.VISIBILITY_SECRET
        notificationChannel.enableLights(true); //是否在桌面icon右上角展示小红点
        notificationChannel.lightColor = Color.RED; //小红点颜色
        notificationChannel.setShowBadge(true); //是否在久按桌面图标时显示此渠道的通知
        notificationManager.createNotificationChannel(notificationChannel)
        notifyBuild = NotificationCompat.Builder(context, todoInfo.id.toString())
        notifyBuild.setChannelId(context.packageName);

    } else {
        notifyBuild = NotificationCompat.Builder(context)
    }

    notifyBuild.setSmallIcon(R.mipmap.icon_todo_item_normal)
        .setStyle(NotificationCompat.DecoratedCustomViewStyle())
        .setCustomContentView(notificationLayout)
        .setPriority(NotificationCompat.PRIORITY_MAX)
        .setAutoCancel(true)
        .setContentIntent(pendingIntent)
        .build()
    notificationManager.notify(todoInfo.id.toInt(), notifyBuild.build())

}

通知的自定义布局中有两个按钮监听,这里是通过动态注册广播来实现的。

4、注册并实现监听

private fun initTodoNotifyEvent() {
    TodoNotifyReceiver.registerTodoNotifyReceiver(context, {
        //更新待办提醒时间
        TodoScheduleUseCase().delayTodoTask5min(it)
        TodoNotification().cancelNotifyById(it.id.toInt())
    }) {
        //已知悉,标记已完成
        TodoScheduleUseCase().markTodoTaskDone(it)
        TodoNotification().cancelNotifyById(it.id.toInt())
    }
}
    fun registerTodoNotifyReceiver(
        context: Context,
        block: (todoInfo: TodoInfo) -> Unit,
        block2: (todoInfo: TodoInfo) -> Unit
    ) {
        context.registerReceiver(TodoNotifyReceiver(block, block2), IntentFilter().apply {
            addAction(TODO_CHANGE_ACTION)
            addAction(TODO_ALREADY_KNOW_ACTION)
        })
    }

    private val TAG = MdmConstant.BaseTAG + TodoNotifyReceiver::class.java.simpleName
    var TODO_CHANGE_ACTION = "android.intent.action.ACTION_TODO_CHANGE"
    var TODO_ALREADY_KNOW_ACTION = "android.intent.action.ACTION_ALREADY_KNOW"
}
class TodoNotifyReceiver(
    private val block: (todoInfo: TodoInfo) -> Unit,
    private val block2: (todoInfo: TodoInfo) -> Unit
) : BroadcastReceiver() {

    override fun onReceive(context: Context, intent: Intent) {

        intent.let { intent ->

            val todoInfo = intent.getSerializableExtra("todoInfo") as TodoInfo
            SpeedyLog.i("yulu", "action = ${intent.action}")

            when (intent.action) {
                TODO_CHANGE_ACTION -> {
                    block.invoke(todoInfo)
                }
                TODO_ALREADY_KNOW_ACTION -> {
                    block2.invoke(todoInfo)
                }
            }
        }
    }
}

5、效果图展示

1684142392169.png

总结

以上就是定时消息提醒的实现方法,文章若出现错误,欢迎各位批评指正,文章乃原创,转载请注明出处谢谢❤。