当前位置: 首页 > news >正文

android端自定义通话通知

在这里插入图片描述

收到通知后实现目标:

  1. 显示接听拒接
  2. 锁屏时,则亮起屏幕
  3. 响铃
  4. 震动

功能分析:

  1. 收到通知后将Intent传递到拉起自定义通知的处理方法(startCustomNotify)
  2. 定义通知管类(CallkitNotificationManager),用来管理通知通道的创建和销毁。
  3. 如果需要自定义全屏,则需要自定义activity
  4. 创建广播监听类(CallkitIncomingBroadcastReceiver),用来处理接听或挂断

一、拉起通知的方法(startCustomNotify)

Application.java

	public void startCustomNotify(Intent intent) {CallkitNotificationManager manager = new CallkitNotificationManager(this);String title = (String) Objects.requireNonNull(Objects.requireNonNull(intent.getExtras()).get("gcm.notification.body"));manager.showIncomingNotification(title);}

二、自定义通知的管理类

CallkitNotificationManager.kt

import android.annotation.SuppressLint
import android.app.Activity
import android.app.Notification
import android.app.NotificationChannel
import android.app.NotificationManager
import android.app.PendingIntent
import android.content.Context
import android.content.Intent
import android.graphics.Color
import android.net.Uri
import android.os.Build
import android.os.Handler
import android.os.Looper
import android.os.PowerManager
import android.os.VibrationEffect
import android.os.Vibrator
import android.os.VibratorManager
import androidx.core.app.NotificationCompat
import androidx.core.app.NotificationManagerCompat
import androidx.core.app.Person
import com.ilifesmart.mslict.MslIctApplicationclass CallkitNotificationManager(private val context: Context) {companion object {const val NOTIFICATION_CHANNEL_ID_INCOMING = "callkit_incoming_channel_id"}private var notificationBuilder: NotificationCompat.Builder? = nullprivate fun getNotificationManager(): NotificationManagerCompat {return NotificationManagerCompat.from(context)}// 唤醒屏幕private fun wakeLockRequest() {val pm = context.getSystemService(Activity.POWER_SERVICE) as PowerManagerval wakeLock = pm.newWakeLock(PowerManager.SCREEN_BRIGHT_WAKE_LOCK or PowerManager.FULL_WAKE_LOCK or PowerManager.ACQUIRE_CAUSES_WAKEUP,"Callkit:PowerManager")wakeLock.acquire(3000L)}@SuppressLint("MissingPermission")fun showIncomingNotification(title: String) {wakeLockRequest()Handler(Looper.getMainLooper()).postDelayed({playVibrator()val callkitNotification = getIncomingNotification(title)callkitNotification?.let {getNotificationManager().notify(it.id, callkitNotification.notification)}}, 500)}fun clearIncomingNotification(isAccepted: Boolean) {vibrator?.cancel()vibrator = nullcontext.sendBroadcast(CallkitIncomingActivity.getIntentEnded(context, isAccepted))val notificationId ="callkit_incoming".hashCode()getNotificationManager().cancel(notificationId)if (isAccepted) {MslIctApplication.getApplication().startActivityInNotify()}}// 获取Notification@SuppressLint("MissingPermission")fun getIncomingNotification(title: String): CallkitNotification? {val notificationId ="callkit_incoming".hashCode()createNotificationChanel()notificationBuilder = NotificationCompat.Builder(context, NOTIFICATION_CHANNEL_ID_INCOMING)val caller = Person.Builder().setName(title).setImportant(true).build()// 设置图标,这个必须要写,值可以自定义notificationBuilder?.setSmallIcon(context.applicationInfo.icon)// 优先级,最好写这个等级notificationBuilder?.priority = NotificationCompat.PRIORITY_MAX// 设置全屏的通知,当使用NotificationCompat.CallStyle时必须绑定前台服务/fullScreenIntent/user-initiated jobnotificationBuilder?.setFullScreenIntent(getActivityPendingIntent(notificationId), true)// 自动超时时间notificationBuilder?.setTimeoutAfter(20 * 1000L)// 点击通知后自动移除通知notificationBuilder?.setAutoCancel(false)// 持续进行的通知,除非卸载APP或手动清除通知
//        notificationBuilder?.setOngoing(true)// 仅限第一次显示时会有声音/震动notificationBuilder?.setOnlyAlertOnce(true)// 设置呼吸灯notificationBuilder?.setLights(Color.RED, 1000, 1000)// 设置通知的发布时间notificationBuilder?.setWhen(System.currentTimeMillis())if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {notificationBuilder?.setStyle(NotificationCompat.CallStyle.forIncomingCall(caller,createDeclineIntent(context),createAnswerIntent(context),))} else {notificationBuilder?.setContentTitle(title)val declineAction: NotificationCompat.Action = NotificationCompat.Action.Builder(R.drawable.ic_decline,context.getString(R.string.text_decline),createDeclineIntent(context)).build()notificationBuilder?.addAction(declineAction)val acceptAction: NotificationCompat.Action = NotificationCompat.Action.Builder(R.drawable.ic_accept,context.getString(R.string.text_accept),createAnswerIntent(context)).build()notificationBuilder?.addAction(acceptAction)}val notification  = notificationBuilder?.build()return notification?.let { CallkitNotification(notificationId, it) }}// 拒绝时的处理事件,实际绑定到CallkitIncomingBroadcastReceiver的广播监听private fun createDeclineIntent(context: Context): PendingIntent {val intent = Intent(context, CallkitIncomingBroadcastReceiver::class.java).setAction(CallkitConstants.ACTION_CALL_DECLINE)return PendingIntent.getBroadcast(context, 0, intent, getFlagPendingIntent())}// 接听时的处理事件,实际绑定到CallkitIncomingBroadcastReceiver的广播监听private fun createAnswerIntent(context: Context): PendingIntent {val intent = Intent(context, CallkitIncomingBroadcastReceiver::class.java).setAction(CallkitConstants.ACTION_CALL_ACCEPT)return PendingIntent.getBroadcast(context, 1, intent, getFlagPendingIntent())}// CallkitIncomingActivity全屏显示的页面private fun getActivityPendingIntent(id: Int): PendingIntent {val intent = CallkitIncomingActivity.getIntent(context)return PendingIntent.getActivity(context, id, intent, getFlagPendingIntent())}private fun getFlagPendingIntent(): Int {val flags = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE} else {PendingIntent.FLAG_UPDATE_CURRENT}return flags}private var vibrator: Vibrator? = null// 播放震动private fun playVibrator() {vibrator = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {val vibratorManager =context.getSystemService(Context.VIBRATOR_MANAGER_SERVICE) as VibratorManagervibratorManager.defaultVibrator} else {context.getSystemService(Context.VIBRATOR_SERVICE) as Vibrator}if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {vibrator?.vibrate(VibrationEffect.createWaveform(longArrayOf(0L, 200L, 100L, 300L),0))} else {vibrator?.vibrate(longArrayOf(0L, 200L, 100L, 300L), 0)}}// 创建通知通道@SuppressLint("DiscouragedApi")private fun createNotificationChanel() {val resId = context.resources.getIdentifier("ringtone_default", "raw", context.packageName)if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {getNotificationManager().apply {var channelCall = getNotificationChannel(NOTIFICATION_CHANNEL_ID_INCOMING)if (channelCall == null) {channelCall = NotificationChannel(NOTIFICATION_CHANNEL_ID_INCOMING,"Incoming Call",NotificationManager.IMPORTANCE_HIGH).apply {description = "Door Phone Call Notification"lightColor = Color.REDenableLights(true)
//                        enableVibration(true)}}channelCall.lockscreenVisibility = Notification.VISIBILITY_PUBLICchannelCall.importance = NotificationManager.IMPORTANCE_HIGHchannelCall.setSound(Uri.parse("android.resource://" + context.packageName + "/$resId"), Notification.AUDIO_ATTRIBUTES_DEFAULT)createNotificationChannel(channelCall)}}}fun destroy() {vibrator?.cancel()vibrator = null}
}data class CallkitNotification(val id: Int, val notification: Notification)

