WIP push notifications likes
This commit is contained in:
parent
0aa7b1001f
commit
16f9d7f90d
@ -1,21 +1,24 @@
|
|||||||
package com.isolaatti
|
package com.isolaatti
|
||||||
|
|
||||||
import android.app.Application
|
import android.app.Application
|
||||||
|
import android.app.NotificationChannel
|
||||||
|
import android.app.NotificationManager
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.net.ConnectivityManager
|
import android.net.ConnectivityManager
|
||||||
|
import android.os.Build
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
|
import androidx.annotation.RequiresApi
|
||||||
import androidx.datastore.core.DataStore
|
import androidx.datastore.core.DataStore
|
||||||
import androidx.datastore.preferences.core.Preferences
|
import androidx.datastore.preferences.core.Preferences
|
||||||
import androidx.datastore.preferences.preferencesDataStore
|
import androidx.datastore.preferences.preferencesDataStore
|
||||||
import com.google.firebase.Firebase
|
|
||||||
import com.google.firebase.messaging.FirebaseMessaging
|
import com.google.firebase.messaging.FirebaseMessaging
|
||||||
import com.isolaatti.connectivity.ConnectivityCallbackImpl
|
import com.isolaatti.connectivity.ConnectivityCallbackImpl
|
||||||
import com.isolaatti.push_notifications.FcmService
|
|
||||||
import com.isolaatti.push_notifications.PushNotificationsApi
|
import com.isolaatti.push_notifications.PushNotificationsApi
|
||||||
import dagger.hilt.android.HiltAndroidApp
|
import dagger.hilt.android.HiltAndroidApp
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
import okhttp3.RequestBody.Companion.toRequestBody
|
||||||
import retrofit2.awaitResponse
|
import retrofit2.awaitResponse
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
@ -27,6 +30,8 @@ class MyApplication : Application() {
|
|||||||
companion object {
|
companion object {
|
||||||
lateinit var myApp: MyApplication
|
lateinit var myApp: MyApplication
|
||||||
const val LOG_TAG = "MyApplication"
|
const val LOG_TAG = "MyApplication"
|
||||||
|
|
||||||
|
const val LIKES_NOTIFICATION_CHANNEL_ID = "like notification"
|
||||||
}
|
}
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
@ -42,18 +47,27 @@ class MyApplication : Application() {
|
|||||||
connectivityCallbackImpl = ConnectivityCallbackImpl()
|
connectivityCallbackImpl = ConnectivityCallbackImpl()
|
||||||
getSystemService(ConnectivityManager::class.java).registerDefaultNetworkCallback(connectivityCallbackImpl)
|
getSystemService(ConnectivityManager::class.java).registerDefaultNetworkCallback(connectivityCallbackImpl)
|
||||||
|
|
||||||
FirebaseMessaging.getInstance().token.addOnCompleteListener {
|
FirebaseMessaging.getInstance().token.addOnSuccessListener {
|
||||||
if(!it.isSuccessful) {
|
CoroutineScope(Dispatchers.IO).launch {
|
||||||
Log.w(LOG_TAG, "Failed fetching fcm token")
|
val response = pushNotificationsApi.registerDevice(it.toRequestBody()).awaitResponse()
|
||||||
return@addOnCompleteListener
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
CoroutineScope(Dispatchers.IO).launch {
|
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||||
val response = pushNotificationsApi.registerDevice(it.result).awaitResponse()
|
val notificationManager = getSystemService(NOTIFICATION_SERVICE) as NotificationManager
|
||||||
Log.d(FcmService.LOG_TAG, "Device registered. FCM token: $it.result")
|
createLikesNotificationChannel(notificationManager)
|
||||||
Log.d(FcmService.LOG_TAG, "Response: isSuccessful: ${response.isSuccessful}")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@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() {
|
override fun onTerminate() {
|
||||||
|
|||||||
@ -19,4 +19,17 @@ object CoilImageLoader {
|
|||||||
add(SvgDecoder.Factory())
|
add(SvgDecoder.Factory())
|
||||||
}.build()
|
}.build()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun getImageLoader(context: Context): ImageLoader {
|
||||||
|
return ImageLoader
|
||||||
|
.Builder(context)
|
||||||
|
.memoryCache {
|
||||||
|
MemoryCache.Builder(context)
|
||||||
|
.maxSizePercent(0.25)
|
||||||
|
.build()
|
||||||
|
}
|
||||||
|
.components {
|
||||||
|
add(SvgDecoder.Factory())
|
||||||
|
}.build()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@ -1,22 +1,52 @@
|
|||||||
package com.isolaatti.push_notifications
|
package com.isolaatti.push_notifications
|
||||||
|
|
||||||
import android.Manifest
|
import android.Manifest
|
||||||
|
import android.content.pm.PackageManager
|
||||||
import android.util.Log
|
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.FirebaseMessagingService
|
||||||
import com.google.firebase.messaging.RemoteMessage
|
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 dagger.hilt.android.AndroidEntryPoint
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
import okhttp3.RequestBody.Companion.toRequestBody
|
||||||
|
import okhttp3.ResponseBody.Companion.toResponseBody
|
||||||
import retrofit2.awaitResponse
|
import retrofit2.awaitResponse
|
||||||
|
import retrofit2.http.Url
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
@AndroidEntryPoint
|
@AndroidEntryPoint
|
||||||
class FcmService : FirebaseMessagingService() {
|
class FcmService : FirebaseMessagingService() {
|
||||||
companion object {
|
companion object {
|
||||||
const val LOG_TAG = "FcmService"
|
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
|
@Inject
|
||||||
@ -25,19 +55,70 @@ class FcmService : FirebaseMessagingService() {
|
|||||||
override fun onNewToken(token: String) {
|
override fun onNewToken(token: String) {
|
||||||
|
|
||||||
CoroutineScope(Dispatchers.IO).launch {
|
CoroutineScope(Dispatchers.IO).launch {
|
||||||
val response = pushNotificationsApi.registerDevice(token).awaitResponse()
|
val response = pushNotificationsApi.registerDevice(token.toRequestBody()).awaitResponse()
|
||||||
Log.d(LOG_TAG, "Device registered. FCM token: $token")
|
|
||||||
Log.d(LOG_TAG, "Response: isSuccessful: ${response.isSuccessful}")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onMessageReceived(message: RemoteMessage) {
|
override fun onMessageReceived(message: RemoteMessage) {
|
||||||
super.onMessageReceived(message)
|
super.onMessageReceived(message)
|
||||||
|
Log.d(LOG_TAG, "onMessageReceived")
|
||||||
|
val type = message.data["type"]
|
||||||
|
|
||||||
Log.d(LOG_TAG, "Message received")
|
when(type) {
|
||||||
message.data.forEach { t, u ->
|
LikeNotification.TYPE -> showLikeNotification(message.data)
|
||||||
Log.d(LOG_TAG, "$t $u")
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,5 +1,7 @@
|
|||||||
package com.isolaatti.push_notifications
|
package com.isolaatti.push_notifications
|
||||||
|
|
||||||
|
import okhttp3.MultipartBody
|
||||||
|
import okhttp3.RequestBody
|
||||||
import retrofit2.Call
|
import retrofit2.Call
|
||||||
import retrofit2.http.Multipart
|
import retrofit2.http.Multipart
|
||||||
import retrofit2.http.PUT
|
import retrofit2.http.PUT
|
||||||
@ -9,6 +11,6 @@ interface PushNotificationsApi {
|
|||||||
|
|
||||||
@PUT("/api/push_notifications/register_device")
|
@PUT("/api/push_notifications/register_device")
|
||||||
@Multipart
|
@Multipart
|
||||||
fun registerDevice(@Part("token") token: String): Call<Unit>
|
fun registerDevice(@Part("token") token: RequestBody): Call<Unit>
|
||||||
|
|
||||||
}
|
}
|
||||||
BIN
app/src/main/res/drawable-hdpi/ic_notification.png
Normal file
BIN
app/src/main/res/drawable-hdpi/ic_notification.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 420 B |
BIN
app/src/main/res/drawable-mdpi/ic_notification.png
Normal file
BIN
app/src/main/res/drawable-mdpi/ic_notification.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 338 B |
BIN
app/src/main/res/drawable-xhdpi/ic_notification.png
Normal file
BIN
app/src/main/res/drawable-xhdpi/ic_notification.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 534 B |
BIN
app/src/main/res/drawable-xxhdpi/ic_notification.png
Normal file
BIN
app/src/main/res/drawable-xxhdpi/ic_notification.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 751 B |
BIN
app/src/main/res/drawable-xxxhdpi/ic_notification.png
Normal file
BIN
app/src/main/res/drawable-xxxhdpi/ic_notification.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 974 B |
@ -189,4 +189,8 @@
|
|||||||
<string name="push_notifications_dialog_title">Push notifications</string>
|
<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_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="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>
|
</resources>
|
||||||
Loading…
x
Reference in New Issue
Block a user