Commit 918d99fe authored by Kateryna Kostiuk's avatar Kateryna Kostiuk

call: use CallKit

- use Call Kit for incoming call screen
- add Jami calls to recent call list
- start Jami call from recent call list

Change-Id: I6ee9bb75ddf7c23c1ff15fca904055bceba1b7ba
parent d2ec58d4
...@@ -90,6 +90,7 @@ ...@@ -90,6 +90,7 @@
0E0FF1B51FC3947B003898C2 /* DBManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E0FF1B41FC3947B003898C2 /* DBManager.swift */; }; 0E0FF1B51FC3947B003898C2 /* DBManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E0FF1B41FC3947B003898C2 /* DBManager.swift */; };
0E0FF1B71FC398B3003898C2 /* ConversationDataHepler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E0FF1B61FC398B3003898C2 /* ConversationDataHepler.swift */; }; 0E0FF1B71FC398B3003898C2 /* ConversationDataHepler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E0FF1B61FC398B3003898C2 /* ConversationDataHepler.swift */; };
0E0FF1B91FC398C5003898C2 /* InteractionDataHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E0FF1B81FC398C5003898C2 /* InteractionDataHelper.swift */; }; 0E0FF1B91FC398C5003898C2 /* InteractionDataHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E0FF1B81FC398C5003898C2 /* InteractionDataHelper.swift */; };
0E13A91C22B844B100A12A54 /* NSUserActivity+Call.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E13A91B22B844B100A12A54 /* NSUserActivity+Call.swift */; };
0E20E4C72031FF560087C868 /* BlockContactsCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E20E4C52031FF560087C868 /* BlockContactsCell.swift */; }; 0E20E4C72031FF560087C868 /* BlockContactsCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E20E4C52031FF560087C868 /* BlockContactsCell.swift */; };
0E20E4C82031FF560087C868 /* BlockContactsCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 0E20E4C62031FF560087C868 /* BlockContactsCell.xib */; }; 0E20E4C82031FF560087C868 /* BlockContactsCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 0E20E4C62031FF560087C868 /* BlockContactsCell.xib */; };
0E2D5F531F9145C800D574BF /* LinkNewDeviceCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E2D5F521F9145C800D574BF /* LinkNewDeviceCell.swift */; }; 0E2D5F531F9145C800D574BF /* LinkNewDeviceCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E2D5F521F9145C800D574BF /* LinkNewDeviceCell.swift */; };
...@@ -152,6 +153,8 @@ ...@@ -152,6 +153,8 @@
0EB1A5D11F8EBE23009923E2 /* DeviceCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EB1A5D01F8EBE23009923E2 /* DeviceCell.swift */; }; 0EB1A5D11F8EBE23009923E2 /* DeviceCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EB1A5D01F8EBE23009923E2 /* DeviceCell.swift */; };
0EBB72A92034F44200D88F46 /* ProfilesService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EBB72A82034F44200D88F46 /* ProfilesService.swift */; }; 0EBB72A92034F44200D88F46 /* ProfilesService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EBB72A82034F44200D88F46 /* ProfilesService.swift */; };
0EBCAA4E202E60F000E2A545 /* default.wav in Resources */ = {isa = PBXBuildFile; fileRef = 0EBCAA4D202E60F000E2A545 /* default.wav */; }; 0EBCAA4E202E60F000E2A545 /* default.wav in Resources */ = {isa = PBXBuildFile; fileRef = 0EBCAA4D202E60F000E2A545 /* default.wav */; };
0ECB4E2822B2D4840097CD7B /* CallsProviderDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0ECB4E2722B2D4840097CD7B /* CallsProviderDelegate.swift */; };
0ECB4E2A22B2D4BB0097CD7B /* CallKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0ECB4E2922B2D4BB0097CD7B /* CallKit.framework */; };
0ECEE9A3220D1935000E1CF4 /* VideoToolbox.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0ECEE9A2220D1935000E1CF4 /* VideoToolbox.framework */; }; 0ECEE9A3220D1935000E1CF4 /* VideoToolbox.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0ECEE9A2220D1935000E1CF4 /* VideoToolbox.framework */; };
0ED2B6FA1F96A075001572F0 /* LinkNewDeviceViewController.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 0ED2B6F91F96A075001572F0 /* LinkNewDeviceViewController.storyboard */; }; 0ED2B6FA1F96A075001572F0 /* LinkNewDeviceViewController.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 0ED2B6F91F96A075001572F0 /* LinkNewDeviceViewController.storyboard */; };
0ED2B6FC1F96A158001572F0 /* LinkNewDeviceViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0ED2B6FB1F96A158001572F0 /* LinkNewDeviceViewController.swift */; }; 0ED2B6FC1F96A158001572F0 /* LinkNewDeviceViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0ED2B6FB1F96A158001572F0 /* LinkNewDeviceViewController.swift */; };
...@@ -411,6 +414,7 @@ ...@@ -411,6 +414,7 @@
0E0FF1B41FC3947B003898C2 /* DBManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DBManager.swift; sourceTree = "<group>"; }; 0E0FF1B41FC3947B003898C2 /* DBManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DBManager.swift; sourceTree = "<group>"; };
0E0FF1B61FC398B3003898C2 /* ConversationDataHepler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConversationDataHepler.swift; sourceTree = "<group>"; }; 0E0FF1B61FC398B3003898C2 /* ConversationDataHepler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConversationDataHepler.swift; sourceTree = "<group>"; };
0E0FF1B81FC398C5003898C2 /* InteractionDataHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InteractionDataHelper.swift; sourceTree = "<group>"; }; 0E0FF1B81FC398C5003898C2 /* InteractionDataHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InteractionDataHelper.swift; sourceTree = "<group>"; };
0E13A91B22B844B100A12A54 /* NSUserActivity+Call.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSUserActivity+Call.swift"; sourceTree = "<group>"; };
0E20E4C52031FF560087C868 /* BlockContactsCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BlockContactsCell.swift; sourceTree = "<group>"; }; 0E20E4C52031FF560087C868 /* BlockContactsCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BlockContactsCell.swift; sourceTree = "<group>"; };
0E20E4C62031FF560087C868 /* BlockContactsCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = BlockContactsCell.xib; sourceTree = "<group>"; }; 0E20E4C62031FF560087C868 /* BlockContactsCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = BlockContactsCell.xib; sourceTree = "<group>"; };
0E2D5F521F9145C800D574BF /* LinkNewDeviceCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LinkNewDeviceCell.swift; sourceTree = "<group>"; }; 0E2D5F521F9145C800D574BF /* LinkNewDeviceCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LinkNewDeviceCell.swift; sourceTree = "<group>"; };
...@@ -477,6 +481,8 @@ ...@@ -477,6 +481,8 @@
0EB1A5D01F8EBE23009923E2 /* DeviceCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DeviceCell.swift; sourceTree = "<group>"; }; 0EB1A5D01F8EBE23009923E2 /* DeviceCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DeviceCell.swift; sourceTree = "<group>"; };
0EBB72A82034F44200D88F46 /* ProfilesService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfilesService.swift; sourceTree = "<group>"; }; 0EBB72A82034F44200D88F46 /* ProfilesService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfilesService.swift; sourceTree = "<group>"; };
0EBCAA4D202E60F000E2A545 /* default.wav */ = {isa = PBXFileReference; lastKnownFileType = audio.wav; path = default.wav; sourceTree = "<group>"; }; 0EBCAA4D202E60F000E2A545 /* default.wav */ = {isa = PBXFileReference; lastKnownFileType = audio.wav; path = default.wav; sourceTree = "<group>"; };
0ECB4E2722B2D4840097CD7B /* CallsProviderDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CallsProviderDelegate.swift; sourceTree = "<group>"; };
0ECB4E2922B2D4BB0097CD7B /* CallKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CallKit.framework; path = System/Library/Frameworks/CallKit.framework; sourceTree = SDKROOT; };
0ECEE9A2220D1935000E1CF4 /* VideoToolbox.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = VideoToolbox.framework; path = System/Library/Frameworks/VideoToolbox.framework; sourceTree = SDKROOT; }; 0ECEE9A2220D1935000E1CF4 /* VideoToolbox.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = VideoToolbox.framework; path = System/Library/Frameworks/VideoToolbox.framework; sourceTree = SDKROOT; };
0ED2B6F91F96A075001572F0 /* LinkNewDeviceViewController.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = LinkNewDeviceViewController.storyboard; sourceTree = "<group>"; }; 0ED2B6F91F96A075001572F0 /* LinkNewDeviceViewController.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = LinkNewDeviceViewController.storyboard; sourceTree = "<group>"; };
0ED2B6FB1F96A158001572F0 /* LinkNewDeviceViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LinkNewDeviceViewController.swift; sourceTree = "<group>"; }; 0ED2B6FB1F96A158001572F0 /* LinkNewDeviceViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LinkNewDeviceViewController.swift; sourceTree = "<group>"; };
...@@ -691,6 +697,7 @@ ...@@ -691,6 +697,7 @@
isa = PBXFrameworksBuildPhase; isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
0ECB4E2A22B2D4BB0097CD7B /* CallKit.framework in Frameworks */,
0ECEE9A3220D1935000E1CF4 /* VideoToolbox.framework in Frameworks */, 0ECEE9A3220D1935000E1CF4 /* VideoToolbox.framework in Frameworks */,
628F4DCF206C0AEE0009C44C /* libcrypto.a in Frameworks */, 628F4DCF206C0AEE0009C44C /* libcrypto.a in Frameworks */,
628F4DCD206BF4740009C44C /* libtls.a in Frameworks */, 628F4DCD206BF4740009C44C /* libtls.a in Frameworks */,
...@@ -797,6 +804,7 @@ ...@@ -797,6 +804,7 @@
02AED8171DD4C4B000F740BA /* Frameworks */ = { 02AED8171DD4C4B000F740BA /* Frameworks */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
0ECB4E2922B2D4BB0097CD7B /* CallKit.framework */,
0E639459224AB32200C0890A /* Contacts.framework */, 0E639459224AB32200C0890A /* Contacts.framework */,
0EB12451224AB1030025F8CA /* ContactsUI.framework */, 0EB12451224AB1030025F8CA /* ContactsUI.framework */,
0ECEE9A2220D1935000E1CF4 /* VideoToolbox.framework */, 0ECEE9A2220D1935000E1CF4 /* VideoToolbox.framework */,
...@@ -854,6 +862,7 @@ ...@@ -854,6 +862,7 @@
0EBB72A82034F44200D88F46 /* ProfilesService.swift */, 0EBB72A82034F44200D88F46 /* ProfilesService.swift */,
62B60AF320489E7C001BEACF /* DataTransferService.swift */, 62B60AF320489E7C001BEACF /* DataTransferService.swift */,
62B60AFA2048A437001BEACF /* DataTransferAdapterDelegate.swift */, 62B60AFA2048A437001BEACF /* DataTransferAdapterDelegate.swift */,
0ECB4E2722B2D4840097CD7B /* CallsProviderDelegate.swift */,
); );
path = Services; path = Services;
sourceTree = "<group>"; sourceTree = "<group>";
...@@ -930,6 +939,7 @@ ...@@ -930,6 +939,7 @@
621231F81F880EDF009B86F0 /* UILabel+Ring.swift */, 621231F81F880EDF009B86F0 /* UILabel+Ring.swift */,
0EDCC85F1F98150500B121D7 /* UIView+Rx.swift */, 0EDCC85F1F98150500B121D7 /* UIView+Rx.swift */,
62006E03203F4DD6003C3197 /* UITextField+Helpers.swift */, 62006E03203F4DD6003C3197 /* UITextField+Helpers.swift */,
0E13A91B22B844B100A12A54 /* NSUserActivity+Call.swift */,
); );
path = Extensions; path = Extensions;
sourceTree = "<group>"; sourceTree = "<group>";
...@@ -1925,6 +1935,7 @@ ...@@ -1925,6 +1935,7 @@
0E6F544F223C0ED600ECC3CE /* AccountPickerAdapter.swift in Sources */, 0E6F544F223C0ED600ECC3CE /* AccountPickerAdapter.swift in Sources */,
1AABA7461F0FE9C000739605 /* UIColor+Ring.swift in Sources */, 1AABA7461F0FE9C000739605 /* UIColor+Ring.swift in Sources */,
1A5DC0201F355DCF0075E8EF /* ContactsService.swift in Sources */, 1A5DC0201F355DCF0075E8EF /* ContactsService.swift in Sources */,
0ECB4E2822B2D4840097CD7B /* CallsProviderDelegate.swift in Sources */,
1A2D18C71F29180700B2C785 /* DeviceModel.swift in Sources */, 1A2D18C71F29180700B2C785 /* DeviceModel.swift in Sources */,
1A20418F1F1EAC0E00C08435 /* Coordinator.swift in Sources */, 1A20418F1F1EAC0E00C08435 /* Coordinator.swift in Sources */,
0E49097C1FEACA4B005CAA50 /* CallViewModel.swift in Sources */, 0E49097C1FEACA4B005CAA50 /* CallViewModel.swift in Sources */,
...@@ -1958,6 +1969,7 @@ ...@@ -1958,6 +1969,7 @@
0E99F1A022417A0400CF8BD6 /* JamiURI.swift in Sources */, 0E99F1A022417A0400CF8BD6 /* JamiURI.swift in Sources */,
1A5DC02E1F3565640075E8EF /* ConversationViewModel.swift in Sources */, 1A5DC02E1F3565640075E8EF /* ConversationViewModel.swift in Sources */,
0EBB72A92034F44200D88F46 /* ProfilesService.swift in Sources */, 0EBB72A92034F44200D88F46 /* ProfilesService.swift in Sources */,
0E13A91C22B844B100A12A54 /* NSUserActivity+Call.swift in Sources */,
1A2D189C1F264AD900B2C785 /* UIViewController+Ring.swift in Sources */, 1A2D189C1F264AD900B2C785 /* UIViewController+Ring.swift in Sources */,
02C9B63F1E1D4E8C00F82F0C /* ServiceEvent.swift in Sources */, 02C9B63F1E1D4E8C00F82F0C /* ServiceEvent.swift in Sources */,
62B60AF420489E7C001BEACF /* DataTransferService.swift in Sources */, 62B60AF420489E7C001BEACF /* DataTransferService.swift in Sources */,
......
...@@ -28,7 +28,7 @@ import PushKit ...@@ -28,7 +28,7 @@ import PushKit
import ContactsUI import ContactsUI
// swiftlint:disable identifier_name // swiftlint:disable identifier_name
// swiftlint:disable type_body_length
@UIApplicationMain @UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterDelegate { class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterDelegate {
var window: UIWindow? var window: UIWindow?
...@@ -42,6 +42,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD ...@@ -42,6 +42,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
private let videoService = VideoService(withVideoAdapter: VideoAdapter()) private let videoService = VideoService(withVideoAdapter: VideoAdapter())
private let audioService = AudioService(withAudioAdapter: AudioAdapter()) private let audioService = AudioService(withAudioAdapter: AudioAdapter())
private let networkService = NetworkService() private let networkService = NetworkService()
private let callsProvider: CallsProviderDelegate = CallsProviderDelegate()
private var conversationManager: ConversationsManager? private var conversationManager: ConversationsManager?
private var interactionsManager: GeneratedInteractionsManager? private var interactionsManager: GeneratedInteractionsManager?
private lazy var callService: CallsService = { private lazy var callService: CallsService = {
...@@ -78,7 +79,8 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD ...@@ -78,7 +79,8 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
withVideoService: self.videoService, withVideoService: self.videoService,
withAudioService: self.audioService, withAudioService: self.audioService,
withDataTransferService: self.dataTransferService, withDataTransferService: self.dataTransferService,
withProfileService: self.profileService) withProfileService: self.profileService,
withCallsProvider: self.callsProvider)
}() }()
private lazy var appCoordinator: AppCoordinator = { private lazy var appCoordinator: AppCoordinator = {
return AppCoordinator(with: self.injectionBag) return AppCoordinator(with: self.injectionBag)
...@@ -171,6 +173,9 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD ...@@ -171,6 +173,9 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
} else { } else {
self.unregisterVoipNotifications() self.unregisterVoipNotifications()
} }
if #available(iOS 10.0, *) {
return
}
// reimit new call signal to show incoming call alert // reimit new call signal to show incoming call alert
self.callService.checkForIncomingCall() self.callService.checkForIncomingCall()
}, onError: { _ in }, onError: { _ in
...@@ -219,8 +224,11 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD ...@@ -219,8 +224,11 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
} }
func applicationDidBecomeActive(_ application: UIApplication) { func applicationDidBecomeActive(_ application: UIApplication) {
self.callService.checkForIncomingCall()
self.clearBadgeNumber() self.clearBadgeNumber()
if #available(iOS 10.0, *) {
return
}
self.callService.checkForIncomingCall()
} }
func prepareVideoAcceleration() { func prepareVideoAcceleration() {
...@@ -234,7 +242,6 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD ...@@ -234,7 +242,6 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
// MARK: - Ring Daemon // MARK: - Ring Daemon
fileprivate func startDaemon() { fileprivate func startDaemon() {
do { do {
try self.daemonService.startDaemon() try self.daemonService.startDaemon()
} catch StartDaemonError.initializationFailure { } catch StartDaemonError.initializationFailure {
...@@ -412,6 +419,76 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD ...@@ -412,6 +419,76 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
} }
return rootViewController return rootViewController
} }
func findContactAndStartCall(hash: String, isVideo: Bool) {
//if saved jami hash
if hash.isSHA1() {
let contactUri = JamiURI(schema: URIType.ring, infoHach: hash)
self.findAccountAndStartCall(uri: contactUri, isVideo: isVideo, type: AccountType.ring)
return
}
//if saved jami registered name
self.nameService.usernameLookupStatus
.observeOn(MainScheduler.instance)
.filter({ usernameLookupStatus in
usernameLookupStatus.name == hash
})
.take(1)
.subscribe(onNext: { usernameLookupStatus in
if usernameLookupStatus.state == .found {
guard let address = usernameLookupStatus.address else {return}
let contactUri = JamiURI(schema: URIType.ring, infoHach: address)
self.findAccountAndStartCall(uri: contactUri, isVideo: isVideo, type: AccountType.ring)
} else {
//if saved sip contact
let contactUri = JamiURI(schema: URIType.sip, infoHach: hash)
self.findAccountAndStartCall(uri: contactUri, isVideo: isVideo, type: AccountType.sip)
}
}).disposed(by: self.disposeBag)
self.nameService.lookupName(withAccount: "", nameserver: "", name: hash)
}
func findAccountAndStartCall(uri: JamiURI, isVideo: Bool, type: AccountType) {
guard let currentAccount = self.accountService
.currentAccount else { return }
var hash = uri.hash ?? ""
var uriString = uri.uriString ?? ""
for account in self.accountService.accounts where account.type == type {
if type == AccountType.sip {
let conatactUri = JamiURI(schema: URIType.sip,
infoHach: hash,
account: account)
hash = conatactUri.hash ?? ""
uriString = conatactUri.uriString ?? ""
}
if hash.isEmpty || uriString.isEmpty {return}
self.contactsService
.getProfileForUri(uri: uriString,
accountId: account.id)
.subscribe(onNext: { (profile) in
if currentAccount != account {
self.accountService.currentAccount = account
}
self.appCoordinator
.startCall(participant: hash,
name: profile.alias ?? "",
isVideo: isVideo)
}).disposed(by: self.disposeBag)
}
}
func application(_ application: UIApplication,
continue userActivity: NSUserActivity,
restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void) -> Bool {
if #available(iOS 10.0, *) {
guard let handle = userActivity.startCallHandle else {
return false
}
self.findContactAndStartCall(hash: handle.hash, isVideo: handle.isVideo)
return true
}
return false
}
} }
extension AppDelegate: PKPushRegistryDelegate { extension AppDelegate: PKPushRegistryDelegate {
...@@ -422,10 +499,13 @@ extension AppDelegate: PKPushRegistryDelegate { ...@@ -422,10 +499,13 @@ extension AppDelegate: PKPushRegistryDelegate {
} }
func pushRegistry(_ registry: PKPushRegistry, didReceiveIncomingPushWith payload: PKPushPayload, for type: PKPushType) { func pushRegistry(_ registry: PKPushRegistry, didReceiveIncomingPushWith payload: PKPushPayload, for type: PKPushType) {
self.accountService.pushNotificationReceived(data: payload.dictionaryPayload)
if #available(iOS 10.0, *) {
return
}
if UIApplication.shared.applicationState != .active { if UIApplication.shared.applicationState != .active {
self.audioService.startAVAudioSession() self.audioService.startAVAudioSession()
} }
self.accountService.pushNotificationReceived(data: payload.dictionaryPayload)
} }
func pushRegistry(_ registry: PKPushRegistry, didUpdate pushCredentials: PKPushCredentials, for type: PKPushType) { func pushRegistry(_ registry: PKPushRegistry, didUpdate pushCredentials: PKPushCredentials, for type: PKPushType) {
......
...@@ -39,6 +39,7 @@ ...@@ -39,6 +39,7 @@
#import <UserNotifications/UserNotifications.h> #import <UserNotifications/UserNotifications.h>
#import "CallsAdapter.h" #import "CallsAdapter.h"
#import <PushKit/PushKit.h> #import <PushKit/PushKit.h>
#import <CallKit/CallKit.h>
#import <UserNotifications/UserNotifications.h> #import <UserNotifications/UserNotifications.h>
#import <GSKStretchyHeaderView/GSKStretchyHeaderView.h> #import <GSKStretchyHeaderView/GSKStretchyHeaderView.h>
#import "DataTransferAdapter.h" #import "DataTransferAdapter.h"
...@@ -101,7 +101,7 @@ class ButtonsContainerView: UIView, NibLoadable { ...@@ -101,7 +101,7 @@ class ButtonsContainerView: UIView, NibLoadable {
if self.viewModel?.isIncoming ?? false { if self.viewModel?.isIncoming ?? false {
acceptCallButton.isHidden = false acceptCallButton.isHidden = false
cancelButtonBottomConstraint.constant = 60 cancelButtonBottomConstraint.constant = 60
cancelButtonCenterConstraint.constant = 55 cancelButtonCenterConstraint.constant = -80
return return
} }
cancelButtonCenterConstraint.constant = 0 cancelButtonCenterConstraint.constant = 0
......
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="14460.31" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES"> <document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="14490.70" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
<device id="retina4_7" orientation="portrait"> <device id="retina4_7" orientation="portrait">
<adaptation id="fullscreen"/> <adaptation id="fullscreen"/>
</device> </device>
<dependencies> <dependencies>
<deployment identifier="iOS"/> <deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14460.20"/> <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14490.49"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/> <capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/> <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies> </dependencies>
...@@ -215,7 +215,7 @@ ...@@ -215,7 +215,7 @@
</userDefinedRuntimeAttributes> </userDefinedRuntimeAttributes>
</button> </button>
<button hidden="YES" opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="rfz-9h-HoH" userLabel="Accept"> <button hidden="YES" opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="rfz-9h-HoH" userLabel="Accept">
<rect key="frame" x="42.5" y="10" width="70" height="70"/> <rect key="frame" x="312.5" y="10" width="70" height="70"/>
<color key="backgroundColor" red="0.45098039215686275" green="0.98039215686274506" blue="0.47450980392156861" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> <color key="backgroundColor" red="0.45098039215686275" green="0.98039215686274506" blue="0.47450980392156861" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints> <constraints>
<constraint firstAttribute="width" constant="70" id="cRi-F4-pPw"/> <constraint firstAttribute="width" constant="70" id="cRi-F4-pPw"/>
...@@ -236,9 +236,9 @@ ...@@ -236,9 +236,9 @@
</subviews> </subviews>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/> <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
<constraints> <constraints>
<constraint firstItem="rfz-9h-HoH" firstAttribute="leading" secondItem="ZxT-mA-1xU" secondAttribute="trailing" constant="90" id="48d-Rh-4pI"/>
<constraint firstAttribute="bottom" secondItem="w5l-pw-1ET" secondAttribute="bottom" id="6qu-Nn-b1Y"/> <constraint firstAttribute="bottom" secondItem="w5l-pw-1ET" secondAttribute="bottom" id="6qu-Nn-b1Y"/>
<constraint firstAttribute="height" constant="200" id="Gjk-7U-rEe"/> <constraint firstAttribute="height" constant="200" id="Gjk-7U-rEe"/>
<constraint firstItem="ZxT-mA-1xU" firstAttribute="leading" secondItem="rfz-9h-HoH" secondAttribute="trailing" constant="40" id="Hwg-PM-KNI"/>
<constraint firstAttribute="bottom" secondItem="ZxT-mA-1xU" secondAttribute="bottom" constant="120" id="Ilu-Zu-JqW"/> <constraint firstAttribute="bottom" secondItem="ZxT-mA-1xU" secondAttribute="bottom" constant="120" id="Ilu-Zu-JqW"/>
<constraint firstItem="rfz-9h-HoH" firstAttribute="centerY" secondItem="ZxT-mA-1xU" secondAttribute="centerY" id="NZg-SL-A31"/> <constraint firstItem="rfz-9h-HoH" firstAttribute="centerY" secondItem="ZxT-mA-1xU" secondAttribute="centerY" id="NZg-SL-A31"/>
<constraint firstAttribute="trailing" secondItem="w5l-pw-1ET" secondAttribute="trailing" id="TnQ-lp-9B9"/> <constraint firstAttribute="trailing" secondItem="w5l-pw-1ET" secondAttribute="trailing" id="TnQ-lp-9B9"/>
...@@ -264,13 +264,13 @@ ...@@ -264,13 +264,13 @@
</view> </view>
</objects> </objects>
<resources> <resources>
<image name="audio_running" width="48" height="48"/> <image name="audio_running" width="24" height="24"/>
<image name="call_button" width="43.5" height="43.5"/> <image name="call_button" width="29" height="29"/>
<image name="dialpad" width="75" height="75"/> <image name="dialpad" width="37.5" height="37.5"/>
<image name="disable_speakerphone" width="48" height="48"/> <image name="disable_speakerphone" width="24" height="24"/>
<image name="pause_call" width="48" height="48"/> <image name="pause_call" width="24" height="24"/>
<image name="stop_call" width="48" height="48"/> <image name="stop_call" width="24" height="24"/>
<image name="switch_camera" width="50" height="50"/> <image name="switch_camera" width="25" height="25"/>
<image name="video_running" width="43.5" height="43.5"/> <image name="video_running" width="29" height="29"/>
</resources> </resources>
</document> </document>
...@@ -177,7 +177,7 @@ class CallViewController: UIViewController, StoryboardBased, ViewModelBased { ...@@ -177,7 +177,7 @@ class CallViewController: UIViewController, StoryboardBased, ViewModelBased {
self.buttonsContainer.viewModel = self.viewModel.containerViewModel self.buttonsContainer.viewModel = self.viewModel.containerViewModel
self.buttonsContainer.cancelButton.rx.tap self.buttonsContainer.cancelButton.rx.tap
.subscribe(onNext: { [weak self] in .subscribe(onNext: { [weak self] in
self?.viewModel.cancelCall() self?.viewModel.cancelCall(stopProvider: true)
self?.removeFromScreen() self?.removeFromScreen()
}).disposed(by: self.disposeBag) }).disposed(by: self.disposeBag)
......
...@@ -24,7 +24,7 @@ import RxSwift ...@@ -24,7 +24,7 @@ import RxSwift
import SwiftyBeaver import SwiftyBeaver
import Contacts import Contacts
import RxCocoa import RxCocoa
// swiftlint:disable type_body_length
class CallViewModel: Stateable, ViewModel { class CallViewModel: Stateable, ViewModel {
//stateable //stateable
...@@ -112,6 +112,9 @@ class CallViewModel: Stateable, ViewModel { ...@@ -112,6 +112,9 @@ class CallViewModel: Stateable, ViewModel {
if hide { if hide {
self?.videoService.setCameraOrientation(orientation: UIDevice.current.orientation, callID: nil) self?.videoService.setCameraOrientation(orientation: UIDevice.current.orientation, callID: nil)
self?.videoService.stopAudioDevice() self?.videoService.stopAudioDevice()
if #available(iOS 10.0, *), let call = self?.call {
self?.callsProvider.stopCall(callUUID: call.callUUID)
}
} }
return hide return hide
}) })
...@@ -300,6 +303,7 @@ class CallViewModel: Stateable, ViewModel { ...@@ -300,6 +303,7 @@ class CallViewModel: Stateable, ViewModel {
var containerViewModel: ButtonsContainerViewModel? var containerViewModel: ButtonsContainerViewModel?
let injectionBag: InjectionBag let injectionBag: InjectionBag
let callsProvider: CallsProviderDelegate
required init(with injectionBag: InjectionBag) { required init(with injectionBag: InjectionBag) {
self.callService = injectionBag.callService self.callService = injectionBag.callService
...@@ -308,6 +312,7 @@ class CallViewModel: Stateable, ViewModel { ...@@ -308,6 +312,7 @@ class CallViewModel: Stateable, ViewModel {
self.videoService = injectionBag.videoService self.videoService = injectionBag.videoService
self.audioService = injectionBag.audioService self.audioService = injectionBag.audioService
self.profileService = injectionBag.profileService self.profileService = injectionBag.profileService
self.callsProvider = injectionBag.callsProvider
self.injectionBag = injectionBag self.injectionBag = injectionBag
callService.currentCall.filter({ [weak self] call in callService.currentCall.filter({ [weak self] call in
...@@ -319,6 +324,20 @@ class CallViewModel: Stateable, ViewModel { ...@@ -319,6 +324,20 @@ class CallViewModel: Stateable, ViewModel {
.setCameraOrientation(orientation: UIDevice.current.orientation, .setCameraOrientation(orientation: UIDevice.current.orientation,
callID: self?.call?.callId) callID: self?.call?.callId)
}).disposed(by: self.disposeBag) }).disposed(by: self.disposeBag)
callsProvider.sharedResponseStream
.filter({ [unowned self] serviceEvent in
guard let callUUID: String = serviceEvent
.getEventInput(ServiceEventInput.callUUID) else {return false}
return callUUID == self.call?.callUUID.uuidString
}).subscribe(onNext: { [unowned self] serviceEvent in
if serviceEvent.eventType == ServiceEventType.callProviderAnswerCall {
self.answerCall()
.subscribe()
.disposed(by: self.disposeBag)
} else if serviceEvent.eventType == ServiceEventType.callProviderCancellCall {
self.cancelCall(stopProvider: false)
}
}).disposed(by: self.disposeBag)
} }
static func formattedDurationFrom(interval: Int) -> String { static func formattedDurationFrom(interval: Int) -> String {
...@@ -333,10 +352,13 @@ class CallViewModel: Stateable, ViewModel { ...@@ -333,10 +352,13 @@ class CallViewModel: Stateable, ViewModel {
} }
} }
func cancelCall() { func cancelCall(stopProvider: Bool) {
guard let call = self.call else { guard let call = self.call else {
return return
} }
if #available(iOS 10.0, *), stopProvider {
self.callsProvider.stopCall(callUUID: call.callUUID)
}
self.callService.hangUp(callId: call.callId) self.callService.hangUp(callId: call.callId)
.subscribe(onCompleted: { [weak self] in .subscribe(onCompleted: { [weak self] in
// switch to either spk or headset (if connected) for loud ringtone // switch to either spk or headset (if connected) for loud ringtone
...@@ -358,7 +380,6 @@ class CallViewModel: Stateable, ViewModel { ...@@ -358,7 +380,6 @@ class CallViewModel: Stateable, ViewModel {
} }
func placeCall(with uri: String, userName: String, isAudioOnly: Bool = false) { func placeCall(with uri: String, userName: String, isAudioOnly: Bool = false) {
guard let account = self.accountService.currentAccount else { guard let account = self.accountService.currentAccount else {
return return
} }
...@@ -371,7 +392,12 @@ class CallViewModel: Stateable, ViewModel { ...@@ -371,7 +392,12 @@ class CallViewModel: Stateable, ViewModel {
userName: userName, userName: userName,
isAudioOnly: isAudioOnly) isAudioOnly: isAudioOnly)
.subscribe(onSuccess: { [weak self] callModel in .subscribe(onSuccess: { [weak self] callModel in
callModel.callUUID = UUID()
self?.call = callModel self?.call = callModel
if #available(iOS 10.0, *) {
self?.callsProvider
.startCall(account: account, call: callModel)
}
}).disposed(by: self.disposeBag) }).disposed(by: self.disposeBag)
} }
......
...@@ -192,4 +192,21 @@ final class AppCoordinator: Coordinator, StateableResponsive { ...@@ -192,4 +192,21 @@ final class AppCoordinator: Coordinator, StateableResponsive {
conversationCoordinator.puchConversation(participantId: participantID) conversationCoordinator.puchConversation(participantId: participantID)
} }
} }
func startCall(participant: String, name: String, isVideo: Bool) {
for child in self.childCoordinators {
if let childCoordinattor = child as? ConversationsCoordinator {
if isVideo {
childCoordinattor.stateSubject
.onNext(ConversationState
.startCall(contactRingId: participant, userName: name))
return
}
childCoordinattor.stateSubject
.onNext(ConversationState
.startAudioCall(contactRingId: participant, userName: name))
}
return
}
}
} }
...@@ -35,6 +35,7 @@ class InjectionBag { ...@@ -35,6 +35,7 @@ class InjectionBag {
let audioService: AudioService let audioService: AudioService
let dataTransferService: DataTransferService let dataTransferService: DataTransferService
let profileService: ProfilesService let profileService: ProfilesService
let callsProvider: CallsProviderDelegate
init (withDaemonService daemonService: DaemonService, init (withDaemonService daemonService: DaemonService,
withAccountService accountService: AccountsService, withAccountService accountService: AccountsService,
...@@ -47,7 +48,8 @@ class InjectionBag { ...@@ -47,7 +48,8 @@ class InjectionBag {
withVideoService videoService: VideoService, withVideoService videoService: VideoService,
withAudioService audioService: AudioService, withAudioService audioService: AudioService,
withDataTransferService dataTransferService: DataTransferService, withDataTransferService dataTransferService: DataTransferService,
withProfileService profileService: ProfilesService) { withProfileService profileService: ProfilesService,
withCallsProvider callsProvider: CallsProviderDelegate) {
self.daemonService = daemonService self.daemonService = daemonService
self.accountService = accountService self.accountService = accountService
self.nameService = nameService self.nameService = nameService
...@@ -60,5 +62,6 @@ class InjectionBag { ...@@ -60,5 +62,6 @@ class InjectionBag {
self.audioService = audioService self.audioService = audioService
self.dataTransferService = dataTransferService self.dataTransferService = dataTransferService
self.profileService = profileService self.profileService = profileService
self.callsProvider = callsProvider
} }
} }
/*
* Copyright (C) 2019 Savoir-faire Linux Inc.
*
* 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 Foundation
import Intents
@available(iOS 10.0, *)
extension NSUserActivity {
var startCallHandle: (hash: String, isVideo: Bool)? {
guard let interaction = interaction else { return nil }
let startVideoCallIntent = interaction.intent as? INStartVideoCallIntent
let startAudioCallIntent = interaction.intent as? INStartAudioCallIntent
if startVideoCallIntent == nil && startAudioCallIntent == nil {
return nil
}
let isVideo = startVideoCallIntent != nil ? true : false
if isVideo {
guard
let intent = startVideoCallIntent,
let contact = intent.contacts?.first,
let handle = contact.personHandle,
let value = handle.value else {
return nil
}
return(value, true)
}
guard
let intent = startAudioCallIntent,
let contact = intent.contacts?.first,
let handle = contact.personHandle,
let value = handle.value else {