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.
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_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
|
-
|
|
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("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(
|
|
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(
|