@nativetalkcommunications/react-native-call-sdk 0.1.0

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.
Files changed (74) hide show
  1. package/LICENSE +21 -0
  2. package/NativetalkCallSdk.podspec +31 -0
  3. package/README.md +494 -0
  4. package/android/build.gradle +58 -0
  5. package/android/gradle.properties +2 -0
  6. package/android/src/main/AndroidManifest.xml +84 -0
  7. package/android/src/main/java/io/nativetalk/callsdk/BackgroundService.kt +149 -0
  8. package/android/src/main/java/io/nativetalk/callsdk/CallActionReceiver.kt +24 -0
  9. package/android/src/main/java/io/nativetalk/callsdk/CallService.kt +45 -0
  10. package/android/src/main/java/io/nativetalk/callsdk/Compatibility.kt +96 -0
  11. package/android/src/main/java/io/nativetalk/callsdk/CoreManager.kt +801 -0
  12. package/android/src/main/java/io/nativetalk/callsdk/NativetalkCallScreeningService.kt +105 -0
  13. package/android/src/main/java/io/nativetalk/callsdk/NativetalkCallSdkModule.kt +205 -0
  14. package/android/src/main/java/io/nativetalk/callsdk/NativetalkCallSdkPackage.kt +18 -0
  15. package/android/src/main/java/io/nativetalk/callsdk/TelephonyMonitor.kt +229 -0
  16. package/android/src/main/java/io/nativetalk/callsdk/Utils.kt +42 -0
  17. package/android/src/main/res/drawable/ic_nativetalk_call.xml +9 -0
  18. package/android/src/main/res/values/strings.xml +9 -0
  19. package/app.plugin.js +1 -0
  20. package/ios/NativetalkCallSdk-Bridging-Header.h +4 -0
  21. package/ios/NativetalkCallSdk.swift +738 -0
  22. package/ios/NativetalkCallSdkBridge.m +35 -0
  23. package/lib/commonjs/CallProvider.js +602 -0
  24. package/lib/commonjs/helpers.js +173 -0
  25. package/lib/commonjs/index.js +96 -0
  26. package/lib/commonjs/native.js +146 -0
  27. package/lib/commonjs/types.js +8 -0
  28. package/lib/commonjs/ui/Avatar.js +29 -0
  29. package/lib/commonjs/ui/Dialer.js +189 -0
  30. package/lib/commonjs/ui/IncomingCallView.js +128 -0
  31. package/lib/commonjs/ui/OutgoingCallView.js +117 -0
  32. package/lib/commonjs/ui/index.js +22 -0
  33. package/lib/commonjs/ui/theme.js +21 -0
  34. package/lib/module/CallProvider.js +573 -0
  35. package/lib/module/helpers.js +161 -0
  36. package/lib/module/index.js +57 -0
  37. package/lib/module/native.js +123 -0
  38. package/lib/module/types.js +7 -0
  39. package/lib/module/ui/Avatar.js +22 -0
  40. package/lib/module/ui/Dialer.js +162 -0
  41. package/lib/module/ui/IncomingCallView.js +101 -0
  42. package/lib/module/ui/OutgoingCallView.js +110 -0
  43. package/lib/module/ui/index.js +13 -0
  44. package/lib/module/ui/theme.js +17 -0
  45. package/lib/typescript/CallProvider.d.ts +46 -0
  46. package/lib/typescript/helpers.d.ts +52 -0
  47. package/lib/typescript/index.d.ts +77 -0
  48. package/lib/typescript/native.d.ts +53 -0
  49. package/lib/typescript/types.d.ts +155 -0
  50. package/lib/typescript/ui/Avatar.d.ts +13 -0
  51. package/lib/typescript/ui/Dialer.d.ts +29 -0
  52. package/lib/typescript/ui/IncomingCallView.d.ts +39 -0
  53. package/lib/typescript/ui/OutgoingCallView.d.ts +28 -0
  54. package/lib/typescript/ui/index.d.ts +13 -0
  55. package/lib/typescript/ui/theme.d.ts +20 -0
  56. package/linphonesw-pod/Sources/LinphoneSdkInfos.swift +4 -0
  57. package/linphonesw-pod/Sources/LinphoneWrapper.swift +42949 -0
  58. package/linphonesw-pod/linphonesw.podspec +46 -0
  59. package/package.json +90 -0
  60. package/plugin/build/index.js +12 -0
  61. package/plugin/build/withAndroid.js +78 -0
  62. package/plugin/build/withIos.js +66 -0
  63. package/src/CallProvider.tsx +675 -0
  64. package/src/helpers.ts +179 -0
  65. package/src/index.ts +84 -0
  66. package/src/native.ts +185 -0
  67. package/src/types.ts +202 -0
  68. package/src/ui/Avatar.tsx +46 -0
  69. package/src/ui/Dialer.tsx +248 -0
  70. package/src/ui/IncomingCallView.tsx +161 -0
  71. package/src/ui/OutgoingCallView.tsx +203 -0
  72. package/src/ui/index.ts +13 -0
  73. package/src/ui/theme.ts +36 -0
  74. package/ui/package.json +6 -0
