rns-nativecall 0.7.9 → 0.8.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,77 +1,28 @@
1
1
  package com.rnsnativecall
2
2
 
3
3
  import android.app.Activity
4
- import android.app.NotificationManager
5
- import android.content.Context
6
4
  import android.content.Intent
7
5
  import android.os.Bundle
8
6
  import android.view.WindowManager
9
- import android.app.KeyguardManager
7
+ import android.os.Build
10
8
 
11
9
  class AcceptCallActivity : Activity() {
12
-
13
10
  override fun onCreate(savedInstanceState: Bundle?) {
14
11
  super.onCreate(savedInstanceState)
15
12
 
16
- window.addFlags(
17
- WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED or
18
- WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON or
19
- WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
20
- )
21
-
22
- val keyguardManager = getSystemService(Context.KEYGUARD_SERVICE) as KeyguardManager
23
- keyguardManager.requestDismissKeyguard(this, null)
24
-
25
- // Check: Did the user actually press "Answer" or did the system auto-launch?
26
- // If the action is null or doesn't match your Answer action,
27
- // it means the system is just "preparing" the activity.
28
- if (intent.action?.startsWith("ACTION_ANSWER") != true) {
29
- // If it's just an auto-launch, we don't fire the JS event!
30
- // We can either finish() or show a tiny "Swipe to Answer" UI.
31
- return
32
- }
33
- processCallIntent(intent)
34
- }
35
-
36
- private fun processCallIntent(intent: Intent) {
37
- NativeCallManager.stopRingtone()
38
-
39
- val extras = intent.extras
40
- val uuid = extras?.getString("callUuid")
41
-
42
- val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
43
- uuid?.let { notificationManager.cancel(it.hashCode()) }
44
-
45
- // ✅ WE STOP SENDING THE JS EVENT HERE.
46
- // Instead, we pass the intent to MainActivity with a specific ACTION.
47
- openMainApp(extras)
48
- finish()
49
- }
50
-
51
- private fun openMainApp(extras: Bundle?) {
52
- try {
53
- // Get the actual MainActivity class name (e.g., com.yourapp.MainActivity)
54
- val mainActivityClassName = "${packageName}.MainActivity"
55
-
56
- val intent = Intent().apply {
57
- setClassName(packageName, mainActivityClassName)
58
- action = "com.rnsnativecall.ACTION_ANSWER"
59
- addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_SINGLE_TOP)
60
-
61
- // Ensure extras are carried over
62
- extras?.let { putExtras(it) }
63
- }
64
-
65
- startActivity(intent)
66
- } catch (e: Exception) {
67
- // Fallback: If explicit mapping fails, try the launch intent but force the action
68
- val launchIntent = packageManager.getLaunchIntentForPackage(packageName)
69
- launchIntent?.apply {
70
- action = "com.rnsnativecall.ACTION_ANSWER"
71
- addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_SINGLE_TOP)
72
- extras?.let { putExtras(it) }
73
- startActivity(this)
13
+ val launchIntent = packageManager.getLaunchIntentForPackage(packageName)
14
+ if (launchIntent != null) {
15
+ 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
+ putExtras(intent.extras ?: Bundle())
20
+ putExtra("navigatingToCall", true)
74
21
  }
22
+ startActivity(launchIntent)
75
23
  }
24
+
25
+ finish()
26
+ overridePendingTransition(0, 0)
76
27
  }
77
28
  }
@@ -4,49 +4,70 @@ import android.content.BroadcastReceiver
4
4
  import android.content.Context
5
5
  import android.content.Intent
6
6
  import android.app.NotificationManager
7
- import android.app.KeyguardManager
7
+ import android.os.Bundle
8
8
 
9
9
  class CallActionReceiver : BroadcastReceiver() {
10
10
  override fun onReceive(context: Context, intent: Intent) {
11
+ // Stop sound immediately
11
12
  NativeCallManager.stopRingtone()
12
13
 
13
14
  val uuid = intent.getStringExtra("EXTRA_CALL_UUID") ?: return
14
15
  val action = intent.action ?: ""
16
+ val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
15
17
 
16
- val notificationManager =
17
- context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
18
-
19
- val dataMap = mutableMapOf<String, String>()
18
+ // 1. Reconstruct the full data map from Intent Extras
19
+ val fullDataMap = mutableMapOf<String, String>()
20
20
  intent.extras?.keySet()?.forEach { key ->
21
- intent.extras?.get(key)?.let { dataMap[key] = it.toString() }
21
+ intent.extras?.get(key)?.let { fullDataMap[key] = it.toString() }
22
22
  }
23
23
 
24
- val name = intent.extras?.getString("name") ?: "Someone"
25
- val callType = intent.extras?.getString("callType") ?: "audio"
26
-
27
- val keyguardManager = context.getSystemService(Context.KEYGUARD_SERVICE) as KeyguardManager
24
+ val name = fullDataMap["name"] ?: "Someone"
25
+ val callType = fullDataMap["callType"] ?: "audio"
28
26
 
29
- if (action.contains("ACTION_REJECT")) {
27
+ // --- HANDLE REJECT ---
28
+ if (action == "ACTION_REJECT") {
30
29
  if (CallModule.isReady()) {
31
- CallModule.sendEventToJS("onCallRejected", mapOf("callUuid" to uuid))
30
+ CallModule.sendEventToJS("onCallRejected", fullDataMap)
32
31
  notificationManager.cancel(uuid.hashCode())
32
+ CallForegroundService.stop(context)
33
33
  } else {
34
- // Queue pending event
35
- CallModule.setPendingCallData("onCallRejected_pending", mapOf("callUuid" to uuid))
36
- // Update notification pill to "Aborting…" state
34
+ CallModule.setPendingCallData("onCallRejected_pending", fullDataMap)
37
35
  NativeCallManager.aborting(context, uuid, name, callType)
36
+ }
37
+ }
38
+
39
+ // --- HANDLE ANSWER ---
40
+ if (action == "ACTION_ANSWER") {
41
+ // Dismiss the "Incoming" pill immediately
42
+ notificationManager.cancel(uuid.hashCode())
38
43
 
39
- // Register callback to auto‑open app once RN is ready
40
- CallModule.registerOnReadyCallback {
41
- val launchIntent = context.packageManager.getLaunchIntentForPackage(context.packageName)
42
- launchIntent?.apply {
43
- addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_SINGLE_TOP)
44
- putExtras(intent.extras ?: android.os.Bundle())
45
- putExtra("navigatingToCall", true)
46
- context.startActivity(this)
47
- }
48
- }
44
+ if (CallModule.isReady()) {
45
+ // Inform JS with full payload
46
+ CallModule.sendEventToJS("onCallAccepted", fullDataMap)
47
+ CallForegroundService.stop(context)
48
+
49
+ // Open the app
50
+ launchApp(context, intent.extras)
51
+ } else {
52
+ // Cold start: Queue full payload and show connecting
53
+ CallModule.setPendingCallData("onCallAccepted_pending", fullDataMap)
54
+ NativeCallManager.connecting(context, uuid, name, callType)
55
+
56
+ launchApp(context, intent.extras)
49
57
  }
50
58
  }
51
59
  }
60
+
61
+
62
+ private fun launchApp(context: Context, extras: Bundle?) {
63
+ val intent = Intent(context, AcceptCallActivity::class.java).apply {
64
+ addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or
65
+ Intent.FLAG_ACTIVITY_SINGLE_TOP or
66
+ Intent.FLAG_ACTIVITY_CLEAR_TOP)
67
+
68
+ if (extras != null) putExtras(extras)
69
+ }
70
+ context.startActivity(intent)
52
71
  }
72
+
73
+ }
@@ -1,20 +1,23 @@
1
1
  package com.rnsnativecall
2
2
 
3
3
  import android.app.*
4
+ import android.content.BroadcastReceiver
4
5
  import android.content.Context
5
6
  import android.content.Intent
7
+ import android.content.IntentFilter
6
8
  import android.content.pm.ServiceInfo
7
9
  import android.os.Build
8
10
  import android.os.Bundle
9
11
  import android.os.IBinder
10
12
  import androidx.core.app.NotificationCompat
11
13
  import com.facebook.react.HeadlessJsTaskService
12
-
13
- import android.os.Handler // Added
14
- import android.os.Looper // Added
14
+ import android.os.Handler
15
+ import android.os.Looper
15
16
 
