WIP push notifications likes

This commit is contained in:
erik-everardo 2024-03-05 22:03:47 -06:00
parent 0aa7b1001f
commit 16f9d7f90d
10 changed files with 134 additions and 20 deletions

View File

@ -1,21 +1,24 @@
package com.isolaatti
import android.app.Application
import android.app.NotificationChannel
import android.app.NotificationManager
import android.content.Context
import android.net.ConnectivityManager
import android.os.Build
import android.util.Log
import androidx.annotation.RequiresApi
import androidx.datastore.core.DataStore
import androidx.datastore.preferences.core.Preferences
import androidx.datastore.preferences.preferencesDataStore
import com.google.firebase.Firebase
import com.google.firebase.messaging.FirebaseMessaging
import com.isolaatti.connectivity.ConnectivityCallbackImpl
import com.isolaatti.push_notifications.FcmService
import com.isolaatti.push_notifications.PushNotificationsApi
import dagger.hilt.android.HiltAndroidApp
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import okhttp3.RequestBody.Companion.toRequestBody
import retrofit2.awaitResponse
import javax.inject.Inject
@ -27,6 +30,8 @@ class MyApplication : Application() {
companion object {
lateinit var myApp: MyApplication
const val LOG_TAG = "MyApplication"
const val LIKES_NOTIFICATION_CHANNEL_ID = "like notification"
}
@Inject
@ -42,18 +47,27 @@ class MyApplication : Application() {
connectivityCallbackImpl = ConnectivityCallbackImpl()
getSystemService(ConnectivityManager::class.java).registerDefaultNetworkCallback(connectivityCallbackImpl)
FirebaseMessaging.getInstance().token.addOnCompleteListener {
if(!it.isSuccessful) {
Log.w(LOG_TAG, "Failed fetching fcm token")
return@addOnCompleteListener
}
FirebaseMessaging.getInstance().token.addOnSuccessListener {
CoroutineScope(Dispatchers.IO).launch {
val response = pushNotificationsApi.registerDevice(it.result).awaitResponse()
Log.d(FcmService.LOG_TAG, "Device registered. FCM token: $it.result")
Log.d(FcmService.LOG_TAG, "Response: isSuccessful: ${response.isSuccessful}")
val response = pushNotificationsApi.registerDevice(it.toRequestBody()).awaitResponse()
}
}
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val notificationManager = getSystemService(NOTIFICATION_SERVICE) as NotificationManager
createLikesNotificationChannel(notificationManager)
}
}
@RequiresApi(Build.VERSION_CODES.O)
private fun createLikesNotificationChannel(notificationManager: NotificationManager) {
val name = getString(R.string.likes_notification_channel)
val description = getString(R.string.likes_notification_channel_description)
val importance = NotificationManager.IMPORTANCE_DEFAULT
val channel = NotificationChannel(LIKES_NOTIFICATION_CHANNEL_ID, name, importance).also {
it.description = description
}
notificationManager.createNotificationChannel(channel)
}
override fun onTerminate() {

View File

@ -19,4 +19,17 @@ object CoilImageLoader {
add(SvgDecoder.Factory())
}.build()
}
fun getImageLoader(context: Context): ImageLoader {
return ImageLoader
.Builder(context)
.memoryCache {
MemoryCache.Builder(context)
.maxSizePercent(0.25)
.build()
}
.components {
add(SvgDecoder.Factory())
}.build()
}
}

View File