@@ -0,0 +1,149 @@
1
+ package io.nativetalk.callsdk
2
+
3
+ import android.app.AlarmManager
4
+ import android.app.NotificationChannel
5
+ import android.app.NotificationManager
6
+ import android.app.PendingIntent
7
+ import android.app.Service
8
+ import android.content.Context
9
+ import android.content.Intent
10
+ import android.net.ConnectivityManager
11
+ import android.net.Network
12
+ import android.net.NetworkCapabilities
13
+ import android.net.NetworkRequest
14
+ import android.os.IBinder
15
+ import android.os.PowerManager
16
+ import android.os.SystemClock
17
+ import android.util.Log
18
+ import androidx.core.app.NotificationCompat
19
+
20
+ /**
21
+ * Long-running foreground service that keeps the SIP registration warm even
22
+ * when the user has backgrounded the app. Hosts a wake lock and (re)starts
23
+ * [CallService] if the OS kills it.
24
+ *
25
+ * Lifecycle: `startService()` → `onCreate` (wake lock + TelephonyMonitor) →
26
+ * `onStartCommand` (foreground notification + spawn CallService). On
27
+ * task-removal we schedule an alarm to relaunch ourselves a second later.
28
+ */
29
+ class BackgroundService : Service() {
30
+
31
+ companion object {
32
+ private const val NOTIFICATION_ID = 1000
33
+ private const val CHANNEL_ID = "nativetalk_background"
34
+ private const val TAG = "NativetalkCallSdk.BgService"
35
+
36
+ // Set to `false` by the JS bridge during explicit logout so that
37
+ // onTaskRemoved doesn't immediately respawn us. @Volatile because
38
+ // the flag is read/written from different threads (RN bridge thread
39
+ // vs main thread handling task-removal).
40
+ @Volatile var shouldRestart = true
41
+
42
+ fun startService(context: Context) {
43
+ val intent = Intent(context, BackgroundService::class.java)
44
+ // `startForegroundService` (not `startService`) is required on
45
+ // Android 8+ — the OS gives us a 5-second window to call
46
+ // `startForeground` or it kills us with a SecurityException.
47
+ context.startForegroundService(intent)
48
+ }
49
+
50
+ fun stopService(context: Context) {
51
+ val intent = Intent(context, BackgroundService::class.java)
52
+ context.stopService(intent)
53
+ }
54
+ }
55
+
56
+ private var wakeLock: PowerManager.WakeLock? = null
57
+ private var connectivityManager: ConnectivityManager? = null
58
+
59
+ private val networkCallback = object : ConnectivityManager.NetworkCallback() {
60
+ override fun onAvailable(network: Network) {
61
+ Log.d(TAG, "Network available — triggering re-registration")
62
+ CoreManager.refreshRegisters()
63
+ }
64
+ override fun onLost(network: Network) {
65
+ Log.d(TAG, "Network lost")
66
+ CoreManager.setNetworkReachable(false)
67
+ }
68
+ }
69
+
70
+ override fun onCreate() {
71
+ super.onCreate()
72
+ createChannel()
73
+ val pm = getSystemService(Context.POWER_SERVICE) as PowerManager
74
+ wakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "NativetalkCallSdk::BackgroundService")
75
+ // 10-minute timeout: Android's StrictMode warns if a wake lock has
76
+ // no timeout, and 10min is long enough to register & answer a call
77
+ // but short enough that a buggy service can't drain the battery.
78
+ wakeLock?.acquire(10 * 60 * 1000L)
79
+
80
+ TelephonyMonitor.start(applicationContext)
81
+
82
+ connectivityManager = getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
83
+ val request = NetworkRequest.Builder()
84
+ .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
85
+ .build()
86
+ connectivityManager?.registerNetworkCallback(request, networkCallback)
87
+ }
88
+
89
+ override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
90
+ // Must call startForeground within ~5s of startForegroundService —
91
+ // do it first thing here.
92
+ startForeground(NOTIFICATION_ID, buildNotification())
93
+ // CallService owns the per-call notification; this service owns
94
+ // the "always-on" registration notification.
95
+ applicationContext.startService(Intent(applicationContext, CallService::class.java))
96
+ // START_STICKY: if the OS kills us under memory pressure, restart
97
+ // us with a null intent. That's what we want for a keep-alive.
98
+ return START_STICKY
99
+ }
100
+
101
+ override fun onBind(intent: Intent?): IBinder? = null
102
+
103
+ override fun onTaskRemoved(rootIntent: Intent?) {
104
+ // Fires when the user swipes the app from recents. The OS often
105
+ // kills our service shortly after, so we set a 1-second alarm to
106
+ // relaunch ourselves. The alarm survives even if our process dies.
107
+ if (shouldRestart) {
108
+ val restart = Intent(applicationContext, BackgroundService::class.java)
109
+ val pending = PendingIntent.getService(
110
+ this,
111
+ 1,
112
+ restart,
113
+ PendingIntent.FLAG_ONE_SHOT or PendingIntent.FLAG_IMMUTABLE
114
+ )
115
+ val alarm = getSystemService(Context.ALARM_SERVICE) as AlarmManager
116
+ alarm.set(AlarmManager.ELAPSED_REALTIME, SystemClock.elapsedRealtime() + 1000, pending)
117
+ }
118
+ super.onTaskRemoved(rootIntent)
119
+ }
120
+
121
+ override fun onDestroy() {
122
+ super.onDestroy()
123
+ runCatching { connectivityManager?.unregisterNetworkCallback(networkCallback) }
124
+ wakeLock?.release()
125
+ TelephonyMonitor.stop()
126
+ }
127
+
128
+ private fun createChannel() {
129
+ val ch = NotificationChannel(
130
+ CHANNEL_ID,
131
+ getString(R.string.nativetalk_call_sdk_notif_channel_background_name),
132
+ NotificationManager.IMPORTANCE_LOW
133
+ ).apply {
134
+ description = getString(R.string.nativetalk_call_sdk_ready_body)
135
+ setShowBadge(false)
136
+ }
137
+ val manager = getSystemService(NotificationManager::class.java)
138
+ manager.createNotificationChannel(ch)
139
+ }
140
+
141
+ private fun buildNotification() = NotificationCompat.Builder(this, CHANNEL_ID)
142
+ .setContentTitle(getString(R.string.nativetalk_call_sdk_ready_title))
143
+ .setContentText(getString(R.string.nativetalk_call_sdk_ready_body))
144
+ .setSmallIcon(R.drawable.ic_nativetalk_call)
145
+ .setOngoing(true)
146
+ .setCategory(NotificationCompat.CATEGORY_SERVICE)
147
+ .setPriority(NotificationCompat.PRIORITY_LOW)
148
+ .build()
149
+ }
@@ -0,0 +1,24 @@
1
+ package io.nativetalk.callsdk
2
+
3
+ import android.content.BroadcastReceiver
4
+ import android.content.Context
5
+ import android.content.Intent
6
+ import android.util.Log
7
+
8
+ /**
9
+ * Receives Answer / Decline button taps on the heads-up call notification and
10
+ * forwards them to [CoreManager].
11
+ */
12
+ class CallActionReceiver : BroadcastReceiver() {
13
+ override fun onReceive(context: Context?, intent: Intent?) {
14
+ Log.d(TAG, "Received action: ${intent?.action}")
15
+ when (intent?.action) {
16
+ CoreManager.ACTION_ANSWER_CALL -> CoreManager.answer()
17
+ CoreManager.ACTION_DECLINE_CALL -> CoreManager.decline()
18
+ }
19
+ }
20
+
21
+ private companion object {
22
+ const val TAG = "NativetalkCallSdk.Receiver"
23
+ }
24
+ }
@@ -0,0 +1,45 @@
1
+ package io.nativetalk.callsdk
2
+
3
+ import android.app.Service
4
+ import android.content.Intent
5
+ import android.os.IBinder
6
+ import android.util.Log
7
+
8
+ /**
9
+ * Foreground service that owns the call notification.
10
+ *
11
+ * Started by [BackgroundService] and by [NativetalkCallSdkModule.startNativeServices].
12
+ * The actual lifecycle of the Linphone core lives in [CoreManager]; this
13
+ * service is mostly a holder for the foreground-notification slot.
14
+ */
15
+ class CallService : Service() {
16
+
17
+ override fun onCreate() {
18
+ super.onCreate()
19
+ Log.i(TAG, "onCreate")
20
+ }
21
+
22
+ override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
23
+ Log.i(TAG, "onStartCommand")
24
+ CoreManager.onCallServiceStarted(this)
25
+ return super.onStartCommand(intent, flags, startId)
26
+ }
27
+
28
+ override fun onBind(intent: Intent?): IBinder? = null
29
+
30
+ override fun onTaskRemoved(rootIntent: Intent?) {
31
+ if (BackgroundService.shouldRestart) {
32
+ startService(Intent(applicationContext, CallService::class.java))
33
+ }
34
+ super.onTaskRemoved(rootIntent)
35
+ }
36
+
37
+ override fun onDestroy() {
38
+ super.onDestroy()
39
+ Log.i(TAG, "onDestroy")
40
+ }
41
+
42
+ private companion object {
43
+ const val TAG = "NativetalkCallSdk.CallService"
44
+ }
45
+ }
@@ -0,0 +1,96 @@
1
+ package io.nativetalk.callsdk
2
+
3
+ import android.Manifest
4
+ import android.annotation.SuppressLint
5
+ import android.app.Notification
6
+ import android.app.Service
7
+ import android.content.Context
8
+ import android.content.pm.PackageManager
9
+ import android.os.Build
10
+ import android.util.Log
11
+ import androidx.annotation.RequiresApi
12
+
13
+ /**
14
+ * Thin shims over `Service.startForeground(…)` and the runtime
15
+ * POST_NOTIFICATIONS check. Required because Android 14 (API 34) made the
16
+ * foreground-service-type parameter MANDATORY for `phoneCall` / `microphone`
17
+ * services — calling the 2-arg overload on API 34 throws SecurityException,
18
+ * while the 3-arg overload doesn't exist on older versions.
19
+ *
20
+ * Centralising the version dispatch here keeps [CoreManager] free of
21
+ * `Build.VERSION.SDK_INT` checks.
22
+ */
23
+ @SuppressLint("NewApi")
24
+ class Compatibility {
25
+ companion object {
26
+ private const val TAG = "NativetalkCallSdk.Compat"
27
+
28
+ // Type bitmask values, copied here so callers don't need to depend
29
+ // on android.content.pm.ServiceInfo (which would force API-30+).
30
+ // 4 = ServiceInfo.FOREGROUND_SERVICE_TYPE_PHONE_CALL
31
+ // 128 = ServiceInfo.FOREGROUND_SERVICE_TYPE_MICROPHONE
32
+ // OR them together when a call needs both mic + phoneCall (active call).
33
+ const val FOREGROUND_SERVICE_TYPE_PHONE_CALL = 4
34
+ const val FOREGROUND_SERVICE_TYPE_MICROPHONE = 128
35
+
36
+ // Dispatch to the right startForeground overload by API level.
37
+ // `UPSIDE_DOWN_CAKE` is the Build.VERSION_CODES constant for API 34.
38
+ fun startServiceForeground(
39
+ service: Service,
40
+ id: Int,
41
+ notification: Notification,
42
+ foregroundServiceType: Int
43
+ ) {
44
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
45
+ Api34.startServiceForeground(service, id, notification, foregroundServiceType)
46
+ } else {
47
+ Legacy.startServiceForeground(service, id, notification)
48
+ }
49
+ }
50
+
51
+ // POST_NOTIFICATIONS only exists as a runtime permission on Android
52
+ // 13+ (TIRAMISU). On older versions, having the manifest entry is
53
+ // enough — return true so callers don't need their own version check.
54
+ fun isPostNotificationsPermissionGranted(context: Context): Boolean {
55
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
56
+ return context.checkSelfPermission(Manifest.permission.POST_NOTIFICATIONS) == PackageManager.PERMISSION_GRANTED
57
+ }
58
+ return true
59
+ }
60
+ }
61
+ }
62
+
63
+ // API 34+ path. The `@RequiresApi` annotation lets the IDE catch any caller
64
+ // that bypasses [Compatibility.startServiceForeground] and reaches this
65
+ // directly on an older device.
66
+ @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
67
+ private object Api34 {
68
+ fun startServiceForeground(
69
+ service: Service,
70
+ id: Int,
71
+ notification: Notification,
72
+ foregroundServiceType: Int
73
+ ) {
74
+ // Wrapped in try/catch because even with the right type mask the
75
+ // OS can still reject the foreground transition (e.g. user disabled
76
+ // POST_NOTIFICATIONS, or background-start restrictions). We log
77
+ // rather than crash because the call itself usually still works —
78
+ // just without a notification.
79
+ try {
80
+ service.startForeground(id, notification, foregroundServiceType)
81
+ } catch (e: Exception) {
82
+ Log.e("NativetalkCallSdk.Compat", "startForeground (API34) failed", e)
83
+ }
84
+ }
85
+ }
86
+
87
+ // Pre-API-34 path: the 2-arg overload. No type mask needed.
88
+ private object Legacy {
89
+ fun startServiceForeground(service: Service, id: Int, notification: Notification) {
90
+ try {
91
+ service.startForeground(id, notification)
92
+ } catch (e: Exception) {
93
+ Log.e("NativetalkCallSdk.Compat", "startForeground (legacy) failed", e)
94
+ }
95
+ }
96
+ }