diff --git a/kmpnotifier/src/androidMain/kotlin/com/mmk/kmpnotifier/di/PlatformModule.android.kt b/kmpnotifier/src/androidMain/kotlin/com/mmk/kmpnotifier/di/PlatformModule.android.kt index 61c2baf..2e89439 100644 --- a/kmpnotifier/src/androidMain/kotlin/com/mmk/kmpnotifier/di/PlatformModule.android.kt +++ b/kmpnotifier/src/androidMain/kotlin/com/mmk/kmpnotifier/di/PlatformModule.android.kt @@ -42,6 +42,7 @@ internal actual val platformModule = module { context = get(), channelData = configuration.notificationChannelData ), + displayNotificationManager = configuration.displayNotificationManager, permissionUtil = get() ) } bind Notifier::class diff --git a/kmpnotifier/src/androidMain/kotlin/com/mmk/kmpnotifier/notification/AndroidNotificationData.kt b/kmpnotifier/src/androidMain/kotlin/com/mmk/kmpnotifier/notification/AndroidNotificationData.kt new file mode 100644 index 0000000..0f33bb8 --- /dev/null +++ b/kmpnotifier/src/androidMain/kotlin/com/mmk/kmpnotifier/notification/AndroidNotificationData.kt @@ -0,0 +1,99 @@ +package com.mmk.kmpnotifier.notification + +import android.app.NotificationManager +import android.app.PendingIntent +import android.content.Context +import com.mmk.kmpnotifier.notification.configuration.NotificationPlatformConfiguration + +public class AndroidNotificationData private constructor( + public val context: Context, + public val notificationManager: NotificationManager +) { + public var notificationChannelData: NotificationPlatformConfiguration.Android.NotificationChannelData? = null + private set + public var notificationId: Int = -1 + private set + public var notificationIconResId: Int = -1 + private set + public var notificationIconColorResId: Int? = null + private set + public var pendingIntent: PendingIntent? = null + private set + public var payloadData: Map? = null + private set + + internal companion object { + + /** + * Builder that assembles data required for the Android notification + * @param context Android application context. By default + * using androidx-startup Context reference is passed without needing to pass one manually. + * @param notificationManager System notification manager that could post notifications + */ + class Builder(context: Context, notificationManager: NotificationManager) { + private val builder: AndroidNotificationData = AndroidNotificationData( + context = context, + notificationManager = notificationManager + ) + + /** + * Set notification id + * @param notificationId ID of the notification + */ + fun setNotificationId(notificationId: Int): Builder { + builder.notificationId = notificationId + return this + } + + /** + * Set channel data + * @param channelData notification channel data for General or Miscellaneous notifications + */ + fun setChannelData( + channelData: NotificationPlatformConfiguration.Android.NotificationChannelData + ): Builder { + builder.notificationChannelData = channelData + return this + } + + /** + * @param notificationIconResId icon ResourceId (R.drawable.ic_notification) + */ + fun setNotificationIconRes(notificationIconResId: Int): Builder { + builder.notificationIconResId = notificationIconResId + return this + } + + /** + * @param notificationIconColorResId icon color ResourceId (R.color.yellow) + */ + fun setNotificationIconColorResId(notificationIconColorResId: Int?): Builder { + builder.notificationIconColorResId = notificationIconColorResId + return this + } + + /** + * @param pendingIntent action that invokes on notification click + */ + fun setPendingIntent(pendingIntent: PendingIntent?): Builder { + builder.pendingIntent = pendingIntent + return this + } + + /** + * @param payloadData extra data for the push notification + */ + fun setPayloadData(payloadData: Map): Builder { + builder.payloadData = payloadData + return this + } + + /** + * @return notification data required for its displaying + */ + fun build(): AndroidNotificationData { + return builder + } + } + } +} \ No newline at end of file diff --git a/kmpnotifier/src/androidMain/kotlin/com/mmk/kmpnotifier/notification/AndroidNotificationManager.kt b/kmpnotifier/src/androidMain/kotlin/com/mmk/kmpnotifier/notification/AndroidNotificationManager.kt new file mode 100644 index 0000000..66484bf --- /dev/null +++ b/kmpnotifier/src/androidMain/kotlin/com/mmk/kmpnotifier/notification/AndroidNotificationManager.kt @@ -0,0 +1,12 @@ +package com.mmk.kmpnotifier.notification + +/** + * Provides all data required for the Android notification + */ +public interface AndroidNotificationManager { + /** + * Provide Android notification data + * @param notificationData - data required for displaying notification + */ + public fun setData(notificationData: AndroidNotificationData) +} \ No newline at end of file diff --git a/kmpnotifier/src/androidMain/kotlin/com/mmk/kmpnotifier/notification/AndroidNotifier.kt b/kmpnotifier/src/androidMain/kotlin/com/mmk/kmpnotifier/notification/AndroidNotifier.kt index 9520932..96184b1 100644 --- a/kmpnotifier/src/androidMain/kotlin/com/mmk/kmpnotifier/notification/AndroidNotifier.kt +++ b/kmpnotifier/src/androidMain/kotlin/com/mmk/kmpnotifier/notification/AndroidNotifier.kt @@ -5,10 +5,9 @@ import android.content.Context import android.content.Intent import android.net.Uri import android.util.Log -import androidx.core.app.NotificationCompat -import androidx.core.content.ContextCompat import com.mmk.kmpnotifier.Constants.ACTION_NOTIFICATION_CLICK import com.mmk.kmpnotifier.extensions.notificationManager +import com.mmk.kmpnotifier.notification.configuration.DisplayNotificationManager import com.mmk.kmpnotifier.notification.configuration.NotificationPlatformConfiguration import com.mmk.kmpnotifier.permission.PermissionUtil import kotlin.random.Random @@ -18,6 +17,7 @@ internal class AndroidNotifier( private val context: Context, private val androidNotificationConfiguration: NotificationPlatformConfiguration.Android, private val notificationChannelFactory: NotificationChannelFactory, + private val displayNotificationManager: DisplayNotificationManager?, private val permissionUtil: PermissionUtil, ) : Notifier { @@ -38,22 +38,23 @@ internal class AndroidNotifier( val notificationManager = context.notificationManager ?: return val pendingIntent = getPendingIntent(payloadData) notificationChannelFactory.createChannels() - val notification = NotificationCompat.Builder( - context, - androidNotificationConfiguration.notificationChannelData.id - ).apply { - setChannelId(androidNotificationConfiguration.notificationChannelData.id) - setContentTitle(title) - setContentText(body) - setSmallIcon(androidNotificationConfiguration.notificationIconResId) - setAutoCancel(true) - setContentIntent(pendingIntent) - androidNotificationConfiguration.notificationIconColorResId?.let { - color = ContextCompat.getColor(context, it) - } - }.build() - - notificationManager.notify(id, notification) + val androidNotificationData = AndroidNotificationData.Companion.Builder( + context = context, + notificationManager = notificationManager + ) + .setNotificationId(id) + .setChannelData(androidNotificationConfiguration.notificationChannelData) + .setNotificationIconRes(androidNotificationConfiguration.notificationIconResId) + .setNotificationIconColorResId(androidNotificationConfiguration.notificationIconColorResId) + .setPayloadData(payloadData) + .setPendingIntent(pendingIntent) + .build() + val notificationFct = (displayNotificationManager ?: DefaultDisplayNotificationManager()) + val androidNotificationManager = notificationFct as AndroidNotificationManager? + ?: throw IllegalStateException("Factory doesn't implement AndroidNotificationFactory") + androidNotificationManager.setData(androidNotificationData) + notificationFct.displayNotification(title, body) + } override fun remove(id: Int) { diff --git a/kmpnotifier/src/androidMain/kotlin/com/mmk/kmpnotifier/notification/DefaultDisplayNotificationManager.kt b/kmpnotifier/src/androidMain/kotlin/com/mmk/kmpnotifier/notification/DefaultDisplayNotificationManager.kt new file mode 100644 index 0000000..572f058 --- /dev/null +++ b/kmpnotifier/src/androidMain/kotlin/com/mmk/kmpnotifier/notification/DefaultDisplayNotificationManager.kt @@ -0,0 +1,39 @@ +package com.mmk.kmpnotifier.notification + +import androidx.core.app.NotificationCompat +import androidx.core.content.ContextCompat +import com.mmk.kmpnotifier.notification.configuration.DisplayNotificationManager + +/** + * Default implementation of the Android notification manager + */ +internal class DefaultDisplayNotificationManager : DisplayNotificationManager, AndroidNotificationManager { + private var notificationData: AndroidNotificationData? = null + + override fun setData(notificationData: AndroidNotificationData) { + this.notificationData = notificationData + } + + override fun displayNotification(title: String, body: String) { + val data = notificationData ?: throw IllegalStateException("Android Notification data must be provided") + val notificationChannelData = + data.notificationChannelData ?: throw IllegalStateException("Notification Channel Data must be provided") + + val notification = NotificationCompat.Builder( + data.context, + notificationChannelData.id + ).apply { + setChannelId(notificationChannelData.id) + setContentTitle(title) + setContentText(body) + setSmallIcon(data.notificationIconResId) + setAutoCancel(true) + setContentIntent(data.pendingIntent) + data.notificationIconColorResId?.let { + color = ContextCompat.getColor(data.context, it) + } + }.build() + + data.notificationManager.notify(data.notificationId, notification) + } +} diff --git a/kmpnotifier/src/commonMain/kotlin/com/mmk/kmpnotifier/notification/configuration/DisplayNotificationManager.kt b/kmpnotifier/src/commonMain/kotlin/com/mmk/kmpnotifier/notification/configuration/DisplayNotificationManager.kt new file mode 100644 index 0000000..2f2d57d --- /dev/null +++ b/kmpnotifier/src/commonMain/kotlin/com/mmk/kmpnotifier/notification/configuration/DisplayNotificationManager.kt @@ -0,0 +1,15 @@ +package com.mmk.kmpnotifier.notification.configuration + +/** + * You can provide your own implementation of the notification UI + */ +public interface DisplayNotificationManager { + + /** + * Implement mechanism of notification displaying + * + * @param title of the notification + * @param body of the notification + */ + public fun displayNotification(title: String, body: String) +} 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 0fc59a5..7a4f8ae 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 @@ -17,12 +17,15 @@ public sealed interface NotificationPlatformConfiguration { * received it will be shown to user. When set to false, it will not be shown to user, * but you can still get notification content using * @see com.mmk.kmpnotifier.notification.NotifierManager.Listener.onPushNotification + * @param displayNotificationManager Assembles and displays notification. If not provided the default manager + * [com.mmk.kmpnotifier.notification.DefaultDisplayNotificationManager] is used. Also */ public class Android( public val notificationIconResId: Int, public val notificationIconColorResId: Int? = null, public val notificationChannelData: NotificationChannelData = NotificationChannelData(), public val showPushNotification: Boolean = true, + public val displayNotificationManager: DisplayNotificationManager? = null ) : NotificationPlatformConfiguration { /**