rns-nativecall 0.1.7 → 0.1.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.
@@ -10,29 +10,45 @@ class AcceptCallActivity : Activity() {
10
10
  override fun onCreate(savedInstanceState: Bundle?) {
11
11
  super.onCreate(savedInstanceState)
12
12
 
13
- // 1. CLEAR THE PILL IMMEDIATELY
13
+ // 1. CLEAR THE NOTIFICATION
14
+ // Use the same ID (101) used in NativeCallManager
14
15
  val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
15
16
  notificationManager.cancel(101)
16
17
 
17
- // 2. PREPARE DATA FOR JS
18
+ // 2. EXTRACT DATA SAFELY
19
+ // We iterate through all extras and convert them to Strings for the JS Map
18
20
  val dataMap = mutableMapOf<String, String>()
19
- intent.extras?.keySet()?.forEach { key ->
20
- intent.getStringExtra(key)?.let { dataMap[key] = it }
21
+ val extras = intent.extras
22
+ extras?.keySet()?.forEach { key ->
23
+ val value = extras.get(key)
24
+ if (value != null) {
25
+ dataMap[key] = value.toString()
26
+ }
21
27
  }
22
28
 
23
- // 3. SEND EVENT TO REACT NATIVE
29
+ // 3. EMIT EVENT TO REACT NATIVE
30
+ // This handles the case where the app is already in the background/foreground
24
31
  CallModule.sendEventToJS("onCallAccepted", dataMap)
25
32
 
26
- // 4. OPEN THE MAIN APP
33
+ // 4. LAUNCH OR BRING MAIN APP TO FOREGROUND
27
34
  val launchIntent = packageManager.getLaunchIntentForPackage(packageName)
28
- launchIntent?.apply {
29
- addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_SINGLE_TOP)
30
- putExtras(intent.extras ?: Bundle())
31
- putExtra("navigatingToCall", true)
35
+ if (launchIntent != null) {
36
+ launchIntent.apply {
37
+ // FLAG_ACTIVITY_SINGLE_TOP: Updates the app if it's already open
38
+ // FLAG_ACTIVITY_NEW_TASK: Required when starting from a non-activity context
39
+ addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_SINGLE_TOP)
40
+
41
+ // Copy all call data to the launch intent
42
+ putExtras(extras ?: Bundle())
43
+
44
+ // Helper flag for your React Native logic
45
+ putExtra("navigatingToCall", true)
46
+ }
47
+ startActivity(launchIntent)
32
48
  }
33
- startActivity(launchIntent)
34
49
 
35
- // 5. KILL THIS INVISIBLE WINDOW
50
+ // 5. FINISH TRAMPOLINE
51
+ // This ensures this invisible activity doesn't stay in the "Recent Apps" list
36
52
  finish()
37
53
  }
38
54
  }
@@ -1,14 +1,34 @@
1
- ///
2
- // Users/bush/Desktop/Packages/rns-nativecall/android/src/main/java/com/rnsnativecall/CallMessagingService.kt
3
1
  package com.rnsnativecall
4
2
 
3
+ import android.app.ActivityManager
4
+ import android.content.Context
5
5
  import com.google.firebase.messaging.FirebaseMessagingService
6
6
  import com.google.firebase.messaging.RemoteMessage
7
7
 
8
8
  class CallMessagingService : FirebaseMessagingService() {
9
9
 
10
10
  override fun onMessageReceived(remoteMessage: RemoteMessage) {
11
- // Directly forward to your native call manager
12
- NativeCallManager.handleIncomingPush(applicationContext, remoteMessage.data)
11
+ if (isAppInForeground(applicationContext)) {
12
+ // 1. App is OPEN: Don't show the system pill.
13
+ // Just send the data to your React Native listeners.
14
+ CallModule.sendEventToJS("onCallReceived", remoteMessage.data)
15
+ } else {
16
+ // 2. App is CLOSED/BACKGROUND: Show the sticky system pill.
17
+ NativeCallManager.handleIncomingPush(applicationContext, remoteMessage.data)
18
+ }
13
19
  }
14
- }
20
+
21
+ private fun isAppInForeground(context: Context): Boolean {
22
+ val activityManager = context.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
23
+ val appProcesses = activityManager.runningAppProcesses ?: return false
24
+ val packageName = context.packageName
25
+
26
+ for (appProcess in appProcesses) {
27
+ if (appProcess.importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND &&
28
+ appProcess.processName == packageName) {
29
+ return true
30
+ }
31
+ }
32
+ return false
33
+ }
34
+ }
@@ -1,5 +1,6 @@
1
1
  package com.rnsnativecall
