Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions apps/flipcash/shared/notifications/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ plugins {

android {
namespace = "${Gradle.flipcashNamespace}.shared.notifications"
testOptions.unitTests.isIncludeAndroidResources = true
}

dependencies {
Expand All @@ -16,4 +17,7 @@ dependencies {
implementation(libs.firebase.messaging)

implementation(libs.androidx.datastore)

testImplementation(kotlin("test"))
testImplementation(libs.robolectric)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
package com.flipcash.app.notifications

import android.content.Context
import androidx.annotation.StringRes
import androidx.core.app.NotificationChannelCompat
import androidx.core.app.NotificationChannelGroupCompat
import androidx.core.app.NotificationManagerCompat
import com.flipcash.services.models.NotificationCategory
import com.flipcash.shared.notifications.R

object NotificationChannels {
const val CHANNEL_DEFAULT = "fcm_fallback_notification_channel"
const val CHANNEL_DEPOSIT_WITHDRAWAL = "channel_deposit_withdrawal"
const val CHANNEL_BUY_SELL = "channel_buy_sell"
const val CHANNEL_GAIN = "channel_gain"
const val CHANNEL_CHAT = "channel_chat"
const val CHANNEL_CONTACT_JOIN = "channel_contact_join"

private const val GROUP_TRANSACTIONS = "group_transactions"
private const val GROUP_SOCIAL = "group_social"

private data class ChannelDef(
val id: String,
@StringRes val nameRes: Int,
@StringRes val descriptionRes: Int,
val importance: Int,
val groupId: String? = null,
val showBadge: Boolean = true,
)

fun ensureChannelGroups(context: Context, manager: NotificationManagerCompat) {
val groups = listOf(
NotificationChannelGroupCompat.Builder(GROUP_TRANSACTIONS)
.setName(context.getString(R.string.notification_group_transactions))
.build(),
NotificationChannelGroupCompat.Builder(GROUP_SOCIAL)
.setName(context.getString(R.string.notification_group_social))
.build(),
)
groups.forEach { manager.createNotificationChannelGroup(it) }
}

fun channelFor(context: Context, category: NotificationCategory): NotificationChannelCompat {
val def = when (category) {
NotificationCategory.DEFAULT -> ChannelDef(
id = CHANNEL_DEFAULT,
nameRes = R.string.notification_channel_default,
descriptionRes = R.string.notification_channel_default_description,
importance = NotificationManagerCompat.IMPORTANCE_DEFAULT,
showBadge = false,
)
NotificationCategory.DEPOSIT_WITHDRAWAL -> ChannelDef(
id = CHANNEL_DEPOSIT_WITHDRAWAL,
nameRes = R.string.notification_channel_deposit_withdrawal,
descriptionRes = R.string.notification_channel_deposit_withdrawal_description,
importance = NotificationManagerCompat.IMPORTANCE_HIGH,
groupId = GROUP_TRANSACTIONS,
)
NotificationCategory.BUY_SELL -> ChannelDef(
id = CHANNEL_BUY_SELL,
nameRes = R.string.notification_channel_buy_sell,
descriptionRes = R.string.notification_channel_buy_sell_description,
importance = NotificationManagerCompat.IMPORTANCE_HIGH,
groupId = GROUP_TRANSACTIONS,
)
NotificationCategory.GAIN -> ChannelDef(
id = CHANNEL_GAIN,
nameRes = R.string.notification_channel_gain,
descriptionRes = R.string.notification_channel_gain_description,
importance = NotificationManagerCompat.IMPORTANCE_HIGH,
groupId = GROUP_TRANSACTIONS,
)
NotificationCategory.CHAT -> ChannelDef(
id = CHANNEL_CHAT,
nameRes = R.string.notification_channel_chat,
descriptionRes = R.string.notification_channel_chat_description,
importance = NotificationManagerCompat.IMPORTANCE_HIGH,
groupId = GROUP_SOCIAL,
)
NotificationCategory.CONTACT_JOIN -> ChannelDef(
id = CHANNEL_CONTACT_JOIN,
nameRes = R.string.notification_channel_contact_join,
descriptionRes = R.string.notification_channel_contact_join_description,
importance = NotificationManagerCompat.IMPORTANCE_HIGH,
groupId = GROUP_SOCIAL,
showBadge = false,
)
}

return NotificationChannelCompat.Builder(def.id, def.importance)
.setName(context.getString(def.nameRes))
.setDescription(context.getString(def.descriptionRes))
.setShowBadge(def.showBadge)
.apply { def.groupId?.let { setGroup(it) } }
.build()
}
}
Original file line number Diff line number Diff line change
@@ -1,17 +1,12 @@
package com.flipcash.app.notifications

import android.Manifest
import android.app.NotificationChannel
import android.app.PendingIntent
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.media.RingtoneManager
import android.net.Uri
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.toArgb
import androidx.core.app.ActivityCompat
import androidx.core.app.NotificationChannelCompat
import androidx.core.app.NotificationCompat
import androidx.core.app.NotificationManagerCompat
import androidx.core.net.toUri
Expand All @@ -20,10 +15,10 @@ import com.flipcash.app.core.util.Linkify
import com.flipcash.app.tokens.TokenCoordinator
import com.flipcash.services.controllers.PushController
import com.flipcash.services.models.NavigationTrigger
import com.flipcash.services.models.NotificationCategory
import com.flipcash.services.models.NotificationPayload
import com.flipcash.services.user.UserManager
import com.flipcash.shared.notifications.R
import com.getcode.opencode.controllers.TokenController
import com.getcode.utils.TraceType
import com.getcode.utils.trace
import com.google.firebase.messaging.FirebaseMessagingService
Expand Down Expand Up @@ -74,12 +69,6 @@ class NotificationService : FirebaseMessagingService(),
override fun onMessageReceived(message: RemoteMessage) {
super.onMessageReceived(message)

// dump everything into FCM fallback channel for now
val channel = NotificationChannelCompat.Builder(
"fcm_fallback_notification_channel",
NotificationManagerCompat.IMPORTANCE_DEFAULT
).setName("Misc.").build()

val title = message.data["push_notification_title"]?.ifEmpty { message.notification?.title }
val body = message.data["push_notification_body"]?.ifEmpty { message.notification?.body }

Expand Down Expand Up @@ -108,8 +97,13 @@ class NotificationService : FirebaseMessagingService(),
}
}

val category = payload?.category ?: NotificationCategory.DEFAULT
NotificationChannels.ensureChannelGroups(this, notificationManager)
val channel = NotificationChannels.channelFor(this, category)
notificationManager.createNotificationChannel(channel)

val groupKey = payload?.groupKey?.takeIf { it.isNotEmpty() }

val notificationBuilder: NotificationCompat.Builder =
NotificationCompat.Builder(this, channel.id)
.setPriority(NotificationCompat.PRIORITY_HIGH)
Expand All @@ -120,16 +114,27 @@ class NotificationService : FirebaseMessagingService(),
.setContentTitle(title)
.setContentText(body)
.setContentIntent(buildContentIntent(payload?.navigation))
.apply { if (groupKey != null) setGroup(groupKey) }

val random = SecureRandom()
val notificationId = random.nextInt(256)
val notificationId = SecureRandom().nextInt(Int.MAX_VALUE)

if (ActivityCompat.checkSelfPermission(
this,
Manifest.permission.POST_NOTIFICATIONS
) == PackageManager.PERMISSION_GRANTED
) {
notificationManager.notify(notificationId, notificationBuilder.build())

if (groupKey != null) {
val summary = NotificationCompat.Builder(this, channel.id)
.setSmallIcon(R.drawable.flipcash_logo)
.setColor(getColor(R.color.notification_color))
.setGroup(groupKey)
.setGroupSummary(true)
.setAutoCancel(true)
.build()
notificationManager.notify(groupKey.hashCode(), summary)
}
}
}

Expand Down
22 changes: 22 additions & 0 deletions apps/flipcash/shared/notifications/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- Channel names -->
<string name="notification_channel_default">Misc.</string>
<string name="notification_channel_deposit_withdrawal">Deposits &amp; Withdrawals</string>
<string name="notification_channel_buy_sell">Buys &amp; Sells</string>
<string name="notification_channel_gain">Price Alerts</string>
<string name="notification_channel_chat">Chat Messages</string>
<string name="notification_channel_contact_join">New Contacts</string>

<!-- Channel descriptions -->
<string name="notification_channel_default_description">General notifications</string>
<string name="notification_channel_deposit_withdrawal_description">Deposit and withdrawal confirmations</string>
<string name="notification_channel_buy_sell_description">Token purchase and sale confirmations</string>
<string name="notification_channel_gain_description">Token price movement alerts</string>
<string name="notification_channel_chat_description">Incoming chat messages</string>
<string name="notification_channel_contact_join_description">When someone you know joins Flipcash</string>

<!-- Channel group names -->
<string name="notification_group_transactions">Transactions</string>
<string name="notification_group_social">Social</string>
</resources>
Loading
Loading