mirror of
https://github.com/aljazceru/breez-sdk-liquid.git
synced 2025-12-24 01:14:22 +01:00
Auto dismiss replaceable notifications (#711)
This commit is contained in:
@@ -14,17 +14,21 @@ class BreezSdkLiquidConnector {
|
||||
internal fun connectSDK(
|
||||
connectRequest: ConnectRequest,
|
||||
sdkListener: EventListener,
|
||||
logger: ServiceLogger
|
||||
logger: ServiceLogger,
|
||||
): BindingLiquidSdk {
|
||||
synchronized(this) {
|
||||
if (liquidSDK == null) {
|
||||
logger.log(
|
||||
TAG, "Connecting to Breez Liquid SDK", "DEBUG"
|
||||
TAG,
|
||||
"Connecting to Breez Liquid SDK",
|
||||
"DEBUG",
|
||||
)
|
||||
liquidSDK = connect(connectRequest)
|
||||
logger.log(TAG, "Connected to Breez Liquid SDK", "DEBUG")
|
||||
liquidSDK!!.addEventListener(sdkListener)
|
||||
} else logger.log(TAG, "Already connected to Breez Liquid SDK", "DEBUG")
|
||||
} else {
|
||||
logger.log(TAG, "Already connected to Breez Liquid SDK", "DEBUG")
|
||||
}
|
||||
|
||||
return liquidSDK!!
|
||||
}
|
||||
|
||||
@@ -5,12 +5,13 @@ object Constants {
|
||||
const val SHUTDOWN_DELAY_MS = 60 * 1000L
|
||||
|
||||
// Notification Channels
|
||||
const val NOTIFICATION_CHANNEL_SWAP_UPDATED = "SWAP_UPDATED"
|
||||
const val NOTIFICATION_CHANNEL_DISMISSIBLE = "DISMISSIBLE"
|
||||
const val NOTIFICATION_CHANNEL_FOREGROUND_SERVICE = "FOREGROUND_SERVICE"
|
||||
const val NOTIFICATION_CHANNEL_LNURL_PAY = "LNURL_PAY"
|
||||
const val NOTIFICATION_CHANNEL_REPLACEABLE = "REPLACEABLE"
|
||||
|
||||
// Notification Ids
|
||||
const val NOTIFICATION_ID_FOREGROUND_SERVICE = 100
|
||||
const val NOTIFICATION_ID_REPLACEABLE = 1001
|
||||
|
||||
// Intent Extras
|
||||
const val EXTRA_REMOTE_MESSAGE = "remote_message"
|
||||
@@ -18,6 +19,7 @@ object Constants {
|
||||
// Message Data
|
||||
@Suppress("unused")
|
||||
const val MESSAGE_DATA_TYPE = "notification_type"
|
||||
|
||||
@Suppress("unused")
|
||||
const val MESSAGE_DATA_PAYLOAD = "notification_payload"
|
||||
|
||||
@@ -26,6 +28,14 @@ object Constants {
|
||||
const val MESSAGE_TYPE_SWAP_UPDATED = "swap_updated"
|
||||
|
||||
// Resource Identifiers
|
||||
const val DISMISSIBLE_NOTIFICATION_CHANNEL_DESCRIPTION =
|
||||
"dismissible_notification_channel_description"
|
||||
const val DISMISSIBLE_NOTIFICATION_CHANNEL_NAME =
|
||||
"dismissible_notification_channel_name"
|
||||
const val DISMISSIBLE_WORKGROUP_ID = "dismissible"
|
||||
const val DISMISSIBLE_WORKGROUP_DESCRIPTION =
|
||||
"dismissible_work_group_description"
|
||||
const val DISMISSIBLE_WORKGROUP_NAME = "dismissible_work_group_name"
|
||||
const val FOREGROUND_SERVICE_NOTIFICATION_CHANNEL_DESCRIPTION =
|
||||
"foreground_service_notification_channel_description"
|
||||
const val FOREGROUND_SERVICE_NOTIFICATION_CHANNEL_NAME =
|
||||
@@ -38,15 +48,8 @@ object Constants {
|
||||
"lnurl_pay_invoice_notification_title"
|
||||
const val LNURL_PAY_METADATA_PLAIN_TEXT =
|
||||
"lnurl_pay_metadata_plain_text"
|
||||
const val LNURL_PAY_NOTIFICATION_CHANNEL_DESCRIPTION =
|
||||
"lnurl_pay_notification_channel_description"
|
||||
const val LNURL_PAY_NOTIFICATION_CHANNEL_NAME =
|
||||
"lnurl_pay_notification_channel_name"
|
||||
const val LNURL_PAY_NOTIFICATION_FAILURE_TITLE =
|
||||
"lnurl_pay_notification_failure_title"
|
||||
const val LNURL_PAY_WORKGROUP_ID = "lnurl_pay"
|
||||
const val LNURL_PAY_WORKGROUP_DESCRIPTION = "lnurl_pay_work_group_description"
|
||||
const val LNURL_PAY_WORKGROUP_NAME = "lnurl_pay_work_group_name"
|
||||
const val NOTIFICATION_COLOR = "default_notification_color"
|
||||
const val NOTIFICATION_ICON = "ic_stat_ic_notification"
|
||||
const val PAYMENT_RECEIVED_NOTIFICATION_TEXT =
|
||||
@@ -61,22 +64,27 @@ object Constants {
|
||||
"payment_waiting_fee_acceptance_notification_title"
|
||||
const val PAYMENT_WAITING_FEE_ACCEPTANCE_TEXT =
|
||||
"payment_waiting_fee_acceptance_text"
|
||||
const val REPLACEABLE_NOTIFICATION_CHANNEL_DESCRIPTION =
|
||||
"replaceable_notification_channel_description"
|
||||
const val REPLACEABLE_NOTIFICATION_CHANNEL_NAME =
|
||||
"replaceable_notification_channel_name"
|
||||
const val REPLACEABLE_WORKGROUP_ID = "replaceable"
|
||||
const val REPLACEABLE_WORKGROUP_DESCRIPTION = "replaceable_work_group_description"
|
||||
const val REPLACEABLE_WORKGROUP_NAME = "replaceable_work_group_name"
|
||||
const val SWAP_CONFIRMED_NOTIFICATION_FAILURE_TEXT =
|
||||
"swap_confirmed_notification_failure_text"
|
||||
const val SWAP_CONFIRMED_NOTIFICATION_FAILURE_TITLE =
|
||||
"swap_confirmed_notification_failure_title"
|
||||
const val SWAP_CONFIRMED_NOTIFICATION_TITLE =
|
||||
"swap_confirmed_notification_title"
|
||||
const val SWAP_UPDATED_NOTIFICATION_CHANNEL_DESCRIPTION =
|
||||
"swap_updated_notification_channel_description"
|
||||
const val SWAP_UPDATED_NOTIFICATION_CHANNEL_NAME =
|
||||
"swap_updated_notification_channel_name"
|
||||
const val SWAP_UPDATED_WORKGROUP_ID = "swap_updated"
|
||||
const val SWAP_UPDATED_WORKGROUP_DESCRIPTION =
|
||||
"swap_updated_work_group_description"
|
||||
const val SWAP_UPDATED_WORKGROUP_NAME = "swap_updated_work_group_name"
|
||||
|
||||
// Resource Identifier Defaults
|
||||
const val DEFAULT_DISMISSIBLE_NOTIFICATION_CHANNEL_DESCRIPTION =
|
||||
"Channel for dismissible notifications when the application is in the background"
|
||||
const val DEFAULT_DISMISSIBLE_NOTIFICATION_CHANNEL_NAME = "Dismissable Notifications"
|
||||
const val DEFAULT_DISMISSIBLE_WORKGROUP_DESCRIPTION =
|
||||
"Required to handle dismissible notifications when the application is in the background"
|
||||
const val DEFAULT_DISMISSIBLE_WORKGROUP_NAME = "Dismissable Notifications"
|
||||
const val DEFAULT_FOREGROUND_SERVICE_NOTIFICATION_CHANNEL_DESCRIPTION =
|
||||
"Shown when the application is in the background"
|
||||
const val DEFAULT_FOREGROUND_SERVICE_NOTIFICATION_CHANNEL_NAME =
|
||||
@@ -89,14 +97,8 @@ object Constants {
|
||||
"Fetching Invoice"
|
||||
const val DEFAULT_LNURL_PAY_METADATA_PLAIN_TEXT =
|
||||
"Pay with LNURL"
|
||||
const val DEFAULT_LNURL_PAY_NOTIFICATION_CHANNEL_DESCRIPTION =
|
||||
"Notifications for receiving payments when the application is in the background"
|
||||
const val DEFAULT_LNURL_PAY_NOTIFICATION_CHANNEL_NAME = "Receiving Payments"
|
||||
const val DEFAULT_LNURL_PAY_NOTIFICATION_FAILURE_TITLE =
|
||||
"Receive Payment Failed"
|
||||
const val DEFAULT_LNURL_PAY_WORKGROUP_DESCRIPTION =
|
||||
"Required to handle LNURL pay requests when the application is in the background"
|
||||
const val DEFAULT_LNURL_PAY_WORKGROUP_NAME = "LNURL Payments"
|
||||
const val DEFAULT_NOTIFICATION_COLOR = "#0089F9"
|
||||
const val DEFAULT_PAYMENT_RECEIVED_NOTIFICATION_TEXT =
|
||||
"Received %d sats"
|
||||
@@ -110,15 +112,15 @@ object Constants {
|
||||
"Payment requires fee acceptance"
|
||||
const val DEFAULT_PAYMENT_WAITING_FEE_ACCEPTANCE_TEXT =
|
||||
"Tap to review updated fees"
|
||||
const val DEFAULT_REPLACEABLE_WORKGROUP_DESCRIPTION =
|
||||
"Required to handle replaceable notifications when the application is in the background"
|
||||
const val DEFAULT_REPLACEABLE_WORKGROUP_NAME = "Replaceable Notifications"
|
||||
const val DEFAULT_REPLACEABLE_NOTIFICATION_CHANNEL_DESCRIPTION =
|
||||
"Channel for replaceable notifications when the application is in the background"
|
||||
const val DEFAULT_REPLACEABLE_NOTIFICATION_CHANNEL_NAME =
|
||||
"Replaceable Notifications"
|
||||
const val DEFAULT_SWAP_CONFIRMED_NOTIFICATION_FAILURE_TEXT =
|
||||
"Tap to complete payment"
|
||||
const val DEFAULT_SWAP_CONFIRMED_NOTIFICATION_FAILURE_TITLE =
|
||||
"Payment Pending"
|
||||
const val DEFAULT_SWAP_UPDATED_NOTIFICATION_CHANNEL_DESCRIPTION =
|
||||
"Notifications for swap updates when the application is in the background"
|
||||
const val DEFAULT_SWAP_UPDATED_NOTIFICATION_CHANNEL_NAME =
|
||||
"Swap Updates"
|
||||
const val DEFAULT_SWAP_UPDATED_WORKGROUP_DESCRIPTION =
|
||||
"Required to handle swap updates when the application is in the background"
|
||||
const val DEFAULT_SWAP_UPDATED_WORKGROUP_NAME = "Swap Updates"
|
||||
}
|
||||
|
||||
@@ -11,9 +11,9 @@ import breez_sdk_liquid.EventListener
|
||||
import breez_sdk_liquid.Logger
|
||||
import breez_sdk_liquid.SdkEvent
|
||||
import breez_sdk_liquid_notification.BreezSdkLiquidConnector.Companion.connectSDK
|
||||
import breez_sdk_liquid_notification.Constants.MESSAGE_TYPE_SWAP_UPDATED
|
||||
import breez_sdk_liquid_notification.Constants.MESSAGE_TYPE_LNURL_PAY_INFO
|
||||
import breez_sdk_liquid_notification.Constants.MESSAGE_TYPE_LNURL_PAY_INVOICE
|
||||
import breez_sdk_liquid_notification.Constants.MESSAGE_TYPE_SWAP_UPDATED
|
||||
import breez_sdk_liquid_notification.Constants.NOTIFICATION_ID_FOREGROUND_SERVICE
|
||||
import breez_sdk_liquid_notification.Constants.SERVICE_TIMEOUT_MS
|
||||
import breez_sdk_liquid_notification.Constants.SHUTDOWN_DELAY_MS
|
||||
@@ -22,19 +22,23 @@ import breez_sdk_liquid_notification.job.Job
|
||||
import breez_sdk_liquid_notification.job.LnurlPayInfoJob
|
||||
import breez_sdk_liquid_notification.job.LnurlPayInvoiceJob
|
||||
import breez_sdk_liquid_notification.job.SwapUpdatedJob
|
||||
import kotlin.io.path.Path
|
||||
import kotlinx.coroutines.CoroutineExceptionHandler
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.SupervisorJob
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlin.io.path.Path
|
||||
|
||||
interface SdkForegroundService {
|
||||
fun onFinished(job: Job)
|
||||
}
|
||||
|
||||
abstract class ForegroundService : SdkForegroundService, EventListener, Service() {
|
||||
abstract class ForegroundService :
|
||||
Service(),
|
||||
SdkForegroundService,
|
||||
EventListener {
|
||||
private var liquidSDK: BindingLiquidSdk? = null
|
||||
|
||||
@Suppress("MemberVisibilityCanBePrivate")
|
||||
val serviceScope = CoroutineScope(Dispatchers.Main.immediate + SupervisorJob())
|
||||
protected var logger: ServiceLogger = ServiceLogger()
|
||||
@@ -48,9 +52,7 @@ abstract class ForegroundService : SdkForegroundService, EventListener, Service(
|
||||
// SERVICE LIFECYCLE //
|
||||
// =========================================================== //
|
||||
|
||||
override fun onBind(intent: Intent): IBinder? {
|
||||
return null
|
||||
}
|
||||
override fun onBind(intent: Intent): IBinder? = null
|
||||
|
||||
/** Called by a Job to signal that it is complete. */
|
||||
override fun onFinished(job: Job) {
|
||||
@@ -63,7 +65,8 @@ abstract class ForegroundService : SdkForegroundService, EventListener, Service(
|
||||
|
||||
/** Stop the service */
|
||||
private val serviceTimeoutHandler = Handler(Looper.getMainLooper())
|
||||
private val serviceTimeoutRunnable: Runnable = Runnable {
|
||||
private val serviceTimeoutRunnable: Runnable =
|
||||
Runnable {
|
||||
logger.log(TAG, "Reached service timeout...", "DEBUG")
|
||||
synchronized(this) {
|
||||
jobs.forEach { job -> job.onShutdown() }
|
||||
@@ -73,7 +76,8 @@ abstract class ForegroundService : SdkForegroundService, EventListener, Service(
|
||||
}
|
||||
|
||||
private val shutdownHandler = Handler(Looper.getMainLooper())
|
||||
private val shutdownRunnable: Runnable = Runnable {
|
||||
private val shutdownRunnable: Runnable =
|
||||
Runnable {
|
||||
logger.log(TAG, "Reached scheduled shutdown...", "DEBUG")
|
||||
shutdown()
|
||||
}
|
||||
@@ -98,7 +102,11 @@ abstract class ForegroundService : SdkForegroundService, EventListener, Service(
|
||||
}
|
||||
|
||||
/** Called when an intent is called for this service. */
|
||||
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
|
||||
override fun onStartCommand(
|
||||
intent: Intent?,
|
||||
flags: Int,
|
||||
startId: Int,
|
||||
): Int {
|
||||
super.onStartCommand(intent, flags, startId)
|
||||
resetDelayedCallbacks()
|
||||
|
||||
@@ -136,43 +144,51 @@ abstract class ForegroundService : SdkForegroundService, EventListener, Service(
|
||||
|
||||
/** Get the job to be executed from the Message data in the Intent.
|
||||
* This can be overridden to handle custom jobs. */
|
||||
open fun getJobFromIntent(intent: Intent?): Job? {
|
||||
return Message.createFromIntent(intent)?.let { message ->
|
||||
open fun getJobFromIntent(intent: Intent?): Job? =
|
||||
Message.createFromIntent(intent)?.let { message ->
|
||||
message.payload?.let { payload ->
|
||||
when (message.type) {
|
||||
MESSAGE_TYPE_SWAP_UPDATED -> SwapUpdatedJob(
|
||||
MESSAGE_TYPE_SWAP_UPDATED ->
|
||||
SwapUpdatedJob(
|
||||
applicationContext,
|
||||
this,
|
||||
payload,
|
||||
logger
|
||||
logger,
|
||||
)
|
||||
|
||||
MESSAGE_TYPE_LNURL_PAY_INFO -> LnurlPayInfoJob(
|
||||
MESSAGE_TYPE_LNURL_PAY_INFO ->
|
||||
LnurlPayInfoJob(
|
||||
applicationContext,
|
||||
this,
|
||||
payload,
|
||||
logger
|
||||
logger,
|
||||
)
|
||||
|
||||
MESSAGE_TYPE_LNURL_PAY_INVOICE -> LnurlPayInvoiceJob(
|
||||
MESSAGE_TYPE_LNURL_PAY_INVOICE ->
|
||||
LnurlPayInvoiceJob(
|
||||
applicationContext,
|
||||
this,
|
||||
payload,
|
||||
logger
|
||||
logger,
|
||||
)
|
||||
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun launchSdkConnection(connectRequest: ConnectRequest, job: Job) {
|
||||
private fun launchSdkConnection(
|
||||
connectRequest: ConnectRequest,
|
||||
job: Job,
|
||||
) {
|
||||
val sdkListener = this
|
||||
serviceScope.launch(Dispatchers.IO + CoroutineExceptionHandler { _, e ->
|
||||
serviceScope.launch(
|
||||
Dispatchers.IO +
|
||||
CoroutineExceptionHandler { _, e ->
|
||||
logger.log(TAG, "Breez Liquid SDK connection failed $e", "ERROR")
|
||||
delayedShutdown()
|
||||
}) {
|
||||
},
|
||||
) {
|
||||
liquidSDK ?: run {
|
||||
liquidSDK = connectSDK(connectRequest, sdkListener, logger)
|
||||
}
|
||||
|
||||
@@ -6,14 +6,18 @@ import android.os.Parcel
|
||||
import android.os.Parcelable
|
||||
import breez_sdk_liquid_notification.Constants.EXTRA_REMOTE_MESSAGE
|
||||
|
||||
data class Message(val type: String?, val payload: String?) : Parcelable {
|
||||
data class Message(
|
||||
val type: String?,
|
||||
val payload: String?,
|
||||
) : Parcelable {
|
||||
constructor(parcel: Parcel) : this(parcel.readString(), parcel.readString())
|
||||
|
||||
override fun describeContents(): Int {
|
||||
return 0
|
||||
}
|
||||
override fun describeContents(): Int = 0
|
||||
|
||||
override fun writeToParcel(parcel: Parcel, flags: Int) {
|
||||
override fun writeToParcel(
|
||||
parcel: Parcel,
|
||||
flags: Int,
|
||||
) {
|
||||
parcel.writeString(type)
|
||||
parcel.writeString(payload)
|
||||
}
|
||||
@@ -22,19 +26,19 @@ data class Message(val type: String?, val payload: String?) : Parcelable {
|
||||
@Suppress("DEPRECATION")
|
||||
fun createFromIntent(intent: Intent?): Message? {
|
||||
return intent?.let {
|
||||
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) it.getParcelableExtra(
|
||||
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
||||
it.getParcelableExtra(
|
||||
EXTRA_REMOTE_MESSAGE,
|
||||
Message::class.java
|
||||
) else it.getParcelableExtra(EXTRA_REMOTE_MESSAGE)
|
||||
Message::class.java,
|
||||
)
|
||||
} else {
|
||||
it.getParcelableExtra(EXTRA_REMOTE_MESSAGE)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun createFromParcel(parcel: Parcel): Message {
|
||||
return Message(parcel)
|
||||
}
|
||||
override fun createFromParcel(parcel: Parcel): Message = Message(parcel)
|
||||
|
||||
override fun newArray(size: Int): Array<Message?> {
|
||||
return arrayOfNulls(size)
|
||||
}
|
||||
override fun newArray(size: Int): Array<Message?> = arrayOfNulls(size)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,14 +21,21 @@ interface MessagingService {
|
||||
|
||||
/** Check if the foreground service is needed depending on the
|
||||
* message type and foreground state of the application. */
|
||||
fun startServiceIfNeeded(context: Context, message: Message) {
|
||||
fun startServiceIfNeeded(
|
||||
context: Context,
|
||||
message: Message,
|
||||
) {
|
||||
val notificationManager = getNotificationManager(context)
|
||||
val isServiceNeeded = when (message.type) {
|
||||
val isServiceNeeded =
|
||||
when (message.type) {
|
||||
MESSAGE_TYPE_SWAP_UPDATED -> !isAppForeground(context)
|
||||
else -> true
|
||||
}
|
||||
if (notificationManager != null && isServiceNeeded) startForegroundService(message)
|
||||
else Log.w(TAG, "Ignoring message ${message.type}: ${message.payload}")
|
||||
if (notificationManager != null && isServiceNeeded) {
|
||||
startForegroundService(message)
|
||||
} else {
|
||||
Log.w(TAG, "Ignoring message ${message.type}: ${message.payload}")
|
||||
}
|
||||
}
|
||||
|
||||
/** Basic implementation to check if the application is in the foreground */
|
||||
|
||||
@@ -17,37 +17,38 @@ import androidx.annotation.RequiresApi
|
||||
import androidx.core.app.ActivityCompat
|
||||
import androidx.core.app.NotificationCompat
|
||||
import androidx.core.app.NotificationManagerCompat
|
||||
import breez_sdk_liquid_notification.Constants.DEFAULT_DISMISSIBLE_NOTIFICATION_CHANNEL_DESCRIPTION
|
||||
import breez_sdk_liquid_notification.Constants.DEFAULT_DISMISSIBLE_NOTIFICATION_CHANNEL_NAME
|
||||
import breez_sdk_liquid_notification.Constants.DEFAULT_DISMISSIBLE_WORKGROUP_DESCRIPTION
|
||||
import breez_sdk_liquid_notification.Constants.DEFAULT_DISMISSIBLE_WORKGROUP_NAME
|
||||
import breez_sdk_liquid_notification.Constants.DEFAULT_FOREGROUND_SERVICE_NOTIFICATION_CHANNEL_DESCRIPTION
|
||||
import breez_sdk_liquid_notification.Constants.DEFAULT_FOREGROUND_SERVICE_NOTIFICATION_CHANNEL_NAME
|
||||
import breez_sdk_liquid_notification.Constants.DEFAULT_FOREGROUND_SERVICE_NOTIFICATION_TITLE
|
||||
import breez_sdk_liquid_notification.Constants.DEFAULT_LNURL_PAY_NOTIFICATION_CHANNEL_DESCRIPTION
|
||||
import breez_sdk_liquid_notification.Constants.DEFAULT_LNURL_PAY_NOTIFICATION_CHANNEL_NAME
|
||||
import breez_sdk_liquid_notification.Constants.DEFAULT_LNURL_PAY_WORKGROUP_DESCRIPTION
|
||||
import breez_sdk_liquid_notification.Constants.DEFAULT_LNURL_PAY_WORKGROUP_NAME
|
||||
import breez_sdk_liquid_notification.Constants.DEFAULT_NOTIFICATION_COLOR
|
||||
import breez_sdk_liquid_notification.Constants.DEFAULT_SWAP_UPDATED_NOTIFICATION_CHANNEL_DESCRIPTION
|
||||
import breez_sdk_liquid_notification.Constants.DEFAULT_SWAP_UPDATED_NOTIFICATION_CHANNEL_NAME
|
||||
import breez_sdk_liquid_notification.Constants.DEFAULT_SWAP_UPDATED_WORKGROUP_DESCRIPTION
|
||||
import breez_sdk_liquid_notification.Constants.DEFAULT_SWAP_UPDATED_WORKGROUP_NAME
|
||||
import breez_sdk_liquid_notification.Constants.DEFAULT_REPLACEABLE_NOTIFICATION_CHANNEL_DESCRIPTION
|
||||
import breez_sdk_liquid_notification.Constants.DEFAULT_REPLACEABLE_NOTIFICATION_CHANNEL_NAME
|
||||
import breez_sdk_liquid_notification.Constants.DEFAULT_REPLACEABLE_WORKGROUP_DESCRIPTION
|
||||
import breez_sdk_liquid_notification.Constants.DEFAULT_REPLACEABLE_WORKGROUP_NAME
|
||||
import breez_sdk_liquid_notification.Constants.DISMISSIBLE_NOTIFICATION_CHANNEL_DESCRIPTION
|
||||
import breez_sdk_liquid_notification.Constants.DISMISSIBLE_NOTIFICATION_CHANNEL_NAME
|
||||
import breez_sdk_liquid_notification.Constants.DISMISSIBLE_WORKGROUP_DESCRIPTION
|
||||
import breez_sdk_liquid_notification.Constants.DISMISSIBLE_WORKGROUP_ID
|
||||
import breez_sdk_liquid_notification.Constants.DISMISSIBLE_WORKGROUP_NAME
|
||||
import breez_sdk_liquid_notification.Constants.FOREGROUND_SERVICE_NOTIFICATION_CHANNEL_DESCRIPTION
|
||||
import breez_sdk_liquid_notification.Constants.FOREGROUND_SERVICE_NOTIFICATION_CHANNEL_NAME
|
||||
import breez_sdk_liquid_notification.Constants.FOREGROUND_SERVICE_NOTIFICATION_TITLE
|
||||
import breez_sdk_liquid_notification.Constants.LNURL_PAY_NOTIFICATION_CHANNEL_DESCRIPTION
|
||||
import breez_sdk_liquid_notification.Constants.LNURL_PAY_NOTIFICATION_CHANNEL_NAME
|
||||
import breez_sdk_liquid_notification.Constants.LNURL_PAY_WORKGROUP_DESCRIPTION
|
||||
import breez_sdk_liquid_notification.Constants.LNURL_PAY_WORKGROUP_ID
|
||||
import breez_sdk_liquid_notification.Constants.LNURL_PAY_WORKGROUP_NAME
|
||||
import breez_sdk_liquid_notification.Constants.NOTIFICATION_CHANNEL_SWAP_UPDATED
|
||||
import breez_sdk_liquid_notification.Constants.NOTIFICATION_CHANNEL_DISMISSIBLE
|
||||
import breez_sdk_liquid_notification.Constants.NOTIFICATION_CHANNEL_FOREGROUND_SERVICE
|
||||
import breez_sdk_liquid_notification.Constants.NOTIFICATION_CHANNEL_LNURL_PAY
|
||||
import breez_sdk_liquid_notification.Constants.NOTIFICATION_CHANNEL_REPLACEABLE
|
||||
import breez_sdk_liquid_notification.Constants.NOTIFICATION_COLOR
|
||||
import breez_sdk_liquid_notification.Constants.NOTIFICATION_ICON
|
||||
import breez_sdk_liquid_notification.Constants.NOTIFICATION_ID_FOREGROUND_SERVICE
|
||||
import breez_sdk_liquid_notification.Constants.SWAP_UPDATED_NOTIFICATION_CHANNEL_DESCRIPTION
|
||||
import breez_sdk_liquid_notification.Constants.SWAP_UPDATED_NOTIFICATION_CHANNEL_NAME
|
||||
import breez_sdk_liquid_notification.Constants.SWAP_UPDATED_WORKGROUP_DESCRIPTION
|
||||
import breez_sdk_liquid_notification.Constants.SWAP_UPDATED_WORKGROUP_ID
|
||||
import breez_sdk_liquid_notification.Constants.SWAP_UPDATED_WORKGROUP_NAME
|
||||
import breez_sdk_liquid_notification.Constants.NOTIFICATION_ID_REPLACEABLE
|
||||
import breez_sdk_liquid_notification.Constants.REPLACEABLE_NOTIFICATION_CHANNEL_DESCRIPTION
|
||||
import breez_sdk_liquid_notification.Constants.REPLACEABLE_NOTIFICATION_CHANNEL_NAME
|
||||
import breez_sdk_liquid_notification.Constants.REPLACEABLE_WORKGROUP_DESCRIPTION
|
||||
import breez_sdk_liquid_notification.Constants.REPLACEABLE_WORKGROUP_ID
|
||||
import breez_sdk_liquid_notification.Constants.REPLACEABLE_WORKGROUP_NAME
|
||||
import breez_sdk_liquid_notification.ResourceHelper.Companion.getColor
|
||||
import breez_sdk_liquid_notification.ResourceHelper.Companion.getDrawable
|
||||
import breez_sdk_liquid_notification.ResourceHelper.Companion.getString
|
||||
@@ -82,7 +83,8 @@ class NotificationHelper {
|
||||
groupDescription: String,
|
||||
) {
|
||||
getNotificationManager(context)?.also { manager ->
|
||||
val channelGroup = NotificationChannelGroup(
|
||||
val channelGroup =
|
||||
NotificationChannelGroup(
|
||||
groupId,
|
||||
groupName,
|
||||
)
|
||||
@@ -106,10 +108,11 @@ class NotificationHelper {
|
||||
) {
|
||||
getNotificationManager(context)?.also { manager ->
|
||||
val applicationId = context.applicationContext.packageName
|
||||
val notificationChannel = NotificationChannel(
|
||||
"${applicationId}.${channelId}",
|
||||
val notificationChannel =
|
||||
NotificationChannel(
|
||||
"$applicationId.$channelId",
|
||||
channelName,
|
||||
importance
|
||||
importance,
|
||||
).apply {
|
||||
description = channelDescription
|
||||
group = groupId
|
||||
@@ -120,7 +123,10 @@ class NotificationHelper {
|
||||
}
|
||||
|
||||
@SuppressLint("NewApi")
|
||||
fun registerNotificationChannels(context: Context, defaultClickAction: String? = null) {
|
||||
fun registerNotificationChannels(
|
||||
context: Context,
|
||||
defaultClickAction: String? = null,
|
||||
) {
|
||||
this.defaultClickAction = defaultClickAction
|
||||
|
||||
getNotificationManager(context)?.also { manager ->
|
||||
@@ -136,59 +142,65 @@ class NotificationHelper {
|
||||
notificationManager: NotificationManager,
|
||||
) {
|
||||
val applicationId = context.applicationContext.packageName
|
||||
val foregroundServiceNotificationChannel = NotificationChannel(
|
||||
"${applicationId}.${NOTIFICATION_CHANNEL_FOREGROUND_SERVICE}",
|
||||
val foregroundServiceNotificationChannel =
|
||||
NotificationChannel(
|
||||
"$applicationId.${NOTIFICATION_CHANNEL_FOREGROUND_SERVICE}",
|
||||
getString(
|
||||
context,
|
||||
FOREGROUND_SERVICE_NOTIFICATION_CHANNEL_NAME,
|
||||
DEFAULT_FOREGROUND_SERVICE_NOTIFICATION_CHANNEL_NAME
|
||||
DEFAULT_FOREGROUND_SERVICE_NOTIFICATION_CHANNEL_NAME,
|
||||
),
|
||||
NotificationManager.IMPORTANCE_LOW
|
||||
NotificationManager.IMPORTANCE_LOW,
|
||||
).apply {
|
||||
description = getString(
|
||||
description =
|
||||
getString(
|
||||
context,
|
||||
FOREGROUND_SERVICE_NOTIFICATION_CHANNEL_DESCRIPTION,
|
||||
DEFAULT_FOREGROUND_SERVICE_NOTIFICATION_CHANNEL_DESCRIPTION
|
||||
DEFAULT_FOREGROUND_SERVICE_NOTIFICATION_CHANNEL_DESCRIPTION,
|
||||
)
|
||||
}
|
||||
val lnurlPayNotificationChannel = NotificationChannel(
|
||||
"${applicationId}.${NOTIFICATION_CHANNEL_LNURL_PAY}",
|
||||
val replaceableNotificationChannel =
|
||||
NotificationChannel(
|
||||
"$applicationId.${NOTIFICATION_CHANNEL_REPLACEABLE}",
|
||||
getString(
|
||||
context,
|
||||
LNURL_PAY_NOTIFICATION_CHANNEL_NAME,
|
||||
DEFAULT_LNURL_PAY_NOTIFICATION_CHANNEL_NAME
|
||||
DISMISSIBLE_NOTIFICATION_CHANNEL_NAME,
|
||||
DEFAULT_DISMISSIBLE_NOTIFICATION_CHANNEL_NAME,
|
||||
),
|
||||
NotificationManager.IMPORTANCE_DEFAULT
|
||||
NotificationManager.IMPORTANCE_DEFAULT,
|
||||
).apply {
|
||||
description = getString(
|
||||
description =
|
||||
getString(
|
||||
context,
|
||||
LNURL_PAY_NOTIFICATION_CHANNEL_DESCRIPTION,
|
||||
DEFAULT_LNURL_PAY_NOTIFICATION_CHANNEL_DESCRIPTION
|
||||
DISMISSIBLE_NOTIFICATION_CHANNEL_DESCRIPTION,
|
||||
DEFAULT_DISMISSIBLE_NOTIFICATION_CHANNEL_DESCRIPTION,
|
||||
)
|
||||
group = LNURL_PAY_WORKGROUP_ID
|
||||
group = REPLACEABLE_WORKGROUP_ID
|
||||
}
|
||||
val swapTxConfirmedNotificationChannel = NotificationChannel(
|
||||
"${applicationId}.${NOTIFICATION_CHANNEL_SWAP_UPDATED}",
|
||||
val dismissibleNotificationChannel =
|
||||
NotificationChannel(
|
||||
"$applicationId.${NOTIFICATION_CHANNEL_DISMISSIBLE}",
|
||||
getString(
|
||||
context,
|
||||
SWAP_UPDATED_NOTIFICATION_CHANNEL_NAME,
|
||||
DEFAULT_SWAP_UPDATED_NOTIFICATION_CHANNEL_NAME
|
||||
DISMISSIBLE_NOTIFICATION_CHANNEL_NAME,
|
||||
DEFAULT_DISMISSIBLE_NOTIFICATION_CHANNEL_NAME,
|
||||
),
|
||||
NotificationManager.IMPORTANCE_DEFAULT
|
||||
NotificationManager.IMPORTANCE_DEFAULT,
|
||||
).apply {
|
||||
description = getString(
|
||||
description =
|
||||
getString(
|
||||
context,
|
||||
SWAP_UPDATED_NOTIFICATION_CHANNEL_DESCRIPTION,
|
||||
DEFAULT_SWAP_UPDATED_NOTIFICATION_CHANNEL_DESCRIPTION
|
||||
DISMISSIBLE_NOTIFICATION_CHANNEL_DESCRIPTION,
|
||||
DEFAULT_DISMISSIBLE_NOTIFICATION_CHANNEL_DESCRIPTION,
|
||||
)
|
||||
group = SWAP_UPDATED_WORKGROUP_ID
|
||||
group = DISMISSIBLE_WORKGROUP_ID
|
||||
}
|
||||
notificationManager.createNotificationChannels(
|
||||
listOf(
|
||||
foregroundServiceNotificationChannel,
|
||||
lnurlPayNotificationChannel,
|
||||
swapTxConfirmedNotificationChannel
|
||||
)
|
||||
replaceableNotificationChannel,
|
||||
dismissibleNotificationChannel,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -197,40 +209,44 @@ class NotificationHelper {
|
||||
context: Context,
|
||||
notificationManager: NotificationManager,
|
||||
) {
|
||||
val lnurlPayNotificationChannelGroup = NotificationChannelGroup(
|
||||
LNURL_PAY_WORKGROUP_ID,
|
||||
val replaceableNotificationChannelGroup =
|
||||
NotificationChannelGroup(
|
||||
REPLACEABLE_WORKGROUP_ID,
|
||||
getString(
|
||||
context,
|
||||
LNURL_PAY_WORKGROUP_NAME,
|
||||
DEFAULT_LNURL_PAY_WORKGROUP_NAME
|
||||
REPLACEABLE_WORKGROUP_NAME,
|
||||
DEFAULT_REPLACEABLE_WORKGROUP_NAME,
|
||||
),
|
||||
)
|
||||
val swapTxConfirmedNotificationChannelGroup = NotificationChannelGroup(
|
||||
SWAP_UPDATED_WORKGROUP_ID,
|
||||
val dismissibleNotificationChannelGroup =
|
||||
NotificationChannelGroup(
|
||||
DISMISSIBLE_WORKGROUP_ID,
|
||||
getString(
|
||||
context,
|
||||
SWAP_UPDATED_WORKGROUP_NAME,
|
||||
DEFAULT_SWAP_UPDATED_WORKGROUP_NAME
|
||||
DISMISSIBLE_WORKGROUP_NAME,
|
||||
DEFAULT_DISMISSIBLE_WORKGROUP_NAME,
|
||||
),
|
||||
)
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
|
||||
lnurlPayNotificationChannelGroup.description = getString(
|
||||
replaceableNotificationChannelGroup.description =
|
||||
getString(
|
||||
context,
|
||||
LNURL_PAY_WORKGROUP_DESCRIPTION,
|
||||
DEFAULT_LNURL_PAY_WORKGROUP_DESCRIPTION
|
||||
REPLACEABLE_WORKGROUP_DESCRIPTION,
|
||||
DEFAULT_REPLACEABLE_WORKGROUP_DESCRIPTION,
|
||||
)
|
||||
swapTxConfirmedNotificationChannelGroup.description = getString(
|
||||
dismissibleNotificationChannelGroup.description =
|
||||
getString(
|
||||
context,
|
||||
SWAP_UPDATED_WORKGROUP_DESCRIPTION,
|
||||
DEFAULT_SWAP_UPDATED_WORKGROUP_DESCRIPTION
|
||||
DISMISSIBLE_WORKGROUP_DESCRIPTION,
|
||||
DEFAULT_DISMISSIBLE_WORKGROUP_DESCRIPTION,
|
||||
)
|
||||
}
|
||||
|
||||
notificationManager.createNotificationChannelGroups(
|
||||
listOf(
|
||||
lnurlPayNotificationChannelGroup,
|
||||
swapTxConfirmedNotificationChannelGroup
|
||||
)
|
||||
replaceableNotificationChannelGroup,
|
||||
dismissibleNotificationChannelGroup,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -240,43 +256,55 @@ class NotificationHelper {
|
||||
getColor(
|
||||
context,
|
||||
NOTIFICATION_COLOR,
|
||||
DEFAULT_NOTIFICATION_COLOR
|
||||
DEFAULT_NOTIFICATION_COLOR,
|
||||
)
|
||||
|
||||
return NotificationCompat.Builder(
|
||||
return NotificationCompat
|
||||
.Builder(
|
||||
context,
|
||||
"${context.applicationInfo.packageName}.$NOTIFICATION_CHANNEL_FOREGROUND_SERVICE"
|
||||
)
|
||||
.apply {
|
||||
"${context.applicationInfo.packageName}.$NOTIFICATION_CHANNEL_FOREGROUND_SERVICE",
|
||||
).apply {
|
||||
setContentTitle(
|
||||
getString(
|
||||
context,
|
||||
FOREGROUND_SERVICE_NOTIFICATION_TITLE,
|
||||
DEFAULT_FOREGROUND_SERVICE_NOTIFICATION_TITLE
|
||||
)
|
||||
DEFAULT_FOREGROUND_SERVICE_NOTIFICATION_TITLE,
|
||||
),
|
||||
)
|
||||
setSmallIcon(
|
||||
getDrawable(
|
||||
context,
|
||||
NOTIFICATION_ICON,
|
||||
android.R.drawable.sym_def_app_icon
|
||||
)
|
||||
android.R.drawable.sym_def_app_icon,
|
||||
),
|
||||
)
|
||||
setColorized(true)
|
||||
setOngoing(true)
|
||||
color = notificationColor
|
||||
}.build().also {
|
||||
}.build()
|
||||
.also {
|
||||
if (ActivityCompat.checkSelfPermission(
|
||||
context,
|
||||
Manifest.permission.POST_NOTIFICATIONS
|
||||
Manifest.permission.POST_NOTIFICATIONS,
|
||||
) == PackageManager.PERMISSION_GRANTED
|
||||
) {
|
||||
NotificationManagerCompat.from(context)
|
||||
NotificationManagerCompat
|
||||
.from(context)
|
||||
.notify(NOTIFICATION_ID_FOREGROUND_SERVICE, it)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("NewApi")
|
||||
fun cancelNotification(
|
||||
context: Context,
|
||||
notificationID: Int,
|
||||
) {
|
||||
getNotificationManager(context)?.also { manager ->
|
||||
manager.cancel(notificationID)
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("MissingPermission")
|
||||
fun notifyChannel(
|
||||
context: Context,
|
||||
@@ -285,12 +313,19 @@ class NotificationHelper {
|
||||
contentText: String? = null,
|
||||
clickAction: String? = defaultClickAction,
|
||||
): Notification {
|
||||
val notificationID: Int = System.currentTimeMillis().toInt() / 1000
|
||||
val notificationID: Int =
|
||||
if (channelId ==
|
||||
NOTIFICATION_CHANNEL_DISMISSIBLE
|
||||
) {
|
||||
System.currentTimeMillis().toInt() / 1000
|
||||
} else {
|
||||
NOTIFICATION_ID_REPLACEABLE
|
||||
}
|
||||
val notificationColor =
|
||||
getColor(
|
||||
context,
|
||||
NOTIFICATION_COLOR,
|
||||
DEFAULT_NOTIFICATION_COLOR
|
||||
DEFAULT_NOTIFICATION_COLOR,
|
||||
)
|
||||
|
||||
val notificationIntent =
|
||||
@@ -298,39 +333,52 @@ class NotificationHelper {
|
||||
notificationIntent.putExtra("click_action", clickAction)
|
||||
|
||||
val flags =
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE else PendingIntent.FLAG_UPDATE_CURRENT
|
||||
val approvePendingIntent = PendingIntent.getActivity(
|
||||
if (Build.VERSION.SDK_INT >=
|
||||
Build.VERSION_CODES.S
|
||||
) {
|
||||
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
|
||||
} else {
|
||||
PendingIntent.FLAG_UPDATE_CURRENT
|
||||
}
|
||||
val approvePendingIntent =
|
||||
PendingIntent.getActivity(
|
||||
context,
|
||||
0,
|
||||
notificationIntent,
|
||||
flags
|
||||
flags,
|
||||
)
|
||||
|
||||
val buttonTitle = "Open"
|
||||
val notificationAction = NotificationCompat.Action.Builder(
|
||||
val notificationAction =
|
||||
NotificationCompat.Action
|
||||
.Builder(
|
||||
android.R.drawable.ic_delete,
|
||||
buttonTitle,
|
||||
approvePendingIntent
|
||||
approvePendingIntent,
|
||||
).build()
|
||||
|
||||
val contentIntent = TaskStackBuilder.create(context).run {
|
||||
val contentIntent =
|
||||
TaskStackBuilder.create(context).run {
|
||||
addNextIntentWithParentStack(notificationIntent)
|
||||
approvePendingIntent
|
||||
}
|
||||
|
||||
return NotificationCompat.Builder(
|
||||
// Cancel any current replaceable notification
|
||||
cancelNotification(context, NOTIFICATION_ID_REPLACEABLE)
|
||||
|
||||
return NotificationCompat
|
||||
.Builder(
|
||||
context,
|
||||
"${context.applicationInfo.packageName}.${channelId}"
|
||||
)
|
||||
.apply {
|
||||
"${context.applicationInfo.packageName}.$channelId",
|
||||
).apply {
|
||||
setContentTitle(contentTitle)
|
||||
setContentText(contentText)
|
||||
setSmallIcon(
|
||||
getDrawable(
|
||||
context,
|
||||
NOTIFICATION_ICON,
|
||||
android.R.drawable.sym_def_app_icon
|
||||
)
|
||||
android.R.drawable.sym_def_app_icon,
|
||||
),
|
||||
)
|
||||
setContentIntent(contentIntent)
|
||||
addAction(notificationAction)
|
||||
@@ -338,10 +386,11 @@ class NotificationHelper {
|
||||
// Dismiss on click
|
||||
setOngoing(false)
|
||||
setAutoCancel(true)
|
||||
}.build().also {
|
||||
}.build()
|
||||
.also {
|
||||
if (ActivityCompat.checkSelfPermission(
|
||||
context,
|
||||
Manifest.permission.POST_NOTIFICATIONS
|
||||
Manifest.permission.POST_NOTIFICATIONS,
|
||||
) == PackageManager.PERMISSION_GRANTED
|
||||
) {
|
||||
// Required for notification to persist after work is complete
|
||||
@@ -349,11 +398,12 @@ class NotificationHelper {
|
||||
delay(200)
|
||||
if (ActivityCompat.checkSelfPermission(
|
||||
context,
|
||||
Manifest.permission.POST_NOTIFICATIONS
|
||||
Manifest.permission.POST_NOTIFICATIONS,
|
||||
) == PackageManager.PERMISSION_GRANTED
|
||||
) {
|
||||
// Use notificationID
|
||||
NotificationManagerCompat.from(context)
|
||||
NotificationManagerCompat
|
||||
.from(context)
|
||||
.notify(notificationID, it)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,38 +12,44 @@ import android.os.Bundle
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.content.res.ResourcesCompat
|
||||
|
||||
|
||||
class ResourceHelper {
|
||||
companion object {
|
||||
private const val ILLEGAL_RESOURCE_ID = 0
|
||||
|
||||
private fun getBundle(context: Context): Bundle? {
|
||||
return try {
|
||||
private fun getBundle(context: Context): Bundle? =
|
||||
try {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
||||
context.packageManager.getApplicationInfo(
|
||||
context.packageManager
|
||||
.getApplicationInfo(
|
||||
context.packageName,
|
||||
PackageManager.ApplicationInfoFlags.of(0)
|
||||
PackageManager.ApplicationInfoFlags.of(0),
|
||||
).metaData
|
||||
} else {
|
||||
@Suppress("DEPRECATION")
|
||||
context.packageManager
|
||||
.getApplicationInfo(
|
||||
context.packageName,
|
||||
PackageManager.GET_META_DATA
|
||||
PackageManager.GET_META_DATA,
|
||||
).metaData
|
||||
}
|
||||
} catch (_: NameNotFoundException) {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("DiscouragedApi")
|
||||
private fun getResourceId(context: Context, name: String, defType: String): Int? {
|
||||
return context.resources.getIdentifier(name, defType, context.packageName)
|
||||
private fun getResourceId(
|
||||
context: Context,
|
||||
name: String,
|
||||
defType: String,
|
||||
): Int? =
|
||||
context.resources
|
||||
.getIdentifier(name, defType, context.packageName)
|
||||
.takeIf { it != ILLEGAL_RESOURCE_ID }
|
||||
}
|
||||
|
||||
private fun isDrawableValid(context: Context, resourceId: Int): Boolean {
|
||||
private fun isDrawableValid(
|
||||
context: Context,
|
||||
resourceId: Int,
|
||||
): Boolean {
|
||||
if (Build.VERSION.SDK_INT != Build.VERSION_CODES.O) {
|
||||
return true
|
||||
}
|
||||
@@ -57,21 +63,29 @@ class ResourceHelper {
|
||||
}
|
||||
}
|
||||
|
||||
fun getColor(context: Context, name: String, fallback: String): Int {
|
||||
fun getColor(
|
||||
context: Context,
|
||||
name: String,
|
||||
fallback: String,
|
||||
): Int {
|
||||
val color =
|
||||
getResourceId(context, name, "color")?.let { ContextCompat.getColor(context, it) }
|
||||
?: run { getBundle(context)?.getInt(name, 0) }
|
||||
return color.takeUnless { it == 0 } ?: Color.parseColor(fallback)
|
||||
}
|
||||
|
||||
fun getDrawable(context: Context, name: String, fallback: Int): Int {
|
||||
fun getDrawable(
|
||||
context: Context,
|
||||
name: String,
|
||||
fallback: Int,
|
||||
): Int {
|
||||
val id =
|
||||
getResourceId(context, name, "drawable")?.takeIf { isDrawableValid(context, it) }
|
||||
?: run {
|
||||
getResourceId(context, name, "mipmap")?.takeIf {
|
||||
isDrawableValid(
|
||||
context,
|
||||
it
|
||||
it,
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -79,14 +93,20 @@ class ResourceHelper {
|
||||
return id ?: fallback
|
||||
}
|
||||
|
||||
fun getString(context: Context, name: String, fallback: String): String {
|
||||
return getString(context, name, null, fallback)
|
||||
}
|
||||
fun getString(
|
||||
context: Context,
|
||||
name: String,
|
||||
fallback: String,
|
||||
): String = getString(context, name, null, fallback)
|
||||
|
||||
fun getString(
|
||||
context: Context, name: String, validateContains: String?, fallback: String
|
||||
context: Context,
|
||||
name: String,
|
||||
validateContains: String?,
|
||||
fallback: String,
|
||||
): String {
|
||||
val str = getResourceId(context, name, "string")?.let { context.getString(it) } ?: run {
|
||||
val str =
|
||||
getResourceId(context, name, "string")?.let { context.getString(it) } ?: run {
|
||||
getBundle(context)?.getString(name, fallback) ?: run { fallback }
|
||||
}
|
||||
|
||||
|
||||
@@ -4,10 +4,16 @@ import android.util.Log
|
||||
import breez_sdk_liquid.LogEntry
|
||||
import breez_sdk_liquid.Logger
|
||||
|
||||
class ServiceLogger(private val logger: Logger?) {
|
||||
class ServiceLogger(
|
||||
private val logger: Logger?,
|
||||
) {
|
||||
constructor() : this(null)
|
||||
|
||||
fun log(tag: String, message: String, level: String) {
|
||||
fun log(
|
||||
tag: String,
|
||||
message: String,
|
||||
level: String,
|
||||
) {
|
||||
logger?.log(LogEntry(message, level)) ?: when (level) {
|
||||
"ERROR" -> Log.e(tag, message)
|
||||
"WARN" -> Log.w(tag, message)
|
||||
|
||||
@@ -55,4 +55,6 @@ interface LnurlPayJob : Job {
|
||||
}
|
||||
}
|
||||
|
||||
class InvalidLnurlPayException(message: String) : Exception(message)
|
||||
class InvalidLnurlPayException(
|
||||
message: String,
|
||||
) : Exception(message)
|
||||
|
||||
@@ -8,7 +8,7 @@ import breez_sdk_liquid_notification.Constants.DEFAULT_LNURL_PAY_NOTIFICATION_FA
|
||||
import breez_sdk_liquid_notification.Constants.LNURL_PAY_INFO_NOTIFICATION_TITLE
|
||||
import breez_sdk_liquid_notification.Constants.LNURL_PAY_METADATA_PLAIN_TEXT
|
||||
import breez_sdk_liquid_notification.Constants.LNURL_PAY_NOTIFICATION_FAILURE_TITLE
|
||||
import breez_sdk_liquid_notification.Constants.NOTIFICATION_CHANNEL_LNURL_PAY
|
||||
import breez_sdk_liquid_notification.Constants.NOTIFICATION_CHANNEL_REPLACEABLE
|
||||
import breez_sdk_liquid_notification.NotificationHelper.Companion.notifyChannel
|
||||
import breez_sdk_liquid_notification.ResourceHelper.Companion.getString
|
||||
import breez_sdk_liquid_notification.SdkForegroundService
|
||||
@@ -59,10 +59,14 @@ class LnurlPayInfoJob(
|
||||
throw InvalidLnurlPayException("Minimum sendable amount is invalid")
|
||||
}
|
||||
// Format the response
|
||||
val plainTextMetadata = getString(
|
||||
context, LNURL_PAY_METADATA_PLAIN_TEXT, DEFAULT_LNURL_PAY_METADATA_PLAIN_TEXT
|
||||
val plainTextMetadata =
|
||||
getString(
|
||||
context,
|
||||
LNURL_PAY_METADATA_PLAIN_TEXT,
|
||||
DEFAULT_LNURL_PAY_METADATA_PLAIN_TEXT,
|
||||
)
|
||||
val response = LnurlPayInfoResponse(
|
||||
val response =
|
||||
LnurlPayInfoResponse(
|
||||
request.callbackURL,
|
||||
maxSendableMsat,
|
||||
minSendableMsat,
|
||||
@@ -72,11 +76,11 @@ class LnurlPayInfoJob(
|
||||
val success = replyServer(Json.encodeToString(response), request.replyURL)
|
||||
notifyChannel(
|
||||
context,
|
||||
NOTIFICATION_CHANNEL_LNURL_PAY,
|
||||
NOTIFICATION_CHANNEL_REPLACEABLE,
|
||||
getString(
|
||||
context,
|
||||
if (success) LNURL_PAY_INFO_NOTIFICATION_TITLE else LNURL_PAY_NOTIFICATION_FAILURE_TITLE,
|
||||
if (success) DEFAULT_LNURL_PAY_INFO_NOTIFICATION_TITLE else DEFAULT_LNURL_PAY_NOTIFICATION_FAILURE_TITLE
|
||||
if (success) DEFAULT_LNURL_PAY_INFO_NOTIFICATION_TITLE else DEFAULT_LNURL_PAY_NOTIFICATION_FAILURE_TITLE,
|
||||
),
|
||||
)
|
||||
} catch (e: Exception) {
|
||||
@@ -86,11 +90,11 @@ class LnurlPayInfoJob(
|
||||
}
|
||||
notifyChannel(
|
||||
context,
|
||||
NOTIFICATION_CHANNEL_LNURL_PAY,
|
||||
NOTIFICATION_CHANNEL_REPLACEABLE,
|
||||
getString(
|
||||
context,
|
||||
LNURL_PAY_NOTIFICATION_FAILURE_TITLE,
|
||||
DEFAULT_LNURL_PAY_NOTIFICATION_FAILURE_TITLE
|
||||
DEFAULT_LNURL_PAY_NOTIFICATION_FAILURE_TITLE,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ import breez_sdk_liquid_notification.Constants.DEFAULT_LNURL_PAY_NOTIFICATION_FA
|
||||
import breez_sdk_liquid_notification.Constants.LNURL_PAY_INVOICE_NOTIFICATION_TITLE
|
||||
import breez_sdk_liquid_notification.Constants.LNURL_PAY_METADATA_PLAIN_TEXT
|
||||
import breez_sdk_liquid_notification.Constants.LNURL_PAY_NOTIFICATION_FAILURE_TITLE
|
||||
import breez_sdk_liquid_notification.Constants.NOTIFICATION_CHANNEL_LNURL_PAY
|
||||
import breez_sdk_liquid_notification.Constants.NOTIFICATION_CHANNEL_REPLACEABLE
|
||||
import breez_sdk_liquid_notification.NotificationHelper.Companion.notifyChannel
|
||||
import breez_sdk_liquid_notification.ResourceHelper.Companion.getString
|
||||
import breez_sdk_liquid_notification.SdkForegroundService
|
||||
@@ -57,20 +57,23 @@ class LnurlPayInvoiceJob(
|
||||
if (amountSat < limits.receive.minSat || amountSat > limits.receive.maxSat) {
|
||||
throw InvalidLnurlPayException("Invalid amount requested ${request.amount}")
|
||||
}
|
||||
val plainTextMetadata = getString(
|
||||
val plainTextMetadata =
|
||||
getString(
|
||||
context,
|
||||
LNURL_PAY_METADATA_PLAIN_TEXT,
|
||||
DEFAULT_LNURL_PAY_METADATA_PLAIN_TEXT
|
||||
DEFAULT_LNURL_PAY_METADATA_PLAIN_TEXT,
|
||||
)
|
||||
val prepareReceivePaymentRes = liquidSDK.prepareReceivePayment(
|
||||
PrepareReceiveRequest(PaymentMethod.LIGHTNING, ReceiveAmount.Bitcoin(amountSat))
|
||||
val prepareReceivePaymentRes =
|
||||
liquidSDK.prepareReceivePayment(
|
||||
PrepareReceiveRequest(PaymentMethod.LIGHTNING, ReceiveAmount.Bitcoin(amountSat)),
|
||||
)
|
||||
val receivePaymentResponse = liquidSDK.receivePayment(
|
||||
val receivePaymentResponse =
|
||||
liquidSDK.receivePayment(
|
||||
ReceivePaymentRequest(
|
||||
prepareReceivePaymentRes,
|
||||
description = "[[\"text/plain\",\"$plainTextMetadata\"]]",
|
||||
useDescriptionHash = true
|
||||
)
|
||||
useDescriptionHash = true,
|
||||
),
|
||||
)
|
||||
val response =
|
||||
LnurlPayInvoiceResponse(
|
||||
@@ -80,11 +83,11 @@ class LnurlPayInvoiceJob(
|
||||
val success = replyServer(Json.encodeToString(response), request.replyURL)
|
||||
notifyChannel(
|
||||
context,
|
||||
NOTIFICATION_CHANNEL_LNURL_PAY,
|
||||
NOTIFICATION_CHANNEL_REPLACEABLE,
|
||||
getString(
|
||||
context,
|
||||
if (success) LNURL_PAY_INVOICE_NOTIFICATION_TITLE else LNURL_PAY_NOTIFICATION_FAILURE_TITLE,
|
||||
if (success) DEFAULT_LNURL_PAY_INVOICE_NOTIFICATION_TITLE else DEFAULT_LNURL_PAY_NOTIFICATION_FAILURE_TITLE
|
||||
if (success) DEFAULT_LNURL_PAY_INVOICE_NOTIFICATION_TITLE else DEFAULT_LNURL_PAY_NOTIFICATION_FAILURE_TITLE,
|
||||
),
|
||||
)
|
||||
} catch (e: Exception) {
|
||||
@@ -94,11 +97,11 @@ class LnurlPayInvoiceJob(
|
||||
}
|
||||
notifyChannel(
|
||||
context,
|
||||
NOTIFICATION_CHANNEL_LNURL_PAY,
|
||||
NOTIFICATION_CHANNEL_REPLACEABLE,
|
||||
getString(
|
||||
context,
|
||||
LNURL_PAY_NOTIFICATION_FAILURE_TITLE,
|
||||
DEFAULT_LNURL_PAY_NOTIFICATION_FAILURE_TITLE
|
||||
DEFAULT_LNURL_PAY_NOTIFICATION_FAILURE_TITLE,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ import breez_sdk_liquid_notification.Constants.DEFAULT_PAYMENT_WAITING_FEE_ACCEP
|
||||
import breez_sdk_liquid_notification.Constants.DEFAULT_PAYMENT_WAITING_FEE_ACCEPTANCE_TITLE
|
||||
import breez_sdk_liquid_notification.Constants.DEFAULT_SWAP_CONFIRMED_NOTIFICATION_FAILURE_TEXT
|
||||
import breez_sdk_liquid_notification.Constants.DEFAULT_SWAP_CONFIRMED_NOTIFICATION_FAILURE_TITLE
|
||||
import breez_sdk_liquid_notification.Constants.NOTIFICATION_CHANNEL_SWAP_UPDATED
|
||||
import breez_sdk_liquid_notification.Constants.NOTIFICATION_CHANNEL_DISMISSIBLE
|
||||
import breez_sdk_liquid_notification.Constants.PAYMENT_RECEIVED_NOTIFICATION_TEXT
|
||||
import breez_sdk_liquid_notification.Constants.PAYMENT_RECEIVED_NOTIFICATION_TITLE
|
||||
import breez_sdk_liquid_notification.Constants.PAYMENT_SENT_NOTIFICATION_TEXT
|
||||
@@ -127,7 +127,7 @@ class SwapUpdatedJob(
|
||||
is SdkEvent.PaymentWaitingFeeAcceptance -> handlePaymentWaitingFeeAcceptance(e.details)
|
||||
|
||||
else -> {
|
||||
logger.log(TAG, "Received event: ${e}", "TRACE")
|
||||
logger.log(TAG, "Received event: $e", "TRACE")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -137,18 +137,18 @@ class SwapUpdatedJob(
|
||||
}
|
||||
|
||||
private fun hashId(id: String): String =
|
||||
MessageDigest.getInstance("SHA-256")
|
||||
MessageDigest
|
||||
.getInstance("SHA-256")
|
||||
.digest(id.toByteArray())
|
||||
.fold(StringBuilder()) { sb, it -> sb.append("%02x".format(it)) }
|
||||
.toString()
|
||||
|
||||
private fun getSwapId(details: PaymentDetails?): String? {
|
||||
return when (details) {
|
||||
private fun getSwapId(details: PaymentDetails?): String? =
|
||||
when (details) {
|
||||
is PaymentDetails.Bitcoin -> details.swapId
|
||||
is PaymentDetails.Lightning -> details.swapId
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
private fun paymentClaimIsBroadcasted(details: PaymentDetails?): Boolean {
|
||||
return when (details) {
|
||||
@@ -166,7 +166,7 @@ class SwapUpdatedJob(
|
||||
logger.log(
|
||||
TAG,
|
||||
"Received payment event: ${this.swapIdHash} ${payment.status}",
|
||||
"TRACE"
|
||||
"TRACE",
|
||||
)
|
||||
notifySuccess(payment)
|
||||
}
|
||||
@@ -181,7 +181,7 @@ class SwapUpdatedJob(
|
||||
logger.log(
|
||||
TAG,
|
||||
"Payment waiting fee acceptance: ${this.swapIdHash}",
|
||||
"TRACE"
|
||||
"TRACE",
|
||||
)
|
||||
notifyPaymentWaitingFeeAcceptance(payment)
|
||||
}
|
||||
@@ -194,20 +194,21 @@ class SwapUpdatedJob(
|
||||
val received = payment.paymentType == PaymentType.RECEIVE
|
||||
notifyChannel(
|
||||
context,
|
||||
NOTIFICATION_CHANNEL_SWAP_UPDATED,
|
||||
NOTIFICATION_CHANNEL_DISMISSIBLE,
|
||||
getString(
|
||||
context,
|
||||
if (received) PAYMENT_RECEIVED_NOTIFICATION_TITLE else PAYMENT_SENT_NOTIFICATION_TITLE,
|
||||
if (received) DEFAULT_PAYMENT_RECEIVED_NOTIFICATION_TITLE else DEFAULT_PAYMENT_SENT_NOTIFICATION_TITLE
|
||||
if (received) DEFAULT_PAYMENT_RECEIVED_NOTIFICATION_TITLE else DEFAULT_PAYMENT_SENT_NOTIFICATION_TITLE,
|
||||
),
|
||||
String.format(
|
||||
getString(
|
||||
context,
|
||||
if (received) PAYMENT_RECEIVED_NOTIFICATION_TEXT else PAYMENT_SENT_NOTIFICATION_TEXT,
|
||||
"%d",
|
||||
if (received) DEFAULT_PAYMENT_RECEIVED_NOTIFICATION_TEXT else DEFAULT_PAYMENT_SENT_NOTIFICATION_TEXT
|
||||
), payment.amountSat.toLong()
|
||||
)
|
||||
if (received) DEFAULT_PAYMENT_RECEIVED_NOTIFICATION_TEXT else DEFAULT_PAYMENT_SENT_NOTIFICATION_TEXT,
|
||||
),
|
||||
payment.amountSat.toLong(),
|
||||
),
|
||||
)
|
||||
this.notified = true
|
||||
fgService.onFinished(this)
|
||||
@@ -219,17 +220,17 @@ class SwapUpdatedJob(
|
||||
logger.log(TAG, "Payment with swap ID ${getSwapId(payment.details)} requires fee acceptance", "INFO")
|
||||
notifyChannel(
|
||||
context,
|
||||
NOTIFICATION_CHANNEL_SWAP_UPDATED,
|
||||
NOTIFICATION_CHANNEL_DISMISSIBLE,
|
||||
getString(
|
||||
context,
|
||||
PAYMENT_WAITING_FEE_ACCEPTANCE_TITLE,
|
||||
DEFAULT_PAYMENT_WAITING_FEE_ACCEPTANCE_TITLE
|
||||
DEFAULT_PAYMENT_WAITING_FEE_ACCEPTANCE_TITLE,
|
||||
),
|
||||
getString(
|
||||
context,
|
||||
PAYMENT_WAITING_FEE_ACCEPTANCE_TEXT,
|
||||
DEFAULT_PAYMENT_WAITING_FEE_ACCEPTANCE_TEXT
|
||||
)
|
||||
DEFAULT_PAYMENT_WAITING_FEE_ACCEPTANCE_TEXT,
|
||||
),
|
||||
)
|
||||
this.notified = true
|
||||
fgService.onFinished(this)
|
||||
@@ -241,16 +242,16 @@ class SwapUpdatedJob(
|
||||
logger.log(TAG, "Swap $swapIdHash processing failed", "INFO")
|
||||
notifyChannel(
|
||||
context,
|
||||
NOTIFICATION_CHANNEL_SWAP_UPDATED,
|
||||
NOTIFICATION_CHANNEL_DISMISSIBLE,
|
||||
getString(
|
||||
context,
|
||||
SWAP_CONFIRMED_NOTIFICATION_FAILURE_TITLE,
|
||||
DEFAULT_SWAP_CONFIRMED_NOTIFICATION_FAILURE_TITLE
|
||||
DEFAULT_SWAP_CONFIRMED_NOTIFICATION_FAILURE_TITLE,
|
||||
),
|
||||
getString(
|
||||
context,
|
||||
SWAP_CONFIRMED_NOTIFICATION_FAILURE_TEXT,
|
||||
DEFAULT_SWAP_CONFIRMED_NOTIFICATION_FAILURE_TEXT
|
||||
DEFAULT_SWAP_CONFIRMED_NOTIFICATION_FAILURE_TEXT,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2,8 +2,8 @@ import Foundation
|
||||
|
||||
struct Constants {
|
||||
// Notification Threads
|
||||
static let NOTIFICATION_THREAD_LNURL_PAY = "LNURL_PAY"
|
||||
static let NOTIFICATION_THREAD_SWAP_UPDATED = "SWAP_UPDATED"
|
||||
static let NOTIFICATION_THREAD_DISMISSIBLE = "DISMISSIBLE"
|
||||
static let NOTIFICATION_THREAD_REPLACEABLE = "REPLACEABLE"
|
||||
|
||||
// Message Data
|
||||
static let MESSAGE_DATA_TYPE = "notification_type"
|
||||
|
||||
@@ -33,12 +33,12 @@ class LnurlPayTask : TaskProtocol {
|
||||
public func onEvent(e: SdkEvent) {}
|
||||
|
||||
func onShutdown() {
|
||||
displayPushNotification(title: self.failNotificationTitle, logger: self.logger, threadIdentifier: Constants.NOTIFICATION_THREAD_LNURL_PAY)
|
||||
displayPushNotification(title: self.failNotificationTitle, logger: self.logger, threadIdentifier: Constants.NOTIFICATION_THREAD_REPLACEABLE)
|
||||
}
|
||||
|
||||
func replyServer(encodable: Encodable, replyURL: String) {
|
||||
guard let serverReplyURL = URL(string: replyURL) else {
|
||||
self.displayPushNotification(title: self.failNotificationTitle, logger: self.logger, threadIdentifier: Constants.NOTIFICATION_THREAD_LNURL_PAY)
|
||||
self.displayPushNotification(title: self.failNotificationTitle, logger: self.logger, threadIdentifier: Constants.NOTIFICATION_THREAD_REPLACEABLE)
|
||||
return
|
||||
}
|
||||
var request = URLRequest(url: serverReplyURL)
|
||||
@@ -48,9 +48,9 @@ class LnurlPayTask : TaskProtocol {
|
||||
let statusCode = (response as! HTTPURLResponse).statusCode
|
||||
|
||||
if statusCode == 200 {
|
||||
self.displayPushNotification(title: self.successNotificationTitle, logger: self.logger, threadIdentifier: Constants.NOTIFICATION_THREAD_LNURL_PAY)
|
||||
self.displayPushNotification(title: self.successNotificationTitle, logger: self.logger, threadIdentifier: Constants.NOTIFICATION_THREAD_REPLACEABLE)
|
||||
} else {
|
||||
self.displayPushNotification(title: self.failNotificationTitle, logger: self.logger, threadIdentifier: Constants.NOTIFICATION_THREAD_LNURL_PAY)
|
||||
self.displayPushNotification(title: self.failNotificationTitle, logger: self.logger, threadIdentifier: Constants.NOTIFICATION_THREAD_REPLACEABLE)
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -68,7 +68,7 @@ class LnurlPayTask : TaskProtocol {
|
||||
task.resume()
|
||||
}
|
||||
let title = failNotificationTitle != nil ? failNotificationTitle! : self.failNotificationTitle
|
||||
self.displayPushNotification(title: title, logger: self.logger, threadIdentifier: Constants.NOTIFICATION_THREAD_LNURL_PAY)
|
||||
self.displayPushNotification(title: title, logger: self.logger, threadIdentifier: Constants.NOTIFICATION_THREAD_REPLACEABLE)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -37,7 +37,7 @@ class LnurlPayInfoTask : LnurlPayTask {
|
||||
request = try JSONDecoder().decode(LnurlInfoRequest.self, from: self.payload.data(using: .utf8)!)
|
||||
} catch let e {
|
||||
self.logger.log(tag: TAG, line: "failed to decode payload: \(e)", level: "ERROR")
|
||||
self.displayPushNotification(title: self.failNotificationTitle, logger: self.logger, threadIdentifier: Constants.NOTIFICATION_THREAD_LNURL_PAY)
|
||||
self.displayPushNotification(title: self.failNotificationTitle, logger: self.logger, threadIdentifier: Constants.NOTIFICATION_THREAD_REPLACEABLE)
|
||||
throw e
|
||||
}
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@ class LnurlPayInvoiceTask : LnurlPayTask {
|
||||
request = try JSONDecoder().decode(LnurlInvoiceRequest.self, from: self.payload.data(using: .utf8)!)
|
||||
} catch let e {
|
||||
self.logger.log(tag: TAG, line: "failed to decode payload: \(e)", level: "ERROR")
|
||||
self.displayPushNotification(title: self.failNotificationTitle, logger: self.logger, threadIdentifier: Constants.NOTIFICATION_THREAD_LNURL_PAY)
|
||||
self.displayPushNotification(title: self.failNotificationTitle, logger: self.logger, threadIdentifier: Constants.NOTIFICATION_THREAD_REPLACEABLE)
|
||||
throw e
|
||||
}
|
||||
|
||||
|
||||
@@ -133,7 +133,7 @@ class SwapUpdatedTask : TaskProtocol {
|
||||
func onShutdown() {
|
||||
let notificationTitle = ResourceHelper.shared.getString(key: Constants.SWAP_CONFIRMED_NOTIFICATION_FAILURE_TITLE, fallback: Constants.DEFAULT_SWAP_CONFIRMED_NOTIFICATION_FAILURE_TITLE)
|
||||
let notificationBody = ResourceHelper.shared.getString(key: Constants.SWAP_CONFIRMED_NOTIFICATION_FAILURE_TEXT, fallback: Constants.DEFAULT_SWAP_CONFIRMED_NOTIFICATION_FAILURE_TEXT)
|
||||
self.displayPushNotification(title: notificationTitle, body: notificationBody, logger: self.logger, threadIdentifier: Constants.NOTIFICATION_THREAD_SWAP_UPDATED)
|
||||
self.displayPushNotification(title: notificationTitle, body: notificationBody, logger: self.logger, threadIdentifier: Constants.NOTIFICATION_THREAD_DISMISSIBLE)
|
||||
}
|
||||
|
||||
func notifySuccess(payment: Payment) {
|
||||
@@ -145,7 +145,7 @@ class SwapUpdatedTask : TaskProtocol {
|
||||
validateContains: "%d",
|
||||
fallback: received ? Constants.DEFAULT_PAYMENT_RECEIVED_NOTIFICATION_TITLE: Constants.DEFAULT_PAYMENT_SENT_NOTIFICATION_TITLE)
|
||||
self.notified = true
|
||||
self.displayPushNotification(title: String(format: notificationTitle, payment.amountSat), logger: self.logger, threadIdentifier: Constants.NOTIFICATION_THREAD_SWAP_UPDATED)
|
||||
self.displayPushNotification(title: String(format: notificationTitle, payment.amountSat), logger: self.logger, threadIdentifier: Constants.NOTIFICATION_THREAD_DISMISSIBLE)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -159,7 +159,7 @@ class SwapUpdatedTask : TaskProtocol {
|
||||
key: Constants.PAYMENT_WAITING_FEE_ACCEPTANCE_TEXT,
|
||||
fallback: Constants.DEFAULT_PAYMENT_WAITING_FEE_ACCEPTANCE_TEXT)
|
||||
self.notified = true
|
||||
self.displayPushNotification(title: notificationTitle, body: notificationBody, logger: self.logger, threadIdentifier: Constants.NOTIFICATION_THREAD_SWAP_UPDATED)
|
||||
self.displayPushNotification(title: notificationTitle, body: notificationBody, logger: self.logger, threadIdentifier: Constants.NOTIFICATION_THREAD_DISMISSIBLE)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,28 @@ public protocol TaskProtocol : EventListener {
|
||||
}
|
||||
|
||||
extension TaskProtocol {
|
||||
func removePushNotifications(threadIdentifier: String, logger: ServiceLogger) {
|
||||
let semaphore = DispatchSemaphore(value: 0)
|
||||
let notificationCenter = UNUserNotificationCenter.current()
|
||||
|
||||
notificationCenter.getDeliveredNotifications(completionHandler: { notifications in
|
||||
defer {
|
||||
semaphore.signal()
|
||||
}
|
||||
|
||||
let removableNotifications = notifications.filter({ $0.request.content.threadIdentifier == threadIdentifier })
|
||||
guard !removableNotifications.isEmpty else {
|
||||
return
|
||||
}
|
||||
// The call to removeDeliveredNotifications() is async in a background thread and
|
||||
// needs to be complete before calling contentHandler()
|
||||
notificationCenter.removeDeliveredNotifications(withIdentifiers: removableNotifications.map({ $0.request.identifier }))
|
||||
logger.log(tag: "TaskProtocol", line:"removePushNotifications: \(removableNotifications.count)", level: "INFO")
|
||||
})
|
||||
|
||||
semaphore.wait()
|
||||
}
|
||||
|
||||
func displayPushNotification(title: String, body: String? = nil, logger: ServiceLogger, threadIdentifier: String? = nil) {
|
||||
logger.log(tag: "TaskProtocol", line:"displayPushNotification \(title)", level: "INFO")
|
||||
guard
|
||||
@@ -19,6 +41,8 @@ extension TaskProtocol {
|
||||
return
|
||||
}
|
||||
|
||||
removePushNotifications(threadIdentifier: Constants.NOTIFICATION_THREAD_REPLACEABLE, logger: logger)
|
||||
|
||||
if let body = body {
|
||||
bestAttemptContent.body = body
|
||||
}
|
||||
@@ -28,6 +52,10 @@ extension TaskProtocol {
|
||||
}
|
||||
|
||||
bestAttemptContent.title = title
|
||||
// The call to contentHandler() needs to be done with a slight delay otherwise
|
||||
// it will be killed before its finished removing the notifications
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
|
||||
contentHandler(bestAttemptContent)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user