blob: 97ecc423982661fb0fa0fca28d6ee7429c7311f0 [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"
20
21#import <QuartzCore/QuartzCore.h>
22
Alexandre Lision210fe212016-01-27 11:15:13 -050023///Qt
24#import <QMimeData>
25#import <QtMacExtras/qmacfunctions.h>
26#import <QtCore/qabstractitemmodel.h>
27#import <QItemSelectionModel>
28#import <QItemSelection>
29#import <QPixmap>
30
31///LRC
Alexandre Lisionc5148052015-03-04 15:10:35 -050032#import <video/previewmanager.h>
33#import <video/renderer.h>
Olivier Soldano994971f2017-12-05 16:30:12 -050034#import <api/newcallmodel.h>
35#import <api/call.h>
36#import <api/conversationmodel.h>
Alexandre Lision210fe212016-01-27 11:15:13 -050037#import <globalinstances.h>
Alexandre Lisionc5148052015-03-04 15:10:35 -050038
Alexandre Lision62005312016-01-28 15:55:16 -050039#import "AppDelegate.h"
Alexandre Lisionf47a2562015-06-15 15:48:29 -040040#import "views/ITProgressIndicator.h"
Alexandre Lision74dd47f2015-04-14 13:47:42 -040041#import "views/CallView.h"
Kateryna Kostiuk81afce72018-10-30 16:40:50 -040042#import "views/NSColor+RingTheme.h"
Alexandre Lision210fe212016-01-27 11:15:13 -050043#import "delegates/ImageManipulationDelegate.h"
Alexandre Lision2db8f472015-07-22 15:05:46 -040044#import "PersonLinkerVC.h"
Alexandre Lision16d9c0a2015-08-10 12:05:15 -040045#import "ChatVC.h"
Alexandre Lision883719f2015-10-22 17:37:45 -040046#import "BrokerVC.h"
Alexandre Lision4baba4c2016-02-11 13:00:57 -050047#import "views/IconButton.h"
Anthony Léonard14e7bf32017-06-08 08:13:16 -040048#import "views/CallLayer.h"
Anthony Léonard6f819752018-01-05 09:53:40 -050049#import "utils.h"
Alexandre Lision74dd47f2015-04-14 13:47:42 -040050
Alexandre Lisionc5148052015-03-04 15:10:35 -050051@interface RendererConnectionsHolder : NSObject
52
53@property QMetaObject::Connection frameUpdated;
54@property QMetaObject::Connection started;
55@property QMetaObject::Connection stopped;
56
57@end
58
59@implementation RendererConnectionsHolder
60
61@end
62
Olivier Soldano994971f2017-12-05 16:30:12 -050063@interface CurrentCallVC () <NSPopoverDelegate, ContactLinkedDelegate> {
64 std::string convUid_;
65 std::string callUid_;
66 const lrc::api::account::Info *accountInfo_;
Anthony Léonard66a050c2018-01-03 16:32:34 -050067 NSTimer* refreshDurationTimer;
Olivier Soldano994971f2017-12-05 16:30:12 -050068}
Alexandre Lisionc5148052015-03-04 15:10:35 -050069
Alexandre Lision210fe212016-01-27 11:15:13 -050070// Main container
71@property (unsafe_unretained) IBOutlet NSSplitView* splitView;
Kateryna Kostiuk89398b62018-11-20 16:45:04 -050072@property (unsafe_unretained) IBOutlet NSView* backgroundImage;
Kateryna Kostiuk81afce72018-10-30 16:40:50 -040073@property (unsafe_unretained) IBOutlet NSBox* bluerBackgroundEffect;
Alexandre Lision210fe212016-01-27 11:15:13 -050074
Alexandre Lision89edc6a2015-11-09 11:30:47 -050075// Header info
76@property (unsafe_unretained) IBOutlet NSView* headerContainer;
Alexandre Lision883719f2015-10-22 17:37:45 -040077@property (unsafe_unretained) IBOutlet NSTextField* personLabel;
78@property (unsafe_unretained) IBOutlet NSTextField* stateLabel;
Alexandre Lision89edc6a2015-11-09 11:30:47 -050079@property (unsafe_unretained) IBOutlet NSTextField* timeSpentLabel;
Alexandre Lision210fe212016-01-27 11:15:13 -050080@property (unsafe_unretained) IBOutlet NSImageView* personPhoto;
Alexandre Lision89edc6a2015-11-09 11:30:47 -050081
Andreas Traczyk77a50d52018-05-08 17:47:31 -040082@property (unsafe_unretained) IBOutlet NSImageView* outgoingPhoto;
83@property (unsafe_unretained) IBOutlet NSTextField* outgoingPersonLabel;
84@property (unsafe_unretained) IBOutlet NSTextField* outgoingStateLabel;
85@property (unsafe_unretained) IBOutlet NSTextField* outgoingId;
86
Alexandre Lision89edc6a2015-11-09 11:30:47 -050087// Call Controls
88@property (unsafe_unretained) IBOutlet NSView* controlsPanel;
Alexandre Lision62005312016-01-28 15:55:16 -050089
Alexandre Lision4baba4c2016-02-11 13:00:57 -050090@property (unsafe_unretained) IBOutlet IconButton* holdOnOffButton;
91@property (unsafe_unretained) IBOutlet IconButton* hangUpButton;
92@property (unsafe_unretained) IBOutlet IconButton* recordOnOffButton;
93@property (unsafe_unretained) IBOutlet IconButton* pickUpButton;
94@property (unsafe_unretained) IBOutlet IconButton* muteAudioButton;
95@property (unsafe_unretained) IBOutlet IconButton* muteVideoButton;
Alexandre Lision4baba4c2016-02-11 13:00:57 -050096@property (unsafe_unretained) IBOutlet IconButton* transferButton;
97@property (unsafe_unretained) IBOutlet IconButton* addParticipantButton;
98@property (unsafe_unretained) IBOutlet IconButton* chatButton;
Andreas Traczyk77a50d52018-05-08 17:47:31 -040099@property (unsafe_unretained) IBOutlet IconButton* qualityButton;
Alexandre Lisiond18fa272015-06-15 11:18:03 -0400100
Alexandre Lision62005312016-01-28 15:55:16 -0500101@property (unsafe_unretained) IBOutlet NSView* advancedPanel;
102@property (unsafe_unretained) IBOutlet IconButton* advancedButton;
Kateryna Kostiuk81afce72018-10-30 16:40:50 -0400103@property (assign) IBOutlet NSLayoutConstraint* callRecordButtonMarginLeft;
Alexandre Lision89edc6a2015-11-09 11:30:47 -0500104
Alexandre Lision2db8f472015-07-22 15:05:46 -0400105@property (strong) NSPopover* addToContactPopover;
Alexandre Lision89edc6a2015-11-09 11:30:47 -0500106@property (strong) NSPopover* brokerPopoverVC;
Alexandre Lision883719f2015-10-22 17:37:45 -0400107@property (strong) IBOutlet ChatVC* chatVC;
Alexandre Lisionf23ec5a2015-07-16 11:24:06 -0400108
Alexandre Lision210fe212016-01-27 11:15:13 -0500109// Ringing call panel
110@property (unsafe_unretained) IBOutlet NSView* ringingPanel;
111@property (unsafe_unretained) IBOutlet NSImageView* incomingPersonPhoto;
112@property (unsafe_unretained) IBOutlet NSTextField* incomingDisplayName;
113
114// Outgoing call panel
115@property (unsafe_unretained) IBOutlet NSView* outgoingPanel;
116@property (unsafe_unretained) IBOutlet ITProgressIndicator *loadingIndicator;
Alexandre Lisionc5148052015-03-04 15:10:35 -0500117
Kateryna Kostiuk81afce72018-10-30 16:40:50 -0400118// audio only view
119@property (unsafe_unretained) IBOutlet NSView* audioCallView;
120@property (unsafe_unretained) IBOutlet NSImageView* audioCallPhoto;
121@property (unsafe_unretained) IBOutlet NSTextField* audioCallTime;
122@property (unsafe_unretained) IBOutlet NSTextField* audioCallPersonLabel;
123@property (unsafe_unretained) IBOutlet NSTextField* audioCallPersonId;
124
Alexandre Lisionc5148052015-03-04 15:10:35 -0500125// Video
Alexandre Lision58cab672015-06-09 15:25:40 -0400126@property (unsafe_unretained) IBOutlet CallView *videoView;
Alexandre Lision58cab672015-06-09 15:25:40 -0400127@property (unsafe_unretained) IBOutlet NSView *previewView;
Alexandre Lisionc5148052015-03-04 15:10:35 -0500128
129@property RendererConnectionsHolder* previewHolder;
130@property RendererConnectionsHolder* videoHolder;
Alexandre Lisionef6333a2015-03-24 12:30:31 -0400131@property QMetaObject::Connection videoStarted;
Alexandre Lision4d9905e2016-01-22 12:01:36 -0500132@property QMetaObject::Connection selectedCallChanged;
Alexandre Lisionb65c0272015-07-22 15:51:29 -0400133@property QMetaObject::Connection messageConnection;
134@property QMetaObject::Connection mediaAddedConnection;
Kateryna Kostiuk133364b2018-10-31 11:36:08 -0400135@property QMetaObject::Connection profileUpdatedConnection;
Alexandre Lisionc5148052015-03-04 15:10:35 -0500136
137@end
138
139@implementation CurrentCallVC
Olivier Soldano994971f2017-12-05 16:30:12 -0500140@synthesize personLabel, personPhoto, stateLabel, holdOnOffButton, hangUpButton,
Alexandre Lision89edc6a2015-11-09 11:30:47 -0500141 recordOnOffButton, pickUpButton, chatButton, transferButton, addParticipantButton, timeSpentLabel,
Olivier Soldano994971f2017-12-05 16:30:12 -0500142 muteVideoButton, muteAudioButton, controlsPanel, advancedPanel, advancedButton, headerContainer, videoView,
Kateryna Kostiuk81afce72018-10-30 16:40:50 -0400143 incomingDisplayName, incomingPersonPhoto, previewView, splitView, loadingIndicator, ringingPanel,
144 outgoingPanel, outgoingPersonLabel, outgoingStateLabel, outgoingPhoto, outgoingId,
145 callRecordButtonMarginLeft, audioCallView, audioCallPhoto, audioCallTime, audioCallPersonLabel, audioCallPersonId, backgroundImage, bluerBackgroundEffect;
Alexandre Lisionc5148052015-03-04 15:10:35 -0500146
147@synthesize previewHolder;
148@synthesize videoHolder;
149
Olivier Soldano994971f2017-12-05 16:30:12 -0500150-(void) setCurrentCall:(const std::string&)callUid
151 conversation:(const std::string&)convUid
152 account:(const lrc::api::account::Info*)account;
Alexandre Lisionc5148052015-03-04 15:10:35 -0500153{
Olivier Soldano994971f2017-12-05 16:30:12 -0500154 if(account == nil)
155 return;
Alexandre Lisionef6333a2015-03-24 12:30:31 -0400156
Olivier Soldano994971f2017-12-05 16:30:12 -0500157 auto* callModel = account->callModel.get();
Alexandre Lisionc5148052015-03-04 15:10:35 -0500158
Olivier Soldano994971f2017-12-05 16:30:12 -0500159 if (not callModel->hasCall(callUid)){
160 callUid_.clear();
Alexandre Lision2db8f472015-07-22 15:05:46 -0400161 return;
162 }
Olivier Soldano994971f2017-12-05 16:30:12 -0500163 callUid_ = callUid;
164 convUid_ = convUid;
165 accountInfo_ = account;
Anthony Léonard4b0935f2017-12-26 12:05:22 -0500166 [self.chatVC setConversationUid:convUid model:account->conversationModel.get()];
Kateryna Kostiuk54d76972018-06-07 15:45:07 -0400167 auto currentCall = callModel->getCall(callUid_);
Kateryna Kostiuk81afce72018-10-30 16:40:50 -0400168 [muteVideoButton setHidden: currentCall.isAudioOnly ? YES: NO];
169 callRecordButtonMarginLeft.constant = currentCall.isAudioOnly ? -40.0f: 10.0f;
170 [previewView setHidden: YES];
Kateryna Kostiukd76f6202018-09-11 17:15:28 -0400171 videoView.callId = callUid;
Alexandre Lisionc5148052015-03-04 15:10:35 -0500172}
173
Kateryna Kostiuk732b4062018-11-28 09:59:28 -0500174- (void) ensureLayoutForCallStatus:(lrc::api::call::Status) status {
175 using Status = lrc::api::call::Status;
176 switch (status) {
177 case Status::IN_PROGRESS:
178 if (![videoView.layer isKindOfClass:[CallLayer class]]) {
179 [videoView setLayer:[[CallLayer alloc] init]];
180 }
181 break;
182 default:
183 if ([videoView.layer isKindOfClass:[CallLayer class]]) {
184 [videoView setLayer:[CALayer layer]];
185 [videoView.layer setBackgroundColor:[[NSColor blackColor] CGColor]];
186 }
187 break;
188 }
189 holdOnOffButton.image = status == lrc::api::call::Status::PAUSED ?
190 [NSImage imageNamed:@"ic_action_holdoff.png"] : [NSImage imageNamed:@"ic_action_hold.png"];
191}
192
Alexandre Lisionc5148052015-03-04 15:10:35 -0500193- (void)awakeFromNib
194{
195 NSLog(@"INIT CurrentCall VC");
Alexandre Lisionef6333a2015-03-24 12:30:31 -0400196 [self.view setWantsLayer:YES];
Alexandre Lisionc5148052015-03-04 15:10:35 -0500197
Alexandre Lisionc5148052015-03-04 15:10:35 -0500198 [previewView setWantsLayer:YES];
Alexandre Lision22d615b2016-01-25 12:54:34 -0500199 [previewView.layer setBackgroundColor:[NSColor blackColor].CGColor];
200 [previewView.layer setContentsGravity:kCAGravityResizeAspectFill];
201 [previewView.layer setFrame:previewView.frame];
Alexandre Lisionc5148052015-03-04 15:10:35 -0500202
203 [controlsPanel setWantsLayer:YES];
Alexandre Lisionc5148052015-03-04 15:10:35 -0500204 [controlsPanel.layer setBackgroundColor:[NSColor clearColor].CGColor];
205 [controlsPanel.layer setFrame:controlsPanel.frame];
206
Alexandre Lisionef6333a2015-03-24 12:30:31 -0400207 previewHolder = [[RendererConnectionsHolder alloc] init];
208 videoHolder = [[RendererConnectionsHolder alloc] init];
209
Alexandre Lisionf47a2562015-06-15 15:48:29 -0400210 [loadingIndicator setColor:[NSColor whiteColor]];
Andreas Traczyk77a50d52018-05-08 17:47:31 -0400211 [loadingIndicator setNumberOfLines:200];
212 [loadingIndicator setWidthOfLine:4];
213 [loadingIndicator setLengthOfLine:4];
214 [loadingIndicator setInnerMargin:59];
Alexandre Lisionf47a2562015-06-15 15:48:29 -0400215
Alexandre Lisiona1eee3c2015-08-10 13:44:51 -0400216 [self.videoView setCallDelegate:self];
Kateryna Kostiuk1004caa2018-03-29 13:48:07 -0400217 CGColor* color = [[[NSColor blackColor] colorWithAlphaComponent:0.2] CGColor];
218 [headerContainer.layer setBackgroundColor:color];
Kateryna Kostiuk81afce72018-10-30 16:40:50 -0400219 [bluerBackgroundEffect setWantsLayer:YES];
220 bluerBackgroundEffect.alphaValue = 0.6;
221 [audioCallView setWantsLayer:YES];
222 [audioCallView.layer setBackgroundColor: [[NSColor clearColor] CGColor]];
Kateryna Kostiuk89398b62018-11-20 16:45:04 -0500223 [backgroundImage setWantsLayer: YES];
224 backgroundImage.layer.contentsGravity = kCAGravityResizeAspectFill;
Alexandre Lisionc5148052015-03-04 15:10:35 -0500225}
226
Anthony Léonard66a050c2018-01-03 16:32:34 -0500227-(void) updateDurationLabel
228{
229 if (accountInfo_ != nil) {
230 auto* callModel = accountInfo_->callModel.get();
231 if (callModel->hasCall(callUid_)) {
232 auto& callStatus = callModel->getCall(callUid_).status;
233 if (callStatus != lrc::api::call::Status::ENDED &&
234 callStatus != lrc::api::call::Status::TERMINATING &&
235 callStatus != lrc::api::call::Status::INVALID) {
Kateryna Kostiuk81afce72018-10-30 16:40:50 -0400236 if(callModel->getCall(callUid_).isAudioOnly) {
237 [audioCallTime setStringValue:@(callModel->getFormattedCallDuration(callUid_).c_str())];
238 } else {
239 [timeSpentLabel setStringValue:@(callModel->getFormattedCallDuration(callUid_).c_str())];
240 }
Anthony Léonard66a050c2018-01-03 16:32:34 -0500241 return;
242 }
243 }
244 }
245
246 // If call is not running anymore or accountInfo_ is not set for any reason
247 // we stop the refresh loop
248 [refreshDurationTimer invalidate];
249 refreshDurationTimer = nil;
250 [timeSpentLabel setHidden:YES];
Kateryna Kostiuk81afce72018-10-30 16:40:50 -0400251 [audioCallView setHidden:YES];
Anthony Léonard66a050c2018-01-03 16:32:34 -0500252}
253
Andreas Traczyk77a50d52018-05-08 17:47:31 -0400254-(void) updateCall
Alexandre Lisionc5148052015-03-04 15:10:35 -0500255{
Olivier Soldano994971f2017-12-05 16:30:12 -0500256 if (accountInfo_ == nil)
257 return;
Alexandre Lisionbb5dbcd2015-07-09 16:36:47 -0400258
Olivier Soldano994971f2017-12-05 16:30:12 -0500259 auto* callModel = accountInfo_->callModel.get();
260 if (not callModel->hasCall(callUid_)) {
261 return;
262 }
Alexandre Lision89edc6a2015-11-09 11:30:47 -0500263
Olivier Soldano994971f2017-12-05 16:30:12 -0500264 auto currentCall = callModel->getCall(callUid_);
265 NSLog(@"\n status %@ \n",@(lrc::api::call::to_string(currentCall.status).c_str()));
Anthony Léonard8ee26782018-01-05 10:23:04 -0500266 auto convIt = getConversationFromUid(convUid_, *accountInfo_->conversationModel);
Andreas Traczyk77a50d52018-05-08 17:47:31 -0400267 if (convIt != accountInfo_->conversationModel->allFilteredConversations().end()) {
268 NSString* bestName = bestNameForConversation(*convIt, *accountInfo_->conversationModel);
269 [personLabel setStringValue:bestName];
270 [outgoingPersonLabel setStringValue:bestName];
Kateryna Kostiuk81afce72018-10-30 16:40:50 -0400271 [audioCallPersonLabel setStringValue:bestName];
Andreas Traczyk77a50d52018-05-08 17:47:31 -0400272 NSString* ringID = bestIDForConversation(*convIt, *accountInfo_->conversationModel);
273 if([bestName isEqualToString:ringID]) {
274 ringID = @"";
275 }
276 [outgoingId setStringValue:ringID];
Kateryna Kostiuk81afce72018-10-30 16:40:50 -0400277 [audioCallPersonId setStringValue:ringID];
Andreas Traczyk77a50d52018-05-08 17:47:31 -0400278 }
279
Anthony Léonard66a050c2018-01-03 16:32:34 -0500280 [timeSpentLabel setStringValue:@(callModel->getFormattedCallDuration(callUid_).c_str())];
Kateryna Kostiuk81afce72018-10-30 16:40:50 -0400281 [audioCallTime setStringValue:@(callModel->getFormattedCallDuration(callUid_).c_str())];
Anthony Léonard66a050c2018-01-03 16:32:34 -0500282 if (refreshDurationTimer == nil)
283 refreshDurationTimer = [NSTimer scheduledTimerWithTimeInterval:1.0
284 target:self
285 selector:@selector(updateDurationLabel)
286 userInfo:nil
287 repeats:YES];
Olivier Soldano994971f2017-12-05 16:30:12 -0500288 [stateLabel setStringValue:@(to_string(currentCall.status).c_str())];
Andreas Traczyk77a50d52018-05-08 17:47:31 -0400289 [outgoingStateLabel setStringValue:@(to_string(currentCall.status).c_str())];
Alexandre Lisionc5148052015-03-04 15:10:35 -0500290
Olivier Soldano994971f2017-12-05 16:30:12 -0500291 // Default values for this views
292 [loadingIndicator setHidden:YES];
Olivier Soldano994971f2017-12-05 16:30:12 -0500293 [ringingPanel setHidden:YES];
294 [outgoingPanel setHidden:YES];
295 [controlsPanel setHidden:NO];
296 [headerContainer setHidden:NO];
Anthony Léonard4b0935f2017-12-26 12:05:22 -0500297 [chatButton setHidden:NO];
Kateryna Kostiuk81afce72018-10-30 16:40:50 -0400298 [self setBackground];
Alexandre Lisionc5148052015-03-04 15:10:35 -0500299
Olivier Soldano994971f2017-12-05 16:30:12 -0500300 using Status = lrc::api::call::Status;
Kateryna Kostiuk732b4062018-11-28 09:59:28 -0500301 [self ensureLayoutForCallStatus:currentCall.status];
Olivier Soldano994971f2017-12-05 16:30:12 -0500302 switch (currentCall.status) {
303 case Status::SEARCHING:
Olivier Soldano994971f2017-12-05 16:30:12 -0500304 case Status::CONNECTING:
Andreas Traczyk77a50d52018-05-08 17:47:31 -0400305 [headerContainer setHidden:YES];
306 [outgoingPanel setHidden:NO];
307 [outgoingPhoto setHidden:NO];
308 [self setupContactInfo:outgoingPhoto];
Olivier Soldano994971f2017-12-05 16:30:12 -0500309 [loadingIndicator setHidden:NO];
Andreas Traczyk77a50d52018-05-08 17:47:31 -0400310 [controlsPanel setHidden:YES];
Olivier Soldano994971f2017-12-05 16:30:12 -0500311 break;
312 case Status::INCOMING_RINGING:
Andreas Traczyk77a50d52018-05-08 17:47:31 -0400313 [outgoingPhoto setHidden:YES];
Olivier Soldano994971f2017-12-05 16:30:12 -0500314 [controlsPanel setHidden:YES];
315 [outgoingPanel setHidden:YES];
316 [self setupIncoming:callUid_];
317 break;
318 case Status::OUTGOING_RINGING:
319 [controlsPanel setHidden:YES];
320 [outgoingPanel setHidden:NO];
Andreas Traczyk77a50d52018-05-08 17:47:31 -0400321 [loadingIndicator setHidden:NO];
322 [headerContainer setHidden:YES];
Olivier Soldano994971f2017-12-05 16:30:12 -0500323 break;
Andreas Traczyk77a50d52018-05-08 17:47:31 -0400324 /*case Status::CONFERENCE:
325 [self setupConference:currentCall];
326 break;*/
Olivier Soldano994971f2017-12-05 16:30:12 -0500327 case Status::PAUSED:
Olivier Soldano994971f2017-12-05 16:30:12 -0500328 case Status::INACTIVE:
329 case Status::IN_PROGRESS:
Andreas Traczyk77a50d52018-05-08 17:47:31 -0400330 // change constraints (uncollapse avatar)
Kateryna Kostiuk81afce72018-10-30 16:40:50 -0400331 if(currentCall.isAudioOnly) {
332 [self setUpAudioOnlyView];
333 } else {
334 [self setUpVideoCallView];
335 }
Andreas Traczyk77a50d52018-05-08 17:47:31 -0400336 break;
Olivier Soldano994971f2017-12-05 16:30:12 -0500337 case Status::CONNECTED:
Olivier Soldano994971f2017-12-05 16:30:12 -0500338 break;
339 case Status::ENDED:
340 case Status::TERMINATING:
341 case Status::INVALID:
342 [controlsPanel setHidden:YES];
343 [outgoingPanel setHidden:NO];
Kateryna Kostiuk732b4062018-11-28 09:59:28 -0500344 [self cleanUp];
Andreas Traczykecdc4fa2018-03-22 16:11:47 -0400345 [self hideWithAnimation:false];
Olivier Soldano994971f2017-12-05 16:30:12 -0500346 break;
347 }
Alexandre Lision4d9905e2016-01-22 12:01:36 -0500348}
349
Kateryna Kostiuk81afce72018-10-30 16:40:50 -0400350-(void) setUpVideoCallView {
351 [self setupContactInfo:personPhoto];
352 [timeSpentLabel setHidden:NO];
353 [outgoingPhoto setHidden:YES];
354 [headerContainer setHidden:NO];
355 [previewView setHidden: NO];
356 [bluerBackgroundEffect setHidden:YES];
Kateryna Kostiuk81afce72018-10-30 16:40:50 -0400357 [backgroundImage setHidden:YES];
358}
359
360-(void) setUpAudioOnlyView {
361 [audioCallView setHidden:NO];
362 [headerContainer setHidden:YES];
363 [audioCallPhoto setImage: [self getContactImageOfSize:120.0 withDefaultAvatar:YES]];
364}
365
366-(void) setBackground {
Andreas Traczyk77a50d52018-05-08 17:47:31 -0400367 auto* convModel = accountInfo_->conversationModel.get();
368 auto it = getConversationFromUid(convUid_, *convModel);
Kateryna Kostiuk81afce72018-10-30 16:40:50 -0400369 NSImage *image= [self getContactImageOfSize:120.0 withDefaultAvatar:NO];
370 if(image) {
Kateryna Kostiuk89398b62018-11-20 16:45:04 -0500371 CIImage * ciImage = [[CIImage alloc] initWithData:[image TIFFRepresentation]];
372 CIContext *context = [[CIContext alloc] init];
373 CIFilter *clamp = [CIFilter filterWithName:@"CIAffineClamp"];
374 [clamp setValue:[NSAffineTransform transform] forKey:@"inputTransform"];
375 [clamp setValue:ciImage forKey: kCIInputImageKey];
Kateryna Kostiuk81afce72018-10-30 16:40:50 -0400376 CIFilter* bluerFilter = [CIFilter filterWithName:@"CIGaussianBlur"];
377 [bluerFilter setDefaults];
Kateryna Kostiuk89398b62018-11-20 16:45:04 -0500378 [bluerFilter setValue:[NSNumber numberWithFloat: 9] forKey:@"inputRadius"];
379 [bluerFilter setValue:[clamp valueForKey:kCIOutputImageKey] forKey: kCIInputImageKey];
380 CIImage *result = [bluerFilter valueForKey:kCIOutputImageKey];
381 CGRect extent = [result extent];
382 CGImageRef cgImage = [context createCGImage:result fromRect: [ciImage extent]];
383 NSImage *bluredImage = [[NSImage alloc] initWithCGImage:cgImage size:NSSizeFromCGSize(CGSizeMake(image.size.width, image.size.height))];
384 backgroundImage.layer.contents = bluredImage;
Kateryna Kostiuk81afce72018-10-30 16:40:50 -0400385 [backgroundImage setHidden:NO];
386 } else {
Kateryna Kostiuk81afce72018-10-30 16:40:50 -0400387 [bluerBackgroundEffect setFillColor:[NSColor ringDarkGrey]];
388 [backgroundImage setHidden:YES];
Andreas Traczyk77a50d52018-05-08 17:47:31 -0400389 }
390}
391
Kateryna Kostiuk133364b2018-10-31 11:36:08 -0400392-(NSImage *) getContactImageOfSize: (double) size withDefaultAvatar:(BOOL) shouldDrawDefault {
Kateryna Kostiuk81afce72018-10-30 16:40:50 -0400393 auto* convModel = accountInfo_->conversationModel.get();
394 auto convIt = getConversationFromUid(convUid_, *convModel);
395 if (convIt == convModel->allFilteredConversations().end()) {
396 return nil;
397 }
398 if(shouldDrawDefault) {
399 auto& imgManip = reinterpret_cast<Interfaces::ImageManipulationDelegate&>(GlobalInstances::pixmapManipulator());
400 QVariant photo = imgManip.conversationPhoto(*convIt, *accountInfo_, QSize(size, size), NO);
401 return QtMac::toNSImage(qvariant_cast<QPixmap>(photo));
402 }
403 auto contact = accountInfo_->contactModel->getContact(convIt->participants[0]);
404 NSData *imageData = [[NSData alloc] initWithBase64EncodedString:@(contact.profileInfo.avatar.c_str()) options:NSDataBase64DecodingIgnoreUnknownCharacters];
405 return [[NSImage alloc] initWithData:imageData];
406}
407
408
409-(void) setupContactInfo:(NSImageView*)imageView
410{
411 [imageView setImage: [self getContactImageOfSize:120.0 withDefaultAvatar:YES]];
412}
413
Olivier Soldano994971f2017-12-05 16:30:12 -0500414-(void) setupIncoming:(const std::string&) callId
Alexandre Lision4d9905e2016-01-22 12:01:36 -0500415{
Olivier Soldano994971f2017-12-05 16:30:12 -0500416 if (accountInfo_ == nil)
417 return;
418
419 auto* callModel = accountInfo_->callModel.get();
420 if (not callModel->hasCall(callUid_)) {
421 return;
422 }
423
424 auto call = callModel->getCall(callUid_);
425 auto* convModel = accountInfo_->conversationModel.get();
426
427 [ringingPanel setHidden:NO];
428 [controlsPanel setHidden:YES];
429 [headerContainer setHidden:YES];
Anthony Léonard6f819752018-01-05 09:53:40 -0500430 auto it = getConversationFromUid(convUid_, *convModel);
Olivier Soldano994971f2017-12-05 16:30:12 -0500431 if (it != convModel->allFilteredConversations().end()) {
Kateryna Kostiuk81afce72018-10-30 16:40:50 -0400432 [incomingPersonPhoto setImage: [self getContactImageOfSize:120.0 withDefaultAvatar:YES]];
Anthony Léonard8ee26782018-01-05 10:23:04 -0500433 [incomingDisplayName setStringValue:bestNameForConversation(*it, *convModel)];
Olivier Soldano994971f2017-12-05 16:30:12 -0500434 }
435}
436
437
438-(void) setupConference:(Call*) c
439{
440 [videoView setShouldAcceptInteractions:YES];
441 [self.chatButton setHidden:NO];
Olivier Soldano994971f2017-12-05 16:30:12 -0500442 [self.addParticipantButton setHidden:NO];
443 [self.transferButton setHidden:YES];
444}
445
446-(void)collapseRightView
447{
448 NSView *right = [[splitView subviews] objectAtIndex:1];
449 NSView *left = [[splitView subviews] objectAtIndex:0];
450 NSRect leftFrame = [left frame];
451 [right setHidden:YES];
452 [splitView display];
453}
454
455- (void) changeCallSelection:(std::string&)callUid
456{
457 if (accountInfo_ == nil)
458 return;
459
460 auto* callModel = accountInfo_->callModel.get();
461 if (not callModel->hasCall(callUid)) {
462 return;
463 }
464
Alexandre Lision4d9905e2016-01-22 12:01:36 -0500465 QObject::disconnect(self.selectedCallChanged);
Olivier Soldano994971f2017-12-05 16:30:12 -0500466 self.selectedCallChanged = QObject::connect(callModel,
467 &lrc::api::NewCallModel::callStatusChanged,
468 [self](const std::string callId) {
Andreas Traczyk77a50d52018-05-08 17:47:31 -0400469 [self updateCall];
Alexandre Lision4d9905e2016-01-22 12:01:36 -0500470 });
Alexandre Lisionc5148052015-03-04 15:10:35 -0500471}
472
473-(void) connectVideoSignals
474{
Olivier Soldano994971f2017-12-05 16:30:12 -0500475 if (accountInfo_ == nil)
476 return;
477
478 auto* callModel = accountInfo_->callModel.get();
Alexandre Lision89edc6a2015-11-09 11:30:47 -0500479 QObject::disconnect(self.videoStarted);
Olivier Soldano994971f2017-12-05 16:30:12 -0500480 self.videoStarted = QObject::connect(callModel,
481 &lrc::api::NewCallModel::remotePreviewStarted,
482 [self](const std::string& callId, Video::Renderer* renderer) {
Kateryna Kostiukc17db8d2018-10-23 09:48:03 -0400483 [videoView setLayer:[[CallLayer alloc] init]];
Kateryna Kostiuk1004caa2018-03-29 13:48:07 -0400484 [videoView setShouldAcceptInteractions:YES];
485 [self mouseIsMoving: NO];
Olivier Soldano994971f2017-12-05 16:30:12 -0500486 [self connectVideoRenderer:renderer];
487 });
Alexandre Lisionc5148052015-03-04 15:10:35 -0500488
Olivier Soldano994971f2017-12-05 16:30:12 -0500489 if (callModel->hasCall(callUid_)) {
490 if (auto renderer = callModel->getRenderer(callUid_)) {
491 QObject::disconnect(self.videoStarted);
492 [self connectVideoRenderer: renderer];
493 }
Alexandre Lisionc5148052015-03-04 15:10:35 -0500494 }
Alexandre Lisionc5148052015-03-04 15:10:35 -0500495 [self connectPreviewRenderer];
496
497}
498
499-(void) connectPreviewRenderer
500{
Alexandre Lisionef6333a2015-03-24 12:30:31 -0400501 QObject::disconnect(previewHolder.frameUpdated);
502 QObject::disconnect(previewHolder.stopped);
503 QObject::disconnect(previewHolder.started);
Alexandre Lisiond3aa3ad2015-10-23 14:28:41 -0400504 previewHolder.started = QObject::connect(&Video::PreviewManager::instance(),
Alexandre Lisionc5148052015-03-04 15:10:35 -0500505 &Video::PreviewManager::previewStarted,
506 [=](Video::Renderer* renderer) {
Alexandre Lisionc5148052015-03-04 15:10:35 -0500507 QObject::disconnect(previewHolder.frameUpdated);
508 previewHolder.frameUpdated = QObject::connect(renderer,
509 &Video::Renderer::frameUpdated,
510 [=]() {
Alexandre Lisiond3aa3ad2015-10-23 14:28:41 -0400511 [self renderer:Video::PreviewManager::instance().previewRenderer()
Anthony Léonard14e7bf32017-06-08 08:13:16 -0400512 renderFrameForPreviewView:previewView];
Alexandre Lisionc5148052015-03-04 15:10:35 -0500513 });
514 });
515
Alexandre Lisiond3aa3ad2015-10-23 14:28:41 -0400516 previewHolder.stopped = QObject::connect(&Video::PreviewManager::instance(),
Alexandre Lisionc5148052015-03-04 15:10:35 -0500517 &Video::PreviewManager::previewStopped,
518 [=](Video::Renderer* renderer) {
Kateryna Kostiuk732b4062018-11-28 09:59:28 -0500519 QObject::disconnect(previewHolder.frameUpdated);
Alexandre Lisionc5148052015-03-04 15:10:35 -0500520 [previewView.layer setContents:nil];
521 });
522
Alexandre Lisiond3aa3ad2015-10-23 14:28:41 -0400523 previewHolder.frameUpdated = QObject::connect(Video::PreviewManager::instance().previewRenderer(),
Alexandre Lisionc5148052015-03-04 15:10:35 -0500524 &Video::Renderer::frameUpdated,
525 [=]() {
Alexandre Lisiond3aa3ad2015-10-23 14:28:41 -0400526 [self renderer:Video::PreviewManager::instance().previewRenderer()
Anthony Léonard14e7bf32017-06-08 08:13:16 -0400527 renderFrameForPreviewView:previewView];
Alexandre Lisionc5148052015-03-04 15:10:35 -0500528 });
529}
530
531-(void) connectVideoRenderer: (Video::Renderer*)renderer
532{
Alexandre Lisionef6333a2015-03-24 12:30:31 -0400533 QObject::disconnect(videoHolder.frameUpdated);
534 QObject::disconnect(videoHolder.started);
535 QObject::disconnect(videoHolder.stopped);
Olivier Soldano994971f2017-12-05 16:30:12 -0500536
537 if(renderer == nil)
538 return;
539
Alexandre Lisionc5148052015-03-04 15:10:35 -0500540 videoHolder.frameUpdated = QObject::connect(renderer,
541 &Video::Renderer::frameUpdated,
542 [=]() {
Kateryna Kostiuk732b4062018-11-28 09:59:28 -0500543 if(!renderer->isRendering()) {
544 return;
545 }
Anthony Léonard14e7bf32017-06-08 08:13:16 -0400546 [self renderer:renderer renderFrameForDistantView:videoView];
Alexandre Lisionc5148052015-03-04 15:10:35 -0500547 });
548
549 videoHolder.started = QObject::connect(renderer,
550 &Video::Renderer::started,
551 [=]() {
Kateryna Kostiukbcf24e92018-11-20 10:09:06 -0500552 if (![videoView.layer isKindOfClass:[CallLayer class]]) {
553 [videoView setLayer:[[CallLayer alloc] init]];
554 }
Kateryna Kostiuk1004caa2018-03-29 13:48:07 -0400555 [self mouseIsMoving: NO];
556 [videoView setShouldAcceptInteractions:YES];
Alexandre Lisionc5148052015-03-04 15:10:35 -0500557 QObject::disconnect(videoHolder.frameUpdated);
558 videoHolder.frameUpdated = QObject::connect(renderer,
559 &Video::Renderer::frameUpdated,
560 [=]() {
Anthony Léonard14e7bf32017-06-08 08:13:16 -0400561 [self renderer:renderer renderFrameForDistantView:videoView];
Alexandre Lisionc5148052015-03-04 15:10:35 -0500562 });
563 });
564
565 videoHolder.stopped = QObject::connect(renderer,
566 &Video::Renderer::stopped,
567 [=]() {
Kateryna Kostiuk732b4062018-11-28 09:59:28 -0500568 [(CallLayer*)videoView.layer setVideoRunning:NO];
Kateryna Kostiukc17db8d2018-10-23 09:48:03 -0400569 [videoView setLayer:[CALayer layer]];
570 [videoView.layer setBackgroundColor:[[NSColor blackColor] CGColor]];
Kateryna Kostiuk1004caa2018-03-29 13:48:07 -0400571 [self mouseIsMoving: YES];
572 [videoView setShouldAcceptInteractions:NO];
Alexandre Lisionc5148052015-03-04 15:10:35 -0500573 QObject::disconnect(videoHolder.frameUpdated);
Alexandre Lisionc5148052015-03-04 15:10:35 -0500574 });
575}
576
Anthony Léonard14e7bf32017-06-08 08:13:16 -0400577-(void) renderer: (Video::Renderer*)renderer renderFrameForPreviewView:(NSView*) view
Alexandre Lisionc5148052015-03-04 15:10:35 -0500578{
Alexandre Lisionc5148052015-03-04 15:10:35 -0500579 QSize res = renderer->size();
580
Alexandre Lision6731e132015-10-14 14:29:06 -0400581 auto frame_ptr = renderer->currentFrame();
582 auto frame_data = frame_ptr.ptr;
583 if (!frame_data)
584 return;
585
Alexandre Lisionc5148052015-03-04 15:10:35 -0500586 CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
Alexandre Lision6731e132015-10-14 14:29:06 -0400587 CGContextRef newContext = CGBitmapContextCreate(frame_data,
Alexandre Lisionc5148052015-03-04 15:10:35 -0500588 res.width(),
589 res.height(),
590 8,
591 4*res.width(),
592 colorSpace,
593 kCGImageAlphaPremultipliedLast);
594
595
596 CGImageRef newImage = CGBitmapContextCreateImage(newContext);
597
598 /*We release some components*/
599 CGContextRelease(newContext);
600 CGColorSpaceRelease(colorSpace);
601
602 [CATransaction begin];
603 view.layer.contents = (__bridge id)newImage;
604 [CATransaction commit];
605
606 CFRelease(newImage);
607}
608
Anthony Léonard14e7bf32017-06-08 08:13:16 -0400609-(void) renderer: (Video::Renderer*)renderer renderFrameForDistantView:(CallView*) view
610{
Anthony Léonard14e7bf32017-06-08 08:13:16 -0400611 auto frame_ptr = renderer->currentFrame();
612 if (!frame_ptr.ptr)
613 return;
614
615 CallLayer* callLayer = (CallLayer*) view.layer;
Kateryna Kostiukbcf24e92018-11-20 10:09:06 -0500616 if ([callLayer respondsToSelector:@selector(setCurrentFrame:)]) {
617 [callLayer setCurrentFrame:std::move(frame_ptr)];
618 [callLayer setVideoRunning:YES];
619 }
Anthony Léonard14e7bf32017-06-08 08:13:16 -0400620}
621
Alexandre Lisionc5148052015-03-04 15:10:35 -0500622- (void) initFrame
623{
624 [self.view setFrame:self.view.superview.bounds];
625 [self.view setHidden:YES];
626 self.view.layer.position = self.view.frame.origin;
Alexandre Lisionbb5dbcd2015-07-09 16:36:47 -0400627 [self collapseRightView];
Alexandre Lisionc5148052015-03-04 15:10:35 -0500628}
629
630# pragma private IN/OUT animations
631
Olivier Soldano994971f2017-12-05 16:30:12 -0500632-(void)uncollapseRightView
633{
634 NSView *left = [[splitView subviews] objectAtIndex:0];
635 NSView *right = [[splitView subviews] objectAtIndex:1];
636 [right setHidden:NO];
637
638 CGFloat dividerThickness = [splitView dividerThickness];
639
640 // get the different frames
641 NSRect leftFrame = [left frame];
642 NSRect rightFrame = [right frame];
643
644 leftFrame.size.width = (leftFrame.size.width - rightFrame.size.width - dividerThickness);
645 rightFrame.origin.x = leftFrame.size.width + dividerThickness;
646 [left setFrameSize:leftFrame.size];
647 [right setFrame:rightFrame];
648 [splitView display];
649
650 [self.chatVC takeFocus];
651}
652
Alexandre Lisionc5148052015-03-04 15:10:35 -0500653-(void) cleanUp
654{
Anthony Léonard5c287642018-02-21 15:54:19 -0500655 if(self.splitView.isInFullScreenMode)
656 [self.splitView exitFullScreenModeWithOptions:nil];
Alexandre Lisionef6333a2015-03-24 12:30:31 -0400657 QObject::disconnect(videoHolder.frameUpdated);
658 QObject::disconnect(videoHolder.started);
659 QObject::disconnect(videoHolder.stopped);
660 QObject::disconnect(previewHolder.frameUpdated);
661 QObject::disconnect(previewHolder.stopped);
662 QObject::disconnect(previewHolder.started);
Anthony Léonardc0013bb2018-02-20 16:23:02 -0500663 QObject::disconnect(self.messageConnection);
Alexandre Lisionc5148052015-03-04 15:10:35 -0500664 [previewView.layer setContents:nil];
Kateryna Kostiuk81afce72018-10-30 16:40:50 -0400665 [previewView setHidden: YES];
Kateryna Kostiukc17db8d2018-10-23 09:48:03 -0400666 [videoView setLayer:[CALayer layer]];
667 [videoView.layer setBackgroundColor:[[NSColor blackColor] CGColor]];
Alexandre Lision883719f2015-10-22 17:37:45 -0400668
Alexandre Lision89edc6a2015-11-09 11:30:47 -0500669 [self.chatButton setHidden:YES];
Alexandre Lision4baba4c2016-02-11 13:00:57 -0500670 [self.chatButton setPressed:NO];
Alexandre Lision883719f2015-10-22 17:37:45 -0400671 [self collapseRightView];
Alexandre Lision57227772016-01-15 17:19:36 -0500672
673 [personLabel setStringValue:@""];
674 [timeSpentLabel setStringValue:@""];
675 [stateLabel setStringValue:@""];
Kateryna Kostiuk81afce72018-10-30 16:40:50 -0400676 //audio view
677 [audioCallTime setStringValue:@""];
678 [audioCallPersonId setStringValue:@""];
679 [audioCallPersonLabel setStringValue:@""];
680 [audioCallView setHidden:YES];
681 [audioCallPhoto setImage:nil];
682 //background view
683 [bluerBackgroundEffect setHidden:NO];
684 [backgroundImage setHidden:NO];
Kateryna Kostiuk81afce72018-10-30 16:40:50 -0400685 //outgoing view
Andreas Traczyk77a50d52018-05-08 17:47:31 -0400686 [outgoingPersonLabel setStringValue:@""];
687 [outgoingStateLabel setStringValue:@""];
Alexandre Lisionc5148052015-03-04 15:10:35 -0500688}
689
Andreas Traczykecdc4fa2018-03-22 16:11:47 -0400690-(void) setupCallView
691{
692 if (accountInfo_ == nil)
693 return;
694
695 auto* callModel = accountInfo_->callModel.get();
696 auto* convModel = accountInfo_->conversationModel.get();
697
698 // when call comes in we want to show the controls/header
Kateryna Kostiuk1004caa2018-03-29 13:48:07 -0400699 [self mouseIsMoving: YES];
700 [videoView setShouldAcceptInteractions: NO];
Andreas Traczykecdc4fa2018-03-22 16:11:47 -0400701
702 [self connectVideoSignals];
703 /* check if text media is already present */
704 if(not callModel->hasCall(callUid_))
705 return;
706
707 [loadingIndicator setAnimates:YES];
Andreas Traczyk77a50d52018-05-08 17:47:31 -0400708 [self updateCall];
Andreas Traczykecdc4fa2018-03-22 16:11:47 -0400709
710 /* monitor media for messaging text messaging */
711 QObject::disconnect(self.messageConnection);
712 self.messageConnection = QObject::connect(convModel,
713 &lrc::api::ConversationModel::interactionStatusUpdated,
714 [self] (std::string convUid,
715 uint64_t msgId,
716 lrc::api::interaction::Info msg) {
717 if (msg.type == lrc::api::interaction::Type::TEXT) {
718 if(not [[self splitView] isSubviewCollapsed:[[[self splitView] subviews] objectAtIndex: 1]]){
719 return;
720 }
721 [self uncollapseRightView];
722 }
723 });
Kateryna Kostiuk133364b2018-10-31 11:36:08 -0400724 //monitor for updated profile
725 QObject::disconnect(self.profileUpdatedConnection);
726 self.profileUpdatedConnection =
727 QObject::connect(accountInfo_->contactModel.get(),
728 &lrc::api::ContactModel::contactAdded,
729 [self](const std::string &contactUri) {
730 auto convIt = getConversationFromUid(convUid_, *accountInfo_->conversationModel.get());
731 if (convIt == accountInfo_->conversationModel->allFilteredConversations().end()) {
732 return;
733 }
734 if (convIt->participants.empty()) {
735 return;
736
737 }
738 auto& contact = accountInfo_->contactModel->getContact(convIt->participants[0]);
739 if (contact.profileInfo.type == lrc::api::profile::Type::RING && contact.profileInfo.uri == contactUri)
740 accountInfo_->conversationModel->makePermanent(convUid_);
741 [incomingPersonPhoto setImage: [self getContactImageOfSize:120.0 withDefaultAvatar:YES]];
742 [outgoingPhoto setImage: [self getContactImageOfSize:120.0 withDefaultAvatar:YES]];
Kateryna Kostiuk732b4062018-11-28 09:59:28 -0500743 [self.delegate conversationInfoUpdatedFor:convUid_];
Kateryna Kostiuk133364b2018-10-31 11:36:08 -0400744 if(accountInfo_->callModel.get()->getCall(callUid_).isAudioOnly) {
745 [audioCallPhoto setImage: [self getContactImageOfSize:120.0 withDefaultAvatar:YES]];
746 [self setBackground];
747 return;
748 }
749 [personPhoto setImage: [self getContactImageOfSize:120.0 withDefaultAvatar:YES]];
750 });
Andreas Traczykecdc4fa2018-03-22 16:11:47 -0400751}
752
753-(void) showWithAnimation:(BOOL)animate
754{
755 if (!animate) {
756 [self.view setHidden:NO];
757 [self setupCallView];
758 return;
759 }
760
761 CGRect frame = CGRectOffset(self.view.superview.bounds, -self.view.superview.bounds.size.width, 0);
762 [self.view setHidden:NO];
763
764 [CATransaction begin];
765 CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"position"];
766 [animation setFromValue:[NSValue valueWithPoint:frame.origin]];
767 [animation setToValue:[NSValue valueWithPoint:self.view.superview.bounds.origin]];
768 [animation setDuration:0.2f];
769 [animation setTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseIn]];
770 [CATransaction setCompletionBlock:^{
771 [self setupCallView];
772 }];
773
774 [self.view.layer addAnimation:animation forKey:animation.keyPath];
775 [CATransaction commit];
776}
777
778-(void) hideWithAnimation:(BOOL)animate
Alexandre Lisionc5148052015-03-04 15:10:35 -0500779{
Alexandre Lisionc5148052015-03-04 15:10:35 -0500780 if(self.view.frame.origin.x < 0) {
Alexandre Lisionc5148052015-03-04 15:10:35 -0500781 return;
782 }
783
Andreas Traczykecdc4fa2018-03-22 16:11:47 -0400784 if (!animate) {
785 [self.view setHidden:YES];
786 return;
787 }
788
Alexandre Lisionc5148052015-03-04 15:10:35 -0500789 CGRect frame = CGRectOffset(self.view.frame, -self.view.frame.size.width, 0);
790 [CATransaction begin];
791 CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"position"];
792 [animation setFromValue:[NSValue valueWithPoint:self.view.frame.origin]];
793 [animation setToValue:[NSValue valueWithPoint:frame.origin]];
794 [animation setDuration:0.2f];
Alexandre Lision22d615b2016-01-25 12:54:34 -0500795 [animation setTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut]];
Alexandre Lisionc5148052015-03-04 15:10:35 -0500796
797 [CATransaction setCompletionBlock:^{
798 [self.view setHidden:YES];
Alexandre Lisionef6333a2015-03-24 12:30:31 -0400799 // first make sure everything is disconnected
Alexandre Lisionc5148052015-03-04 15:10:35 -0500800 [self cleanUp];
Olivier Soldano994971f2017-12-05 16:30:12 -0500801// if (currentCall_) {
802// [self animateIn];
803// }
Alexandre Lisionc5148052015-03-04 15:10:35 -0500804 }];
805 [self.view.layer addAnimation:animation forKey:animation.keyPath];
Alexandre Lisiona1c6d752015-06-23 12:27:38 -0400806
807 [self.view.layer setPosition:frame.origin];
Alexandre Lisionc5148052015-03-04 15:10:35 -0500808 [CATransaction commit];
809}
810
811/**
812 * Debug purpose
813 */
814-(void) dumpFrame:(CGRect) frame WithName:(NSString*) name
815{
816 NSLog(@"frame %@ : %f %f %f %f \n\n",name ,frame.origin.x, frame.origin.y, frame.size.width, frame.size.height);
817}
818
Alexandre Lision58cab672015-06-09 15:25:40 -0400819#pragma mark - Button methods
820
Alexandre Lisionc5148052015-03-04 15:10:35 -0500821- (IBAction)hangUp:(id)sender {
Olivier Soldano994971f2017-12-05 16:30:12 -0500822 if (accountInfo_ == nil)
823 return;
824
825 auto* callModel = accountInfo_->callModel.get();
826
827 callModel->hangUp(callUid_);
Alexandre Lisionc5148052015-03-04 15:10:35 -0500828}
829
830- (IBAction)accept:(id)sender {
Olivier Soldano994971f2017-12-05 16:30:12 -0500831 if (accountInfo_ == nil)
832 return;
833
Anthony Léonardf584cc52018-01-19 12:00:19 -0500834 // If we accept a conversation with a non trusted contact, we first accept it
835 auto convIt = getConversationFromUid(convUid_, *accountInfo_->conversationModel.get());
836 if (convIt != accountInfo_->conversationModel->allFilteredConversations().end()) {
837 auto& contact = accountInfo_->contactModel->getContact(convIt->participants[0]);
838 if (contact.profileInfo.type == lrc::api::profile::Type::PENDING)
839 accountInfo_->conversationModel->makePermanent(convUid_);
840 }
841
Olivier Soldano994971f2017-12-05 16:30:12 -0500842 auto* callModel = accountInfo_->callModel.get();
843
844 callModel->accept(callUid_);
Alexandre Lisionc5148052015-03-04 15:10:35 -0500845}
846
847- (IBAction)toggleRecording:(id)sender {
Olivier Soldano994971f2017-12-05 16:30:12 -0500848 if (accountInfo_ == nil)
849 return;
850
851 auto* callModel = accountInfo_->callModel.get();
852
853 callModel->toggleAudioRecord(callUid_);
Kateryna Kostiuk54d76972018-06-07 15:45:07 -0400854 [recordOnOffButton setPressed:!recordOnOffButton.isPressed];
Alexandre Lisionc5148052015-03-04 15:10:35 -0500855}
856
857- (IBAction)toggleHold:(id)sender {
Olivier Soldano994971f2017-12-05 16:30:12 -0500858 if (accountInfo_ == nil)
859 return;
860
861 auto* callModel = accountInfo_->callModel.get();
Kateryna Kostiuk54d76972018-06-07 15:45:07 -0400862 auto currentCall = callModel->getCall(callUid_);
863 if (currentCall.status != lrc::api::call::Status::PAUSED) {
864 holdOnOffButton.image = [NSImage imageNamed:@"ic_action_holdoff.png"];
865 } else {
866 holdOnOffButton.image = [NSImage imageNamed:@"ic_action_hold.png"];
867 }
Olivier Soldano994971f2017-12-05 16:30:12 -0500868
869 callModel->togglePause(callUid_);
Alexandre Lisionc5148052015-03-04 15:10:35 -0500870}
871
Alexandre Lision62005312016-01-28 15:55:16 -0500872- (IBAction)toggleAdvancedControls:(id)sender {
873 [advancedButton setPressed:!advancedButton.isPressed];
874 [advancedPanel setHidden:![advancedButton isPressed]];
875}
876
877- (IBAction)showDialpad:(id)sender {
878 AppDelegate* appDelegate = (AppDelegate *)[[NSApplication sharedApplication] delegate];
879 [appDelegate showDialpad];
880}
881
Alexandre Lision58cab672015-06-09 15:25:40 -0400882-(IBAction)toggleChat:(id)sender;
883{
884 BOOL rightViewCollapsed = [[self splitView] isSubviewCollapsed:[[[self splitView] subviews] objectAtIndex: 1]];
885 if (rightViewCollapsed) {
886 [self uncollapseRightView];
Alexandre Lision58cab672015-06-09 15:25:40 -0400887 } else {
888 [self collapseRightView];
889 }
Alexandre Lision62005312016-01-28 15:55:16 -0500890 [chatButton setPressed:rightViewCollapsed];
Alexandre Lision58cab672015-06-09 15:25:40 -0400891}
892
Alexandre Lisiond18fa272015-06-15 11:18:03 -0400893- (IBAction)muteAudio:(id)sender
894{
Olivier Soldano994971f2017-12-05 16:30:12 -0500895 if (accountInfo_ == nil)
896 return;
897
898 auto* callModel = accountInfo_->callModel.get();
Kateryna Kostiuk54d76972018-06-07 15:45:07 -0400899 auto currentCall = callModel->getCall(callUid_);
900 if (currentCall.audioMuted) {
901 muteAudioButton.image = [NSImage imageNamed:@"ic_action_audio.png"];
902 } else {
903 muteAudioButton.image = [NSImage imageNamed:@"ic_action_mute_audio.png"];
904 }
Olivier Soldano994971f2017-12-05 16:30:12 -0500905 callModel->toggleMedia(callUid_, lrc::api::NewCallModel::Media::AUDIO);
Alexandre Lisiond18fa272015-06-15 11:18:03 -0400906}
907
908- (IBAction)muteVideo:(id)sender
909{
Olivier Soldano994971f2017-12-05 16:30:12 -0500910 if (accountInfo_ == nil)
911 return;
Olivier Soldano994971f2017-12-05 16:30:12 -0500912 auto* callModel = accountInfo_->callModel.get();
Kateryna Kostiuk54d76972018-06-07 15:45:07 -0400913 auto currentCall = callModel->getCall(callUid_);
914 if(!currentCall.isAudioOnly) {
915 if (currentCall.videoMuted) {
916 muteVideoButton.image = [NSImage imageNamed:@"ic_action_video.png"];
917 } else {
918 muteVideoButton.image = [NSImage imageNamed:@"ic_action_mute_video.png"];
919 }
920 }
Olivier Soldano994971f2017-12-05 16:30:12 -0500921 callModel->toggleMedia(callUid_, lrc::api::NewCallModel::Media::VIDEO);
Alexandre Lisiond18fa272015-06-15 11:18:03 -0400922}
Alexandre Lision883719f2015-10-22 17:37:45 -0400923
924- (IBAction)toggleTransferView:(id)sender {
Alexandre Lision89edc6a2015-11-09 11:30:47 -0500925 if (_brokerPopoverVC != nullptr) {
926 [_brokerPopoverVC performClose:self];
927 _brokerPopoverVC = NULL;
Alexandre Lision62005312016-01-28 15:55:16 -0500928 [self.transferButton setPressed:NO];
Alexandre Lision883719f2015-10-22 17:37:45 -0400929 } else {
Alexandre Lision89edc6a2015-11-09 11:30:47 -0500930 auto* brokerVC = [[BrokerVC alloc] initWithMode:BrokerMode::TRANSFER];
931 _brokerPopoverVC = [[NSPopover alloc] init];
932 [_brokerPopoverVC setContentSize:brokerVC.view.frame.size];
933 [_brokerPopoverVC setContentViewController:brokerVC];
934 [_brokerPopoverVC setAnimates:YES];
935 [_brokerPopoverVC setBehavior:NSPopoverBehaviorTransient];
936 [_brokerPopoverVC setDelegate:self];
937 [_brokerPopoverVC showRelativeToRect:[sender bounds] ofView:sender preferredEdge:NSMinYEdge];
Alexandre Lision883719f2015-10-22 17:37:45 -0400938 [videoView setCallDelegate:nil];
939 }
Alexandre Lisionf23ec5a2015-07-16 11:24:06 -0400940}
Alexandre Lisiond18fa272015-06-15 11:18:03 -0400941
Alexandre Lision89edc6a2015-11-09 11:30:47 -0500942- (IBAction)toggleAddParticipantView:(id)sender {
943 if (_brokerPopoverVC != nullptr) {
944 [_brokerPopoverVC performClose:self];
945 _brokerPopoverVC = NULL;
Alexandre Lision62005312016-01-28 15:55:16 -0500946 [self.addParticipantButton setPressed:NO];
Alexandre Lision89edc6a2015-11-09 11:30:47 -0500947 } else {
948 auto* brokerVC = [[BrokerVC alloc] initWithMode:BrokerMode::CONFERENCE];
949 _brokerPopoverVC = [[NSPopover alloc] init];
950 [_brokerPopoverVC setContentSize:brokerVC.view.frame.size];
951 [_brokerPopoverVC setContentViewController:brokerVC];
952 [_brokerPopoverVC setAnimates:YES];
953 [_brokerPopoverVC setBehavior:NSPopoverBehaviorTransient];
954 [_brokerPopoverVC setDelegate:self];
955 [_brokerPopoverVC showRelativeToRect:[sender bounds] ofView:sender preferredEdge:NSMinYEdge];
956 [videoView setCallDelegate:nil];
957 }
958}
959
Alexandre Lision2db8f472015-07-22 15:05:46 -0400960#pragma mark - NSPopOverDelegate
961
Alexandre Lision266fca02015-09-28 14:47:05 -0400962- (void)popoverWillClose:(NSNotification *)notification
963{
Alexandre Lision89edc6a2015-11-09 11:30:47 -0500964 if (_brokerPopoverVC != nullptr) {
965 [_brokerPopoverVC performClose:self];
966 _brokerPopoverVC = NULL;
Alexandre Lision883719f2015-10-22 17:37:45 -0400967 }
Alexandre Lision266fca02015-09-28 14:47:05 -0400968
Alexandre Lision2db8f472015-07-22 15:05:46 -0400969 if (self.addToContactPopover != nullptr) {
970 [self.addToContactPopover performClose:self];
971 self.addToContactPopover = NULL;
972 }
Alexandre Lision883719f2015-10-22 17:37:45 -0400973
Alexandre Lision62005312016-01-28 15:55:16 -0500974 [self.transferButton setPressed:NO];
Alexandre Lision89edc6a2015-11-09 11:30:47 -0500975 [self.addParticipantButton setState:NSOffState];
Alexandre Lision883719f2015-10-22 17:37:45 -0400976}
977
978- (void)popoverDidClose:(NSNotification *)notification
979{
980 [videoView setCallDelegate:self];
Alexandre Lision2db8f472015-07-22 15:05:46 -0400981}
982
983#pragma mark - ContactLinkedDelegate
984
985- (void)contactLinked
986{
987 if (self.addToContactPopover != nullptr) {
988 [self.addToContactPopover performClose:self];
989 self.addToContactPopover = NULL;
990 }
991}
992
Alexandre Lision58cab672015-06-09 15:25:40 -0400993#pragma mark - NSSplitViewDelegate
994
995/* 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.
996 */
997- (BOOL)splitView:(NSSplitView *)splitView shouldCollapseSubview:(NSView *)subview forDoubleClickOnDividerAtIndex:(NSInteger)dividerIndex;
998{
999 NSView* rightView = [[splitView subviews] objectAtIndex:1];
1000 return ([subview isEqual:rightView]);
1001}
1002
1003
1004- (BOOL)splitView:(NSSplitView *)splitView canCollapseSubview:(NSView *)subview;
1005{
1006 NSView* rightView = [[splitView subviews] objectAtIndex:1];
1007 return ([subview isEqual:rightView]);
1008}
1009
1010
Alexandre Lisiona1eee3c2015-08-10 13:44:51 -04001011# pragma mark - CallnDelegate
Alexandre Lision58cab672015-06-09 15:25:40 -04001012
1013- (void) callShouldToggleFullScreen
1014{
1015 if(self.splitView.isInFullScreenMode)
1016 [self.splitView exitFullScreenModeWithOptions:nil];
1017 else {
1018 NSApplicationPresentationOptions options = NSApplicationPresentationDefault +NSApplicationPresentationAutoHideDock +
1019 NSApplicationPresentationAutoHideMenuBar + NSApplicationPresentationAutoHideToolbar;
1020 NSDictionary *opts = [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithUnsignedInt:options],
1021 NSFullScreenModeApplicationPresentationOptions, nil];
1022
1023 [self.splitView enterFullScreenMode:[NSScreen mainScreen] withOptions:opts];
1024 }
1025}
1026
Alexandre Lisiona1eee3c2015-08-10 13:44:51 -04001027-(void) mouseIsMoving:(BOOL) move
1028{
1029 [[controlsPanel animator] setAlphaValue:move]; // fade out
1030 [[headerContainer animator] setAlphaValue:move];
1031}
1032
Kateryna Kostiuk89398b62018-11-20 16:45:04 -05001033- (BOOL)splitView:(NSSplitView *)splitView shouldHideDividerAtIndex:(NSInteger)dividerIndex
1034{
1035 return YES;
1036}
1037
Alexandre Lisionc5148052015-03-04 15:10:35 -05001038@end