Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: VoIP PoC #6051

Draft
wants to merge 30 commits into
base: develop
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
23579f4
chore: type definitions
aleksandernsilva Dec 11, 2024
b804695
chore: voip endpoints
aleksandernsilva Dec 11, 2024
0315fb6
feat: voip utils
aleksandernsilva Dec 11, 2024
24b089c
feat: voip hooks
aleksandernsilva Dec 11, 2024
ee991df
chore: voip settings
aleksandernsilva Dec 11, 2024
2e9841a
feat: voip client and its components
aleksandernsilva Dec 11, 2024
1305340
feat: voip actions
aleksandernsilva Dec 11, 2024
3735a9f
feat: voip reducer
aleksandernsilva Dec 11, 2024
b36e007
feat: voip sagas
aleksandernsilva Dec 11, 2024
e55bc63
feat: init voip
aleksandernsilva Dec 11, 2024
b917bb7
chore: dependencies
aleksandernsilva Dec 11, 2024
143e477
feat: added callkeep
aleksandernsilva Dec 12, 2024
162b992
chore: permissions and network config
aleksandernsilva Dec 30, 2024
eddba6c
chore: added voip state type
aleksandernsilva Dec 30, 2024
8190693
feat: voip client and callkeep event listening
aleksandernsilva Dec 30, 2024
e496819
feat: enable/disable voice call button
aleksandernsilva Dec 30, 2024
ab49854
feat: start call action
aleksandernsilva Dec 30, 2024
9669042
feat: stream changes
aleksandernsilva Dec 30, 2024
7835baa
feat: improved session id
aleksandernsilva Dec 30, 2024
17c851a
feat: defined uuid's for calls
aleksandernsilva Jan 3, 2025
22aab82
refactor: media stream improvements
aleksandernsilva Jan 3, 2025
68ee54e
refactor: sagas improvements
aleksandernsilva Jan 3, 2025
4eede9c
chore: removed unused actions and types
aleksandernsilva Jan 6, 2025
0e69882
refactor: removed unused provider and context
aleksandernsilva Jan 6, 2025
95c1063
refactor: adjusted StartCallActionSheet style
aleksandernsilva Jan 8, 2025
e5c3a09
refactor: removed headless task
aleksandernsilva Jan 9, 2025
5f1f035
refactor: removed unused variable
aleksandernsilva Jan 9, 2025
c80ed86
test: temporarily removed test
aleksandernsilva Jan 9, 2025
801ab09
chore: removed trailing comma
aleksandernsilva Jan 10, 2025
866b8cf
chore: removed unused type
aleksandernsilva Jan 10, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion android/app/src/debug/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,6 @@
android:usesCleartextTraffic="true"
tools:targetApi="28"
tools:ignore="GoogleAppIndexingWarning"/>
</manifest>

</manifest>

4 changes: 4 additions & 0 deletions android/app/src/debug/res/xml/network_security_config.xml
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,8 @@
tools:ignore="AcceptsUserCertificates" />
</trust-anchors>
</base-config>

<domain-config cleartextTrafficPermitted="false">
<domain includeSubdomains="true">voip-cert.dev.rocket.chat</domain>
</domain-config>
</network-security-config>
25 changes: 25 additions & 0 deletions android/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,18 @@
<uses-permission android:name="android.permission.READ_MEDIA_VIDEO" />
<uses-permission android:name="android.permission.READ_MEDIA_AUDIO" />

<!-- permissions related to voip -->
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MICROPHONE" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.BIND_TELECOM_CONNECTION_SERVICE" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
<!-- <uses-permission android:name="android.permission.MANAGE_OWN_CALLS" /> -->
<!-- <uses-permission android:name="android.permission.CALL_PHONE" /> -->
<uses-feature android:name="android.hardware.audio.output" />
<uses-feature android:name="android.hardware.microphone" />

<application
android:name="chat.rocket.reactnative.MainApplication"
android:allowBackup="false"
Expand Down Expand Up @@ -77,6 +89,19 @@
<data android:mimeType="*/*" />
</intent-filter>
</activity>

<service android:name="io.wazo.callkeep.VoiceConnectionService"
android:label="Wazo"
android:permission="android.permission.BIND_TELECOM_CONNECTION_SERVICE"
android:exported="true"
android:foregroundServiceType="microphone"
>
<intent-filter>
<action android:name="android.telecom.ConnectionService" />
</intent-filter>
</service>

