Skip to content

Commit

Permalink
feat: notification color on Android, returning the scheduled id, more…
Browse files Browse the repository at this point in the history
… null checks
  • Loading branch information
cristiangu committed Apr 27, 2024
1 parent 0d6ef2b commit 2d79255
Show file tree
Hide file tree
Showing 9 changed files with 93 additions and 51 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -16,31 +16,34 @@ class LocalNotificationsModule internal constructor(val context: ReactApplicatio

@ReactMethod
override fun scheduleNotification(
notification: ReadableMap?,
trigger: ReadableMap?,
notification: ReadableMap,
trigger: ReadableMap,
promise: Promise?
) {
if(notification == null || trigger == null) {
val title = notification.getString("title")
if(title == null) {
promise?.reject("Error", "Title prop is missing.")
return
}
val title = notification.getString("title") ?: return
val body = notification.getString("body")
val data = notification.getMap("data")
val scheduleId = notification.getString("id")?: return
val scheduleId = notification.getString("id")
val androidParamsMap = notification.getMap("android")
val smallIconResName = androidParamsMap?.getString("smallIcon")
val colorHex = androidParamsMap?.getString("color")
val dateInMillis = trigger.getDouble("timestamp").toLong()

NotificationScheduler.scheduleNotification(
val scheduledId = NotificationScheduler.scheduleNotification(
context,
title,
body,
colorHex,
data,
smallIconResName,
scheduleId,
dateInMillis
)
promise?.resolve(null)
promise?.resolve(scheduledId)
}

@ReactMethod
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import android.app.PendingIntent
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.graphics.Color
import android.util.Log
import androidx.core.app.NotificationCompat
import com.localnotifications.util.IntentUtil
import com.localnotifications.util.JsonUtil
Expand All @@ -16,12 +18,13 @@ const val EXTRA_SCHEDULE_ID = "com.localnotifications.EXTRA_SCHEDULE_ID"
const val EXTRA_TITLE = "com.localnotifications.EXTRA_TITLE"
const val EXTRA_MESSAGE = "com.localnotifications.EXTRA_MESSAGE"
const val EXTRA_DATA = "com.localnotifications.EXTRA_DATA"
const val EXTRA_COLOR = "com.localnotifications.EXTRA_COLOR"
const val EXTRA_SMALL_ICON_RES_ID = "com.localnotifications.EXTRA_SMALL_ICON_RES_ID"


// BroadcastReceiver for handling notifications
class NotificationReceiver : BroadcastReceiver() {

private val TAG = "NotificationReceiver"
override fun onReceive(context: Context, intent: Intent) {
val id = intent.getIntExtra(EXTRA_SCHEDULE_ID, -1)

Expand All @@ -35,18 +38,27 @@ class NotificationReceiver : BroadcastReceiver() {
receiverIntent,
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_MUTABLE
)
val notification = NotificationCompat.Builder(context, channelID)

try {
val colorHex = intent.getStringExtra(EXTRA_COLOR)
if(colorHex != null) {
notification.setColor(Color.parseColor(colorHex))
}
} catch (e: Exception) {
Log.e(TAG, "Failed to parse the HEX color string.", e)
}

val jsonObject = JSONObject(intent.getStringExtra(EXTRA_DATA) ?: "{}")
val notification = NotificationCompat.Builder(context, channelID)
notification
.setSmallIcon(intent.getIntExtra(EXTRA_SMALL_ICON_RES_ID, 0))
.setContentTitle(intent.getStringExtra(EXTRA_TITLE)) // Set title from intent
.setContentText(intent.getStringExtra(EXTRA_MESSAGE)) // Set content text from intent
.setContentTitle(intent.getStringExtra(EXTRA_TITLE))
.setContentText(intent.getStringExtra(EXTRA_MESSAGE))
.setExtras(JsonUtil.convertJsonToBundle(jsonObject))
.setContentIntent(pendingIntent)
.setAutoCancel(true)
.build()

val manager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
manager.notify(id, notification)
manager.notify(id, notification.build())
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,37 +14,46 @@ import com.localnotifications.util.MapUtil
import com.localnotifications.util.ResourceUtil
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.runBlocking
import java.util.UUID


object NotificationScheduler {

@SuppressLint("ScheduleExactAlarm")
public fun scheduleNotification(
fun scheduleNotification(
context: ReactApplicationContext,
title: String,
body: String?,
colorHex: String?,
data: ReadableMap?,
smallIconResName: String?,
scheduleId: String,
scheduleId: String?,
triggerDateInMillis: Long
) {
): String {
createNotificationChannel(context)

// Create an intent for the Notification BroadcastReceiver
val intent = getNotificationIntent(context)
intent.putExtra(EXTRA_TITLE, title)
intent.putExtra(EXTRA_MESSAGE, body)
intent.putExtra(EXTRA_SCHEDULE_ID, scheduleId.hashCode())
intent.putExtra(EXTRA_DATA, MapUtil.toJSONObject(data).toString())
val safeScheduleId = scheduleId ?: UUID.randomUUID().toString()
intent.putExtra(EXTRA_SCHEDULE_ID, safeScheduleId.hashCode())
if(data != null) {
intent.putExtra(EXTRA_DATA, MapUtil.toJSONObject(data).toString())
}

val safeSmallIconResName = ResourceUtil.getImageResourceId(
smallIconResName ?: "ic_launcher",
context
)
intent.putExtra(EXTRA_SMALL_ICON_RES_ID, safeSmallIconResName)

if(colorHex != null) {
intent.putExtra(EXTRA_COLOR, colorHex)
}

// Create a PendingIntent for the broadcast
val pendingIntent = getBroadcastPendingIntent(context, scheduleId, intent)
val pendingIntent = getBroadcastPendingIntent(context, safeScheduleId, intent)
// Get the AlarmManager service
val alarmManager = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager

Expand All @@ -63,9 +72,10 @@ object NotificationScheduler {
}

runBlocking(Dispatchers.IO) {
LocalStorage.addScheduleIds(arrayOf(scheduleId), context)
LocalStorage.addScheduleIds(arrayOf(safeScheduleId), context)
}

return safeScheduleId
}

private fun createNotificationChannel(context: Context) {
Expand Down
4 changes: 2 additions & 2 deletions android/src/oldarch/LocalNotificationsSpec.kt
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ abstract class LocalNotificationsSpec internal constructor(context: ReactApplica
ReactContextBaseJavaModule(context) {

abstract fun scheduleNotification(
notification: ReadableMap?,
trigger: ReadableMap?,
notification: ReadableMap,
trigger: ReadableMap,
promise: Promise?
)

Expand Down
4 changes: 2 additions & 2 deletions example/ios/Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -944,7 +944,7 @@ PODS:
- React-Mapbuffer (0.73.6):
- glog
- React-debug
- react-native-local-notifications (0.2.0):
- react-native-local-notifications (0.3.0):
- glog
- RCT-Folly (= 2022.05.16.00)
- React-Core
Expand Down Expand Up @@ -1351,7 +1351,7 @@ SPEC CHECKSUMS:
React-jsinspector: 85583ef014ce53d731a98c66a0e24496f7a83066
React-logger: 3eb80a977f0d9669468ef641a5e1fabbc50a09ec
React-Mapbuffer: 84ea43c6c6232049135b1550b8c60b2faac19fab
react-native-local-notifications: e59a16b137462d558332fa928b2374ad845ee466
react-native-local-notifications: 8a2a81edea8c3d4cddc1874eaeae1e359da913a7
React-nativeconfig: b4d4e9901d4cabb57be63053fd2aa6086eb3c85f
React-NativeModulesApple: cd26e56d56350e123da0c1e3e4c76cb58a05e1ee
React-perflogger: 5f49905de275bac07ac7ea7f575a70611fa988f2
Expand Down
8 changes: 3 additions & 5 deletions example/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,22 +9,20 @@ import {

export default function App() {
const onPress = useCallback(async () => {
await scheduleNotification(
const id = await scheduleNotification(
{
id: 'my_id',
title: 'Title',
body: 'New',
data: {
url: 'https://example.com',
},
android: {
smallIcon: 'ic_launcher',
color: '#0000ff',
},
},
{
timestamp: Date.now() + 5000,
}
);
console.log('Scheduled notification with id:', id);
}, []);

const cancelById = useCallback(async () => {
Expand Down
47 changes: 32 additions & 15 deletions ios/LocalNotifications.mm
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@
@implementation LocalNotifications
RCT_EXPORT_MODULE()


NSString *TAG = @"[react-native-local-notifications]";

#ifdef RCT_NEW_ARCH_ENABLED
RCT_EXPORT_METHOD(scheduleNotification:
(JS::NativeLocalNotifications::Notification &)notification
Expand All @@ -13,11 +16,11 @@ @implementation LocalNotifications
{

[NotificationScheduler
scheduleNotificationWithTitle: notification.title()
body: notification.body()
data: (NSMutableDictionary * _Nullable) notification.data()
scheduleId: notification.id_()
triggerDate: [NSDate dateWithTimeIntervalSince1970: trigger.timestamp() / 1000]
scheduleNotificationWithTitle: notification.title()
body: notification.body()
data: (NSMutableDictionary * _Nullable) notification.data()
scheduleId: notification.id_()
triggerDate: [NSDate dateWithTimeIntervalSince1970: trigger.timestamp() / 1000]
];
resolve(NULL);
}
Expand All @@ -30,16 +33,30 @@ @implementation LocalNotifications
reject: (RCTPromiseRejectBlock) reject)
{

[NotificationScheduler
scheduleNotificationWithTitle: [notification objectForKey:@"title"]
body: [notification objectForKey:@"body"]
data: (NSMutableDictionary * _Nullable) [notification objectForKey:@"data"]
scheduleId: [notification valueForKey:@"id"]
triggerDate: [NSDate dateWithTimeIntervalSince1970:
[[trigger valueForKey:@"timestamp"] longValue] / 1000
]

if(notification == NULL || trigger == NULL) {
NSString *message = [NSString stringWithFormat:@"%@ Missing notification or trigger config.", TAG];
reject(@"error", message, NULL);
return;
}

NSString *title = [notification objectForKey:@"title"];
if(title == NULL) {
NSString *message = [NSString stringWithFormat:@"%@ Title prop is missing.", TAG];
reject(@"error", message, NULL);
return;
}

NSString *scheduleId = [NotificationScheduler
scheduleNotificationWithTitle: title
body: [notification objectForKey:@"body"]
data: (NSMutableDictionary * _Nullable) [notification objectForKey:@"data"]
scheduleId: [notification valueForKey:@"id"]
triggerDate: [NSDate dateWithTimeIntervalSince1970:
[[trigger valueForKey:@"timestamp"] longValue] / 1000
]
];
resolve(NULL);
resolve(scheduleId);
}
#endif

Expand All @@ -63,7 +80,7 @@ - (void)cancelAllScheduledNotifications:(RCTPromiseResolveBlock)resolve reject:(
// Don't compile this code when we build for the old architecture.
#ifdef RCT_NEW_ARCH_ENABLED
- (std::shared_ptr<facebook::react::TurboModule>)getTurboModule:
(const facebook::react::ObjCTurboModule::InitParams &)params
(const facebook::react::ObjCTurboModule::InitParams &)params
{
return std::make_shared<facebook::react::NativeLocalNotificationsSpecJSI>(params);
}
Expand Down
15 changes: 8 additions & 7 deletions ios/NotificationScheduler.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,9 @@ import UserNotifications
title: String,
body: String,
data: NSMutableDictionary?,
scheduleId: String,
scheduleId: String?,
triggerDate: Date
) {

) -> String {
let content = UNMutableNotificationContent()
content.title = title
content.body = body
Expand All @@ -26,15 +25,17 @@ import UserNotifications
),
repeats: false
)

let request = UNNotificationRequest(identifier: scheduleId, content: content, trigger: trigger)
let safeScheduleId = scheduleId ?? UUID().uuidString
let request = UNNotificationRequest(identifier: safeScheduleId, content: content, trigger: trigger)
UNUserNotificationCenter.current().add(request) { error in
let tag = "[react-native-local-notifications]"
if let error = error {
print("Error scheduling notification: \(error.localizedDescription)")
print("\(tag) Error scheduling notification: \(error.localizedDescription)")
} else {
print("Notification scheduled successfully at " + triggerDate.description)
print("\(tag) Notification scheduled successfully at \(triggerDate.description)")
}
}
return safeScheduleId
}

@objc public static func cancelScheduledNotifications(scheduleIds: [String]) {
Expand Down
1 change: 1 addition & 0 deletions src/NativeLocalNotifications.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { TurboModuleRegistry } from 'react-native';

export interface NotificationAndroid {
smallIcon?: string;
color?: string;
}

export interface Notification {
Expand Down

0 comments on commit 2d79255

Please sign in to comment.