Commit 0ee17cc7 authored by Yuri Bondarenko's avatar Yuri Bondarenko

up

parent 5fd20849
import React, {useState, useEffect} from 'react';
import {
Platform,
StyleSheet,
Text,
View,
TouchableOpacity,
ScrollView,
} from 'react-native';
import {v4 as uuidv4} from 'uuid';
import RNCallKeep from 'react-native-callkeep';
import BackgroundTimer from 'react-native-background-timer';
import DeviceInfo from 'react-native-device-info';
BackgroundTimer.start();
const hitSlop = {top: 10, left: 10, right: 10, bottom: 10};
const styles = StyleSheet.create({
container: {
flex: 1,
marginTop: 20,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
},
button: {
marginTop: 20,
marginBottom: 20,
},
callButtons: {
flexDirection: 'row',
justifyContent: 'space-between',
paddingHorizontal: 30,
width: '100%',
},
logContainer: {
flex: 3,
width: '100%',
backgroundColor: '#D9D9D9',
},
log: {
fontSize: 10,
},
});
RNCallKeep.setup({
ios: {
appName: 'CallKeepDemo',
},
android: {
alertTitle: 'Permissions required',
alertDescription: 'This application needs to access your phone accounts',
cancelButton: 'Cancel',
okButton: 'ok',
},
});
const getNewUuid = () => uuidv4().toLowerCase();
const format = uuid => uuid.split('-')[0];
const getRandomNumber = () => String(Math.floor(Math.random() * 100000));
const isIOS = Platform.OS === 'ios';
export default function App() {
const [logText, setLog] = useState('');
const [heldCalls, setHeldCalls] = useState({}); // callKeep uuid: held
const [mutedCalls, setMutedCalls] = useState({}); // callKeep uuid: muted
const [calls, setCalls] = useState({}); // callKeep uuid: number
const log = text => {
console.info(text);
setLog(logText + '\n' + text);
};
const addCall = (callUUID, number) => {
setHeldCalls({...heldCalls, [callUUID]: false});
setCalls({...calls, [callUUID]: number});
};
const removeCall = callUUID => {
const {[callUUID]: _, ...updated} = calls;
const {[callUUID]: __, ...updatedHeldCalls} = heldCalls;
setCalls(updated);
setCalls(updatedHeldCalls);
};
const setCallHeld = (callUUID, held) => {
setHeldCalls({...heldCalls, [callUUID]: held});
};
const setCallMuted = (callUUID, muted) => {
setMutedCalls({...mutedCalls, [callUUID]: muted});
};
const displayIncomingCall = number => {
const callUUID = getNewUuid();
addCall(callUUID, number);
log(`[displayIncomingCall] ${format(callUUID)}, number: ${number}`);
RNCallKeep.displayIncomingCall(callUUID, number, number, 'number', false);
};
const displayIncomingCallNow = () => {
displayIncomingCall(getRandomNumber());
};
const displayIncomingCallDelayed = () => {
BackgroundTimer.setTimeout(() => {
displayIncomingCall(getRandomNumber());
}, 3000);
};
const answerCall = ({callUUID}) => {
const number = calls[callUUID];
log(`[answerCall] ${format(callUUID)}, number: ${number}`);
RNCallKeep.startCall(callUUID, number, number);
BackgroundTimer.setTimeout(() => {
log(`[setCurrentCallActive] ${format(callUUID)}, number: ${number}`);
RNCallKeep.setCurrentCallActive(callUUID);
}, 1000);
};
const didPerformDTMFAction = ({callUUID, digits}) => {
const number = calls[callUUID];
log(
`[didPerformDTMFAction] ${format(
callUUID,
)}, number: ${number} (${digits})`,
);
};
const didReceiveStartCallAction = ({handle}) => {
if (!handle) {
// @TODO: sometime we receive `didReceiveStartCallAction` with handle` undefined`
return;
}
const callUUID = getNewUuid();
addCall(callUUID, handle);
log(`[didReceiveStartCallAction] ${callUUID}, number: ${handle}`);
RNCallKeep.startCall(callUUID, handle, handle);
BackgroundTimer.setTimeout(() => {
log(`[setCurrentCallActive] ${format(callUUID)}, number: ${handle}`);
RNCallKeep.setCurrentCallActive(callUUID);
}, 1000);
};
const didPerformSetMutedCallAction = ({muted, callUUID}) => {
const number = calls[callUUID];
log(
`[didPerformSetMutedCallAction] ${format(
callUUID,
)}, number: ${number} (${muted})`,
);
setCallMuted(callUUID, muted);
};
const didToggleHoldCallAction = ({hold, callUUID}) => {
const number = calls[callUUID];
log(
`[didToggleHoldCallAction] ${format(
callUUID,
)}, number: ${number} (${hold})`,
);
setCallHeld(callUUID, hold);
};
const endCall = ({callUUID}) => {
const handle = calls[callUUID];
log(`[endCall] ${format(callUUID)}, number: ${handle}`);
removeCall(callUUID);
};
const hangup = callUUID => {
RNCallKeep.endCall(callUUID);
removeCall(callUUID);
};
const setOnHold = (callUUID, held) => {
const handle = calls[callUUID];
RNCallKeep.setOnHold(callUUID, held);
log(`[setOnHold: ${held}] ${format(callUUID)}, number: ${handle}`);
setCallHeld(callUUID, held);
};
const setOnMute = (callUUID, muted) => {
const handle = calls[callUUID];
RNCallKeep.setMutedCall(callUUID, muted);
log(`[setMutedCall: ${muted}] ${format(callUUID)}, number: ${handle}`);
setCallMuted(callUUID, muted);
};
const updateDisplay = callUUID => {
const number = calls[callUUID];
// Workaround because Android doesn't display well displayName, se we have to switch ...
if (isIOS) {
RNCallKeep.updateDisplay(callUUID, 'New Name', number);
} else {
RNCallKeep.updateDisplay(callUUID, number, 'New Name');
}
log(`[updateDisplay: ${number}] ${format(callUUID)}`);
};
useEffect(() => {
RNCallKeep.addEventListener('answerCall', answerCall);
RNCallKeep.addEventListener('didPerformDTMFAction', didPerformDTMFAction);
RNCallKeep.addEventListener(
'didReceiveStartCallAction',
didReceiveStartCallAction,
);
RNCallKeep.addEventListener(
'didPerformSetMutedCallAction',
didPerformSetMutedCallAction,
);
RNCallKeep.addEventListener(
'didToggleHoldCallAction',
didToggleHoldCallAction,
);
RNCallKeep.addEventListener('endCall', endCall);
return () => {
RNCallKeep.removeEventListener('answerCall', answerCall);
RNCallKeep.removeEventListener(
'didPerformDTMFAction',
didPerformDTMFAction,
);
RNCallKeep.removeEventListener(
'didReceiveStartCallAction',
didReceiveStartCallAction,
);
RNCallKeep.removeEventListener(
'didPerformSetMutedCallAction',
didPerformSetMutedCallAction,
);
RNCallKeep.removeEventListener(
'didToggleHoldCallAction',
didToggleHoldCallAction,
);
RNCallKeep.removeEventListener('endCall', endCall);
};
}, []);
if (isIOS && DeviceInfo.isEmulator()) {
return (
<Text style={styles.container}>
CallKeep doesn't work on iOS emulator
</Text>
);
}
return (
<View style={styles.container}>
<TouchableOpacity
onPress={displayIncomingCallNow}
style={styles.button}
hitSlop={hitSlop}>
<Text>Display incoming call now</Text>
</TouchableOpacity>
<TouchableOpacity
onPress={displayIncomingCallDelayed}
style={styles.button}
hitSlop={hitSlop}>
<Text>Display incoming call now in 3s</Text>
</TouchableOpacity>
{Object.keys(calls).map(callUUID => (
<View key={callUUID} style={styles.callButtons}>
<TouchableOpacity
onPress={() => setOnHold(callUUID, !heldCalls[callUUID])}
style={styles.button}
hitSlop={hitSlop}>
<Text>
{heldCalls[callUUID] ? 'Unhold' : 'Hold'} {calls[callUUID]}
</Text>
</TouchableOpacity>
<TouchableOpacity
onPress={() => updateDisplay(callUUID)}
style={styles.button}
hitSlop={hitSlop}>
<Text>Update display</Text>
</TouchableOpacity>
<TouchableOpacity
onPress={() => setOnMute(callUUID, !mutedCalls[callUUID])}
style={styles.button}
hitSlop={hitSlop}>
<Text>
{mutedCalls[callUUID] ? 'Unmute' : 'Mute'} {calls[callUUID]}
</Text>
</TouchableOpacity>
<TouchableOpacity
onPress={() => hangup(callUUID)}
style={styles.button}
hitSlop={hitSlop}>
<Text>Hangup {calls[callUUID]}</Text>
</TouchableOpacity>
</View>
))}
<ScrollView style={styles.logContainer}>
<Text style={styles.log}>{logText}</Text>
</ScrollView>
</View>
);
}
import React, {useState, useEffect, useRef} from 'react';
import {
Platform,
StyleSheet,
Text,
View,
TouchableOpacity,
ScrollView,
} from 'react-native';
import RNCallKeep, {AnswerCallPayload, Events} from 'react-native-callkeep';
const EVENTS: Events[] = [
'didReceiveStartCallAction',
'answerCall',
'endCall',
'didActivateAudioSession',
'didDeactivateAudioSession',
'didDisplayIncomingCall',
'didToggleHoldCallAction',
'didPerformDTMFAction',
'didResetProvider',
'checkReachability',
'didPerformSetMutedCallAction',
'didLoadWithEvents',
'showIncomingCallUi',
'silenceIncomingCall',
];
type DidDisplayIncomingCallArgs = {
error: string;
callUUID: string;
handle: string;
localizedCallerName: string;
hasVideo: string;
fromPushKit: string;
payload: string;
};
export default function App() {
const [readyCK, setReadyCK] = useState(false);
const [supportCS, setSupportCS] = useState(false);
const [hasPhoneAccount, setHasPhoneAccount] = useState(false);
const [callId, setCallId] = useState('');
const [callerName, setCallerName] = useState('');
const [callerId, setCallerId] = useState('');
const isRinging = useRef(false);
const removeEventListeners = () => {
RNCallKeep.removeEventListener('endCall');
RNCallKeep.removeEventListener('didDisplayIncomingCall');
};
const endCall = () => {
RNCallKeep.endCall(callId);
removeEventListeners();
};
const displayCallAndroid = () => {
isRinging.current = true;
RNCallKeep.displayIncomingCall(callId, callerName, callerName, 'generic');
setTimeout(() => {
if (isRinging.current) {
isRinging.current = false;
// 6 = MissedCall
// https://github.com/react-native-webrtc/react-native-callkeep#constants
RNCallKeep.reportEndCallWithUUID(callId, 6);
}
}, 15000);
};
const answerCall = ({callUUID}: AnswerCallPayload) => {
isRinging.current = false;
console.log('somewhere'); // navigated to call screen in our app
};
const didDisplayIncomingCall = (args: DidDisplayIncomingCallArgs) => {
if (args.error) {
console.log(`Callkeep didDisplayIncomingCall error: ${args.error}`);
}
isRinging.current = true;
RNCallKeep.updateDisplay(callId, `${callerName}`, callerId);
setTimeout(() => {
if (isRinging.current) {
isRinging.current = false;
// 6 = MissedCall
// https://github.com/react-native-webrtc/react-native-callkeep#constants
RNCallKeep.reportEndCallWithUUID(callId, 6);
}
}, 15000);
};
const setupEventListeners = () => {
RNCallKeep.addEventListener('endCall', endCall);
RNCallKeep.addEventListener(
'didDisplayIncomingCall',
didDisplayIncomingCall,
);
};
const initCK = async () => {
try {
RNCallKeep.setup({
ios: {
appName: 'WazoReactNativeDemo',
},
android: {
alertTitle: 'Permissions required',
alertDescription:
'This application needs to access your phone accounts',
cancelButton: 'Cancel',
okButton: 'ok',
selfManaged: true,
additionalPermissions: [],
foregroundService: {
channelId: 'com.company.my',
channelName: 'Foreground service for my app',
notificationTitle: 'My app is running on background',
notificationIcon: 'Path to the resource icon of the notification',
},
},
});
RNCallKeep.setAvailable(true);
setupEventListeners();
setSupportCS(RNCallKeep.supportConnectionService());
const hasPA = await RNCallKeep.hasPhoneAccount();
setHasPhoneAccount(hasPA);
setReadyCK(true);
} catch (err) {
console.error('initializeCallKeep error:', (err as Error).message);
}
};
useEffect(() => {
initCK();
return () => {
removeEventListeners();
};
}, []);
return (
<View>
<Text>CallKeep Demo: {readyCK ? 'Ready' : 'Waiting...'}</Text>
<Text>Support Connection Service: {supportCS ? 'Yes' : 'No'}</Text>
<Text>Has Phone account: {hasPhoneAccount ? 'Yes' : 'No'}</Text>
</View>
);
}
# CallKeep
- [CallKeep React Native](https://github.com/react-native-webrtc/react-native-callkeep)
- [Статья с нативным модулем](https://blog.theodo.com/2021/03/react-native-incoming-call-ui-callkeep/)
- [Дока от Гугла про selfManaged](https://developer.android.com/guide/topics/connectivity/telecom/selfManaged)
......@@ -8,6 +8,8 @@
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.CALL_PHONE" />
<uses-permission android:name="android.permission.MANAGE_OWN_CALLS"/>
<uses-permission android:name="android.permission.READ_CALL_LOG" />
<!-- OPTIONAL PERMISSIONS, REMOVE WHATEVER YOU DO NOT NEED -->
<uses-permission android:name="android.permission.MANAGE_DOCUMENTS" />
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment