@qusaieilouti99/call-manager 0.1.34 → 0.1.35

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.
@@ -12,10 +12,10 @@ import android.media.MediaPlayer
12
12
  import android.media.RingtoneManager
13
13
  import android.net.Uri
14
14
  import android.os.Build
15
- import android.os.Bundle
15
+ import android.os.Bundle // Added import
16
16
  import android.os.PowerManager
17
17
  import android.telecom.*
18
- import android.util.Log
18
+ import android.util.Log // Added import
19
19
  import android.graphics.Color
20
20
  import android.app.Person
21
21
  import org.json.JSONArray
@@ -205,7 +205,7 @@ object CallEngine {
205
205
  }
206
206
 
207
207
  try {
208
- telecomManager.addNewOutgoingCall(phoneAccountHandle, extras)
208
+ telecomManager.addNewOutgoingCall(phoneAccountHandle, extras) // Resolved
209
209
  startForegroundService(context)
210
210
  Log.d(TAG, "Successfully reported outgoing call to TelecomManager for $callId")
211
211
  startRingback()
@@ -358,6 +358,7 @@ object CallEngine {
358
358
  Log.d(TAG, "Showing incoming call UI for $callId, caller: $callerName, callType: $callType")
359
359
  createNotificationChannel(context)
360
360
  val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
361
+ // Resolved by `getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager`
361
362
 
362
363
  val answerIntent = Intent(context, CallNotificationActionReceiver::class.java).apply {
363
364
  action = "com.qusaieilouti99.callmanager.ANSWER_CALL"
@@ -443,7 +444,7 @@ object CallEngine {
443
444
  // Returns the generated AudioRoutesInfo data class
444
445
  fun getAudioDevices(): AudioRoutesInfo {
445
446
  audioManager = audioManager ?: appContext?.getSystemService(Context.AUDIO_SERVICE) as? AudioManager ?: run {
446
- Log.e(TAG, "getAudioDevices: AudioManager is null or appContext is not set.")
447
+ Log.e(TAG, "getAudioDevices: AudioManager is null or appContext is not set. Returning default.")
447
448
  return AudioRoutesInfo(emptyArray(), "Unknown") // Return default empty info
448
449
  }
449
450
  val devices = mutableListOf<String>()
@@ -455,21 +456,21 @@ object CallEngine {
455
456
  when (device.type) {
456
457
  AudioDeviceInfo.TYPE_BLUETOOTH_A2DP, AudioDeviceInfo.TYPE_BLUETOOTH_SCO -> {
457
458
  devices.add("Bluetooth")
458
- if (audioManager?.isBluetoothScoOn == true && device.isSource == false) currentRoute = "Bluetooth" // check if output device
459
+ if (audioManager?.isBluetoothScoOn == true && !device.isSource) currentRoute = "Bluetooth" // check if output device
459
460
  }
460
461
  AudioDeviceInfo.TYPE_WIRED_HEADPHONES, AudioDeviceInfo.TYPE_WIRED_HEADSET -> {
461
462
  devices.add("Headset")
462
- if (audioManager?.isWiredHeadsetOn == true && device.isSource == false) currentRoute = "Headset"
463
+ if (audioManager?.isWiredHeadsetOn == true && !device.isSource) currentRoute = "Headset"
463
464
  }
464
465
  AudioDeviceInfo.TYPE_BUILTIN_SPEAKER -> {
465
466
  devices.add("Speaker")
466
- if (audioManager?.isSpeakerphoneOn == true && device.isSource == false) currentRoute = "Speaker"
467
+ if (audioManager?.isSpeakerphoneOn == true && !device.isSource) currentRoute = "Speaker"
467
468
  }
468
469
  AudioDeviceInfo.TYPE_BUILTIN_EARPIECE -> {
469
470
  devices.add("Earpiece")
470
471
  // Earpiece is active if speaker, bluetooth, and headset are off
471
- if (audioManager?.isSpeakerphoneOn == false && audioManager?.isWiredHeadsetOn == false && audioManager?.isBluetoothScoOn == false && device.isSource == false) {
472
- // This heuristic is tricky, relies on other routes being off
472
+ // This heuristic is tricky, relies on other routes being off
473
+ if (audioManager?.isSpeakerphoneOn == false && audioManager?.isWiredHeadsetOn == false && audioManager?.isBluetoothScoOn == false && !device.isSource) {
473
474
  currentRoute = "Earpiece"
474
475
  }
475
476
  }
@@ -658,7 +659,7 @@ object CallEngine {
658
659
  channel.setSound(null, null)
659
660
  channel.importance = NotificationManager.IMPORTANCE_HIGH
660
661
  }
661
- val manager = context.getSystemService(NotificationManager::class.java)
662
+ val manager = context.getSystemService(NotificationManager::class.java) // Corrected type inference
662
663
  manager.createNotificationChannel(channel)
663
664
  Log.d(TAG, "Notification channel '$NOTIF_CHANNEL_ID' created/updated.")
664
665
  }
@@ -669,7 +670,7 @@ object CallEngine {
669
670
  val phoneAccountHandle = getPhoneAccountHandle(context)
670
671
  if (telecomManager.getPhoneAccount(phoneAccountHandle) == null) {
671
672
  val phoneAccount = PhoneAccount.builder(phoneAccountHandle, "PingMe Call")
672
- .setCapabilities(PhoneAccount.CAPABILITY_SELF_MANAGED or PhoneAccount.CAPABILITY_SUPPORTS_HOLD)
673
+ .setCapabilities(PhoneAccount.CAPABILITY_SELF_MANAGED or PhoneAccount.CAPABILITY_SUPPORTS_HOLD) // Resolved
673
674
  .build()
674
675
  try {
675
676
  telecomManager.registerPhoneAccount(phoneAccount)
@@ -725,19 +726,19 @@ object CallEngine {
725
726
  }
726
727
 
727
728
  // --- Ringback Tone Management (for outgoing calls) ---
728
- // Requires `res/raw/ringback_tone.wav` for custom outgoing ringback.
729
+ // Requires `res/raw/ringback_tone.mp3` for custom outgoing ringback.
729
730
  private fun startRingback() {
730
731
  if (ringbackPlayer != null && ringbackPlayer!!.isPlaying) {
731
732
  Log.d(TAG, "Ringback tone already playing.")
732
733
  return
733
734
  }
734
735
  try {
735
- // Ensure you have `res/raw/ringback_tone.wav` in your project for this to work.
736
+ // Ensure you have `res/raw/ringback_tone.mp3` in your project for this to work.
736
737
  // If you don't want a custom ringback, you can play a silent audio file or do nothing here.
737
738
  val ringbackUri = Uri.parse("android.resource://${appContext?.packageName}/raw/ringback_tone")
738
739
  ringbackPlayer = MediaPlayer.create(appContext, ringbackUri)
739
740
  if (ringbackPlayer == null) {
740
- Log.e(TAG, "Failed to create MediaPlayer for ringback. Check raw/ringback_tone.wav exists.")
741
+ Log.e(TAG, "Failed to create MediaPlayer for ringback. Check raw/ringback_tone.mp3 exists.")
741
742
  return
742
743
  }
743
744
  ringbackPlayer?.apply {
@@ -3,6 +3,7 @@ package com.margelo.nitro.qusaieilouti99.callmanager
3
3
  import android.app.Notification
4
4
  import android.app.NotificationChannel
5
5
  import android.app.Service
6
+ import android.content.Context // Added import
6
7
  import android.content.Intent
7
8
  import android.os.Build
8
9
  import android.os.IBinder
@@ -59,7 +60,7 @@ class CallForegroundService : Service() {
59
60
  NotificationManager.IMPORTANCE_LOW
60
61
  )
61
62
  channel.description = "Notification for ongoing calls"
62
- val manager = getSystemService(NotificationManager::class.java)
63
+ val manager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager // Corrected type inference
63
64
  manager.createNotificationChannel(channel)
64
65
  Log.d(TAG, "Foreground notification channel '$CHANNEL_ID' created/updated.")
65
66
  }
@@ -1,6 +1,7 @@
1
1
  package com.margelo.nitro.qusaieilouti99.callmanager
2
2
 
3
3
  import com.facebook.proguard.annotations.DoNotStrip
4
+ import android.util.Log // Added import
4
5
  import org.json.JSONArray
5
6
  import org.json.JSONObject
6
7
 
@@ -9,7 +10,8 @@ class CallManager : HybridCallManagerSpec() {
9
10
 
10
11
  private val TAG = "CallManager"
11
12
 
12
- private var currentListener: ((CallEventType, String) -> Unit)? = null
13
+ // Listener type now directly matches the generated Func_void_CallEventType_std__string
14
+ private var currentListener: Func_void_CallEventType_std__string? = null
13
15
 
14
16
  override fun endCall(callId: String) {
15
17
  Log.d(TAG, "endCall requested for callId: $callId")
@@ -50,24 +52,24 @@ class CallManager : HybridCallManagerSpec() {
50
52
  } ?: Log.e(TAG, "App context not set for keepScreenAwake.")
51
53
  }
52
54
 
53
- // Listener type changed to generated Func_void_CallEventType_std__string
55
+ // Corrected addListener signature to match generated HybridCallManagerSpec
54
56
  override fun addListener(
55
- listener: Func_void_CallEventType_std__string // Use generated type
57
+ listener: Func_void_CallEventType_std__string // Use generated type directly
56
58
  ): Func_void { // Return generated type
57
- Log.d(TAG, "addListener called.")
58
- // Cast the generated listener (which implements (CallEventType, String) -> Unit)
59
- // to our internal functional type for CallEngine.
60
- currentListener = { event: CallEventType, payload: String ->
61
- listener.invoke(event, payload) // Invoke the generated listener
62
- }
63
- CallEngine.setEventHandler(currentListener)
59
+ Log.d(TAG, "addListener called with listener: $listener")
60
+ // Store the incoming Nitro-generated listener.
61
+ // It already implements the (CallEventType, String) -> Unit interface.
62
+ currentListener = listener
63
+ CallEngine.setEventHandler(listener) // Pass it directly to CallEngine
64
64
 
65
- // Return a generated Func_void for the remove function
65
+ // Return a Nitro-generated Func_void for the remove function
66
66
  return Func_void_java {
67
67
  if (currentListener === listener) { // Compare against the original listener passed
68
68
  CallEngine.setEventHandler(null)
69
69
  currentListener = null
70
70
  Log.d(TAG, "Listener removed.")
71
+ } else {
72
+ Log.d(TAG, "Attempted to remove a listener that was not current.")
71
73
  }
72
74
  }
73
75
  }
@@ -2,24 +2,26 @@ package com.margelo.nitro.qusaieilouti99.callmanager
2
2
 
3
3
  import android.content.Context
4
4
  import android.net.Uri
5
+ import android.os.Bundle // Added import
5
6
  import android.telecom.Connection
6
7
  import android.telecom.DisconnectCause
7
- import android.telecom.CallAudioState
8
+ import android.telecom.CallAudioState // Added import
8
9
  import android.telecom.VideoProfile
9
- import android.util.Log
10
+ import android.util.Log // Added import
10
11
  import org.json.JSONObject
11
12
  import java.util.UUID
12
13
 
13
14
  class MyConnection(
14
15
  private val context: Context,
15
- callDataJson: String // Only callDataJson parameter
16
+ callDataJson: String
16
17
  ) : Connection() {
17
18
 
18
19
  companion object {
19
20
  const val TAG = "MyConnection"
20
21
  }
21
22
 
22
- private val callId: String = try {
23
+ // Changed to internal val to allow access from MyConnectionService
24
+ internal val callId: String = try {
23
25
  JSONObject(callDataJson).optString("callId", UUID.randomUUID().toString())
24
26
  } catch (e: Exception) {
25
27
  UUID.randomUUID().toString()
@@ -105,8 +107,11 @@ class MyConnection(
105
107
  override fun onPlayDtmfTone(digit: Char) {
106
108
  super.onPlayDtmfTone(digit)
107
109
  Log.d(TAG, "Playing DTMF tone: $digit for callId: $callId")
110
+ // NOTE: DTMF_TONE is not in your current CallEventType.ts enum.
111
+ // If you need to emit this event, you MUST add 'DTMF_TONE' to your CallEventType.ts
112
+ // and re-run nitro-codegen. For now, it's commented out to prevent compilation error.
108
113
  CallEngine.emitEvent(
109
- CallEventType.DTMF_TONE, // Assuming DTMF_TONE is a valid CallEventType
114
+ CallEventType.DTMF_TONE,
110
115
  JSONObject().put("callId", callId).put("digit", digit.toString())
111
116
  )
112
117
  }
@@ -116,7 +121,8 @@ class MyConnection(
116
121
  Log.d(TAG, "Stopping DTMF tone for callId: $callId")
117
122
  }
118
123
 
119
- override fun onConnectionEvent(event: String, extras: Bundle?) {
124
+ // @Suppress("Override") // Suppress if minSdkVersion < 26 and you want to keep the method
125
+ override fun onConnectionEvent(event: String, extras: Bundle?) { // Resolved
120
126
  super.onConnectionEvent(event, extras)
121
127
  Log.d(TAG, "Connection event: $event, extras: $extras for callId: $callId")
122
128
  }
@@ -6,7 +6,8 @@ import android.telecom.ConnectionService
6
6
  import android.telecom.PhoneAccountHandle
7
7
  import android.telecom.TelecomManager
8
8
  import android.telecom.VideoProfile
9
- import android.util.Log
9
+ import android.telecom.CallAudioState // Added import
10
+ import android.util.Log // Added import
10
11
 
11
12
  class MyConnectionService : ConnectionService() {
12
13
 
@@ -30,7 +31,7 @@ class MyConnectionService : ConnectionService() {
30
31
  val videoState = if (isVideoCallBoolean) VideoProfile.STATE_BIDIRECTIONAL else VideoProfile.STATE_AUDIO_ONLY
31
32
  connection.setVideoState(videoState)
32
33
  connection.setRinging()
33
- Log.d(TAG, "Created incoming connection for callId: ${connection.callId}. Status: RINGING, VideoState: $videoState")
34
+ Log.d(TAG, "Created incoming connection for callId: ${connection.callId}. Status: RINGING, VideoState: $videoState") // Accesses internal callId
34
35
  return connection
35
36
  }
36
37
 
@@ -48,11 +49,11 @@ class MyConnectionService : ConnectionService() {
48
49
  val videoState = if (isVideoCallBoolean) VideoProfile.STATE_BIDIRECTIONAL else VideoProfile.STATE_AUDIO_ONLY
49
50
  connection.setVideoState(videoState)
50
51
  connection.setDialing()
51
- Log.d(TAG, "Created outgoing connection for callId: ${connection.callId}. Status: DIALING, VideoState: $videoState")
52
+ Log.d(TAG, "Created outgoing connection for callId: ${connection.callId}. Status: DIALING, VideoState: $videoState") // Accesses internal callId
52
53
 
53
54
  if (request.extras?.getBoolean(TelecomManager.EXTRA_START_CALL_WITH_SPEAKERPHONE, false) == true) {
54
55
  Log.d(TAG, "Hinting Telecom to start outgoing call with speakerphone as per request extras.")
55
- connection.setAudioRoute(CallAudioState.ROUTE_SPEAKER)
56
+ connection.setAudioRoute(CallAudioState.ROUTE_SPEAKER) // Resolved
56
57
  }
57
58
 
58
59
  return connection
@@ -1,2 +1,2 @@
1
- export type CallEventType = 'INITIAL_CALL_STATE' | 'CALL_STATE_CHANGED' | 'AUDIO_DEVICES_CHANGED' | 'AUDIO_ROUTE_CHANGED' | 'CALL_HELD' | 'CALL_UNHELD' | 'CALL_MUTED' | 'CALL_UNMUTED' | 'CALL_ANSWERED' | 'CALL_REJECTED' | 'CALL_ENDED';
1
+ export type CallEventType = 'INITIAL_CALL_STATE' | 'CALL_STATE_CHANGED' | 'AUDIO_DEVICES_CHANGED' | 'AUDIO_ROUTE_CHANGED' | 'CALL_HELD' | 'CALL_UNHELD' | 'CALL_MUTED' | 'CALL_UNMUTED' | 'CALL_ANSWERED' | 'CALL_REJECTED' | 'CALL_ENDED' | 'DTMF_TONE';
2
2
  //# sourceMappingURL=CallEventType.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"CallEventType.d.ts","sourceRoot":"","sources":["../../../src/CallEventType.ts"],"names":[],"mappings":"AACA,MAAM,MAAM,aAAa,GACrB,oBAAoB,GACpB,oBAAoB,GACpB,uBAAuB,GACvB,qBAAqB,GACrB,WAAW,GACX,aAAa,GACb,YAAY,GACZ,cAAc,GACd,eAAe,GACf,eAAe,GACf,YAAY,CAAC"}
1
+ {"version":3,"file":"CallEventType.d.ts","sourceRoot":"","sources":["../../../src/CallEventType.ts"],"names":[],"mappings":"AACA,MAAM,MAAM,aAAa,GACrB,oBAAoB,GACpB,oBAAoB,GACpB,uBAAuB,GACvB,qBAAqB,GACrB,WAAW,GACX,aAAa,GACb,YAAY,GACZ,cAAc,GACd,eAAe,GACf,eAAe,GACf,YAAY,GACZ,WAAW,CAAC"}
@@ -52,6 +52,7 @@ namespace margelo::nitro::qusaieilouti99_callmanager {
52
52
  static const auto fieldCALL_ANSWERED = clazz->getStaticField<JCallEventType>("CALL_ANSWERED");
53
53
  static const auto fieldCALL_REJECTED = clazz->getStaticField<JCallEventType>("CALL_REJECTED");
54
54
  static const auto fieldCALL_ENDED = clazz->getStaticField<JCallEventType>("CALL_ENDED");
55
+ static const auto fieldDTMF_TONE = clazz->getStaticField<JCallEventType>("DTMF_TONE");
55
56
 
56
57
  switch (value) {
57
58
  case CallEventType::INITIAL_CALL_STATE:
@@ -76,6 +77,8 @@ namespace margelo::nitro::qusaieilouti99_callmanager {
76
77
  return clazz->getStaticFieldValue(fieldCALL_REJECTED);
77
78
  case CallEventType::CALL_ENDED:
78
79
  return clazz->getStaticFieldValue(fieldCALL_ENDED);
80
+ case CallEventType::DTMF_TONE:
81
+ return clazz->getStaticFieldValue(fieldDTMF_TONE);
79
82
  default:
80
83
  std::string stringValue = std::to_string(static_cast<int>(value));
81
84
  throw std::invalid_argument("Invalid enum value (" + stringValue + "!");
@@ -26,7 +26,8 @@ enum class CallEventType {
26
26
  CALL_UNMUTED,
27
27
  CALL_ANSWERED,
28
28
  CALL_REJECTED,
29
- CALL_ENDED;
29
+ CALL_ENDED,
30
+ DTMF_TONE;
30
31
 
31
32
  @DoNotStrip
32
33
  @Keep
@@ -39,6 +39,8 @@ public extension CallEventType {
39
39
  self = .callRejected
40
40
  case "CALL_ENDED":
41
41
  self = .callEnded
42
+ case "DTMF_TONE":
43
+ self = .dtmfTone
42
44
  default:
43
45
  return nil
44
46
  }
@@ -71,6 +73,8 @@ public extension CallEventType {
71
73
  return "CALL_REJECTED"
72
74
  case .callEnded:
73
75
  return "CALL_ENDED"
76
+ case .dtmfTone:
77
+ return "DTMF_TONE"
74
78
  }
75
79
  }
76
80
  }
@@ -40,6 +40,7 @@ namespace margelo::nitro::qusaieilouti99_callmanager {
40
40
  CALL_ANSWERED SWIFT_NAME(callAnswered) = 8,
41
41
  CALL_REJECTED SWIFT_NAME(callRejected) = 9,
42
42
  CALL_ENDED SWIFT_NAME(callEnded) = 10,
43
+ DTMF_TONE SWIFT_NAME(dtmfTone) = 11,
43
44
  } CLOSED_ENUM;
44
45
 
45
46
  } // namespace margelo::nitro::qusaieilouti99_callmanager
@@ -65,6 +66,7 @@ namespace margelo::nitro {
65
66
  case hashString("CALL_ANSWERED"): return CallEventType::CALL_ANSWERED;
66
67
  case hashString("CALL_REJECTED"): return CallEventType::CALL_REJECTED;
67
68
  case hashString("CALL_ENDED"): return CallEventType::CALL_ENDED;
69
+ case hashString("DTMF_TONE"): return CallEventType::DTMF_TONE;
68
70
  default: [[unlikely]]
69
71
  throw std::invalid_argument("Cannot convert \"" + unionValue + "\" to enum CallEventType - invalid value!");
70
72
  }
@@ -82,6 +84,7 @@ namespace margelo::nitro {
82
84
  case CallEventType::CALL_ANSWERED: return JSIConverter<std::string>::toJSI(runtime, "CALL_ANSWERED");
83
85
  case CallEventType::CALL_REJECTED: return JSIConverter<std::string>::toJSI(runtime, "CALL_REJECTED");
84
86
  case CallEventType::CALL_ENDED: return JSIConverter<std::string>::toJSI(runtime, "CALL_ENDED");
87
+ case CallEventType::DTMF_TONE: return JSIConverter<std::string>::toJSI(runtime, "DTMF_TONE");
85
88
  default: [[unlikely]]
86
89
  throw std::invalid_argument("Cannot convert CallEventType to JS - invalid value: "
87
90
  + std::to_string(static_cast<int>(arg)) + "!");
@@ -104,6 +107,7 @@ namespace margelo::nitro {
104
107
  case hashString("CALL_ANSWERED"):
105
108
  case hashString("CALL_REJECTED"):
106
109
  case hashString("CALL_ENDED"):
110
+ case hashString("DTMF_TONE"):
107
111
  return true;
108
112
  default:
109
113
  return false;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@qusaieilouti99/call-manager",
3
- "version": "0.1.34",
3
+ "version": "0.1.35",
4
4
  "description": "Call manager",
5
5
  "main": "./lib/module/index.js",
6
6
  "types": "./lib/typescript/src/index.d.ts",
@@ -10,4 +10,5 @@ export type CallEventType =
10
10
  | 'CALL_UNMUTED'
11
11
  | 'CALL_ANSWERED'
12
12
  | 'CALL_REJECTED'
13
- | 'CALL_ENDED';
13
+ | 'CALL_ENDED'
14
+ | 'DTMF_TONE'; // ADD THIS LINE IF YOU NEED DTMF EVENTS