Skip to content

Commit 0ee9a37

Browse files
committed
Call invite
1 parent 403c6f1 commit 0ee9a37

File tree

24 files changed

+1032
-77
lines changed

24 files changed

+1032
-77
lines changed

App.js

Lines changed: 352 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,360 @@
11
import React, { Component } from 'react';
22
import Routes from './pages/Routes.js';
3+
import messaging from '@react-native-firebase/messaging';
4+
import { Alert} from 'react-native'
5+
import notifee, { AuthorizationStatus, EventType, AndroidImportance, AndroidVisibility } from '@notifee/react-native';
6+
7+
var killedIncomingCallRoomId = '';
8+
9+
// For android
10+
notifee.createChannel({
11+
id: 'callinvite',
12+
name: 'Call Invite',
13+
badge: false,
14+
vibration: true,
15+
vibrationPattern: [300, 500],
16+
importance: AndroidImportance.HIGH,
17+
visibility: AndroidVisibility.PUBLIC,
18+
// https://clideo.com/merge-audio
19+
// https://www.zedge.net/find/ringtones/sound-effects
20+
sound: 'call_invite',
21+
});
22+
23+
async function onBackgroundMessageReceived(message) {
24+
// invokeApp();
25+
console.log(">>>>>>>>>>Message: ", message, message.data.callerUserName);
26+
killedIncomingCallRoomId = message.data.roomID;
27+
notifee.displayNotification({
28+
title: '<p style="color: #4caf50;"><b>' + '📞 ' + message.data.callerUserName + ' incoming call..' + '</span></p></b></p>',
29+
body: 'Tap to view contact.',
30+
data: { "roomID": message.data.roomID, "callType": message.data.callType },
31+
android: {
32+
channelId: 'callinvite',
33+
largeIcon: message.data.callerIconUrl,
34+
// Launch the app on lock screen
35+
fullScreenAction: {
36+
// For Android Activity other than the default:
37+
id: 'full_screen_body_press',
38+
launchActivity: 'default',
39+
},
40+
pressAction: {
41+
id: 'body_press',
42+
launchActivity: 'default',
43+
},
44+
actions: [
45+
{
46+
title: 'Denied',
47+
// icon: 'https://my-cdn.com/icons/snooze.png',
48+
pressAction: {
49+
id: 'denied',
50+
},
51+
},
52+
{
53+
title: 'Accept',
54+
// icon: 'https://my-cdn.com/icons/snooze.png',
55+
pressAction: {
56+
id: 'accept',
57+
launchActivity: 'default',
58+
},
59+
},
60+
],
61+
},
62+
});
63+
console.log('Show completed.')
64+
}
65+
messaging().setBackgroundMessageHandler(onBackgroundMessageReceived);
66+
67+
const config = {
68+
// Get your AppID from ZEGOCLOUD Console [My Projects] : https://console.zegocloud.com/project
69+
appID: ,
70+
// Heroku server url for example
71+
// Get the server from: https://github.com/ZEGOCLOUD/dynamic_token_server_nodejs
72+
serverUrl: ''
73+
}
374

