203 lines
8.8 KiB
Kotlin
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)
|
|
}
|
|
}
|
|
}
|