16
17
  class CallForegroundService : Service() {
17
18
 
19
+ private var unlockReceiver: UnlockReceiver? = null // Store reference for unregistering
20
+
18
21
  companion object {
19
22
  private const val NOTIFICATION_ID = 101
20
23
  private const val CHANNEL_ID = "incoming_call_service"
@@ -25,6 +28,23 @@ class CallForegroundService : Service() {
25
28
  }
26
29
  }
27
30
 
31
+ override fun onCreate() {
32
+ 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
+ unlockReceiver = UnlockReceiver()
38
+ val filter = IntentFilter(Intent.ACTION_USER_PRESENT)
39
+
40
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
41
+ // Android 13+ requires the RECEIVER_EXPORTED flag for system broadcasts
42
+ registerReceiver(unlockReceiver, filter, Context.RECEIVER_EXPORTED)
43
+ } else {
44
+ registerReceiver(unlockReceiver, filter)
45
+ }
46
+ }
47
+
28
48
  override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
29
49
  val data = intent?.extras
30
50
  val name = data?.getString("name") ?: "Someone"
@@ -33,14 +53,13 @@ class CallForegroundService : Service() {
33
53
 
34
54
  val notification = NotificationCompat.Builder(this, CHANNEL_ID)
35
55
  .setContentTitle(name)
36
- .setContentText("Connecting...")
56
+ .setContentText("Incoming Call...")
37
57
  .setSmallIcon(applicationInfo.icon)
38
58
  .setCategory(NotificationCompat.CATEGORY_CALL)
39
59
  .setPriority(NotificationCompat.PRIORITY_HIGH)
40
60
  .setOngoing(true)
41
61
  .build()
42
62
 
43
- // --- ANDROID 14 FIX START ---
44
63
  if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
45
64
  startForeground(
46
65
  NOTIFICATION_ID,
@@ -50,11 +69,9 @@ class CallForegroundService : Service() {
50
69
  } else {
51
70
  startForeground(NOTIFICATION_ID, notification)
52
71
  }
53
- // --- ANDROID 14 FIX END ---
54
72
 
55
73
  // Launch the Headless Task
56
74
  val headlessIntent = Intent(this, CallHeadlessTask::class.java).apply {
57
- // Safe bundle copy
58
75
  val bundle = Bundle()
59
76
  data?.let { b ->
60
77
  for (key in b.keySet()) {
@@ -67,38 +84,44 @@ class CallForegroundService : Service() {
67
84
  try {
68
85
  this.startService(headlessIntent)
69
86
  HeadlessJsTaskService.acquireWakeLockNow(this)
70
- } catch (e: Exception) {
71
- e.printStackTrace()
72
- }
87
+ } catch (e: Exception) { e.printStackTrace() }
73
88
 
89
+ // Auto-stop after 30s
90
+ Handler(Looper.getMainLooper()).postDelayed({
91
+ try { stopSelf() } catch (e: Exception) {}
92
+ }, 30000)
74
93
 
75
- // Automatically stop this service if JS doesn't stop it within 30 seconds
76
- Handler(Looper.getMainLooper()).postDelayed({
77
- try {
78
- stopSelf()
79
- } catch (e: Exception) {
80
- // Service might already be stopped
81
- }
82
- }, 30000)
83
94
  return START_NOT_STICKY
84
95
  }
85
96
 
97
+ override fun onDestroy() {
98
+ // --- CLEANUP ---
99
+ // Unregister to prevent memory leaks once the call ends or service stops
100
+ try {
101
+ unlockReceiver?.let { unregisterReceiver(it) }
102
+ } catch (e: Exception) { e.printStackTrace() }
103
+
104
+ try { stopForeground(true) } catch (_: Exception) {}
105
+ super.onDestroy()
106
+ }
107
+
86
108
  private fun createNotificationChannel() {
87
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
88
- val channel = NotificationChannel(
89
- CHANNEL_ID,
90
- "Call Service",
91
- NotificationManager.IMPORTANCE_HIGH
92
- ).apply {
93
- description = "Handles incoming call connection state"
94
- // Optional: makes the "Connecting" notification silent
95
- // so it doesn't double-beep with the actual ringtone
96
- setSound(null, null)
109
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
110
+ val channel = NotificationChannel(
111
+ CHANNEL_ID,
112
+ "Call Service",
113
+ NotificationManager.IMPORTANCE_LOW
114
+ ).apply {
115
+ description = "Handles incoming call connection state"
116
+ setSound(null, null)
117
+ enableVibration(false)
118
+ setBypassDnd(true)
119
+ lockscreenVisibility = NotificationCompat.VISIBILITY_PUBLIC
120
+ }
121
+ val manager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
122
+ manager.createNotificationChannel(channel)
97
123
  }
98
- val manager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
99
- manager.createNotificationChannel(channel)
100
124
  }
101
- }
102
125
 
103
126
  override fun onBind(intent: Intent?): IBinder? = null
104
127
  }
@@ -34,7 +34,6 @@ class CallMessagingService : FirebaseMessagingService() {
34
34
  CallState.clear(uuid, context)
35
35
  }
36
36
 
37
- // Dismiss the "Connecting..." or "Incoming Call" UI
38
37
  NativeCallManager.dismissIncomingCall(context, uuid)
39
38
  showMissedCallNotification(context, data, uuid)
40
39
  return
@@ -5,6 +5,9 @@ import android.content.Context
5
5
  import com.facebook.react.bridge.*
6
6
  import com.facebook.react.modules.core.DeviceEventManagerModule
7
7
  import android.content.Intent
8
+ import android.os.Build
9
+ import android.provider.Settings
10
+ import android.net.Uri
8
11
 
9
12
  class CallModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaModule(reactContext) {
10
13
 
@@ -51,6 +54,29 @@ class CallModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaMo
51
54
  }
52
55
  }
53
56
 
57
+ // Inside your CallModule class
58
+ @ReactMethod
59
+ fun checkOverlayPermission(promise: Promise) {
60
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
61
+ promise.resolve(Settings.canDrawOverlays(reactApplicationContext))
62
+ } else {
63
+ promise.resolve(true)
64
+ }
65
+ }
66
+
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
+ }
54
80
 