475
class App extends Component {
76+
routesInstance;
77+
messageListener;
78+
backgroundMessageListener;
79+
80+
state = {
81+
userID: '',
82+
zegoToken: '',
83+
fcmToken: '',
84+
}
85+
86+
componentDidMount() {
87+
this.onAppBootstrap();
88+
}
89+
90+
componentWillUnmount() {
91+
this.messageListener;
92+
// this.backgroundMessageListener;
93+
}
94+
95+
async onAppBootstrap() {
96+
await this.checkPermission();
97+
98+
await this.prepareBasicData();
99+
100+
await this.setupNotification();
101+
102+
if(killedIncomingCallRoomId != '') {
103+
this.routesInstance.handleIncomingCall(killedIncomingCallRoomId);
104+
}
105+
}
106+
107+
//////////////////////////// notification /////////////////////////
108+
async setupNotification() {
109+
notifee.onForegroundEvent(async ({ type, detail }) => {
110+
if (type === EventType.PRESS) {
111+
console.log('User press on froeground event: ', detail)
112+
await notifee.cancelAllNotifications();
113+
} else if (type == EventType.ACTION_PRESS && detail.pressAction.id) {
114+
if (detail.pressAction.id == 'accept') {
115+
console.log('Accept the call...', detail.notification.data.roomID)
116+
if (this.routesInstance != undefined) {
117+
this.routesInstance.handleIncomingCall(detail.notification.data.roomID);
118+
}
119+
}
120+
await notifee.cancelAllNotifications();
121+
}
122+
});
123+
notifee.onBackgroundEvent(async ({ type, detail }) => {
124+
if (type === EventType.PRESS) {
125+
console.log('User press on background event: ', detail)
126+
// await notifee.cancelNotification(detail.notification.id);
127+
await notifee.cancelAllNotifications();
128+
} else if (type == EventType.ACTION_PRESS && detail.pressAction.id) {
129+
if (detail.pressAction.id == 'accept') {
130+
console.log('Accept the call...', detail.notification.data.roomID)
131+
if (this.routesInstance != undefined) {
132+
this.routesInstance.handleIncomingCall(detail.notification.data.roomID);
133+
}
134+
}
135+
await notifee.cancelAllNotifications();
136+
}
137+
});
138+
139+
this.messageListener = messaging().onMessage(this.onMessageReceived);
140+
}
141+
async onMessageReceived(message) {
142+
// invokeApp();
143+
console.log(">>>>>>>>>>Foreground Message: ", message, message.data.callerUserName);
144+
notifee.displayNotification({
145+
title: '<p style="color: #4caf50;"><b>' + '📞 ' + message.data.callerUserName + ' incoming call..' + '</span></p></b></p>',
146+
body: 'Tap to view contact.',
147+
data: { "roomID": message.data.roomID, "callType": message.data.callType },
148+
android: {
149+
channelId: 'callinvite',
150+
largeIcon: message.data.callerIconUrl,
151+
// Launch the app on lock screen
152+
fullScreenAction: {
153+
// For Android Activity other than the default:
154+
id: 'full_screen_body_press',
155+
launchActivity: 'default',
156+
},
157+
pressAction: {
158+
id: 'body_press',
159+
launchActivity: 'default',
160+
},
161+
actions: [
162+
{
163+
title: 'Denied',
164+
// icon: 'https://my-cdn.com/icons/snooze.png',
165+
pressAction: {
166+
id: 'denied',
167+
launchActivity: 'default',
168+
},
169+
},
170+
{
171+
title: 'Accept',
172+
// icon: 'https://my-cdn.com/icons/snooze.png',
173+
pressAction: {
174+
id: 'accept',
175+
launchActivity: 'default',
176+
},
177+
},
178+
],
179+
},
180+
});
181+
console.log('Show completed.')
182+
}
183+
//////////////////////////// notification /////////////////////////
184+
185+
//////////////////////////// basic data /////////////////////////
186+
async prepareBasicData() {
187+
// Get fcm token
188+
await this.updateFcmToken();
189+
// Generate user id
190+
this.setState({
191+
userID: Math.floor(Math.random() * 1000000).toString()
192+
})
193+
// Save the fcm token with user id to server
194+
const requestOptions = {
195+
method: 'POST',
196+
headers: { 'Content-Type': 'application/json' },
197+
body: JSON.stringify({ userID: this.state.userID, token: this.state.fcmToken })
198+
};
199+
const reps = await fetch(`${config.serverUrl}/store_fcm_token`, requestOptions);
200+
console.log('Store fcm token reps: ', reps);
201+
await this.updateZegoToken();
202+
}
203+
async updateZegoToken() {
204+
// Obtain the token interface provided by the App Server
205+
const reps = await fetch(
206+
`${config.serverUrl}/access_token?uid=${this.state.userID}`,
207+
{
208+
method: 'GET',
209+
headers: {
210+
'Content-Type': 'application/json; charset=utf-8',
211+
},
212+
},
213+
);
214+
if (reps.ok) {
215+
const tokenObj = await reps.json();
216+
console.log('Get zego token succeed');
217+
this.setState({
218+
zegoToken: tokenObj['token']
219+
});
220+
} else {
221+
console.warn('Get zego token error: ', reps.text);
222+
}
223+
};
224+
async updateFcmToken() {
225+
// Register the device with FCM
226+
await messaging().registerDeviceForRemoteMessages();
227+
228+
// Get the token
229+
const token = await messaging().getToken();
230+
console.log('Fcm token: ', token);
231+
this.setState({
232+
fcmToken: token
233+
});
234+
235+
}
236+
//////////////////////////// basic data /////////////////////////
237+
238+
//////////////////////////// permission stuffs /////////////////////////
239+
async checkPermission() {
240+
// For ios
241+
await this.requestiOSUserPermission();
242+
// For android
243+
await this.checkAndroidNotificationPermission();
244+
await this.checkAndroidChannelPermission('callinvite');
245+
await this.checkBatteryOptimization();
246+
await this.checkPowerManagerRestrictions();
247+
}
248+
async checkAndroidNotificationPermission() {
249+
const settings = await notifee.getNotificationSettings();
250+
251+
if (settings.authorizationStatus == AuthorizationStatus.AUTHORIZED) {
252+
console.log('Notification permissions has been authorized');
253+
} else if (settings.authorizationStatus == AuthorizationStatus.DENIED) {
254+
console.log('Notification permissions has been denied');
255+
}
256+
}
257+
async checkAndroidChannelPermission(channelId) {
258+
const channel = await notifee.getChannel(channelId);
259+
260+
if (channel.blocked) {
261+
console.log('Channel is disabled');
262+
Alert.alert(
263+
'Restrictions Detected',
264+
'To ensure notifications are delivered, please enable notification for the app.',
265+
[
266+
// 3. launch intent to navigate the user to the appropriate screen
267+
{
268+
text: 'OK, open settings',
269+
onPress: async () => await notifee.openNotificationSettings(),
270+
},
271+
{
272+
text: "Cancel",
273+
onPress: () => console.log("Cancel Pressed"),
274+
style: "cancel"
275+
},
276+
],
277+
{ cancelable: false }
278+
);
279+
} else {
280+
console.log('Channel is enabled');
281+
}
282+
}
283+
// Need for background message
284+
async requestiOSUserPermission() {
285+
const authStatus = await messaging().requestPermission();
286+
const enabled =
287+
authStatus === messaging.AuthorizationStatus.AUTHORIZED ||
288+
authStatus === messaging.AuthorizationStatus.PROVISIONAL;
289+
290+
if (enabled) {
291+
console.log('Authorization status:', authStatus);
292+
}
293+
}
294+
async checkBatteryOptimization() {
295+
const batteryOptimizationEnabled = await notifee.isBatteryOptimizationEnabled();
296+
console.log("batteryOptimizationEnabled", batteryOptimizationEnabled)
297+
if (batteryOptimizationEnabled) {
298+
// 2. ask your users to disable the feature
299+
Alert.alert(
300+
'Restrictions Detected',
301+
'To ensure notifications are delivered, please disable battery optimization for the app.',
302+
[
303+
// 3. launch intent to navigate the user to the appropriate screen
304+
{
305+
text: 'OK, open settings',
306+
onPress: async () => await notifee.openBatteryOptimizationSettings(),
307+
},
308+
{
309+
text: "Cancel",
310+
onPress: () => console.log("Cancel Pressed"),
311+
style: "cancel"
312+
},
313+
],
314+
{ cancelable: false }
315+
);
316+
};
317+
}
318+
async checkPowerManagerRestrictions() {
319+
const powerManagerInfo = await notifee.getPowerManagerInfo();
320+
console.log("powerManagerInfo", powerManagerInfo.activity)
321+
if (powerManagerInfo.activity) {
322+
// 2. ask your users to adjust their settings
323+
Alert.alert(
324+
'Restrictions Detected',
325+
'To ensure notifications are delivered, please adjust your settings to prevent the app from being killed',
326+
[
327+
// 3. launch intent to navigate the user to the appropriate screen
328+
{
329+
text: 'OK, open settings',
330+
onPress: async () => await notifee.openPowerManagerSettings(),
331+
},
332+
{
333+
text: "Cancel",
334+
onPress: () => console.log("Cancel Pressed"),
335+
style: "cancel"
336+
},
337+
],
338+
{ cancelable: false }
339+
);
340+
};
341+
}
342+
//////////////////////////// permission stuffs /////////////////////////
343+
5344
render() {
6-
return (
7-
<Routes/>
8-
)
345+
if (this.state.userID != '' && this.state.zegoToken != '' && this.state.fcmToken != '') {
346+
var appData = {
347+
appID: config.appID,
348+
serverUrl: config.serverUrl,
349+
userID: this.state.userID,
350+
zegoToken: this.state.zegoToken,
351+
}
352+
return (
353+
<Routes appData={appData} ref={instance => { this.routesInstance = instance; }} />
354+
)
355+
} else {
356+
return (null)
357+
}
9358
}
10359
}
11360
export default App

android/app/_BUCK

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,12 +35,12 @@ android_library(
3535

3636
android_build_config(
3737
name = "build_config",
38-
package = "com.zegoeasyexample",
38+
package = "im.zego.easyexample.rn.android",
3939
)
4040

4141
android_resource(
4242
name = "res",
43-
package = "com.zegoeasyexample",
43+
package = "im.zego.easyexample.rn.android",
4444
res = "src/main/res",
4545
)
4646

android/app/build.gradle

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
apply plugin: "com.android.application"
2+
apply plugin: 'com.google.gms.google-services'
23

34
import com.android.build.OutputFile
45
import org.apache.tools.ant.taskdefs.condition.Os
@@ -135,7 +136,7 @@ android {
135136
compileSdkVersion rootProject.ext.compileSdkVersion
136137

137138
defaultConfig {
138-
applicationId "com.zegoeasyexample"
139+
applicationId "im.zego.easyexample.rn.android"
139140
minSdkVersion rootProject.ext.minSdkVersion
140141
targetSdkVersion rootProject.ext.targetSdkVersion
141142
versionCode 1

0 commit comments

Comments
 (0)