Commit 19c4ce68 authored by Kateryna Kostiuk's avatar Kateryna Kostiuk Committed by Andreas Traczyk

vCard: add profile service

This patch introduce profile service. The service updates profiles
and post event s when new data is available.

Change-Id: I3e780cc3f19498379a49bacb20fa7faa2365705c
Reviewed-by: Andreas Traczyk's avatarAndreas Traczyk <andreas.traczyk@savoirfairelinux.com>
parent e26b621b
...@@ -135,6 +135,7 @@ ...@@ -135,6 +135,7 @@
0EB1A5CF1F8EBE03009923E2 /* DeviceCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 0EB1A5CE1F8EBE03009923E2 /* DeviceCell.xib */; }; 0EB1A5CF1F8EBE03009923E2 /* DeviceCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 0EB1A5CE1F8EBE03009923E2 /* DeviceCell.xib */; };
0EB1A5D11F8EBE23009923E2 /* DeviceCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EB1A5D01F8EBE23009923E2 /* DeviceCell.swift */; }; 0EB1A5D11F8EBE23009923E2 /* DeviceCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EB1A5D01F8EBE23009923E2 /* DeviceCell.swift */; };
0EB479951FA28A7300106AFD /* ButtonTransparentBackground.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EB479941FA28A7300106AFD /* ButtonTransparentBackground.swift */; }; 0EB479951FA28A7300106AFD /* ButtonTransparentBackground.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EB479941FA28A7300106AFD /* ButtonTransparentBackground.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 */; };
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 */; };
...@@ -416,6 +417,7 @@ ...@@ -416,6 +417,7 @@
0EB1A5CE1F8EBE03009923E2 /* DeviceCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = DeviceCell.xib; sourceTree = "<group>"; }; 0EB1A5CE1F8EBE03009923E2 /* DeviceCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = DeviceCell.xib; sourceTree = "<group>"; };
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>"; };
0EB479941FA28A7300106AFD /* ButtonTransparentBackground.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ButtonTransparentBackground.swift; sourceTree = "<group>"; }; 0EB479941FA28A7300106AFD /* ButtonTransparentBackground.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ButtonTransparentBackground.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>"; };
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>"; };
...@@ -771,6 +773,7 @@ ...@@ -771,6 +773,7 @@
62AA15C21FFC39C80064A063 /* VideoAdapterDelegate.swift */, 62AA15C21FFC39C80064A063 /* VideoAdapterDelegate.swift */,
62AA15C91FFD3D7E0064A063 /* VideoService.swift */, 62AA15C91FFD3D7E0064A063 /* VideoService.swift */,
62AF685D201A61FF003AA9E8 /* AudioService.swift */, 62AF685D201A61FF003AA9E8 /* AudioService.swift */,
0EBB72A82034F44200D88F46 /* ProfilesService.swift */,
); );
path = Services; path = Services;
sourceTree = "<group>"; sourceTree = "<group>";
...@@ -1762,6 +1765,7 @@ ...@@ -1762,6 +1765,7 @@
1A0C4EE51F1D67DF00550433 /* WalkthroughCoordinator.swift in Sources */, 1A0C4EE51F1D67DF00550433 /* WalkthroughCoordinator.swift in Sources */,
1A2D18DD1F29192D00B2C785 /* MessableBubble.swift in Sources */, 1A2D18DD1F29192D00B2C785 /* MessableBubble.swift in Sources */,
1A5DC02E1F3565640075E8EF /* ConversationViewModel.swift in Sources */, 1A5DC02E1F3565640075E8EF /* ConversationViewModel.swift in Sources */,
0EBB72A92034F44200D88F46 /* ProfilesService.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 */,
62AA15BF1FFC36840064A063 /* VideoAdapter.mm in Sources */, 62AA15BF1FFC36840064A063 /* VideoAdapter.mm in Sources */,
......
...@@ -40,6 +40,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD ...@@ -40,6 +40,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 profileService = ProfilesService()
private var conversationManager: ConversationsManager? private var conversationManager: ConversationsManager?
private var contactRequestManager: ContactRequestManager? private var contactRequestManager: ContactRequestManager?
...@@ -55,7 +56,8 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD ...@@ -55,7 +56,8 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
withNetworkService: self.networkService, withNetworkService: self.networkService,
withCallService: self.callService, withCallService: self.callService,
withVideoService: self.videoService, withVideoService: self.videoService,
withAudioService: self.audioService) withAudioService: self.audioService,
withProfileService: self.profileService)
}() }()
private lazy var appCoordinator: AppCoordinator = { private lazy var appCoordinator: AppCoordinator = {
return AppCoordinator(with: self.injectionBag) return AppCoordinator(with: self.injectionBag)
......
...@@ -158,7 +158,7 @@ class CallViewController: UIViewController, StoryboardBased, ViewModelBased { ...@@ -158,7 +158,7 @@ class CallViewController: UIViewController, StoryboardBased, ViewModelBased {
func setupBindings() { func setupBindings() {
self.viewModel.contactImageData.asObservable() self.viewModel.contactImageData?.asObservable()
.observeOn(MainScheduler.instance) .observeOn(MainScheduler.instance)
.subscribe(onNext: { [weak self] dataOrNil in .subscribe(onNext: { [weak self] dataOrNil in
if let imageData = dataOrNil { if let imageData = dataOrNil {
......
...@@ -37,6 +37,7 @@ class CallViewModel: Stateable, ViewModel { ...@@ -37,6 +37,7 @@ class CallViewModel: Stateable, ViewModel {
fileprivate let accountService: AccountsService fileprivate let accountService: AccountsService
fileprivate let videoService: VideoService fileprivate let videoService: VideoService
fileprivate let audioService: AudioService fileprivate let audioService: AudioService
fileprivate let profileService: ProfilesService
private let disposeBag = DisposeBag() private let disposeBag = DisposeBag()
fileprivate let log = SwiftyBeaver.self fileprivate let log = SwiftyBeaver.self
...@@ -49,30 +50,6 @@ class CallViewModel: Stateable, ViewModel { ...@@ -49,30 +50,6 @@ class CallViewModel: Stateable, ViewModel {
guard let call = self.call else { guard let call = self.call else {
return return
} }
self.contactsService.getProfileForUri(uri: call.participantRingId)
.subscribe(onNext: { [unowned self] profile in
self.profileUpdated(profile: profile)
})
.disposed(by: self.disposeBag)
self.callService
.sharedResponseStream
.filter({ (event) in
if let uri: String = event.getEventInput(ServiceEventInput.uri) {
return event.eventType == ServiceEventType.profileUpdated
&& uri == call.participantRingId
}
return false
})
.subscribe(onNext: { [unowned self] _ in
self.contactsService.getProfileForUri(uri: call.participantRingId)
.subscribe(onNext: { profile in
self.profileUpdated(profile: profile)
})
.disposed(by: self.disposeBag)
})
.disposed(by: disposeBag)
isHeadsetConnected = self.audioService.isHeadsetConnected.value isHeadsetConnected = self.audioService.isHeadsetConnected.value
isAudioOnly = call.isAudioOnly isAudioOnly = call.isAudioOnly
...@@ -82,7 +59,22 @@ class CallViewModel: Stateable, ViewModel { ...@@ -82,7 +59,22 @@ class CallViewModel: Stateable, ViewModel {
// data for ViewController binding // data for ViewController binding
var contactImageData = Variable<Data?>(nil) lazy var contactImageData: Observable<Data?>? = {
guard let call = self.call else {
return nil
}
return self.profileService.getProfile(ringId: call.participantRingId,
createIfNotexists: true)
.filter({ profile in
guard let photo = profile.photo else {
return false
}
return true
}).map({ profile in
return NSData(base64Encoded: profile.photo!,
options: NSData.Base64DecodingOptions.ignoreUnknownCharacters) as Data?
})
}()
lazy var incomingFrame: Observable<UIImage?> = { lazy var incomingFrame: Observable<UIImage?> = {
return videoService.incomingVideoFrame.asObservable().map({ frame in return videoService.incomingVideoFrame.asObservable().map({ frame in
...@@ -277,6 +269,7 @@ class CallViewModel: Stateable, ViewModel { ...@@ -277,6 +269,7 @@ class CallViewModel: Stateable, ViewModel {
self.accountService = injectionBag.accountService self.accountService = injectionBag.accountService
self.videoService = injectionBag.videoService self.videoService = injectionBag.videoService
self.audioService = injectionBag.audioService self.audioService = injectionBag.audioService
self.profileService = injectionBag.profileService
} }
static func formattedDurationFrom(interval: Int) -> String { static func formattedDurationFrom(interval: Int) -> String {
...@@ -321,17 +314,6 @@ class CallViewModel: Stateable, ViewModel { ...@@ -321,17 +314,6 @@ class CallViewModel: Stateable, ViewModel {
}).disposed(by: self.disposeBag) }).disposed(by: self.disposeBag)
} }
func profileUpdated(profile: Profile) {
guard let photo = profile.photo else {
return
}
guard let data = NSData(base64Encoded: photo,
options: NSData.Base64DecodingOptions.ignoreUnknownCharacters) as Data? else {
return
}
self.contactImageData.value = data
}
func respondOnTap() { func respondOnTap() {
self.screenTapped.onNext(true) self.screenTapped.onNext(true)
} }
......
...@@ -33,6 +33,7 @@ class InjectionBag { ...@@ -33,6 +33,7 @@ class InjectionBag {
let callService: CallsService let callService: CallsService
let videoService: VideoService let videoService: VideoService
let audioService: AudioService let audioService: AudioService
let profileService: ProfilesService
init (withDaemonService daemonService: DaemonService, init (withDaemonService daemonService: DaemonService,
withAccountService accountService: AccountsService, withAccountService accountService: AccountsService,
...@@ -43,7 +44,8 @@ class InjectionBag { ...@@ -43,7 +44,8 @@ class InjectionBag {
withNetworkService networkService: NetworkService, withNetworkService networkService: NetworkService,
withCallService callService: CallsService, withCallService callService: CallsService,
withVideoService videoService: VideoService, withVideoService videoService: VideoService,
withAudioService audioService: AudioService) { withAudioService audioService: AudioService,
withProfileService profileService: ProfilesService) {
self.daemonService = daemonService self.daemonService = daemonService
self.accountService = accountService self.accountService = accountService
self.nameService = nameService self.nameService = nameService
...@@ -54,6 +56,7 @@ class InjectionBag { ...@@ -54,6 +56,7 @@ class InjectionBag {
self.callService = callService self.callService = callService
self.videoService = videoService self.videoService = videoService
self.audioService = audioService self.audioService = audioService
self.profileService = profileService
} }
} }
...@@ -30,40 +30,18 @@ class ContactRequestItem { ...@@ -30,40 +30,18 @@ class ContactRequestItem {
let profileImageData = Variable<Data?>(nil) let profileImageData = Variable<Data?>(nil)
let disposeBag = DisposeBag() let disposeBag = DisposeBag()
init(withContactRequest contactRequest: ContactRequestModel, callService: CallsService, init(withContactRequest contactRequest: ContactRequestModel, profileService: ProfilesService,
contactService: ContactsService) { contactService: ContactsService) {
self.contactRequest = contactRequest self.contactRequest = contactRequest
self.userName.value = contactRequest.ringId self.userName.value = contactRequest.ringId
self.profileImageData.value = self.contactRequest.vCard?.imageData self.profileImageData.value = self.contactRequest.vCard?.imageData
profileService.getProfile(ringId: contactRequest.ringId, createIfNotexists: false)
contactService.getProfileForUri(uri: contactRequest.ringId)
.subscribe(onNext: { [unowned self] profile in .subscribe(onNext: { [unowned self] profile in
if let photo = profile.photo, if let photo = profile.photo,
let data = NSData(base64Encoded: photo, options: NSData.Base64DecodingOptions.ignoreUnknownCharacters) as Data? { let data = NSData(base64Encoded: photo,
options: NSData.Base64DecodingOptions.ignoreUnknownCharacters) as Data? {
self.profileImageData.value = data self.profileImageData.value = data
} }
}) }).disposed(by: self.disposeBag)
.disposed(by: self.disposeBag)
callService
.sharedResponseStream
.filter({ (event) in
if let uri: String = event.getEventInput(ServiceEventInput.uri) {
return event.eventType == ServiceEventType.profileUpdated
&& uri == contactRequest.ringId
}
return false
})
.subscribe(onNext: { [unowned self] _ in
contactService.getProfileForUri(uri: contactRequest.ringId)
.subscribe(onNext: { profile in
if let photo = profile.photo,
let data = NSData(base64Encoded: photo, options: NSData.Base64DecodingOptions.ignoreUnknownCharacters) as Data? {
self.profileImageData.value = data
}
})
.disposed(by: self.disposeBag)
})
.disposed(by: self.disposeBag)
} }
} }
...@@ -35,7 +35,7 @@ class ContactRequestsViewModel: Stateable, ViewModel { ...@@ -35,7 +35,7 @@ class ContactRequestsViewModel: Stateable, ViewModel {
let conversationService: ConversationsService let conversationService: ConversationsService
let nameService: NameService let nameService: NameService
let presenceService: PresenceService let presenceService: PresenceService
let callsService: CallsService let profileService: ProfilesService
fileprivate let disposeBag = DisposeBag() fileprivate let disposeBag = DisposeBag()
fileprivate let log = SwiftyBeaver.self fileprivate let log = SwiftyBeaver.self
...@@ -48,7 +48,7 @@ class ContactRequestsViewModel: Stateable, ViewModel { ...@@ -48,7 +48,7 @@ class ContactRequestsViewModel: Stateable, ViewModel {
self.conversationService = injectionBag.conversationsService self.conversationService = injectionBag.conversationsService
self.nameService = injectionBag.nameService self.nameService = injectionBag.nameService
self.presenceService = injectionBag.presenceService self.presenceService = injectionBag.presenceService
self.callsService = injectionBag.callService self.profileService = injectionBag.profileService
self.injectionBag = injectionBag self.injectionBag = injectionBag
} }
...@@ -62,7 +62,7 @@ class ContactRequestsViewModel: Stateable, ViewModel { ...@@ -62,7 +62,7 @@ class ContactRequestsViewModel: Stateable, ViewModel {
.sorted { $0.receivedDate > $1.receivedDate } .sorted { $0.receivedDate > $1.receivedDate }
.map { contactRequest in .map { contactRequest in
let item = ContactRequestItem(withContactRequest: contactRequest, let item = ContactRequestItem(withContactRequest: contactRequest,
callService: self.callsService, profileService: self.profileService,
contactService: self.contactsService) contactService: self.contactsService)
self.lookupUserName(withItem: item) self.lookupUserName(withItem: item)
return item return item
......
...@@ -36,7 +36,7 @@ class ConversationViewModel: Stateable, ViewModel { ...@@ -36,7 +36,7 @@ class ConversationViewModel: Stateable, ViewModel {
private let nameService: NameService private let nameService: NameService
private let contactsService: ContactsService private let contactsService: ContactsService
private let presenceService: PresenceService private let presenceService: PresenceService
private let callsService: CallsService private let profileService: ProfilesService
private let injectionBag: InjectionBag private let injectionBag: InjectionBag
private let stateSubject = PublishSubject<State>() private let stateSubject = PublishSubject<State>()
...@@ -51,7 +51,7 @@ class ConversationViewModel: Stateable, ViewModel { ...@@ -51,7 +51,7 @@ class ConversationViewModel: Stateable, ViewModel {
self.nameService = injectionBag.nameService self.nameService = injectionBag.nameService
self.contactsService = injectionBag.contactsService self.contactsService = injectionBag.contactsService
self.presenceService = injectionBag.presenceService self.presenceService = injectionBag.presenceService
self.callsService = injectionBag.callService self.profileService = injectionBag.profileService
dateFormatter.dateStyle = .medium dateFormatter.dateStyle = .medium
hourFormatter.dateFormat = "HH:mm" hourFormatter.dateFormat = "HH:mm"
...@@ -87,24 +87,16 @@ class ConversationViewModel: Stateable, ViewModel { ...@@ -87,24 +87,16 @@ class ConversationViewModel: Stateable, ViewModel {
}).disposed(by: self.disposeBag) }).disposed(by: self.disposeBag)
let contact = self.contactsService.contact(withRingId: contactRingId) let contact = self.contactsService.contact(withRingId: contactRingId)
self.contactsService.getContactRequestVCard(forContactWithRingId: contactRingId)
if let profile = conversation.value.participantProfile, let photo = profile.photo { .subscribe(onSuccess: { vCard in
self.displayName.value = profile.alias guard let imageData = vCard.imageData else {
if let data = NSData(base64Encoded: photo, options: NSData.Base64DecodingOptions.ignoreUnknownCharacters) as Data? { self.log.warning("vCard for ringId: \(contactRingId) has no image")
self.profileImageData.value = data return
} }
} else { self.profileImageData.value = imageData
self.contactsService.loadVCard(forContactWithRingId: contactRingId) self.displayName.value = VCardUtils.getName(from: vCard)
.subscribe(onSuccess: { vCard in })
guard let imageData = vCard.imageData else { .disposed(by: self.disposeBag)
self.log.warning("vCard for ringId: \(contactRingId) has no image")
return
}
self.profileImageData.value = imageData
self.displayName.value = VCardUtils.getName(from: vCard)
})
.disposed(by: self.disposeBag)
}
// invite and block buttons // invite and block buttons
if let contact = contact { if let contact = contact {
...@@ -143,26 +135,15 @@ class ConversationViewModel: Stateable, ViewModel { ...@@ -143,26 +135,15 @@ class ConversationViewModel: Stateable, ViewModel {
}) })
.disposed(by: disposeBag) .disposed(by: disposeBag)
self.callsService self.profileService.getProfile(ringId: contactRingId,
.sharedResponseStream createIfNotexists: false)
.filter({ (event) in .subscribe(onNext: { [unowned self] profile in
if let uri: String = event.getEventInput(ServiceEventInput.uri) { self.displayName.value = profile.alias
return event.eventType == ServiceEventType.profileUpdated if let photo = profile.photo,
&& uri == contactRingId let data = NSData(base64Encoded: photo, options: NSData.Base64DecodingOptions.ignoreUnknownCharacters) as Data? {
self.profileImageData.value = data
} }
return false }).disposed(by: disposeBag)
})
.subscribe(onNext: { [unowned self] _ in
self.contactsService.getProfileForUri(uri: contactRingId)
.subscribe(onNext: { profile in
if let photo = profile.photo,
let data = NSData(base64Encoded: photo, options: NSData.Base64DecodingOptions.ignoreUnknownCharacters) as Data? {
self.profileImageData.value = data
}
})
.disposed(by: self.disposeBag)
})
.disposed(by: disposeBag)
if let contactUserName = contact?.userName { if let contactUserName = contact?.userName {
self.userName.value = contactUserName self.userName.value = contactUserName
......
...@@ -41,11 +41,6 @@ enum MediaType: String, CustomStringConvertible { ...@@ -41,11 +41,6 @@ enum MediaType: String, CustomStringConvertible {
} }
} }
struct Base64VCard {
var data: [Int: String] //The key is the number of vCard part
var partsReceived: Int
}
class CallsService: CallsAdapterDelegate { class CallsService: CallsAdapterDelegate {
fileprivate let disposeBag = DisposeBag() fileprivate let disposeBag = DisposeBag()
...@@ -54,26 +49,19 @@ class CallsService: CallsAdapterDelegate { ...@@ -54,26 +49,19 @@ class CallsService: CallsAdapterDelegate {
fileprivate var calls = [String: CallModel]() fileprivate var calls = [String: CallModel]()
fileprivate var base64VCards = [Int: Base64VCard]() //The key is the vCard id
fileprivate let ringVCardMIMEType = "x-ring/ring.profile.vcard;" fileprivate let ringVCardMIMEType = "x-ring/ring.profile.vcard;"
let currentCall = ReplaySubject<CallModel>.create(bufferSize: 1) let currentCall = ReplaySubject<CallModel>.create(bufferSize: 1)
let newCall = Variable<CallModel>(CallModel(withCallId: "", callDetails: [:])) let newCall = Variable<CallModel>(CallModel(withCallId: "", callDetails: [:]))
//let receivedVCard = PublishSubject<Profile>()
let dbManager = DBManager(profileHepler: ProfileDataHelper(), conversationHelper: ConversationDataHelper(), interactionHepler: InteractionDataHelper())
fileprivate let responseStream = PublishSubject<ServiceEvent>()
var sharedResponseStream: Observable<ServiceEvent>
init(withCallsAdapter callsAdapter: CallsAdapter) { init(withCallsAdapter callsAdapter: CallsAdapter) {
self.callsAdapter = callsAdapter self.callsAdapter = callsAdapter
self.responseStream.disposed(by: disposeBag)
self.sharedResponseStream = responseStream.share()
CallsAdapter.delegate = self CallsAdapter.delegate = self
NotificationCenter.default.addObserver(self, selector: #selector(self.refuseUnansweredCall(_:)), NotificationCenter.default.addObserver(self, selector: #selector(self.refuseUnansweredCall(_:)),
name: NSNotification.Name(rawValue: NotificationName.refuseCallFromNotifications.rawValue), name: NSNotification.Name(rawValue: NotificationName.refuseCallFromNotifications.rawValue),
object: nil) object: nil)
} }
@objc func refuseUnansweredCall(_ notification: NSNotification) { @objc func refuseUnansweredCall(_ notification: NSNotification) {
guard let callid = notification.userInfo?[NotificationUserInfoKeys.callID.rawValue] as? String else { guard let callid = notification.userInfo?[NotificationUserInfoKeys.callID.rawValue] as? String else {
return return
...@@ -259,88 +247,13 @@ class CallsService: CallsAdapterDelegate { ...@@ -259,88 +247,13 @@ class CallsService: CallsAdapterDelegate {
} }
} }
// swiftlint:disable cyclomatic_complexity
func didReceiveMessage(withCallId callId: String, fromURI uri: String, message: [String: String]) { func didReceiveMessage(withCallId callId: String, fromURI uri: String, message: [String: String]) {
if let vCardKey = message.keys.filter({ $0.hasPrefix(self.ringVCardMIMEType) }).first { if message.keys.filter({ $0.hasPrefix(self.ringVCardMIMEType) }).first != nil {
var data = [String: Any]()
//Parse the key to get the number of parts and the current part number data[ProfileNotificationsKeys.ringID.rawValue] = uri
let components = vCardKey.components(separatedBy: ",") data[ProfileNotificationsKeys.message.rawValue] = message
NotificationCenter.default.post(name: NSNotification.Name(ProfileNotifications.messageReceived.rawValue), object: nil, userInfo: data)
guard let partComponent = components.filter({$0.hasPrefix("part=")}).first else {
return
}
guard let ofComponent = components.filter({$0.hasPrefix("of=")}).first else {
return
}
guard let idComponent = components.filter({$0.hasPrefix("x-ring/ring.profile.vcard;id=")}).first else {
return
}
guard let part = Int(partComponent.components(separatedBy: "=")[1]) else {
return
}
guard let of = Int(ofComponent.components(separatedBy: "=")[1]) else {
return
}
guard let id = Int(idComponent.components(separatedBy: "=")[1]) else {
return
}
var numberOfReceivedChunk = 1
if var chunk = self.base64VCards[id] {
chunk.data[part] = message[vCardKey]
chunk.partsReceived += 1
numberOfReceivedChunk = chunk.partsReceived
self.base64VCards[id] = chunk
} else {
let partMessage = message[vCardKey]
let data: [Int: String] = [part: partMessage!]
let chunk = Base64VCard(data: data, partsReceived: numberOfReceivedChunk)
self.base64VCards[id] = chunk
}
//Emit the vCard when all data are appended
if of == numberOfReceivedChunk {
guard let vcard = self.base64VCards[id] else {
return
}
let vCardChunks = vcard.data
//Append data from sorted part numbers
var vCardData = Data()
for currentPartNumber in vCardChunks.keys.sorted() {
if let currentData = vCardChunks[currentPartNumber]?.data(using: String.Encoding.utf8) {
vCardData.append(currentData)
}
}
//Create the vCard, save and db and emite an event
do {
if let vCard = try CNContactVCardSerialization.contacts(with: vCardData).first {
let name = VCardUtils.getName(from: vCard)
var stringImage: String?
if let image = vCard.imageData {
stringImage = image.base64EncodedString()
}
let uri = uri.replacingOccurrences(of: "@ring.dht", with: "")
_ = self.dbManager
.createOrUpdateRingProfile(profileUri: uri,
alias: name,
image: stringImage,
status: ProfileStatus.untrasted)
var event = ServiceEvent(withEventType: .profileUpdated)
event.addEventInput(.uri, value: uri)
self.responseStream.onNext(event)
}
} catch {
self.log.error(error)
}
}
} }
} }
// swiftlint:enable cyclomatic_complexity // swiftlint:enable cyclomatic_complexity
......
...@@ -313,6 +313,21 @@ extension ContactsService: ContactsAdapterDelegate { ...@@ -313,6 +313,21 @@ extension ContactsService: ContactsAdapterDelegate {
return vCard return vCard
} }
func getContactRequestVCard(forContactWithRingId ringID: String) -> Single<CNContact> {
return Single.create(subscribe: { single in
if let contactRequest = self.contactRequest(withRingId: ringID) {
if let vCard = contactRequest.vCard {
single(.success(vCard))
} else {
single(.error(ContactServiceError.loadVCardFailed))
}
} else {
single(.error(ContactServiceError.loadVCardFailed))
}
return Disposables.create { }
})
}
func getProfileForUri(uri: String) ->Observable<Profile> { func getProfileForUri(uri: String) ->Observable<Profile> {
return self.dbManager.profileObservable(for: uri, createIfNotExists: false) return self.dbManager.profileObservable(for: uri, createIfNotExists: false)
.subscribeOn(ConcurrentDispatchQueueScheduler(qos: .background)) .subscribeOn(ConcurrentDispatchQueueScheduler(qos: .background))
......
/*
* Copyright (C) 2017 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 RxSwift
import SwiftyBeaver
enum ProfileNotifications: String {
case messageReceived
}
enum ProfileNotificationsKeys: String {
case ringID
case message
}
struct Base64VCard {
var data: [Int: String] //The key is the number of vCard part
var partsReceived: Int
}
class ProfilesService {
fileprivate let ringVCardMIMEType = "x-ring/ring.profile.vcard;"
fileprivate var base64VCards = [Int: Base64VCard]()
fileprivate let log = SwiftyBeaver.self
var profiles = [String: ReplaySubject<Profile>]()
let dbManager = DBManager(profileHepler: ProfileDataHelper(),
conversationHelper: ConversationDataHelper(),
interactionHepler: InteractionDataHelper())