Commit 2ebdc425 authored by Kateryna Kostiuk's avatar Kateryna Kostiuk Committed by Andreas Traczyk

call: add call screen

Author: Silbino Gonçalves Matado <silbino.gmatado@savoirfairelinux.com>
Change-Id: I7dfcc20e864967c7d34964c9f7699b5495146ea8
Reviewed-by: Andreas Traczyk's avatarAndreas Traczyk <andreas.traczyk@savoirfairelinux.com>
parent c6c0df8b
......@@ -110,6 +110,9 @@
0E49096C1FEAB225005CAA50 /* CallsAdapterDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E49096B1FEAB225005CAA50 /* CallsAdapterDelegate.swift */; };
0E49096E1FEAC0DE005CAA50 /* CallsService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E49096D1FEAC0DE005CAA50 /* CallsService.swift */; };
0E4909701FEAC1C6005CAA50 /* CallModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E49096F1FEAC1C6005CAA50 /* CallModel.swift */; };
0E4909751FEAC943005CAA50 /* CallViewController.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 0E4909741FEAC943005CAA50 /* CallViewController.storyboard */; };
0E49097A1FEAC9E1005CAA50 /* CallViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E4909791FEAC9E1005CAA50 /* CallViewController.swift */; };
0E49097C1FEACA4B005CAA50 /* CallViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E49097B1FEACA4B005CAA50 /* CallViewModel.swift */; };
0E6949791FA7E71C0029B60A /* BaseViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E6949781FA7E71C0029B60A /* BaseViewController.swift */; };
0E983E6E1FC77C3E0082103E /* ConversationModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E983E6D1FC77C3E0082103E /* ConversationModel.swift */; };
0E9D84491FA7DA6A00C561EB /* ChatTabBarItemViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E9D84481FA7DA6A00C561EB /* ChatTabBarItemViewModel.swift */; };
......@@ -365,6 +368,9 @@
0E49096B1FEAB225005CAA50 /* CallsAdapterDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CallsAdapterDelegate.swift; sourceTree = "<group>"; };
0E49096D1FEAC0DE005CAA50 /* CallsService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CallsService.swift; sourceTree = "<group>"; };
0E49096F1FEAC1C6005CAA50 /* CallModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CallModel.swift; sourceTree = "<group>"; };
0E4909741FEAC943005CAA50 /* CallViewController.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = CallViewController.storyboard; sourceTree = "<group>"; };
0E4909791FEAC9E1005CAA50 /* CallViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CallViewController.swift; sourceTree = "<group>"; };
0E49097B1FEACA4B005CAA50 /* CallViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CallViewModel.swift; sourceTree = "<group>"; };
0E6949781FA7E71C0029B60A /* BaseViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BaseViewController.swift; sourceTree = "<group>"; };
0E983E6D1FC77C3E0082103E /* ConversationModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConversationModel.swift; sourceTree = "<group>"; };
0E9D84481FA7DA6A00C561EB /* ChatTabBarItemViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatTabBarItemViewModel.swift; sourceTree = "<group>"; };
......@@ -944,6 +950,16 @@
path = DBHelpers;
sourceTree = "<group>";
};
0E4909711FEAC822005CAA50 /* Calls */ = {
isa = PBXGroup;
children = (
0E4909741FEAC943005CAA50 /* CallViewController.storyboard */,
0E4909791FEAC9E1005CAA50 /* CallViewController.swift */,
0E49097B1FEACA4B005CAA50 /* CallViewModel.swift */,
);
path = Calls;
sourceTree = "<group>";
};
0E5AFE0A1F8EBC040040D539 /* Cells */ = {
isa = PBXGroup;
children = (
......@@ -996,6 +1012,7 @@
isa = PBXGroup;
children = (
5CE66F721FBF765D00EE9291 /* InitialLoading */,
0E4909711FEAC822005CAA50 /* Calls */,
0E9D84471FA7D9EC00C561EB /* TabBar */,
0EDE34C51F868D2D00FFA15C /* Shared */,
1A0C4EBD1F1D48DD00550433 /* Walkthrough */,
......@@ -1463,6 +1480,7 @@
623660AA20092081002598C1 /* src in Resources */,
1A2D18B11F2915B600B2C785 /* SmartlistViewController.storyboard in Resources */,
0E403F831F7D79B000C80BC2 /* MessageCellGenerated.xib in Resources */,
0E4909751FEAC943005CAA50 /* CallViewController.storyboard in Resources */,
04399A031D1C2D9D00E99CD9 /* Images.xcassets in Resources */,
1A2041841F1EA0FC00C08435 /* CreateAccountViewController.storyboard in Resources */,
0E2D5F551F9145F200D574BF /* LinkNewDeviceCell.xib in Resources */,
......@@ -1600,6 +1618,7 @@
1A5DC0201F355DCF0075E8EF /* ContactsService.swift in Sources */,
1A2D18C71F29180700B2C785 /* DeviceModel.swift in Sources */,
1A20418F1F1EAC0E00C08435 /* Coordinator.swift in Sources */,
0E49097C1FEACA4B005CAA50 /* CallViewModel.swift in Sources */,
1A2D18A11F27A6D600B2C785 /* LinkDeviceViewController.swift in Sources */,
1A0C4EDC1F1D4B7E00550433 /* WelcomeViewController.swift in Sources */,
1A2D18D81F2918EE00B2C785 /* MeDetailViewController.swift in Sources */,
......@@ -1608,6 +1627,7 @@
56BBC99F1ED714CB00CDAF8B /* MessagesAdapter.mm in Sources */,
0E49096A1FEAB156005CAA50 /* CallsAdapter.mm in Sources */,
1A2D18A61F27F7A400B2C785 /* UIViewController+Rx.swift in Sources */,
0E49097A1FEAC9E1005CAA50 /* CallViewController.swift in Sources */,
1A5DC0241F3564360075E8EF /* ContactRequestModel.swift in Sources */,
0E4909701FEAC1C6005CAA50 /* CallModel.swift in Sources */,
1A5DC03F1F35678D0075E8EF /* ContactRequestsViewController.swift in Sources */,
......
......@@ -36,6 +36,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
private let conversationsService = ConversationsService(withMessageAdapter: MessagesAdapter())
private let contactsService = ContactsService(withContactsAdapter: ContactsAdapter())
private let presenceService = PresenceService(withPresenceAdapter: PresenceAdapter())
private let callService = CallsService(withCallsAdapter: CallsAdapter())
private let networkService = NetworkService()
private var conversationManager: ConversationsManager?
private var contactRequestManager: ContactRequestManager?
......@@ -47,8 +48,8 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
withConversationService: self.conversationsService,
withContactsService: self.contactsService,
withPresenceService: self.presenceService,
withNetworkService: self.networkService
)
withNetworkService: self.networkService,
withCallService: self.callService)
}()
private lazy var appCoordinator: AppCoordinator = {
return AppCoordinator(with: self.injectionBag)
......
This diff is collapsed.
/*
* Copyright (C) 2017 Savoir-faire Linux Inc.
*
* Author: Silbino Gonçalves Matado <silbino.gmatado@savoirfairelinux.com>
* Author: Kateryna Kostiuk <kateryna.kostiuk@savoirfairelinux.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
import UIKit
import Chameleon
import RxSwift
import Reusable
class CallViewController: UIViewController, StoryboardBased, ViewModelBased {
@IBOutlet weak var profileImageView: UIImageView!
@IBOutlet weak var nameLabel: UILabel!
@IBOutlet weak var durationLabel: UILabel!
@IBOutlet weak var infoBottomLabel: UILabel!
@IBOutlet weak var cancelButton: UIButton!
var viewModel: CallViewModel!
fileprivate let disposeBag = DisposeBag()
override func viewDidLoad() {
super.viewDidLoad()
self.setupUI()
self.setupBindings()
}
func setupUI() {
self.cancelButton.backgroundColor = UIColor.red
}
func setupBindings() {
//Cancel button action
self.cancelButton.rx.tap
.subscribe(onNext: { [weak self] in
self?.removeFromScreen()
self?.viewModel.cancelCall()
}).disposed(by: self.disposeBag)
//Data bindings
self.viewModel.dismisVC
.observeOn(MainScheduler.instance)
.subscribe(onNext: { [weak self] dismiss in
if dismiss {
self?.removeFromScreen()
}
}).disposed(by: self.disposeBag)
self.viewModel.contactName
.observeOn(MainScheduler.instance)
.bind(to: self.nameLabel.rx.text)
.disposed(by: self.disposeBag)
self.viewModel.callDuration
.observeOn(MainScheduler.instance)
.bind(to: self.durationLabel.rx.text)
.disposed(by: self.disposeBag)
self.viewModel.bottomInfo
.observeOn(MainScheduler.instance)
.bind(to: self.infoBottomLabel.rx.text)
.disposed(by: self.disposeBag)
}
func removeFromScreen() {
self.dismiss(animated: false)
}
}
/*
* Copyright (C) 2017 Savoir-faire Linux Inc.
*
* Author: Silbino Gonçalves Matado <silbino.gmatado@savoirfairelinux.com>
* Author: Kateryna Kostiuk <kateryna.kostiuk@savoirfairelinux.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
import RxSwift
import SwiftyBeaver
import Contacts
class CallViewModel: Stateable, ViewModel {
//stateable
private let stateSubject = PublishSubject<State>()
lazy var state: Observable<State> = {
return self.stateSubject.asObservable()
}()
fileprivate let callService: CallsService
fileprivate let contactsService: ContactsService
fileprivate let accountService: AccountsService
private let disposeBag = DisposeBag()
fileprivate let log = SwiftyBeaver.self
var call: CallModel?
// data for ViewCintroller binding
lazy var dismisVC: Observable<Bool> = {
return callService.currentCall.map({[weak self] call in
return call.state == .over || call.state == .failure && call.callId == self?.call?.callId
}).map({ hide in
return hide
})
}()
lazy var contactName: Observable<String> = {
return callService.currentCall.filter({ [weak self] call in
return call.state != .over && call.state != .inactive && call.callId == self?.call?.callId
}).map({ call in
if !call.displayName.isEmpty {
return call.displayName
} else if !call.registeredName.isEmpty {
return call.registeredName
} else {
return L10n.Calls.unknown
}
})
}()
lazy var callDuration: Observable<String> = {
let timer = Observable<Int>.interval(1, scheduler: MainScheduler.instance)
.takeUntil(self.callService.currentCall
.filter { [weak self] call in
call.state == .over &&
call.callId == self?.call?.callId
})
.map({ elapsed in
return CallViewModel.formattedDurationFrom(interval: elapsed)
})
return self.callService.currentCall.filter({ call in
return call.state == .current
}).flatMap({ _ in
return timer
})
}()
lazy var bottomInfo: Observable<String> = {
return callService.currentCall.map({ [weak self] call in
if call.state == .connecting || call.state == .ringing && call.callType == .outgoing && call.callId == self?.call?.callId {
return L10n.Calls.calling
} else if call.state == .over {
return L10n.Calls.callFinished
} else {
return ""
}
})
}()
required init(with injectionBag: InjectionBag) {
self.callService = injectionBag.callService
self.contactsService = injectionBag.contactsService
self.accountService = injectionBag.accountService
}
static func formattedDurationFrom(interval: Int) -> String {
let seconds = interval % 60
let minutes = (interval / 60) % 60
let hours = (interval / 3600)
return String(format: "%02d:%02d:%02d", hours, minutes, seconds)
}
func cancelCall() {
guard let call = self.call else {
return
}
self.callService.hangUp(callId: call.callId)
.subscribe(onCompleted: { [weak self] in
self?.log.info("Call canceled")
}, onError: { [weak self] error in
self?.log.error("Failed to cancel the call")
}).disposed(by: self.disposeBag)
}
func answerCall() {
guard let call = self.call else {
return
}
self.callService.accept(callId: call.callId)
.subscribe(onCompleted: { [weak self] in
self?.log.info("Call answered")
}, onError: { [weak self] error in
self?.log.error("Failed to answer the call")
}).disposed(by: self.disposeBag)
}
func placeCall(with uri: String, userName: String) {
guard let account = self.accountService.currentAccount else {
return
}
self.callService.placeCall(withAccount: account,
toRingId: uri,
userName: userName)
.subscribe(onSuccess: { [unowned self] callModel in
self.call = callModel
self.log.info("Call placed: \(callModel.callId)")
}, onError: { [unowned self] error in
self.log.error("Failed to place the call")
}).disposed(by: self.disposeBag)
}
}
......@@ -50,6 +50,7 @@ enum Asset {
static let addPerson = ImageAsset(name: "add_person")
static let backgroundRing = ImageAsset(name: "background_ring")
static let blockIcon = ImageAsset(name: "block_icon")
static let callButton = ImageAsset(name: "call_button")
static let contactRequestIcon = ImageAsset(name: "contact_request_icon")
static let conversationIcon = ImageAsset(name: "conversation_icon")
static let device = ImageAsset(name: "device")
......@@ -66,6 +67,7 @@ enum Asset {
addPerson,
backgroundRing,
blockIcon,
callButton,
contactRequestIcon,
conversationIcon,
device,
......
......@@ -50,6 +50,11 @@ extension UIViewController {
// swiftlint:disable explicit_type_interface identifier_name line_length type_body_length type_name
enum StoryboardScene {
enum CallViewController: StoryboardType {
static let storyboardName = "CallViewController"
static let initialScene = InitialSceneType<Ring.CallViewController>(storyboard: CallViewController.self)
}
enum ContactRequestsViewController: StoryboardType {
static let storyboardName = "ContactRequestsViewController"
......
......@@ -51,6 +51,12 @@ enum L10n {
static let dbFailedMessage = L10n.tr("Localizable", "alerts.dbFailedMessage")
/// An error happned when launching Ring
static let dbFailedTitle = L10n.tr("Localizable", "alerts.dbFailedTitle")
/// Incoming call from
static let incomingCallAllertTitle = L10n.tr("Localizable", "alerts.incomingCallAllertTitle")
/// Accept
static let incomingCallButtonAccept = L10n.tr("Localizable", "alerts.incomingCallButtonAccept")
/// Ignore
static let incomingCallButtonIgnore = L10n.tr("Localizable", "alerts.incomingCallButtonIgnore")
/// Cancel
static let profileCancelPhoto = L10n.tr("Localizable", "alerts.profileCancelPhoto")
/// Take photo
......@@ -59,6 +65,19 @@ enum L10n {
static let profileUploadPhoto = L10n.tr("Localizable", "alerts.profileUploadPhoto")
}
enum Calls {
/// Call finished
static let callFinished = L10n.tr("Localizable", "calls.callFinished")
/// Calling...
static let calling = L10n.tr("Localizable", "calls.calling")
/// Call
static let callItemTitle = L10n.tr("Localizable", "calls.callItemTitle")
/// wants to talk to you
static let incomingCallInfo = L10n.tr("Localizable", "calls.incomingCallInfo")
/// Unknown
static let unknown = L10n.tr("Localizable", "calls.unknown")
}
enum Createaccount {
/// Choose strong password you will remember to protect your Ring account.
static let chooseStrongPassword = L10n.tr("Localizable", "createAccount.chooseStrongPassword")
......
......@@ -30,6 +30,7 @@ class InjectionBag {
let contactsService: ContactsService
let presenceService: PresenceService
let networkService: NetworkService
let callService: CallsService
init (withDaemonService daemonService: DaemonService,
withAccountService accountService: AccountsService,
......@@ -37,7 +38,8 @@ class InjectionBag {
withConversationService conversationService: ConversationsService,
withContactsService contactsService: ContactsService,
withPresenceService presenceService: PresenceService,
withNetworkService networkService: NetworkService) {
withNetworkService networkService: NetworkService,
withCallService callService: CallsService) {
self.daemonService = daemonService
self.accountService = accountService
self.nameService = nameService
......@@ -45,6 +47,7 @@ class InjectionBag {
self.contactsService = contactsService
self.presenceService = presenceService
self.networkService = networkService
self.callService = callService
}
}
......@@ -46,6 +46,8 @@ class ContactRequestsCoordinator: Coordinator, StateableResponsive {
switch state {
case .conversationDetail (let conversationViewModel):
self.showConversation(withConversationViewModel: conversationViewModel)
default:
break
}
}).disposed(by: self.disposeBag)
}
......
......@@ -107,14 +107,19 @@ class ConversationViewController: UIViewController, UITextFieldDelegate, Storybo
self.tableView.contentInset.bottom = messageAccessoryView.frame.size.height
self.tableView.scrollIndicatorInsets.bottom = messageAccessoryView.frame.size.height
//invite button
//set navigation buttons - call and send contact request
let inviteItem = UIBarButtonItem()
inviteItem.image = UIImage(named: "add_person")
inviteItem.rx.tap.throttle(0.5, scheduler: MainScheduler.instance)
.subscribe(onNext: { [unowned self] in
self.inviteItemTapped()
}).disposed(by: self.disposeBag)
let callItem = UIBarButtonItem()
callItem.image = UIImage(asset: Asset.callButton)
callItem.rx.tap.throttle(0.5, scheduler: MainScheduler.instance)
.subscribe(onNext: { [unowned self] in
self.placeCall()
}).disposed(by: self.disposeBag)
self.viewModel.inviteButtonIsAvailable.asObservable().bind(to: inviteItem.rx.isEnabled).disposed(by: disposeBag)
//block contact button
......@@ -125,13 +130,14 @@ class ConversationViewController: UIViewController, UITextFieldDelegate, Storybo
self.blockItemTapped()
}).disposed(by: self.disposeBag)
self.navigationItem.rightBarButtonItems = [blockItem, inviteItem]
self.navigationItem.rightBarButtonItems = [blockItem, inviteItem, callItem]
Observable<[UIBarButtonItem]>
.combineLatest(self.viewModel.inviteButtonIsAvailable.asObservable(),
self.viewModel.blockButtonIsAvailable.asObservable(),
resultSelector: { inviteButton, blockButton in
var buttons = [UIBarButtonItem]()
buttons.append(callItem)
if blockButton {
buttons.append(blockItem)
}
......@@ -161,6 +167,10 @@ class ConversationViewController: UIViewController, UITextFieldDelegate, Storybo
self.present(alert, animated: true, completion: nil)
}
func placeCall() {
self.viewModel.startCall()
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
......
......@@ -23,7 +23,7 @@ import UIKit
import RxSwift
import SwiftyBeaver
class ConversationViewModel: ViewModel {
class ConversationViewModel: Stateable, ViewModel {
/**
logguer
......@@ -38,6 +38,11 @@ class ConversationViewModel: ViewModel {
private let presenceService: PresenceService
private let injectionBag: InjectionBag
private let stateSubject = PublishSubject<State>()
lazy var state: Observable<State> = {
return self.stateSubject.asObservable()
}()
required init(with injectionBag: InjectionBag) {
self.injectionBag = injectionBag
self.accountService = injectionBag.accountService
......@@ -335,4 +340,11 @@ class ConversationViewModel: ViewModel {
return
}
}
func startCall() {
if self.conversation.value.messages.isEmpty {
self.sendContactRequest()
}
self.stateSubject.onNext(ConversationsState.startCall(contactRingId: self.conversation.value.recipientRingId, userName: self.userName.value))
}
}
......@@ -26,6 +26,7 @@ import RxSwift
/// - conversationDetail: user want to see a conversation detail
enum ConversationsState: State {
case conversationDetail(conversationViewModel: ConversationViewModel)
case startCall(contactRingId: String, userName: String)
}
/// This Coordinator drives the conversation navigation (Smartlist / Conversation detail)
......@@ -42,19 +43,27 @@ class ConversationsCoordinator: Coordinator, StateableResponsive {
let disposeBag = DisposeBag()
let stateSubject = PublishSubject<State>()
let conversationsService: ConversationsService
let accountService: AccountsService
let callService: CallsService
required init (with injectionBag: InjectionBag) {
self.injectionBag = injectionBag
self.conversationsService = injectionBag.conversationsService
self.accountService = injectionBag.accountService
self.callService = injectionBag.callService
self.callService.newCall.asObservable()
.map({ call in
return call
}).subscribe(onNext: { (call) in
self.showCallAlert(call: call)
}).disposed(by: self.disposeBag)
self.stateSubject.subscribe(onNext: { [unowned self] (state) in
guard let state = state as? ConversationsState else { return }
switch state {
case .conversationDetail (let conversationViewModel):
self.showConversation(withConversationViewModel: conversationViewModel)
case .startCall(let contactRingId, let name):
self.startOutgoingCall(contactRingId: contactRingId, userName: name)
}
}).disposed(by: self.disposeBag)
self.navigationViewController.viewModel = ChatTabBarItemViewModel(with: self.injectionBag)
......@@ -69,6 +78,39 @@ class ConversationsCoordinator: Coordinator, StateableResponsive {
private func showConversation (withConversationViewModel conversationViewModel: ConversationViewModel) {
let conversationViewController = ConversationViewController.instantiate(with: self.injectionBag)
conversationViewController.viewModel = conversationViewModel
self.present(viewController: conversationViewController, withStyle: .show, withAnimation: true)
self.present(viewController: conversationViewController, withStyle: .show, withAnimation: true, withStateable: conversationViewController.viewModel)
}
private func startOutgoingCall(contactRingId: String, userName: String) {
let callViewController = CallViewController.instantiate(with: self.injectionBag)
callViewController.viewModel.placeCall(with: contactRingId, userName: userName)
self.present(viewController: callViewController, withStyle: .present, withAnimation: false)
}
private func answerIncomingCall(call: CallModel) {
let callViewController = CallViewController.instantiate(with: self.injectionBag)
callViewController.viewModel.call = call
callViewController.viewModel.answerCall()
self.present(viewController: callViewController, withStyle: .present, withAnimation: false)
}
private func showCallAlert(call: CallModel) {
let alert = UIAlertController(title: L10n.Alerts.incomingCallAllertTitle + "\(call.displayName)", message: nil, preferredStyle: UIAlertControllerStyle.actionSheet)
alert.addAction(UIAlertAction(title: L10n.Alerts.incomingCallButtonAccept, style: UIAlertActionStyle.default, handler: { (_) in
self.answerIncomingCall(call: call)
alert.dismiss(animated: true, completion: nil)}))
alert.addAction(UIAlertAction(title: L10n.Alerts.incomingCallButtonIgnore, style: UIAlertActionStyle.default, handler: { (_) in
self.injectionBag.callService.refuse(callId: call.callId)
.subscribe({_ in
print("Call ignored")
}).disposed(by: self.disposeBag)
alert.dismiss(animated: true, completion: nil)
}))
if let controller = self.rootViewController.presentedViewController {
controller.present(alert, animated: false, completion: nil)
} else {
self.present(viewController: alert, withStyle: .present, withAnimation: true)
}
}
}
......@@ -117,11 +117,11 @@ class CallModel {
self.dateReceived = Date()
if let displayName = dictionary[CallDetailKey.displayNameKey.rawValue] {
if let displayName = dictionary[CallDetailKey.displayNameKey.rawValue], !displayName.isEmpty {
self.displayName = displayName
}
if let registeredName = dictionary[CallDetailKey.registeredNameKey.rawValue] {
if let registeredName = dictionary[CallDetailKey.registeredNameKey.rawValue], !registeredName.isEmpty {
self.registeredName = registeredName
}
......
{
"images" : [
{
"idiom" : "universal",
"filename" : "ic_call.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "ic_call_2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "ic_call_3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
\ No newline at end of file
......@@ -92,6 +92,16 @@
"actions.blockAction" = "Block";
"actions.deleteAction" = "Delete";
"actions.cancelAction" = "Cancel";
"alerts.incomingCallAllertTitle" = "Incoming call from ";
"alerts.incomingCallButtonAccept" = "Accept";
"alerts.incomingCallButtonIgnore" = "Ignore";
//Calls
"calls.callItemTitle" = "Call";
"calls.unknown" = "Unknown";
"calls.incomingCallInfo" = "wants to talk to you";
"calls.calling" = "Calling...";
"calls.callFinished" = "Call finished";
//Account Page
"accountPage.devicesListHeader" = "Devices";
......
......@@ -42,7 +42,8 @@ class CallsService: CallsAdapterDelegate {
fileprivate let ringVCardMIMEType = "x-ring/ring.profile.vcard"
let currentCall = ReplaySubject<CallModel>.create(bufferSize: 1)
let newcall = Variable<CallModel>(CallModel(withCallId: "", callDetails: [:]))
let newCall = Variable<CallModel>(CallModel(withCallId: "", callDetails: [:]))
init(withCallsAdapter callsAdapter: CallsAdapter) {
self.callsAdapter = callsAdapter
......@@ -109,12 +110,12 @@ class CallsService: CallsAdapterDelegate {
})
}
func placeCall(withAccount account: AccountModel, toRingId ringId: String) -> Single<CallModel> {
func placeCall(withAccount account: AccountModel, toRingId ringId: String, userName: String) -> Single<CallModel> {
//Create and emit the call
let call = CallModel(withCallId: ringId, callDetails: [String: String]())
call.state = .connecting
call.registeredName = userName
return Single<CallModel>.create(subscribe: { single in
if let callId = self.callsAdapter.placeCall(withAccountId: account.id,
toRingId: "ring:\(ringId)"),
......@@ -141,6 +142,7 @@ class CallsService: CallsAdapterDelegate {
var call = self.calls[callId]
if call == nil {
call = CallModel(withCallId: callId, callDetails: callDictionary)
self.calls[callId] = call
} else {
call?.update(withDictionary: callDictionary)
}
......@@ -174,7 +176,7 @@ class CallsService: CallsAdapterDelegate {
call?.update(withDictionary: callDictionary)
}
//Emit the call to the observers
self.newcall.value = call!
self.newCall.value = call!
}