Skip to content

Commit 377ceef

Browse files
Merge pull request #41 from linhvovan29546/dev/example-callkeep
Dev/example callkeep
2 parents 0e78cc6 + 4557cda commit 377ceef

File tree

6 files changed

+201
-55
lines changed

6 files changed

+201
-55
lines changed

android/src/main/java/com/reactnativefullscreennotificationincomingcall/IncomingCallActivity.java

+1-2
Original file line numberDiff line numberDiff line change
@@ -83,8 +83,7 @@ protected void onCreate(Bundle savedInstanceState) {
8383
| WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON
8484
| WindowManager.LayoutParams.FLAG_ALLOW_LOCK_WHILE_SCREEN_ON);
8585
Bundle bundle = getIntent().getExtras();
86-
87-
if (bundle.containsKey("mainComponent")) {
86+
if (bundle.containsKey("mainComponent") && bundle.getString("mainComponent")!=null) {
8887
setContentView(R.layout.custom_ingcoming_call_rn);
8988
Fragment reactNativeFragment = new ReactFragment.Builder()
9089
.setComponentName(bundle.getString("mainComponent"))

example/android/app/src/main/AndroidManifest.xml

+19-2
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,12 @@
88
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
99
<uses-permission android:name="android.permission.WAKE_LOCK" />
1010
<uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
11-
11+
<!--callkeep-->
12+
<uses-permission android:name="android.permission.BIND_TELECOM_CONNECTION_SERVICE"/>
13+
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
14+
<uses-permission android:name="android.permission.CALL_PHONE" />
15+
<uses-permission android:name="android.permission.RECORD_AUDIO" />
16+
<uses-permission android:name="android.permission.READ_PHONE_NUMBERS" />
1217
<application
1318
android:name=".MainApplication"
1419
android:label="@string/app_name"
@@ -50,7 +55,19 @@
5055
android:enabled="true"
5156
android:stopWithTask="false"
5257
android:exported="true" />
53-
58+
<!--callkeep-->
59+
<service android:name="io.wazo.callkeep.VoiceConnectionService"
60+
android:label="Wazo"
61+
android:permission="android.permission.BIND_TELECOM_CONNECTION_SERVICE"
62+
android:foregroundServiceType="camera|microphone"
63+
android:exported="true"
64+
>
65+
66+
<intent-filter>
67+
<action android:name="android.telecom.ConnectionService" />
68+
</intent-filter>
69+
</service>
70+
<service android:name="io.wazo.callkeep.RNCallKeepBackgroundMessagingService" android:exported="true"/>
5471
</application>
5572

5673
</manifest>

example/package.json

+6-4
Original file line numberDiff line numberDiff line change
@@ -10,21 +10,23 @@
1010
},
1111
"dependencies": {
1212
"@react-native-community/masked-view": "^0.1.11",
13+
"@react-navigation/native": "^5.8.0",
14+
"@react-navigation/stack": "^5.10.0",
1315
"react": "17.0.2",
1416
"react-native": "0.65.1",
1517
"react-native-background-timer": "^2.4.1",
18+
"react-native-callkeep": "^4.3.8",
1619
"react-native-gesture-handler": "^1.10.3",
20+
"react-native-permissions": "^3.8.0",
1721
"react-native-reanimated": "^1.13.1",
1822
"react-native-safe-area-context": "^4.4.1",
1923
"react-native-screens": "^3.18.2",
20-
"uuid-random": "^1.3.2",
21-
"@react-navigation/native": "^5.8.0",
22-
"@react-navigation/stack": "^5.10.0"
24+
"uuid-random": "^1.3.2"
2325
},
2426
"devDependencies": {
2527
"@babel/core": "^7.12.10",
2628
"@babel/runtime": "^7.12.5",
2729
"babel-plugin-module-resolver": "^4.0.0",
2830
"metro-react-native-babel-preset": "^0.66.0"
2931
}
30-
}
32+
}

example/src/Home/index.tsx

