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"