rns-nativecall 1.1.7 → 1.1.9
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.
|
@@ -61,7 +61,6 @@ class CallModule(
|
|
|
61
61
|
}
|
|
62
62
|
}
|
|
63
63
|
|
|
64
|
-
// Inside your CallModule class
|
|
65
64
|
@ReactMethod
|
|
66
65
|
fun checkOverlayPermission(promise: Promise) {
|
|
67
66
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
|
@@ -71,6 +70,16 @@ class CallModule(
|
|
|
71
70
|
}
|
|
72
71
|
}
|
|
73
72
|
|
|
73
|
+
@ReactMethod
|
|
74
|
+
fun reportRemoteEnded(
|
|
75
|
+
uuid: String,
|
|
76
|
+
reason: String,
|
|
77
|
+
promise: Promise,
|
|
78
|
+
) {
|
|
79
|
+
NativeCallManager.dismissIncomingCall(reactApplicationContext, uuid)
|
|
80
|
+
promise.resolve(true)
|
|
81
|
+
}
|
|
82
|
+
|
|
74
83
|
@ReactMethod
|
|
75
84
|
fun requestOverlayPermission() {
|
|
76
85
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
|
@@ -86,10 +95,39 @@ class CallModule(
|
|
|
86
95
|
}
|
|
87
96
|
}
|
|
88
97
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
98
|
+
@ReactMethod
|
|
99
|
+
fun checkFullScreenIntentPermission(promise: Promise) {
|
|
100
|
+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
|
|
101
|
+
val notificationManager = reactApplicationContext.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
|
|
102
|
+
promise.resolve(notificationManager.canUseFullScreenIntent())
|
|
103
|
+
} else {
|
|
104
|
+
// Automatically true for Android 13 and below
|
|
105
|
+
promise.resolve(true)
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
@ReactMethod
|
|
110
|
+
fun openFullScreenIntentSettings() {
|
|
111
|
+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
|
|
112
|
+
try {
|
|
113
|
+
val intent =
|
|
114
|
+
Intent(Settings.ACTION_MANAGE_APP_USE_FULL_SCREEN_INTENT).apply {
|
|
115
|
+
data = Uri.fromParts("package", reactApplicationContext.packageName, null)
|
|
116
|
+
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
|
117
|
+
}
|
|
118
|
+
reactApplicationContext.startActivity(intent)
|
|
119
|
+
} catch (e: Exception) {
|
|
120
|
+
// Fallback for some OEM skins that might not support the direct intent
|
|
121
|
+
val intent =
|
|
122
|
+
Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS).apply {
|
|
123
|
+
data = Uri.fromParts("package", reactApplicationContext.packageName, null)
|
|
124
|
+
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
|
125
|
+
}
|
|
126
|
+
reactApplicationContext.startActivity(intent)
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
93
131
|
@ReactMethod
|
|
94
132
|
fun checkCallValidity(
|
|
95
133
|
uuid: String,
|
|
@@ -107,22 +145,6 @@ class CallModule(
|
|
|
107
145
|
promise.resolve(map)
|
|
108
146
|
}
|
|
109
147
|
|
|
110
|
-
@ReactMethod
|
|
111
|
-
fun stopForegroundService(promise: Promise) {
|
|
112
|
-
try {
|
|
113
|
-
// Using the current react context to stop the service
|
|
114
|
-
val intent = Intent(reactApplicationContext, CallForegroundService::class.java)
|
|
115
|
-
reactApplicationContext.stopService(intent)
|
|
116
|
-
promise.resolve(true)
|
|
117
|
-
} catch (e: Exception) {
|
|
118
|
-
promise.reject("SERVICE_STOP_ERROR", e.message)
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
/**
|
|
123
|
-
* General Status Check:
|
|
124
|
-
* Useful for checking if the UI is still relevant.
|
|
125
|
-
*/
|
|
126
148
|
@ReactMethod
|
|
127
149
|
fun checkCallStatus(
|
|
128
150
|
uuid: String,
|
|
@@ -117,6 +117,14 @@ object NativeCallManager {
|
|
|
117
117
|
}
|
|
118
118
|
}
|
|
119
119
|
|
|
120
|
+
fun refreshNotificationOnly(
|
|
121
|
+
context: Context,
|
|
122
|
+
uuid: String,
|
|
123
|
+
) {
|
|
124
|
+
val manager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
|
|
125
|
+
manager.cancel(uuid.hashCode())
|
|
126
|
+
}
|
|
127
|
+
|
|
120
128
|
fun dismissIncomingCall(
|
|
121
129
|
context: Context,
|
|
122
130
|
uuid: String?,
|
package/index.d.ts
CHANGED
|
@@ -48,7 +48,7 @@ export interface CallHandlerType {
|
|
|
48
48
|
/**
|
|
49
49
|
* Registers the background "Headless" task.
|
|
50
50
|
* This is the bridge between a Native push notification and your React Native UI.
|
|
51
|
-
*
|
|
51
|
+
* @param callback Async function receiving call data and the event type.
|
|
52
52
|
*/
|
|
53
53
|
registerHeadlessTask(
|
|
54
54
|
callback: (data: CallData, eventType: CallEventType) => Promise<void>
|
|
@@ -57,7 +57,7 @@ export interface CallHandlerType {
|
|
|
57
57
|
/**
|
|
58
58
|
* Displays the full-screen Native Incoming Call UI.
|
|
59
59
|
* Usually called inside registerHeadlessTask after validating the call.
|
|
60
|
-
*
|
|
60
|
+
* @param uuid The unique call ID.
|
|
61
61
|
* @param name Name to display on the call screen.
|
|
62
62
|
* @param callType Defaults to 'audio'.
|
|
63
63
|
* @returns Promise resolving to true if the UI was successfully launched.
|
|
@@ -65,7 +65,7 @@ export interface CallHandlerType {
|
|
|
65
65
|
displayCall(
|
|
66
66
|
uuid: string,
|
|
67
67
|
name: string,
|
|
68
|
-
callType
|
|
68
|
+
callType?: 'audio' | 'video',
|
|
69
69
|
): Promise<boolean>;
|
|
70
70
|
|
|
71
71
|
/** * Checks if a call is still valid and has not been canceled.
|
|
@@ -78,12 +78,36 @@ export interface CallHandlerType {
|
|
|
78
78
|
*/
|
|
79
79
|
checkCallStatus(uuid: string): Promise<CallStatus>;
|
|
80
80
|
|
|
81
|
-
|
|
81
|
+
/**
|
|
82
|
+
* [Android 14+] Checks if the user has granted the special "Full Screen Intent" permission.
|
|
83
|
+
* This is required for the call to show over the lockscreen.
|
|
84
|
+
*/
|
|
85
|
+
checkFullScreenPermission(): Promise<boolean>;
|
|
82
86
|
|
|
83
|
-
|
|
87
|
+
/**
|
|
88
|
+
* [Android 14+] Opens the specific system settings page for the user to
|
|
89
|
+
* manually enable "Full Screen Intent" if it was revoked.
|
|
90
|
+
*/
|
|
91
|
+
requestFullScreenSettings(): void;
|
|
84
92
|
|
|
93
|
+
/**
|
|
94
|
+
* Checks if the app has permission to draw over other apps (Overlay).
|
|
95
|
+
* Necessary for showing the "Pill" or answer buttons while the phone is unlocked.
|
|
96
|
+
*/
|
|
85
97
|
checkOverlayPermission(): Promise<boolean>;
|
|
86
98
|
|
|
99
|
+
/**
|
|
100
|
+
* Opens system settings to request the "Draw over other apps" (Overlay) permission.
|
|
101
|
+
*/
|
|
102
|
+
requestOverlayPermission(): void;
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Reports to the native side that the remote party has terminated the call.
|
|
106
|
+
* @param uuid The unique call ID.
|
|
107
|
+
* @param reason Custom string reason (e.g., "RemoteEnded", "TimedOut").
|
|
108
|
+
*/
|
|
109
|
+
reportRemoteEnded(uuid: string, reason?: string): Promise<void>;
|
|
110
|
+
|
|
87
111
|
/**
|
|
88
112
|
* Forcefully dismisses the native call UI, stops the ringtone, and clears state.
|
|
89
113
|
* Use this when the call is hung up or timed out.
|
|
@@ -105,7 +129,7 @@ export interface CallHandlerType {
|
|
|
105
129
|
|
|
106
130
|
/**
|
|
107
131
|
* Subscribes to user interactions on the Native Call UI.
|
|
108
|
-
*
|
|
132
|
+
* @param onAccept Callback when user presses the Answer button.
|
|
109
133
|
* @param onReject Callback when user presses the Decline button.
|
|
110
134
|
* @param onFailed Callback for system errors (optional).
|
|
111
135
|
* @returns A cleanup function to unsubscribe.
|
package/index.js
CHANGED
|
@@ -2,12 +2,18 @@ import {
|
|
|
2
2
|
NativeModules,
|
|
3
3
|
NativeEventEmitter,
|
|
4
4
|
AppRegistry,
|
|
5
|
+
Platform,
|
|
5
6
|
} from 'react-native';
|
|
6
7
|
|
|
7
8
|
const { CallModule } = NativeModules;
|
|
8
9
|
const callEventEmitter = CallModule ? new NativeEventEmitter(CallModule) : null;
|
|
9
10
|
|
|
10
11
|
export const CallHandler = {
|
|
12
|
+
/**
|
|
13
|
+
* REGISTER HEADLESS TASK
|
|
14
|
+
* This is the "Engine" that listens for incoming push notifications
|
|
15
|
+
* while the app is killed or in the background.
|
|
16
|
+
*/
|
|
11
17
|
registerHeadlessTask: (onAction) => {
|
|
12
18
|
AppRegistry.registerHeadlessTask('ColdStartCallTask', () => async (data) => {
|
|
13
19
|
const { callUuid, isBusySignal } = data;
|
|
@@ -19,64 +25,81 @@ export const CallHandler = {
|
|
|
19
25
|
return;
|
|
20
26
|
}
|
|
21
27
|
|
|
22
|
-
//
|
|
28
|
+
// Check if the call is still valid before showing UI
|
|
23
29
|
const status = await CallModule.checkCallValidity(uuid);
|
|
24
30
|
if (!status.isValid) {
|
|
25
31
|
if (onAction) await onAction(data, 'ABORTED_CALL');
|
|
26
32
|
return;
|
|
27
33
|
}
|
|
28
34
|
|
|
29
|
-
if (onAction)
|
|
30
|
-
await onAction(data, 'INCOMING_CALL');
|
|
31
|
-
}
|
|
35
|
+
if (onAction) await onAction(data, 'INCOMING_CALL');
|
|
32
36
|
} catch (error) {
|
|
33
37
|
console.error('[RNSNativeCall] Headless Task Error:', error);
|
|
34
38
|
}
|
|
35
39
|
});
|
|
36
40
|
},
|
|
37
41
|
|
|
38
|
-
// ---
|
|
39
|
-
checkCallValidity: async (uuid) => {
|
|
40
|
-
if (!CallModule?.checkCallValidity) return { isValid: false, isCanceled: true };
|
|
41
|
-
return await CallModule.checkCallValidity(uuid.toLowerCase().trim());
|
|
42
|
-
},
|
|
43
|
-
|
|
44
|
-
checkCallStatus: async (uuid) => {
|
|
45
|
-
if (!CallModule?.checkCallStatus) return { isCanceled: true, isActive: false, shouldDisplay: false };
|
|
46
|
-
return await CallModule.checkCallStatus(uuid.toLowerCase().trim());
|
|
47
|
-
},
|
|
48
|
-
// ------------------------------------
|
|
42
|
+
// --- PERMISSION HEALTH CHECKS ---
|
|
49
43
|
|
|
50
|
-
|
|
51
|
-
if (!CallModule?.
|
|
52
|
-
await CallModule.
|
|
44
|
+
checkFullScreenPermission: async () => {
|
|
45
|
+
if (Platform.OS !== 'android' || !CallModule?.checkFullScreenIntentPermission) return true;
|
|
46
|
+
return await CallModule.checkFullScreenIntentPermission();
|
|
53
47
|
},
|
|
54
48
|
|
|
55
|
-
|
|
56
|
-
if (
|
|
57
|
-
|
|
49
|
+
openFullScreenSettings: () => {
|
|
50
|
+
if (Platform.OS === 'android' && CallModule?.openFullScreenIntentSettings) {
|
|
51
|
+
CallModule.openFullScreenIntentSettings();
|
|
52
|
+
}
|
|
58
53
|
},
|
|
59
54
|
|
|
60
55
|
checkOverlayPermission: async () => {
|
|
61
|
-
if (!CallModule?.checkOverlayPermission) return
|
|
56
|
+
if (Platform.OS !== 'android' || !CallModule?.checkOverlayPermission) return true;
|
|
62
57
|
return await CallModule.checkOverlayPermission();
|
|
63
58
|
},
|
|
64
59
|
|
|
60
|
+
requestFullScreenSettings: () => {
|
|
61
|
+
if (Platform.OS === 'android' && CallModule?.requestOverlayPermission) {
|
|
62
|
+
CallModule.requestOverlayPermission();
|
|
63
|
+
}
|
|
64
|
+
},
|
|
65
|
+
|
|
66
|
+
// --- CALL LIFECYCLE ACTIONS ---
|
|
67
|
+
|
|
65
68
|
displayCall: async (uuid, name, callType = "audio") => {
|
|
66
69
|
if (!CallModule) return false;
|
|
67
70
|
return await CallModule.displayIncomingCall(uuid.toLowerCase().trim(), name, callType);
|
|
68
71
|
},
|
|
69
72
|
|
|
73
|
+
/** Stops the UI and the Foreground Service */
|
|
74
|
+
destroyNativeCallUI: (uuid) => {
|
|
75
|
+
if (CallModule?.endNativeCall) {
|
|
76
|
+
CallModule.endNativeCall(uuid.toLowerCase().trim());
|
|
77
|
+
}
|
|
78
|
+
},
|
|
79
|
+
|
|
80
|
+
/** Reports to the native side that the other person hung up */
|
|
81
|
+
reportRemoteEnded: async (uuid, endReason = "RemoteEnded") => {
|
|
82
|
+
if (CallModule?.reportRemoteEnded) {
|
|
83
|
+
await CallModule.reportRemoteEnded(uuid.toLowerCase().trim(), endReason);
|
|
84
|
+
}
|
|
85
|
+
},
|
|
86
|
+
|
|
70
87
|
stopForegroundService: async () => {
|
|
71
88
|
if (CallModule?.stopForegroundService) {
|
|
72
89
|
await CallModule.stopForegroundService();
|
|
73
90
|
}
|
|
74
91
|
},
|
|
75
92
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
}
|
|
93
|
+
// --- STATE VERIFICATION ---
|
|
94
|
+
|
|
95
|
+
checkCallValidity: async (uuid) => {
|
|
96
|
+
if (!CallModule?.checkCallValidity) return { isValid: false, isCanceled: true };
|
|
97
|
+
return await CallModule.checkCallValidity(uuid.toLowerCase().trim());
|
|
98
|
+
},
|
|
99
|
+
|
|
100
|
+
checkCallStatus: async (uuid) => {
|
|
101
|
+
if (!CallModule?.checkCallStatus) return { isCanceled: true, isActive: false, shouldDisplay: false };
|
|
102
|
+
return await CallModule.checkCallStatus(uuid.toLowerCase().trim());
|
|
80
103
|
},
|
|
81
104
|
|
|
82
105
|
getInitialCallData: async () => {
|
|
@@ -84,6 +107,8 @@ export const CallHandler = {
|
|
|
84
107
|
return await CallModule.getInitialCallData();
|
|
85
108
|
},
|
|
86
109
|
|
|
110
|
+
// --- EVENT SUBSCRIPTION ---
|
|
111
|
+
|
|
87
112
|
subscribe: (onAccept, onReject, onFailed) => {
|
|
88
113
|
if (!callEventEmitter) return () => { };
|
|
89
114
|
const subs = [
|