<service android:name="io.wazo.callkeep.RNCallKeepBackgroundMessagingService" />
</application>

<queries>
Expand Down
12 changes: 12 additions & 0 deletions app/actions/actionsTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -104,3 +104,15 @@ export const VIDEO_CONF = createRequestTypes('VIDEO_CONF', [
export const TROUBLESHOOTING_NOTIFICATION = createRequestTypes('TROUBLESHOOTING_NOTIFICATION', ['INIT', 'SET']);
export const SUPPORTED_VERSIONS = createRequestTypes('SUPPORTED_VERSIONS', ['SET']);
export const IN_APP_FEEDBACK = createRequestTypes('IN_APP_FEEDBACK', ['SET', 'REMOVE', 'CLEAR']);

// TODO: import createRequestType return type
export const VOIP = {
INIT: 'VOIP_INIT',
CLIENT_ERROR: 'VOIP_CLIENT_ERROR',
START_CALL: 'VOIP_START_CALL',
UPDATE_SESSION: 'VOIP_UPDATE_SESSION',
UPDATE_STATE: 'VOIP_UPDATE_STATE',
REGISTER: 'VOIP_REGISTER',
UNREGISTER: 'VOIP_UNREGISTER',
UPDATE_REGISTER_STATUS: 'VOIP_UPDATE_REGISTER_STATUS'
} as const;
58 changes: 58 additions & 0 deletions app/actions/voip.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import { Action } from 'redux';

import { VOIP } from './actionsTypes';
import { VoipSession, VoipState } from '../lib/voip/definitions';

type ActionWithPayload<T = string, P = {}> = Action<T> & { payload: P };

export type TUpdateSessionAction = ActionWithPayload<'VOIP_UPDATE_SESSION', VoipSession | null>;

export type TUpdateStateAction = ActionWithPayload<'VOIP_UPDATE_STATE', VoipState | null>;
export type TStartCallAction = ActionWithPayload<'VOIP_START_CALL', string>;
export type TClientErrorAction = ActionWithPayload<'VOIP_CLIENT_ERROR', string>;
export type TRegisterAction = Action<'VOIP_REGISTER'>;
export type TUnregisterAction = Action<'VOIP_UNREGISTER'>;
export type TUpdateRegisterStatusAction = ActionWithPayload<
'VOIP_UPDATE_REGISTER_STATUS',
'REGISTERED' | 'UNREGISTERED' | 'REGISTERING' | 'UNREGISTERING'
>;

export type TActionVoip =
| TUpdateSessionAction
| TStartCallAction
| TClientErrorAction
| TUpdateStateAction
| TRegisterAction
| TUpdateRegisterStatusAction;

export function initVoip(): Action {
return { type: VOIP.INIT };
}

export function clientError(payload: string): TClientErrorAction {
return { type: VOIP.CLIENT_ERROR, payload };
}

export function register(): Action {
return { type: VOIP.REGISTER };
}

export function unregister(): Action {
return { type: VOIP.UNREGISTER };
}

export function startCall(payload: TStartCallAction['payload']): TStartCallAction {
return { type: VOIP.START_CALL, payload };
}

export function updateSession(payload: TUpdateSessionAction['payload']): TUpdateSessionAction {
return { type: VOIP.UPDATE_SESSION, payload };
}

export function updateState(payload: TUpdateStateAction['payload']): TUpdateStateAction {
return { type: VOIP.UPDATE_STATE, payload };
}

export function updateRegisterStatus(payload: TUpdateRegisterStatusAction['payload']): TUpdateRegisterStatusAction {
return { type: VOIP.UPDATE_REGISTER_STATUS, payload };
}
1 change: 1 addition & 0 deletions app/definitions/IUser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,7 @@ export interface IUser extends IRocketChatRecord, ILoggedUser {
defaultRoom?: string;
ldap?: boolean;
muted?: boolean;
freeSwitchExtension?: string;
}

export interface IRegisterUser extends IUser {
Expand Down
4 changes: 4 additions & 0 deletions app/definitions/redux/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ import { ITroubleshootingNotification } from '../../reducers/troubleshootingNoti
import { TActionTroubleshootingNotification } from '../../actions/troubleshootingNotification';
import { ISupportedVersionsState } from '../../reducers/supportedVersions';
import { IInAppFeedbackState } from '../../reducers/inAppFeedback';
import { IVoip } from '../../reducers/voip';
import { TActionVoip } from '../../actions/voip';

export interface IApplicationState {
settings: TSettingsState;
Expand All @@ -68,6 +70,7 @@ export interface IApplicationState {
permissions: IPermissionsState;
roles: IRoles;
videoConf: IVideoConf;
voip: IVoip;
usersRoles: TUsersRoles;
troubleshootingNotification: ITroubleshootingNotification;
supportedVersions: ISupportedVersionsState;
Expand All @@ -92,6 +95,7 @@ export type TApplicationActions = TActionActiveUsers &
TActionPermissions &
TActionEnterpriseModules &
TActionVideoConf &
TActionVoip &
TActionUsersRoles &
TActionTroubleshootingNotification &
TActionSupportedVersions &
Expand Down
4 changes: 3 additions & 1 deletion app/definitions/rest/v1/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import { PushEndpoints } from './push';
import { DirectoryEndpoint } from './directory';
import { AutoTranslateEndpoints } from './autotranslate';
import { ModerationEndpoints } from './moderation';
import { VoipFreeSwitchEndpoints } from './voip';

export type Endpoints = ChannelsEndpoints &
ChatEndpoints &
Expand All @@ -44,4 +45,5 @@ export type Endpoints = ChannelsEndpoints &
PushEndpoints &
DirectoryEndpoint &
AutoTranslateEndpoints &
ModerationEndpoints;
ModerationEndpoints &
VoipFreeSwitchEndpoints;
14 changes: 14 additions & 0 deletions app/definitions/rest/v1/voip.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { FreeSwitchExtension } from '../../../lib/voip/definitions/FreeSwitchExtension';

export type VoipFreeSwitchExtensionGetInfoProps = {
userId: string;
};

export type VoipFreeSwitchEndpoints = {
'voip-freeswitch.extension.getRegistrationInfoByUserId': {
GET: (params: VoipFreeSwitchExtensionGetInfoProps) => {
extension: FreeSwitchExtension;
credentials: { password: string; websocketPath: string };
};
};
};
4 changes: 4 additions & 0 deletions app/i18n/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,10 @@
"Enabled_E2E_Encryption_for_this_room": "enabled E2E encryption for this room",
"Encrypt__room_type__": "Encrypt {{room_type}}",
"Encrypt__room_type__info__room_name__": "Ensure only intended recipients can access messages and files in {{room_name}}.",
"Enable_voice_calling": "Enable voice calling",
"Disable_voice_calling": "Disable voice calling",
"Enabling_voice_calling": "Enabling voice calling",
"Disabling_voice_calling": "Disabling voice calling",
"Encrypted": "Encrypted",
"Encrypted_file": "Encrypted file",
"Encrypted_message": "Encrypted message",
Expand Down
6 changes: 6 additions & 0 deletions app/lib/constants/defaultSettings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -270,5 +270,11 @@ export const defaultSettings = {
Cloud_Workspace_AirGapped_Restrictions_Remaining_Days: {
type: 'valueAsNumber'
},
WebRTC_Servers: {
type: 'valueAsString'
},
VoIP_TeamCollab_FreeSwitch_Host: {
type: 'valueAsString'
},
...deprecatedSettings
} as const;
80 changes: 80 additions & 0 deletions app/lib/hooks/useStartCall/StartCallActionSheet.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import React from 'react';
import { FlatList, View } from 'react-native';
import { useSafeAreaInsets } from 'react-native-safe-area-context';

import * as List from '../../../containers/List';
import { useTheme } from '../../../theme';
import styles from './styles';
import Button from '../../../containers/Button';
import { useVideoConf } from '../useVideoConf';
import { useVoip } from '../useVoip';
import useUserData from '../useUserData';
import { useActionSheet } from '../../../containers/ActionSheet';

const ROW_HEIGHT = 60;
const MAX_ROWS = 2;

function StartCallActionSheet({ rid, ruid }: { ruid: string; rid: string }): React.ReactElement {
const user = useUserData(ruid);
const { colors } = useTheme();
const insets = useSafeAreaInsets();
const { hideActionSheet } = useActionSheet();

const { callEnabled, showInitCallActionSheet } = useVideoConf(rid);
const { startCall: startVoiceCall } = useVoip();

const options = [
{
id: 'start-voice-call',
label: 'Voice call',
icon: '',
enabled: true,
onPress: () => {
startVoiceCall(user.freeSwitchExtension as string);
hideActionSheet();
}
},
{
id: 'start-video-call',
label: 'Video call',
icon: '',
enabled: callEnabled,
onPress: () => {
hideActionSheet();
setTimeout(() => showInitCallActionSheet(), 500);
}
}
];

const renderItem = ({ item }: { item: { id: string; icon: string; label: string; onPress(): void } }) => (
<Button
title={item.label}
type='secondary'
onPress={item.onPress}
style={styles.button}
color={colors.badgeBackgroundLevel2}
backgroundColor={colors.surfaceRoom}
styleText={[styles.buttonText, { textAlign: 'center', color: colors.fontInfo }]}
/>
);

return (
<View
style={{
backgroundColor: colors.surfaceRoom,
borderColor: colors.strokeLight,
marginBottom: insets.bottom
}}>
<FlatList
style={{ maxHeight: MAX_ROWS * ROW_HEIGHT }}
data={options}
keyExtractor={item => item.id}
renderItem={renderItem}
ItemSeparatorComponent={List.Separator}
keyboardShouldPersistTaps='always'
/>
</View>
);
}

export default StartCallActionSheet;
16 changes: 16 additions & 0 deletions app/lib/hooks/useStartCall/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { useActionSheet } from '../../../containers/ActionSheet';
import StartCallActionSheet from './StartCallActionSheet';

export const useStartCall = ({ rid, ruid }: { rid: string; ruid: string }) => {
const { showActionSheet } = useActionSheet();

const startCall = () => {
showActionSheet({
children: <StartCallActionSheet rid={rid} ruid={ruid} />
});
};

return {
startCall
};
};
19 changes: 19 additions & 0 deletions app/lib/hooks/useStartCall/styles.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { StyleSheet } from 'react-native';

import sharedStyles from '../../../views/Styles';

export const ROW_HEIGHT = 56;

export default StyleSheet.create({
buttonText: {
fontSize: 16,
marginRight: 12,
paddingVertical: 10,
...sharedStyles.textRegular
},
button: {
height: 46,
justifyContent: 'center',
marginBottom: 0
}
});
14 changes: 12 additions & 2 deletions app/lib/hooks/useUserData.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,17 @@ import { SubscriptionType } from '../../definitions';
import { Services } from '../services';
import { useAppSelector } from './useAppSelector';

type UserState = {
username: string;
avatar: string;
uid: string;
type: string;
direct: boolean;
freeSwitchExtension?: string;
};

const useUserData = (rid: string) => {
const [user, setUser] = useState({ username: '', avatar: '', uid: '', type: '', direct: false });
const [user, setUser] = useState<UserState>({ username: '', avatar: '', uid: '', type: '', direct: false });
const { useRealName } = useAppSelector(state => ({
useRealName: state.settings.UI_Use_Real_Name as boolean
}));
Expand Down Expand Up @@ -36,7 +45,8 @@ const useUserData = (rid: string) => {
avatar: user.username,
uid: user._id,
type: SubscriptionType.DIRECT,
direct: true
direct: true,
freeSwitchExtension: user.freeSwitchExtension
});
}
} catch (error) {
Expand Down
9 changes: 9 additions & 0 deletions app/lib/hooks/useVoip/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { useDispatch } from 'react-redux';

import { startCall } from '../../../actions/voip';

export const useVoip = (): { startCall: (digitis: string) => void } => {
const dispatch = useDispatch();

return { startCall: (digits: string) => dispatch(startCall(digits)) };
};
3 changes: 3 additions & 0 deletions app/lib/services/restApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1083,3 +1083,6 @@ export const getSupportedVersionsCloud = (uniqueId?: string, domain?: string) =>
fetch(`https://releases.rocket.chat/v2/server/supportedVersions?uniqueId=${uniqueId}&domain=${domain}&source=mobile`);

export const setUserPassword = (password: string) => sdk.methodCall('setUserPassword', password);

export const getRegistrationInfo = (userId: string) =>
sdk.get('voip-freeswitch.extension.getRegistrationInfoByUserId', { userId });
Loading