rns-nativecall 1.0.7 → 1.0.9

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.
@@ -2,20 +2,25 @@ package com.rnsnativecall
2
2
 
3
3
  import android.app.Activity
4
4
  import android.content.Intent
5
+ import android.os.Build
5
6
  import android.os.Bundle
6
7
  import android.view.WindowManager
7
- import android.os.Build
8
8
 
9
9
  class AcceptCallActivity : Activity() {
10
10
  override fun onCreate(savedInstanceState: Bundle?) {
11
11
  super.onCreate(savedInstanceState)
12
-
12
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1) {
13
+ setShowWhenLocked(true)
14
+ setTurnScreenOn(true)
15
+ }
13
16
  val launchIntent = packageManager.getLaunchIntentForPackage(packageName)
14
17
  if (launchIntent != null) {
15
18
  launchIntent.apply {
16
- addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or
17
- Intent.FLAG_ACTIVITY_SINGLE_TOP or
18
- Intent.FLAG_ACTIVITY_REORDER_TO_FRONT)
19
+ addFlags(
20
+ Intent.FLAG_ACTIVITY_NEW_TASK or
21
+ Intent.FLAG_ACTIVITY_SINGLE_TOP or
22
+ Intent.FLAG_ACTIVITY_REORDER_TO_FRONT,
23
+ )
19
24
  putExtras(intent.extras ?: Bundle())
20
25
  putExtra("navigatingToCall", true)
21
26
  }
@@ -25,4 +30,4 @@ class AcceptCallActivity : Activity() {
25
30
  finish()
26
31
  overridePendingTransition(0, 0)
27
32
  }
28
- }
33
+ }
@@ -1,8 +1,6 @@
1
1
  package com.rnsnativecall
2
2
 
3
3
  import android.app.*
4
- import android.app.Service
5
- import android.content.BroadcastReceiver
6
4
  import android.content.Context
7
5
  import android.content.Intent
8
6
  import android.content.IntentFilter
@@ -13,33 +11,22 @@ import android.os.Handler
13
11
  import android.os.IBinder
14
12
  import android.os.Looper
15
13
  import androidx.core.app.NotificationCompat
16
- import androidx.core.content.ContextCompat
17
14
  import com.facebook.react.HeadlessJsTaskService
18
15
 
19
16
  class CallForegroundService : Service() {
20
- private var unlockReceiver: UnlockReceiver? = null // Store reference for unregistering
17
+ private var unlockReceiver: UnlockReceiver? = null
21
18
 
22
19
  companion object {
23
- private const val CHANNEL_ID = "CALL_WAKE_SILENT"
24
-
25
20
  fun stop(context: Context) {
26
- val intent = Intent(context, CallForegroundService::class.java)
27
- context.stopService(intent)
21
+ context.stopService(Intent(context, CallForegroundService::class.java))
28
22
  }
29
23
  }
30
24
 
31
25
  override fun onCreate() {
32
26
  super.onCreate()
33
-
34
- // --- DYNAMIC REGISTRATION FIX ---
35
- // Registering here makes the system allow the USER_PRESENT broadcast
36
- // because the app is already in the Foreground (via this service).
37
27
  unlockReceiver = UnlockReceiver()
38
28
  val filter = IntentFilter(Intent.ACTION_USER_PRESENT)
39
-
40
29
  if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
41
- // Prefer NOT_EXPORTED for dynamically-registered receivers to avoid
42
- // background delivery issues and to restrict broadcasts to our process.
43
30
  registerReceiver(unlockReceiver, filter, Context.RECEIVER_NOT_EXPORTED)
44
31
  } else {
45
32
  registerReceiver(unlockReceiver, filter)
@@ -52,43 +39,10 @@ class CallForegroundService : Service() {
52
39
  startId: Int,
53
40
  ): Int {
54
41
  val data = intent?.extras
55
- val name = data?.getString("name") ?: "Someone"
56
- val uuid = data?.getString("callUuid") ?: "default_uuid"
57
-
58
- val notificationId = uuid.hashCode()
59
-
60
- createNotificationChannel()
61
-
62
- val notification =
63
- NotificationCompat
64
- .Builder(this, CHANNEL_ID)
65
- .setContentTitle(name)
66
- .setContentText("Connecting...")
67
- .setSmallIcon(applicationInfo.icon)
68
- .setPriority(NotificationCompat.PRIORITY_LOW)
69
- .setCategory(NotificationCompat.CATEGORY_SERVICE)
70
- .setOngoing(true)
71
- .build()
72
-
73
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
74
- startForeground(
75
- notificationId,
76
- notification,
77
- ServiceInfo.FOREGROUND_SERVICE_TYPE_DATA_SYNC,
78
- )
79
- } else {
80
- startForeground(notificationId, notification)
81
- }
82
-
83
- // Launch the Headless Task
84
42
  val headlessIntent =
85
43
  Intent(this, CallHeadlessTask::class.java).apply {
86
44
  val bundle = Bundle()
87
- data?.let { b ->
88
- for (key in b.keySet()) {
89
- bundle.putString(key, b.get(key)?.toString())
90
- }
91
- }
45
+ data?.let { b -> for (key in b.keySet()) bundle.putString(key, b.get(key)?.toString()) }
92
46
  putExtras(bundle)
93
47
  }
94
48
 
@@ -99,52 +53,18 @@ class CallForegroundService : Service() {
99
53
  e.printStackTrace()
100
54
  }
101
55
 
102
- // / Auto - stop after 30 s
103
- Handler(Looper.getMainLooper()).postDelayed({
104
- try {
105
- stopSelf()
106
- } catch (e: Exception) {
107
- }
108
- }, 30000)
109
-
56
+ Handler(Looper.getMainLooper()).postDelayed({ stopSelf() }, 30000)
110
57
  return START_NOT_STICKY
111
58
  }
112
59
 
113
60
  override fun onDestroy() {
114
- // --- CLEANUP ---
115
- // Unregister to prevent memory leaks once the call ends or service stops
116
61
  try {
117
62
  unlockReceiver?.let { unregisterReceiver(it) }
118
63
  } catch (e: Exception) {
119
- e.printStackTrace()
120
- }
121
-
122
- try {
123
- stopForeground(true)
124
- } catch (_: Exception) {
125
64
  }
126
65
  super.onDestroy()
127
66
  }
128
67
 
129
- private fun createNotificationChannel() {
130
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
131
- val channel =
132
- NotificationChannel(
133
- CHANNEL_ID,
134
- "Call Service",
135
- NotificationManager.IMPORTANCE_LOW,
136
- ).apply {
137
- description = "Incoming call connection state"
138
- setSound(null, null)
139
- enableVibration(false)
140
- setBypassDnd(false)
141
- lockscreenVisibility = NotificationCompat.VISIBILITY_PUBLIC
142
- }
143
- val manager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
144
- manager.createNotificationChannel(channel)
145
- }
146
- }
147
-
148
68
  override fun onBind(intent: Intent?): IBinder? = null
149
69
  }
150
70
 
@@ -154,36 +74,19 @@ class CallUiForegroundService : Service() {
154
74
  flags: Int,
155
75
  startId: Int,
156
76
  ): Int {
157
- val notification =
158
- NativeCallManager.pendingCallNotification
159
- ?: run {
160
- stopSelf()
161
- return START_NOT_STICKY
162
- }
77
+ val notification = NativeCallManager.pendingCallNotification ?: return START_NOT_STICKY
78
+ val id = NativeCallManager.pendingNotificationId ?: return START_NOT_STICKY
163
79
 
164
- val notificationId =
165
- NativeCallManager.pendingNotificationId
166
- ?: run {
167
- stopSelf()
168
- return START_NOT_STICKY
169
- }
170
-
171
- // Inside CallUiForegroundService.onStartCommand
172
80
  if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
173
- startForeground(
174
- notificationId,
175
- notification,
176
- ServiceInfo.FOREGROUND_SERVICE_TYPE_PHONE_CALL,
177
- )
81
+ startForeground(id, notification, ServiceInfo.FOREGROUND_SERVICE_TYPE_PHONE_CALL)
178
82
  } else {
179
- startForeground(notificationId, notification)
83
+ startForeground(id, notification)
180
84
  }
181
-
182
85
  return START_NOT_STICKY
183
86
  }
184
87
 
185
88
  override fun onDestroy() {
186
- stopForeground(true)
89
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) stopForeground(STOP_FOREGROUND_REMOVE)
187
90
  super.onDestroy()
188
91
  }
189
92
 
@@ -24,7 +24,6 @@ class CallMessagingService : FirebaseMessagingService() {
24
24
  val type = data["type"] ?: ""
25
25
 
26
26
  if (type == "CANCEL") {
27
- NativeCallManager.stopRingtone()
28
27
  // Pass context here to persist the cancellation
29
28
  CallState.markCanceled(uuid, context)
30
29
 
@@ -57,10 +56,10 @@ class CallMessagingService : FirebaseMessagingService() {
57
56
  // Foreground → send event directly
58
57
  CallModule.sendEventToJS("onCallReceived", data)
59
58
  } else {
60
- // Background → start foreground service (which in turn starts headless)
59
+ // Background → start the SILENT wake service
61
60
  val serviceIntent =
62
61
  Intent(context, CallForegroundService::class.java).apply {
63
- putExtra("callUuid", uuid) // Key: Pass the UUID here
62
+ putExtra("callUuid", uuid)
64
63
  putExtras(
65
64
  Bundle().apply {
66
65
  data.forEach { (k, v) -> putString(k, v) }
@@ -68,11 +67,9 @@ class CallMessagingService : FirebaseMessagingService() {
68
67
  )
69
68
  }
70
69
 
71
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
72
- context.startForegroundService(serviceIntent)
73
- } else {
74
- context.startService(serviceIntent)
75
- }
70
+ // FIX: Always use startService here now.
71
+ // We are no longer making this a "Foreground Service" at the wake stage.
72
+ context.startService(serviceIntent)
76
73
  }
77
74
  }
78
75
 
@@ -2,16 +2,17 @@ package com.rnsnativecall
2
2
 
3
3
  import android.app.NotificationManager
4
4
  import android.content.Context
5
- import com.facebook.react.bridge.*
6
- import com.facebook.react.modules.core.DeviceEventManagerModule
7
5
  import android.content.Intent
6
+ import android.net.Uri
8
7
  import android.os.Build
9
8
  import android.provider.Settings
10
- import android.net.Uri
11
-
12
- class CallModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaModule(reactContext) {
9
+ import com.facebook.react.bridge.*
10
+ import com.facebook.react.modules.core.DeviceEventManagerModule
13
11
 
14
- init {
12
+ class CallModule(
13
+ reactContext: ReactApplicationContext,
14
+ ) : ReactContextBaseJavaModule(reactContext) {
15
+ init {
15
16
  instance = this
16
17
  notifyReady()
17
18
  }
@@ -40,20 +41,26 @@ class CallModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaMo
40
41
  }
41
42
 
42
43
  @ReactMethod
43
- fun displayIncomingCall(uuid: String, name: String, callType: String, promise: Promise) {
44
+ fun displayIncomingCall(
45
+ uuid: String,
46
+ name: String,
47
+ callType: String,
48
+ promise: Promise,
49
+ ) {
44
50
  try {
45
- val data = mapOf(
46
- "callUuid" to uuid,
47
- "name" to name,
48
- "callType" to callType
49
- )
51
+ val data =
52
+ mapOf(
53
+ "callUuid" to uuid,
54
+ "name" to name,
55
+ "callType" to callType,
56
+ )
50
57
  NativeCallManager.handleIncomingPush(reactApplicationContext, data)
51
58
  promise.resolve(true)
52
59
  } catch (e: Exception) {
53
60
  promise.reject("CALL_ERROR", e.message)
54
61
  }
55
62
  }
56
-
63
+
57
64
  // Inside your CallModule class
58
65
  @ReactMethod
59
66
  fun checkOverlayPermission(promise: Promise) {
@@ -64,34 +71,39 @@ class CallModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaMo
64
71
  }
65
72
  }
66
73
 
67
- @ReactMethod
68
- fun requestOverlayPermission() {
69
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
70
- if (!Settings.canDrawOverlays(reactApplicationContext)) {
71
- val intent = Intent(
72
- Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
73
- Uri.parse("package:${reactApplicationContext.packageName}")
74
- )
75
- intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
76
- reactApplicationContext.startActivity(intent)
77
- }
78
- }
79
- }
74
+ @ReactMethod
75
+ fun requestOverlayPermission() {
76
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
77
+ if (!Settings.canDrawOverlays(reactApplicationContext)) {
78
+ val intent =
79
+ Intent(
80
+ Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
81
+ Uri.parse("package:${reactApplicationContext.packageName}"),
82
+ )
83
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
84
+ reactApplicationContext.startActivity(intent)
85
+ }
86
+ }
87
+ }
80
88
 
81
89
  /**
82
- * Combined Validity Check:
90
+ * Combined Validity Check:
83
91
  * Used by Headless Task to see if it should proceed with the UI.
84
92
  */
85
93
  @ReactMethod
86
- fun checkCallValidity(uuid: String, promise: Promise) {
87
- // Pass context so it can check the persistent disk storage
88
- val isValid = CallState.shouldProceed(uuid, reactApplicationContext)
89
- val isCanceled = CallState.isCanceled(uuid, reactApplicationContext)
90
-
91
- val map = Arguments.createMap().apply {
92
- putBoolean("isValid", isValid)
93
- putBoolean("isCanceled", isCanceled)
94
- }
94
+ fun checkCallValidity(
95
+ uuid: String,
96
+ promise: Promise,
97
+ ) {
98
+ // Pass context so it can check the persistent disk storage
99
+ val isValid = CallState.shouldProceed(uuid, reactApplicationContext)
100
+ val isCanceled = CallState.isCanceled(uuid, reactApplicationContext)
101
+
102
+ val map =
103
+ Arguments.createMap().apply {
104
+ putBoolean("isValid", isValid)
105
+ putBoolean("isCanceled", isCanceled)
106
+ }
95
107
  promise.resolve(map)
96
108
  }
97
109
 
@@ -112,29 +124,34 @@ class CallModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaMo
112
124
  * Useful for checking if the UI is still relevant.
113
125
  */
114
126
  @ReactMethod
115
- fun checkCallStatus(uuid: String, promise: Promise) {
127
+ fun checkCallStatus(
128
+ uuid: String,
129
+ promise: Promise,
130
+ ) {
116
131
  val isCanceled = CallState.isCanceled(uuid)
117
132
  val isCurrent = CallState.getCurrent() == uuid
118
-
119
- val map = Arguments.createMap().apply {
120
- putBoolean("isCanceled", isCanceled)
121
- putBoolean("isActive", isCurrent)
122
- putBoolean("shouldDisplay", isCurrent && !isCanceled)
123
- }
133
+
134
+ val map =
135
+ Arguments.createMap().apply {
136
+ putBoolean("isCanceled", isCanceled)
137
+ putBoolean("isActive", isCurrent)
138
+ putBoolean("shouldDisplay", isCurrent && !isCanceled)
139
+ }
124
140
  promise.resolve(map)
125
141
  }
126
142
 
127
143
  @ReactMethod
128
144
  fun endNativeCall(uuid: String) {
129
- NativeCallManager.stopRingtone()
145
+ NativeCallManager.dismissIncomingCall(this, uuid)
130
146
  CallState.clear(uuid)
131
147
  pendingCallDataMap = null
132
-
148
+
133
149
  val notificationManager = reactApplicationContext.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
134
150
  notificationManager.cancel(101)
135
151
  }
136
152
 
137
153
  @ReactMethod fun addListener(eventName: String) {}
154
+
138
155
  @ReactMethod fun removeListeners(count: Int) {}
139
156
 
140
157
  companion object {
@@ -149,14 +166,17 @@ class CallModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaMo
149
166
  return reactContext != null && reactContext.hasActiveCatalystInstance()
150
167
  }
151
168
 
152
- @JvmStatic
169
+ @JvmStatic
153
170
  fun setPendingCallData(data: Map<String, String>) {
154
- pendingCallDataMap = data
171
+ pendingCallDataMap = data
155
172
  }
156
173
 
157
- @JvmStatic
158
- fun setPendingCallData(eventName: String, data: Map<String, String>) {
159
- pendingEvents[eventName] = data
174
+ @JvmStatic
175
+ fun setPendingCallData(
176
+ eventName: String,
177
+ data: Map<String, String>,
178
+ ) {
179
+ pendingEvents[eventName] = data
160
180
  }
161
181
 
162
182
  @JvmStatic
@@ -174,23 +194,33 @@ class CallModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaMo
174
194
  }
175
195
 
176
196
  @JvmStatic
177
- fun setPendingEvent(eventName: String, data: Map<String, String>) {
197
+ fun setPendingEvent(
198
+ eventName: String,
199
+ data: Map<String, String>,
200
+ ) {
178
201
  pendingEvents[eventName] = data
179
202
  }
180
203
 
181
204
  @JvmStatic
182
- fun sendEventToJS(eventName: String, params: Any?) {
205
+ fun sendEventToJS(
206
+ eventName: String,
207
+ params: Any?,
208
+ ) {
183
209
  val reactContext = instance?.reactApplicationContext
184
- val bridgeData = when (params) {
185
- is Map<*, *> -> {
186
- val map = Arguments.createMap()
187
- params.forEach { (key, value) ->
188
- map.putString(key.toString(), value.toString())
210
+ val bridgeData =
211
+ when (params) {
212
+ is Map<*, *> -> {
213
+ val map = Arguments.createMap()
214
+ params.forEach { (key, value) ->
215
+ map.putString(key.toString(), value.toString())
216
+ }
217
+ map
218
+ }
219
+
220
+ else -> {
221
+ null
189
222
  }
190
- map
191
223
  }
192
- else -> null
193
- }
194
224
 
195
225
  if (isReady()) {
196
226
  reactContext
@@ -202,4 +232,4 @@ class CallModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaMo
202
232
  }
203
233
  }
204
234
  }
205
- }
235
+ }
@@ -1,17 +1,12 @@
1
1
  package com.rnsnativecall
2
2
 
3
- import android.app.Notification
4
- import android.app.NotificationChannel
5
- import android.app.NotificationManager
6
- import android.app.PendingIntent
3
+ import android.app.*
7
4
  import android.content.Context
8
5
  import android.content.Intent
9
- import android.content.pm.ServiceInfo
10
6
  import android.graphics.Color
11
7
  import android.media.AudioAttributes
12
8
  import android.media.RingtoneManager
13
9
  import android.os.Build
14
- import android.os.Bundle
15
10
  import android.os.Handler
16
11
  import android.os.Looper
17
12
  import androidx.core.app.NotificationCompat
@@ -22,13 +17,10 @@ object NativeCallManager {
22
17
  const val channelId = "CALL_CHANNEL_V15_URGENT"
23
18
  private var currentCallData: Map<String, String>? = null
24
19
 
25
- // Change these to internal so the Service can access them
26
- @JvmStatic internal var pendingCallNotification: android.app.Notification? = null
20
+ @JvmStatic internal var pendingCallNotification: Notification? = null
27
21
 
28
22
  @JvmStatic internal var pendingNotificationId: Int? = null
29
23
 
30
- fun getCurrentCallData(): Map<String, String>? = currentCallData
31
-
32
24
  fun handleIncomingPush(
33
25
  context: Context,
34
26
  data: Map<String, String>,
@@ -36,7 +28,6 @@ object NativeCallManager {
36
28
  Handler(Looper.getMainLooper()).post {
37
29
  val uuid = data["callUuid"] ?: return@post
38
30
  this.currentCallData = data
39
-
40
31
  val name = data["name"] ?: "Incoming Call"
41
32
  val notificationId = uuid.hashCode()
42
33
  val ringtoneUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_RINGTONE)
@@ -48,15 +39,7 @@ object NativeCallManager {
48
39
  PendingIntent.FLAG_UPDATE_CURRENT
49
40
  }
50
41
 
51
- // 1. Content Intent (Tap notification)
52
- val noOpIntent =
53
- Intent("com.rnsnativecall.ACTION_NOTIFICATION_TAP_NOOP").apply {
54
- `package` = context.packageName
55
- putExtra("EXTRA_CALL_UUID", uuid)
56
- }
57
- val contentIntent = PendingIntent.getBroadcast(context, notificationId + 3, noOpIntent, pendingFlags)
58
-
59
- // 2. Full Screen Intent (Wake lockscreen)
42
+ // Intents
60
43
  val overlayIntent =
61
44
  Intent(context, NotificationOverlayActivity::class.java).apply {
62
45
  putExtra("EXTRA_CALL_UUID", uuid)
@@ -65,7 +48,6 @@ object NativeCallManager {
65
48
  }
66
49
  val fullScreenPendingIntent = PendingIntent.getActivity(context, notificationId, overlayIntent, pendingFlags)
67
50
 
68
- // 3. Answer Action
69
51
  val answerIntent =
70
52
  Intent(context, CallActionReceiver::class.java).apply {
71
53
  action = "ACTION_ANSWER"
@@ -74,7 +56,6 @@ object NativeCallManager {
74
56
  }
75
57
  val answerPendingIntent = PendingIntent.getBroadcast(context, notificationId + 1, answerIntent, pendingFlags)
76
58
 
77
- // 4. Reject Action
78
59
  val rejectIntent =
79
60
  Intent(context, CallActionReceiver::class.java).apply {
80
61
  action = "ACTION_REJECT"
@@ -83,24 +64,25 @@ object NativeCallManager {
83
64
  }
84
65
  val rejectPendingIntent = PendingIntent.getBroadcast(context, notificationId + 2, rejectIntent, pendingFlags)
85
66
 
86
- // Setup Channel with System-Managed Sound
67
+ // Channel
87
68
  val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
88
-
89
- val channel =
90
- NotificationChannel(channelId, "Incoming Calls", NotificationManager.IMPORTANCE_HIGH).apply {
91
- lockscreenVisibility = NotificationCompat.VISIBILITY_PUBLIC
92
- enableVibration(true)
93
- setBypassDnd(true)
94
- setSound(
95
- ringtoneUri,
96
- AudioAttributes
97
- .Builder()
98
- .setUsage(AudioAttributes.USAGE_NOTIFICATION_RINGTONE)
99
- .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
100
- .build(),
101
- )
102
- }
103
- notificationManager.createNotificationChannel(channel)
69
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
70
+ val channel =
71
+ NotificationChannel(channelId, "Incoming Calls", NotificationManager.IMPORTANCE_HIGH).apply {
72
+ lockscreenVisibility = Notification.VISIBILITY_PUBLIC
73
+ enableVibration(true)
74
+ setBypassDnd(true)
75
+ setSound(
76
+ ringtoneUri,
77
+ AudioAttributes
78
+ .Builder()
79
+ .setUsage(AudioAttributes.USAGE_NOTIFICATION_RINGTONE)
80
+ .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
81
+ .build(),
82
+ )
83
+ }
84
+ notificationManager.createNotificationChannel(channel)
85
+ }
104
86
 
105
87
  val caller =
106
88
  Person
@@ -116,31 +98,19 @@ object NativeCallManager {
116
98
  .setPriority(NotificationCompat.PRIORITY_MAX)
117
99
  .setCategory(NotificationCompat.CATEGORY_CALL)
118
100
  .setOngoing(true)
119
- .setAutoCancel(false)
120
- .setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
121
- .setWhen(0)
122
- .setUsesChronometer(false)
123
- .setContentIntent(contentIntent)
124
101
  .setFullScreenIntent(fullScreenPendingIntent, true)
125
- .setStyle(
126
- NotificationCompat.CallStyle
127
- .forIncomingCall(caller, rejectPendingIntent, answerPendingIntent)
128
- .setAnswerButtonColorHint(Color.GREEN)
129
- .setDeclineButtonColorHint(Color.RED),
130
- ).setForegroundServiceBehavior(NotificationCompat.FOREGROUND_SERVICE_IMMEDIATE)
102
+ .setStyle(NotificationCompat.CallStyle.forIncomingCall(caller, rejectPendingIntent, answerPendingIntent))
103
+ .setForegroundServiceBehavior(NotificationCompat.FOREGROUND_SERVICE_IMMEDIATE)
131
104
 
132
105
  val notification = builder.build()
133
106
  pendingCallNotification = notification
134
107
  pendingNotificationId = notificationId
135
108
 
136
- // Relay to UI Service
109
+ // 1. Start the LOUD UI Service (Google allows this for PHONE_CALL)
137
110
  val uiIntent = Intent(context, CallUiForegroundService::class.java)
138
111
  ContextCompat.startForegroundService(context, uiIntent)
139
112
 
140
- // Manual notify ensures the banner pops immediately
141
- val manager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
142
- manager.notify(notificationId, notification)
143
-
113
+ // 2. Stop the SILENT Wake service (which no longer needs DATA_SYNC)
144
114
  CallForegroundService.stop(context)
145
115
  }
146
116
  }
@@ -151,55 +121,8 @@ object NativeCallManager {
151
121
  ) {
152
122
  pendingCallNotification = null
153
123
  pendingNotificationId = null
154
-
155
- val notificationManager =
156
- context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
157
-
158
- uuid?.let { notificationManager.cancel(it.hashCode()) }
159
-
124
+ val manager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
125
+ uuid?.let { manager.cancel(it.hashCode()) }
160
126
  context.stopService(Intent(context, CallUiForegroundService::class.java))
161
127
  }
162
-
163
- fun connecting(
164
- context: Context,
165
- uuid: String,
166
- name: String,
167
- callType: String,
168
- ) {
169
- val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
170
- val builder =
171
- NotificationCompat
172
- .Builder(context, channelId)
173
- .setSmallIcon(context.applicationInfo.icon)
174
- .setContentTitle(name)
175
- .setContentText("Connecting…")
176
- .setPriority(NotificationCompat.PRIORITY_MAX)
177
- .setCategory(NotificationCompat.CATEGORY_CALL)
178
- .setOngoing(true)
179
- .setStyle(NotificationCompat.BigTextStyle().bigText("Connecting…"))
180
- notificationManager.notify(uuid.hashCode(), builder.build())
181
- }
182
-
183
- fun aborting(
184
- context: Context,
185
- uuid: String,
186
- name: String,
187
- callType: String,
188
- ) {
189
- val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
190
- val builder =
191
- NotificationCompat
192
- .Builder(context, channelId)
193
- .setSmallIcon(context.applicationInfo.icon)
194
- .setContentTitle(name)
195
- .setContentText("Aborting…")
196
- .setPriority(NotificationCompat.PRIORITY_MAX)
197
- .setCategory(NotificationCompat.CATEGORY_CALL)
198
- .setOngoing(true)
199
- notificationManager.notify(uuid.hashCode(), builder.build())
200
- }
201
-
202
- fun stopRingtone() {
203
- // No-op: Sound is now managed by the Notification Channel life-cycle
204
- }
205
128
  }
@@ -5,18 +5,20 @@ import android.content.Context
5
5
  import android.content.Intent
6
6
 
7
7
  class UnlockReceiver : BroadcastReceiver() {
8
- override fun onReceive(context: Context, intent: Intent) {
8
+ override fun onReceive(
9
+ context: Context,
10
+ intent: Intent,
11
+ ) {
9
12
  android.util.Log.d("UnlockReceiver", "Device Unlocked! Action: ${intent.action}")
10
-
13
+
11
14
  if (intent.action == Intent.ACTION_USER_PRESENT) {
12
- val activeData = NativeCallManager.getCurrentCallData()
13
-
14
- if (activeData != null) {
15
- android.util.Log.d("UnlockReceiver", "Re-triggering call for: ${activeData["name"]}")
16
- NativeCallManager.handleIncomingPush(context, activeData)
17
- } else {
18
- android.util.Log.d("UnlockReceiver", "No active call data found to re-trigger.")
19
- }
15
+ // val activeData = NativeCallManager.getCurrentCallData()
16
+
17
+ // if (activeData != null) {
18
+ // NativeCallManager.handleIncomingPush(context, activeData)
19
+ // } else {
20
+ // android.util.Log.d("UnlockReceiver", "No active call data found to re-trigger.")
21
+ // }
20
22
  }
21
23
  }
22
- }
24
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rns-nativecall",
3
- "version": "1.0.7",
3
+ "version": "1.0.9",
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",
@@ -86,10 +86,9 @@ function withAndroidConfig(config) {
86
86
  const permissions = [
87
87
  'android.permission.VIBRATE',
88
88
  'android.permission.FOREGROUND_SERVICE',
89
- 'android.permission.FOREGROUND_SERVICE_PHONE_CALL', // ADDED THIS
90
- 'android.permission.FOREGROUND_SERVICE_DATA_SYNC', // ADDED THIS
91
- 'android.permission.USE_FULL_SCREEN_INTENT', // ADDED THIS
92
- 'android.permission.MANAGE_ONGOING_CALLS', // ADDED THIS
89
+ 'android.permission.FOREGROUND_SERVICE_PHONE_CALL',
90
+ 'android.permission.USE_FULL_SCREEN_INTENT',
91
+ 'android.permission.MANAGE_ONGOING_CALLS',
93
92
  'android.permission.POST_NOTIFICATIONS',
94
93
  'android.permission.WAKE_LOCK',
95
94
  'android.permission.DISABLE_KEYGUARD',
@@ -108,7 +107,7 @@ function withAndroidConfig(config) {
108
107
 
109
108
  const services = [
110
109
  { name: 'com.rnsnativecall.CallMessagingService', exported: 'false', filter: 'com.google.firebase.MESSAGING_EVENT' },
111
- { name: 'com.rnsnativecall.CallForegroundService', type: 'dataSync' },
110
+ { name: 'com.rnsnativecall.CallForegroundService' },
112
111
  { name: 'com.rnsnativecall.CallUiForegroundService', type: 'phoneCall' },
113
112
  { name: 'com.rnsnativecall.CallHeadlessTask' }
114
113
  ];