三、自定义通知的管理类(CallkitIncomingActivity)

我是参考的这里的
flutter_callkit_incoming/CallkitIncomingActivity.kt

四、广播监听类

CallkitIncomingBroadcastReceiver.kt

import android.annotation.SuppressLint
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.os.Bundle
import android.util.Log
import com.ilifesmart.mslict.notibar.NotificationMgrclass CallkitIncomingBroadcastReceiver : BroadcastReceiver() {companion object {private const val TAG = "CallkitIncomingReceiver"fun getIntentDecline(context: Context, data: Bundle?) =Intent(context, CallkitIncomingBroadcastReceiver::class.java).apply {action = "${context.packageName}.ACTION_CALL_DECLINE"putExtra("EXTRA_CALLKIT_INCOMING_DATA", data)}}@SuppressLint("MissingPermission")override fun onReceive(context: Context, intent: Intent) {val action = intent.action ?: returnval callkitNotificationManager = CallkitNotificationManager(context)when (action) {CallkitConstants.ACTION_CALL_DECLINE -> {try {callkitNotificationManager.clearIncomingNotification(false)// 处理拒绝逻辑} catch (error: Exception) {Log.e(TAG, null, error)}}CallkitConstants.ACTION_CALL_ACCEPT -> {try {NotificationMgr.getInstance().acceptCallNotify()// 处理接听逻辑} catch (error: Exception) {Log.e(TAG, null, error)}}}}
}

五、常量

CallkitConstants.kt

object CallkitConstants {const val ACTION_CALL_ACCEPT ="com.example.app.ACTION_CALL_ACCEPT"const val ACTION_CALL_DECLINE ="com.example.app.ACTION_CALL_DECLINE"
}

六、注意

// 允许在锁屏时显示界面可以在onCreate中添加如下
getWindow().addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED| WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON| WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON| WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD);
http://www.dtcms.com/a/327682.html

相关文章:

  • VUE的8个生命周期
  • Orange的运维学习日记--41.Ansible基础入门
  • sqli-labs通关笔记-第44关 POST字符型堆叠注入(单引号闭合 手工注入+脚本注入3种方法)
  • demo 英雄热度榜 (条件筛选—高亮切换—列表渲染—日期显示)
  • Full GC 频率优化实战
  • RGWRados::get_obj_state_impl()
  • 25C机场航班调度程序(JS 100)
  • 【智能硬件】2025年儿童智能手表革命:守护隐私的科技堡垒
  • AQS的理解
  • B树索引和B+树索引有什么区别?
  • 编译 BusyBox for ARM 平台
  • 数据结构:图
  • 1、正则表达式入门
  • (LeetCode 每日一题) 2787. 将一个数字表示成幂的和的方案数(动态规划dp+01背包)
  • Python 常用的正则表达式
  • CodeRush AI 助手进驻 Visual Studio:AiGen/AiFind 亮相(五)
  • RL推理的尽头,是熵坍缩?统一SFT与强化学习的新视角
  • 零基础学Java第七讲---调试(IDEA)
  • 面试经典150题[001]:合并两个有序数组(LeetCode 88)
  • 【代码随想录day 17】 力扣 98.验证二叉搜索树
  • iis无法访问文件
  • NTP常见日志分析
  • 每日五个pyecharts可视化图表-line:从入门到精通 (4)
  • 多轮问答与指代消解
  • 测试匠谈 | AI语音合成之大模型性能优化实践
  • @JsonAnyGetter 动态表格渲染的“神”
  • 「机器学习」:金融风控贷款违约预测,天池比赛解决详细思路
  • Redis面试精讲 Day 19:Redis缓存设计模式与策略
  • 剑指offer第2版——面试题3:数组中重复的数字
  • RabbitMQ-知识技能图谱(总结篇)