From 77a6932c41800d50fbc0b42f77d592b8fa1ad777 Mon Sep 17 00:00:00 2001 From: Tomas Timinskas Date: Wed, 13 Dec 2023 12:58:08 -0300 Subject: [PATCH] Handling push notification to fix issues: - Tap on push not taking user to specific chat - Tap on push not opening app when force closed --- .../chat/sphinx/activitymain/MainActivity.kt | 46 +++++++++---- .../chat/sphinx/activitymain/MainViewModel.kt | 17 ++++- .../drivers/AuthenticationNavigationDriver.kt | 2 +- .../drivers/DetailNavigationDriver.kt | 2 +- .../drivers/PrimaryNavigationDriver.kt | 2 +- .../sphinx/activitymain/ui/MainViewState.kt | 2 +- .../chat/PushNotificationLink.kt | 43 ++++++++++++ .../sphinx/chat_common/ui/ChatFragment.kt | 17 +++++ .../sphinx/dashboard/ui/DashboardFragment.kt | 26 +++++-- .../sphinx/dashboard/ui/DashboardViewModel.kt | 22 ++++++ .../build.gradle | 1 + .../SphinxFirebaseMessagingService.kt | 69 +++++++++++++++++-- 12 files changed, 222 insertions(+), 27 deletions(-) create mode 100644 sphinx/application/common/wrappers/wrapper-common/src/main/java/chat/sphinx/wrapper_common/chat/PushNotificationLink.kt diff --git a/sphinx/activity/main/activitymain/src/main/java/chat/sphinx/activitymain/MainActivity.kt b/sphinx/activity/main/activitymain/src/main/java/chat/sphinx/activitymain/MainActivity.kt index ba61dc4998..ba64985afd 100644 --- a/sphinx/activity/main/activitymain/src/main/java/chat/sphinx/activitymain/MainActivity.kt +++ b/sphinx/activity/main/activitymain/src/main/java/chat/sphinx/activitymain/MainActivity.kt @@ -7,7 +7,6 @@ import android.os.Build import android.os.Bundle import androidx.activity.result.contract.ActivityResultContracts import androidx.activity.viewModels -import androidx.annotation.RequiresApi import androidx.constraintlayout.motion.widget.MotionLayout import androidx.core.content.ContextCompat import androidx.core.view.ViewCompat @@ -27,12 +26,12 @@ import dagger.hilt.android.AndroidEntryPoint import dev.chrisbanes.insetter.applyInsetter import io.matthewnelson.android_feature_navigation.requests.PopBackStack import io.matthewnelson.android_feature_viewmodel.updateViewState -import kotlinx.coroutines.flow.collect import kotlinx.coroutines.launch import chat.sphinx.resources.R as R_common + @AndroidEntryPoint -internal class MainActivity: MotionLayoutNavigationActivity< +class MainActivity: MotionLayoutNavigationActivity< MainViewState, MainViewModel, PrimaryNavigationDriver, @@ -72,6 +71,8 @@ internal class MainActivity: MotionLayoutNavigationActivity< private var statusBarInsets: InsetPadding? = null private var navigationBarInsets: InsetPadding? = null private var keyboardInsets: InsetPadding? = null + + var isActive = false } override val statusBarInsetHeight: InsetPadding by lazy(LazyThreadSafetyMode.NONE) { @@ -122,6 +123,10 @@ internal class MainActivity: MotionLayoutNavigationActivity< binding.viewMainInputLock.setOnClickListener { viewModel } askNotificationPermission() addWindowInsetChangeListener() + + intent.extras?.getString("chat_id")?.toLongOrNull()?.let { chatId -> + handlePushNotification(chatId) + } } override fun onStart() { @@ -165,7 +170,11 @@ internal class MainActivity: MotionLayoutNavigationActivity< } } + override fun onResume() { + super.onResume() + isActive = true + } override fun onStop() { super.onStop() @@ -175,6 +184,8 @@ internal class MainActivity: MotionLayoutNavigationActivity< if (sessionDepth == 0) { viewModel.saveContentFeedStatuses() } + + isActive = false } override fun onNewIntent(intent: Intent) { @@ -183,6 +194,8 @@ internal class MainActivity: MotionLayoutNavigationActivity< intent.dataString?.let { deepLink -> handleDeepLink(deepLink) } + + setIntent(intent) } private fun askNotificationPermission() { @@ -203,12 +216,19 @@ internal class MainActivity: MotionLayoutNavigationActivity< } } } + private fun handleDeepLink(deepLink: String) { onStopSupervisor.scope.launch(viewModel.mainImmediate) { viewModel.handleDeepLink(deepLink) } } + private fun handlePushNotification(chatId: Long) { + onStopSupervisor.scope.launch(viewModel.mainImmediate) { + viewModel.handlePushNotification(chatId) + } + } + override suspend fun onViewStateFlowCollect(viewState: MainViewState) { when (viewState) { is MainViewState.DetailScreenActive -> { @@ -237,16 +257,16 @@ internal class MainActivity: MotionLayoutNavigationActivity< // To Handle swipe behaviour override fun onTransitionCompleted(motionLayout: MotionLayout?, currentId: Int) { transitionInProgress = false - if ( - currentId == MainViewState.DetailScreenInactive.endSetId && - detailNavController.previousBackStackEntry != null - ) { - lifecycleScope.launch { - viewModel.detailDriver.submitNavigationRequest( - PopBackStack(R.id.navigation_detail_blank_fragment) - ) - } + if ( + currentId == MainViewState.DetailScreenInactive.endSetId && + detailNavController.previousBackStackEntry != null + ) { + lifecycleScope.launch { + viewModel.detailDriver.submitNavigationRequest( + PopBackStack(R.id.navigation_detail_blank_fragment) + ) } + } } override fun onBackPressed() { @@ -295,6 +315,8 @@ internal class MainActivity: MotionLayoutNavigationActivity< viewModel.syncActions() viewModel.getDeleteExcessFileIfApplicable() + + isActive = false } override var isKeyboardVisible: Boolean = false diff --git a/sphinx/activity/main/activitymain/src/main/java/chat/sphinx/activitymain/MainViewModel.kt b/sphinx/activity/main/activitymain/src/main/java/chat/sphinx/activitymain/MainViewModel.kt index a1c6fc571d..51a419e292 100644 --- a/sphinx/activity/main/activitymain/src/main/java/chat/sphinx/activitymain/MainViewModel.kt +++ b/sphinx/activity/main/activitymain/src/main/java/chat/sphinx/activitymain/MainViewModel.kt @@ -22,6 +22,7 @@ import chat.sphinx.wrapper_common.StorageData import chat.sphinx.wrapper_common.StorageLimit.DEFAULT_STORAGE_LIMIT import chat.sphinx.wrapper_common.StorageLimit.STORAGE_LIMIT_KEY import chat.sphinx.wrapper_common.calculateUserStorageLimit +import chat.sphinx.wrapper_common.chat.PushNotificationLink import chat.sphinx.wrapper_common.toFileSize import dagger.hilt.android.lifecycle.HiltViewModel import io.matthewnelson.android_feature_activity.NavigationViewModel @@ -36,7 +37,7 @@ import kotlinx.coroutines.flow.collect import javax.inject.Inject @HiltViewModel -internal class MainViewModel @Inject constructor( +class MainViewModel @Inject constructor( private val authenticationCoordinator: AuthenticationCoordinator, private val authenticationStateManager: AuthenticationStateManager, val authenticationDriver: AuthenticationNavigationDriver, @@ -88,6 +89,20 @@ internal class MainViewModel @Inject constructor( } } + suspend fun handlePushNotification(chatId: Long) { + val pushNotificationLink = PushNotificationLink("sphinx.chat://?action=push&chatId=$chatId") + + if (authenticationStateManager.authenticationStateFlow.value == AuthenticationState.NotRequired) { + navigationDriver.submitNavigationRequest( + ToDashboardScreen( + popUpToId = R.id.main_primary_nav_graph, + updateBackgroundLoginTime = false, + deepLink = pushNotificationLink.value + ) + ) + } + } + fun syncActions() { actionsRepository.syncActions() } diff --git a/sphinx/activity/main/activitymain/src/main/java/chat/sphinx/activitymain/navigation/drivers/AuthenticationNavigationDriver.kt b/sphinx/activity/main/activitymain/src/main/java/chat/sphinx/activitymain/navigation/drivers/AuthenticationNavigationDriver.kt index 72904886a9..d6706285e1 100644 --- a/sphinx/activity/main/activitymain/src/main/java/chat/sphinx/activitymain/navigation/drivers/AuthenticationNavigationDriver.kt +++ b/sphinx/activity/main/activitymain/src/main/java/chat/sphinx/activitymain/navigation/drivers/AuthenticationNavigationDriver.kt @@ -4,7 +4,7 @@ import androidx.navigation.NavController import io.matthewnelson.concept_navigation.NavigationRequest import io.matthewnelson.feature_navigation.NavigationDriver -internal class AuthenticationNavigationDriver: NavigationDriver(replayCacheSize = 1) { +class AuthenticationNavigationDriver: NavigationDriver(replayCacheSize = 1) { override suspend fun whenTrueExecuteRequest(request: NavigationRequest): Boolean { return true } diff --git a/sphinx/activity/main/activitymain/src/main/java/chat/sphinx/activitymain/navigation/drivers/DetailNavigationDriver.kt b/sphinx/activity/main/activitymain/src/main/java/chat/sphinx/activitymain/navigation/drivers/DetailNavigationDriver.kt index 37a4b920ab..71982f5797 100644 --- a/sphinx/activity/main/activitymain/src/main/java/chat/sphinx/activitymain/navigation/drivers/DetailNavigationDriver.kt +++ b/sphinx/activity/main/activitymain/src/main/java/chat/sphinx/activitymain/navigation/drivers/DetailNavigationDriver.kt @@ -6,7 +6,7 @@ import io.matthewnelson.android_feature_navigation.requests.PopBackStack import io.matthewnelson.concept_navigation.NavigationRequest import io.matthewnelson.feature_navigation.NavigationDriver -internal class DetailNavigationDriver: NavigationDriver(replayCacheSize = 2) { +class DetailNavigationDriver: NavigationDriver(replayCacheSize = 2) { override suspend fun whenTrueExecuteRequest(request: NavigationRequest): Boolean { return true } diff --git a/sphinx/activity/main/activitymain/src/main/java/chat/sphinx/activitymain/navigation/drivers/PrimaryNavigationDriver.kt b/sphinx/activity/main/activitymain/src/main/java/chat/sphinx/activitymain/navigation/drivers/PrimaryNavigationDriver.kt index 79789a16e2..879cbc9966 100644 --- a/sphinx/activity/main/activitymain/src/main/java/chat/sphinx/activitymain/navigation/drivers/PrimaryNavigationDriver.kt +++ b/sphinx/activity/main/activitymain/src/main/java/chat/sphinx/activitymain/navigation/drivers/PrimaryNavigationDriver.kt @@ -4,7 +4,7 @@ import androidx.navigation.NavController import io.matthewnelson.concept_navigation.NavigationRequest import io.matthewnelson.feature_navigation.NavigationDriver -internal class PrimaryNavigationDriver: NavigationDriver(replayCacheSize = 5) { +class PrimaryNavigationDriver: NavigationDriver(replayCacheSize = 5) { override suspend fun whenTrueExecuteRequest(request: NavigationRequest): Boolean { return true } diff --git a/sphinx/activity/main/activitymain/src/main/java/chat/sphinx/activitymain/ui/MainViewState.kt b/sphinx/activity/main/activitymain/src/main/java/chat/sphinx/activitymain/ui/MainViewState.kt index 7aa2859e50..229bd05378 100644 --- a/sphinx/activity/main/activitymain/src/main/java/chat/sphinx/activitymain/ui/MainViewState.kt +++ b/sphinx/activity/main/activitymain/src/main/java/chat/sphinx/activitymain/ui/MainViewState.kt @@ -5,7 +5,7 @@ import chat.sphinx.activitymain.R import io.matthewnelson.android_concept_views.MotionLayoutViewState @Suppress("ClassName") -internal sealed class MainViewState: MotionLayoutViewState() { +sealed class MainViewState: MotionLayoutViewState() { object DetailScreenActive: MainViewState() { override val startSetId: Int diff --git a/sphinx/application/common/wrappers/wrapper-common/src/main/java/chat/sphinx/wrapper_common/chat/PushNotificationLink.kt b/sphinx/application/common/wrappers/wrapper-common/src/main/java/chat/sphinx/wrapper_common/chat/PushNotificationLink.kt new file mode 100644 index 0000000000..5f1f2e0486 --- /dev/null +++ b/sphinx/application/common/wrappers/wrapper-common/src/main/java/chat/sphinx/wrapper_common/chat/PushNotificationLink.kt @@ -0,0 +1,43 @@ +package chat.sphinx.wrapper_common.chat + +@Suppress("NOTHING_TO_INLINE") +inline fun String.toPushNotificationLink(): PushNotificationLink? = + try { + PushNotificationLink(this) + } catch (e: IllegalArgumentException) { + null + } + +inline val String.isValidPushNotificationLink: Boolean + get() = isNotEmpty() && matches("^${PushNotificationLink.REGEX}\$".toRegex()) + +@JvmInline +value class PushNotificationLink(val value: String) { + + companion object { + const val REGEX = "sphinx\\.chat:\\/\\/\\?action=push&chatId=.*" + const val CHAT_ID = "chatId" + } + + init { + require(value.isValidPushNotificationLink) { + "Invalid Push Notification Link" + } + } + + inline val chatId : Long? + get() = (getComponent(CHAT_ID) ?: "").trim().toLongOrNull() + + fun getComponent(k: String): String? { + val components = value.replace("sphinx.chat://", "").split("&") + for (component in components) { + val subComponents = component.split("=") + val key:String? = if (subComponents.isNotEmpty()) subComponents.elementAtOrNull(0) else null + val value:String? = if (subComponents.size > 1) subComponents.elementAtOrNull(1) else null + + if (key == k) return value + } + return null + } + +} \ No newline at end of file diff --git a/sphinx/screens/chats/chat-common/chat-common/src/main/java/chat/sphinx/chat_common/ui/ChatFragment.kt b/sphinx/screens/chats/chat-common/chat-common/src/main/java/chat/sphinx/chat_common/ui/ChatFragment.kt index 35b717ad51..0989f6739d 100644 --- a/sphinx/screens/chats/chat-common/chat-common/src/main/java/chat/sphinx/chat_common/ui/ChatFragment.kt +++ b/sphinx/screens/chats/chat-common/chat-common/src/main/java/chat/sphinx/chat_common/ui/ChatFragment.kt @@ -78,6 +78,7 @@ import chat.sphinx.menu_bottom.ui.MenuBottomViewState import chat.sphinx.resources.* import chat.sphinx.wrapper_common.FileSize import chat.sphinx.wrapper_common.asFormattedString +import chat.sphinx.wrapper_common.chat.PushNotificationLink import chat.sphinx.wrapper_common.lightning.asFormattedString import chat.sphinx.wrapper_common.lightning.toSat import chat.sphinx.wrapper_common.message.MessageId @@ -212,6 +213,22 @@ abstract class ChatFragment< viewModel.screenInit() } + override fun onResume() { + super.onResume() + + handlePushNotification() + } + + private fun handlePushNotification() { + val chatId = activity?.intent?.extras?.getString("chat_id")?.toLongOrNull() ?: activity?.intent?.extras?.getLong("chat_id") + + chatId?.let { _ -> + lifecycleScope.launch(viewModel.mainImmediate) { + viewModel.chatNavigator.popBackStack() + } + } + } + override fun onKeyboardToggle() { addViewKeyboardBottomPadding( (requireActivity() as InsetterActivity) diff --git a/sphinx/screens/dashboard/dashboard/src/main/java/chat/sphinx/dashboard/ui/DashboardFragment.kt b/sphinx/screens/dashboard/dashboard/src/main/java/chat/sphinx/dashboard/ui/DashboardFragment.kt index 57c6d77167..b9e3ad342d 100644 --- a/sphinx/screens/dashboard/dashboard/src/main/java/chat/sphinx/dashboard/ui/DashboardFragment.kt +++ b/sphinx/screens/dashboard/dashboard/src/main/java/chat/sphinx/dashboard/ui/DashboardFragment.kt @@ -1,7 +1,6 @@ package chat.sphinx.dashboard.ui import android.content.Context -import android.content.res.ColorStateList import android.os.Bundle import android.util.Log import android.view.LayoutInflater @@ -10,7 +9,6 @@ import android.widget.ImageView import android.widget.TextView import androidx.constraintlayout.motion.widget.MotionLayout import androidx.core.content.ContextCompat -import androidx.core.view.ViewCompat.setBackgroundTintList import androidx.fragment.app.viewModels import androidx.lifecycle.lifecycleScope import androidx.navigation.NavController @@ -28,18 +26,17 @@ import chat.sphinx.concept_signer_manager.SignerManager import chat.sphinx.dashboard.R import chat.sphinx.dashboard.databinding.FragmentDashboardBinding import chat.sphinx.dashboard.ui.viewstates.* -import chat.sphinx.dashboard.ui.viewstates.DashboardMotionViewState import chat.sphinx.insetter_activity.InsetterActivity import chat.sphinx.insetter_activity.addNavigationBarPadding import chat.sphinx.insetter_activity.addStatusBarPadding import chat.sphinx.kotlin_response.LoadResponse import chat.sphinx.kotlin_response.Response -import chat.sphinx.menu_bottom.databinding.LayoutMenuBottomBinding import chat.sphinx.menu_bottom_scanner.BottomScannerMenu import chat.sphinx.resources.databinding.LayoutPodcastPlayerFooterBinding -import chat.sphinx.wrapper_view.Px import chat.sphinx.swipe_reveal_layout.SwipeRevealLayout +import chat.sphinx.wrapper_common.chat.PushNotificationLink import chat.sphinx.wrapper_common.lightning.* +import chat.sphinx.wrapper_view.Px import com.google.android.material.tabs.TabLayoutMediator import dagger.hilt.android.AndroidEntryPoint import io.matthewnelson.android_feature_screens.ui.motionlayout.MotionLayoutFragment @@ -50,7 +47,6 @@ import io.matthewnelson.concept_views.viewstate.collect import io.matthewnelson.concept_views.viewstate.value import kotlinx.coroutines.Job import kotlinx.coroutines.flow.asStateFlow -import kotlinx.coroutines.flow.collect import kotlinx.coroutines.launch import javax.inject.Inject @@ -117,12 +113,30 @@ internal class DashboardFragment : MotionLayoutFragment< viewModel.networkRefresh(false) + handleDeepLinks() + handlePushNotification() + + activity?.intent = null + } + + private fun handleDeepLinks() { activity?.intent?.dataString?.let { deepLink -> viewModel.handleDeepLink(deepLink) activity?.intent?.data = null } } + private fun handlePushNotification() { + val chatId = activity?.intent?.extras?.getString("chat_id")?.toLongOrNull() ?: activity?.intent?.extras?.getLong("chat_id") + chatId?.let { nnChatId -> + viewModel.handleDeepLink( + PushNotificationLink("sphinx.chat://?action=push&chatId=$nnChatId").value + ) + } + + activity?.intent = null + } + override fun onRefresh() { binding.swipeRefreshLayoutDataReload.isRefreshing = false viewModel.networkRefresh(true) diff --git a/sphinx/screens/dashboard/dashboard/src/main/java/chat/sphinx/dashboard/ui/DashboardViewModel.kt b/sphinx/screens/dashboard/dashboard/src/main/java/chat/sphinx/dashboard/ui/DashboardViewModel.kt index 1d1911022d..71a9e23bb4 100644 --- a/sphinx/screens/dashboard/dashboard/src/main/java/chat/sphinx/dashboard/ui/DashboardViewModel.kt +++ b/sphinx/screens/dashboard/dashboard/src/main/java/chat/sphinx/dashboard/ui/DashboardViewModel.kt @@ -48,10 +48,14 @@ import chat.sphinx.scanner_view_model_coordinator.request.ScannerFilter import chat.sphinx.scanner_view_model_coordinator.request.ScannerRequest import chat.sphinx.scanner_view_model_coordinator.response.ScannerResponse import chat.sphinx.wrapper_chat.Chat +import chat.sphinx.wrapper_chat.isConversation import chat.sphinx.wrapper_common.* import chat.sphinx.wrapper_common.chat.ChatUUID +import chat.sphinx.wrapper_common.chat.PushNotificationLink +import chat.sphinx.wrapper_common.chat.toPushNotificationLink import chat.sphinx.wrapper_common.dashboard.ChatId import chat.sphinx.wrapper_common.dashboard.RestoreProgressViewState +import chat.sphinx.wrapper_common.dashboard.toChatId import chat.sphinx.wrapper_common.feed.* import chat.sphinx.wrapper_common.lightning.* import chat.sphinx.wrapper_common.tribe.TribeJoinLink @@ -189,6 +193,24 @@ internal class DashboardViewModel @Inject constructor( handleRedeemSatsLink(redeemSatsLink) } ?: deepLink?.toFeedItemLink()?.let { feedItemLink -> handleFeedItemLink(feedItemLink) + } ?: deepLink?.toPushNotificationLink()?.let { pushNotificationLink -> + handlePushNotification(pushNotificationLink) + } + } + } + + private fun handlePushNotification(pushNotificationLink: PushNotificationLink) { + viewModelScope.launch(mainImmediate) { + pushNotificationLink.chatId?.toChatId()?.let { nnChatId -> + chatRepository.getChatById(nnChatId).firstOrNull()?.let { chat -> + if (chat.isConversation()) { + chat.contactIds.elementAtOrNull(1)?.let { contactId -> + dashboardNavigator.toChatContact(nnChatId, contactId) + } + } else { + dashboardNavigator.toChatTribe(nnChatId) + } + } } } } diff --git a/sphinx/service/features/notifications/feature-service-notification-firebase/build.gradle b/sphinx/service/features/notifications/feature-service-notification-firebase/build.gradle index afb630ecab..d2c94399d9 100644 --- a/sphinx/service/features/notifications/feature-service-notification-firebase/build.gradle +++ b/sphinx/service/features/notifications/feature-service-notification-firebase/build.gradle @@ -42,6 +42,7 @@ dependencies { api project(path: ':kotlin:concepts:concept-coroutines') // Sphinx + api project(path: ':sphinx:activity:main:activitymain') api project(path: ':sphinx:application:common:logger') implementation project(path: ':sphinx:application:common:resources') api project(path: ':sphinx:application:data:concepts:repositories:concept-repository-contact') diff --git a/sphinx/service/features/notifications/feature-service-notification-firebase/src/main/java/chat/sphinx/feature_service_notification_firebase/SphinxFirebaseMessagingService.kt b/sphinx/service/features/notifications/feature-service-notification-firebase/src/main/java/chat/sphinx/feature_service_notification_firebase/SphinxFirebaseMessagingService.kt index 1be7f63733..596a7fee7e 100644 --- a/sphinx/service/features/notifications/feature-service-notification-firebase/src/main/java/chat/sphinx/feature_service_notification_firebase/SphinxFirebaseMessagingService.kt +++ b/sphinx/service/features/notifications/feature-service-notification-firebase/src/main/java/chat/sphinx/feature_service_notification_firebase/SphinxFirebaseMessagingService.kt @@ -1,5 +1,12 @@ package chat.sphinx.feature_service_notification_firebase +import android.app.NotificationChannel +import android.app.NotificationManager +import android.app.PendingIntent +import android.content.Intent +import android.os.Build +import androidx.core.app.NotificationCompat +import chat.sphinx.activitymain.MainActivity import chat.sphinx.logger.SphinxLogger import chat.sphinx.logger.d import com.google.firebase.messaging.FirebaseMessagingService @@ -8,6 +15,8 @@ import dagger.hilt.android.AndroidEntryPoint import io.matthewnelson.concept_coroutines.CoroutineDispatchers import kotlinx.coroutines.* import javax.inject.Inject +import chat.sphinx.resources.R as R_common + /* * https://firebase.google.com/docs/cloud-messaging/android/first-message @@ -37,11 +46,63 @@ internal class SphinxFirebaseMessagingService: FirebaseMessagingService() { override fun onMessageReceived(p0: RemoteMessage) { super.onMessageReceived(p0) - LOG.d(TAG, "From: ${p0.from}") - LOG.d(TAG, "Notification Body: ${p0.notification?.body}") - for ((index, entry) in p0.data.entries.withIndex()) { - LOG.d(TAG, "Data: Index(value=$index), Key(value=${entry.key}), Value(value=${entry.value})") + + if (p0.notification != null) { + ///Old notification, no need to create notification from code + return + } + + if (MainActivity.isActive) { + ///If app is in foreground, then do not show notification + return } + + // Extract data from the message + // Extract data from the message + val title: String = p0.data["message"] ?: "" + val messageBody: String = p0.data["body"] ?: "" + val chatId: Long? = (p0.data["chat_id"] ?: "").toLongOrNull() + + // Create an intent to open MainActivity when the notification is clicked + val intent = Intent( + this, + MainActivity::class.java + ) + + intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP) + intent.putExtra("chat_id", chatId) + + val pendingIntent = PendingIntent.getActivity( + this, + 0, + intent, + PendingIntent.FLAG_ONE_SHOT or PendingIntent.FLAG_IMMUTABLE + ) + + // Build the notification + val notificationBuilder: NotificationCompat.Builder = + NotificationCompat.Builder(this, "channel_id") + .setSmallIcon(R_common.drawable.sphinx_white_logo) + .setContentTitle(title) + .setContentText(messageBody) + .setAutoCancel(true) + .setContentIntent(pendingIntent) + + // Get the NotificationManager + val notificationManager = getSystemService(NOTIFICATION_SERVICE) as NotificationManager + + // Check if the device is running Android Oreo or higher + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + val channel = NotificationChannel( + "channel_id", + "Channel Name", + NotificationManager.IMPORTANCE_DEFAULT + ) + notificationManager.createNotificationChannel(channel) + } + + // Show the notification + notificationManager.notify(0, notificationBuilder.build()) } override fun onNewToken(p0: String) {