+6-45
Original file line numberDiff line numberDiff line change
@@ -7,56 +7,17 @@ import { StyleSheet, View, Text, TouchableOpacity } from 'react-native';
77
import ramdomUuid from 'uuid-random';
88
import BackgroundTimer from 'react-native-background-timer';
99
import { useNavigation } from '@react-navigation/native';
10-
10+
import { CallKeepService } from '../services/CallKeepService';
11+
import RNCallKeep from 'react-native-callkeep';
12+
CallKeepService.instance().setupCallKeep()
1113
export default function Home() {
1214
const navigation = useNavigation();
13-
React.useEffect(() => {
14-
RNNotificationCall.addEventListener('answer', (data: answerPayload) => {
15-
const { callUUID, payload } = data;
16-
console.log('press answer', data);
17-
RNNotificationCall.backToApp();
18-
});
19-
RNNotificationCall.addEventListener('endCall', (data: declinePayload) => {
20-
const { callUUID, endAction, payload } = data;
21-
console.log('press endCall', data);
22-
});
23-
24-
return () => {
25-
RNNotificationCall.removeEventListener('answer');
26-
RNNotificationCall.removeEventListener('endCall');
27-
};
28-
}, []);
29-
const getCurrentCallId = () => {
30-
return ramdomUuid().toLowerCase();
31-
};
15+
CallKeepService.navigation = navigation
3216
const display = () => {
3317
// Start a timer that runs once after X milliseconds
34-
const timeoutId = BackgroundTimer.setTimeout(() => {
35-
// this will be executed once after 10 seconds
36-
// even when app is the the background
37-
const uid = getCurrentCallId();
38-
console.log('uid', uid);
39-
RNNotificationCall.displayNotification(uid, null, 30000, {
40-
channelId: 'com.abc.incomingcall',
41-
channelName: 'Incoming video call',
42-
notificationIcon: 'ic_launcher', //mipmap
43-
notificationTitle: 'Linh Vo',
44-
notificationBody: 'Incoming video call',
45-
answerText: 'Answer',
46-
declineText: 'Decline',
47-
notificationColor: 'colorAccent', //path color in android
48-
notificationSound: undefined, //raw
49-
mainComponent: 'MyReactNativeApp',
50-
payload: {
51-
kiokas: 'ádada',
52-
ssskis: 'awq',
53-
},
54-
});
55-
// Cancel the timeout if necessary
56-
BackgroundTimer.clearTimeout(timeoutId);
57-
}, 0);
58-
5918
//rest of code will be performing for iOS on background too
19+
const uuid = ramdomUuid()
20+
CallKeepService.instance().displayCall(uuid)
6021
};
6122
const onHide = () => {
6223
RNNotificationCall.hideNotification();
+166
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
1+
import { PermissionsAndroid, Platform } from 'react-native';
2+
import RNNotificationCall, {
3+
answerPayload,
4+
declinePayload,
5+
} from '../../../src/index';
6+
import RNCallKeep from 'react-native-callkeep';
7+
import { request, check, PERMISSIONS, RESULTS } from 'react-native-permissions';
8+
9+
const appName = 'Incoming-Test';
10+
const isAndroid = Platform.OS === 'android';
11+
const answerOption = {
12+
channelId: 'com.abc.incomingcall',
13+
channelName: 'Incoming video call',
14+
notificationIcon: 'ic_launcher', //mipmap
15+
notificationTitle: 'Linh Vo',
16+
answerText: 'Answer',
17+
declineText: 'Decline',
18+
notificationColor: 'colorAccent', //path color in android
19+
notificationSound: undefined, //raw
20+
};
21+
// this service only focus for Android
22+
23+
export class CallKeepService {
24+
private static _instance?: CallKeepService;
25+
static navigation: any;
26+
constructor() {
27+
//setup callkeep
28+
// this.setupCallKeep();
29+
}
30+
static instance(): CallKeepService {
31+
if (!CallKeepService._instance) {
32+
CallKeepService._instance = new CallKeepService();
33+
}
34+
return CallKeepService._instance;
35+
}
36+
37+
async setupCallKeep() {
38+
await new Promise((resolve) => {
39+
console.log('setup call keep done in promise');
40+
this.setupCallKeepFunc().then(resolve);
41+
});
42+
}
43+
44+
async setupCallKeepFunc() {
45+
const granted = await request(PERMISSIONS.ANDROID.READ_PHONE_NUMBERS);
46+
if (granted !== RESULTS.GRANTED) return;
47+
//only setup when granted permission
48+
await this.setup();
49+
//setup done
50+
if (isAndroid) {
51+
RNCallKeep.setAvailable(true);
52+
}
53+
this.registerEvent();
54+
}
55+
async setup() {
56+
try {
57+
await RNCallKeep.setup({
58+
ios: {
59+
appName: appName,
60+
maximumCallGroups: '1',
61+
maximumCallsPerCallGroup: '1',
62+
includesCallsInRecents: false,
63+
imageName: 'callkitIcon', //image name from ios
64+
},
65+
android: {
66+
alertTitle: 'Permissions required',
67+
alertDescription:
68+
'This application needs to access your phone accounts',
69+
cancelButton: 'Cancel',
70+
okButton: 'ok',
71+
selfManaged: true,
72+
additionalPermissions: [],
73+
foregroundService: {
74+
channelId: 'com.test.app.callkeep',
75+
channelName: 'Incoming Call',
76+
notificationTitle: 'Incoming Call',
77+
notificationIcon: 'ic_launcher_round',
78+
},
79+
},
80+
});
81+
return {
82+
result: 'setupDone',
83+
};
84+
} catch (error) {
85+
console.log('error setup callkeep', error);
86+
return error;
87+
}
88+
}
89+
registerEvent() {
90+
isAndroid &&
91+
RNCallKeep.addEventListener(
92+
'createIncomingConnectionFailed',
93+
this.onFailCallAction
94+
);
95+
RNCallKeep.addEventListener('answerCall', this.onCallKeepAnswerCallAction);
96+
RNCallKeep.addEventListener('endCall', this.onCallKeepEndCallAction);
97+
if (isAndroid) {
98+
//event only on android
99+
RNCallKeep.addEventListener(
100+
'showIncomingCallUi',
101+
({ handle, callUUID, name }) => {
102+
RNNotificationCall.displayNotification(callUUID, null, 30000, {
103+
...answerOption,
104+
channelId: 'com.abc.incomingcall',
105+
channelName: 'Incoming video call',
106+
notificationTitle: 'Linh Vo',
107+
notificationBody: 'Incoming video call',
108+
payload: {
109+
kiokas: 'ádada',
110+
ssskis: 'awq',
111+
},
112+
});
113+
}
114+
);
115+
// Listen to headless action events
116+
RNNotificationCall.addEventListener('endCall', (data: declinePayload) => {
117+
const { callUUID } = data;
118+
// End call action here
119+
console.log('endCall', callUUID);
120+
RNCallKeep.endCall(callUUID);
121+
});
122+
RNNotificationCall.addEventListener('answer', (data: answerPayload) => {
123+
const { callUUID } = data;
124+
//open app from quit state
125+
RNNotificationCall.backToApp();
126+
//call api answer
127+
console.log('answer', callUUID);
128+
RNCallKeep.answerIncomingCall(callUUID);
129+
});
130+
// You can listener firebase message event here
131+
}
132+
}
133+
onFailCallAction() {
134+
RNCallKeep.endAllCalls();
135+
}
136+
137+
//handle event
138+
onCallKeepAnswerCallAction(answerData: any) {
139+
const { callUUID } = answerData;
140+
// called when the user answer the incoming call
141+
//navigate to another screen
142+
//some project need to rehandle with redux state or other state manager refer https://github.com/linhvovan29546/react-native-full-screen-notification-incoming-call/issues/17#issuecomment-1318225574
143+
CallKeepService.navigation.navigate('Detail');
144+
}
145+
onCallKeepEndCallAction(answerData: any) {
146+
const { callUUID } = answerData;
147+
//end call action of callkit
148+
//action destroy screen
149+
//You need to call RNCallKeep.endCall(callUUID) to end call
150+
}
151+
152+
async displayCall(uuid: string) {
153+
const granted = await check(PERMISSIONS.ANDROID.READ_PHONE_NUMBERS);
154+
//only display call when permission granted
155+
if (granted !== RESULTS.GRANTED) return;
156+
console.log('display call', uuid);
157+
RNCallKeep.displayIncomingCall(
158+
uuid,
159+
'Linh Vo',
160+
'Linh Vo',
161+
'number',
162+
true,
163+
undefined
164+
);
165+
}
166+
}

package.json

+3-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "react-native-full-screen-notification-incoming-call",
3-
"version": "0.1.10",
3+
"version": "0.1.11",
44
"description": "Android full screen notification incoming call for React Native",
55
"main": "lib/commonjs/index",
66
"module": "lib/module/index",
@@ -143,5 +143,6 @@
143143
}
144144
]
145145
]
146-
}
146+
},
147+
"dependencies": {}
147148
}

0 commit comments

Comments
 (0)