@ -1,22 +1,52 @@
package com.isolaatti.push_notifications
import android.Manifest
import android.content.pm.PackageManager
import android.util.Log
import androidx.core.content.ContextCompat
import androidx.core.app.ActivityCompat
import androidx.core.app.NotificationCompat
import androidx.core.app.NotificationManagerCompat
import androidx.core.graphics.drawable.toBitmap
import coil.Coil
import coil.request.ImageRequest
import com.google.firebase.messaging.FirebaseMessagingService
import com.google.firebase.messaging.RemoteMessage
import dagger.hilt.EntryPoint
import com.isolaatti.MyApplication
import com.isolaatti.R
import com.isolaatti.common.CoilImageLoader
import com.isolaatti.notifications.domain.LikeNotification
import com.isolaatti.utils.UrlGen
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import okhttp3.RequestBody.Companion.toRequestBody
import okhttp3.ResponseBody.Companion.toResponseBody
import retrofit2.awaitResponse
import retrofit2.http.Url
import javax.inject.Inject
@AndroidEntryPoint
class FcmService : FirebaseMessagingService() {
companion object {
const val LOG_TAG = "FcmService"
object NotificationsConstants {
const val NOTIFICATION_ID = "id"
const val TIMESTAMP = "timeStamp"
const val USER_ID = "userId"
const val READ = "read"
const val RELATED_NOTIFICATIONS = "relatedNotifications"
object LikeNotificationConstants {
const val LIKE_ID = "likeId"
const val POST_ID = "postId"
const val AUTHOR_ID = "authorId"
const val AUTHOR_NAME = "authorName"
}
}
}
@Inject
@ -25,19 +55,70 @@ class FcmService : FirebaseMessagingService() {
override fun onNewToken(token: String) {
CoroutineScope(Dispatchers.IO).launch {
val response = pushNotificationsApi.registerDevice(token).awaitResponse()
Log.d(LOG_TAG, "Device registered. FCM token: $token")
Log.d(LOG_TAG, "Response: isSuccessful: ${response.isSuccessful}")
val response = pushNotificationsApi.registerDevice(token.toRequestBody()).awaitResponse()
}
}
override fun onMessageReceived(message: RemoteMessage) {
super.onMessageReceived(message)
Log.d(LOG_TAG, "onMessageReceived")
val type = message.data["type"]
Log.d(LOG_TAG, "Message received")
message.data.forEach { t, u ->
Log.d(LOG_TAG, "$t $u")
when(type) {
LikeNotification.TYPE -> showLikeNotification(message.data)
else -> {
Log.i(LOG_TAG, "Not showing notification of unknown type: ${message.data}")
}
}
}
private fun showLikeNotification(data: Map<String, String>) {
val notificationId = data[NotificationsConstants.NOTIFICATION_ID]?.toIntOrNull()
val relatedNotifications = data[NotificationsConstants.RELATED_NOTIFICATIONS]?.trimStart('[')?.trimEnd(']')?.split(",")
val likeId = data[NotificationsConstants.LikeNotificationConstants.LIKE_ID]
val postId = data[NotificationsConstants.LikeNotificationConstants.POST_ID]
val authorId = data[NotificationsConstants.LikeNotificationConstants.AUTHOR_ID]?.toIntOrNull()
val authorName = data[NotificationsConstants.LikeNotificationConstants.AUTHOR_NAME]
val imageUrl = authorId?.let { UrlGen.userProfileImage(it, true) }
Log.d(LOG_TAG, data.toString())
val imageRequest = ImageRequest
.Builder(this)
.data(imageUrl)
.fallback(R.drawable.baseline_person_24)
.target { drawable ->
val notificationBuilder = NotificationCompat.Builder(this, MyApplication.LIKES_NOTIFICATION_CHANNEL_ID)
.setSmallIcon(R.drawable.ic_notification)
.setContentTitle(getString(R.string.like_notification_title, authorName ?: ""))
.setContentText(getString(R.string.like_notification_text))
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
.setLargeIcon(drawable.toBitmap())
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.POST_NOTIFICATIONS) != PackageManager.PERMISSION_GRANTED) {
return@target
}
Log.v(LOG_TAG, "Notification id: $notificationId")
// notificationId should never be null
if(notificationId == null) {
return@target
}
NotificationManagerCompat.from(this).run {
relatedNotifications?.forEach {
it.toIntOrNull()?.let { relatedNotificationId -> cancel(relatedNotificationId) }
}
notify(notificationId, notificationBuilder.build())
}
}.build()
CoilImageLoader.getImageLoader(this).enqueue(imageRequest)
}
}

View File

@ -1,5 +1,7 @@
package com.isolaatti.push_notifications
import okhttp3.MultipartBody
import okhttp3.RequestBody
import retrofit2.Call
import retrofit2.http.Multipart
import retrofit2.http.PUT
@ -9,6 +11,6 @@ interface PushNotificationsApi {
@PUT("/api/push_notifications/register_device")
@Multipart
fun registerDevice(@Part("token") token: String): Call<Unit>
fun registerDevice(@Part("token") token: RequestBody): Call<Unit>
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 420 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 338 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 534 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 751 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 974 B

View File

@ -189,4 +189,8 @@
<string name="push_notifications_dialog_title">Push notifications</string>
<string name="push_notifications_dialog_message">Receive notifications to stay informed about your profile activity</string>
<string name="push_notifications_dialog_rejected_message">You won\'t receive notifications. You can change this on your device settings.</string>
<string name="likes_notification_channel">Likes notifications</string>
<string name="likes_notification_channel_description">Get notified when someone likes your posts.</string>
<string name="like_notification_title">%s claps to your post</string>
<string name="like_notification_text">Tap this notification to go to the post.</string>
</resources>