diff --git a/app/build.gradle b/app/build.gradle
index 43024621c3..c7a75acf90 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -245,6 +245,9 @@ dependencies {
implementation libs.wireRuntime
+ implementation(platform("com.google.firebase:firebase-bom:33.10.0"))
+ implementation "com.google.firebase:firebase-messaging"
+
//Unit/Integration tests dependencies
testImplementation libs.androidXTestCore
testImplementation libs.junit
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 3a055cc121..583aace7f2 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -160,6 +160,15 @@
+
+
+
+
+
+
+
diff --git a/app/src/main/java/com/anytypeio/anytype/app/AndroidApplication.kt b/app/src/main/java/com/anytypeio/anytype/app/AndroidApplication.kt
index 2500bbc746..b377bd5bdc 100644
--- a/app/src/main/java/com/anytypeio/anytype/app/AndroidApplication.kt
+++ b/app/src/main/java/com/anytypeio/anytype/app/AndroidApplication.kt
@@ -19,6 +19,8 @@ import com.anytypeio.anytype.di.main.DaggerMainComponent
import com.anytypeio.anytype.di.main.MainComponent
import com.anytypeio.anytype.middleware.discovery.MDNSProvider
import com.anytypeio.anytype.middleware.discovery.adresshandler.LocalNetworkAddressProvider
+import com.google.android.gms.tasks.OnCompleteListener
+import com.google.firebase.messaging.FirebaseMessaging
import javax.inject.Inject
import timber.log.Timber
@@ -63,6 +65,18 @@ class AndroidApplication : Application(), HasComponentDependencies {
setupCrashReporter()
setupLocalNetworkAddressHandler()
setupNotificationChannel()
+
+ FirebaseMessaging.getInstance().token.addOnCompleteListener(OnCompleteListener { task ->
+ if (!task.isSuccessful) {
+ Timber.e(task.exception, "Fetching FCM registration token failed")
+ return@OnCompleteListener
+ }
+
+ // Get new FCM registration token
+ val token = task.result
+
+ Timber.d("FCM registration token: $token")
+ })
}
private fun enableStrictMode() {
diff --git a/app/src/main/java/com/anytypeio/anytype/other/AnytypeFirebaseMessagingService.kt b/app/src/main/java/com/anytypeio/anytype/other/AnytypeFirebaseMessagingService.kt
new file mode 100644
index 0000000000..413acb91ab
--- /dev/null
+++ b/app/src/main/java/com/anytypeio/anytype/other/AnytypeFirebaseMessagingService.kt
@@ -0,0 +1,62 @@
+package com.anytypeio.anytype.other
+
+import android.app.NotificationChannel
+import android.app.NotificationManager
+import android.content.Context
+import android.os.Build
+import com.anytypeio.anytype.R
+import androidx.core.app.NotificationCompat
+import com.google.firebase.messaging.FirebaseMessagingService
+import com.google.firebase.messaging.RemoteMessage
+import service.Service
+import timber.log.Timber
+
+class AnytypeFirebaseMessagingService : FirebaseMessagingService() {
+ override fun onNewToken(token: String) {
+ super.onNewToken(token)
+ // This callback is fired whenever a new token is generated for the device.
+ // Send this token to your app server for registration.
+ }
+
+ override fun onMessageReceived(remoteMessage: RemoteMessage) {
+ super.onMessageReceived(remoteMessage)
+ Timber.d("onMessageReceived")
+
+ // Check if message contains a data payload.
+ remoteMessage.data.let { data ->
+ Timber.d("onMessageReceivedData: $data")
+
+
+ }
+
+ // Check if message contains a notification payload.
+ remoteMessage.notification?.let { notification ->
+ val title = notification.title
+ val body = notification.body
+ // Display the notification
+ sendNotification(title, body)
+ }
+ }
+
+ private fun sendNotification(title: String?, body: String?) {
+ // Build and show a notification via NotificationManager
+ val channelId = "fcm_default_channel"
+ val notificationBuilder = NotificationCompat.Builder(this, channelId)
+ .setSmallIcon(R.drawable.ic_anytype_qr_code_logo)
+ .setContentTitle(title)
+ .setContentText(body)
+ .setPriority(NotificationCompat.PRIORITY_HIGH)
+ .setAutoCancel(true)
+
+ val notificationManager =
+ getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
+ // Create channel for Android Oreo and above
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ val channel =
+ NotificationChannel(channelId, "FCM Channel", NotificationManager.IMPORTANCE_HIGH)
+ notificationManager.createNotificationChannel(channel)
+ }
+
+ notificationManager.notify(System.currentTimeMillis().toInt(), notificationBuilder.build())
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/anytypeio/anytype/ui/main/MainActivity.kt b/app/src/main/java/com/anytypeio/anytype/ui/main/MainActivity.kt
index 326c14270b..aae332b889 100644
--- a/app/src/main/java/com/anytypeio/anytype/ui/main/MainActivity.kt
+++ b/app/src/main/java/com/anytypeio/anytype/ui/main/MainActivity.kt
@@ -8,8 +8,10 @@ import android.graphics.Color
import android.os.Build
import android.os.Bundle
import androidx.activity.enableEdgeToEdge
+import androidx.activity.result.contract.ActivityResultContracts
import androidx.activity.viewModels
import androidx.appcompat.app.AppCompatActivity
+import androidx.core.content.ContextCompat
import androidx.core.os.bundleOf
import androidx.fragment.app.FragmentContainerView
import androidx.lifecycle.Lifecycle
@@ -105,6 +107,7 @@ class MainActivity : AppCompatActivity(R.layout.activity_main), AppNavigation.Pr
setupWindowInsets()
inject()
setupTheme()
+ askNotificationPermission()
startAppUpdater()
@@ -675,6 +678,36 @@ class MainActivity : AppCompatActivity(R.layout.activity_main), AppNavigation.Pr
componentManager().mainEntryComponent.release()
}
+ // Declare the launcher at the top of your Activity/Fragment:
+ private val requestPermissionLauncher = registerForActivityResult(
+ ActivityResultContracts.RequestPermission(),
+ ) { isGranted: Boolean ->
+ if (isGranted) {
+ // FCM SDK (and your app) can post notifications.
+ } else {
+ // TODO: Inform user that that your app will not show notifications.
+ }
+ }
+
+ private fun askNotificationPermission() {
+ // This is only necessary for API level >= 33 (TIRAMISU)
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
+ if (ContextCompat.checkSelfPermission(this, Manifest.permission.POST_NOTIFICATIONS) ==
+ PackageManager.PERMISSION_GRANTED
+ ) {
+ // FCM SDK (and your app) can post notifications.
+ } else if (shouldShowRequestPermissionRationale(Manifest.permission.POST_NOTIFICATIONS)) {
+ // TODO: display an educational UI explaining to the user the features that will be enabled
+ // by them granting the POST_NOTIFICATION permission. This UI should provide the user
+ // "OK" and "No thanks" buttons. If the user selects "OK," directly request the permission.
+ // If the user selects "No thanks," allow the user to continue without notifications.
+ } else {
+ // Directly ask for the permission
+ requestPermissionLauncher.launch(Manifest.permission.POST_NOTIFICATIONS)
+ }
+ }
+ }
+
companion object {
const val AUTO_UPDATE_URL = "https://fra1.digitaloceanspaces.com/anytype-release/latest-android.json"
const val SHARE_DIALOG_LABEL = "anytype.dialog.share.label"