Skip to content

Commit

Permalink
VoIP: Fallback to matrix.org STUN server with a confirmation dialog
Browse files Browse the repository at this point in the history
  • Loading branch information
manuroe committed Aug 28, 2019
1 parent 78d7c99 commit c51977a
Show file tree
Hide file tree
Showing 10 changed files with 208 additions and 2 deletions.
1 change: 1 addition & 0 deletions CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ Improvements:
* Privacy: Make clear that device names are publicly readable (#2662).
* Widgets: Whitelist [MSC1961](https://github.com/matrix-org/matrix-doc/pull/1961) widget urls.
* Settings: CALLS section: Always display the CallKit option but grey it out when not available (only on China).
* VoIP: Fallback to matrix.org STUN server with a confirmation dialog (#2646).

Changes in 0.9.2 (2019-08-08)
===============================================
Expand Down
57 changes: 57 additions & 0 deletions Riot/AppDelegate.m
Original file line number Diff line number Diff line change
Expand Up @@ -2605,6 +2605,11 @@ - (void)initMatrixSessions
// Let's call invite be valid for 1 minute
mxSession.callManager.inviteLifetime = 60000;

if (RiotSettings.shared.allowStunServerFallback)
{
mxSession.callManager.fallbackSTUNServer = RiotSettings.shared.stunServerFallback;
}

// Setup CallKit
if ([MXCallKitAdapter callKitAvailable])
{
Expand Down Expand Up @@ -3850,6 +3855,12 @@ - (void)dismissCallViewController:(MXKCallViewController *)callViewController co
NSString *btnTitle = [NSString stringWithFormat:NSLocalizedStringFromTable(@"active_call_details", @"Vector", nil), callViewController.callerNameLabel.text];
[self addCallStatusBar:btnTitle];
}

if ([callViewController isKindOfClass:[CallViewController class]]
&& ((CallViewController*)callViewController).shouldPromptForStunServerFallback)
{
[self promptForStunServerFallback];
}

if (completion)
{
Expand Down Expand Up @@ -3883,6 +3894,52 @@ - (void)dismissCallViewController:(MXKCallViewController *)callViewController co
}
}

- (void)promptForStunServerFallback
{
[_errorNotification dismissViewControllerAnimated:NO completion:nil];

NSString *stunFallbackHost = RiotSettings.shared.stunServerFallback;
// Remove "stun:"
stunFallbackHost = [stunFallbackHost componentsSeparatedByString:@":"].lastObject;

MXSession *mainSession = self.mxSessions.firstObject;
NSString *homeServerName = mainSession.matrixRestClient.credentials.homeServerName;

NSString *message = [NSString stringWithFormat:@"%@\n\n%@",
[NSString stringWithFormat:NSLocalizedStringFromTable(@"call_no_stun_server_error_message_1", @"Vector", nil), homeServerName],
[NSString stringWithFormat: NSLocalizedStringFromTable(@"call_no_stun_server_error_message_2", @"Vector", nil), stunFallbackHost]];

_errorNotification = [UIAlertController alertControllerWithTitle:NSLocalizedStringFromTable(@"call_no_stun_server_error_title", @"Vector", nil)
message:message
preferredStyle:UIAlertControllerStyleAlert];

[_errorNotification addAction:[UIAlertAction actionWithTitle:[NSString stringWithFormat: NSLocalizedStringFromTable(@"call_no_stun_server_error_use_fallback_button", @"Vector", nil), stunFallbackHost]
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action) {

RiotSettings.shared.allowStunServerFallback = YES;
mainSession.callManager.fallbackSTUNServer = RiotSettings.shared.stunServerFallback;

[AppDelegate theDelegate].errorNotification = nil;
}]];

[_errorNotification addAction:[UIAlertAction actionWithTitle:[NSBundle mxk_localizedStringForKey:@"cancel"]
style:UIAlertActionStyleCancel
handler:^(UIAlertAction * action) {

RiotSettings.shared.allowStunServerFallback = NO;

[AppDelegate theDelegate].errorNotification = nil;
}]];

// Display the error notification
if (!isErrorNotificationSuspended)
{
[_errorNotification mxk_setAccessibilityIdentifier:@"AppDelegateErrorAlert"];
[self showNotificationAlert:_errorNotification];
}
}

#pragma mark - Jitsi call

- (void)displayJitsiViewControllerWithWidget:(Widget*)jitsiWidget andVideo:(BOOL)video
Expand Down
2 changes: 2 additions & 0 deletions Riot/Assets/Riot-Defaults.plist
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,8 @@
<false/>
<key>createConferenceCallsWithJitsi</key>
<true/>
<key>stunServerFallback</key>
<string>stun:turn.matrix.org</string>
<key>enableRageShake</key>
<true/>
<key>maxAllowedMediaCacheSize</key>
Expand Down
8 changes: 8 additions & 0 deletions Riot/Assets/en.lproj/Vector.strings
Original file line number Diff line number Diff line change
Expand Up @@ -405,6 +405,9 @@

"settings_enable_callkit" = "Integrated calling";
"settings_callkit_info" = "Receive incoming calls on your lock screen. See your Riot calls in the system's call history. If iCloud is enabled, this call history will be shared with Apple.";
"settings_calls_stun_server_fallback_button" = "Allow fallback call assist server";
"settings_calls_stun_server_fallback_description" = "Allow fallback call assist server %@ when your homeserver does not offer one (your IP address would be shared during a call).";

"settings_ui_language" = "Language";
"settings_ui_theme" = "Theme";
"settings_ui_theme_auto" = "Auto";
Expand Down Expand Up @@ -631,6 +634,11 @@
"call_already_displayed" = "There is already a call in progress.";
"call_jitsi_error" = "Failed to join the conference call.";

"call_no_stun_server_error_title" ="Call failed due to misconfigured server";
"call_no_stun_server_error_message_1" ="Please ask the administrator of your homeserver %@ to configure a TURN server in order for calls to work reliably.";
"call_no_stun_server_error_message_2" ="Alternatively, you can try to use the public server at %@, but this will not be as reliable, and it will share your IP address with that server. You can also manage this in Settings";
"call_no_stun_server_error_use_fallback_button" = "Try using %@";

// No VoIP support
"no_voip_title" = "Incoming call";
"no_voip" = "%@ is calling you but %@ does not support calls yet.\nYou can ignore this notification and answer the call from another device or you can reject it.";
Expand Down
1 change: 1 addition & 0 deletions Riot/Generated/RiotDefaults.swift
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ internal enum RiotDefaults {
internal static let showRedactionsInRoomHistory: Bool = _document["showRedactionsInRoomHistory"]
internal static let showUnsupportedEventsInRoomHistory: Bool = _document["showUnsupportedEventsInRoomHistory"]
internal static let sortRoomMembersUsingLastSeenTime: Bool = _document["sortRoomMembersUsingLastSeenTime"]
internal static let stunServerFallback: String = _document["stunServerFallback"]
internal static let syncLocalContacts: Bool = _document["syncLocalContacts"]
internal static let webAppUrl: String = _document["webAppUrl"]
internal static let webAppUrlBeta: String = _document["webAppUrlBeta"]
Expand Down
24 changes: 24 additions & 0 deletions Riot/Generated/Strings.swift
Original file line number Diff line number Diff line change
Expand Up @@ -370,6 +370,22 @@ internal enum VectorL10n {
internal static var callJitsiError: String {
return VectorL10n.tr("Vector", "call_jitsi_error")
}
/// Please ask the administrator of your homeserver %@ to configure a TURN server in order for calls to work reliably.
internal static func callNoStunServerErrorMessage1(_ p1: String) -> String {
return VectorL10n.tr("Vector", "call_no_stun_server_error_message_1", p1)
}
/// Alternatively, you can try to use the public server at %@, but this will not be as reliable, and it will share your IP address with that server. You can also manage this in Settings
internal static func callNoStunServerErrorMessage2(_ p1: String) -> String {
return VectorL10n.tr("Vector", "call_no_stun_server_error_message_2", p1)
}
/// Call failed due to misconfigured server
internal static var callNoStunServerErrorTitle: String {
return VectorL10n.tr("Vector", "call_no_stun_server_error_title")
}
/// Try using %@
internal static func callNoStunServerErrorUseFallbackButton(_ p1: String) -> String {
return VectorL10n.tr("Vector", "call_no_stun_server_error_use_fallback_button", p1)
}
/// Camera
internal static var camera: String {
return VectorL10n.tr("Vector", "camera")
Expand Down Expand Up @@ -2394,6 +2410,14 @@ internal enum VectorL10n {
internal static var settingsCallsSettings: String {
return VectorL10n.tr("Vector", "settings_calls_settings")
}
/// Allow fallback call assist server
internal static var settingsCallsStunServerFallbackButton: String {
return VectorL10n.tr("Vector", "settings_calls_stun_server_fallback_button")
}
/// Allow fallback call assist server %@ when your homeserver does not offer one (your IP address would be shared during a call).
internal static func settingsCallsStunServerFallbackDescription(_ p1: String) -> String {
return VectorL10n.tr("Vector", "settings_calls_stun_server_fallback_description", p1)
}
/// Change password
internal static var settingsChangePassword: String {
return VectorL10n.tr("Vector", "settings_change_password")
Expand Down
22 changes: 22 additions & 0 deletions Riot/Managers/Settings/RiotSettings.swift
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ final class RiotSettings: NSObject {
static let notificationsShowDecryptedContent = "showDecryptedContent"
static let pinRoomsWithMissedNotifications = "pinRoomsWithMissedNotif"
static let pinRoomsWithUnreadMessages = "pinRoomsWithUnread"
static let allowStunServerFallback = "allowStunServerFallback"
static let stunServerFallback = "stunServerFallback"
}

/// Riot Standard Room Member Power Level
Expand Down Expand Up @@ -119,4 +121,24 @@ final class RiotSettings: NSObject {
UserDefaults.standard.set(newValue, forKey: UserDefaultsKeys.createConferenceCallsWithJitsi)
}
}


// MARK: Calls

/// Indicate if `allowStunServerFallback` settings has been set once.
var isAllowStunServerFallbackHasBeenSetOnce: Bool {
return UserDefaults.standard.object(forKey: UserDefaultsKeys.allowStunServerFallback) != nil
}

var allowStunServerFallback: Bool {
get {
return UserDefaults.standard.bool(forKey: UserDefaultsKeys.allowStunServerFallback)
} set {
UserDefaults.standard.set(newValue, forKey: UserDefaultsKeys.allowStunServerFallback)
}
}

var stunServerFallback: String? {
return UserDefaults.standard.string(forKey: UserDefaultsKeys.stunServerFallback)
}
}
3 changes: 3 additions & 0 deletions Riot/Modules/Call/CallViewController.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,4 +26,7 @@

@property (unsafe_unretained, nonatomic) IBOutlet NSLayoutConstraint *callerImageViewWidthConstraint;

// At the end of call, this flag indicates if the prompt to use the fallback should be displayed
@property (nonatomic) BOOL shouldPromptForStunServerFallback;

@end
49 changes: 49 additions & 0 deletions Riot/Modules/Call/CallViewController.m
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,9 @@ @interface CallViewController ()

// Observe kThemeServiceDidChangeThemeNotification to handle user interface theme change.
id kThemeServiceDidChangeThemeNotificationObserver;

// Flag to compute self.shouldPromptForStunServerFallback
BOOL promptForStunServerFallback;
}

@end
Expand All @@ -52,6 +55,9 @@ - (void)finalizeInit
// Setup `MXKViewControllerHandling` properties
self.enableBarTintColorStatusChange = NO;
self.rageShakeManager = [RageShakeManager sharedManager];

promptForStunServerFallback = NO;
_shouldPromptForStunServerFallback = NO;
}

- (void)viewDidLoad
Expand Down Expand Up @@ -229,6 +235,13 @@ - (UIView *)createIncomingCallView

#pragma mark - MXCallDelegate

- (void)call:(MXCall *)call stateDidChange:(MXCallState)state reason:(MXEvent *)event
{
[super call:call stateDidChange:state reason:event];

[self checkStunServerFallbackWithCallState:state];
}

- (void)call:(MXCall *)call didEncounterError:(NSError *)error
{
if ([error.domain isEqualToString:MXEncryptingErrorDomain]
Expand Down Expand Up @@ -333,6 +346,42 @@ - (void)call:(MXCall *)call didEncounterError:(NSError *)error
}
}


#pragma mark - Fallback STUN server

- (void)checkStunServerFallbackWithCallState:(MXCallState)callState
{
// Detect if we should display the prompt to fallback to the STUN server defined
// in the app plist if the homeserver does not provide STUN or TURN servers.
// We should if the call ends while we were in connecting state
if (!self.mainSession.callManager.turnServers
&& !self.mainSession.callManager.fallbackSTUNServer
&& !RiotSettings.shared.isAllowStunServerFallbackHasBeenSetOnce)
{
switch (callState)
{
case MXCallStateConnecting:
promptForStunServerFallback = YES;
break;

case MXCallStateConnected:
promptForStunServerFallback = NO;
break;

case MXCallStateEnded:
if (promptForStunServerFallback)
{
_shouldPromptForStunServerFallback = YES;
//NSLog(@"####### :)");
}

default:
break;
}
}
}


#pragma mark - Properties

- (UIImage*)picturePlaceholder
Expand Down
43 changes: 41 additions & 2 deletions Riot/Modules/Settings/SettingsViewController.m
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,9 @@
enum
{
CALLS_ENABLE_CALLKIT_INDEX = 0,
CALLS_DESCRIPTION_INDEX,
CALLS_CALLKIT_DESCRIPTION_INDEX,
CALLS_ENABLE_STUN_SERVER_FALLBACK_INDEX,
CALLS_STUN_SERVER_FALLBACK_DESCRIPTION_INDEX,
CALLS_COUNT
};

Expand Down Expand Up @@ -1266,6 +1268,11 @@ - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger
else if (section == SETTINGS_SECTION_CALLS_INDEX)
{
count = CALLS_COUNT;

if (!RiotSettings.shared.stunServerFallback)
{
count -= 2;
}
}
else if (section == SETTINGS_SECTION_USER_INTERFACE_INDEX)
{
Expand Down Expand Up @@ -1834,7 +1841,7 @@ - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(N

cell = labelAndSwitchCell;
}
else if (row == CALLS_DESCRIPTION_INDEX)
else if (row == CALLS_CALLKIT_DESCRIPTION_INDEX)
{
MXKTableViewCell *globalInfoCell = [self getDefaultTableViewCell:tableView];
globalInfoCell.textLabel.text = NSLocalizedStringFromTable(@"settings_callkit_info", @"Vector", nil);
Expand All @@ -1846,6 +1853,30 @@ - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(N
globalInfoCell.textLabel.enabled = NO;
}

cell = globalInfoCell;
}
else if (row == CALLS_ENABLE_STUN_SERVER_FALLBACK_INDEX)
{
MXKTableViewCellWithLabelAndSwitch* labelAndSwitchCell = [self getLabelAndSwitchCell:tableView forIndexPath:indexPath];
labelAndSwitchCell.mxkLabel.text = NSLocalizedStringFromTable(@"settings_calls_stun_server_fallback_button", @"Vector", nil);
labelAndSwitchCell.mxkSwitch.on = RiotSettings.shared.allowStunServerFallback;
labelAndSwitchCell.mxkSwitch.onTintColor = ThemeService.shared.theme.tintColor;
labelAndSwitchCell.mxkSwitch.enabled = YES;
[labelAndSwitchCell.mxkSwitch addTarget:self action:@selector(toggleStunServerFallback:) forControlEvents:UIControlEventTouchUpInside];

cell = labelAndSwitchCell;
}
else if (row == CALLS_STUN_SERVER_FALLBACK_DESCRIPTION_INDEX)
{
NSString *stunFallbackHost = RiotSettings.shared.stunServerFallback;
// Remove "stun:"
stunFallbackHost = [stunFallbackHost componentsSeparatedByString:@":"].lastObject;

MXKTableViewCell *globalInfoCell = [self getDefaultTableViewCell:tableView];
globalInfoCell.textLabel.text = [NSString stringWithFormat:NSLocalizedStringFromTable(@"settings_calls_stun_server_fallback_description", @"Vector", nil), stunFallbackHost];
globalInfoCell.textLabel.numberOfLines = 0;
globalInfoCell.selectionStyle = UITableViewCellSelectionStyleNone;

cell = globalInfoCell;
}
}
Expand Down Expand Up @@ -2945,6 +2976,14 @@ - (void)toggleCallKit:(id)sender
[MXKAppSettings standardAppSettings].enableCallKit = switchButton.isOn;
}

- (void)toggleStunServerFallback:(id)sender
{
UISwitch *switchButton = (UISwitch*)sender;
RiotSettings.shared.allowStunServerFallback = switchButton.isOn;

self.mainSession.callManager.fallbackSTUNServer = RiotSettings.shared.allowStunServerFallback ? RiotSettings.shared.stunServerFallback : nil;
}

- (void)toggleShowDecodedContent:(id)sender
{
UISwitch *switchButton = (UISwitch*)sender;
Expand Down

0 comments on commit c51977a

Please sign in to comment.