blob: 6d81af68df7efb91821b0f1884937a7635740a01 [file] [log] [blame]
Alexandre Lisionc5148052015-03-04 15:10:35 -05001/*
Alexandre Lision9fe374b2016-01-06 10:17:31 -05002 * Copyright (C) 2015-2016 Savoir-faire Linux Inc.
Alexandre Lisionc5148052015-03-04 15:10:35 -05003 * Author: Alexandre Lision <alexandre.lision@savoirfairelinux.com>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
Alexandre Lisionc5148052015-03-04 15:10:35 -050018 */
19#import "CurrentCallVC.h"
Kateryna Kostiuk00dcbff2019-07-11 15:42:13 -040020extern "C" {
21#import "libavutil/frame.h"
22#import "libavutil/display.h"
23}
Alexandre Lisionc5148052015-03-04 15:10:35 -050024
25#import <QuartzCore/QuartzCore.h>
26
Alexandre Lision210fe212016-01-27 11:15:13 -050027///Qt
28#import <QMimeData>
29#import <QtMacExtras/qmacfunctions.h>
30#import <QtCore/qabstractitemmodel.h>
Alexandre Lision210fe212016-01-27 11:15:13 -050031#import <QPixmap>
Kateryna Kostiuk5d90c3b2019-07-18 12:03:52 -040032#import <QUrl>
Alexandre Lision210fe212016-01-27 11:15:13 -050033
34///LRC
Alexandre Lisionc5148052015-03-04 15:10:35 -050035#import <video/renderer.h>
Olivier Soldano994971f2017-12-05 16:30:12 -050036#import <api/newcallmodel.h>
37#import <api/call.h>
38#import <api/conversationmodel.h>
Kateryna Kostiuk5d90c3b2019-07-18 12:03:52 -040039#import <api/avmodel.h>
Alexandre Lision210fe212016-01-27 11:15:13 -050040#import <globalinstances.h>
Alexandre Lisionc5148052015-03-04 15:10:35 -050041
Alexandre Lision62005312016-01-28 15:55:16 -050042#import "AppDelegate.h"
Alexandre Lisionf47a2562015-06-15 15:48:29 -040043#import "views/ITProgressIndicator.h"
Alexandre Lision74dd47f2015-04-14 13:47:42 -040044#import "views/CallView.h"
Kateryna Kostiuk81afce72018-10-30 16:40:50 -040045#import "views/NSColor+RingTheme.h"
Alexandre Lision210fe212016-01-27 11:15:13 -050046#import "delegates/ImageManipulationDelegate.h"
Alexandre Lision16d9c0a2015-08-10 12:05:15 -040047#import "ChatVC.h"
Alexandre Lision4baba4c2016-02-11 13:00:57 -050048#import "views/IconButton.h"
Anthony Léonard6f819752018-01-05 09:53:40 -050049#import "utils.h"
Kateryna Kostiuk00dcbff2019-07-11 15:42:13 -040050#import "views/CallMTKView.h"
Kateryna Kostiukc5fd3b92019-07-24 10:12:44 -040051#import "VideoCommon.h"
Kateryna Kostiuk88a6ee82019-09-10 17:55:08 -040052#import "views/GradientView.h"
Kateryna Kostiuk6891d4f2019-09-19 17:44:33 -040053#import "views/MovableView.h"
Alexandre Lision74dd47f2015-04-14 13:47:42 -040054
Kateryna Kostiuk1ba5a012018-12-12 17:07:38 -050055@interface CurrentCallVC () <NSPopoverDelegate> {
Olivier Soldano994971f2017-12-05 16:30:12 -050056 std::string convUid_;
57 std::string callUid_;
Kateryna Kostiuk4db61092019-10-17 16:57:40 -040058 std::string confUid_;
Olivier Soldano994971f2017-12-05 16:30:12 -050059 const lrc::api::account::Info *accountInfo_;
Anthony Léonard66a050c2018-01-03 16:32:34 -050060 NSTimer* refreshDurationTimer;
Olivier Soldano994971f2017-12-05 16:30:12 -050061}
Alexandre Lisionc5148052015-03-04 15:10:35 -050062
Alexandre Lision210fe212016-01-27 11:15:13 -050063// Main container
64@property (unsafe_unretained) IBOutlet NSSplitView* splitView;
Kateryna Kostiuk89398b62018-11-20 16:45:04 -050065@property (unsafe_unretained) IBOutlet NSView* backgroundImage;
Kateryna Kostiuk81afce72018-10-30 16:40:50 -040066@property (unsafe_unretained) IBOutlet NSBox* bluerBackgroundEffect;
Alexandre Lision210fe212016-01-27 11:15:13 -050067
Alexandre Lision89edc6a2015-11-09 11:30:47 -050068// Header info
69@property (unsafe_unretained) IBOutlet NSView* headerContainer;
Kateryna Kostiuk2aa39232019-10-10 11:55:11 -040070@property (unsafe_unretained) IBOutlet NSView* headerGradientView;
Alexandre Lision89edc6a2015-11-09 11:30:47 -050071@property (unsafe_unretained) IBOutlet NSTextField* timeSpentLabel;
72
Kateryna Kostiuk9e9f7822019-09-27 11:29:45 -040073// info
74@property (unsafe_unretained) IBOutlet NSStackView* infoContainer;
75@property (unsafe_unretained) IBOutlet NSImageView* contactPhoto;
76@property (unsafe_unretained) IBOutlet NSTextField* contactNameLabel;
77@property (unsafe_unretained) IBOutlet NSTextField* callStateLabel;
78@property (unsafe_unretained) IBOutlet NSTextField* contactIdLabel;
79@property (unsafe_unretained) IBOutlet IconButton* cancelCallButton;
80@property (unsafe_unretained) IBOutlet IconButton* pickUpButton;
81@property (unsafe_unretained) IBOutlet ITProgressIndicator *loadingIndicator;
Andreas Traczyk77a50d52018-05-08 17:47:31 -040082
Alexandre Lision89edc6a2015-11-09 11:30:47 -050083// Call Controls
Kateryna Kostiuk88a6ee82019-09-10 17:55:08 -040084@property (unsafe_unretained) IBOutlet GradientView* controlsPanel;
Kateryna Kostiuk2aa39232019-10-10 11:55:11 -040085@property (unsafe_unretained) IBOutlet NSStackView* controlsStackView;
Alexandre Lision62005312016-01-28 15:55:16 -050086
Alexandre Lision4baba4c2016-02-11 13:00:57 -050087@property (unsafe_unretained) IBOutlet IconButton* holdOnOffButton;
88@property (unsafe_unretained) IBOutlet IconButton* hangUpButton;
89@property (unsafe_unretained) IBOutlet IconButton* recordOnOffButton;
Alexandre Lision4baba4c2016-02-11 13:00:57 -050090@property (unsafe_unretained) IBOutlet IconButton* muteAudioButton;
91@property (unsafe_unretained) IBOutlet IconButton* muteVideoButton;
Alexandre Lision4baba4c2016-02-11 13:00:57 -050092@property (unsafe_unretained) IBOutlet IconButton* transferButton;
93@property (unsafe_unretained) IBOutlet IconButton* addParticipantButton;
94@property (unsafe_unretained) IBOutlet IconButton* chatButton;
Andreas Traczyk77a50d52018-05-08 17:47:31 -040095@property (unsafe_unretained) IBOutlet IconButton* qualityButton;
Alexandre Lisiond18fa272015-06-15 11:18:03 -040096
Alexandre Lisionc5148052015-03-04 15:10:35 -050097// Video
Alexandre Lision58cab672015-06-09 15:25:40 -040098@property (unsafe_unretained) IBOutlet CallView *videoView;
Kateryna Kostiuk00dcbff2019-07-11 15:42:13 -040099@property (unsafe_unretained) IBOutlet CallMTKView *previewView;
Kateryna Kostiuk6891d4f2019-09-19 17:44:33 -0400100@property (unsafe_unretained) IBOutlet MovableView *movableBaseForView;
101@property (unsafe_unretained) IBOutlet NSView* hidePreviewBackground;
102@property (unsafe_unretained) IBOutlet NSButton* hidePreviewButton;
Kateryna Kostiuk00dcbff2019-07-11 15:42:13 -0400103
104@property (unsafe_unretained) IBOutlet CallMTKView *videoMTKView;
Alexandre Lisionc5148052015-03-04 15:10:35 -0500105
Kateryna Kostiuk5d90c3b2019-07-18 12:03:52 -0400106@property RendererConnectionsHolder* renderConnections;
Alexandre Lisionef6333a2015-03-24 12:30:31 -0400107@property QMetaObject::Connection videoStarted;
Kateryna Kostiuk4db61092019-10-17 16:57:40 -0400108@property QMetaObject::Connection callStateChanged;
Alexandre Lisionb65c0272015-07-22 15:51:29 -0400109@property QMetaObject::Connection messageConnection;
Kateryna Kostiuk133364b2018-10-31 11:36:08 -0400110@property QMetaObject::Connection profileUpdatedConnection;
Alexandre Lisionc5148052015-03-04 15:10:35 -0500111
Kateryna Kostiuk4db61092019-10-17 16:57:40 -0400112//conference
113@property (unsafe_unretained) IBOutlet NSStackView *callingWidgetsContainer;
114
115@property (strong) NSPopover* brokerPopoverVC;
116
Kateryna Kostiuk9e9f7822019-09-27 11:29:45 -0400117@property (strong) IBOutlet ChatVC* chatVC;
118
Alexandre Lisionc5148052015-03-04 15:10:35 -0500119@end
120
121@implementation CurrentCallVC
Kateryna Kostiuk5d90c3b2019-07-18 12:03:52 -0400122lrc::api::AVModel* mediaModel;
Kateryna Kostiuk4db61092019-10-17 16:57:40 -0400123NSMutableDictionary *connectingCalls;
Kateryna Kostiuk6891d4f2019-09-19 17:44:33 -0400124
125NSInteger const PREVIEW_WIDTH = 185;
126NSInteger const PREVIEW_HEIGHT = 130;
Kateryna Kostiuk2aa39232019-10-10 11:55:11 -0400127NSInteger const HIDE_PREVIEW_BUTTON_SIZE = 25;
Kateryna Kostiuk6891d4f2019-09-19 17:44:33 -0400128NSInteger const PREVIEW_MARGIN = 20;
129
Kateryna Kostiuk4db61092019-10-17 16:57:40 -0400130@synthesize holdOnOffButton, hangUpButton, recordOnOffButton, pickUpButton, chatButton, transferButton, addParticipantButton, timeSpentLabel, muteVideoButton, muteAudioButton, controlsPanel, headerContainer, videoView, previewView, splitView, loadingIndicator, backgroundImage, bluerBackgroundEffect, hidePreviewButton, hidePreviewBackground, movableBaseForView, infoContainer, contactPhoto, contactNameLabel, callStateLabel, contactIdLabel, cancelCallButton, headerGradientView, controlsStackView, callingWidgetsContainer, brokerPopoverVC;
Alexandre Lisionc5148052015-03-04 15:10:35 -0500131
Kateryna Kostiuk5d90c3b2019-07-18 12:03:52 -0400132@synthesize renderConnections;
Kateryna Kostiuk00dcbff2019-07-11 15:42:13 -0400133CVPixelBufferPoolRef pixelBufferPoolDistantView;
134CVPixelBufferRef pixelBufferDistantView;
135CVPixelBufferPoolRef pixelBufferPoolPreview;
136CVPixelBufferRef pixelBufferPreview;
Alexandre Lisionc5148052015-03-04 15:10:35 -0500137
Kateryna Kostiuk4db61092019-10-17 16:57:40 -0400138/* update call and conversation info
139 * set info for chat view
140 * connect signals
141 */
Olivier Soldano994971f2017-12-05 16:30:12 -0500142-(void) setCurrentCall:(const std::string&)callUid
143 conversation:(const std::string&)convUid
Kateryna Kostiuk5d90c3b2019-07-18 12:03:52 -0400144 account:(const lrc::api::account::Info*)account
145 avModel:(lrc::api::AVModel *)avModel;
Alexandre Lisionc5148052015-03-04 15:10:35 -0500146{
Olivier Soldano994971f2017-12-05 16:30:12 -0500147 if(account == nil)
148 return;
Alexandre Lisionef6333a2015-03-24 12:30:31 -0400149
Kateryna Kostiuk5d90c3b2019-07-18 12:03:52 -0400150 mediaModel = avModel;
Olivier Soldano994971f2017-12-05 16:30:12 -0500151 auto* callModel = account->callModel.get();
Alexandre Lisionc5148052015-03-04 15:10:35 -0500152
Olivier Soldano994971f2017-12-05 16:30:12 -0500153 if (not callModel->hasCall(callUid)){
154 callUid_.clear();
Kateryna Kostiuk4db61092019-10-17 16:57:40 -0400155 confUid_.clear();
Alexandre Lision2db8f472015-07-22 15:05:46 -0400156 return;
157 }
Olivier Soldano994971f2017-12-05 16:30:12 -0500158 callUid_ = callUid;
159 convUid_ = convUid;
160 accountInfo_ = account;
Kateryna Kostiuk4db61092019-10-17 16:57:40 -0400161 auto convIt = getConversationFromUid(convUid_, *accountInfo_->conversationModel);
162 confUid_ = convIt->confId;
Anthony Léonard4b0935f2017-12-26 12:05:22 -0500163 [self.chatVC setConversationUid:convUid model:account->conversationModel.get()];
Kateryna Kostiuk4db61092019-10-17 16:57:40 -0400164 [self connectSignals];
165}
166
167-(void) connectSignals {
168 auto* callModel = accountInfo_->callModel.get();
169 auto* convModel = accountInfo_->conversationModel.get();
170 //monitor for updated call state
171 QObject::disconnect(self.callStateChanged);
172 self.callStateChanged = QObject::connect(callModel,
173 &lrc::api::NewCallModel::callStatusChanged,
174 [self](const std::string callId) {
175 if ([self isCurrentCall: callId]) {
176 [self updateCall];
177 }
178 });
179 /* monitor media for messaging text messaging */
180 QObject::disconnect(self.messageConnection);
181 self.messageConnection = QObject::connect(convModel,
182 &lrc::api::ConversationModel::interactionStatusUpdated,
183 [self] (std::string convUid,
184 uint64_t msgId,
185 lrc::api::interaction::Info msg) {
186 if (msg.type == lrc::api::interaction::Type::TEXT) {
187 if(not [[self splitView] isSubviewCollapsed:[[[self splitView] subviews] objectAtIndex: 1]]){
188 return;
189 }
190 [self uncollapseRightView];
191 }
192 });
193 //monitor for updated profile
194 QObject::disconnect(self.profileUpdatedConnection);
195 self.profileUpdatedConnection =
196 QObject::connect(accountInfo_->contactModel.get(),
197 &lrc::api::ContactModel::contactAdded,
198 [self](const std::string &contactUri) {
199 auto convIt = getConversationFromUid(convUid_, *accountInfo_->conversationModel.get());
200 if (convIt == accountInfo_->conversationModel->allFilteredConversations().end()) {
201 return;
202 }
203 if (convIt->participants.empty()) {
204 return;
205
206 }
207 auto& contact = accountInfo_->contactModel->getContact(convIt->participants[0]);
208 if (contact.profileInfo.type == lrc::api::profile::Type::RING && contact.profileInfo.uri == contactUri)
209 accountInfo_->conversationModel->makePermanent(convUid_);
210 [contactPhoto setImage: [self getContactImageOfSize:120.0 withDefaultAvatar:YES]];
211 [self.delegate conversationInfoUpdatedFor:convUid_];
212 [self setBackground];
213 });
214 [self connectVideoSignals];
215}
216
217-(void) updatePendingCalls {
218 for (NSView *view in callingWidgetsContainer.subviews) {
219 view.removeFromSuperview;
220 }
221 NSDictionary * calls = connectingCalls[@(callUid_.c_str())];
222 for (NSViewController * callView in calls.allValues) {
223 [self.callingWidgetsContainer addView: callView.view inGravity:NSStackViewGravityBottom];
224 }
Alexandre Lisionc5148052015-03-04 15:10:35 -0500225}
226
Kateryna Kostiuk88a6ee82019-09-10 17:55:08 -0400227-(void) setUpButtons:(lrc::api::call::Info&)callInfo isRecording:(BOOL) isRecording {
228 muteAudioButton.image = callInfo.audioMuted ? [NSImage imageNamed:@"ic_action_mute_audio.png"] :
229 [NSImage imageNamed:@"ic_action_audio.png"];
230 muteVideoButton.image = callInfo.videoMuted ? [NSImage imageNamed:@"ic_action_mute_video.png"] :
231 [NSImage imageNamed:@"ic_action_video.png"];
232 [muteVideoButton setHidden: callInfo.isAudioOnly ? YES: NO];
233 if (isRecording) {
234 [recordOnOffButton startBlinkAnimationfrom:[NSColor buttonBlinkColorColor] to:[NSColor whiteColor] scaleFactor: 1 duration: 1.5];
235 } else {
236 [recordOnOffButton stopBlinkAnimation];
237 }
238}
239
Kateryna Kostiuk4db61092019-10-17 16:57:40 -0400240- (void) setUpPreviewFrame {
Kateryna Kostiuk6891d4f2019-09-19 17:44:33 -0400241 CGPoint previewOrigin = CGPointMake(self.videoView.frame.size.width - PREVIEW_WIDTH - PREVIEW_MARGIN, PREVIEW_MARGIN);
242 movableBaseForView.frame = CGRectMake(previewOrigin.x, previewOrigin.y, PREVIEW_WIDTH, PREVIEW_HEIGHT);
Kateryna Kostiuk6891d4f2019-09-19 17:44:33 -0400243 self.movableBaseForView.movable = true;
244 previewView.frame = movableBaseForView.bounds;
Kateryna Kostiuk2aa39232019-10-10 11:55:11 -0400245 hidePreviewBackground.frame = [self frameForExpendPreviewButton];
246 hidePreviewButton.contentTintColor = [NSColor blackColor];
Kateryna Kostiuk6891d4f2019-09-19 17:44:33 -0400247}
248
Alexandre Lisionc5148052015-03-04 15:10:35 -0500249- (void)awakeFromNib
250{
Alexandre Lisionef6333a2015-03-24 12:30:31 -0400251 [self.view setWantsLayer:YES];
Alexandre Lisionc5148052015-03-04 15:10:35 -0500252
Kateryna Kostiuk5d90c3b2019-07-18 12:03:52 -0400253 renderConnections = [[RendererConnectionsHolder alloc] init];
Alexandre Lisionef6333a2015-03-24 12:30:31 -0400254
Alexandre Lisionf47a2562015-06-15 15:48:29 -0400255 [loadingIndicator setColor:[NSColor whiteColor]];
Andreas Traczyk77a50d52018-05-08 17:47:31 -0400256 [loadingIndicator setNumberOfLines:200];
257 [loadingIndicator setWidthOfLine:4];
258 [loadingIndicator setLengthOfLine:4];
259 [loadingIndicator setInnerMargin:59];
Alexandre Lisionf47a2562015-06-15 15:48:29 -0400260
Alexandre Lisiona1eee3c2015-08-10 13:44:51 -0400261 [self.videoView setCallDelegate:self];
Kateryna Kostiuk81afce72018-10-30 16:40:50 -0400262 [bluerBackgroundEffect setWantsLayer:YES];
Kateryna Kostiuk89398b62018-11-20 16:45:04 -0500263 [backgroundImage setWantsLayer: YES];
264 backgroundImage.layer.contentsGravity = kCAGravityResizeAspectFill;
Kateryna Kostiuk9e9f7822019-09-27 11:29:45 -0400265 movableBaseForView.wantsLayer = YES;
Kateryna Kostiuk2aa39232019-10-10 11:55:11 -0400266 movableBaseForView.shadow = [[NSShadow alloc] init];
267 movableBaseForView.layer.shadowOpacity = 0.6;
268 movableBaseForView.layer.shadowColor = [[NSColor blackColor] CGColor];
269 movableBaseForView.layer.shadowOffset = NSMakeSize(0, -3);
270 movableBaseForView.layer.shadowRadius = 10;
271 previewView.wantsLayer = YES;
272 previewView.layer.cornerRadius = 5;
273 previewView.layer.masksToBounds = true;
274 hidePreviewBackground.wantsLayer = YES;
275 hidePreviewBackground.layer.cornerRadius = 5;
276 hidePreviewBackground.layer.maskedCorners = kCALayerMinXMinYCorner;
277 hidePreviewBackground.layer.masksToBounds = true;
Kateryna Kostiuk9e9f7822019-09-27 11:29:45 -0400278 movableBaseForView.hostingView = self.videoView;
279 [movableBaseForView setAutoresizingMask: NSViewNotSizable | NSViewMaxXMargin | NSViewMaxYMargin | NSViewMinXMargin | NSViewMinYMargin];
280 [previewView setAutoresizingMask: NSViewNotSizable | NSViewMaxXMargin | NSViewMaxYMargin | NSViewMinXMargin | NSViewMinYMargin];
Kateryna Kostiuk4db61092019-10-17 16:57:40 -0400281 connectingCalls = [[NSMutableDictionary alloc] init];
Alexandre Lisionc5148052015-03-04 15:10:35 -0500282}
283
Anthony Léonard66a050c2018-01-03 16:32:34 -0500284-(void) updateDurationLabel
285{
286 if (accountInfo_ != nil) {
287 auto* callModel = accountInfo_->callModel.get();
288 if (callModel->hasCall(callUid_)) {
289 auto& callStatus = callModel->getCall(callUid_).status;
290 if (callStatus != lrc::api::call::Status::ENDED &&
291 callStatus != lrc::api::call::Status::TERMINATING &&
292 callStatus != lrc::api::call::Status::INVALID) {
Kateryna Kostiuk9e9f7822019-09-27 11:29:45 -0400293 [timeSpentLabel setStringValue:@(callModel->getFormattedCallDuration(callUid_).c_str())];
Anthony Léonard66a050c2018-01-03 16:32:34 -0500294 return;
295 }
296 }
297 }
298
299 // If call is not running anymore or accountInfo_ is not set for any reason
300 // we stop the refresh loop
301 [refreshDurationTimer invalidate];
302 refreshDurationTimer = nil;
Anthony Léonard66a050c2018-01-03 16:32:34 -0500303}
304
Andreas Traczyk77a50d52018-05-08 17:47:31 -0400305-(void) updateCall
Alexandre Lisionc5148052015-03-04 15:10:35 -0500306{
Olivier Soldano994971f2017-12-05 16:30:12 -0500307 if (accountInfo_ == nil)
308 return;
Alexandre Lisionbb5dbcd2015-07-09 16:36:47 -0400309
Olivier Soldano994971f2017-12-05 16:30:12 -0500310 auto* callModel = accountInfo_->callModel.get();
311 if (not callModel->hasCall(callUid_)) {
312 return;
313 }
Alexandre Lision89edc6a2015-11-09 11:30:47 -0500314
Olivier Soldano994971f2017-12-05 16:30:12 -0500315 auto currentCall = callModel->getCall(callUid_);
316 NSLog(@"\n status %@ \n",@(lrc::api::call::to_string(currentCall.status).c_str()));
Anthony Léonard8ee26782018-01-05 10:23:04 -0500317 auto convIt = getConversationFromUid(convUid_, *accountInfo_->conversationModel);
Andreas Traczyk77a50d52018-05-08 17:47:31 -0400318 if (convIt != accountInfo_->conversationModel->allFilteredConversations().end()) {
319 NSString* bestName = bestNameForConversation(*convIt, *accountInfo_->conversationModel);
Kateryna Kostiuk9e9f7822019-09-27 11:29:45 -0400320 [contactNameLabel setStringValue:bestName];
Andreas Traczyk77a50d52018-05-08 17:47:31 -0400321 NSString* ringID = bestIDForConversation(*convIt, *accountInfo_->conversationModel);
322 if([bestName isEqualToString:ringID]) {
323 ringID = @"";
324 }
Kateryna Kostiuk9e9f7822019-09-27 11:29:45 -0400325 [contactIdLabel setStringValue:ringID];
Andreas Traczyk77a50d52018-05-08 17:47:31 -0400326 }
Kateryna Kostiuk9e9f7822019-09-27 11:29:45 -0400327 [self setupContactInfo:contactPhoto];
Andreas Traczyk77a50d52018-05-08 17:47:31 -0400328
Kateryna Kostiuk4db61092019-10-17 16:57:40 -0400329 confUid_ = convIt->confId;
330 [muteAudioButton setHidden:!confUid_.empty()];
331 [muteVideoButton setHidden:!confUid_.empty()];
332 [recordOnOffButton setHidden:!confUid_.empty()];
333
Anthony Léonard66a050c2018-01-03 16:32:34 -0500334 [timeSpentLabel setStringValue:@(callModel->getFormattedCallDuration(callUid_).c_str())];
Anthony Léonard66a050c2018-01-03 16:32:34 -0500335 if (refreshDurationTimer == nil)
336 refreshDurationTimer = [NSTimer scheduledTimerWithTimeInterval:1.0
337 target:self
338 selector:@selector(updateDurationLabel)
339 userInfo:nil
340 repeats:YES];
Kateryna Kostiuk81afce72018-10-30 16:40:50 -0400341 [self setBackground];
Alexandre Lisionc5148052015-03-04 15:10:35 -0500342
Olivier Soldano994971f2017-12-05 16:30:12 -0500343 using Status = lrc::api::call::Status;
Kateryna Kostiuk88a6ee82019-09-10 17:55:08 -0400344 if (currentCall.status == Status::PAUSED) {
345 [holdOnOffButton startBlinkAnimationfrom:[NSColor buttonBlinkColorColor] to:[NSColor whiteColor] scaleFactor: 1.0 duration: 1.5];
346 } else {
347 [holdOnOffButton stopBlinkAnimation];
348 }
Kateryna Kostiuk4db61092019-10-17 16:57:40 -0400349
Kateryna Kostiuka7404812019-10-28 12:24:46 -0400350 [self setUpButtons: currentCall isRecording: (callModel->isRecording([self getcallID]) || mediaModel->getAlwaysRecord())];
351
Kateryna Kostiuk4db61092019-10-17 16:57:40 -0400352 [videoView setShouldAcceptInteractions: currentCall.status == Status::IN_PROGRESS];
Kateryna Kostiuk9e9f7822019-09-27 11:29:45 -0400353 callStateLabel.stringValue = currentCall.status == Status::INCOMING_RINGING ? @"wants to talk to you" : @(to_string(currentCall.status).c_str());
354 loadingIndicator.hidden = (currentCall.status == Status::SEARCHING ||
355 currentCall.status == Status::CONNECTING ||
356 currentCall.status == Status::OUTGOING_RINGING) ? NO : YES;
357 pickUpButton.hidden = (currentCall.status == Status::INCOMING_RINGING) ? NO : YES;
358 callStateLabel.hidden = (currentCall.status == Status::IN_PROGRESS) ? YES : NO;
359 cancelCallButton.hidden = (currentCall.status == Status::IN_PROGRESS ||
360 currentCall.status == Status::PAUSED) ? YES : NO;
Kateryna Kostiuk4db61092019-10-17 16:57:40 -0400361 callingWidgetsContainer.hidden = (currentCall.status == Status::IN_PROGRESS) ? NO : YES;
Kateryna Kostiuk9e9f7822019-09-27 11:29:45 -0400362
Olivier Soldano994971f2017-12-05 16:30:12 -0500363 switch (currentCall.status) {
364 case Status::SEARCHING:
Olivier Soldano994971f2017-12-05 16:30:12 -0500365 case Status::CONNECTING:
Olivier Soldano994971f2017-12-05 16:30:12 -0500366 case Status::OUTGOING_RINGING:
Kateryna Kostiuk9e9f7822019-09-27 11:29:45 -0400367 case Status::INCOMING_RINGING:
368 [infoContainer setHidden: NO];
Andreas Traczyk77a50d52018-05-08 17:47:31 -0400369 [headerContainer setHidden:YES];
Kateryna Kostiuk2aa39232019-10-10 11:55:11 -0400370 [headerGradientView setHidden:YES];
Kateryna Kostiuk9e9f7822019-09-27 11:29:45 -0400371 [controlsPanel setHidden:YES];
Kateryna Kostiuk2aa39232019-10-10 11:55:11 -0400372 [controlsStackView setHidden:YES];
Kateryna Kostiuk4db61092019-10-17 16:57:40 -0400373 [self.videoMTKView fillWithBlack];
374 [self.previewView fillWithBlack];
375 [hidePreviewBackground setHidden:YES];
376 [self.previewView setHidden: YES];
377 [self.videoMTKView setHidden: YES];
378 self.previewView.stopRendering = true;
379 self.videoMTKView.stopRendering = true;
380 [backgroundImage setHidden:NO];
381 [bluerBackgroundEffect setHidden:NO];
Olivier Soldano994971f2017-12-05 16:30:12 -0500382 break;
Olivier Soldano994971f2017-12-05 16:30:12 -0500383 case Status::PAUSED:
Kateryna Kostiuk9e9f7822019-09-27 11:29:45 -0400384 [infoContainer setHidden: NO];
385 [headerContainer setHidden:NO];
Kateryna Kostiuk2aa39232019-10-10 11:55:11 -0400386 [headerGradientView setHidden:NO];
Kateryna Kostiuk9e9f7822019-09-27 11:29:45 -0400387 [controlsPanel setHidden:NO];
Kateryna Kostiuk2aa39232019-10-10 11:55:11 -0400388 [controlsStackView setHidden:NO];
Kateryna Kostiuk00dcbff2019-07-11 15:42:13 -0400389 [backgroundImage setHidden:NO];
Kateryna Kostiuk9e9f7822019-09-27 11:29:45 -0400390 [bluerBackgroundEffect setHidden:NO];
391 if(!currentCall.isAudioOnly) {
392 [self.videoMTKView fillWithBlack];
393 [self.previewView fillWithBlack];
394 [hidePreviewBackground setHidden:YES];
395 [self.previewView setHidden: YES];
396 [self.videoMTKView setHidden: YES];
397 self.previewView.stopRendering = true;
398 self.videoMTKView.stopRendering = true;
399 }
Kateryna Kostiuk00dcbff2019-07-11 15:42:13 -0400400 break;
Olivier Soldano994971f2017-12-05 16:30:12 -0500401 case Status::INACTIVE:
Kateryna Kostiuk00dcbff2019-07-11 15:42:13 -0400402 if(currentCall.isAudioOnly) {
403 [self setUpAudioOnlyView];
404 } else {
405 [self setUpVideoCallView];
406 }
407 break;
Olivier Soldano994971f2017-12-05 16:30:12 -0500408 case Status::IN_PROGRESS:
Kateryna Kostiuk9e9f7822019-09-27 11:29:45 -0400409 [headerContainer setHidden:NO];
Kateryna Kostiuk2aa39232019-10-10 11:55:11 -0400410 [headerGradientView setHidden:NO];
Kateryna Kostiuk9e9f7822019-09-27 11:29:45 -0400411 [controlsPanel setHidden:NO];
Kateryna Kostiuk2aa39232019-10-10 11:55:11 -0400412 [controlsStackView setHidden:NO];
Kateryna Kostiuk81afce72018-10-30 16:40:50 -0400413 if(currentCall.isAudioOnly) {
414 [self setUpAudioOnlyView];
415 } else {
416 [self setUpVideoCallView];
417 }
Andreas Traczyk77a50d52018-05-08 17:47:31 -0400418 break;
Olivier Soldano994971f2017-12-05 16:30:12 -0500419 case Status::CONNECTED:
Olivier Soldano994971f2017-12-05 16:30:12 -0500420 break;
421 case Status::ENDED:
422 case Status::TERMINATING:
423 case Status::INVALID:
Kateryna Kostiuk4db61092019-10-17 16:57:40 -0400424 case Status::PEER_BUSY:
425 case Status::TIMEOUT:
426 connectingCalls[@(callUid_.c_str())] = nil;
427 //[self cleanUp];
428 [self.delegate callFinished];
Olivier Soldano994971f2017-12-05 16:30:12 -0500429 break;
430 }
Alexandre Lision4d9905e2016-01-22 12:01:36 -0500431}
432
Kateryna Kostiuk81afce72018-10-30 16:40:50 -0400433-(void) setUpVideoCallView {
Kateryna Kostiuk9e9f7822019-09-27 11:29:45 -0400434 [previewView fillWithBlack];
435 [self.videoMTKView fillWithBlack];
Kateryna Kostiuk81afce72018-10-30 16:40:50 -0400436 [previewView setHidden: NO];
Kateryna Kostiuk00dcbff2019-07-11 15:42:13 -0400437 [self.videoMTKView setHidden:NO];
Kateryna Kostiuk0ee5cbd2019-10-08 12:14:33 -0400438 [hidePreviewBackground setHidden: self.previewView.stopRendering];
Kateryna Kostiuk81afce72018-10-30 16:40:50 -0400439 [bluerBackgroundEffect setHidden:YES];
Kateryna Kostiuk81afce72018-10-30 16:40:50 -0400440 [backgroundImage setHidden:YES];
Kateryna Kostiuk0ee5cbd2019-10-08 12:14:33 -0400441 self.previewView.stopRendering = false;
442 self.videoMTKView.stopRendering = false;
Kateryna Kostiuk81afce72018-10-30 16:40:50 -0400443}
444
445-(void) setUpAudioOnlyView {
Kateryna Kostiuk00dcbff2019-07-11 15:42:13 -0400446 [self.previewView setHidden: YES];
447 [self.videoMTKView setHidden: YES];
Kateryna Kostiuk9e9f7822019-09-27 11:29:45 -0400448 [hidePreviewBackground setHidden: YES];
449 [bluerBackgroundEffect setHidden:NO];
450 [backgroundImage setHidden:NO];
Kateryna Kostiuk81afce72018-10-30 16:40:50 -0400451}
452
453-(void) setBackground {
Andreas Traczyk77a50d52018-05-08 17:47:31 -0400454 auto* convModel = accountInfo_->conversationModel.get();
455 auto it = getConversationFromUid(convUid_, *convModel);
Kateryna Kostiuk81afce72018-10-30 16:40:50 -0400456 NSImage *image= [self getContactImageOfSize:120.0 withDefaultAvatar:NO];
457 if(image) {
Kateryna Kostiuk89398b62018-11-20 16:45:04 -0500458 CIImage * ciImage = [[CIImage alloc] initWithData:[image TIFFRepresentation]];
459 CIContext *context = [[CIContext alloc] init];
460 CIFilter *clamp = [CIFilter filterWithName:@"CIAffineClamp"];
461 [clamp setValue:[NSAffineTransform transform] forKey:@"inputTransform"];
462 [clamp setValue:ciImage forKey: kCIInputImageKey];
Kateryna Kostiuk81afce72018-10-30 16:40:50 -0400463 CIFilter* bluerFilter = [CIFilter filterWithName:@"CIGaussianBlur"];
464 [bluerFilter setDefaults];
Kateryna Kostiuk89398b62018-11-20 16:45:04 -0500465 [bluerFilter setValue:[NSNumber numberWithFloat: 9] forKey:@"inputRadius"];
466 [bluerFilter setValue:[clamp valueForKey:kCIOutputImageKey] forKey: kCIInputImageKey];
467 CIImage *result = [bluerFilter valueForKey:kCIOutputImageKey];
468 CGRect extent = [result extent];
469 CGImageRef cgImage = [context createCGImage:result fromRect: [ciImage extent]];
470 NSImage *bluredImage = [[NSImage alloc] initWithCGImage:cgImage size:NSSizeFromCGSize(CGSizeMake(image.size.width, image.size.height))];
471 backgroundImage.layer.contents = bluredImage;
Kateryna Kostiuk81afce72018-10-30 16:40:50 -0400472 [backgroundImage setHidden:NO];
Kateryna Kostiuk4db61092019-10-17 16:57:40 -0400473 [bluerBackgroundEffect setFillColor:[NSColor darkGrayColor]];
474 [bluerBackgroundEffect setAlphaValue:0.6];
Kateryna Kostiuk81afce72018-10-30 16:40:50 -0400475 } else {
Kateryna Kostiuk2aa39232019-10-10 11:55:11 -0400476 contactNameLabel.textColor = [NSColor textColor];
Kateryna Kostiuk2aa39232019-10-10 11:55:11 -0400477 contactIdLabel.textColor = [NSColor textColor];
478 callStateLabel.textColor = [NSColor textColor];
Kateryna Kostiuk9e9f7822019-09-27 11:29:45 -0400479 backgroundImage.layer.contents = nil;
Kateryna Kostiuk2aa39232019-10-10 11:55:11 -0400480 [bluerBackgroundEffect setFillColor:[NSColor windowBackgroundColor]];
Kateryna Kostiuk9e9f7822019-09-27 11:29:45 -0400481 [bluerBackgroundEffect setAlphaValue:1];
Kateryna Kostiuk81afce72018-10-30 16:40:50 -0400482 [backgroundImage setHidden:YES];
Andreas Traczyk77a50d52018-05-08 17:47:31 -0400483 }
484}
485
Kateryna Kostiuk133364b2018-10-31 11:36:08 -0400486-(NSImage *) getContactImageOfSize: (double) size withDefaultAvatar:(BOOL) shouldDrawDefault {
Kateryna Kostiuk81afce72018-10-30 16:40:50 -0400487 auto* convModel = accountInfo_->conversationModel.get();
488 auto convIt = getConversationFromUid(convUid_, *convModel);
489 if (convIt == convModel->allFilteredConversations().end()) {
490 return nil;
491 }
492 if(shouldDrawDefault) {
493 auto& imgManip = reinterpret_cast<Interfaces::ImageManipulationDelegate&>(GlobalInstances::pixmapManipulator());
494 QVariant photo = imgManip.conversationPhoto(*convIt, *accountInfo_, QSize(size, size), NO);
495 return QtMac::toNSImage(qvariant_cast<QPixmap>(photo));
496 }
497 auto contact = accountInfo_->contactModel->getContact(convIt->participants[0]);
498 NSData *imageData = [[NSData alloc] initWithBase64EncodedString:@(contact.profileInfo.avatar.c_str()) options:NSDataBase64DecodingIgnoreUnknownCharacters];
499 return [[NSImage alloc] initWithData:imageData];
500}
501
Kateryna Kostiuk81afce72018-10-30 16:40:50 -0400502-(void) setupContactInfo:(NSImageView*)imageView
503{
504 [imageView setImage: [self getContactImageOfSize:120.0 withDefaultAvatar:YES]];
505}
506
Olivier Soldano994971f2017-12-05 16:30:12 -0500507-(void)collapseRightView
508{
509 NSView *right = [[splitView subviews] objectAtIndex:1];
510 NSView *left = [[splitView subviews] objectAtIndex:0];
511 NSRect leftFrame = [left frame];
512 [right setHidden:YES];
513 [splitView display];
514}
515
Alexandre Lisionc5148052015-03-04 15:10:35 -0500516-(void) connectVideoSignals
517{
Olivier Soldano994971f2017-12-05 16:30:12 -0500518 if (accountInfo_ == nil)
519 return;
Kateryna Kostiuk5d90c3b2019-07-18 12:03:52 -0400520 [self connectRenderer];
Alexandre Lisionc5148052015-03-04 15:10:35 -0500521}
522
Kateryna Kostiuk5d90c3b2019-07-18 12:03:52 -0400523-(void) connectRenderer
Alexandre Lisionc5148052015-03-04 15:10:35 -0500524{
Kateryna Kostiuk5d90c3b2019-07-18 12:03:52 -0400525 QObject::disconnect(renderConnections.frameUpdated);
526 QObject::disconnect(renderConnections.stopped);
527 QObject::disconnect(renderConnections.started);
528 renderConnections.started =
529 QObject::connect(mediaModel,
530 &lrc::api::AVModel::rendererStarted,
531 [=](const std::string& id) {
532 if (id == lrc::api::video::PREVIEW_RENDERER_ID) {
533 [self.previewView setHidden:NO];
Kateryna Kostiuk4db61092019-10-17 16:57:40 -0400534 [hidePreviewBackground setHidden: NO];
Kateryna Kostiuk5d90c3b2019-07-18 12:03:52 -0400535 self.previewView.stopRendering = false;
Kateryna Kostiuk4db61092019-10-17 16:57:40 -0400536 } else if ([self isCurrentCall: id]) {
Kateryna Kostiuk5d90c3b2019-07-18 12:03:52 -0400537 [self mouseIsMoving: NO];
538 self.videoMTKView.stopRendering = false;
539 [self.videoMTKView setHidden:NO];
540 [bluerBackgroundEffect setHidden:YES];
541 [backgroundImage setHidden:YES];
Kateryna Kostiuk5d90c3b2019-07-18 12:03:52 -0400542 }
Alexandre Lisionc5148052015-03-04 15:10:35 -0500543 });
Kateryna Kostiuk5d90c3b2019-07-18 12:03:52 -0400544 renderConnections.stopped =
545 QObject::connect(mediaModel,
546 &lrc::api::AVModel::rendererStopped,
547 [=](const std::string& id) {
Kateryna Kostiuk5d90c3b2019-07-18 12:03:52 -0400548 if (id == lrc::api::video::PREVIEW_RENDERER_ID) {
549 [self.previewView setHidden:YES];
550 self.previewView.stopRendering = true;
Kateryna Kostiuk4db61092019-10-17 16:57:40 -0400551 } else if ([self isCurrentCall: id]) {
Kateryna Kostiuk5d90c3b2019-07-18 12:03:52 -0400552 [self mouseIsMoving: YES];
553 self.videoMTKView.stopRendering = true;
554 [self.videoMTKView setHidden:YES];
555 [bluerBackgroundEffect setHidden:NO];
556 [backgroundImage setHidden:NO];
Kateryna Kostiuk00dcbff2019-07-11 15:42:13 -0400557 }
Kateryna Kostiuk5d90c3b2019-07-18 12:03:52 -0400558 });
559 renderConnections.frameUpdated =
560 QObject::connect(mediaModel,
561 &lrc::api::AVModel::frameUpdated,
562 [=](const std::string& id) {
563 if (id == lrc::api::video::PREVIEW_RENDERER_ID) {
564 auto renderer = &mediaModel->getRenderer(lrc::api::video::PREVIEW_RENDERER_ID);
565 if(!renderer->isRendering()) {
566 return;
567 }
Kateryna Kostiuk4db61092019-10-17 16:57:40 -0400568 [hidePreviewBackground setHidden: self.previewView.stopRendering];
Kateryna Kostiuk5d90c3b2019-07-18 12:03:52 -0400569 [self renderer: renderer renderFrameForPreviewView:previewView];
Kateryna Kostiuk4db61092019-10-17 16:57:40 -0400570 } else if ([self isCurrentCall: id]) {
Kateryna Kostiuk5d90c3b2019-07-18 12:03:52 -0400571 auto renderer = &mediaModel->getRenderer(id);
572 if(!renderer->isRendering()) {
573 return;
574 }
575 [self renderer:renderer renderFrameForDistantView: self.videoMTKView];
Kateryna Kostiuk732b4062018-11-28 09:59:28 -0500576 }
Alexandre Lisionc5148052015-03-04 15:10:35 -0500577 });
578}
579
Kateryna Kostiuk5d90c3b2019-07-18 12:03:52 -0400580-(void) renderer: (const lrc::api::video::Renderer*)renderer renderFrameForPreviewView:(CallMTKView*) view
Alexandre Lisionc5148052015-03-04 15:10:35 -0500581{
Kateryna Kostiuk00dcbff2019-07-11 15:42:13 -0400582 @autoreleasepool {
583 auto framePtr = renderer->currentAVFrame();
584 auto frame = framePtr.get();
585 if(!frame || !frame->width || !frame->height) {
586 return;
587 }
588 auto frameSize = CGSizeMake(frame->width, frame->height);
589 auto rotation = 0;
590 if (frame->data[3] != NULL && (CVPixelBufferRef)frame->data[3]) {
591 [view renderWithPixelBuffer:(CVPixelBufferRef)frame->data[3]
592 size: frameSize
593 rotation: rotation
594 fillFrame: true];
595 return;
596 }
597 else if (CVPixelBufferRef pixelBuffer = [self getBufferForPreviewFromFrame:frame]) {
598 [view renderWithPixelBuffer: pixelBuffer
599 size: frameSize
600 rotation: rotation
601 fillFrame: true];
602 }
603 }
Alexandre Lisionc5148052015-03-04 15:10:35 -0500604}
605
Kateryna Kostiuk5d90c3b2019-07-18 12:03:52 -0400606-(void) renderer: (const lrc::api::video::Renderer*)renderer renderFrameForDistantView:(CallMTKView*) view
Anthony Léonard14e7bf32017-06-08 08:13:16 -0400607{
Kateryna Kostiuk00dcbff2019-07-11 15:42:13 -0400608 @autoreleasepool {
609 auto framePtr = renderer->currentAVFrame();
610 auto frame = framePtr.get();
611 if(!frame || !frame->width || !frame->height) {
612 return;
613 }
614 auto frameSize = CGSizeMake(frame->width, frame->height);
615 auto rotation = 0;
616 if (auto matrix = av_frame_get_side_data(frame, AV_FRAME_DATA_DISPLAYMATRIX)) {
617 const int32_t* data = reinterpret_cast<int32_t*>(matrix->data);
618 rotation = av_display_rotation_get(data);
619 }
620 if (frame->data[3] != NULL && (CVPixelBufferRef)frame->data[3]) {
621 [view renderWithPixelBuffer: (CVPixelBufferRef)frame->data[3]
622 size: frameSize
623 rotation: rotation
624 fillFrame: false];
625 }
626 if (CVPixelBufferRef pixelBuffer = [self getBufferForDistantViewFromFrame:frame]) {
627 [view renderWithPixelBuffer: pixelBuffer
628 size: frameSize
629 rotation: rotation
630 fillFrame: false];
631 }
Kateryna Kostiukbcf24e92018-11-20 10:09:06 -0500632 }
Anthony Léonard14e7bf32017-06-08 08:13:16 -0400633}
634
Kateryna Kostiuk00dcbff2019-07-11 15:42:13 -0400635-(CVPixelBufferRef) getBufferForPreviewFromFrame:(const AVFrame*)frame {
Kateryna Kostiukc5fd3b92019-07-24 10:12:44 -0400636 [VideoCommon fillPixelBuffr:&pixelBufferPreview fromFrame:frame bufferPool:&pixelBufferPoolPreview];
637 CVPixelBufferRef buffer = pixelBufferPreview;
638 return buffer;
Kateryna Kostiuk00dcbff2019-07-11 15:42:13 -0400639}
640
641-(CVPixelBufferRef) getBufferForDistantViewFromFrame:(const AVFrame*)frame {
Kateryna Kostiukc5fd3b92019-07-24 10:12:44 -0400642 [VideoCommon fillPixelBuffr:&pixelBufferDistantView fromFrame:frame bufferPool:&pixelBufferPoolDistantView];
643 CVPixelBufferRef buffer = pixelBufferDistantView;
644 return buffer;
Kateryna Kostiuk00dcbff2019-07-11 15:42:13 -0400645}
646
Alexandre Lisionc5148052015-03-04 15:10:35 -0500647- (void) initFrame
648{
649 [self.view setFrame:self.view.superview.bounds];
650 [self.view setHidden:YES];
651 self.view.layer.position = self.view.frame.origin;
Alexandre Lisionbb5dbcd2015-07-09 16:36:47 -0400652 [self collapseRightView];
Alexandre Lisionc5148052015-03-04 15:10:35 -0500653}
654
655# pragma private IN/OUT animations
656
Olivier Soldano994971f2017-12-05 16:30:12 -0500657-(void)uncollapseRightView
658{
659 NSView *left = [[splitView subviews] objectAtIndex:0];
660 NSView *right = [[splitView subviews] objectAtIndex:1];
661 [right setHidden:NO];
662
663 CGFloat dividerThickness = [splitView dividerThickness];
664
665 // get the different frames
666 NSRect leftFrame = [left frame];
667 NSRect rightFrame = [right frame];
668
669 leftFrame.size.width = (leftFrame.size.width - rightFrame.size.width - dividerThickness);
670 rightFrame.origin.x = leftFrame.size.width + dividerThickness;
671 [left setFrameSize:leftFrame.size];
672 [right setFrame:rightFrame];
673 [splitView display];
674
675 [self.chatVC takeFocus];
676}
677
Alexandre Lisionc5148052015-03-04 15:10:35 -0500678-(void) cleanUp
679{
Anthony Léonard5c287642018-02-21 15:54:19 -0500680 if(self.splitView.isInFullScreenMode)
681 [self.splitView exitFullScreenModeWithOptions:nil];
Kateryna Kostiuk5d90c3b2019-07-18 12:03:52 -0400682 QObject::disconnect(renderConnections.frameUpdated);
683 QObject::disconnect(renderConnections.started);
684 QObject::disconnect(renderConnections.stopped);
Anthony Léonardc0013bb2018-02-20 16:23:02 -0500685 QObject::disconnect(self.messageConnection);
Alexandre Lision883719f2015-10-22 17:37:45 -0400686
Alexandre Lision4baba4c2016-02-11 13:00:57 -0500687 [self.chatButton setPressed:NO];
Alexandre Lision883719f2015-10-22 17:37:45 -0400688 [self collapseRightView];
Alexandre Lision57227772016-01-15 17:19:36 -0500689
Alexandre Lision57227772016-01-15 17:19:36 -0500690 [timeSpentLabel setStringValue:@""];
Kateryna Kostiuk9e9f7822019-09-27 11:29:45 -0400691 [contactIdLabel setStringValue:@""];
692 [contactNameLabel setStringValue:@""];
693 [contactPhoto setImage:nil];
Kateryna Kostiuk81afce72018-10-30 16:40:50 -0400694 //background view
695 [bluerBackgroundEffect setHidden:NO];
696 [backgroundImage setHidden:NO];
Kateryna Kostiuk9e9f7822019-09-27 11:29:45 -0400697 backgroundImage.layer.contents = nil;
Kateryna Kostiuk00dcbff2019-07-11 15:42:13 -0400698 [self.previewView setHidden:YES];
699 [self.videoMTKView setHidden:YES];
Kateryna Kostiuk9e9f7822019-09-27 11:29:45 -0400700
701 contactNameLabel.textColor = [NSColor highlightColor];
702 contactNameLabel.textColor = [NSColor highlightColor];
703 contactIdLabel.textColor = [NSColor highlightColor];
704 callStateLabel.textColor = [NSColor highlightColor];
Alexandre Lisionc5148052015-03-04 15:10:35 -0500705}
706
Kateryna Kostiuk4db61092019-10-17 16:57:40 -0400707/*
708 * update ui
709*/
710
Andreas Traczykecdc4fa2018-03-22 16:11:47 -0400711-(void) setupCallView
712{
713 if (accountInfo_ == nil)
714 return;
715
716 auto* callModel = accountInfo_->callModel.get();
Andreas Traczykecdc4fa2018-03-22 16:11:47 -0400717
Andreas Traczykecdc4fa2018-03-22 16:11:47 -0400718 /* check if text media is already present */
719 if(not callModel->hasCall(callUid_))
720 return;
721
Kateryna Kostiuk4db61092019-10-17 16:57:40 -0400722 // when call comes in we want to show the controls/header
723 [self mouseIsMoving: YES];
Andreas Traczykecdc4fa2018-03-22 16:11:47 -0400724 [loadingIndicator setAnimates:YES];
Kateryna Kostiuk4db61092019-10-17 16:57:40 -0400725 auto currentCall = callModel->getCall([self getcallID]);
Kateryna Kostiuk4db61092019-10-17 16:57:40 -0400726 [previewView setHidden: YES];
727 [self setUpPreviewFrame];
728 [self updatePendingCalls];
Andreas Traczyk77a50d52018-05-08 17:47:31 -0400729 [self updateCall];
Andreas Traczykecdc4fa2018-03-22 16:11:47 -0400730}
731
732-(void) showWithAnimation:(BOOL)animate
733{
734 if (!animate) {
735 [self.view setHidden:NO];
736 [self setupCallView];
737 return;
738 }
739
740 CGRect frame = CGRectOffset(self.view.superview.bounds, -self.view.superview.bounds.size.width, 0);
741 [self.view setHidden:NO];
742
743 [CATransaction begin];
744 CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"position"];
745 [animation setFromValue:[NSValue valueWithPoint:frame.origin]];
746 [animation setToValue:[NSValue valueWithPoint:self.view.superview.bounds.origin]];
747 [animation setDuration:0.2f];
748 [animation setTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseIn]];
749 [CATransaction setCompletionBlock:^{
750 [self setupCallView];
751 }];
752
753 [self.view.layer addAnimation:animation forKey:animation.keyPath];
754 [CATransaction commit];
755}
756
757-(void) hideWithAnimation:(BOOL)animate
Alexandre Lisionc5148052015-03-04 15:10:35 -0500758{
Alexandre Lisionc5148052015-03-04 15:10:35 -0500759 if(self.view.frame.origin.x < 0) {
Alexandre Lisionc5148052015-03-04 15:10:35 -0500760 return;
761 }
762
Andreas Traczykecdc4fa2018-03-22 16:11:47 -0400763 if (!animate) {
764 [self.view setHidden:YES];
765 return;
766 }
767
Alexandre Lisionc5148052015-03-04 15:10:35 -0500768 CGRect frame = CGRectOffset(self.view.frame, -self.view.frame.size.width, 0);
769 [CATransaction begin];
770 CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"position"];
771 [animation setFromValue:[NSValue valueWithPoint:self.view.frame.origin]];
772 [animation setToValue:[NSValue valueWithPoint:frame.origin]];
773 [animation setDuration:0.2f];
Alexandre Lision22d615b2016-01-25 12:54:34 -0500774 [animation setTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut]];
Alexandre Lisionc5148052015-03-04 15:10:35 -0500775
776 [CATransaction setCompletionBlock:^{
777 [self.view setHidden:YES];
Alexandre Lisionef6333a2015-03-24 12:30:31 -0400778 // first make sure everything is disconnected
Alexandre Lisionc5148052015-03-04 15:10:35 -0500779 [self cleanUp];
Olivier Soldano994971f2017-12-05 16:30:12 -0500780// if (currentCall_) {
781// [self animateIn];
782// }
Alexandre Lisionc5148052015-03-04 15:10:35 -0500783 }];
784 [self.view.layer addAnimation:animation forKey:animation.keyPath];
Alexandre Lisiona1c6d752015-06-23 12:27:38 -0400785
786 [self.view.layer setPosition:frame.origin];
Alexandre Lisionc5148052015-03-04 15:10:35 -0500787 [CATransaction commit];
788}
789
790/**
791 * Debug purpose
792 */
793-(void) dumpFrame:(CGRect) frame WithName:(NSString*) name
794{
795 NSLog(@"frame %@ : %f %f %f %f \n\n",name ,frame.origin.x, frame.origin.y, frame.size.width, frame.size.height);
796}
797
Alexandre Lision58cab672015-06-09 15:25:40 -0400798#pragma mark - Button methods
799
Alexandre Lisionc5148052015-03-04 15:10:35 -0500800- (IBAction)hangUp:(id)sender {
Olivier Soldano994971f2017-12-05 16:30:12 -0500801 if (accountInfo_ == nil)
802 return;
803
804 auto* callModel = accountInfo_->callModel.get();
Kateryna Kostiuk4db61092019-10-17 16:57:40 -0400805 callModel->hangUp([self getcallID]);
Alexandre Lisionc5148052015-03-04 15:10:35 -0500806}
807
808- (IBAction)accept:(id)sender {
Olivier Soldano994971f2017-12-05 16:30:12 -0500809 if (accountInfo_ == nil)
810 return;
811
Anthony Léonardf584cc52018-01-19 12:00:19 -0500812 // If we accept a conversation with a non trusted contact, we first accept it
813 auto convIt = getConversationFromUid(convUid_, *accountInfo_->conversationModel.get());
814 if (convIt != accountInfo_->conversationModel->allFilteredConversations().end()) {
815 auto& contact = accountInfo_->contactModel->getContact(convIt->participants[0]);
816 if (contact.profileInfo.type == lrc::api::profile::Type::PENDING)
817 accountInfo_->conversationModel->makePermanent(convUid_);
818 }
819
Olivier Soldano994971f2017-12-05 16:30:12 -0500820 auto* callModel = accountInfo_->callModel.get();
821
822 callModel->accept(callUid_);
Alexandre Lisionc5148052015-03-04 15:10:35 -0500823}
824
825- (IBAction)toggleRecording:(id)sender {
Olivier Soldano994971f2017-12-05 16:30:12 -0500826 if (accountInfo_ == nil)
827 return;
828
829 auto* callModel = accountInfo_->callModel.get();
Olivier Soldano994971f2017-12-05 16:30:12 -0500830 callModel->toggleAudioRecord(callUid_);
Kateryna Kostiuk88a6ee82019-09-10 17:55:08 -0400831 if (callModel->isRecording(callUid_)) {
832 [recordOnOffButton startBlinkAnimationfrom:[NSColor buttonBlinkColorColor] to:[NSColor whiteColor] scaleFactor: 1 duration: 1.5];
833 } else {
834 [recordOnOffButton stopBlinkAnimation];
835 }
Alexandre Lisionc5148052015-03-04 15:10:35 -0500836}
837
838- (IBAction)toggleHold:(id)sender {
Olivier Soldano994971f2017-12-05 16:30:12 -0500839 if (accountInfo_ == nil)
840 return;
841
842 auto* callModel = accountInfo_->callModel.get();
Kateryna Kostiuk4db61092019-10-17 16:57:40 -0400843 callModel->togglePause([self getcallID]);
Alexandre Lisionc5148052015-03-04 15:10:35 -0500844}
845
Alexandre Lision62005312016-01-28 15:55:16 -0500846- (IBAction)showDialpad:(id)sender {
847 AppDelegate* appDelegate = (AppDelegate *)[[NSApplication sharedApplication] delegate];
848 [appDelegate showDialpad];
849}
850
Alexandre Lision58cab672015-06-09 15:25:40 -0400851-(IBAction)toggleChat:(id)sender;
852{
853 BOOL rightViewCollapsed = [[self splitView] isSubviewCollapsed:[[[self splitView] subviews] objectAtIndex: 1]];
854 if (rightViewCollapsed) {
855 [self uncollapseRightView];
Alexandre Lision58cab672015-06-09 15:25:40 -0400856 } else {
857 [self collapseRightView];
858 }
Alexandre Lision62005312016-01-28 15:55:16 -0500859 [chatButton setPressed:rightViewCollapsed];
Alexandre Lision58cab672015-06-09 15:25:40 -0400860}
861
Alexandre Lisiond18fa272015-06-15 11:18:03 -0400862- (IBAction)muteAudio:(id)sender
863{
Olivier Soldano994971f2017-12-05 16:30:12 -0500864 if (accountInfo_ == nil)
865 return;
866
867 auto* callModel = accountInfo_->callModel.get();
Kateryna Kostiuk54d76972018-06-07 15:45:07 -0400868 auto currentCall = callModel->getCall(callUid_);
869 if (currentCall.audioMuted) {
870 muteAudioButton.image = [NSImage imageNamed:@"ic_action_audio.png"];
871 } else {
872 muteAudioButton.image = [NSImage imageNamed:@"ic_action_mute_audio.png"];
873 }
Olivier Soldano994971f2017-12-05 16:30:12 -0500874 callModel->toggleMedia(callUid_, lrc::api::NewCallModel::Media::AUDIO);
Alexandre Lisiond18fa272015-06-15 11:18:03 -0400875}
876
877- (IBAction)muteVideo:(id)sender
878{
Olivier Soldano994971f2017-12-05 16:30:12 -0500879 if (accountInfo_ == nil)
880 return;
Olivier Soldano994971f2017-12-05 16:30:12 -0500881 auto* callModel = accountInfo_->callModel.get();
Kateryna Kostiuk54d76972018-06-07 15:45:07 -0400882 auto currentCall = callModel->getCall(callUid_);
883 if(!currentCall.isAudioOnly) {
884 if (currentCall.videoMuted) {
885 muteVideoButton.image = [NSImage imageNamed:@"ic_action_video.png"];
886 } else {
887 muteVideoButton.image = [NSImage imageNamed:@"ic_action_mute_video.png"];
888 }
889 }
Olivier Soldano994971f2017-12-05 16:30:12 -0500890 callModel->toggleMedia(callUid_, lrc::api::NewCallModel::Media::VIDEO);
Alexandre Lisiond18fa272015-06-15 11:18:03 -0400891}
Alexandre Lision883719f2015-10-22 17:37:45 -0400892
893- (IBAction)toggleTransferView:(id)sender {
Kateryna Kostiuk1ba5a012018-12-12 17:07:38 -0500894
Alexandre Lisionf23ec5a2015-07-16 11:24:06 -0400895}
Alexandre Lisiond18fa272015-06-15 11:18:03 -0400896
Alexandre Lision89edc6a2015-11-09 11:30:47 -0500897- (IBAction)toggleAddParticipantView:(id)sender {
Kateryna Kostiuk4db61092019-10-17 16:57:40 -0400898 if (brokerPopoverVC != nullptr) {
899 [brokerPopoverVC performClose:self];
900 brokerPopoverVC = NULL;
901 } else {
902 auto* contactSelectorVC = [[ChooseContactVC alloc] initWithNibName:@"ChooseContactVC" bundle:nil];
903 auto* convModel = accountInfo_->conversationModel.get();
904 [contactSelectorVC setConversationModel:convModel andCurrentConversation: convUid_];
905 contactSelectorVC.delegate = self;
906 brokerPopoverVC = [[NSPopover alloc] init];
907 [brokerPopoverVC setContentSize:contactSelectorVC.view.frame.size];
908 [brokerPopoverVC setContentViewController:contactSelectorVC];
909 [brokerPopoverVC setAnimates:YES];
910 [brokerPopoverVC setBehavior:NSPopoverBehaviorTransient];
911 [brokerPopoverVC setDelegate:self];
912 [brokerPopoverVC showRelativeToRect:[sender bounds] ofView:sender preferredEdge:NSMinYEdge];
913 }
Alexandre Lision89edc6a2015-11-09 11:30:47 -0500914}
Kateryna Kostiuk4db61092019-10-17 16:57:40 -0400915
Kateryna Kostiuk6891d4f2019-09-19 17:44:33 -0400916- (IBAction)hidePreview:(id)sender {
917 CGRect previewFrame = previewView.frame;
Kateryna Kostiuk2aa39232019-10-10 11:55:11 -0400918 CGRect newPreviewFrame;//, bcHidePreviewFrame;
919 if (previewFrame.size.width > HIDE_PREVIEW_BUTTON_SIZE) {
Kateryna Kostiuk6891d4f2019-09-19 17:44:33 -0400920 self.movableBaseForView.movable = false;
921 newPreviewFrame = self.getVideoPreviewCollapsedSize;
Kateryna Kostiuk6891d4f2019-09-19 17:44:33 -0400922 hidePreviewButton.image = [NSImage imageNamed: NSImageNameTouchBarEnterFullScreenTemplate];
923 } else {
924 self.movableBaseForView.movable = true;
925 newPreviewFrame = CGRectMake(0, 0, PREVIEW_WIDTH, PREVIEW_HEIGHT);
Kateryna Kostiuk6891d4f2019-09-19 17:44:33 -0400926 hidePreviewButton.image = [NSImage imageNamed: NSImageNameTouchBarExitFullScreenTemplate];
927 }
928 [NSAnimationContext runAnimationGroup:^(NSAnimationContext * _Nonnull context) {
929 context.duration = 0.2f;
930 context.timingFunction = [CAMediaTimingFunction functionWithName: kCAMediaTimingFunctionEaseOut];
931 previewView.animator.frame = newPreviewFrame;
932 } completionHandler: nil];
Kateryna Kostiuk6891d4f2019-09-19 17:44:33 -0400933}
Alexandre Lision89edc6a2015-11-09 11:30:47 -0500934
Alexandre Lision58cab672015-06-09 15:25:40 -0400935#pragma mark - NSSplitViewDelegate
936
937/* Return YES if the subview should be collapsed because the user has double-clicked on an adjacent divider. If a split view has a delegate, and the delegate responds to this message, it will be sent once for the subview before a divider when the user double-clicks on that divider, and again for the subview after the divider, but only if the delegate returned YES when sent -splitView:canCollapseSubview: for the subview in question. When the delegate indicates that both subviews should be collapsed NSSplitView's behavior is undefined.
938 */
939- (BOOL)splitView:(NSSplitView *)splitView shouldCollapseSubview:(NSView *)subview forDoubleClickOnDividerAtIndex:(NSInteger)dividerIndex;
940{
941 NSView* rightView = [[splitView subviews] objectAtIndex:1];
942 return ([subview isEqual:rightView]);
943}
944
945
946- (BOOL)splitView:(NSSplitView *)splitView canCollapseSubview:(NSView *)subview;
947{
948 NSView* rightView = [[splitView subviews] objectAtIndex:1];
949 return ([subview isEqual:rightView]);
950}
951
952
Alexandre Lisiona1eee3c2015-08-10 13:44:51 -0400953# pragma mark - CallnDelegate
Alexandre Lision58cab672015-06-09 15:25:40 -0400954
955- (void) callShouldToggleFullScreen
956{
957 if(self.splitView.isInFullScreenMode)
958 [self.splitView exitFullScreenModeWithOptions:nil];
959 else {
960 NSApplicationPresentationOptions options = NSApplicationPresentationDefault +NSApplicationPresentationAutoHideDock +
961 NSApplicationPresentationAutoHideMenuBar + NSApplicationPresentationAutoHideToolbar;
962 NSDictionary *opts = [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithUnsignedInt:options],
963 NSFullScreenModeApplicationPresentationOptions, nil];
964
965 [self.splitView enterFullScreenMode:[NSScreen mainScreen] withOptions:opts];
966 }
967}
968
Alexandre Lisiona1eee3c2015-08-10 13:44:51 -0400969-(void) mouseIsMoving:(BOOL) move
970{
Kateryna Kostiuk2aa39232019-10-10 11:55:11 -0400971 [[controlsPanel animator] setAlphaValue:move];// fade out
972 [[controlsStackView animator] setAlphaValue:move];
Alexandre Lisiona1eee3c2015-08-10 13:44:51 -0400973 [[headerContainer animator] setAlphaValue:move];
Kateryna Kostiuk2aa39232019-10-10 11:55:11 -0400974 [[headerGradientView animator] setAlphaValue:move];
Alexandre Lisiona1eee3c2015-08-10 13:44:51 -0400975}
976
Kateryna Kostiuk89398b62018-11-20 16:45:04 -0500977- (BOOL)splitView:(NSSplitView *)splitView shouldHideDividerAtIndex:(NSInteger)dividerIndex
978{
979 return YES;
980}
981
Kateryna Kostiuk5d90c3b2019-07-18 12:03:52 -0400982-(void) screenShare {
983 NSScreen *mainScreen = [NSScreen mainScreen];
984 NSRect screenFrame = mainScreen.frame;
985 QRect captureRect = QRect(screenFrame.origin.x, screenFrame.origin.y, screenFrame.size.width, screenFrame.size.height);
986 mediaModel->setDisplay(0, screenFrame.origin.x, screenFrame.origin.y, screenFrame.size.width, screenFrame.size.height);
987}
988-(void) switchToDevice:(int)deviceID {
989 auto devices = mediaModel->getDevices();
990 auto device = devices[deviceID];
991 mediaModel->switchInputTo(device);
992}
993
994-(std::vector<std::string>) getDeviceList {
Kateryna Kostiuk64533102019-10-29 15:34:29 -0400995 std::vector<std::string> devicesVector;
996 for (auto device : mediaModel->getDevices()) {
997 try {
998 auto settings = mediaModel->getDeviceSettings(device);
999 devicesVector.emplace_back(settings.name);
1000 } catch (...) {}
1001 }
1002 return devicesVector;
Kateryna Kostiuk5d90c3b2019-07-18 12:03:52 -04001003}
1004
Kateryna Kostiuk32cf6be2019-10-28 12:22:45 -04001005-(NSString*) getDefaultDeviceName {
1006 auto type = mediaModel->getCurrentRenderedDevice(callUid_).type;
1007 switch (type) {
1008 case lrc::api::video::DeviceType::CAMERA:
Kateryna Kostiuk64533102019-10-29 15:34:29 -04001009 try {
1010 auto device = mediaModel->getCurrentRenderedDevice(callUid_).name;
1011 auto settings = mediaModel->getDeviceSettings(device);
1012 return @(settings.name.c_str());
1013 } catch (...) {}
Kateryna Kostiuk32cf6be2019-10-28 12:22:45 -04001014 case lrc::api::video::DeviceType::DISPLAY:
1015 return NSLocalizedString(@"Share screen", @"Contextual menu entry");
1016 default:
1017 return @"";
1018 }
1019}
1020
Kateryna Kostiuk5d90c3b2019-07-18 12:03:52 -04001021-(void) switchToFile:(std::string)uri {
1022 mediaModel->setInputFile(QUrl::fromLocalFile(uri.c_str()).toLocalFile().toStdString());
1023}
1024
Kateryna Kostiuk6891d4f2019-09-19 17:44:33 -04001025-(CGRect) getVideoPreviewCollapsedSize {
1026 CGPoint origin;
1027 switch (movableBaseForView.closestCorner) {
1028 case TOP_LEFT:
Kateryna Kostiuk2aa39232019-10-10 11:55:11 -04001029 origin = CGPointMake(0, movableBaseForView.frame.size.height - HIDE_PREVIEW_BUTTON_SIZE);
Kateryna Kostiuk6891d4f2019-09-19 17:44:33 -04001030 break;
1031 case BOTTOM_LEFT:
1032 origin = CGPointMake(0, 0);
1033 break;
1034 case TOP_RIGHT:
Kateryna Kostiuk2aa39232019-10-10 11:55:11 -04001035 origin = CGPointMake(movableBaseForView.frame.size.width - HIDE_PREVIEW_BUTTON_SIZE, movableBaseForView.frame.size.height - HIDE_PREVIEW_BUTTON_SIZE);
Kateryna Kostiuk6891d4f2019-09-19 17:44:33 -04001036 break;
1037 case BOTTOM_RIGHT:
Kateryna Kostiuk2aa39232019-10-10 11:55:11 -04001038 origin = CGPointMake(movableBaseForView.frame.size.width - HIDE_PREVIEW_BUTTON_SIZE, 0);
Kateryna Kostiuk6891d4f2019-09-19 17:44:33 -04001039 break;
1040 }
Kateryna Kostiuk2aa39232019-10-10 11:55:11 -04001041 return CGRectMake(origin.x, origin.y, HIDE_PREVIEW_BUTTON_SIZE, HIDE_PREVIEW_BUTTON_SIZE);
Kateryna Kostiuk6891d4f2019-09-19 17:44:33 -04001042}
1043
Kateryna Kostiuk2aa39232019-10-10 11:55:11 -04001044-(CGRect) frameForExpendPreviewButton {
1045 CGPoint origin = CGPointMake(self.previewView.frame.size.width - HIDE_PREVIEW_BUTTON_SIZE,
1046 self.previewView.frame.size.height - HIDE_PREVIEW_BUTTON_SIZE);
1047 return CGRectMake(origin.x, origin.y, HIDE_PREVIEW_BUTTON_SIZE, HIDE_PREVIEW_BUTTON_SIZE);
Kateryna Kostiuk6891d4f2019-09-19 17:44:33 -04001048}
1049
Kateryna Kostiuk4db61092019-10-17 16:57:40 -04001050# pragma mark -ChooseContactVCDelegate
1051
1052-(void)callToContact:(std::string)contactUri convUID:(std::string)convID {
1053 if (brokerPopoverVC != nullptr) {
1054 [brokerPopoverVC performClose:self];
1055 brokerPopoverVC = NULL;
1056 }
1057 auto* callModel = accountInfo_->callModel.get();
1058 auto currentCall = callModel->getCall([self getcallID]);
1059 auto* convModel = accountInfo_->conversationModel.get();
1060 auto newCall = callModel->callAndAddParticipant(contactUri,
1061 [self getcallID],
1062 currentCall.isAudioOnly);
1063 [self addPreviewForContactUri:contactUri call: newCall];
1064}
1065
1066-(void)joinCall:(std::string)callId {
1067 if (brokerPopoverVC != nullptr) {
1068 [brokerPopoverVC performClose:self];
1069 brokerPopoverVC = NULL;
1070 }
1071 auto* callModel = accountInfo_->callModel.get();
1072 callModel->joinCalls([self getcallID], callId);
1073}
1074
1075# pragma mark -CallInConferenceVCDelegate
1076
1077-(void)removePreviewForContactUri:(std::string)uri forCall:(std::string) callId {
1078 NSMutableDictionary * calls = connectingCalls[@(callId.c_str())];
1079 if (!calls) {
1080 return;
1081 }
1082 NSViewController *callView = calls[@(uri.c_str())];
1083 if (!callView) {
1084 return;
1085 }
1086 calls[@(uri.c_str())] = nil;
1087 [self.callingWidgetsContainer removeView:callView.view];
1088}
1089
1090-(void)addPreviewForContactUri:(std::string)uri call:(std::string)callId {
1091 NSMutableDictionary *calls = connectingCalls[@(callUid_.c_str())];
1092 if (!calls) {
1093 calls = [[NSMutableDictionary alloc] init];
1094 }
1095 if (calls[@(uri.c_str())]) {
1096 return;
1097 }
1098 CallInConferenceVC *callingView = [self callingViewForCallId: callId];
1099 calls[@(uri.c_str())] = callingView;
1100 connectingCalls[@(callUid_.c_str())] = calls;
1101 [self.callingWidgetsContainer addView:callingView.view inGravity:NSStackViewGravityBottom];
1102 [self.callingWidgetsContainer updateConstraints];
1103 [self.callingWidgetsContainer updateLayer];
1104 [self.callingWidgetsContainer setNeedsDisplay:YES];
1105}
1106
1107-(CallInConferenceVC*) callingViewForCallId:(std::string)callId {
1108 CallInConferenceVC *callView = [[CallInConferenceVC alloc]
1109 initWithNibName:@"CallInConferenceVC"
1110 bundle:nil
1111 callId:callId
1112 accountInfo:accountInfo_];
1113 callView.delegate = self;
1114 callView.initiatorCallId = callUid_;
1115 return callView;
1116}
1117
1118-(std::string)getcallID {
1119 return confUid_.empty() ? callUid_ : confUid_;
1120}
1121
1122-(bool)isCurrentCall:(std::string)callId {
1123 return (callId == confUid_ || callId == callUid_);
1124}
1125
1126#pragma mark Popover delegate
1127
1128- (void)popoverWillClose:(NSNotification *)notification
1129{
1130 if (brokerPopoverVC != nullptr) {
1131 [brokerPopoverVC performClose:self];
1132 brokerPopoverVC = NULL;
1133 }
1134}
Alexandre Lisionc5148052015-03-04 15:10:35 -05001135@end