diff --git a/README.md b/README.md index 9fa50ed..ec6dcfb 100644 --- a/README.md +++ b/README.md @@ -5,6 +5,8 @@ ![badge-android](http://img.shields.io/badge/platform-android-6EDB8D.svg?style=flat) ![badge-ios](http://img.shields.io/badge/platform-ios-CDCDCD.svg?style=flat) +![badge-desktop](https://img.shields.io/badge/platform-desktop-3474eb.svg?style=flat) + Simple and easy to use Kotlin Multiplatform Push Notification library (using Firebase Cloud Messaging) targeting ios and android. This library is used in [FindTravelNow](https://github.com/mirzemehdi/FindTravelNow-KMM/) production KMP project. @@ -12,7 +14,7 @@ You can check out [Documentation](https://mirzemehdi.github.io/KMPNotifier) for ## Features - 🔔 Local and Push Notification (Firebase Cloud Messaging) - - 📱 Multiplatform (android and iOS) + - 📱 Multiplatform (android, iOS and desktop(alpha)) (Desktop supports only local notification for now) ## Installation Before starting you need to setup basic setup using Firebase official guideline (like initializing project in Firebase, adding `google-services.json` to android, `GoogleService-Info.plist` to iOS). @@ -64,7 +66,7 @@ plugins { ### Platform Setup -In both platforms on Application Start you need to initialize library using +In all platforms on Application Start you need to initialize library using ```kotlin NotifierManager.initialize(NotificationPlatformConfiguration) //passing android or ios configuration depending on the platform ``` @@ -152,6 +154,39 @@ struct iOSApp: App { + + +
+ Desktop + +### Desktop Setup +You need to put notification icon into resources/common folder. For more information: +[Compose Desktop Resources](https://github.com/JetBrains/compose-multiplatform/blob/master/tutorials/Native_distributions_and_local_execution/README.md#packaging-resources) + ```kotlin +fun main() = application { + + NotifierManager.initialize( + NotificationPlatformConfiguration.Desktop( + showPushNotification = true, + notificationIconPath = composeDesktopResourcesPath() + File.separator + "ic_notification.png" + ) + ) + + AppInitializer.onApplicationStart() + Window( + onCloseRequest = ::exitApplication, + title = "KMPNotifier Desktop", + ) { + println("Desktop app is started") + App() + + } +} +``` + + + +
## Usage diff --git a/build.gradle.kts b/build.gradle.kts index 1fba091..a28eadc 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -21,7 +21,7 @@ plugins { allprojects { group = "io.github.mirzemehdi" - version = "1.0.1" + version = "1.1.0" val sonatypeUsername = gradleLocalProperties(rootDir).getProperty("sonatypeUsername") val sonatypePassword = gradleLocalProperties(rootDir).getProperty("sonatypePassword") val gpgKeySecret = gradleLocalProperties(rootDir).getProperty("gpgKeySecret") diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 8eaf580..f21d1f7 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -47,7 +47,6 @@ koin-core = { group = "io.insert-koin", name = "koin-core", version.ref = "koin" firebase-messaging = { group = "com.google.firebase", name = "firebase-messaging-ktx" ,version.ref="firebase-messaging"} - [plugins] jetbrainsCompose = { id = "org.jetbrains.compose", version.ref = "compose-plugin" } composeCompiler = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" } diff --git a/kmpnotifier/api/android/kmpnotifier.api b/kmpnotifier/api/android/kmpnotifier.api new file mode 100644 index 0000000..534da55 --- /dev/null +++ b/kmpnotifier/api/android/kmpnotifier.api @@ -0,0 +1,117 @@ +public final class com/mmk/kmpnotifier/extensions/NotifierManagerExtKt { + public static final fun onCreateOrOnNewIntent (Lcom/mmk/kmpnotifier/notification/NotifierManager;Landroid/content/Intent;)V +} + +public abstract interface class com/mmk/kmpnotifier/notification/Notifier { + public abstract fun notify (ILjava/lang/String;Ljava/lang/String;Ljava/util/Map;)V + public abstract fun notify (Ljava/lang/String;Ljava/lang/String;Ljava/util/Map;)I + public abstract fun remove (I)V + public abstract fun removeAll ()V +} + +public final class com/mmk/kmpnotifier/notification/Notifier$DefaultImpls { + public static synthetic fun notify$default (Lcom/mmk/kmpnotifier/notification/Notifier;ILjava/lang/String;Ljava/lang/String;Ljava/util/Map;ILjava/lang/Object;)V + public static synthetic fun notify$default (Lcom/mmk/kmpnotifier/notification/Notifier;Ljava/lang/String;Ljava/lang/String;Ljava/util/Map;ILjava/lang/Object;)I +} + +public final class com/mmk/kmpnotifier/notification/NotifierManager { + public static final field INSTANCE Lcom/mmk/kmpnotifier/notification/NotifierManager; + public final fun addListener (Lcom/mmk/kmpnotifier/notification/NotifierManager$Listener;)V + public final fun getLocalNotifier ()Lcom/mmk/kmpnotifier/notification/Notifier; + public final fun getPermissionUtil ()Lcom/mmk/kmpnotifier/permission/PermissionUtil; + public final fun getPushNotifier ()Lcom/mmk/kmpnotifier/notification/PushNotifier; + public final fun initialize (Lcom/mmk/kmpnotifier/notification/configuration/NotificationPlatformConfiguration;)V +} + +public abstract interface class com/mmk/kmpnotifier/notification/NotifierManager$Listener { + public abstract fun onNewToken (Ljava/lang/String;)V + public abstract fun onNotificationClicked (Ljava/util/Map;)V + public abstract fun onPayloadData (Ljava/util/Map;)V + public abstract fun onPushNotification (Ljava/lang/String;Ljava/lang/String;)V +} + +public final class com/mmk/kmpnotifier/notification/NotifierManager$Listener$DefaultImpls { + public static fun onNewToken (Lcom/mmk/kmpnotifier/notification/NotifierManager$Listener;Ljava/lang/String;)V + public static fun onNotificationClicked (Lcom/mmk/kmpnotifier/notification/NotifierManager$Listener;Ljava/util/Map;)V + public static fun onPayloadData (Lcom/mmk/kmpnotifier/notification/NotifierManager$Listener;Ljava/util/Map;)V + public static fun onPushNotification (Lcom/mmk/kmpnotifier/notification/NotifierManager$Listener;Ljava/lang/String;Ljava/lang/String;)V +} + +public abstract interface class com/mmk/kmpnotifier/notification/PushNotifier { + public abstract fun deleteMyToken (Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public abstract fun getToken (Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public abstract fun subscribeToTopic (Ljava/lang/String;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public abstract fun unSubscribeFromTopic (Ljava/lang/String;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; +} + +public abstract interface class com/mmk/kmpnotifier/notification/configuration/NotificationPlatformConfiguration { +} + +public final class com/mmk/kmpnotifier/notification/configuration/NotificationPlatformConfiguration$Android : com/mmk/kmpnotifier/notification/configuration/NotificationPlatformConfiguration { + public fun (ILjava/lang/Integer;Lcom/mmk/kmpnotifier/notification/configuration/NotificationPlatformConfiguration$Android$NotificationChannelData;Z)V + public synthetic fun (ILjava/lang/Integer;Lcom/mmk/kmpnotifier/notification/configuration/NotificationPlatformConfiguration$Android$NotificationChannelData;ZILkotlin/jvm/internal/DefaultConstructorMarker;)V + public final fun getNotificationChannelData ()Lcom/mmk/kmpnotifier/notification/configuration/NotificationPlatformConfiguration$Android$NotificationChannelData; + public final fun getNotificationIconColorResId ()Ljava/lang/Integer; + public final fun getNotificationIconResId ()I + public final fun getShowPushNotification ()Z +} + +public final class com/mmk/kmpnotifier/notification/configuration/NotificationPlatformConfiguration$Android$NotificationChannelData { + public fun ()V + public fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V + public synthetic fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public final fun getDescription ()Ljava/lang/String; + public final fun getId ()Ljava/lang/String; + public final fun getName ()Ljava/lang/String; +} + +public final class com/mmk/kmpnotifier/notification/configuration/NotificationPlatformConfiguration$Desktop : com/mmk/kmpnotifier/notification/configuration/NotificationPlatformConfiguration { + public fun ()V + public fun (ZLjava/lang/String;)V + public synthetic fun (ZLjava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public final fun component1 ()Z + public final fun component2 ()Ljava/lang/String; + public final fun copy (ZLjava/lang/String;)Lcom/mmk/kmpnotifier/notification/configuration/NotificationPlatformConfiguration$Desktop; + public static synthetic fun copy$default (Lcom/mmk/kmpnotifier/notification/configuration/NotificationPlatformConfiguration$Desktop;ZLjava/lang/String;ILjava/lang/Object;)Lcom/mmk/kmpnotifier/notification/configuration/NotificationPlatformConfiguration$Desktop; + public fun equals (Ljava/lang/Object;)Z + public final fun getNotificationIconPath ()Ljava/lang/String; + public final fun getShowPushNotification ()Z + public fun hashCode ()I + public fun toString ()Ljava/lang/String; +} + +public final class com/mmk/kmpnotifier/notification/configuration/NotificationPlatformConfiguration$Ios : com/mmk/kmpnotifier/notification/configuration/NotificationPlatformConfiguration { + public fun ()V + public fun (ZZ)V + public synthetic fun (ZZILkotlin/jvm/internal/DefaultConstructorMarker;)V + public final fun component1 ()Z + public final fun component2 ()Z + public final fun copy (ZZ)Lcom/mmk/kmpnotifier/notification/configuration/NotificationPlatformConfiguration$Ios; + public static synthetic fun copy$default (Lcom/mmk/kmpnotifier/notification/configuration/NotificationPlatformConfiguration$Ios;ZZILjava/lang/Object;)Lcom/mmk/kmpnotifier/notification/configuration/NotificationPlatformConfiguration$Ios; + public fun equals (Ljava/lang/Object;)Z + public final fun getAskNotificationPermissionOnStart ()Z + public final fun getShowPushNotification ()Z + public fun hashCode ()I + public fun toString ()Ljava/lang/String; +} + +public final class com/mmk/kmpnotifier/permission/AndroidPermissionUtil { + public fun (Landroidx/activity/ComponentActivity;)V + public final fun askNotificationPermission (Lkotlin/jvm/functions/Function1;)V + public static synthetic fun askNotificationPermission$default (Lcom/mmk/kmpnotifier/permission/AndroidPermissionUtil;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)V +} + +public final class com/mmk/kmpnotifier/permission/AndroidPermissionUtilKt { + public static final fun permissionUtil (Landroidx/activity/ComponentActivity;)Lkotlin/Lazy; +} + +public abstract interface class com/mmk/kmpnotifier/permission/PermissionUtil { + public abstract fun askNotificationPermission (Lkotlin/jvm/functions/Function0;)V + public abstract fun hasNotificationPermission (Lkotlin/jvm/functions/Function1;)V +} + +public final class com/mmk/kmpnotifier/permission/PermissionUtil$DefaultImpls { + public static synthetic fun askNotificationPermission$default (Lcom/mmk/kmpnotifier/permission/PermissionUtil;Lkotlin/jvm/functions/Function0;ILjava/lang/Object;)V + public static synthetic fun hasNotificationPermission$default (Lcom/mmk/kmpnotifier/permission/PermissionUtil;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)V +} + diff --git a/kmpnotifier/api/jvm/kmpnotifier.api b/kmpnotifier/api/jvm/kmpnotifier.api new file mode 100644 index 0000000..1bf571a --- /dev/null +++ b/kmpnotifier/api/jvm/kmpnotifier.api @@ -0,0 +1,107 @@ +public final class com/mmk/kmpnotifier/extensions/DesktopPlatformExtKt { + public static final fun composeDesktopResourcesPath ()Ljava/lang/String; +} + +public abstract interface class com/mmk/kmpnotifier/notification/Notifier { + public abstract fun notify (ILjava/lang/String;Ljava/lang/String;Ljava/util/Map;)V + public abstract fun notify (Ljava/lang/String;Ljava/lang/String;Ljava/util/Map;)I + public abstract fun remove (I)V + public abstract fun removeAll ()V +} + +public final class com/mmk/kmpnotifier/notification/Notifier$DefaultImpls { + public static synthetic fun notify$default (Lcom/mmk/kmpnotifier/notification/Notifier;ILjava/lang/String;Ljava/lang/String;Ljava/util/Map;ILjava/lang/Object;)V + public static synthetic fun notify$default (Lcom/mmk/kmpnotifier/notification/Notifier;Ljava/lang/String;Ljava/lang/String;Ljava/util/Map;ILjava/lang/Object;)I +} + +public final class com/mmk/kmpnotifier/notification/NotifierManager { + public static final field INSTANCE Lcom/mmk/kmpnotifier/notification/NotifierManager; + public final fun addListener (Lcom/mmk/kmpnotifier/notification/NotifierManager$Listener;)V + public final fun getLocalNotifier ()Lcom/mmk/kmpnotifier/notification/Notifier; + public final fun getPermissionUtil ()Lcom/mmk/kmpnotifier/permission/PermissionUtil; + public final fun getPushNotifier ()Lcom/mmk/kmpnotifier/notification/PushNotifier; + public final fun initialize (Lcom/mmk/kmpnotifier/notification/configuration/NotificationPlatformConfiguration;)V +} + +public abstract interface class com/mmk/kmpnotifier/notification/NotifierManager$Listener { + public abstract fun onNewToken (Ljava/lang/String;)V + public abstract fun onNotificationClicked (Ljava/util/Map;)V + public abstract fun onPayloadData (Ljava/util/Map;)V + public abstract fun onPushNotification (Ljava/lang/String;Ljava/lang/String;)V +} + +public final class com/mmk/kmpnotifier/notification/NotifierManager$Listener$DefaultImpls { + public static fun onNewToken (Lcom/mmk/kmpnotifier/notification/NotifierManager$Listener;Ljava/lang/String;)V + public static fun onNotificationClicked (Lcom/mmk/kmpnotifier/notification/NotifierManager$Listener;Ljava/util/Map;)V + public static fun onPayloadData (Lcom/mmk/kmpnotifier/notification/NotifierManager$Listener;Ljava/util/Map;)V + public static fun onPushNotification (Lcom/mmk/kmpnotifier/notification/NotifierManager$Listener;Ljava/lang/String;Ljava/lang/String;)V +} + +public abstract interface class com/mmk/kmpnotifier/notification/PushNotifier { + public abstract fun deleteMyToken (Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public abstract fun getToken (Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public abstract fun subscribeToTopic (Ljava/lang/String;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public abstract fun unSubscribeFromTopic (Ljava/lang/String;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; +} + +public abstract interface class com/mmk/kmpnotifier/notification/configuration/NotificationPlatformConfiguration { +} + +public final class com/mmk/kmpnotifier/notification/configuration/NotificationPlatformConfiguration$Android : com/mmk/kmpnotifier/notification/configuration/NotificationPlatformConfiguration { + public fun (ILjava/lang/Integer;Lcom/mmk/kmpnotifier/notification/configuration/NotificationPlatformConfiguration$Android$NotificationChannelData;Z)V + public synthetic fun (ILjava/lang/Integer;Lcom/mmk/kmpnotifier/notification/configuration/NotificationPlatformConfiguration$Android$NotificationChannelData;ZILkotlin/jvm/internal/DefaultConstructorMarker;)V + public final fun getNotificationChannelData ()Lcom/mmk/kmpnotifier/notification/configuration/NotificationPlatformConfiguration$Android$NotificationChannelData; + public final fun getNotificationIconColorResId ()Ljava/lang/Integer; + public final fun getNotificationIconResId ()I + public final fun getShowPushNotification ()Z +} + +public final class com/mmk/kmpnotifier/notification/configuration/NotificationPlatformConfiguration$Android$NotificationChannelData { + public fun ()V + public fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V + public synthetic fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public final fun getDescription ()Ljava/lang/String; + public final fun getId ()Ljava/lang/String; + public final fun getName ()Ljava/lang/String; +} + +public final class com/mmk/kmpnotifier/notification/configuration/NotificationPlatformConfiguration$Desktop : com/mmk/kmpnotifier/notification/configuration/NotificationPlatformConfiguration { + public fun ()V + public fun (ZLjava/lang/String;)V + public synthetic fun (ZLjava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public final fun component1 ()Z + public final fun component2 ()Ljava/lang/String; + public final fun copy (ZLjava/lang/String;)Lcom/mmk/kmpnotifier/notification/configuration/NotificationPlatformConfiguration$Desktop; + public static synthetic fun copy$default (Lcom/mmk/kmpnotifier/notification/configuration/NotificationPlatformConfiguration$Desktop;ZLjava/lang/String;ILjava/lang/Object;)Lcom/mmk/kmpnotifier/notification/configuration/NotificationPlatformConfiguration$Desktop; + public fun equals (Ljava/lang/Object;)Z + public final fun getNotificationIconPath ()Ljava/lang/String; + public final fun getShowPushNotification ()Z + public fun hashCode ()I + public fun toString ()Ljava/lang/String; +} + +public final class com/mmk/kmpnotifier/notification/configuration/NotificationPlatformConfiguration$Ios : com/mmk/kmpnotifier/notification/configuration/NotificationPlatformConfiguration { + public fun ()V + public fun (ZZ)V + public synthetic fun (ZZILkotlin/jvm/internal/DefaultConstructorMarker;)V + public final fun component1 ()Z + public final fun component2 ()Z + public final fun copy (ZZ)Lcom/mmk/kmpnotifier/notification/configuration/NotificationPlatformConfiguration$Ios; + public static synthetic fun copy$default (Lcom/mmk/kmpnotifier/notification/configuration/NotificationPlatformConfiguration$Ios;ZZILjava/lang/Object;)Lcom/mmk/kmpnotifier/notification/configuration/NotificationPlatformConfiguration$Ios; + public fun equals (Ljava/lang/Object;)Z + public final fun getAskNotificationPermissionOnStart ()Z + public final fun getShowPushNotification ()Z + public fun hashCode ()I + public fun toString ()Ljava/lang/String; +} + +public abstract interface class com/mmk/kmpnotifier/permission/PermissionUtil { + public abstract fun askNotificationPermission (Lkotlin/jvm/functions/Function0;)V + public abstract fun hasNotificationPermission (Lkotlin/jvm/functions/Function1;)V +} + +public final class com/mmk/kmpnotifier/permission/PermissionUtil$DefaultImpls { + public static synthetic fun askNotificationPermission$default (Lcom/mmk/kmpnotifier/permission/PermissionUtil;Lkotlin/jvm/functions/Function0;ILjava/lang/Object;)V + public static synthetic fun hasNotificationPermission$default (Lcom/mmk/kmpnotifier/permission/PermissionUtil;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)V +} + diff --git a/kmpnotifier/build.gradle.kts b/kmpnotifier/build.gradle.kts index c594d31..639f8be 100644 --- a/kmpnotifier/build.gradle.kts +++ b/kmpnotifier/build.gradle.kts @@ -16,7 +16,7 @@ kotlin { } } - + jvm() iosX64() iosArm64() iosSimulatorArm64() @@ -47,6 +47,11 @@ kotlin { implementation(libs.koin.core) implementation(libs.kotlinx.coroutine) } + + commonTest.dependencies { + implementation(libs.kotlin.test) + } + } } diff --git a/kmpnotifier/src/commonMain/kotlin/com/mmk/kmpnotifier/di/LibDependencyInitializer.kt b/kmpnotifier/src/commonMain/kotlin/com/mmk/kmpnotifier/di/LibDependencyInitializer.kt index 03b0a11..b01358f 100644 --- a/kmpnotifier/src/commonMain/kotlin/com/mmk/kmpnotifier/di/LibDependencyInitializer.kt +++ b/kmpnotifier/src/commonMain/kotlin/com/mmk/kmpnotifier/di/LibDependencyInitializer.kt @@ -42,13 +42,14 @@ private fun Koin.onLibraryInitialized() { get() //This will make sure that that when lib is initialized, init method is called when (platform) { - Platform.Android -> Unit //In Android platform permission should be asked in activity + Platform.Android, Platform.Desktop -> Unit //In Android platform permission should be asked in activity Platform.Ios -> { val askNotificationPermissionOnStart = (configuration as? NotificationPlatformConfiguration.Ios)?.askNotificationPermissionOnStart ?: true if (askNotificationPermissionOnStart) permissionUtil.askNotificationPermission() } + } } diff --git a/kmpnotifier/src/commonMain/kotlin/com/mmk/kmpnotifier/di/PlatformModule.kt b/kmpnotifier/src/commonMain/kotlin/com/mmk/kmpnotifier/di/PlatformModule.kt index 5d8d5a6..97e225d 100644 --- a/kmpnotifier/src/commonMain/kotlin/com/mmk/kmpnotifier/di/PlatformModule.kt +++ b/kmpnotifier/src/commonMain/kotlin/com/mmk/kmpnotifier/di/PlatformModule.kt @@ -6,5 +6,6 @@ import org.koin.core.module.Module internal sealed interface Platform { data object Android : Platform data object Ios : Platform + data object Desktop : Platform } internal expect val platformModule: Module \ No newline at end of file diff --git a/kmpnotifier/src/commonMain/kotlin/com/mmk/kmpnotifier/notification/configuration/NotificationPlatformConfiguration.kt b/kmpnotifier/src/commonMain/kotlin/com/mmk/kmpnotifier/notification/configuration/NotificationPlatformConfiguration.kt index 6a9261a..2c1363a 100644 --- a/kmpnotifier/src/commonMain/kotlin/com/mmk/kmpnotifier/notification/configuration/NotificationPlatformConfiguration.kt +++ b/kmpnotifier/src/commonMain/kotlin/com/mmk/kmpnotifier/notification/configuration/NotificationPlatformConfiguration.kt @@ -1,5 +1,6 @@ package com.mmk.kmpnotifier.notification.configuration + /** * You can configure some customization for notifications depending on the platform */ @@ -55,4 +56,10 @@ public sealed interface NotificationPlatformConfiguration { public val showPushNotification: Boolean = true, public val askNotificationPermissionOnStart: Boolean = true ) : NotificationPlatformConfiguration + + + public data class Desktop( + public val showPushNotification: Boolean = true, + public val notificationIconPath: String? = null + ) : NotificationPlatformConfiguration } \ No newline at end of file diff --git a/kmpnotifier/src/jvmMain/kotlin/com/mmk/kmpnotifier/di/PlatformModule.jvm.kt b/kmpnotifier/src/jvmMain/kotlin/com/mmk/kmpnotifier/di/PlatformModule.jvm.kt new file mode 100644 index 0000000..aae4cc1 --- /dev/null +++ b/kmpnotifier/src/jvmMain/kotlin/com/mmk/kmpnotifier/di/PlatformModule.jvm.kt @@ -0,0 +1,25 @@ +package com.mmk.kmpnotifier.di + +import com.mmk.kmpnotifier.firebase.FirebaseDesktopPushNotifier +import com.mmk.kmpnotifier.notification.DesktopNotifierFactory +import com.mmk.kmpnotifier.notification.Notifier +import com.mmk.kmpnotifier.notification.PushNotifier +import com.mmk.kmpnotifier.notification.configuration.NotificationPlatformConfiguration +import com.mmk.kmpnotifier.permission.DesktopPermissionUtil +import com.mmk.kmpnotifier.permission.PermissionUtil +import org.koin.core.module.Module +import org.koin.core.module.dsl.factoryOf +import org.koin.dsl.bind +import org.koin.dsl.module + +internal actual val platformModule: Module = module { + factory { Platform.Desktop } bind Platform::class + + factory { + val configuration = + get() as NotificationPlatformConfiguration.Desktop + DesktopNotifierFactory.getNotifier(configuration = configuration) + } bind Notifier::class + factoryOf(::DesktopPermissionUtil) bind PermissionUtil::class + factoryOf(::FirebaseDesktopPushNotifier) bind PushNotifier::class +} \ No newline at end of file diff --git a/kmpnotifier/src/jvmMain/kotlin/com/mmk/kmpnotifier/extensions/DesktopPlatformExt.kt b/kmpnotifier/src/jvmMain/kotlin/com/mmk/kmpnotifier/extensions/DesktopPlatformExt.kt new file mode 100644 index 0000000..c4d483c --- /dev/null +++ b/kmpnotifier/src/jvmMain/kotlin/com/mmk/kmpnotifier/extensions/DesktopPlatformExt.kt @@ -0,0 +1,27 @@ +package com.mmk.kmpnotifier.extensions + +import java.io.File + +internal sealed interface DesktopPlatform { + data object Linux : DesktopPlatform + data object Windows : DesktopPlatform + data object MacOs : DesktopPlatform +} + +internal fun getDesktopPlatformType(): DesktopPlatform? { + val name = System.getProperty("os.name") + return when { + name?.contains("Linux") == true -> DesktopPlatform.Linux + name?.contains("Win") == true -> DesktopPlatform.Windows + name?.contains("Mac") == true -> DesktopPlatform.MacOs + else -> null + } +} + +public fun composeDesktopResourcesPath(): String? { + return runCatching { + val resourcesDirectory = File(System.getProperty("compose.application.resources.dir")) + return resourcesDirectory.canonicalPath + }.getOrNull() + +} \ No newline at end of file diff --git a/kmpnotifier/src/jvmMain/kotlin/com/mmk/kmpnotifier/firebase/FirebaseDesktopPushNotifier.kt b/kmpnotifier/src/jvmMain/kotlin/com/mmk/kmpnotifier/firebase/FirebaseDesktopPushNotifier.kt new file mode 100644 index 0000000..6b5d2ec --- /dev/null +++ b/kmpnotifier/src/jvmMain/kotlin/com/mmk/kmpnotifier/firebase/FirebaseDesktopPushNotifier.kt @@ -0,0 +1,22 @@ +package com.mmk.kmpnotifier.firebase + +import com.mmk.kmpnotifier.notification.PushNotifier + +internal class FirebaseDesktopPushNotifier:PushNotifier { + override suspend fun getToken(): String? { + println("Get firebase toekn") + return null + } + + override suspend fun deleteMyToken() { + println("Delete firebase toekn") + } + + override suspend fun subscribeToTopic(topic: String) { + println("Subscribe firebase topic") + } + + override suspend fun unSubscribeFromTopic(topic: String) { + println("Unsubscribe firebase topic") + } +} \ No newline at end of file diff --git a/kmpnotifier/src/jvmMain/kotlin/com/mmk/kmpnotifier/notification/DesktopNotifierFactory.kt b/kmpnotifier/src/jvmMain/kotlin/com/mmk/kmpnotifier/notification/DesktopNotifierFactory.kt new file mode 100644 index 0000000..32c881c --- /dev/null +++ b/kmpnotifier/src/jvmMain/kotlin/com/mmk/kmpnotifier/notification/DesktopNotifierFactory.kt @@ -0,0 +1,15 @@ +package com.mmk.kmpnotifier.notification + +import com.mmk.kmpnotifier.notification.configuration.NotificationPlatformConfiguration +import com.mmk.kmpnotifier.notification.impl.JOptionPaneNotifier +import com.mmk.kmpnotifier.notification.impl.TrayNotifier + +internal object DesktopNotifierFactory { + fun getNotifier(configuration: NotificationPlatformConfiguration.Desktop): Notifier { + return when { + TrayNotifier.isSupported -> TrayNotifier(configuration = configuration) + //TODO for now return JOptionPaneNotifier for not supported platforms + else -> JOptionPaneNotifier(configuration = configuration) + } + } +} \ No newline at end of file diff --git a/kmpnotifier/src/jvmMain/kotlin/com/mmk/kmpnotifier/notification/impl/JOptionPaneNotifier.kt b/kmpnotifier/src/jvmMain/kotlin/com/mmk/kmpnotifier/notification/impl/JOptionPaneNotifier.kt new file mode 100644 index 0000000..6340759 --- /dev/null +++ b/kmpnotifier/src/jvmMain/kotlin/com/mmk/kmpnotifier/notification/impl/JOptionPaneNotifier.kt @@ -0,0 +1,34 @@ +package com.mmk.kmpnotifier.notification.impl + +import com.mmk.kmpnotifier.notification.Notifier +import com.mmk.kmpnotifier.notification.configuration.NotificationPlatformConfiguration +import javax.swing.ImageIcon +import javax.swing.JOptionPane + +internal class JOptionPaneNotifier(private val configuration: NotificationPlatformConfiguration.Desktop) : + Notifier { + + override fun notify(title: String, body: String, payloadData: Map): Int { + val id = -1 + notify(id = id, title = title, body = body, payloadData) + return id + } + + override fun notify(id: Int, title: String, body: String, payloadData: Map) { + JOptionPane.showMessageDialog( + null, + body, + title, + JOptionPane.INFORMATION_MESSAGE, + ImageIcon(configuration.notificationIconPath) + ) + } + + override fun remove(id: Int) { + println("No remove functionality") + } + + override fun removeAll() { + println("No removeAll functionality") + } +} \ No newline at end of file diff --git a/kmpnotifier/src/jvmMain/kotlin/com/mmk/kmpnotifier/notification/impl/TrayNotifier.kt b/kmpnotifier/src/jvmMain/kotlin/com/mmk/kmpnotifier/notification/impl/TrayNotifier.kt new file mode 100644 index 0000000..ed4fe07 --- /dev/null +++ b/kmpnotifier/src/jvmMain/kotlin/com/mmk/kmpnotifier/notification/impl/TrayNotifier.kt @@ -0,0 +1,58 @@ +package com.mmk.kmpnotifier.notification.impl + +import com.mmk.kmpnotifier.notification.Notifier +import com.mmk.kmpnotifier.notification.configuration.NotificationPlatformConfiguration +import java.awt.SystemTray +import java.awt.Toolkit +import java.awt.TrayIcon +import kotlin.random.Random + +internal class TrayNotifier(private val configuration: NotificationPlatformConfiguration.Desktop) : + Notifier { + + private val trayIcons: MutableMap = mutableMapOf() + + companion object { + val isSupported by lazy { + SystemTray.isSupported().also { + if (it.not()) System.err.println( + "Tray is not supported on the current platform. " + ) + } + } + } + + override fun notify(title: String, body: String, payloadData: Map): Int { + if (isSupported.not()) return -1 + val notificationID = Random.nextInt(0, Int.MAX_VALUE) + notify(notificationID, title, body, payloadData) + return notificationID + } + + override fun notify( + id: Int, + title: String, + body: String, + payloadData: Map + ) { + if (isSupported.not()) return + val icon = Toolkit.getDefaultToolkit().getImage(configuration.notificationIconPath) + val trayIcon = TrayIcon(icon).apply { + isImageAutoSize = true + } + SystemTray.getSystemTray().add(trayIcon) + .also { trayIcons[id] = trayIcon } + trayIcon.displayMessage(title, body, TrayIcon.MessageType.INFO) + } + + override fun remove(id: Int) { + val systemTray = SystemTray.getSystemTray() + val trayIcon = trayIcons.getOrDefault(id, null) + trayIcon?.let { systemTray.remove(it) } + } + + override fun removeAll() { + val systemTray = SystemTray.getSystemTray() + systemTray.trayIcons.forEach { systemTray.remove(it) } + } +} \ No newline at end of file diff --git a/kmpnotifier/src/jvmMain/kotlin/com/mmk/kmpnotifier/permission/DesktopPermissionUtil.kt b/kmpnotifier/src/jvmMain/kotlin/com/mmk/kmpnotifier/permission/DesktopPermissionUtil.kt new file mode 100644 index 0000000..5c5aa21 --- /dev/null +++ b/kmpnotifier/src/jvmMain/kotlin/com/mmk/kmpnotifier/permission/DesktopPermissionUtil.kt @@ -0,0 +1,13 @@ +package com.mmk.kmpnotifier.permission + +internal class DesktopPermissionUtil:PermissionUtil { + override fun hasNotificationPermission(onPermissionResult: (Boolean) -> Unit) { + println("Desktop has permission result") + onPermissionResult(true) + } + + override fun askNotificationPermission(onPermissionGranted: () -> Unit) { + println("Desktop ask permission") + onPermissionGranted() + } +} \ No newline at end of file diff --git a/sample/api/android/sample.api b/sample/api/android/sample.api new file mode 100644 index 0000000..78fbd7a --- /dev/null +++ b/sample/api/android/sample.api @@ -0,0 +1,47 @@ +public final class com/mmk/kmpnotifier/sample/AppInitializer { + public static final field $stable I + public static final field INSTANCE Lcom/mmk/kmpnotifier/sample/AppInitializer; + public final fun onApplicationStart ()V +} + +public final class com/mmk/kmpnotifier/sample/AppKt { + public static final fun App (Landroidx/compose/runtime/Composer;I)V +} + +public final class com/mmk/kmpnotifier/sample/ComposableSingletons$AppKt { + public static final field INSTANCE Lcom/mmk/kmpnotifier/sample/ComposableSingletons$AppKt; + public static field lambda-1 Lkotlin/jvm/functions/Function3; + public static field lambda-2 Lkotlin/jvm/functions/Function3; + public static field lambda-3 Lkotlin/jvm/functions/Function3; + public fun ()V + public final fun getLambda-1$sample_release ()Lkotlin/jvm/functions/Function3; + public final fun getLambda-2$sample_release ()Lkotlin/jvm/functions/Function3; + public final fun getLambda-3$sample_release ()Lkotlin/jvm/functions/Function3; +} + +public final class com/mmk/kmpnotifier/sample/ComposableSingletons$MainActivityKt { + public static final field INSTANCE Lcom/mmk/kmpnotifier/sample/ComposableSingletons$MainActivityKt; + public static field lambda-1 Lkotlin/jvm/functions/Function2; + public fun ()V + public final fun getLambda-1$sample_release ()Lkotlin/jvm/functions/Function2; +} + +public final class com/mmk/kmpnotifier/sample/MainActivity : androidx/activity/ComponentActivity { + public static final field $stable I + public fun ()V +} + +public final class com/mmk/kmpnotifier/sample/MainActivityKt { + public static final fun AppAndroidPreview (Landroidx/compose/runtime/Composer;I)V +} + +public final class com/mmk/kmpnotifier/sample/MainApplication : android/app/Application { + public static final field $stable I + public fun ()V + public fun onCreate ()V +} + +public final class com/mmk/kmpnotifier/sample/Platform_androidKt { + public static final fun onApplicationStartPlatformSpecific ()V +} + diff --git a/sample/api/desktop/sample.api b/sample/api/desktop/sample.api new file mode 100644 index 0000000..9289235 --- /dev/null +++ b/sample/api/desktop/sample.api @@ -0,0 +1,39 @@ +public final class com/mmk/kmpnotifier/sample/AppInitializer { + public static final field $stable I + public static final field INSTANCE Lcom/mmk/kmpnotifier/sample/AppInitializer; + public final fun onApplicationStart ()V +} + +public final class com/mmk/kmpnotifier/sample/AppKt { + public static final fun App (Landroidx/compose/runtime/Composer;I)V +} + +public final class com/mmk/kmpnotifier/sample/ComposableSingletons$AppKt { + public static final field INSTANCE Lcom/mmk/kmpnotifier/sample/ComposableSingletons$AppKt; + public static field lambda-1 Lkotlin/jvm/functions/Function3; + public static field lambda-2 Lkotlin/jvm/functions/Function3; + public static field lambda-3 Lkotlin/jvm/functions/Function3; + public fun ()V + public final fun getLambda-1$sample ()Lkotlin/jvm/functions/Function3; + public final fun getLambda-2$sample ()Lkotlin/jvm/functions/Function3; + public final fun getLambda-3$sample ()Lkotlin/jvm/functions/Function3; +} + +public final class com/mmk/kmpnotifier/sample/ComposableSingletons$MainKt { + public static final field INSTANCE Lcom/mmk/kmpnotifier/sample/ComposableSingletons$MainKt; + public static field lambda-1 Lkotlin/jvm/functions/Function3; + public static field lambda-2 Lkotlin/jvm/functions/Function3; + public fun ()V + public final fun getLambda-1$sample ()Lkotlin/jvm/functions/Function3; + public final fun getLambda-2$sample ()Lkotlin/jvm/functions/Function3; +} + +public final class com/mmk/kmpnotifier/sample/MainKt { + public static final fun main ()V + public static synthetic fun main ([Ljava/lang/String;)V +} + +public final class com/mmk/kmpnotifier/sample/Platform_desktopKt { + public static final fun onApplicationStartPlatformSpecific ()V +} + diff --git a/sample/build.gradle.kts b/sample/build.gradle.kts index 5cbb6fe..1ae25be 100644 --- a/sample/build.gradle.kts +++ b/sample/build.gradle.kts @@ -1,4 +1,5 @@ import org.jetbrains.compose.ExperimentalComposeLibrary +import org.jetbrains.compose.desktop.application.dsl.TargetFormat plugins { alias(libs.plugins.kotlinMultiplatform) @@ -17,6 +18,7 @@ kotlin { } } } + jvm("desktop") listOf( iosX64(), iosArm64(), @@ -29,6 +31,7 @@ kotlin { } } sourceSets { + val desktopMain by getting androidMain.dependencies { implementation(libs.compose.ui) @@ -39,10 +42,13 @@ kotlin { implementation(compose.runtime) implementation(compose.foundation) implementation(compose.material) - @OptIn(ExperimentalComposeLibrary::class) implementation(compose.components.resources) api(project(":kmpnotifier")) } + desktopMain.dependencies { + implementation(compose.desktop.currentOs) + implementation(compose.desktop.common) + } } } @@ -82,4 +88,15 @@ android { debugImplementation(libs.compose.ui.tooling) } } +compose.desktop { + application { + mainClass = "com.mmk.kmpnotifier.sample.MainKt" + nativeDistributions { + targetFormats(TargetFormat.Dmg, TargetFormat.Msi, TargetFormat.Deb) + packageName = "KMPNotifier" + packageVersion = "1.0.0" + appResourcesRootDir.set(project.layout.projectDirectory.dir("resources")) + } + } +} diff --git a/sample/resources/common/ic_notification.png b/sample/resources/common/ic_notification.png new file mode 100644 index 0000000..4a0728a Binary files /dev/null and b/sample/resources/common/ic_notification.png differ diff --git a/sample/src/commonMain/composeResources/drawable/ic_notification.png b/sample/src/commonMain/composeResources/drawable/ic_notification.png new file mode 100644 index 0000000..4a0728a Binary files /dev/null and b/sample/src/commonMain/composeResources/drawable/ic_notification.png differ diff --git a/sample/src/commonMain/kotlin/com/mmk/kmpnotifier/sample/App.kt b/sample/src/commonMain/kotlin/com/mmk/kmpnotifier/sample/App.kt index 8ab6ab3..c85a110 100644 --- a/sample/src/commonMain/kotlin/com/mmk/kmpnotifier/sample/App.kt +++ b/sample/src/commonMain/kotlin/com/mmk/kmpnotifier/sample/App.kt @@ -23,6 +23,7 @@ import com.mmk.kmpnotifier.notification.NotifierManager fun App() { var myPushNotificationToken by remember { mutableStateOf("") } LaunchedEffect(true) { + println("LaunchedEffectApp is called") NotifierManager.addListener(object : NotifierManager.Listener { override fun onNewToken(token: String) { diff --git a/sample/src/desktopMain/kotlin/com/mmk/kmpnotifier/sample/Main.kt b/sample/src/desktopMain/kotlin/com/mmk/kmpnotifier/sample/Main.kt new file mode 100644 index 0000000..2634374 --- /dev/null +++ b/sample/src/desktopMain/kotlin/com/mmk/kmpnotifier/sample/Main.kt @@ -0,0 +1,17 @@ +package com.mmk.kmpnotifier.sample + +import androidx.compose.ui.window.Window +import androidx.compose.ui.window.application + + +fun main() = application { + AppInitializer.onApplicationStart() + Window( + onCloseRequest = ::exitApplication, + title = "KMPNotifier Desktop", + ) { + println("Desktop app is started") + App() + + } +} \ No newline at end of file diff --git a/sample/src/desktopMain/kotlin/com/mmk/kmpnotifier/sample/Platform.desktop.kt b/sample/src/desktopMain/kotlin/com/mmk/kmpnotifier/sample/Platform.desktop.kt new file mode 100644 index 0000000..8944288 --- /dev/null +++ b/sample/src/desktopMain/kotlin/com/mmk/kmpnotifier/sample/Platform.desktop.kt @@ -0,0 +1,16 @@ +package com.mmk.kmpnotifier.sample + +import com.mmk.kmpnotifier.extensions.composeDesktopResourcesPath +import com.mmk.kmpnotifier.notification.NotifierManager +import com.mmk.kmpnotifier.notification.configuration.NotificationPlatformConfiguration +import java.io.File + +actual fun onApplicationStartPlatformSpecific() { + println("Desktop app is initialized") + NotifierManager.initialize( + NotificationPlatformConfiguration.Desktop( + showPushNotification = true, + notificationIconPath = composeDesktopResourcesPath() + File.separator + "ic_notification.png" + ) + ) +} \ No newline at end of file