rns-nativecall 1.2.0 → 1.2.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.
- package/README.md +11 -12
- package/android/src/main/java/com/rnsnativecall/CallModule.kt +1 -1
- package/index.d.ts +5 -5
- package/index.js +40 -34
- package/ios/CallModule.m +66 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -132,18 +132,17 @@ export default function App() {
|
|
|
132
132
|
|
|
133
133
|
| Method | Platform | Description |
|
|
134
134
|
| :--- | :--- | :--- |
|
|
135
|
-
| **registerHeadlessTask(callback)** |
|
|
136
|
-
| **
|
|
137
|
-
| **
|
|
138
|
-
| **
|
|
139
|
-
| **
|
|
140
|
-
| **
|
|
141
|
-
| **
|
|
142
|
-
| **
|
|
143
|
-
| **
|
|
144
|
-
| **
|
|
145
|
-
| **
|
|
146
|
-
| **subscribe(onAccept, onReject, onFailed)** | Android | Listens for native Answer/Decline button presses and system errors. |
|
|
135
|
+
| **registerHeadlessTask(callback)** | All | Registers the background task. `eventType` is 'INCOMING_CALL', 'BUSY', or 'ABORTED_CALL'. |
|
|
136
|
+
| **displayCall(uuid, name, type)** | All | Android: Launches full-screen Activity. iOS: Reports incoming call to CallKit. |
|
|
137
|
+
| **checkCallValidity(uuid)** | All | Android only: Returns {isValid, isCanceled} to prevent ghost/canceled calls. |
|
|
138
|
+
| **checkCallStatus(uuid)** | All | Android only: Returns {isCanceled, isActive, shouldDisplay} for UI syncing. |
|
|
139
|
+
| **checkOverlayPermission()** | Android | Android only: Returns true if app can draw over other apps while unlocked. |
|
|
140
|
+
| **checkFullScreenPermission()** | Android | Android 14+: Checks if app can trigger full-screen intents on lockscreen. |
|
|
141
|
+
| **requestOverlayPermission()** | Android | Android only: Navigates user to "Draw over other apps" system settings. |
|
|
142
|
+
| **requestFullScreenSettings()** | Android | Android 14+: Navigates user to "Full Screen Intent" system settings. |
|
|
143
|
+
| **destroyNativeCallUI(uuid)** | All | Android: Stops ringtone/Activity. iOS: Ends CallKit (Handoff vs Hangup logic). |
|
|
144
|
+
| **getInitialCallData()** | All | Retrieves the call payload if the app was cold-started via a notification Answer. |
|
|
145
|
+
| **subscribe(onAccept, onReject, onFailed)** | All | Listens for Answer/Decline button presses and system-level bridge errors. |
|
|
147
146
|
---
|
|
148
147
|
|
|
149
148
|
# Implementation Notes
|
|
@@ -96,7 +96,7 @@ class CallModule(
|
|
|
96
96
|
}
|
|
97
97
|
|
|
98
98
|
@ReactMethod
|
|
99
|
-
fun
|
|
99
|
+
fun checkFullScreenPermission(promise: Promise) {
|
|
100
100
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
|
|
101
101
|
val notificationManager = reactApplicationContext.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
|
|
102
102
|
promise.resolve(notificationManager.canUseFullScreenIntent())
|
package/index.d.ts
CHANGED
|
@@ -75,6 +75,11 @@ export interface CallHandlerType {
|
|
|
75
75
|
*/
|
|
76
76
|
checkOverlayPermission(): Promise<boolean>;
|
|
77
77
|
|
|
78
|
+
/**
|
|
79
|
+
* Checks if the app has permission to use full screen intent.
|
|
80
|
+
*/
|
|
81
|
+
checkFullScreenPermission(): Promise<boolean>;
|
|
82
|
+
|
|
78
83
|
/**
|
|
79
84
|
* Opens system settings to request the "Draw over other apps" (Overlay) permission.
|
|
80
85
|
* Note: Returns a promise that resolves to false if module is missing.
|
|
@@ -86,11 +91,6 @@ export interface CallHandlerType {
|
|
|
86
91
|
*/
|
|
87
92
|
requestFullScreenSettings(): Promise<boolean | void>;
|
|
88
93
|
|
|
89
|
-
/**
|
|
90
|
-
* Reports to the native side that the remote party has terminated the call.
|
|
91
|
-
*/
|
|
92
|
-
reportRemoteEnded(uuid: string, reason?: string): Promise<void>;
|
|
93
|
-
|
|
94
94
|
/**
|
|
95
95
|
* Forcefully dismisses the native call UI, stops the ringtone, and clears state.
|
|
96
96
|
*/
|
package/index.js
CHANGED
|
@@ -2,6 +2,7 @@ import {
|
|
|
2
2
|
NativeModules,
|
|
3
3
|
NativeEventEmitter,
|
|
4
4
|
AppRegistry,
|
|
5
|
+
Platform,
|
|
5
6
|
} from 'react-native';
|
|
6
7
|
|
|
7
8
|
const { CallModule } = NativeModules;
|
|
@@ -36,38 +37,6 @@ export const CallHandler = {
|
|
|
36
37
|
});
|
|
37
38
|
},
|
|
38
39
|
|
|
39
|
-
// --- Added missing bridge methods ---
|
|
40
|
-
checkCallValidity: async (uuid) => {
|
|
41
|
-
if (!CallModule?.checkCallValidity) return { isValid: false, isCanceled: true };
|
|
42
|
-
return await CallModule.checkCallValidity(uuid.toLowerCase().trim());
|
|
43
|
-
},
|
|
44
|
-
|
|
45
|
-
checkCallStatus: async (uuid) => {
|
|
46
|
-
if (!CallModule?.checkCallStatus) return { isCanceled: true, isActive: false, shouldDisplay: false };
|
|
47
|
-
return await CallModule.checkCallStatus(uuid.toLowerCase().trim());
|
|
48
|
-
},
|
|
49
|
-
// ------------------------------------
|
|
50
|
-
|
|
51
|
-
reportRemoteEnded: async (uuid, endReason) => {
|
|
52
|
-
if (!CallModule?.reportRemoteEnded) return;
|
|
53
|
-
await CallModule.reportRemoteEnded(uuid.toLowerCase().trim(), endReason);
|
|
54
|
-
},
|
|
55
|
-
|
|
56
|
-
requestOverlayPermission: async () => {
|
|
57
|
-
if (!CallModule?.requestOverlayPermission) return false;
|
|
58
|
-
return await CallModule.requestOverlayPermission();
|
|
59
|
-
},
|
|
60
|
-
|
|
61
|
-
requestFullScreenSettings: async () => {
|
|
62
|
-
if (!CallModule?.requestFullScreenSettings) return false;
|
|
63
|
-
return await CallModule.requestFullScreenSettings();
|
|
64
|
-
},
|
|
65
|
-
|
|
66
|
-
checkOverlayPermission: async () => {
|
|
67
|
-
if (!CallModule?.checkOverlayPermission) return false;
|
|
68
|
-
return await CallModule.checkOverlayPermission();
|
|
69
|
-
},
|
|
70
|
-
|
|
71
40
|
displayCall: async (uuid, name, callType = "audio") => {
|
|
72
41
|
if (!CallModule) return false;
|
|
73
42
|
return await CallModule.displayIncomingCall(uuid.toLowerCase().trim(), name, callType);
|
|
@@ -79,7 +48,6 @@ export const CallHandler = {
|
|
|
79
48
|
}
|
|
80
49
|
},
|
|
81
50
|
|
|
82
|
-
|
|
83
51
|
getInitialCallData: async () => {
|
|
84
52
|
if (!CallModule?.getInitialCallData) return null;
|
|
85
53
|
const data = await CallModule.getInitialCallData();
|
|
@@ -98,7 +66,45 @@ export const CallHandler = {
|
|
|
98
66
|
subs.push(callEventEmitter.addListener('onCallFailed', onFailed));
|
|
99
67
|
}
|
|
100
68
|
return () => subs.forEach(s => s.remove());
|
|
101
|
-
}
|
|
69
|
+
},
|
|
70
|
+
|
|
71
|
+
checkCallValidity: async (uuid) => {
|
|
72
|
+
if (!CallModule?.checkCallValidity) return { isValid: false, isCanceled: true };
|
|
73
|
+
return await CallModule.checkCallValidity(uuid.toLowerCase().trim());
|
|
74
|
+
},
|
|
75
|
+
|
|
76
|
+
checkCallStatus: async (uuid) => {
|
|
77
|
+
if (!CallModule?.checkCallStatus) return { isCanceled: true, isActive: false, shouldDisplay: false };
|
|
78
|
+
return await CallModule.checkCallStatus(uuid.toLowerCase().trim());
|
|
79
|
+
},
|
|
80
|
+
|
|
81
|
+
//--------------------------------------------------------------------------------------
|
|
82
|
+
|
|
83
|
+
requestOverlayPermission: async () => {
|
|
84
|
+
if (Platform.OS === 'ios') return true;
|
|
85
|
+
if (!CallModule?.requestOverlayPermission) return false;
|
|
86
|
+
return await CallModule.requestOverlayPermission();
|
|
87
|
+
},
|
|
88
|
+
|
|
89
|
+
requestFullScreenSettings: async () => {
|
|
90
|
+
if (Platform.OS === 'ios') return true;
|
|
91
|
+
if (!CallModule?.requestFullScreenSettings) return false;
|
|
92
|
+
return await CallModule.requestFullScreenSettings();
|
|
93
|
+
},
|
|
94
|
+
|
|
95
|
+
checkOverlayPermission: async () => {
|
|
96
|
+
if (Platform.OS === 'ios') return true;
|
|
97
|
+
if (!CallModule?.checkOverlayPermission) return false;
|
|
98
|
+
return await CallModule.checkOverlayPermission();
|
|
99
|
+
},
|
|
100
|
+
|
|
101
|
+
checkFullScreenPermission: async () => {
|
|
102
|
+
if (Platform.OS === 'ios') return true;
|
|
103
|
+
if (!CallModule?.checkFullScreenPermission) return false;
|
|
104
|
+
return await CallModule.checkFullScreenPermission();
|
|
105
|
+
},
|
|
106
|
+
|
|
107
|
+
//--------------------------------------------------------------------------------------
|
|
102
108
|
};
|
|
103
109
|
|
|
104
110
|
export default CallHandler;
|
package/ios/CallModule.m
CHANGED
|
@@ -137,6 +137,72 @@ RCT_EXPORT_METHOD(getInitialCallData:(RCTPromiseResolveBlock)resolve reject:(RCT
|
|
|
137
137
|
resolve(result.count > 0 ? result : [NSNull null]);
|
|
138
138
|
}
|
|
139
139
|
|
|
140
|
+
RCT_EXPORT_METHOD(checkCallValidity:(NSString *)uuidString
|
|
141
|
+
resolve:(RCTPromiseResolveBlock)resolve
|
|
142
|
+
reject:(RCTPromiseRejectBlock)reject)
|
|
143
|
+
{
|
|
144
|
+
NSUUID *uuid = [[NSUUID alloc] initWithUUIDString:uuidString];
|
|
145
|
+
if (!uuid) {
|
|
146
|
+
resolve(@{@"isValid": @NO, @"isCanceled": @YES});
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// On iOS, we check if the call is currently known to the system observer
|
|
151
|
+
CXCallObserver *callObserver = [[CXCallObserver alloc] init];
|
|
152
|
+
BOOL found = NO;
|
|
153
|
+
|
|
154
|
+
for (CXCall *call in callObserver.calls) {
|
|
155
|
+
if ([call.uuid.UUIDString isEqualToString:uuid.UUIDString]) {
|
|
156
|
+
if (!call.hasEnded) {
|
|
157
|
+
found = YES;
|
|
158
|
+
break;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// isValid: True if CallKit is currently tracking this call
|
|
164
|
+
// isCanceled: False if the call is active and valid
|
|
165
|
+
resolve(@{
|
|
166
|
+
@"isValid": @(found),
|
|
167
|
+
@"isCanceled": @(!found)
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
RCT_EXPORT_METHOD(checkCallStatus:(NSString *)uuidString
|
|
172
|
+
resolve:(RCTPromiseResolveBlock)resolve
|
|
173
|
+
reject:(RCTPromiseRejectBlock)reject)
|
|
174
|
+
{
|
|
175
|
+
NSUUID *uuid = [[NSUUID alloc] initWithUUIDString:uuidString];
|
|
176
|
+
if (!uuid) {
|
|
177
|
+
resolve(@{
|
|
178
|
+
@"isCanceled": @YES,
|
|
179
|
+
@"isActive": @NO,
|
|
180
|
+
@"shouldDisplay": @NO
|
|
181
|
+
});
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
CXCallObserver *callObserver = [[CXCallObserver alloc] init];
|
|
186
|
+
BOOL isActive = NO;
|
|
187
|
+
BOOL isCanceled = YES;
|
|
188
|
+
|
|
189
|
+
for (CXCall *call in callObserver.calls) {
|
|
190
|
+
if ([call.uuid.UUIDString isEqualToString:uuid.UUIDString]) {
|
|
191
|
+
if (!call.hasEnded) {
|
|
192
|
+
isActive = YES;
|
|
193
|
+
isCanceled = NO;
|
|
194
|
+
break;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
resolve(@{
|
|
200
|
+
@"isCanceled": @(isCanceled),
|
|
201
|
+
@"isActive": @(isActive),
|
|
202
|
+
@"shouldDisplay": @(isActive && !isCanceled)
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
|
|
140
206
|
// MARK: - CXProviderDelegate
|
|
141
207
|
|
|
142
208
|
- (void)provider:(CXProvider *)provider performAnswerCallAction:(CXAnswerCallAction *)action {
|