rns-nativecall 0.3.1 → 0.3.2
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,12 +10,13 @@ class AcceptCallActivity : Activity() {
|
|
|
10
10
|
override fun onCreate(savedInstanceState: Bundle?) {
|
|
11
11
|
super.onCreate(savedInstanceState)
|
|
12
12
|
NativeCallManager.stopRingtone()
|
|
13
|
-
|
|
14
13
|
// 1. CLEAR THE NOTIFICATION
|
|
14
|
+
// Use the same ID (101) used in NativeCallManager
|
|
15
15
|
val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
|
|
16
16
|
notificationManager.cancel(101)
|
|
17
17
|
|
|
18
18
|
// 2. EXTRACT DATA SAFELY
|
|
19
|
+
// We iterate through all extras and convert them to Strings for the JS Map
|
|
19
20
|
val dataMap = mutableMapOf<String, String>()
|
|
20
21
|
val extras = intent.extras
|
|
21
22
|
extras?.keySet()?.forEach { key ->
|
|
@@ -25,26 +26,30 @@ class AcceptCallActivity : Activity() {
|
|
|
25
26
|
}
|
|
26
27
|
}
|
|
27
28
|
|
|
28
|
-
//
|
|
29
|
-
//
|
|
30
|
-
// We save this in a static variable inside CallModule so getInitialCallData() can find it
|
|
31
|
-
CallModule.setPendingCallData(dataMap)
|
|
32
|
-
|
|
33
|
-
// 4. EMIT EVENT TO REACT NATIVE
|
|
34
|
-
// This works if the app is already running
|
|
29
|
+
// 3. EMIT EVENT TO REACT NATIVE
|
|
30
|
+
// This handles the case where the app is already in the background/foreground
|
|
35
31
|
CallModule.sendEventToJS("onCallAccepted", dataMap)
|
|
36
32
|
|
|
37
|
-
//
|
|
33
|
+
// 4. LAUNCH OR BRING MAIN APP TO FOREGROUND
|
|
38
34
|
val launchIntent = packageManager.getLaunchIntentForPackage(packageName)
|
|
39
35
|
if (launchIntent != null) {
|
|
40
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
|
|
41
39
|
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_SINGLE_TOP)
|
|
40
|
+
|
|
41
|
+
// Copy all call data to the launch intent
|
|
42
42
|
putExtras(extras ?: Bundle())
|
|
43
|
+
|
|
44
|
+
// Helper flag for your React Native logic
|
|
43
45
|
putExtra("navigatingToCall", true)
|
|
44
46
|
}
|
|
45
47
|
startActivity(launchIntent)
|
|
46
48
|
}
|
|
47
|
-
|
|
49
|
+
|
|
50
|
+
CallModule.setPendingCallData(dataMap)
|
|
51
|
+
// 5. FINISH TRAMPOLINE
|
|
52
|
+
// This ensures this invisible activity doesn't stay in the "Recent Apps" list
|
|
48
53
|
finish()
|
|
49
54
|
}
|
|
50
55
|
}
|
|
@@ -2,6 +2,8 @@ package com.rnsnativecall
|
|
|
2
2
|
|
|
3
3
|
import android.app.ActivityManager
|
|
4
4
|
import android.content.Context
|
|
5
|
+
import android.os.Handler
|
|
6
|
+
import android.os.Looper
|
|
5
7
|
import com.google.firebase.messaging.FirebaseMessagingService
|
|
6
8
|
import com.google.firebase.messaging.RemoteMessage
|
|
7
9
|
|
|
@@ -10,17 +12,33 @@ class CallMessagingService : FirebaseMessagingService() {
|
|
|
10
12
|
override fun onMessageReceived(remoteMessage: RemoteMessage) {
|
|
11
13
|
val data = remoteMessage.data
|
|
12
14
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
// ALWAYS trigger the native logic.
|
|
17
|
-
// The NativeCallManager should decide if it needs to show a Notification
|
|
18
|
-
// or just play a sound. This ensures the "Ringtone" starts natively
|
|
19
|
-
// which is more reliable than JS audio.
|
|
20
|
-
NativeCallManager.handleIncomingPush(applicationContext, data)
|
|
21
|
-
|
|
22
|
-
// Still notify JS so it can update the UI or pre-load the call screen
|
|
15
|
+
if (isAppInForeground(applicationContext)) {
|
|
16
|
+
// 1. App is OPEN: Send to JS immediately (No delay)
|
|
23
17
|
CallModule.sendEventToJS("onCallReceived", data)
|
|
18
|
+
} else {
|
|
19
|
+
// 2. App is CLOSED/BACKGROUND: Delay the system pill by 18 seconds
|
|
20
|
+
val handler = Handler(Looper.getMainLooper())
|
|
21
|
+
handler.postDelayed({
|
|
22
|
+
// Re-check: If the user opened the app during these 18 seconds,
|
|
23
|
+
// we might want to cancel the pill.
|
|
24
|
+
if (!isAppInForeground(applicationContext)) {
|
|
25
|
+
NativeCallManager.handleIncomingPush(applicationContext, data)
|
|
26
|
+
}
|
|
27
|
+
}, 18000)
|
|
24
28
|
}
|
|
25
29
|
}
|
|
30
|
+
|
|
31
|
+
private fun isAppInForeground(context: Context): Boolean {
|
|
32
|
+
val activityManager = context.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
|
|
33
|
+
val appProcesses = activityManager.runningAppProcesses ?: return false
|
|
34
|
+
val packageName = context.packageName
|
|
35
|
+
|
|
36
|
+
for (appProcess in appProcesses) {
|
|
37
|
+
if (appProcess.importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND &&
|
|
38
|
+
appProcess.processName == packageName) {
|
|
39
|
+
return true
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
return false
|
|
43
|
+
}
|
|
26
44
|
}
|
|
@@ -23,7 +23,7 @@ class CallModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaMo
|
|
|
23
23
|
writableMap
|
|
24
24
|
}
|
|
25
25
|
promise.resolve(data)
|
|
26
|
-
pendingCallDataMap = null
|
|
26
|
+
pendingCallDataMap = null // Clear after use
|
|
27
27
|
}
|
|
28
28
|
|
|
29
29
|
@ReactMethod
|
|
@@ -34,6 +34,7 @@ class CallModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaMo
|
|
|
34
34
|
"name" to name,
|
|
35
35
|
"callType" to callType
|
|
36
36
|
)
|
|
37
|
+
// Use reactApplicationContext safely here
|
|
37
38
|
NativeCallManager.handleIncomingPush(reactApplicationContext, data)
|
|
38
39
|
promise.resolve(true)
|
|
39
40
|
} catch (e: Exception) {
|
|
@@ -43,17 +44,13 @@ class CallModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaMo
|
|
|
43
44
|
|
|
44
45
|
@ReactMethod
|
|
45
46
|
fun endNativeCall(uuid: String) {
|
|
46
|
-
// Since we aren't using Telecom, we just stop the ringtone and clear notification
|
|
47
47
|
NativeCallManager.stopRingtone()
|
|
48
48
|
pendingCallDataMap = null
|
|
49
49
|
|
|
50
50
|
val notificationManager = reactApplicationContext.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
|
|
51
51
|
notificationManager.cancel(101)
|
|
52
|
-
|
|
53
|
-
// No more ConnectionService to destroy!
|
|
54
52
|
}
|
|
55
53
|
|
|
56
|
-
// This is a dummy now since we don't need PhoneAccount permissions anymore
|
|
57
54
|
@ReactMethod
|
|
58
55
|
fun checkTelecomPermissions(promise: Promise) {
|
|
59
56
|
promise.resolve(true)
|
|
@@ -66,6 +63,7 @@ class CallModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaMo
|
|
|
66
63
|
private var instance: CallModule? = null
|
|
67
64
|
private var pendingCallDataMap: Map<String, String>? = null
|
|
68
65
|
|
|
66
|
+
// This is the static gate accessible from AcceptCallActivity
|
|
69
67
|
@JvmStatic
|
|
70
68
|
fun setPendingCallData(data: Map<String, String>) {
|
|
71
69
|
pendingCallDataMap = data
|
|
@@ -86,11 +84,15 @@ class CallModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaMo
|
|
|
86
84
|
else -> null
|
|
87
85
|
}
|
|
88
86
|
|
|
87
|
+
// If app is alive, send via Bridge
|
|
89
88
|
if (reactContext != null && reactContext.hasActiveCatalystInstance()) {
|
|
90
89
|
reactContext
|
|
91
90
|
.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)
|
|
92
91
|
?.emit(eventName, bridgeData)
|
|
93
|
-
}
|
|
92
|
+
}
|
|
93
|
+
// If app is in killed state, save for polling
|
|
94
|
+
else if (eventName == "onCallAccepted" && params is Map<*, *>) {
|
|
95
|
+
@Suppress("UNCHECKED_CAST")
|
|
94
96
|
setPendingCallData(params as Map<String, String>)
|
|
95
97
|
}
|
|
96
98
|
}
|
package/package.json
CHANGED