rns-nativecall 0.2.8 → 0.3.1
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.
|
@@ -1,14 +1,7 @@
|
|
|
1
1
|
package com.rnsnativecall
|
|
2
2
|
|
|
3
3
|
import android.app.NotificationManager
|
|
4
|
-
import android.content.ComponentName
|
|
5
4
|
import android.content.Context
|
|
6
|
-
import android.content.Intent
|
|
7
|
-
import android.telecom.DisconnectCause
|
|
8
|
-
import android.telecom.PhoneAccount
|
|
9
|
-
import android.telecom.PhoneAccountHandle
|
|
10
|
-
import android.telecom.TelecomManager
|
|
11
|
-
import android.widget.Toast
|
|
12
5
|
import com.facebook.react.bridge.*
|
|
13
6
|
import com.facebook.react.modules.core.DeviceEventManagerModule
|
|
14
7
|
|
|
@@ -16,16 +9,12 @@ class CallModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaMo
|
|
|
16
9
|
|
|
17
10
|
init {
|
|
18
11
|
instance = this
|
|
19
|
-
registerPhoneAccount()
|
|
20
12
|
}
|
|
21
13
|
|
|
22
14
|
override fun getName() = "CallModule"
|
|
23
15
|
|
|
24
|
-
// ... getPhoneAccountHandle and registerPhoneAccount remain the same ...
|
|
25
|
-
|
|
26
16
|
@ReactMethod
|
|
27
17
|
fun getInitialCallData(promise: Promise) {
|
|
28
|
-
// Convert our stored simple Map into a WritableMap for JS
|
|
29
18
|
val data = pendingCallDataMap?.let { map ->
|
|
30
19
|
val writableMap = Arguments.createMap()
|
|
31
20
|
map.forEach { (key, value) ->
|
|
@@ -33,9 +22,8 @@ class CallModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaMo
|
|
|
33
22
|
}
|
|
34
23
|
writableMap
|
|
35
24
|
}
|
|
36
|
-
|
|
37
25
|
promise.resolve(data)
|
|
38
|
-
pendingCallDataMap = null
|
|
26
|
+
pendingCallDataMap = null
|
|
39
27
|
}
|
|
40
28
|
|
|
41
29
|
@ReactMethod
|
|
@@ -55,25 +43,27 @@ class CallModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaMo
|
|
|
55
43
|
|
|
56
44
|
@ReactMethod
|
|
57
45
|
fun endNativeCall(uuid: String) {
|
|
46
|
+
// Since we aren't using Telecom, we just stop the ringtone and clear notification
|
|
58
47
|
NativeCallManager.stopRingtone()
|
|
59
|
-
pendingCallDataMap = null
|
|
48
|
+
pendingCallDataMap = null
|
|
60
49
|
|
|
61
50
|
val notificationManager = reactApplicationContext.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
|
|
62
51
|
notificationManager.cancel(101)
|
|
52
|
+
|
|
53
|
+
// No more ConnectionService to destroy!
|
|
54
|
+
}
|
|
63
55
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
MyConnectionService.removeConnection(uuid)
|
|
69
|
-
}
|
|
56
|
+
// This is a dummy now since we don't need PhoneAccount permissions anymore
|
|
57
|
+
@ReactMethod
|
|
58
|
+
fun checkTelecomPermissions(promise: Promise) {
|
|
59
|
+
promise.resolve(true)
|
|
70
60
|
}
|
|
71
61
|
|
|
72
|
-
|
|
62
|
+
@ReactMethod fun addListener(eventName: String) {}
|
|
63
|
+
@ReactMethod fun removeListeners(count: Int) {}
|
|
73
64
|
|
|
74
65
|
companion object {
|
|
75
66
|
private var instance: CallModule? = null
|
|
76
|
-
// Use a standard Map to store data so we don't depend on React Context being alive
|
|
77
67
|
private var pendingCallDataMap: Map<String, String>? = null
|
|
78
68
|
|
|
79
69
|
@JvmStatic
|
|
@@ -85,7 +75,6 @@ class CallModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaMo
|
|
|
85
75
|
fun sendEventToJS(eventName: String, params: Any?) {
|
|
86
76
|
val reactContext = instance?.reactApplicationContext
|
|
87
77
|
|
|
88
|
-
// Convert params to WritableMap
|
|
89
78
|
val bridgeData = when (params) {
|
|
90
79
|
is Map<*, *> -> {
|
|
91
80
|
val map = Arguments.createMap()
|
|
@@ -102,7 +91,6 @@ class CallModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaMo
|
|
|
102
91
|
.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)
|
|
103
92
|
?.emit(eventName, bridgeData)
|
|
104
93
|
} else if (eventName == "onCallAccepted" && params is Map<*, *>) {
|
|
105
|
-
// FALLBACK: Store data for polling if JS isn't ready
|
|
106
94
|
setPendingCallData(params as Map<String, String>)
|
|
107
95
|
}
|
|
108
96
|
}
|
package/package.json
CHANGED
|
@@ -1,93 +0,0 @@
|
|
|
1
|
-
package com.rnsnativecall
|
|
2
|
-
|
|
3
|
-
import android.app.*
|
|
4
|
-
import android.content.Context
|
|
5
|
-
import android.content.Intent
|
|
6
|
-
import android.media.AudioAttributes
|
|
7
|
-
import android.media.MediaPlayer
|
|
8
|
-
import android.media.RingtoneManager
|
|
9
|
-
import android.os.Build
|
|
10
|
-
import androidx.core.app.NotificationCompat
|
|
11
|
-
|
|
12
|
-
object NativeCallManager {
|
|
13
|
-
private var mediaPlayer: MediaPlayer? = null
|
|
14
|
-
private const val CHANNEL_ID = "incoming_calls"
|
|
15
|
-
private const val NOTIFICATION_ID = 101
|
|
16
|
-
|
|
17
|
-
fun handleIncomingPush(context: Context, data: Map<String, String>) {
|
|
18
|
-
startRingtone(context)
|
|
19
|
-
showIncomingCallNotification(context, data)
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
private fun startRingtone(context: Context) {
|
|
23
|
-
if (mediaPlayer != null) return
|
|
24
|
-
try {
|
|
25
|
-
val uri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_RINGTONE)
|
|
26
|
-
mediaPlayer = MediaPlayer().apply {
|
|
27
|
-
setDataSource(context, uri)
|
|
28
|
-
setAudioAttributes(AudioAttributes.Builder()
|
|
29
|
-
.setUsage(AudioAttributes.USAGE_NOTIFICATION_RINGTONE)
|
|
30
|
-
.setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
|
|
31
|
-
.build())
|
|
32
|
-
isLooping = true
|
|
33
|
-
prepare()
|
|
34
|
-
start()
|
|
35
|
-
}
|
|
36
|
-
} catch (e: Exception) { e.printStackTrace() }
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
fun stopRingtone() {
|
|
40
|
-
mediaPlayer?.stop()
|
|
41
|
-
mediaPlayer?.release()
|
|
42
|
-
mediaPlayer = null
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
private fun showIncomingCallNotification(context: Context, data: Map<String, String>) {
|
|
46
|
-
val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
|
|
47
|
-
|
|
48
|
-
// Create Channel for Android O+
|
|
49
|
-
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
|
50
|
-
val channel = NotificationChannel(CHANNEL_ID, "Incoming Calls", NotificationManager.IMPORTANCE_HIGH).apply {
|
|
51
|
-
lockscreenVisibility = Notification.VISIBILITY_PUBLIC
|
|
52
|
-
enableVibration(true)
|
|
53
|
-
setSound(null, null) // We handle sound manually via MediaPlayer
|
|
54
|
-
}
|
|
55
|
-
notificationManager.createNotificationChannel(channel)
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
// 1. Full Screen Intent (To wake up the screen)
|
|
59
|
-
val fullScreenIntent = Intent(context, AcceptCallActivity::class.java).apply {
|
|
60
|
-
data.forEach { (k, v) -> putExtra(k, v) }
|
|
61
|
-
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_NO_USER_ACTION)
|
|
62
|
-
}
|
|
63
|
-
val fullScreenPendingIntent = PendingIntent.getActivity(context, 0, fullScreenIntent, PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE)
|
|
64
|
-
|
|
65
|
-
// 2. Answer Action
|
|
66
|
-
val answerIntent = Intent(context, CallActionReceiver::class.java).apply {
|
|
67
|
-
action = "ACTION_ACCEPT"
|
|
68
|
-
data.forEach { (k, v) -> putExtra(k, v) }
|
|
69
|
-
}
|
|
70
|
-
val answerPendingIntent = PendingIntent.getBroadcast(context, 1, answerIntent, PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE)
|
|
71
|
-
|
|
72
|
-
// 3. Reject Action
|
|
73
|
-
val rejectIntent = Intent(context, CallActionReceiver::class.java).apply {
|
|
74
|
-
action = "ACTION_REJECT"
|
|
75
|
-
data.forEach { (k, v) -> putExtra(k, v) }
|
|
76
|
-
}
|
|
77
|
-
val rejectPendingIntent = PendingIntent.getBroadcast(context, 2, rejectIntent, PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE)
|
|
78
|
-
|
|
79
|
-
val builder = NotificationCompat.Builder(context, CHANNEL_ID)
|
|
80
|
-
.setSmallIcon(context.applicationInfo.icon)
|
|
81
|
-
.setContentTitle("Incoming Call")
|
|
82
|
-
.setContentText(data["name"] ?: "Unknown Caller")
|
|
83
|
-
.setPriority(NotificationCompat.PRIORITY_MAX)
|
|
84
|
-
.setCategory(NotificationCompat.CATEGORY_CALL)
|
|
85
|
-
.setAutoCancel(true)
|
|
86
|
-
.setOngoing(true)
|
|
87
|
-
.setFullScreenIntent(fullScreenPendingIntent, true)
|
|
88
|
-
.addAction(0, "Answer", answerPendingIntent)
|
|
89
|
-
.addAction(0, "Decline", rejectPendingIntent)
|
|
90
|
-
|
|
91
|
-
notificationManager.notify(NOTIFICATION_ID, builder.build())
|
|
92
|
-
}
|
|
93
|
-
}
|
|
@@ -1,101 +0,0 @@
|
|
|
1
|
-
package com.rnsnativecall
|
|
2
|
-
|
|
3
|
-
import android.app.*
|
|
4
|
-
import android.content.Context
|
|
5
|
-
import android.content.Intent
|
|
6
|
-
import android.media.AudioAttributes
|
|
7
|
-
import android.media.MediaPlayer
|
|
8
|
-
import android.media.RingtoneManager
|
|
9
|
-
import android.os.Build
|
|
10
|
-
import androidx.core.app.NotificationCompat
|
|
11
|
-
|
|
12
|
-
object NativeCallManager {
|
|
13
|
-
private var mediaPlayer: MediaPlayer? = null
|
|
14
|
-
private const val CHANNEL_ID = "incoming_calls"
|
|
15
|
-
private const val NOTIFICATION_ID = 101
|
|
16
|
-
|
|
17
|
-
fun handleIncomingPush(context: Context, data: Map<String, String>) {
|
|
18
|
-
startRingtone(context)
|
|
19
|
-
showNotification(context, data)
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
private fun startRingtone(context: Context) {
|
|
23
|
-
if (mediaPlayer != null) return
|
|
24
|
-
try {
|
|
25
|
-
val uri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_RINGTONE)
|
|
26
|
-
mediaPlayer = MediaPlayer().apply {
|
|
27
|
-
setDataSource(context, uri)
|
|
28
|
-
setAudioAttributes(AudioAttributes.Builder()
|
|
29
|
-
.setUsage(AudioAttributes.USAGE_NOTIFICATION_RINGTONE)
|
|
30
|
-
.setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
|
|
31
|
-
.build())
|
|
32
|
-
isLooping = true
|
|
33
|
-
prepare()
|
|
34
|
-
start()
|
|
35
|
-
}
|
|
36
|
-
} catch (e: Exception) { e.printStackTrace() }
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
fun stopRingtone() {
|
|
40
|
-
mediaPlayer?.stop()
|
|
41
|
-
mediaPlayer?.release()
|
|
42
|
-
mediaPlayer = null
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
private fun showNotification(context: Context, data: Map<String, String>) {
|
|
46
|
-
val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
|
|
47
|
-
|
|
48
|
-
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
|
49
|
-
val channel = NotificationChannel(CHANNEL_ID, "Calls", NotificationManager.IMPORTANCE_HIGH).apply {
|
|
50
|
-
description = "Incoming call notifications"
|
|
51
|
-
setSound(null, null)
|
|
52
|
-
enableVibration(true)
|
|
53
|
-
}
|
|
54
|
-
notificationManager.createNotificationChannel(channel)
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
// 1. Full Screen Intent (Wakes up screen & goes to AcceptCallActivity)
|
|
58
|
-
val fullScreenIntent = Intent(context, AcceptCallActivity::class.java).apply {
|
|
59
|
-
data.forEach { (k, v) -> putExtra(k, v) }
|
|
60
|
-
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
|
61
|
-
}
|
|
62
|
-
val fullScreenPendingIntent = PendingIntent.getActivity(
|
|
63
|
-
context, 0, fullScreenIntent,
|
|
64
|
-
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
|
|
65
|
-
)
|
|
66
|
-
|
|
67
|
-
// 2. Answer Button
|
|
68
|
-
val answerIntent = Intent(context, CallActionReceiver::class.java).apply {
|
|
69
|
-
action = "ACTION_ACCEPT"
|
|
70
|
-
data.forEach { (k, v) -> putExtra(k, v) }
|
|
71
|
-
}
|
|
72
|
-
val answerPendingIntent = PendingIntent.getBroadcast(
|
|
73
|
-
context, 1, answerIntent,
|
|
74
|
-
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
|
|
75
|
-
)
|
|
76
|
-
|
|
77
|
-
// 3. Reject Button
|
|
78
|
-
val rejectIntent = Intent(context, CallActionReceiver::class.java).apply {
|
|
79
|
-
action = "ACTION_REJECT"
|
|
80
|
-
data.forEach { (k, v) -> putExtra(k, v) }
|
|
81
|
-
}
|
|
82
|
-
val rejectPendingIntent = PendingIntent.getBroadcast(
|
|
83
|
-
context, 2, rejectIntent,
|
|
84
|
-
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
|
|
85
|
-
)
|
|
86
|
-
|
|
87
|
-
val builder = NotificationCompat.Builder(context, CHANNEL_ID)
|
|
88
|
-
.setSmallIcon(android.R.drawable.ic_menu_call) // Replace with your app icon
|
|
89
|
-
.setContentTitle("Incoming Call")
|
|
90
|
-
.setContentText(data["name"] ?: "Raiidr User")
|
|
91
|
-
.setPriority(NotificationCompat.PRIORITY_MAX)
|
|
92
|
-
.setCategory(NotificationCompat.CATEGORY_CALL)
|
|
93
|
-
.setFullScreenIntent(fullScreenPendingIntent, true)
|
|
94
|
-
.setOngoing(true)
|
|
95
|
-
.setAutoCancel(true)
|
|
96
|
-
.addAction(0, "Answer", answerPendingIntent)
|
|
97
|
-
.addAction(0, "Decline", rejectPendingIntent)
|
|
98
|
-
|
|
99
|
-
notificationManager.notify(NOTIFICATION_ID, builder.build())
|
|
100
|
-
}
|
|
101
|
-
}
|