2
2
 
3
+ import android.app.NotificationManager
3
4
  import android.content.ComponentName
4
5
  import android.content.Context
5
6
  import android.content.Intent
@@ -9,11 +10,9 @@ import android.telecom.DisconnectCause
9
10
  import android.telecom.PhoneAccount
10
11
  import android.telecom.PhoneAccountHandle
11
12
  import android.telecom.TelecomManager
13
+ import android.widget.Toast
12
14
  import com.facebook.react.bridge.*
13
15
  import com.facebook.react.modules.core.DeviceEventManagerModule
14
- import android.widget.Toast
15
- import com.google.android.material.snackbar.Snackbar
16
- import android.view.View
17
16
 
18
17
  class CallModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaModule(reactContext) {
19
18
 
@@ -30,45 +29,33 @@ class CallModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaMo
30
29
  }
31
30
 
32
31
  private fun registerPhoneAccount() {
33
- val telecomManager = reactApplicationContext.getSystemService(Context.TELECOM_SERVICE) as TelecomManager
34
- val phoneAccountHandle = getPhoneAccountHandle()
35
-
36
- val appName = reactApplicationContext.applicationInfo.loadLabel(reactApplicationContext.packageManager).toString()
37
-
38
- // ✅ FIXED: Removed CAPABILITY_CALL_PROVIDER
39
- // Self-managed apps only need these specific flags
40
- val capabilities = PhoneAccount.CAPABILITY_VIDEO_CALLING or
41
- PhoneAccount.CAPABILITY_SUPPORTS_VIDEO_CALLING
42
-
43
- val phoneAccount = PhoneAccount.builder(phoneAccountHandle, appName)
44
- .setCapabilities(capabilities)
45
- .setShortDescription(appName)
46
- .addSupportedUriScheme("sip")
47
- .addSupportedUriScheme("tel")
48
- .setCapabilities(PhoneAccount.CAPABILITY_SELF_MANAGED)
49
- .setCapabilities(PhoneAccount.CAPABILITY_CALL_PROVIDER)
50
- .build()
51
-
52
- telecomManager.registerPhoneAccount(phoneAccount)
53
- }
54
-
55
- @ReactMethod
56
- fun displayIncomingCall(uuid: String, number: String, name: String, hasVideo: Boolean, playRing: Boolean, promise: Promise) {
57
32
  val telecomManager = reactApplicationContext.getSystemService(Context.TELECOM_SERVICE) as TelecomManager
58
33
  val phoneAccountHandle = getPhoneAccountHandle()
34
+ val appName = reactApplicationContext.applicationInfo.loadLabel(reactApplicationContext.packageManager).toString()
59
35
 
60
- val extras = Bundle().apply {
61
- putParcelable(TelecomManager.EXTRA_INCOMING_CALL_ADDRESS, Uri.fromParts("sip", number, null))
62
- putString("EXTRA_CALL_UUID", uuid)
36
+ val capabilities = PhoneAccount.CAPABILITY_VIDEO_CALLING or
37
+ PhoneAccount.CAPABILITY_SUPPORTS_VIDEO_CALLING or
38
+ PhoneAccount.CAPABILITY_SELF_MANAGED
63
39
 
64
- putString("EXTRA_CALLER_NAME", name)
65
- putString(TelecomManager.EXTRA_CALL_SUBJECT, number)
66
- putBoolean("EXTRA_PLAY_RING", false)
67
- putBoolean(TelecomManager.EXTRA_START_CALL_WITH_VIDEO_STATE, hasVideo)
40
+ val phoneAccount = PhoneAccount.builder(phoneAccountHandle, appName)
41
+ .setCapabilities(capabilities)
42
+ .setShortDescription(appName)
43
+ .addSupportedUriScheme("sip")
44
+ .addSupportedUriScheme("tel")
45
+ .build()
68
46
 
69
- }
47
+ telecomManager.registerPhoneAccount(phoneAccount)
48
+ }
49
+
50
+ @ReactMethod
51
+ fun displayIncomingCall(uuid: String, name: String, callType: String, promise: Promise) {
70
52
  try {
71
- telecomManager.addNewIncomingCall(phoneAccountHandle, extras)
53
+ val data = mapOf(
54
+ "callUuid" to uuid,
55
+ "name" to name,
56
+ "callType" to callType
57
+ )
58
+ NativeCallManager.handleIncomingPush(reactApplicationContext, data)
72
59
  promise.resolve(true)
73
60
  } catch (e: Exception) {
74
61
  promise.reject("CALL_ERROR", e.message)
@@ -77,68 +64,56 @@ class CallModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaMo
77
64
 
78
65
  @ReactMethod
79
66
  fun endNativeCall(uuid: String) {
67
+ val notificationManager = reactApplicationContext.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
68
+ notificationManager.cancel(101)
69
+
80
70
  val connection = MyConnectionService.getConnection(uuid)
81
71
  connection?.let {
82
- it.setDisconnected(DisconnectCause(DisconnectCause.LOCAL))
72
+ it.setDisconnected(DisconnectCause(DisconnectCause.MISSED))
83
73
  it.destroy()
84
74
  MyConnectionService.removeConnection(uuid)
85
75
  }
86
76
  }
87
77
 
88
- //done // val fallbackIntent = Intent(Intent.ACTION_MAIN).apply {
89
- // addCategory(Intent.CATEGORY_LAUNCHER)
90
- // component = ComponentName("com.android.phone", "com.android.phone.CallFeaturesSetting")
91
- // addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
92
- // }
93
- // reactApplicationContext.startActivity(fallbackIntent)
94
-
95
78
  @ReactMethod
96
- fun checkTelecomPermissions(promise: Promise) {
97
- val telecomManager = reactApplicationContext.getSystemService(Context.TELECOM_SERVICE) as TelecomManager
98
- val phoneAccountHandle = getPhoneAccountHandle()
99
- val appName = reactApplicationContext.applicationInfo.loadLabel(reactApplicationContext.packageManager).toString()
100
-
101
- val account = telecomManager.getPhoneAccount(phoneAccountHandle)
102
- if (account != null && account.isEnabled) {
103
- promise.resolve(true)
104
- } else {
105
- try {
106
- // Show a quick message to guide the user
107
- val text = "Tap 'Active calling accounts' and then enable $appName"
108
- val duration = Toast.LENGTH_LONG
109
- val toast = Toast.makeText(reactApplicationContext, text, duration)
110
-
111
- toast.show()
112
-
113
- // Show again after 3.5s
114
- android.os.Handler(android.os.Looper.getMainLooper()).postDelayed({
115
- toast.show()
116
- }, 3500)
117
-
118
-
119
- // Open Calling Accounts settings
120
- val intent = Intent(TelecomManager.ACTION_CHANGE_PHONE_ACCOUNTS)
121
- intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
122
- reactApplicationContext.startActivity(intent)
79
+ fun checkTelecomPermissions(promise: Promise) {
80
+ val telecomManager = reactApplicationContext.getSystemService(Context.TELECOM_SERVICE) as TelecomManager
81
+ val phoneAccountHandle = getPhoneAccountHandle()
82
+ val appName = reactApplicationContext.applicationInfo.loadLabel(reactApplicationContext.packageManager).toString()
123
83
 
124
- promise.resolve(false)
125
- } catch (e: Exception) {
126
- promise.reject("PERMISSION_ERROR", "Could not open settings: ${e.message}")
84
+ val account = telecomManager.getPhoneAccount(phoneAccountHandle)
85
+ if (account != null && account.isEnabled) {
86
+ promise.resolve(true)
87
+ } else {
88
+ try {
89
+ Toast.makeText(reactApplicationContext, "Tap 'Active calling accounts' and then enable $appName", Toast.LENGTH_LONG).show()
90
+ val intent = Intent(TelecomManager.ACTION_CHANGE_PHONE_ACCOUNTS)
91
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
92
+ reactApplicationContext.startActivity(intent)
93
+ promise.resolve(false)
94
+ } catch (e: Exception) {
95
+ promise.reject("PERMISSION_ERROR", e.message)
96
+ }
127
97
  }
128
98
  }
129
- }
130
99
 
100
+ @ReactMethod
101
+ fun getInitialCallData(promise: Promise) {
102
+ promise.resolve(pendingCallData)
103
+ pendingCallData = null // Clear after consumption
104
+ }
131
105
 
132
106
  @ReactMethod fun addListener(eventName: String) {}
133
107
  @ReactMethod fun removeListeners(count: Int) {}
134
108
 
135
109
  companion object {
136
110
  private var instance: CallModule? = null
111
+ private var pendingCallData: WritableMap? = null
137
112
 
138
113
  @JvmStatic
139
114
  fun sendEventToJS(eventName: String, params: Any?) {
140
- val reactContext = instance?.reactApplicationContext ?: return
141
-
115
+ val reactContext = instance?.reactApplicationContext
116
+
142
117
  val bridgeData = when (params) {
143
118
  is Map<*, *> -> {
144
119
  val map = Arguments.createMap()
@@ -148,14 +123,21 @@ android.os.Handler(android.os.Looper.getMainLooper()).postDelayed({
148
123
  map
149
124
  }
150
125
  is String -> {
151
- Arguments.createMap().apply { putString("callUUID", params) }
126
+ Arguments.createMap().apply { putString("callUuid", params) }
152
127
  }
153
128
  else -> null
154
129
  }
155
130
 
156
- reactContext
157
- .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)
158
- ?.emit(eventName, bridgeData)
131
+ if (reactContext != null && reactContext.hasActiveCatalystInstance()) {
132
+ reactContext
133
+ .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)
134
+ ?.emit(eventName, bridgeData)
135
+ } else {
136
+ // If app is dead/cold-starting, cache the accept event
137
+ if (eventName == "onCallAccepted") {
138
+ pendingCallData = bridgeData
139
+ }
140
+ }
159
141
  }
160
142
  }
161
143
  }
@@ -11,49 +11,55 @@ import androidx.core.app.NotificationCompat
11
11
  object NativeCallManager {
12
12
 
13
13
  fun handleIncomingPush(context: Context, data: Map<String, String>) {
14
- val uuid = data["callId"] ?: return
14
+ val uuid = data["callUuid"] ?: return
15
15
  val name = data["name"] ?: "Incoming Call"
16
16
  val callType = data["callType"] ?: "audio"
17
17
 
18
- val pendingFlags = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
19
- PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
18
+ // Use MUTABLE flag to ensure extras are properly passed on Android 12+
19
+ val pendingFlags = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
20
+ PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_MUTABLE
20
21
  } else {
21
22
  PendingIntent.FLAG_UPDATE_CURRENT
22
23
  }
23
24
 
24
- // 1. DUMMY INTENT: This is the key to making the Pill "Sticky".
25
- // It prevents the notification from disappearing but doesn't open a screen.
25
+ // 1. DUMMY INTENT: Keeps the notification "Sticky" (Persistent)
26
26
  val dummyIntent = PendingIntent.getActivity(
27
27
  context,
28
28
  0,
29
29
  Intent(),
30
- pendingFlags
30
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) PendingIntent.FLAG_IMMUTABLE else 0
31
31
  )
32
32
 
33
- // 2. Accept Action
34
- val acceptIntent = Intent(context, AcceptCallActivity::class.java).apply {
35
- action = "ACTION_ACCEPT"
36
- putExtra("EXTRA_CALL_UUID", uuid)
37
- data.forEach { (key, value) -> putExtra(key, value) }
38
- // Crucial for launching from a notification
39
- flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP
40
- }
33
+ // 2. Accept Action - Use unique action and requestCode
34
+ val acceptIntent = Intent(context, AcceptCallActivity::class.java).apply {
35
+ // Unique action string prevents intent caching issues
36
+ action = "ACTION_ACCEPT_$uuid"
37
+ putExtra("EXTRA_CALL_UUID", uuid)
38
+ // Flatten the map into the intent extras
39
+ data.forEach { (key, value) -> putExtra(key, value) }
40
+ flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP
41
+ }
42
+
43
+ val acceptPendingIntent = PendingIntent.getActivity(
44
+ context,
45
+ uuid.hashCode(), // Unique RequestCode
46
+ acceptIntent,
47
+ pendingFlags
48
+ )
41
49
 
42
- // Keep this as getActivity
43
- val acceptPendingIntent = PendingIntent.getActivity(
44
- context,
45
- 1001,
46
- acceptIntent,
47
- pendingFlags
48
- )
49
- // 3. Reject Action
50
+ // 3. Reject Action - Use unique action and requestCode
50
51
  val rejectIntent = Intent(context, CallActionReceiver::class.java).apply {
51
- action = "ACTION_REJECT"
52
+ action = "ACTION_REJECT_$uuid"
52
53
  putExtra("EXTRA_CALL_UUID", uuid)
53
54
  }
54
- val rejectPendingIntent = PendingIntent.getBroadcast(context, 1002, rejectIntent, pendingFlags)
55
+ val rejectPendingIntent = PendingIntent.getBroadcast(
56
+ context,
57
+ uuid.hashCode() + 1, // Unique RequestCode
58
+ rejectIntent,
59
+ pendingFlags
60
+ )
55
61
 
56
- // 4. Setup Channel (Ensure Importance is HIGH)
62
+ // 4. Setup Channel
57
63
  val channelId = "CALL_CHANNEL_ID"
58
64
  val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
59
65
 
@@ -61,7 +67,6 @@ val acceptPendingIntent = PendingIntent.getActivity(
61
67
  val channel = NotificationChannel(channelId, "Incoming Calls", NotificationManager.IMPORTANCE_HIGH).apply {
62
68
  description = "Shows incoming call notifications"
63
69
  enableVibration(true)
64
- // Critical for bypassing "Do Not Disturb"
65
70
  setBypassDnd(true)
66
71
  lockscreenVisibility = NotificationCompat.VISIBILITY_PUBLIC
67
72
  }
@@ -75,10 +80,11 @@ val acceptPendingIntent = PendingIntent.getActivity(
75
80
  .setContentText(name)
76
81
  .setPriority(NotificationCompat.PRIORITY_MAX)
77
82
  .setCategory(NotificationCompat.CATEGORY_CALL)
78
- .setOngoing(true) // Prevents user from swiping it away
83
+ .setOngoing(true)
79
84
  .setAutoCancel(false)
85
+ .setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
80
86
 
81
- // This is what keeps the Heads-Up "Pill" visible indefinitely:
87
+ // This hack keeps the Heads-Up "Pill" visible until action is taken
82
88
  .setFullScreenIntent(dummyIntent, true)
83
89
 
84
90
  .addAction(0, "Answer", acceptPendingIntent)
package/index.d.ts CHANGED
@@ -18,18 +18,14 @@ export interface CallHandlerType {
18
18
  /**
19
19
  * Display an incoming call UI using ConnectionService (Android) or CallKit (iOS).
20
20
  * @param uuid Unique call identifier
21
- * @param number Caller number (or URI)
22
21
  * @param name Caller display name
23
- * @param hasVideo True if video call (default: false)
24
- * @param shouldRing True to play native ringtone (default: true)
22
+ * @param callType True if video call (default: false)
25
23
  * @returns Promise resolving to true if successfully displayed
26
24
  */
27
25
  displayCall(
28
26
  uuid: string,
29
- number: string,
30
27
  name: string,
31
- hasVideo?: boolean,
32
- shouldRing?: boolean
28
+ callType: string,
33
29
  ): Promise<boolean>;
34
30
 
35
31
  /**
package/index.js CHANGED
@@ -35,7 +35,7 @@ export async function ensureAndroidPermissions() {
35
35
  }
36
36
 
37
37
  export const CallHandler = {
38
- displayCall: async (uuid, number, name, hasVideo = false, shouldRing = true) => {
38
+ displayCall: async (uuid, name, callType) => {
39
39
  if (!CallModule) return false;
40
40
 
41
41
  // if (Platform.OS === 'android') {
@@ -46,10 +46,8 @@ export const CallHandler = {
46
46
  try {
47
47
  return await CallModule.displayIncomingCall(
48
48
  uuid.toLowerCase().trim(),
49
- number,
50
49
  name,
51
- hasVideo,
52
- shouldRing
50
+ callType,
53
51
  );
54
52
  } catch (e) {
55
53
  // console.log("Native Call Error:", e);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rns-nativecall",
3
- "version": "0.1.7",
3
+ "version": "0.1.8",
4
4
  "description": "RNS nativecall component with native Android/iOS for handling native call ui, when app is not open or open.",
5
5
  "main": "index.js",
6
6
  "types": "index.d.ts",
@@ -1,11 +1,41 @@
1
- const { withAndroidManifest, withInfoPlist, withPlugins } = require('@expo/config-plugins');
1
+ const { withAndroidManifest, withInfoPlist, withPlugins, withMainActivity } = require('@expo/config-plugins');
2
2
 
3
+ /** 1. ANDROID MAIN ACTIVITY MOD (Fixes data passing) **/
4
+ function withMainActivityDataFix(config) {
5
+ return withMainActivity(config, (config) => {
6
+ let contents = config.modResults.contents;
7
+
8
+ // Add necessary import
9
+ if (!contents.includes('import android.content.Intent')) {
10
+ contents = contents.replace(/package .*/, (match) => `${match}\n\nimport android.content.Intent`);
11
+ }
12
+
13
+ // Add onNewIntent override to ensure data is updated when app is already open
14
+ const onNewIntentCode = `
15
+ override fun onNewIntent(intent: Intent) {
16
+ super.onNewIntent(intent)
17
+ setIntent(intent)
18
+ }
19
+ `;
20
+
21
+ if (!contents.includes('override fun onNewIntent')) {
22
+ // Find the last closing brace and insert before it
23
+ const lastBraceIndex = contents.lastIndexOf('}');
24
+ contents = contents.slice(0, lastBraceIndex) + onNewIntentCode + contents.slice(lastBraceIndex);
25
+ }
26
+
27
+ config.modResults.contents = contents;
28
+ return config;
29
+ });
30
+ }
31
+
32
+ /** 2. ANDROID MANIFEST CONFIG **/
3
33
  function withAndroidConfig(config) {
4
34
  return withAndroidManifest(config, (config) => {
5
35
  const manifest = config.modResults;
6
36
  const application = manifest.manifest.application[0];
7
37
 
8
- // 1. Permissions
38
+ // Permissions
9
39
  const permissions = [
10
40
  'android.permission.READ_PHONE_NUMBERS',
11
41
  'android.permission.CALL_PHONE',
@@ -23,22 +53,16 @@ function withAndroidConfig(config) {
23
53
  }
24
54
  });
25
55
 
26
- // 2. Activities (UI Components)
56
+ // Activities
27
57
  application.activity = application.activity || [];
28
58
 
29
- // IncomingCallActivity (Optional lock screen UI)
30
- if (!application.activity.some(a => a.$['android:name'] === 'com.rnsnativecall.IncomingCallActivity')) {
31
- application.activity.push({
32
- $: {
33
- 'android:name': 'com.rnsnativecall.IncomingCallActivity',
34
- 'android:showOnLockScreen': 'true',
35
- 'android:launchMode': 'singleInstance',
36
- 'android:theme': '@style/Theme.AppCompat.Light.NoActionBar'
37
- }
38
- });
59
+ // Update MainActivity to use singleTask for better call handling
60
+ const mainActivity = application.activity.find(a => a.$['android:name'] === '.MainActivity');
61
+ if (mainActivity) {
62
+ mainActivity.$['android:launchMode'] = 'singleTask';
39
63
  }
40
64
 
41
- // AcceptCallActivity (The "Trampoline" that fixes the Answer button)
65
+ // AcceptCallActivity (Trampoline)
42
66
  if (!application.activity.some(a => a.$['android:name'] === 'com.rnsnativecall.AcceptCallActivity')) {
43
67
  application.activity.push({
44
68
  $: {
@@ -52,7 +76,7 @@ function withAndroidConfig(config) {
52
76
  });
53
77
  }
54
78
 
55
- // 3. Services (FCM and Telecom)
79
+ // Services
56
80
  application.service = application.service || [];
57
81
  const services = [
58
82
  { name: 'com.rnsnativecall.MyConnectionService', permission: 'android.permission.BIND_CONNECTION_SERVICE', action: 'android.telecom.ConnectionService' },
@@ -68,7 +92,7 @@ function withAndroidConfig(config) {
68
92
  }
69
93
  });
70
94
 
71
- // 4. Receivers (The Decline Button)
95
+ // Receivers
72
96
  application.receiver = application.receiver || [];
73
97
  if (!application.receiver.some(r => r.$['android:name'] === 'com.rnsnativecall.CallActionReceiver')) {
74
98
  application.receiver.push({
@@ -80,7 +104,7 @@ function withAndroidConfig(config) {
80
104
  });
81
105
  }
82
106
 
83
- /** IOS Config remains the same **/
107
+ /** 3. IOS CONFIG **/
84
108
  function withIosConfig(config) {
85
109
  return withInfoPlist(config, (config) => {
86
110
  const infoPlist = config.modResults;
@@ -91,5 +115,5 @@ function withIosConfig(config) {
91
115
  }
92
116
 
93
117
  module.exports = (config) => {
94
- return withPlugins(config, [withAndroidConfig, withIosConfig]);
118
+ return withPlugins(config, [withAndroidConfig, withMainActivityDataFix, withIosConfig]);
95
119
  };