rns-nativecall 1.2.5 → 1.2.7
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.
package/README.md
CHANGED
|
@@ -127,10 +127,8 @@ export default function App() {
|
|
|
127
127
|
}
|
|
128
128
|
```
|
|
129
129
|
---
|
|
130
|
-
|
|
131
|
-
# rns-nativecall API Reference
|
|
130
|
+
## 📖 rns-nativecall API Reference
|
|
132
131
|
|
|
133
|
-
## API Reference
|
|
134
132
|
|
|
135
133
|
### Core Methods
|
|
136
134
|
| Method | Platform | Description |
|
|
@@ -138,8 +136,8 @@ export default function App() {
|
|
|
138
136
|
| **registerHeadlessTask(callback)** | All | Registers the background task. `eventType` is 'INCOMING_CALL', 'BUSY', or 'ABORTED_CALL'. |
|
|
139
137
|
| **displayCall(uuid, name, type)** | All | Launches full-screen Activity (Android) or reports to CallKit (iOS). |
|
|
140
138
|
| **destroyNativeCallUI(uuid)** | All | Stops ringtone/Activity (Android) or ends CallKit session (iOS). |
|
|
141
|
-
| **showMissedCall(uuid, name, type)** | Android | Posts a persistent notification in the device tray for missed calls. |
|
|
142
139
|
| **subscribe(onAccept, onReject, onFailed)** | All | Listens for Answer/Decline actions and system-level bridge errors. |
|
|
140
|
+
| **showMissedCall(uuid, name, type)** | Android | Posts a persistent notification in the device tray for missed calls. |
|
|
143
141
|
|
|
144
142
|
### Data & State Management
|
|
145
143
|
| Method | Platform | Description |
|
package/android/build.gradle
CHANGED
|
@@ -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
|
-
|
|
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 = "
|
|
18
|
-
private const val MISSED_CHANNEL_ID = "
|
|
40
|
+
const val channelId = "CALL_CHANNEL_1yURGENT_Io"
|
|
41
|
+
private const val MISSED_CHANNEL_ID = "missed_hcall_190"
|
|
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
|
-
|
|
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
|
|
140
|
-
.setContentText("Incoming $callType
|
|
141
|
-
.setSubText("Incoming $callType
|
|
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
|
|
249
|
+
// 1. Setup Channel
|
|
170
250
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
|
171
|
-
val channel =
|
|
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
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
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
|
|
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("Missed $callType call", System.currentTimeMillis(), caller)
|
|
321
|
+
|
|
322
|
+
val builder =
|
|
187
323
|
NotificationCompat
|
|
188
324
|
.Builder(context, MISSED_CHANNEL_ID)
|
|
189
|
-
.setSmallIcon(
|
|
190
|
-
.
|
|
191
|
-
.
|
|
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
|
-
.
|
|
197
|
-
.
|
|
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
|
-
|
|
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(
|