Skip to content

Commit

Permalink
MM-54553 - Calls: Enable microphone input in background (Android) (#7585
Browse files Browse the repository at this point in the history
)

* Add foreground service for Android; add voip UIBackgroundMode for iOS

* remove iOS change for this PR

* create foreground notification channel once at startup
  • Loading branch information
cpoile authored Oct 13, 2023
1 parent f2b4312 commit 1fb01df
Show file tree
Hide file tree
Showing 6 changed files with 79 additions and 2 deletions.
6 changes: 5 additions & 1 deletion android/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>

<!-- Request legacy Bluetooth permissions on older devices. -->
<uses-permission android:name="android.permission.BLUETOOTH"
Expand Down Expand Up @@ -102,5 +103,8 @@
<data android:mimeType="*/*" />
</intent-filter>
</activity>
</application>

<!-- For Calls microphone to work in the background -->
<service android:name="com.voximplant.foregroundservice.VIForegroundService"/>
</application>
</manifest>
17 changes: 17 additions & 0 deletions app/products/calls/connection/connection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,11 @@ import InCallManager from 'react-native-incall-manager';
import {mediaDevices, MediaStream, MediaStreamTrack, RTCPeerConnection} from 'react-native-webrtc';

import {setPreferredAudioRoute, setSpeakerphoneOn} from '@calls/actions/calls';
import {
foregroundServiceStart,
foregroundServiceStop,
foregroundServiceSetup,
} from '@calls/connection/foreground_service';
import {processMeanOpinionScore, setAudioDeviceInfo} from '@calls/state';
import {AudioDevice, type AudioDeviceInfo, type AudioDeviceInfoRaw, type CallsConnection} from '@calls/types/calls';
import {getICEServersConfigs} from '@calls/utils';
Expand All @@ -26,6 +31,11 @@ const rtcMonitorInterval = 4000;

const InCallManagerEmitter = new NativeEventEmitter(NativeModules.InCallManager);

// Setup the foreground service channel
if (Platform.OS === 'android') {
foregroundServiceSetup();
}

export async function newConnection(
serverUrl: string,
channelID: string,
Expand Down Expand Up @@ -111,6 +121,10 @@ export async function newConnection(
audioDeviceChanged?.remove();
wiredHeadsetEvent?.remove();

if (Platform.OS === 'android') {
foregroundServiceStop();
}

if (closeCb) {
closeCb();
}
Expand Down Expand Up @@ -249,6 +263,9 @@ export async function newConnection(
}
}
});

// To allow us to use microphone in the background
await foregroundServiceStart();
}

// We default to speakerphone, but not if the WiredHeadset is plugged in.
Expand Down
40 changes: 40 additions & 0 deletions app/products/calls/connection/foreground_service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.

// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
import VIForegroundService from '@voximplant/react-native-foreground-service';

import {logError} from '@utils/log';

const channelConfig = {
id: 'calls_channel',
name: 'Mattermost',
description: 'Mattermost Calls microphone while app is in the background',
enableVibration: false,
};

// Note: multiple calls with same arguments are a noop.
export const foregroundServiceSetup = () => {
VIForegroundService.getInstance().createNotificationChannel(channelConfig);
};

export const foregroundServiceStart = async () => {
const notificationConfig = {
channelId: 'calls_channel',
id: 345678,
title: 'Mattermost',
text: 'Mattermost Calls Microphone',
icon: '',
button: 'Stop',
};
try {
await VIForegroundService.getInstance().startService(notificationConfig);
} catch (e) {
logError('Calls: Cannot start ForegroundService, error:', e);
}
};

export const foregroundServiceStop = async () => {
await VIForegroundService.getInstance().stopService();
};
2 changes: 1 addition & 1 deletion jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ module.exports = {
'\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$': '<rootDir>/test/file_transformer.js',
},
transformIgnorePatterns: [
'node_modules/(?!(@react-native|react-native)|jail-monkey|@sentry/react-native|react-clone-referenced-element|@react-native-community|react-navigation|@react-navigation/.*|validator|react-syntax-highlighter/.*|hast-util-from-selector|hastscript|property-information|hast-util-parse-selector|space-separated-tokens|comma-separated-tokens|zwitch|@mattermost/calls)',
'node_modules/(?!(@react-native|react-native)|jail-monkey|@sentry/react-native|react-clone-referenced-element|@react-native-community|react-navigation|@react-navigation/.*|validator|react-syntax-highlighter/.*|hast-util-from-selector|hastscript|property-information|hast-util-parse-selector|space-separated-tokens|comma-separated-tokens|zwitch|@mattermost/calls|@voximplant/react-native-foreground-service)',
],
moduleNameMapper: {

Expand Down
15 changes: 15 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
"@sentry/react-native": "5.9.0",
"@stream-io/flat-list-mvcp": "0.10.3",
"@tsconfig/react-native": "3.0.2",
"@voximplant/react-native-foreground-service": "3.0.2",
"base-64": "1.0.0",
"commonmark": "npm:@mattermost/[email protected]",
"commonmark-react-renderer": "github:mattermost/commonmark-react-renderer#235bc817bcade503fb81fa51bbbe3c84f958ed12",
Expand Down

0 comments on commit 1fb01df

Please sign in to comment.