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

VoIP: DTMF Support #3932

Merged
merged 28 commits into from
Feb 10, 2021
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
8993f61
Introduce DialpadConfiguration
ismailgulek Jan 14, 2021
d34bd5a
Introduce digit delegate method on dial pad
ismailgulek Jan 14, 2021
8523524
Make call delegate method optional
ismailgulek Jan 14, 2021
6f57c99
Implement openDialpad on call screen
ismailgulek Jan 14, 2021
15a9777
Update CHANGES.rst
ismailgulek Jan 14, 2021
d2d16be
Merge branch 'voip_dialpad' into voip_dtmf
ismailgulek Jan 20, 2021
eb2f587
Fix pasted phone number issue
ismailgulek Jan 20, 2021
9807889
Add localizations
ismailgulek Jan 21, 2021
536147c
Improve dial pad screen with new config
ismailgulek Jan 21, 2021
9e3c083
Add call transfer module
ismailgulek Jan 21, 2021
c0693f1
Adapt new dial pad config
ismailgulek Jan 21, 2021
9af9487
Show transfer view and implement delegate methods
ismailgulek Jan 21, 2021
0931fae
Update CHANGES.rst
ismailgulek Jan 21, 2021
e85aea7
Add MatrixContactsDataSource
ismailgulek Jan 22, 2021
c9ff513
Add localization for error case
ismailgulek Jan 22, 2021
c28a1db
Use Matrix only contacts data source
ismailgulek Jan 22, 2021
e355707
Implement phone number lookup
ismailgulek Jan 22, 2021
c74e778
Refresh local contacts when showing contact selection
ismailgulek Jan 22, 2021
412213e
Change section title
ismailgulek Jan 22, 2021
6ee30ff
Implement recent section with ignoring current call user
ismailgulek Jan 22, 2021
109c603
Fix colors
ismailgulek Jan 22, 2021
dc48cdc
Fix indexpaths
ismailgulek Jan 22, 2021
7e7a3dd
Remove empty section spaces
ismailgulek Jan 22, 2021
b14c8f8
Merge branch 'voip_dialpad' into voip_dtmf
ismailgulek Feb 2, 2021
9d6beb1
Merge branch 'voip_dtmf' into voip_call_transfer
ismailgulek Feb 2, 2021
46f4a4f
Merge branch 'voip_dialpad' into voip_dtmf
ismailgulek Feb 9, 2021
190e111
Merge branch 'voip_dtmf' into voip_call_transfer
ismailgulek Feb 9, 2021
7c7b512
Merge pull request #3963 from vector-im/voip_call_transfer
ismailgulek Feb 10, 2021
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
1 change: 1 addition & 0 deletions CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ Changes to be released in next version
🙌 Improvements
* Social login: Handle new identity provider brand field in order to customize buttons (#3980).
* Widgets: Support $matrix_room_id and $matrix_widget_id parameters (#3987).
* VoIP: Implement DTMF on call screen (#3929).

🐛 Bugfix
* Push: Fix PushKit crashes due to undecryptable call invites (#3986).
Expand Down
4 changes: 4 additions & 0 deletions Riot.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -898,6 +898,7 @@
EC757B2925B85C7F00DF5787 /* DialpadActionButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC757B2525B85C7F00DF5787 /* DialpadActionButton.swift */; };
EC757B2D25B864DA00DF5787 /* CustomSizedPresentable.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC757B2B25B864DA00DF5787 /* CustomSizedPresentable.swift */; };
EC757B2E25B864DA00DF5787 /* CustomSizedPresentationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC757B2C25B864DA00DF5787 /* CustomSizedPresentationController.swift */; };
EC757B3025B8671100DF5787 /* DialpadConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC757B2F25B8671000DF5787 /* DialpadConfiguration.swift */; };
EC7749C925A47E2600AB1295 /* CallVCEnterPipOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC7749C825A47E2600AB1295 /* CallVCEnterPipOperation.swift */; };
EC7749CB25A48E7500AB1295 /* CallVCExitPipOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC7749CA25A48E7500AB1295 /* CallVCExitPipOperation.swift */; };
EC7749CD25A48F6100AB1295 /* PiPAnimator.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC7749CC25A48F6100AB1295 /* PiPAnimator.swift */; };
Expand Down Expand Up @@ -2182,6 +2183,7 @@
EC757B2525B85C7F00DF5787 /* DialpadActionButton.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DialpadActionButton.swift; sourceTree = "<group>"; };
EC757B2B25B864DA00DF5787 /* CustomSizedPresentable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CustomSizedPresentable.swift; sourceTree = "<group>"; };
EC757B2C25B864DA00DF5787 /* CustomSizedPresentationController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CustomSizedPresentationController.swift; sourceTree = "<group>"; };
EC757B2F25B8671000DF5787 /* DialpadConfiguration.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DialpadConfiguration.swift; sourceTree = "<group>"; };
EC7749C825A47E2600AB1295 /* CallVCEnterPipOperation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CallVCEnterPipOperation.swift; sourceTree = "<group>"; };
EC7749CA25A48E7500AB1295 /* CallVCExitPipOperation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CallVCExitPipOperation.swift; sourceTree = "<group>"; };
EC7749CC25A48F6100AB1295 /* PiPAnimator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PiPAnimator.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -5275,6 +5277,7 @@
EC757B2025B85C7F00DF5787 /* Dialpad */ = {
isa = PBXGroup;
children = (
EC757B2F25B8671000DF5787 /* DialpadConfiguration.swift */,
EC757B2125B85C7F00DF5787 /* DialpadViewController.storyboard */,
EC757B2225B85C7F00DF5787 /* DialpadViewController.swift */,
EC757B2325B85C7F00DF5787 /* Views */,
Expand Down Expand Up @@ -6734,6 +6737,7 @@
B10A3E9324FE8254007C380F /* AppCoordinator.swift in Sources */,
B1B5572A20EE6C4D00210D55 /* RoomMemberDetailsViewController.m in Sources */,
EC3B066C24AC6ADE000DF9BF /* CrossSigningSetupBannerCell.swift in Sources */,
EC757B3025B8671100DF5787 /* DialpadConfiguration.swift in Sources */,
ECF57A4E25090C23004BBF9D /* EnterNewRoomDetailsViewModelType.swift in Sources */,
B1B5590120EF768F00210D55 /* RoomMembershipExpandedWithPaginationTitleBubbleCell.m in Sources */,
32F6B96B2270623100BBA352 /* KeyVerificationDataLoadingViewAction.swift in Sources */,
Expand Down
39 changes: 38 additions & 1 deletion Riot/Modules/Call/CallViewController.m
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@

#import "IncomingCallView.h"

@interface CallViewController () <PictureInPicturable>
@interface CallViewController () <PictureInPicturable, DialpadViewControllerDelegate>
{
// Current alert (if any).
UIAlertController *currentAlert;
Expand All @@ -40,6 +40,8 @@ @interface CallViewController () <PictureInPicturable>
@property (nonatomic, strong) id<Theme> overriddenTheme;
@property (nonatomic, assign) BOOL inPiP;

@property (nonatomic, strong) CustomSizedPresentationController *customSizedPresentationController;

@end

@implementation CallViewController
Expand Down Expand Up @@ -509,6 +511,41 @@ - (void)showOverlayContainer:(BOOL)isShown
[super showOverlayContainer:isShown];
}

#pragma mark - DTMF

- (void)openDialpad
{
DialpadConfiguration *config = [[DialpadConfiguration alloc] initWithShowsBackspaceButton:NO
showsCallButton:NO
formattingEnabled:NO
editingEnabled:NO];
DialpadViewController *controller = [DialpadViewController instantiateWithConfiguration:config];
controller.delegate = self;
self.customSizedPresentationController = [[CustomSizedPresentationController alloc] initWithPresentedViewController:controller presentingViewController:self];
self.customSizedPresentationController.dismissOnBackgroundTap = NO;
self.customSizedPresentationController.cornerRadius = 16;

controller.transitioningDelegate = self.customSizedPresentationController;
[self presentViewController:controller animated:YES completion:nil];
}

#pragma mark - DialpadViewControllerDelegate

- (void)dialpadViewControllerDidTapClose:(DialpadViewController *)viewController
{
[viewController dismissViewControllerAnimated:YES completion:nil];
self.customSizedPresentationController = nil;
}

- (void)dialpadViewControllerDidTapDigit:(DialpadViewController *)viewController digit:(NSString *)digit
{
BOOL result = [self.mxCall sendDTMF:digit
duration:0
interToneGap:0];

NSLog(@"[CallViewController] Sending DTMF tones %@", result ? @"succeeded": @"failed");
}

#pragma mark - PictureInPicturable

- (void)enterPiP
Expand Down
49 changes: 49 additions & 0 deletions Riot/Modules/Call/Dialpad/DialpadConfiguration.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
//
// Copyright 2020 New Vector Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

import Foundation

/// Dial pad configuration object, to be passed when initializing a `DialpadViewController`.
@objcMembers
class DialpadConfiguration: NSObject {

/// Option for a dial pad to show the backspace button or not.
var showsBackspaceButton: Bool

/// Option for a dial pad to show the call button or not.
var showsCallButton: Bool

/// Option for a dial pad to enable number formatting when typing or not.
var formattingEnabled: Bool

/// Option for a dial pad to enable editing on typed text or not.
var editingEnabled: Bool

/// Default configuration object. All options are enabled by default.
static let `default`: DialpadConfiguration = DialpadConfiguration()

init(showsBackspaceButton: Bool = true,
showsCallButton: Bool = true,
formattingEnabled: Bool = true,
editingEnabled: Bool = true) {
self.showsBackspaceButton = showsBackspaceButton
self.showsCallButton = showsCallButton
self.formattingEnabled = formattingEnabled
self.editingEnabled = editingEnabled
super.init()
}

}
37 changes: 31 additions & 6 deletions Riot/Modules/Call/Dialpad/DialpadViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,11 @@ import UIKit
import libPhoneNumber_iOS

@objc protocol DialpadViewControllerDelegate: class {
func dialpadViewControllerDidTapCall(_ viewController: DialpadViewController, withPhoneNumber phoneNumber: String)
@objc optional func dialpadViewControllerDidTapCall(_ viewController: DialpadViewController,
withPhoneNumber phoneNumber: String)
func dialpadViewControllerDidTapClose(_ viewController: DialpadViewController)

@objc optional func dialpadViewControllerDidTapDigit(_ viewController: DialpadViewController, digit: String)
}

@objcMembers
Expand All @@ -37,18 +40,21 @@ class DialpadViewController: UIViewController {
// avoid showing keyboard on text field
phoneNumberTextField.inputView = UIView()
phoneNumberTextField.inputAccessoryView = UIView()
phoneNumberTextField.isUserInteractionEnabled = configuration.editingEnabled
}
}
@IBOutlet private weak var lineView: UIView!
@IBOutlet private weak var digitsStackView: UIStackView!
@IBOutlet private weak var backspaceButton: DialpadActionButton! {
didSet {
backspaceButton.type = .backspace
backspaceButton.isHidden = !configuration.showsBackspaceButton
}
}
@IBOutlet private weak var callButton: DialpadActionButton! {
didSet {
callButton.type = .call
callButton.isHidden = !configuration.showsCallButton
}
}

Expand All @@ -64,10 +70,12 @@ class DialpadViewController: UIViewController {
/// Phone number as formatted
private var phoneNumber: String = "" {
willSet {
wasCursorAtTheEnd = isCursorAtTheEnd()
if configuration.editingEnabled {
wasCursorAtTheEnd = isCursorAtTheEnd()
}
} didSet {
phoneNumberTextField.text = phoneNumber
if wasCursorAtTheEnd {
if configuration.editingEnabled && wasCursorAtTheEnd {
moveCursorToTheEnd()
}
}
Expand All @@ -77,16 +85,18 @@ class DialpadViewController: UIViewController {
return phoneNumber.vc_removingAllWhitespaces()
}
private var theme: Theme!
private var configuration: DialpadConfiguration!

// MARK: Public

weak var delegate: DialpadViewControllerDelegate?

// MARK: - Setup

class func instantiate() -> DialpadViewController {
class func instantiate(withConfiguration configuration: DialpadConfiguration = .default) -> DialpadViewController {
let viewController = StoryboardScene.DialpadViewController.initialScene.instantiate()
viewController.theme = ThemeService.shared().theme
viewController.configuration = configuration
return viewController
}

Expand Down Expand Up @@ -155,7 +165,7 @@ class DialpadViewController: UIViewController {
}

private func reformatPhoneNumber() {
guard let phoneNumberUtil = NBPhoneNumberUtil.sharedInstance() else {
guard configuration.formattingEnabled, let phoneNumberUtil = NBPhoneNumberUtil.sharedInstance() else {
// no formatter
return
}
Expand Down Expand Up @@ -225,6 +235,15 @@ class DialpadViewController: UIViewController {
@IBAction private func digitButtonAction(_ sender: DialpadButton) {
let digit = sender.title(for: .normal) ?? ""

defer {
delegate?.dialpadViewControllerDidTapDigit?(self, digit: digit)
}

if !configuration.editingEnabled {
phoneNumber += digit
return
}

if let selectedRange = phoneNumberTextField.selectedTextRange {
if isCursorAtTheEnd() {
phoneNumber += digit
Expand Down Expand Up @@ -254,6 +273,11 @@ class DialpadViewController: UIViewController {
return
}

if !configuration.editingEnabled {
phoneNumber.removeLast()
return
}

if let selectedRange = phoneNumberTextField.selectedTextRange {
let cursorStartPos = phoneNumberTextField.offset(from: phoneNumberTextField.beginningOfDocument, to: selectedRange.start)
let cursorEndPos = phoneNumberTextField.offset(from: phoneNumberTextField.beginningOfDocument, to: selectedRange.end)
Expand Down Expand Up @@ -291,7 +315,8 @@ class DialpadViewController: UIViewController {
}

@IBAction private func callButtonAction(_ sender: DialpadActionButton) {
delegate?.dialpadViewControllerDidTapCall(self, withPhoneNumber: rawPhoneNumber)
phoneNumber = phoneNumberTextField.text ?? ""
delegate?.dialpadViewControllerDidTapCall?(self, withPhoneNumber: rawPhoneNumber)
}

}
Expand Down
2 changes: 1 addition & 1 deletion Riot/Modules/Room/RoomViewController.m
Original file line number Diff line number Diff line change
Expand Up @@ -1658,7 +1658,7 @@ - (void)showMemberDetails:(MXRoomMember *)member

- (void)openDialpad
{
DialpadViewController *controller = [DialpadViewController instantiate];
DialpadViewController *controller = [DialpadViewController instantiateWithConfiguration:[DialpadConfiguration default]];
controller.delegate = self;
self.customSizedPresentationController = [[CustomSizedPresentationController alloc] initWithPresentedViewController:controller presentingViewController:self];
self.customSizedPresentationController.dismissOnBackgroundTap = NO;
Expand Down