Files
gudari/app/src/main/java/com/bitcointxoko/gudariwallet/util/NotificationHelper.kt
T

203 lines
8.8 KiB
Kotlin

package com.bitcointxoko.gudariwallet.util
import android.app.Notification
import android.app.NotificationChannel
import android.app.NotificationManager
import android.app.PendingIntent
import android.content.Context
import android.content.Intent
import androidx.core.app.NotificationCompat
import androidx.core.app.NotificationManagerCompat
import com.bitcointxoko.gudariwallet.MainActivity
import com.bitcointxoko.gudariwallet.R
object NotificationHelper {
// ── Intent extra key — read by MainActivity ───────────────────────────────
const val EXTRA_PAYMENT_HASH = "extra_payment_hash"
// ── Channel setup ─────────────────────────────────────────────────────────
fun createChannels(context: Context) {
val nm = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
nm.createNotificationChannel(
NotificationChannel(
NotificationConstants.CHANNEL_PAYMENTS,
context.getString(R.string.notif_channel_payments_name),
NotificationManager.IMPORTANCE_HIGH
).apply {
description = context.getString(R.string.notif_channel_payments_desc)
enableVibration(true)
enableLights(true)
}
)
nm.createNotificationChannel(
NotificationChannel(
NotificationConstants.CHANNEL_SERVICE_STATUS,
context.getString(R.string.notif_channel_service_name),
NotificationManager.IMPORTANCE_LOW
).apply {
description = context.getString(R.string.notif_channel_service_desc)
setShowBadge(false)
enableVibration(false)
setSound(null, null)
}
)
nm.createNotificationChannel(
NotificationChannel(
NotificationConstants.CHANNEL_SYNC,
context.getString(R.string.notif_channel_sync_name),
NotificationManager.IMPORTANCE_DEFAULT
).apply {
description = context.getString(R.string.notif_channel_sync_desc)
enableVibration(false)
setSound(null, null)
}
)
}
// ── User-facing notifications ─────────────────────────────────────────────
/**
* Posts a high-priority notification for an incoming payment.
* Tapping opens MainActivity and navigates directly to the payment detail
* screen for [paymentHash].
*/
fun notifyPaymentReceived(context: Context, amountSats: Long, memo: String?, paymentHash: String) {
val title = context.getString(R.string.notif_payment_received_title, amountSats)
val text = if (!memo.isNullOrBlank()) memo
else context.getString(R.string.notif_payment_received_no_memo)
val notification = NotificationCompat.Builder(context, NotificationConstants.CHANNEL_PAYMENTS)
.setSmallIcon(R.drawable.ic_notification)
.setContentTitle(title)
.setContentText(text)
.setAutoCancel(true)
.setContentIntent(mainActivityPendingIntent(context, paymentHash))
// BigTextStyle shows the full memo when the notification is expanded,
// in case it is long enough to be truncated in the compact view.
.setStyle(
NotificationCompat.BigTextStyle()
.bigText(text)
.setSummaryText(context.getString(R.string.notif_payment_received_summary, amountSats))
)
.build()
notify(context, NotificationConstants.NOTIF_ID_PAYMENT_RECEIVED, notification)
}
/**
* Posts a default-priority notification for a successfully sent payment.
* Tapping opens MainActivity and navigates directly to the payment detail
* screen for [paymentHash].
*/
fun notifyPaymentSent(context: Context, amountSats: Long, paymentHash: String) {
val title = context.getString(R.string.notif_payment_sent_title)
val text = context.getString(R.string.notif_payment_sent_text, amountSats)
notify(
context,
NotificationConstants.NOTIF_ID_PAYMENT_SENT,
buildPaymentNotification(context, title, text, paymentHash)
)
}
/**
* Posts a notification when the one-time historical payment sync completes.
* No tap destination needed — the user is already in the app or will land
* on the main screen via the default MainActivity launch.
*/
fun notifySyncComplete(context: Context, paymentCount: Int) {
val title = context.getString(R.string.notif_sync_complete_title)
val text = context.getString(R.string.notif_sync_complete_text, paymentCount)
notify(
context,
NotificationConstants.NOTIF_ID_SYNC_COMPLETE,
NotificationCompat.Builder(context, NotificationConstants.CHANNEL_SYNC)
.setSmallIcon(R.drawable.ic_notification)
.setContentTitle(title)
.setContentText(text)
.setAutoCancel(true)
.build()
)
}
// ── Foreground service notification ───────────────────────────────────────
fun buildServiceNotification(context: Context): Notification {
return NotificationCompat.Builder(context, NotificationConstants.CHANNEL_SERVICE_STATUS)
.setSmallIcon(R.drawable.ic_notification)
.setContentTitle(context.getString(R.string.notif_service_title))
.setContentText(context.getString(R.string.notif_service_text))
.setOngoing(true)
.setSilent(true)
.setShowWhen(false)
.setForegroundServiceBehavior(NotificationCompat.FOREGROUND_SERVICE_IMMEDIATE)
// Service notification taps open the app with no specific destination
.setContentIntent(mainActivityPendingIntent(context, paymentHash = null))
.build()
}
// ── Dismiss ───────────────────────────────────────────────────────────────
fun cancel(context: Context, notificationId: Int) {
NotificationManagerCompat.from(context).cancel(notificationId)
}
// ── Private helpers ───────────────────────────────────────────────────────
private fun buildPaymentNotification(
context : Context,
title : String,
text : String,
paymentHash: String
): Notification {
return NotificationCompat.Builder(context, NotificationConstants.CHANNEL_PAYMENTS)
.setSmallIcon(R.drawable.ic_notification)
.setContentTitle(title)
.setContentText(text)
.setAutoCancel(true)
.setContentIntent(mainActivityPendingIntent(context, paymentHash))
.build()
}
/**
* Builds a PendingIntent that opens MainActivity.
*
* If [paymentHash] is non-null, it is attached as [EXTRA_PAYMENT_HASH].
* MainActivity reads this in onCreate / onNewIntent and navigates to the
* payment detail screen.
*
* FLAG_UPDATE_CURRENT ensures that if a second notification arrives before
* the first is tapped, the hash is updated to the latest payment.
* Each notification ID gets its own requestCode so they don't overwrite
* each other's extras.
*/
private fun mainActivityPendingIntent(context: Context, paymentHash: String?): PendingIntent {
val intent = Intent(context, MainActivity::class.java).apply {
flags = Intent.FLAG_ACTIVITY_SINGLE_TOP or Intent.FLAG_ACTIVITY_CLEAR_TOP
if (paymentHash != null) {
putExtra(EXTRA_PAYMENT_HASH, paymentHash)
}
}
// Use the hash's hashCode as requestCode so each unique payment gets
// its own PendingIntent — prevents one notification's tap from
// overwriting another's extras.
val requestCode = paymentHash?.hashCode() ?: 0
return PendingIntent.getActivity(
context,
requestCode,
intent,
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
)
}
private fun notify(context: Context, id: Int, notification: Notification) {
runCatching {
NotificationManagerCompat.from(context).notify(id, notification)
}
}
}