rns-nativecall 1.2.6 → 1.2.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -55,16 +55,15 @@ repositories {
55
55
  }
56
56
 
57
57
  dependencies {
58
- // Use 'provided' or 'implementation' depending on how you want to link RN
59
- // For most modules, we use implementation but let the app provide the actual binary
60
58
  implementation "com.facebook.react:react-native:+"
61
59
 
62
60
  implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
63
61
  implementation "com.google.firebase:firebase-messaging:23.4.0"
64
62
  implementation "androidx.appcompat:appcompat:1.6.1"
65
- implementation "androidx.core:core-ktx:1.10.1"
63
+
66
64
  implementation "com.google.android.material:material:1.9.0"
67
-
65
+ implementation "androidx.core:core-ktx:1.12.0"
66
+
68
67
  // Glide for profile pictures
69
68
  implementation "com.github.bumptech.glide:glide:4.15.1"
70
69
  annotationProcessor "com.github.bumptech.glide:compiler:4.15.1"
@@ -1,21 +1,44 @@
1
1
  package com.rnsnativecall
2
2
 
3
3
  import android.app.*
4
+ import android.app.Notification
5
+ import android.app.Notification.Style
6
+ import android.app.NotificationChannel
7
+ import android.app.NotificationManager
8
+ import android.app.PendingIntent
4
9
  import android.content.Context
5
10
  import android.content.Intent
11
+ import android.graphics.*
12
+ import android.graphics.Bitmap
13
+ import android.graphics.BitmapFactory
14
+ import android.graphics.Canvas
6
15
  import android.graphics.Color
16
+ import android.graphics.Matrix
17
+ import android.graphics.Paint
18
+ import android.graphics.PorterDuff
19
+ import android.graphics.PorterDuffXfermode
20
+ import android.graphics.drawable.BitmapDrawable
21
+ import android.graphics.drawable.Drawable
7
22
  import android.media.AudioAttributes
8
23
  import android.media.RingtoneManager
9
24
  import android.os.Build
10
25
  import android.os.Handler
11
26
  import android.os.Looper
27
+ import android.os.Vibrator
28
+ import android.util.DisplayMetrics
29
+ import android.widget.RemoteViews
30
+ import androidx.annotation.DrawableRes
12
31
  import androidx.core.app.NotificationCompat
32
+ import androidx.core.app.NotificationManagerCompat
13
33
  import androidx.core.app.Person
14
34
  import androidx.core.content.ContextCompat
35
+ import androidx.core.content.pm.ShortcutInfoCompat
36
+ import androidx.core.content.pm.ShortcutManagerCompat
37
+ import androidx.core.graphics.drawable.IconCompat
15
38
 
16
39
  object NativeCallManager {
17
- const val channelId = "CALL_CHANNEL_URGENT_V2"
18
- private const val MISSED_CHANNEL_ID = "missed_calls"
40
+ const val channelId = "CALL_CHANNEL_1yURGENT89_Io0-"
41
+ private const val MISSED_CHANNEL_ID = "missed_h89call_19090"
19
42
 
20
43
  private var currentCallData: Map<String, String>? = null
21
44
 
@@ -25,8 +48,6 @@ object NativeCallManager {
25
48
 
26
49
  fun getCurrentCallData(): Map<String, String>? = currentCallData
27
50
 
28
- // --- REUSABLE HELPERS ---
29
-
30
51
  private fun getPendingIntentFlags(): Int =
31
52
  if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
32
53
  PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
@@ -39,6 +60,36 @@ object NativeCallManager {
39
60
  return if (colorId != 0) ContextCompat.getColor(context, colorId) else Color.parseColor("#ffffff")
40
61
  }
41
62
 
63
+ private fun getBridgeColor(context: Context): Int = Color.parseColor("#ffffffff")
64
+
65
+ private fun getCircularIconWithBackground(
66
+ context: Context,
67
+ iconId: Int,
68
+ ): IconCompat {
69
+ val iconDrawable = ContextCompat.getDrawable(context, iconId)
70
+ val backgroundColor = getResourceColor(context)
71
+
72
+ val size = 128 // Standard size for notification icons
73
+ val bitmap = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888)
74
+ val canvas = Canvas(bitmap)
75
+
76
+ val paint =
77
+ Paint().apply {
78
+ color = backgroundColor
79
+ isAntiAlias = true
80
+ }
81
+
82
+ // Draw background circle
83
+ canvas.drawCircle(size / 2f, size / 2f, size / 2f, paint)
84
+
85
+ // Draw the icon on top (padding it slightly so it doesn't touch the edges)
86
+ val padding = size / 4
87
+ iconDrawable?.setBounds(padding, padding, size - padding, size - padding)
88
+ iconDrawable?.draw(canvas)
89
+
90
+ return IconCompat.createWithBitmap(bitmap)
91
+ }
92
+
42
93
  private fun createCallIntent(
43
94
  context: Context,
44
95
  targetClass: Class<*>,
@@ -54,7 +105,33 @@ object NativeCallManager {
54
105
 
55
106
  private fun getNotificationManager(context: Context) = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
56
107
 
57
- // --- MAIN LOGIC ---
108
+ private fun getInteractionPendingIntent(
109
+ context: Context,
110
+ uuid: String,
111
+ ): PendingIntent {
112
+ // Dynamically get the main activity of the host app
113
+ val packageName = context.packageName
114
+ val launchIntent = context.packageManager.getLaunchIntentForPackage(packageName)
115
+
116
+ val intent =
117
+ if (launchIntent != null) {
118
+ launchIntent.apply {
119
+ action = "ACTION_CALL_INTERACTION" // Custom action to handle in JS/Java
120
+ putExtra("EXTRA_CALL_UUID", uuid)
121
+ addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_SINGLE_TOP)
122
+ }
123
+ } else {
124
+ // Fallback if launch intent is null (rare)
125
+ Intent()
126
+ }
127
+
128
+ return PendingIntent.getActivity(
129
+ context,
130
+ uuid.hashCode(),
131
+ intent,
132
+ getPendingIntentFlags() or PendingIntent.FLAG_UPDATE_CURRENT,
133
+ )
134
+ }
58
135
 
59
136
  fun handleIncomingPush(
60
137
  context: Context,
@@ -68,6 +145,7 @@ object NativeCallManager {
68
145
  val notificationId = uuid.hashCode()
69
146
  val flags = getPendingIntentFlags()
70
147
 
148
+ val vibrationPattern = longArrayOf(0, 1000, 500, 1000, 500, 1000)
71
149
  // 1. Setup Intents using helper
72
150
  val overlayIntent =
73
151
  createCallIntent(context, NotificationOverlayActivity::class.java, null, uuid, data).apply {
@@ -98,6 +176,7 @@ object NativeCallManager {
98
176
  lockscreenVisibility = Notification.VISIBILITY_PUBLIC
99
177
  enableVibration(true)
100
178
  setBypassDnd(true)
179
+ setVibrationPattern(longArrayOf(0, 1000, 500, 1000))
101
180
  setSound(
102
181
  RingtoneManager.getDefaultUri(RingtoneManager.TYPE_RINGTONE),
103
182
  AudioAttributes
@@ -136,13 +215,14 @@ object NativeCallManager {
136
215
  NotificationCompat
137
216
  .Builder(context, channelId)
138
217
  .setSmallIcon(iconId)
139
- .setContentTitle("Incoming $callType Call from $name")
140
- .setContentText("Incoming $callType Call")
141
- .setSubText("Incoming $callType Call")
218
+ .setContentTitle("Incoming $callType call from $name")
219
+ .setContentText("Incoming $callType call")
220
+ .setSubText("Incoming $callType call")
142
221
  .setColor(getResourceColor(context))
143
222
  .setPriority(NotificationCompat.PRIORITY_MAX)
144
223
  .setCategory(NotificationCompat.CATEGORY_CALL)
145
224
  .setOngoing(true)
225
+ .setVibrate(vibrationPattern) // Add this line
146
226
  .setFullScreenIntent(fullScreenPI, true)
147
227
  .setStyle(incomingCallStyle)
148
228
  .setForegroundServiceBehavior(NotificationCompat.FOREGROUND_SERVICE_IMMEDIATE)
@@ -166,38 +246,172 @@ object NativeCallManager {
166
246
  val name = data["name"] ?: "Unknown"
167
247
  val callType = data["callType"] ?: "video"
168
248
 
169
- // Setup Missed Channel
249
+ // 1. Setup Channel
170
250
  if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
171
- val channel = NotificationChannel(MISSED_CHANNEL_ID, "Missed Calls", NotificationManager.IMPORTANCE_DEFAULT)
251
+ val channel =
252
+ NotificationChannel(
253
+ MISSED_CHANNEL_ID,
254
+ "Missed Calls",
255
+ NotificationManager.IMPORTANCE_LOW,
256
+ ).apply {
257
+ lockscreenVisibility = Notification.VISIBILITY_PUBLIC
258
+ }
172
259
  getNotificationManager(context).createNotificationChannel(channel)
173
260
  }
174
261
 
175
- val iconResId =
176
- context.resources
177
- .getIdentifier("ic_missed_call", "drawable", context.packageName)
178
- .let { if (it != 0) it else android.R.drawable.sym_call_missed }
179
-
180
- val launchIntent =
181
- context.packageManager.getLaunchIntentForPackage(context.packageName)?.apply {
182
- addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_SINGLE_TOP)
262
+ // 2. Resolve Icon
263
+ val iconId =
264
+ context.resources.getIdentifier("notification_icon", "mipmap", context.packageName).let { mipmapId ->
265
+ if (mipmapId != 0) {
266
+ mipmapId
267
+ } else {
268
+ context.resources.getIdentifier("notification_icon", "drawable", context.packageName).let { drawableId ->
269
+ if (drawableId != 0) drawableId else context.applicationInfo.icon
270
+ }
271
+ }
183
272
  }
184
- val pendingIntent = PendingIntent.getActivity(context, uuid.hashCode(), launchIntent, getPendingIntentFlags())
185
273
 
186
- val notification =
274
+ val avatarIcon = getCircularIconWithBackground(context, iconId)
275
+
276
+ // 3. Create Person & Link to Shortcut Key
277
+ val caller =
278
+ Person
279
+ .Builder()
280
+ .setName(name)
281
+ .setIcon(avatarIcon)
282
+ .setKey(uuid) // Links person to the shortcut
283
+ .setImportant(true)
284
+ .build()
285
+
286
+ // 4. Create & Push Dynamic Shortcut (The "Telegram" Magic)
287
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N_MR1) {
288
+ val launchIntent =
289
+ context.packageManager.getLaunchIntentForPackage(context.packageName)?.apply {
290
+ action = Intent.ACTION_VIEW
291
+ putExtra("conversation_id", uuid)
292
+ } ?: Intent()
293
+
294
+ val shortcut =
295
+ ShortcutInfoCompat
296
+ .Builder(context, uuid)
297
+ .setShortLabel(name)
298
+ .setIcon(avatarIcon)
299
+ .setIntent(launchIntent)
300
+ .setPerson(caller)
301
+ .setLongLived(true)
302
+ .build()
303
+
304
+ ShortcutManagerCompat.pushDynamicShortcut(context, shortcut)
305
+ }
306
+
307
+ // 5. Build Action & Notification
308
+ val openAppIntent = getInteractionPendingIntent(context, uuid)
309
+ val openAppAction =
310
+ NotificationCompat.Action
311
+ .Builder(
312
+ android.R.drawable.ic_menu_view,
313
+ "Open App",
314
+ openAppIntent,
315
+ ).build()
316
+
317
+ val messagingStyle =
318
+ NotificationCompat
319
+ .MessagingStyle(caller)
320
+ .addMessage("You missed a $callType call from $name", System.currentTimeMillis(), caller)
321
+
322
+ val builder =
187
323
  NotificationCompat
188
324
  .Builder(context, MISSED_CHANNEL_ID)
189
- .setSmallIcon(iconResId)
190
- .setContentTitle(context.applicationInfo.loadLabel(context.packageManager).toString())
191
- .setContentText("You missed a $callType call from $name")
192
- .setPriority(NotificationCompat.PRIORITY_HIGH)
193
- .setColor(getResourceColor(context))
194
- .setAutoCancel(true)
325
+ .setSmallIcon(android.R.drawable.sym_call_missed)
326
+ .setStyle(messagingStyle)
327
+ .setShortcutId(uuid)
195
328
  .setCategory(NotificationCompat.CATEGORY_MISSED_CALL)
196
- .setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
197
- .setContentIntent(pendingIntent)
329
+ .setOngoing(false)
330
+ .setAutoCancel(true)
331
+ .setColor(getBridgeColor(context))
332
+ .setContentIntent(openAppIntent)
333
+ // .addAction(openAppAction) later for future use
334
+ .setPriority(NotificationCompat.PRIORITY_HIGH)
335
+
336
+ getNotificationManager(context).notify(uuid.hashCode() + 10000, builder.build())
337
+ }
338
+
339
+ fun showOnGoingCall(
340
+ context: Context,
341
+ data: Map<String, String>,
342
+ ) {
343
+ val uuid = data["callUuid"] ?: return
344
+ val name = data["name"] ?: "Unknown"
345
+ val callType = data["callType"] ?: "video"
346
+
347
+ // 1. Setup the Channel
348
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
349
+ val channel =
350
+ NotificationChannel(
351
+ MISSED_CHANNEL_ID,
352
+ "Active Calls",
353
+ NotificationManager.IMPORTANCE_LOW,
354
+ ).apply {
355
+ lockscreenVisibility = Notification.VISIBILITY_PUBLIC
356
+ }
357
+ getNotificationManager(context).createNotificationChannel(channel)
358
+ }
359
+
360
+ val openAppIntent = getInteractionPendingIntent(context, uuid)
361
+
362
+ // 2. The Person - No Icon means Android will show Initials
363
+ val caller =
364
+ Person
365
+ .Builder()
366
+ .setName(name)
367
+ .setImportant(true)
198
368
  .build()
199
369
 
200
- getNotificationManager(context).notify(uuid.hashCode() + 10000, notification)
370
+ // 3. Intents for CallStyle
371
+ val hangUpIntent =
372
+ Intent(context, CallActionReceiver::class.java).apply {
373
+ action = "ACTION_HANG_UP"
374
+ putExtra("EXTRA_UUID", uuid)
375
+ }
376
+ val hangUpPendingIntent =
377
+ PendingIntent.getBroadcast(
378
+ context,
379
+ uuid.hashCode(),
380
+ hangUpIntent,
381
+ getPendingIntentFlags(),
382
+ )
383
+
384
+ val callStyle = NotificationCompat.CallStyle.forOngoingCall(caller, hangUpPendingIntent)
385
+
386
+ // 4. Custom Action Button
387
+ val openAppAction =
388
+ NotificationCompat.Action
389
+ .Builder(
390
+ android.R.drawable.ic_menu_view,
391
+ "Open App",
392
+ openAppIntent,
393
+ ).build()
394
+
395
+ // 5. Build Notification
396
+ val builder =
397
+ NotificationCompat
398
+ .Builder(context, MISSED_CHANNEL_ID)
399
+ // Use system icon for the status bar
400
+ .setSmallIcon(android.R.drawable.stat_sys_phone_call)
401
+ .setStyle(callStyle)
402
+ .setCategory(NotificationCompat.CATEGORY_CALL)
403
+ .setOngoing(true)
404
+ .setAutoCancel(false)
405
+ .addPerson(caller)
406
+ .addAction(openAppAction)
407
+ .setPriority(NotificationCompat.PRIORITY_LOW)
408
+ // Sets the circular background of the Small Icon in the drawer to White
409
+ .setColor(getBridgeColor(context))
410
+ .setContentTitle(name)
411
+ .setContentText("Ongoing $callType call")
412
+ .setFullScreenIntent(openAppIntent, true)
413
+
414
+ getNotificationManager(context).notify(uuid.hashCode(), builder.build())
201
415
  }
202
416
 
203
417
  fun refreshNotificationOnly(
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rns-nativecall",
3
- "version": "1.2.6",
3
+ "version": "1.2.8",
4
4
  "description": "High-performance React Native module for handling native VoIP call UI on Android and iOS.",
5
5
  "main": "index.js",
6
6
  "types": "index.d.ts",