55
81
  /**
56
82
  * Combined Validity Check:
@@ -108,11 +134,6 @@ class CallModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaMo
108
134
  notificationManager.cancel(101)
109
135
  }
110
136
 
111
- @ReactMethod
112
- fun checkTelecomPermissions(promise: Promise) {
113
- promise.resolve(true)
114
- }
115
-
116
137
  @ReactMethod fun addListener(eventName: String) {}
117
138
  @ReactMethod fun removeListeners(count: Int) {}
118
139
 
@@ -10,85 +10,89 @@ import androidx.core.app.NotificationCompat
10
10
  import android.media.Ringtone
11
11
  import android.media.RingtoneManager
12
12
  import android.graphics.Color
13
-
14
13
  import android.app.KeyguardManager
15
14
 
16
- import com.rnsnativecall.CallMessagingService
17
-
18
15
  object NativeCallManager {
19
16
 
20
17
  private var ringtone: Ringtone? = null
21
18
  const val channelId = "CALL_CHANNEL_ID"
22
19
 
20
+ private var currentCallData: Map<String, String>? = null
21
+
22
+ fun getCurrentCallData(): Map<String, String>? = currentCallData
23
+
23
24
  fun handleIncomingPush(context: Context, data: Map<String, String>) {
24
25
  val uuid = data["callUuid"] ?: return
26
+ this.currentCallData = data
25
27
  stopRingtone()
26
28
 
27
29
  val name = data["name"] ?: "Incoming Call"
28
30
  val callType = data["callType"] ?: "audio"
29
31
  val notificationId = uuid.hashCode()
30
32
 
31
-
32
-
33
-
34
- // --- LOCK SCREEN GATEKEEPER ---
35
- val keyguardManager = context.getSystemService(Context.KEYGUARD_SERVICE) as KeyguardManager
36
- if (keyguardManager.isKeyguardLocked) {
37
- // Device is locked? We bounce it!
38
- CallState.markCanceled(uuid, context)
39
- showMissedCallNotification(context, data, uuid)
40
- return // 🛑 Flow stops here
41
- }
42
-
43
-
44
-
45
33
  val pendingFlags = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
46
34
  PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_MUTABLE
47
35
  } else {
48
36
  PendingIntent.FLAG_UPDATE_CURRENT
49
37
  }
50
38
 
51
- val noOpIntent = PendingIntent.getActivity(
52
- context,
53
- notificationId + 1,
54
- Intent(),
55
- pendingFlags
56
- )
57
-
58
- val intentToActivity = Intent(context, AcceptCallActivity::class.java).apply {
59
- this.action = "ACTION_SHOW_UI_$uuid"
60
- data.forEach { (key, value) -> this.putExtra(key, value) }
61
- this.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP)
39
+ // Dummy intent for content click
40
+ val noOpIntent = PendingIntent.getActivity(context, notificationId + 3, Intent(), pendingFlags)
41
+
42
+ // 1. Define the flags properly (DO NOT MIX THEM)
43
+ val mutableFlags = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
44
+ PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_MUTABLE
45
+ } else {
46
+ PendingIntent.FLAG_UPDATE_CURRENT
47
+ }
48
+
49
+ val immutableFlags = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
50
+ PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
51
+ } else {
52
+ PendingIntent.FLAG_UPDATE_CURRENT
53
+ }
54
+
55
+ // 2. Setup the Overlay Intent
56
+ val overlayIntent = Intent(context, NotificationOverlayActivity::class.java).apply {
57
+ this.putExtra("EXTRA_CALL_UUID", uuid)
58
+ data.forEach { (k, v) -> this.putExtra(k, v) }
59
+ // No User Action flag prevents the intent from triggering an auto-dismiss of notifications
60
+ addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_NO_USER_ACTION)
61
+ }
62
+
63
+ // 3. Create the Full Screen PendingIntent (Use IMMUTABLE)
64
+ val fullScreenPendingIntent = PendingIntent.getActivity(
65
+ context,
66
+ notificationId,
67
+ overlayIntent,
68
+ immutableFlags // <--- Use the immutable version here
69
+ )
70
+
71
+
72
+
73
+ // --- ANSWER ACTION ---
74
+ val answerIntent = Intent(context, CallActionReceiver::class.java).apply {
75
+ this.action = "ACTION_ANSWER"
76
+ this.putExtra("EXTRA_CALL_UUID", uuid)
77
+
78
+ data.forEach { (key, value) -> this.putExtra(key, value) } // Pass full data
62
79
  }
63
80
 
64
- val fullScreenPendingIntent = PendingIntent.getActivity(
65
- context,
66
- notificationId,
67
- intentToActivity,
68
- pendingFlags
69
- )
81
+ val answerPendingIntent = PendingIntent.getBroadcast(context, notificationId + 1, answerIntent, mutableFlags)
70
82
 
83
+ // --- REJECT ACTION ---
71
84
  val rejectIntent = Intent(context, CallActionReceiver::class.java).apply {
72
- this.action = "ACTION_REJECT_$uuid"
85
+ this.action = "ACTION_REJECT"
73
86
  this.putExtra("EXTRA_CALL_UUID", uuid)
74
- data.forEach { (key, value) -> this.putExtra(key, value) }
87
+ data.forEach { (key, value) -> this.putExtra(key, value) } // Pass full data
75
88
  }
76
-
77
- val rejectPendingIntent = PendingIntent.getBroadcast(
78
- context,
79
- notificationId,
80
- rejectIntent,
81
- pendingFlags
82
- )
89
+
90
+ val rejectPendingIntent = PendingIntent.getBroadcast(context, notificationId + 2, rejectIntent, mutableFlags)
83
91
 
84
92
  val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
85
93
 
86
94
  if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
87
- val channel = NotificationChannel(
88
- channelId,
89
- "Incoming Calls",
90
- NotificationManager.IMPORTANCE_HIGH // NotificationManager.IMPORTANCE_HIGH
91
- ).apply {
95
+ val channel = NotificationChannel(channelId, "Incoming Calls", NotificationManager.IMPORTANCE_HIGH).apply {
92
96
  enableVibration(true)
93
97
  vibrationPattern = longArrayOf(0, 500, 500, 500)
94
98
  lightColor = Color.GREEN
@@ -99,83 +103,70 @@ object NativeCallManager {
99
103
  notificationManager.createNotificationChannel(channel)
100
104
  }
101
105
 
106
+ val keyguardManager = context.getSystemService(Context.KEYGUARD_SERVICE) as KeyguardManager
107
+ val isLocked = keyguardManager.isKeyguardLocked
108
+
109
+
102
110
  val builder = NotificationCompat.Builder(context, channelId)
103
111
  .setSmallIcon(context.applicationInfo.icon)
104
112
  .setContentTitle("Incoming $callType call")
105
113
  .setContentText(name)
106
- .setPriority(NotificationCompat.PRIORITY_MAX) // PRIORITY_HIGH
107
- .setCategory(NotificationCompat.CATEGORY_CALL) // CATEGORY_CALL
114
+ .setPriority(NotificationCompat.PRIORITY_MAX)
115
+ .setCategory(NotificationCompat.CATEGORY_CALL)
108
116
  .setOngoing(true)
109
117
  .setAutoCancel(false)
110
118
  .setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
111
- .setFullScreenIntent(fullScreenPendingIntent, true)
112
119
  .setContentIntent(noOpIntent)
113
- .addAction(0, "Answer", fullScreenPendingIntent)
120
+ .addAction(0, "Answer", answerPendingIntent)
114
121
  .addAction(0, "Decline", rejectPendingIntent)
122
+ .setDefaults(NotificationCompat.DEFAULT_ALL)
123
+
124
+ builder.setFullScreenIntent(fullScreenPendingIntent, true)
125
+
115
126
 
116
127
  notificationManager.notify(notificationId, builder.build())
117
128
 
118
129
  try {
119
130
  val ringtoneUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_RINGTONE)
120
131
  ringtone = RingtoneManager.getRingtone(context, ringtoneUri)
121
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
122
- ringtone?.isLooping = true
123
- }
132
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) ringtone?.isLooping = true
124
133
  ringtone?.play()
125
- } catch (e: Exception) {
126
- e.printStackTrace()
127
- }
134
+ } catch (e: Exception) { e.printStackTrace() }
128
135
  }
129
136
 
130
137
  fun stopRingtone() {
131
138
  try {
132
139
  ringtone?.let { if (it.isPlaying) it.stop() }
133
140
  ringtone = null
134
- } catch (e: Exception) {
135
- ringtone = null
136
- }
141
+ } catch (e: Exception) { ringtone = null }
137
142
  }
138
143
 
139
- fun connecting(context: Context, uuid: String, name: String, callType: String) {
140
- val notificationId = uuid.hashCode()
141
- val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
142
-
143
- val builder = NotificationCompat.Builder(context, channelId)
144
- .setSmallIcon(context.applicationInfo.icon)
145
- .setContentTitle("Incoming $callType call")
146
- .setContentText("Connecting…") // ✅ show connecting text
147
- .setSubText("Connecting…") // status line
148
- .setPriority(NotificationCompat.PRIORITY_MAX)
149
- .setCategory(NotificationCompat.CATEGORY_CALL)
150
- .setOngoing(true)
151
- .setAutoCancel(false)
152
- .setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
153
- .setProgress(0, 0, true) // ✅ system activity indicator (indeterminate progress bar)
154
-
155
- notificationManager.notify(notificationId, builder.build())
156
- }
157
-
158
- fun aborting(context: Context, uuid: String, name: String, callType: String) {
159
- val notificationId = uuid.hashCode()
160
- val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
161
-
162
- val builder = NotificationCompat.Builder(context, channelId)
163
- .setSmallIcon(context.applicationInfo.icon)
164
- .setContentTitle("Incoming $callType call")
165
- .setContentText("Aborting…") // ✅ show aborting text
166
- .setSubText("Aborting…") // status line
167
- .setPriority(NotificationCompat.PRIORITY_MAX)
168
- .setCategory(NotificationCompat.CATEGORY_CALL)
169
- .setOngoing(true)
170
- .setAutoCancel(false)
171
- .setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
172
- .setProgress(0, 0, true) // ✅ indeterminate progress indicator
173
-
174
- notificationManager.notify(notificationId, builder.build())
175
- }
144
+ fun connecting(context: Context, uuid: String, name: String, callType: String) {
145
+ val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
146
+ val builder = NotificationCompat.Builder(context, channelId)
147
+ .setSmallIcon(context.applicationInfo.icon)
148
+ .setContentTitle("Incoming $callType call")
149
+ .setContentText("Connecting…")
150
+ .setPriority(NotificationCompat.PRIORITY_MAX)
151
+ .setOngoing(true)
152
+ .setProgress(0, 0, true)
153
+ notificationManager.notify(uuid.hashCode(), builder.build())
154
+ }
176
155
 
156
+ fun aborting(context: Context, uuid: String, name: String, callType: String) {
157
+ val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
158
+ val builder = NotificationCompat.Builder(context, channelId)
159
+ .setSmallIcon(context.applicationInfo.icon)
160
+ .setContentTitle("Incoming $callType call")
161
+ .setContentText("Aborting…")
162
+ .setPriority(NotificationCompat.PRIORITY_MAX)
163
+ .setOngoing(true)
164
+ .setProgress(0, 0, true)
165
+ notificationManager.notify(uuid.hashCode(), builder.build())
166
+ }
177
167
 
178
168
  fun dismissIncomingCall(context: Context, uuid: String?) {
169
+ this.currentCallData = null
179
170
  stopRingtone()
180
171
  val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
181
172
  if (uuid != null) notificationManager.cancel(uuid.hashCode())