Kotlin Compose Multiplatform下全局通知组件构建

原文链接

欢迎大家对于本站的访问 - AsterCasc

前言

在前文Kotlin Compose Multiplatform下全局通知组件构建,我们介绍了在应用软件内的通知方式,这里我们处理应用软件外系统层级的通知的。在安卓端我们正常使用androidx.core.app.NotificationCompat进行消息的通知处理。而在桌面端,由于常用的使用习惯,我们使用系统托盘闪烁的方式进行消息通知,当然这里小伙伴们也可以选择正常的通知方式,如果这样可以参考使用KMPNotifier进行处理

实现

我们这里使用Koin进行依赖注入,对这里不太了解的小伙伴可以参考前文Kotlin Compose Multiplatform下导航解决方案中的Koin部分,当然也可以自己使用静态对象做单例做相似处理

我们这里主要需要在公共部分调用三个方法:发送系统通知,清除系统通知,以及检查通知权限(移动端):

expect fun sendAppNotification(title: String, content: String)  
  
expect fun clearAppNotification()  
  
@Composable  
expect fun CheckAppNotificationPermission(requestPermission: (()->Unit) -> Unit)

安卓端

安卓端初始化

对于安卓端来说,我们需要先初始化通知渠道:

override fun onCreate(savedInstanceState: Bundle?) {  
	//...
  
    super.onCreate(savedInstanceState)  
    
    createAppNotificationChannel(this)  
  
    setContent {  
        MainApp(  
        //...
        )  
  
    }  
}
fun createAppNotificationChannel(context: Context) {  
    val channelId = "your_id"  
    val channelName = "Your Name"  
    val channelDescription = "Your Desc"  
    val importance = NotificationManager.IMPORTANCE_DEFAULT  
    val channel = NotificationChannel(channelId, channelName, importance).apply {  
        description = channelDescription  
    }  
    val notificationManager: NotificationManager =  
        context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager  
    notificationManager.createNotificationChannel(channel)  
}

对于通知来说,我们需要在AndroidManifest添加POST_NOTIFICATIONS权限:

<?xml version="1.0" encoding="utf-8"?>  
<manifest xmlns:android="http://schemas.android.com/apk/res/android">  
	<!--    ...-->
    <uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
    <!--    ...-->
</manifest>

对于检查通知权限的实现:

@SuppressLint("PermissionLaunchedDuringComposition")  
@RequiresApi(Build.VERSION_CODES.TIRAMISU)  
@OptIn(ExperimentalPermissionsApi::class)  
@Composable  
actual fun CheckAppNotificationPermission(  
    requestPermission: (() -> Unit) -> Unit  
) {  
  
    val notificationPermission = rememberPermissionState(  
        permission = Manifest.permission.POST_NOTIFICATIONS  
    )  
  
    if (!notificationPermission.status.isGranted) {  
        requestPermission {  
            notificationPermission.launchPermissionRequest()  
        }  
    }  
}

我们这里requestPermission是提供了一个参数为函数的函数,目的是为了将launchPermissionRequest() 方法传入NotificationManager.createDialogAlert给用户提示,并开启权限。关于createDialogAlert的内容参考前文Kotlin Compose Multiplatform下全局通知组件构建。使用时代码如下:

CheckAppNotificationPermission { func ->  
    NotificationManager.createDialogAlert(  
        MainDialogAlert(  
            message = "Need Notification Permission",  
            confirmOperationText = "Confirm",  
            confirmOperation = {  
                func()  
                NotificationManager.removeDialogAlert()  
            },  
        )  
    )  
}

这里只是核心代码示例,小伙伴们写的时候还需要更多处理来保证用户体验,比如CheckAppNotificationPermission的调用时机和次数

安卓端发送和清除通知

对于发送通知:

actual fun sendAppNotification(title: String, content: String) {  
    val context = MainActivity.mainContext!!  
  
    val channelId = "some_channel_idaa"  
  
    val builder = NotificationCompat.Builder(context, channelId)  
        .setSmallIcon(android.R.drawable.ic_dialog_info)  
        .setContentTitle(title)  
        .setContentText(content)  
        .setPriority(NotificationCompat.PRIORITY_DEFAULT)  
  
    val notificationManager: NotificationManager =  
        context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager  
  
    notificationManager.notify(notificationId++, builder.build())
}

对于通知清除:

actual fun clearAppNotification() {  
    val context = MainActivity.mainContext!!  
    val notificationManager =  
        context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager  
    notificationManager.cancelAll()  
}

这里对于notificationId的处理,我们可以使用方法内部生成的id,也可以从服务端获取处理,根据小伙伴的流程设计决定

桌面端

桌面端系统托盘一般有两种处理方式,一种是图标闪动,一种是角标标红。角标标红比较简单,我们这里以图标闪动作为通知来演示桌面端的处理

桌面端初始化

如果要在桌面端实现系统托盘,一般的处理是使用java.awt.SystemTray,首先在外部创建全局托盘和Koin

lateinit var koin: Koin  
val tray: SystemTray = SystemTray.getSystemTray()

main函数中初始化:

fun main() {  

    koin = KoinInit().init()  
    
	//...
  
    val showIcon: BufferedImage = koin.get(named("showIcon"))  
    val trayIcon = TrayIcon(showIcon, "Tomoyo")  
    tray.add(trayIcon)  
  
    application {  
		//...
        trayIcon.apply {  
            isImageAutoSize = true  
            addMouseListener(  
	            //...
            )  
        }  
  
        Window(  
			//...
        ) {  
	        //...
        }  
    }}

由于我们在Koin初始化的方法中包含了平台初始化模块:

class KoinInit {  
    fun init(appDeclaration: KoinAppDeclaration = {}): Koin {  
        return startKoin {  
            modules(  
                listOf(  
	                //...
                    platformModule(),  
                ),  
            )  
            appDeclaration()  
        }.koin  
    }  
}

expect fun platformModule(): Module

而对于桌面端的平台初始化模块中包含两张闪动图标的资源的初始化:

@OptIn(ExperimentalSettingsApi::class)  
actual fun platformModule(): Module = module {  

	//...
  
    single<BufferedImage>(qualifier = named("showIcon")) {  
        ImageIO.read(  
            Thread.currentThread().contextClassLoader  
                .getResource("logo.png")  
        )  
  
    }  
  
    single<BufferedImage>(qualifier = named("hideIcon")) {  
        ImageIO.read(  
            Thread.currentThread().contextClassLoader  
                .getResource("notification-lightning.png")  
        )  
    }  
}

所以我们在主函数中可以直接使用val showIcon: BufferedImage = koin.get(named("showIcon")) 拿到图标资源。由于我们需要图标闪动,故而我们一般的处理方案是开一个定时器切换图标资源进行闪动,如果小伙伴选择红标通知,那么就不需要定时器了只需要切红标图片即可:

val timer = Timer(500, object : ActionListener {  
    var toggle = true  
    val showIcon: BufferedImage = koin.get(named("showIcon"))  
    val hideIcon: BufferedImage = koin.get(named("hideIcon"))  
  
  
    override fun actionPerformed(event: java.awt.event.ActionEvent?) {  
        tray.trayIcons[0].image = if (toggle) showIcon else hideIcon  
        toggle = !toggle  
  
    }  
})
桌面端发送和清除通知

对于发送通知:

actual fun sendAppNotification(title: String, content: String) {  
    timer.start()  
}

对于清除通知:

actual fun clearAppNotification() {  
    timer.stop()  
    val showIcon: BufferedImage = koin.get(named("showIcon"))  
    tray.trayIcons[0].image = showIcon  
}

源码

Tomoyo

参考资料

KMPNotifier

permission-requesting

notification-permission

Jetpack Compose Permissions

Jetpack Compose Permissions Sample

原文链接

欢迎大家对于本站的访问 - AsterCasc

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值