@qusaieilouti99/call-manager 0.1.199 → 0.1.201

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.
@@ -247,4 +247,11 @@ class AudioManager {
247
247
  return "Earpiece"
248
248
  }
249
249
  switch output.portType {
250
- case
250
+ case .bluetoothHFP, .bluetoothA2DP, .bluetoothLE: return "Bluetooth"
251
+ case .builtInSpeaker: return "Speaker"
252
+ case .builtInReceiver: return "Earpiece"
253
+ case .headphones, .headsetMic: return "Headset"
254
+ default: return "Earpiece"
255
+ }
256
+ }
257
+ }
@@ -315,7 +315,13 @@ class CallEngine {
315
315
 
316
316
  private func updateOverallIdleTimerDisabledState() {
317
317
  let shouldDisable = manualIdleTimerDisabled || hasActiveCalls()
318
+
318
319
  DispatchQueue.main.async {
320
+ // Only modify UI if app is in foreground/active state
321
+ guard UIApplication.shared.applicationState == .active else {
322
+ self.logger.info("Skipping idle timer update - app not active")
323
+ return
324
+ }
319
325
  UIApplication.shared.isIdleTimerDisabled = shouldDisable
320
326
  }
321
327
  }
@@ -3,6 +3,9 @@ import NitroModules
3
3
  import OSLog
4
4
  import UIKit
5
5
 
6
+ /// The public-facing bridge class that implements the `HybridCallManagerSpec` protocol.
7
+ /// Its responsibility is to translate calls from the JavaScript layer into commands
8
+ /// for the `CallEngine` singleton. It acts as a thin, safe interface to the native call logic.
6
9
  public class CallManager: HybridCallManagerSpec {
7
10
  private let logger = Logger(
8
11
  subsystem: "com.qusaieilouti99.callmanager",
@@ -53,6 +56,7 @@ public class CallManager: HybridCallManagerSpec {
53
56
  self.logger.debug("🎯 event \(event.stringValue), payload.len=\(payload.count)")
54
57
  listener(event, payload)
55
58
  }
59
+ // Return a closure that will be called by the JS layer to unsubscribe.
56
60
  return {
57
61
  self.logger.info("🎯 removeListener ▶ js → native")
58
62
  CallEngine.shared.setEventHandler(nil)
@@ -150,10 +154,12 @@ public class CallManager: HybridCallManagerSpec {
150
154
  }
151
155
 
152
156
  public func requestOverlayPermissionAndroid() throws -> Bool {
157
+ // This is an Android-specific method, so we provide a no-op success case for iOS.
153
158
  return true
154
159
  }
155
160
 
156
161
  public func hasOverlayPermissionAndroid() throws -> Bool {
162
+ // This is an Android-specific method, so we provide a no-op success case for iOS.
157
163
  return true
158
164
  }
159
165
  }
@@ -2,7 +2,12 @@ import Foundation
2
2
  import PushKit
3
3
  import OSLog
4
4
 
5
+ /// Manages the VoIP push token lifecycle using Apple's PushKit framework.
6
+ /// This class is a singleton responsible for registering for VoIP pushes, receiving the token,
7
+ /// and handling incoming push payloads that represent new calls.
5
8
  class VoIPTokenManager: NSObject, PKPushRegistryDelegate {
9
+ // MARK: - Singleton and Properties
10
+
6
11
  static let shared = VoIPTokenManager()
7
12
  private let logger = Logger(
8
13
  subsystem: "com.qusaieilouti99.callmanager",
@@ -14,39 +19,49 @@ class VoIPTokenManager: NSObject, PKPushRegistryDelegate {
14
19
 
15
20
  private override init() {
16
21
  super.init()
17
- logger.info("VoIPTokenManager init")
22
+ logger.info("VoIPTokenManager singleton created.")
18
23
  }
19
24
 
25
+ // MARK: - Public Methods
26
+
27
+ /// Configures the PushKit registry. This should be called once when the app's call engine is initialized.
20
28
  func setupPushKit() {
21
29
  guard pushRegistry == nil else {
22
- logger.info("PushKit already setup")
30
+ logger.info("PushKit already set up. Ignoring.")
23
31
  return
24
32
  }
25
33
  pushRegistry = PKPushRegistry(queue: .main)
26
34
  pushRegistry?.delegate = self
27
35
  pushRegistry?.desiredPushTypes = [.voIP]
28
- logger.info("PushKit registry configured")
36
+ logger.info("PushKit registry configured.")
29
37
  }
30
38
 
39
+ /// Registers a listener (typically from the JS bridge) to receive the VoIP token.
31
40
  func registerTokenListener(_ listener: @escaping (String) -> Void) {
32
- logger.info("registerTokenListener")
41
+ logger.info("Registering VoIP token listener.")
33
42
  tokenListener = listener
43
+ // If the token has already been received, provide it to the new listener immediately.
34
44
  if let t = cachedToken {
35
- logger.info("returning cached token")
45
+ logger.info("Providing cached VoIP token to new listener.")
36
46
  listener(t)
37
47
  }
38
48
  }
39
49
 
50
+ /// Unregisters the token listener.
40
51
  func unregisterTokenListener() {
41
- logger.info("unregisterTokenListener")
52
+ logger.info("Unregistering VoIP token listener.")
42
53
  tokenListener = nil
43
54
  }
44
55
 
56
+ // MARK: - PKPushRegistryDelegate
57
+
58
+ /// Called by the system when a new VoIP push token is available or has been updated.
45
59
  func pushRegistry(
46
60
  _ registry: PKPushRegistry,
47
61
  didUpdate pushCredentials: PKPushCredentials,
48
62
  for type: PKPushType
49
63
  ) {
64
+ // Convert the token data to a hex string for transmission.
50
65
  let token = pushCredentials.token
51
66
  .map { String(format: "%02.2hhx", $0) }
52
67
  .joined()
@@ -55,24 +70,42 @@ class VoIPTokenManager: NSObject, PKPushRegistryDelegate {
55
70
  tokenListener?(token)
56
71
  }
57
72
 
73
+ /// Called by the system when the VoIP push token has been invalidated.
58
74
  func pushRegistry(_ registry: PKPushRegistry, didInvalidatePushTokenFor type: PKPushType) {
59
75
  logger.warning("VoIP token invalidated.")
60
76
  cachedToken = nil
61
77
  tokenListener?("")
62
78
  }
63
79
 
64
- // MARK: - PKPushRegistryDelegate
80
+ /// This is the entry point for an incoming VoIP call when the app is in the background or killed.
81
+ /// It is CRITICAL that the `completion` handler is called in all code paths to avoid the
82
+ /// system terminating the app.
65
83
  func pushRegistry(
66
84
  _ registry: PKPushRegistry,
67
85
  didReceiveIncomingPushWith payload: PKPushPayload,
68
86
  for type: PKPushType,
69
87
  completion: @escaping () -> Void
70
88
  ) {
71
-
72
89
  logger.info("🔔 Received VoIP Push")
73
90
 
91
+ // Initialize if not already initialized
92
+ CallEngine.shared.initialize()
93
+
94
+ // *** THIS IS THE WATCHDOG TIMER ***
95
+ // It creates a failsafe that will call the completion handler after 10 seconds
96
+ // if our main logic hangs or fails silently, preventing the OS from killing the app.
97
+ let completionTimeout = DispatchWorkItem {
98
+ self.logger.error("⚠️ VoIP push handling timed out. Forcing completion.")
99
+ completion()
100
+ }
101
+
102
+ DispatchQueue.main.asyncAfter(deadline: .now() + 10, execute: completionTimeout)
103
+
104
+ // The payload is expected to have a specific structure.
105
+ // Using a key like "custom_payload" or "data" is common.
74
106
  guard let info = payload.dictionaryPayload["custom_payload"] as? [AnyHashable: Any] else {
75
107
  logger.error("❌ Invalid payload format or missing 'custom_payload' dictionary.")
108
+ completionTimeout.cancel() // Defuse the timer
76
109
  completion()
77
110
  return
78
111
  }
@@ -85,6 +118,7 @@ class VoIPTokenManager: NSObject, PKPushRegistryDelegate {
85
118
  .error(
86
119
  "❌ Missing required fields in VoIP payload: callId, callType, or name."
87
120
  )
121
+ completionTimeout.cancel() // Defuse the timer
88
122
  completion()
89
123
  return
90
124
  }
@@ -93,7 +127,9 @@ class VoIPTokenManager: NSObject, PKPushRegistryDelegate {
93
127
  let metadata = info["metadata"] as? String
94
128
 
95
129
  logger.info("📞 Reporting incoming call from VoIP push: \(callId)")
96
- // *** FIX: Updated to match the corrected CallEngine signature ***
130
+
131
+ // Delegate the call reporting to the CallEngine.
132
+ // The CallEngine is self-initializing, making this call safe.
97
133
  CallEngine.shared.reportIncomingCall(
98
134
  callId: callId,
99
135
  callType: callType,
@@ -102,6 +138,8 @@ class VoIPTokenManager: NSObject, PKPushRegistryDelegate {
102
138
  metadata: metadata
103
139
  ) { success in
104
140
  self.logger.info("📞 CallKit report from VoIP push completed. Success: \(success)")
141
+ // This is the crucial call to satisfy the PushKit watchdog.
142
+ completionTimeout.cancel() // Defuse the timer
105
143
  completion()
106
144
  }
107
145
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@qusaieilouti99/call-manager",
3
- "version": "0.1.199",
3
+ "version": "0.1.201",
4
4
  "description": "Call manager",
5
5
  "main": "./lib/module/index.js",
6
6
  "types": "./lib/typescript/src/index.d.ts",