blob: a08f77c01dfb035d8b7ae86a74da89009efe3156 [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>
Alexandre Lision210fe212016-01-27 11:15:13 -050027#import <QPixmap>
28
29///LRC
Alexandre Lisionc5148052015-03-04 15:10:35 -050030#import <video/previewmanager.h>
31#import <video/renderer.h>
Olivier Soldano994971f2017-12-05 16:30:12 -050032#import <api/newcallmodel.h>
33#import <api/call.h>
34#import <api/conversationmodel.h>
Alexandre Lision210fe212016-01-27 11:15:13 -050035#import <globalinstances.h>
Alexandre Lisionc5148052015-03-04 15:10:35 -050036
Alexandre Lision62005312016-01-28 15:55:16 -050037#import "AppDelegate.h"
Alexandre Lisionf47a2562015-06-15 15:48:29 -040038#import "views/ITProgressIndicator.h"
Alexandre Lision74dd47f2015-04-14 13:47:42 -040039#import "views/CallView.h"
Kateryna Kostiuk81afce72018-10-30 16:40:50 -040040#import "views/NSColor+RingTheme.h"
Alexandre Lision210fe212016-01-27 11:15:13 -050041#import "delegates/ImageManipulationDelegate.h"
Alexandre Lision16d9c0a2015-08-10 12:05:15 -040042#import "ChatVC.h"
Alexandre Lision4baba4c2016-02-11 13:00:57 -050043#import "views/IconButton.h"
Anthony Léonard14e7bf32017-06-08 08:13:16 -040044#import "views/CallLayer.h"
Anthony Léonard6f819752018-01-05 09:53:40 -050045#import "utils.h"
Alexandre Lision74dd47f2015-04-14 13:47:42 -040046
Alexandre Lisionc5148052015-03-04 15:10:35 -050047@interface RendererConnectionsHolder : NSObject
48
49@property QMetaObject::Connection frameUpdated;
50@property QMetaObject::Connection started;
51@property QMetaObject::Connection stopped;
52
53@end
54
55@implementation RendererConnectionsHolder
56
57@end
58
Kateryna Kostiuk1ba5a012018-12-12 17:07:38 -050059@interface CurrentCallVC () <NSPopoverDelegate> {
Olivier Soldano994971f2017-12-05 16:30:12 -050060 std::string convUid_;
61 std::string callUid_;
62 const lrc::api::account::Info *accountInfo_;
Anthony Léonard66a050c2018-01-03 16:32:34 -050063 NSTimer* refreshDurationTimer;
Olivier Soldano994971f2017-12-05 16:30:12 -050064}
Alexandre Lisionc5148052015-03-04 15:10:35 -050065
Alexandre Lision210fe212016-01-27 11:15:13 -050066// Main container
67@property (unsafe_unretained) IBOutlet NSSplitView* splitView;
Kateryna Kostiuk89398b62018-11-20 16:45:04 -050068@property (unsafe_unretained) IBOutlet NSView* backgroundImage;
Kateryna Kostiuk81afce72018-10-30 16:40:50 -040069@property (unsafe_unretained) IBOutlet NSBox* bluerBackgroundEffect;
Alexandre Lision210fe212016-01-27 11:15:13 -050070
Alexandre Lision89edc6a2015-11-09 11:30:47 -050071// Header info
72@property (unsafe_unretained) IBOutlet NSView* headerContainer;
Alexandre Lision883719f2015-10-22 17:37:45 -040073@property (unsafe_unretained) IBOutlet NSTextField* personLabel;
74@property (unsafe_unretained) IBOutlet NSTextField* stateLabel;
Alexandre Lision89edc6a2015-11-09 11:30:47 -050075@property (unsafe_unretained) IBOutlet NSTextField* timeSpentLabel;
Alexandre Lision210fe212016-01-27 11:15:13 -050076@property (unsafe_unretained) IBOutlet NSImageView* personPhoto;
Alexandre Lision89edc6a2015-11-09 11:30:47 -050077
Andreas Traczyk77a50d52018-05-08 17:47:31 -040078@property (unsafe_unretained) IBOutlet NSImageView* outgoingPhoto;
79@property (unsafe_unretained) IBOutlet NSTextField* outgoingPersonLabel;
80@property (unsafe_unretained) IBOutlet NSTextField* outgoingStateLabel;
81@property (unsafe_unretained) IBOutlet NSTextField* outgoingId;
82
Alexandre Lision89edc6a2015-11-09 11:30:47 -050083// Call Controls
84@property (unsafe_unretained) IBOutlet NSView* controlsPanel;
Alexandre Lision62005312016-01-28 15:55:16 -050085
Alexandre Lision4baba4c2016-02-11 13:00:57 -050086@property (unsafe_unretained) IBOutlet IconButton* holdOnOffButton;
87@property (unsafe_unretained) IBOutlet IconButton* hangUpButton;
88@property (unsafe_unretained) IBOutlet IconButton* recordOnOffButton;
89@property (unsafe_unretained) IBOutlet IconButton* pickUpButton;
90@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 Lision62005312016-01-28 15:55:16 -050097@property (unsafe_unretained) IBOutlet NSView* advancedPanel;
98@property (unsafe_unretained) IBOutlet IconButton* advancedButton;
Kateryna Kostiuk81afce72018-10-30 16:40:50 -040099@property (assign) IBOutlet NSLayoutConstraint* callRecordButtonMarginLeft;
Alexandre Lision89edc6a2015-11-09 11:30:47 -0500100
Alexandre Lision2db8f472015-07-22 15:05:46 -0400101@property (strong) NSPopover* addToContactPopover;
Alexandre Lision89edc6a2015-11-09 11:30:47 -0500102@property (strong) NSPopover* brokerPopoverVC;
Alexandre Lision883719f2015-10-22 17:37:45 -0400103@property (strong) IBOutlet ChatVC* chatVC;
Alexandre Lisionf23ec5a2015-07-16 11:24:06 -0400104
Alexandre Lision210fe212016-01-27 11:15:13 -0500105// Ringing call panel
106@property (unsafe_unretained) IBOutlet NSView* ringingPanel;
107@property (unsafe_unretained) IBOutlet NSImageView* incomingPersonPhoto;
108@property (unsafe_unretained) IBOutlet NSTextField* incomingDisplayName;
109
110// Outgoing call panel
111@property (unsafe_unretained) IBOutlet NSView* outgoingPanel;
112@property (unsafe_unretained) IBOutlet ITProgressIndicator *loadingIndicator;
Alexandre Lisionc5148052015-03-04 15:10:35 -0500113
Kateryna Kostiuk81afce72018-10-30 16:40:50 -0400114// audio only view
115@property (unsafe_unretained) IBOutlet NSView* audioCallView;
116@property (unsafe_unretained) IBOutlet NSImageView* audioCallPhoto;
117@property (unsafe_unretained) IBOutlet NSTextField* audioCallTime;
118@property (unsafe_unretained) IBOutlet NSTextField* audioCallPersonLabel;
119@property (unsafe_unretained) IBOutlet NSTextField* audioCallPersonId;
120
Alexandre Lisionc5148052015-03-04 15:10:35 -0500121// Video
Alexandre Lision58cab672015-06-09 15:25:40 -0400122@property (unsafe_unretained) IBOutlet CallView *videoView;
Alexandre Lision58cab672015-06-09 15:25:40 -0400123@property (unsafe_unretained) IBOutlet NSView *previewView;
Alexandre Lisionc5148052015-03-04 15:10:35 -0500124
125@property RendererConnectionsHolder* previewHolder;
126@property RendererConnectionsHolder* videoHolder;
Alexandre Lisionef6333a2015-03-24 12:30:31 -0400127@property QMetaObject::Connection videoStarted;
Alexandre Lision4d9905e2016-01-22 12:01:36 -0500128@property QMetaObject::Connection selectedCallChanged;
Alexandre Lisionb65c0272015-07-22 15:51:29 -0400129@property QMetaObject::Connection messageConnection;
130@property QMetaObject::Connection mediaAddedConnection;
Kateryna Kostiuk133364b2018-10-31 11:36:08 -0400131@property QMetaObject::Connection profileUpdatedConnection;
Alexandre Lisionc5148052015-03-04 15:10:35 -0500132
133@end
134
135@implementation CurrentCallVC
Olivier Soldano994971f2017-12-05 16:30:12 -0500136@synthesize personLabel, personPhoto, stateLabel, holdOnOffButton, hangUpButton,
Alexandre Lision89edc6a2015-11-09 11:30:47 -0500137 recordOnOffButton, pickUpButton, chatButton, transferButton, addParticipantButton, timeSpentLabel,
Olivier Soldano994971f2017-12-05 16:30:12 -0500138 muteVideoButton, muteAudioButton, controlsPanel, advancedPanel, advancedButton, headerContainer, videoView,
Kateryna Kostiuk81afce72018-10-30 16:40:50 -0400139 incomingDisplayName, incomingPersonPhoto, previewView, splitView, loadingIndicator, ringingPanel,
140 outgoingPanel, outgoingPersonLabel, outgoingStateLabel, outgoingPhoto, outgoingId,
141 callRecordButtonMarginLeft, audioCallView, audioCallPhoto, audioCallTime, audioCallPersonLabel, audioCallPersonId, backgroundImage, bluerBackgroundEffect;
Alexandre Lisionc5148052015-03-04 15:10:35 -0500142
143@synthesize previewHolder;
144@synthesize videoHolder;
145
Olivier Soldano994971f2017-12-05 16:30:12 -0500146-(void) setCurrentCall:(const std::string&)callUid
147 conversation:(const std::string&)convUid
148 account:(const lrc::api::account::Info*)account;
Alexandre Lisionc5148052015-03-04 15:10:35 -0500149{
Olivier Soldano994971f2017-12-05 16:30:12 -0500150 if(account == nil)
151 return;
Alexandre Lisionef6333a2015-03-24 12:30:31 -0400152
Olivier Soldano994971f2017-12-05 16:30:12 -0500153 auto* callModel = account->callModel.get();
Alexandre Lisionc5148052015-03-04 15:10:35 -0500154
Olivier Soldano994971f2017-12-05 16:30:12 -0500155 if (not callModel->hasCall(callUid)){
156 callUid_.clear();
Alexandre Lision2db8f472015-07-22 15:05:46 -0400157 return;
158 }
Olivier Soldano994971f2017-12-05 16:30:12 -0500159 callUid_ = callUid;
160 convUid_ = convUid;
161 accountInfo_ = account;
Anthony Léonard4b0935f2017-12-26 12:05:22 -0500162 [self.chatVC setConversationUid:convUid model:account->conversationModel.get()];
Kateryna Kostiuk54d76972018-06-07 15:45:07 -0400163 auto currentCall = callModel->getCall(callUid_);
Kateryna Kostiuk81afce72018-10-30 16:40:50 -0400164 [muteVideoButton setHidden: currentCall.isAudioOnly ? YES: NO];
165 callRecordButtonMarginLeft.constant = currentCall.isAudioOnly ? -40.0f: 10.0f;
166 [previewView setHidden: YES];
Kateryna Kostiukd76f6202018-09-11 17:15:28 -0400167 videoView.callId = callUid;
Alexandre Lisionc5148052015-03-04 15:10:35 -0500168}
169
Kateryna Kostiuk732b4062018-11-28 09:59:28 -0500170- (void) ensureLayoutForCallStatus:(lrc::api::call::Status) status {
171 using Status = lrc::api::call::Status;
172 switch (status) {
173 case Status::IN_PROGRESS:
174 if (![videoView.layer isKindOfClass:[CallLayer class]]) {
175 [videoView setLayer:[[CallLayer alloc] init]];
176 }
177 break;
178 default:
179 if ([videoView.layer isKindOfClass:[CallLayer class]]) {
180 [videoView setLayer:[CALayer layer]];
181 [videoView.layer setBackgroundColor:[[NSColor blackColor] CGColor]];
182 }
183 break;
184 }
185 holdOnOffButton.image = status == lrc::api::call::Status::PAUSED ?
186 [NSImage imageNamed:@"ic_action_holdoff.png"] : [NSImage imageNamed:@"ic_action_hold.png"];
187}
188
Alexandre Lisionc5148052015-03-04 15:10:35 -0500189- (void)awakeFromNib
190{
191 NSLog(@"INIT CurrentCall VC");
Alexandre Lisionef6333a2015-03-24 12:30:31 -0400192 [self.view setWantsLayer:YES];
Alexandre Lisionc5148052015-03-04 15:10:35 -0500193
Alexandre Lisionc5148052015-03-04 15:10:35 -0500194 [previewView setWantsLayer:YES];
Alexandre Lision22d615b2016-01-25 12:54:34 -0500195 [previewView.layer setBackgroundColor:[NSColor blackColor].CGColor];
196 [previewView.layer setContentsGravity:kCAGravityResizeAspectFill];
197 [previewView.layer setFrame:previewView.frame];
Alexandre Lisionc5148052015-03-04 15:10:35 -0500198
199 [controlsPanel setWantsLayer:YES];
Alexandre Lisionc5148052015-03-04 15:10:35 -0500200 [controlsPanel.layer setBackgroundColor:[NSColor clearColor].CGColor];
201 [controlsPanel.layer setFrame:controlsPanel.frame];
202
Alexandre Lisionef6333a2015-03-24 12:30:31 -0400203 previewHolder = [[RendererConnectionsHolder alloc] init];
204 videoHolder = [[RendererConnectionsHolder alloc] init];
205
Alexandre Lisionf47a2562015-06-15 15:48:29 -0400206 [loadingIndicator setColor:[NSColor whiteColor]];
Andreas Traczyk77a50d52018-05-08 17:47:31 -0400207 [loadingIndicator setNumberOfLines:200];
208 [loadingIndicator setWidthOfLine:4];
209 [loadingIndicator setLengthOfLine:4];
210 [loadingIndicator setInnerMargin:59];
Alexandre Lisionf47a2562015-06-15 15:48:29 -0400211
Alexandre Lisiona1eee3c2015-08-10 13:44:51 -0400212 [self.videoView setCallDelegate:self];
Kateryna Kostiuk1004caa2018-03-29 13:48:07 -0400213 CGColor* color = [[[NSColor blackColor] colorWithAlphaComponent:0.2] CGColor];
214 [headerContainer.layer setBackgroundColor:color];
Kateryna Kostiuk81afce72018-10-30 16:40:50 -0400215 [bluerBackgroundEffect setWantsLayer:YES];
216 bluerBackgroundEffect.alphaValue = 0.6;
217 [audioCallView setWantsLayer:YES];
218 [audioCallView.layer setBackgroundColor: [[NSColor clearColor] CGColor]];
Kateryna Kostiuk89398b62018-11-20 16:45:04 -0500219 [backgroundImage setWantsLayer: YES];
220 backgroundImage.layer.contentsGravity = kCAGravityResizeAspectFill;
Alexandre Lisionc5148052015-03-04 15:10:35 -0500221}
222
Anthony Léonard66a050c2018-01-03 16:32:34 -0500223-(void) updateDurationLabel
224{
225 if (accountInfo_ != nil) {
226 auto* callModel = accountInfo_->callModel.get();
227 if (callModel->hasCall(callUid_)) {
228 auto& callStatus = callModel->getCall(callUid_).status;
229 if (callStatus != lrc::api::call::Status::ENDED &&
230 callStatus != lrc::api::call::Status::TERMINATING &&
231 callStatus != lrc::api::call::Status::INVALID) {
Kateryna Kostiuk81afce72018-10-30 16:40:50 -0400232 if(callModel->getCall(callUid_).isAudioOnly) {
233 [audioCallTime setStringValue:@(callModel->getFormattedCallDuration(callUid_).c_str())];
234 } else {
235 [timeSpentLabel setStringValue:@(callModel->getFormattedCallDuration(callUid_).c_str())];
236 }
Anthony Léonard66a050c2018-01-03 16:32:34 -0500237 return;
238 }
239 }
240 }
241
242 // If call is not running anymore or accountInfo_ is not set for any reason
243 // we stop the refresh loop
244 [refreshDurationTimer invalidate];
245 refreshDurationTimer = nil;
246 [timeSpentLabel setHidden:YES];
Kateryna Kostiuk81afce72018-10-30 16:40:50 -0400247 [audioCallView setHidden:YES];
Anthony Léonard66a050c2018-01-03 16:32:34 -0500248}
249
Andreas Traczyk77a50d52018-05-08 17:47:31 -0400250-(void) updateCall
Alexandre Lisionc5148052015-03-04 15:10:35 -0500251{
Olivier Soldano994971f2017-12-05 16:30:12 -0500252 if (accountInfo_ == nil)
253 return;
Alexandre Lisionbb5dbcd2015-07-09 16:36:47 -0400254
Olivier Soldano994971f2017-12-05 16:30:12 -0500255 auto* callModel = accountInfo_->callModel.get();
256 if (not callModel->hasCall(callUid_)) {
257 return;
258 }
Alexandre Lision89edc6a2015-11-09 11:30:47 -0500259
Olivier Soldano994971f2017-12-05 16:30:12 -0500260 auto currentCall = callModel->getCall(callUid_);
261 NSLog(@"\n status %@ \n",@(lrc::api::call::to_string(currentCall.status).c_str()));
Anthony Léonard8ee26782018-01-05 10:23:04 -0500262 auto convIt = getConversationFromUid(convUid_, *accountInfo_->conversationModel);
Andreas Traczyk77a50d52018-05-08 17:47:31 -0400263 if (convIt != accountInfo_->conversationModel->allFilteredConversations().end()) {
264 NSString* bestName = bestNameForConversation(*convIt, *accountInfo_->conversationModel);
265 [personLabel setStringValue:bestName];
266 [outgoingPersonLabel setStringValue:bestName];
Kateryna Kostiuk81afce72018-10-30 16:40:50 -0400267 [audioCallPersonLabel setStringValue:bestName];
Andreas Traczyk77a50d52018-05-08 17:47:31 -0400268 NSString* ringID = bestIDForConversation(*convIt, *accountInfo_->conversationModel);
269 if([bestName isEqualToString:ringID]) {
270 ringID = @"";
271 }
272 [outgoingId setStringValue:ringID];
Kateryna Kostiuk81afce72018-10-30 16:40:50 -0400273 [audioCallPersonId setStringValue:ringID];
Andreas Traczyk77a50d52018-05-08 17:47:31 -0400274 }
275
Anthony Léonard66a050c2018-01-03 16:32:34 -0500276 [timeSpentLabel setStringValue:@(callModel->getFormattedCallDuration(callUid_).c_str())];
Kateryna Kostiuk81afce72018-10-30 16:40:50 -0400277 [audioCallTime setStringValue:@(callModel->getFormattedCallDuration(callUid_).c_str())];
Anthony Léonard66a050c2018-01-03 16:32:34 -0500278 if (refreshDurationTimer == nil)
279 refreshDurationTimer = [NSTimer scheduledTimerWithTimeInterval:1.0
280 target:self
281 selector:@selector(updateDurationLabel)
282 userInfo:nil
283 repeats:YES];
Olivier Soldano994971f2017-12-05 16:30:12 -0500284 [stateLabel setStringValue:@(to_string(currentCall.status).c_str())];
Andreas Traczyk77a50d52018-05-08 17:47:31 -0400285 [outgoingStateLabel setStringValue:@(to_string(currentCall.status).c_str())];
Alexandre Lisionc5148052015-03-04 15:10:35 -0500286
Olivier Soldano994971f2017-12-05 16:30:12 -0500287 // Default values for this views
288 [loadingIndicator setHidden:YES];
Olivier Soldano994971f2017-12-05 16:30:12 -0500289 [ringingPanel setHidden:YES];
290 [outgoingPanel setHidden:YES];
291 [controlsPanel setHidden:NO];
292 [headerContainer setHidden:NO];
Anthony Léonard4b0935f2017-12-26 12:05:22 -0500293 [chatButton setHidden:NO];
Kateryna Kostiuk81afce72018-10-30 16:40:50 -0400294 [self setBackground];
Alexandre Lisionc5148052015-03-04 15:10:35 -0500295
Olivier Soldano994971f2017-12-05 16:30:12 -0500296 using Status = lrc::api::call::Status;
Kateryna Kostiuk732b4062018-11-28 09:59:28 -0500297 [self ensureLayoutForCallStatus:currentCall.status];
Olivier Soldano994971f2017-12-05 16:30:12 -0500298 switch (currentCall.status) {
299 case Status::SEARCHING:
Olivier Soldano994971f2017-12-05 16:30:12 -0500300 case Status::CONNECTING:
Andreas Traczyk77a50d52018-05-08 17:47:31 -0400301 [headerContainer setHidden:YES];
302 [outgoingPanel setHidden:NO];
303 [outgoingPhoto setHidden:NO];
304 [self setupContactInfo:outgoingPhoto];
Olivier Soldano994971f2017-12-05 16:30:12 -0500305 [loadingIndicator setHidden:NO];
Andreas Traczyk77a50d52018-05-08 17:47:31 -0400306 [controlsPanel setHidden:YES];
Olivier Soldano994971f2017-12-05 16:30:12 -0500307 break;
308 case Status::INCOMING_RINGING:
Andreas Traczyk77a50d52018-05-08 17:47:31 -0400309 [outgoingPhoto setHidden:YES];
Olivier Soldano994971f2017-12-05 16:30:12 -0500310 [controlsPanel setHidden:YES];
311 [outgoingPanel setHidden:YES];
312 [self setupIncoming:callUid_];
313 break;
314 case Status::OUTGOING_RINGING:
315 [controlsPanel setHidden:YES];
316 [outgoingPanel setHidden:NO];
Andreas Traczyk77a50d52018-05-08 17:47:31 -0400317 [loadingIndicator setHidden:NO];
318 [headerContainer setHidden:YES];
Olivier Soldano994971f2017-12-05 16:30:12 -0500319 break;
Andreas Traczyk77a50d52018-05-08 17:47:31 -0400320 /*case Status::CONFERENCE:
321 [self setupConference:currentCall];
322 break;*/
Olivier Soldano994971f2017-12-05 16:30:12 -0500323 case Status::PAUSED:
Olivier Soldano994971f2017-12-05 16:30:12 -0500324 case Status::INACTIVE:
325 case Status::IN_PROGRESS:
Andreas Traczyk77a50d52018-05-08 17:47:31 -0400326 // change constraints (uncollapse avatar)
Kateryna Kostiuk81afce72018-10-30 16:40:50 -0400327 if(currentCall.isAudioOnly) {
328 [self setUpAudioOnlyView];
329 } else {
330 [self setUpVideoCallView];
331 }
Andreas Traczyk77a50d52018-05-08 17:47:31 -0400332 break;
Olivier Soldano994971f2017-12-05 16:30:12 -0500333 case Status::CONNECTED:
Olivier Soldano994971f2017-12-05 16:30:12 -0500334 break;
335 case Status::ENDED:
336 case Status::TERMINATING:
337 case Status::INVALID:
Olivier Soldano994971f2017-12-05 16:30:12 -0500338 break;
339 }
Alexandre Lision4d9905e2016-01-22 12:01:36 -0500340}
341
Kateryna Kostiuk81afce72018-10-30 16:40:50 -0400342-(void) setUpVideoCallView {
343 [self setupContactInfo:personPhoto];
344 [timeSpentLabel setHidden:NO];
345 [outgoingPhoto setHidden:YES];
346 [headerContainer setHidden:NO];
347 [previewView setHidden: NO];
348 [bluerBackgroundEffect setHidden:YES];
Kateryna Kostiuk81afce72018-10-30 16:40:50 -0400349 [backgroundImage setHidden:YES];
350}
351
352-(void) setUpAudioOnlyView {
353 [audioCallView setHidden:NO];
354 [headerContainer setHidden:YES];
355 [audioCallPhoto setImage: [self getContactImageOfSize:120.0 withDefaultAvatar:YES]];
356}
357
358-(void) setBackground {
Andreas Traczyk77a50d52018-05-08 17:47:31 -0400359 auto* convModel = accountInfo_->conversationModel.get();
360 auto it = getConversationFromUid(convUid_, *convModel);
Kateryna Kostiuk81afce72018-10-30 16:40:50 -0400361 NSImage *image= [self getContactImageOfSize:120.0 withDefaultAvatar:NO];
362 if(image) {
Kateryna Kostiuk89398b62018-11-20 16:45:04 -0500363 CIImage * ciImage = [[CIImage alloc] initWithData:[image TIFFRepresentation]];
364 CIContext *context = [[CIContext alloc] init];
365 CIFilter *clamp = [CIFilter filterWithName:@"CIAffineClamp"];
366 [clamp setValue:[NSAffineTransform transform] forKey:@"inputTransform"];
367 [clamp setValue:ciImage forKey: kCIInputImageKey];
Kateryna Kostiuk81afce72018-10-30 16:40:50 -0400368 CIFilter* bluerFilter = [CIFilter filterWithName:@"CIGaussianBlur"];
369 [bluerFilter setDefaults];
Kateryna Kostiuk89398b62018-11-20 16:45:04 -0500370 [bluerFilter setValue:[NSNumber numberWithFloat: 9] forKey:@"inputRadius"];
371 [bluerFilter setValue:[clamp valueForKey:kCIOutputImageKey] forKey: kCIInputImageKey];
372 CIImage *result = [bluerFilter valueForKey:kCIOutputImageKey];
373 CGRect extent = [result extent];
374 CGImageRef cgImage = [context createCGImage:result fromRect: [ciImage extent]];
375 NSImage *bluredImage = [[NSImage alloc] initWithCGImage:cgImage size:NSSizeFromCGSize(CGSizeMake(image.size.width, image.size.height))];
376 backgroundImage.layer.contents = bluredImage;
Kateryna Kostiuk81afce72018-10-30 16:40:50 -0400377 [backgroundImage setHidden:NO];
378 } else {
Kateryna Kostiuk81afce72018-10-30 16:40:50 -0400379 [bluerBackgroundEffect setFillColor:[NSColor ringDarkGrey]];
380 [backgroundImage setHidden:YES];
Andreas Traczyk77a50d52018-05-08 17:47:31 -0400381 }
382}
383
Kateryna Kostiuk133364b2018-10-31 11:36:08 -0400384-(NSImage *) getContactImageOfSize: (double) size withDefaultAvatar:(BOOL) shouldDrawDefault {
Kateryna Kostiuk81afce72018-10-30 16:40:50 -0400385 auto* convModel = accountInfo_->conversationModel.get();
386 auto convIt = getConversationFromUid(convUid_, *convModel);
387 if (convIt == convModel->allFilteredConversations().end()) {
388 return nil;
389 }
390 if(shouldDrawDefault) {
391 auto& imgManip = reinterpret_cast<Interfaces::ImageManipulationDelegate&>(GlobalInstances::pixmapManipulator());
392 QVariant photo = imgManip.conversationPhoto(*convIt, *accountInfo_, QSize(size, size), NO);
393 return QtMac::toNSImage(qvariant_cast<QPixmap>(photo));
394 }
395 auto contact = accountInfo_->contactModel->getContact(convIt->participants[0]);
396 NSData *imageData = [[NSData alloc] initWithBase64EncodedString:@(contact.profileInfo.avatar.c_str()) options:NSDataBase64DecodingIgnoreUnknownCharacters];
397 return [[NSImage alloc] initWithData:imageData];
398}
399
400
401-(void) setupContactInfo:(NSImageView*)imageView
402{
403 [imageView setImage: [self getContactImageOfSize:120.0 withDefaultAvatar:YES]];
404}
405
Olivier Soldano994971f2017-12-05 16:30:12 -0500406-(void) setupIncoming:(const std::string&) callId
Alexandre Lision4d9905e2016-01-22 12:01:36 -0500407{
Olivier Soldano994971f2017-12-05 16:30:12 -0500408 if (accountInfo_ == nil)
409 return;
410
411 auto* callModel = accountInfo_->callModel.get();
412 if (not callModel->hasCall(callUid_)) {
413 return;
414 }
415
416 auto call = callModel->getCall(callUid_);
417 auto* convModel = accountInfo_->conversationModel.get();
418
419 [ringingPanel setHidden:NO];
420 [controlsPanel setHidden:YES];
421 [headerContainer setHidden:YES];
Anthony Léonard6f819752018-01-05 09:53:40 -0500422 auto it = getConversationFromUid(convUid_, *convModel);
Olivier Soldano994971f2017-12-05 16:30:12 -0500423 if (it != convModel->allFilteredConversations().end()) {
Kateryna Kostiuk81afce72018-10-30 16:40:50 -0400424 [incomingPersonPhoto setImage: [self getContactImageOfSize:120.0 withDefaultAvatar:YES]];
Anthony Léonard8ee26782018-01-05 10:23:04 -0500425 [incomingDisplayName setStringValue:bestNameForConversation(*it, *convModel)];
Olivier Soldano994971f2017-12-05 16:30:12 -0500426 }
427}
428
429
430-(void) setupConference:(Call*) c
431{
432 [videoView setShouldAcceptInteractions:YES];
433 [self.chatButton setHidden:NO];
Olivier Soldano994971f2017-12-05 16:30:12 -0500434 [self.addParticipantButton setHidden:NO];
435 [self.transferButton setHidden:YES];
436}
437
438-(void)collapseRightView
439{
440 NSView *right = [[splitView subviews] objectAtIndex:1];
441 NSView *left = [[splitView subviews] objectAtIndex:0];
442 NSRect leftFrame = [left frame];
443 [right setHidden:YES];
444 [splitView display];
445}
446
447- (void) changeCallSelection:(std::string&)callUid
448{
449 if (accountInfo_ == nil)
450 return;
451
452 auto* callModel = accountInfo_->callModel.get();
453 if (not callModel->hasCall(callUid)) {
454 return;
455 }
456
Alexandre Lision4d9905e2016-01-22 12:01:36 -0500457 QObject::disconnect(self.selectedCallChanged);
Olivier Soldano994971f2017-12-05 16:30:12 -0500458 self.selectedCallChanged = QObject::connect(callModel,
459 &lrc::api::NewCallModel::callStatusChanged,
460 [self](const std::string callId) {
Andreas Traczyk77a50d52018-05-08 17:47:31 -0400461 [self updateCall];
Alexandre Lision4d9905e2016-01-22 12:01:36 -0500462 });
Alexandre Lisionc5148052015-03-04 15:10:35 -0500463}
464
465-(void) connectVideoSignals
466{
Olivier Soldano994971f2017-12-05 16:30:12 -0500467 if (accountInfo_ == nil)
468 return;
469
470 auto* callModel = accountInfo_->callModel.get();
Alexandre Lision89edc6a2015-11-09 11:30:47 -0500471 QObject::disconnect(self.videoStarted);
Olivier Soldano994971f2017-12-05 16:30:12 -0500472 self.videoStarted = QObject::connect(callModel,
473 &lrc::api::NewCallModel::remotePreviewStarted,
474 [self](const std::string& callId, Video::Renderer* renderer) {
Kateryna Kostiukc17db8d2018-10-23 09:48:03 -0400475 [videoView setLayer:[[CallLayer alloc] init]];
Kateryna Kostiuk1004caa2018-03-29 13:48:07 -0400476 [videoView setShouldAcceptInteractions:YES];
477 [self mouseIsMoving: NO];
Olivier Soldano994971f2017-12-05 16:30:12 -0500478 [self connectVideoRenderer:renderer];
479 });
Alexandre Lisionc5148052015-03-04 15:10:35 -0500480
Olivier Soldano994971f2017-12-05 16:30:12 -0500481 if (callModel->hasCall(callUid_)) {
482 if (auto renderer = callModel->getRenderer(callUid_)) {
483 QObject::disconnect(self.videoStarted);
484 [self connectVideoRenderer: renderer];
485 }
Alexandre Lisionc5148052015-03-04 15:10:35 -0500486 }
Alexandre Lisionc5148052015-03-04 15:10:35 -0500487 [self connectPreviewRenderer];
488
489}
490
491-(void) connectPreviewRenderer
492{
Alexandre Lisionef6333a2015-03-24 12:30:31 -0400493 QObject::disconnect(previewHolder.frameUpdated);
494 QObject::disconnect(previewHolder.stopped);
495 QObject::disconnect(previewHolder.started);
Alexandre Lisiond3aa3ad2015-10-23 14:28:41 -0400496 previewHolder.started = QObject::connect(&Video::PreviewManager::instance(),
Alexandre Lisionc5148052015-03-04 15:10:35 -0500497 &Video::PreviewManager::previewStarted,
498 [=](Video::Renderer* renderer) {
Alexandre Lisionc5148052015-03-04 15:10:35 -0500499 QObject::disconnect(previewHolder.frameUpdated);
500 previewHolder.frameUpdated = QObject::connect(renderer,
501 &Video::Renderer::frameUpdated,
502 [=]() {
Alexandre Lisiond3aa3ad2015-10-23 14:28:41 -0400503 [self renderer:Video::PreviewManager::instance().previewRenderer()
Anthony Léonard14e7bf32017-06-08 08:13:16 -0400504 renderFrameForPreviewView:previewView];
Alexandre Lisionc5148052015-03-04 15:10:35 -0500505 });
506 });
507
Alexandre Lisiond3aa3ad2015-10-23 14:28:41 -0400508 previewHolder.stopped = QObject::connect(&Video::PreviewManager::instance(),
Alexandre Lisionc5148052015-03-04 15:10:35 -0500509 &Video::PreviewManager::previewStopped,
510 [=](Video::Renderer* renderer) {
Kateryna Kostiuk732b4062018-11-28 09:59:28 -0500511 QObject::disconnect(previewHolder.frameUpdated);
Alexandre Lisionc5148052015-03-04 15:10:35 -0500512 [previewView.layer setContents:nil];
513 });
514
Alexandre Lisiond3aa3ad2015-10-23 14:28:41 -0400515 previewHolder.frameUpdated = QObject::connect(Video::PreviewManager::instance().previewRenderer(),
Alexandre Lisionc5148052015-03-04 15:10:35 -0500516 &Video::Renderer::frameUpdated,
517 [=]() {
Alexandre Lisiond3aa3ad2015-10-23 14:28:41 -0400518 [self renderer:Video::PreviewManager::instance().previewRenderer()
Anthony Léonard14e7bf32017-06-08 08:13:16 -0400519 renderFrameForPreviewView:previewView];
Alexandre Lisionc5148052015-03-04 15:10:35 -0500520 });
521}
522
523-(void) connectVideoRenderer: (Video::Renderer*)renderer
524{
Alexandre Lisionef6333a2015-03-24 12:30:31 -0400525 QObject::disconnect(videoHolder.frameUpdated);
526 QObject::disconnect(videoHolder.started);
527 QObject::disconnect(videoHolder.stopped);
Olivier Soldano994971f2017-12-05 16:30:12 -0500528
529 if(renderer == nil)
530 return;
531
Alexandre Lisionc5148052015-03-04 15:10:35 -0500532 videoHolder.frameUpdated = QObject::connect(renderer,
533 &Video::Renderer::frameUpdated,
534 [=]() {
Kateryna Kostiuk732b4062018-11-28 09:59:28 -0500535 if(!renderer->isRendering()) {
536 return;
537 }
Anthony Léonard14e7bf32017-06-08 08:13:16 -0400538 [self renderer:renderer renderFrameForDistantView:videoView];
Alexandre Lisionc5148052015-03-04 15:10:35 -0500539 });
540
541 videoHolder.started = QObject::connect(renderer,
542 &Video::Renderer::started,
543 [=]() {
Kateryna Kostiukbcf24e92018-11-20 10:09:06 -0500544 if (![videoView.layer isKindOfClass:[CallLayer class]]) {
545 [videoView setLayer:[[CallLayer alloc] init]];
546 }
Kateryna Kostiuk1004caa2018-03-29 13:48:07 -0400547 [self mouseIsMoving: NO];
548 [videoView setShouldAcceptInteractions:YES];
Alexandre Lisionc5148052015-03-04 15:10:35 -0500549 QObject::disconnect(videoHolder.frameUpdated);
550 videoHolder.frameUpdated = QObject::connect(renderer,
551 &Video::Renderer::frameUpdated,
552 [=]() {
Anthony Léonard14e7bf32017-06-08 08:13:16 -0400553 [self renderer:renderer renderFrameForDistantView:videoView];
Alexandre Lisionc5148052015-03-04 15:10:35 -0500554 });
555 });
556
557 videoHolder.stopped = QObject::connect(renderer,
558 &Video::Renderer::stopped,
559 [=]() {
Kateryna Kostiuk732b4062018-11-28 09:59:28 -0500560 [(CallLayer*)videoView.layer setVideoRunning:NO];
Kateryna Kostiukc17db8d2018-10-23 09:48:03 -0400561 [videoView setLayer:[CALayer layer]];
562 [videoView.layer setBackgroundColor:[[NSColor blackColor] CGColor]];
Kateryna Kostiuk1004caa2018-03-29 13:48:07 -0400563 [self mouseIsMoving: YES];
564 [videoView setShouldAcceptInteractions:NO];
Alexandre Lisionc5148052015-03-04 15:10:35 -0500565 QObject::disconnect(videoHolder.frameUpdated);
Alexandre Lisionc5148052015-03-04 15:10:35 -0500566 });
567}
568
Anthony Léonard14e7bf32017-06-08 08:13:16 -0400569-(void) renderer: (Video::Renderer*)renderer renderFrameForPreviewView:(NSView*) view
Alexandre Lisionc5148052015-03-04 15:10:35 -0500570{
Alexandre Lisionc5148052015-03-04 15:10:35 -0500571 QSize res = renderer->size();
572
Alexandre Lision6731e132015-10-14 14:29:06 -0400573 auto frame_ptr = renderer->currentFrame();
574 auto frame_data = frame_ptr.ptr;
575 if (!frame_data)
576 return;
577
Alexandre Lisionc5148052015-03-04 15:10:35 -0500578 CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
Alexandre Lision6731e132015-10-14 14:29:06 -0400579 CGContextRef newContext = CGBitmapContextCreate(frame_data,
Alexandre Lisionc5148052015-03-04 15:10:35 -0500580 res.width(),
581 res.height(),
582 8,
583 4*res.width(),
584 colorSpace,
585 kCGImageAlphaPremultipliedLast);
586
587
588 CGImageRef newImage = CGBitmapContextCreateImage(newContext);
589
590 /*We release some components*/
591 CGContextRelease(newContext);
592 CGColorSpaceRelease(colorSpace);
593
594 [CATransaction begin];
595 view.layer.contents = (__bridge id)newImage;
596 [CATransaction commit];
597
598 CFRelease(newImage);
599}
600
Anthony Léonard14e7bf32017-06-08 08:13:16 -0400601-(void) renderer: (Video::Renderer*)renderer renderFrameForDistantView:(CallView*) view
602{
Anthony Léonard14e7bf32017-06-08 08:13:16 -0400603 auto frame_ptr = renderer->currentFrame();
604 if (!frame_ptr.ptr)
605 return;
606
607 CallLayer* callLayer = (CallLayer*) view.layer;
Kateryna Kostiukbcf24e92018-11-20 10:09:06 -0500608 if ([callLayer respondsToSelector:@selector(setCurrentFrame:)]) {
609 [callLayer setCurrentFrame:std::move(frame_ptr)];
610 [callLayer setVideoRunning:YES];
611 }
Anthony Léonard14e7bf32017-06-08 08:13:16 -0400612}
613
Alexandre Lisionc5148052015-03-04 15:10:35 -0500614- (void) initFrame
615{
616 [self.view setFrame:self.view.superview.bounds];
617 [self.view setHidden:YES];
618 self.view.layer.position = self.view.frame.origin;
Alexandre Lisionbb5dbcd2015-07-09 16:36:47 -0400619 [self collapseRightView];
Alexandre Lisionc5148052015-03-04 15:10:35 -0500620}
621
622# pragma private IN/OUT animations
623
Olivier Soldano994971f2017-12-05 16:30:12 -0500624-(void)uncollapseRightView
625{
626 NSView *left = [[splitView subviews] objectAtIndex:0];
627 NSView *right = [[splitView subviews] objectAtIndex:1];
628 [right setHidden:NO];
629
630 CGFloat dividerThickness = [splitView dividerThickness];
631
632 // get the different frames
633 NSRect leftFrame = [left frame];
634 NSRect rightFrame = [right frame];
635
636 leftFrame.size.width = (leftFrame.size.width - rightFrame.size.width - dividerThickness);
637 rightFrame.origin.x = leftFrame.size.width + dividerThickness;
638 [left setFrameSize:leftFrame.size];
639 [right setFrame:rightFrame];
640 [splitView display];
641
642 [self.chatVC takeFocus];
643}
644
Alexandre Lisionc5148052015-03-04 15:10:35 -0500645-(void) cleanUp
646{
Anthony Léonard5c287642018-02-21 15:54:19 -0500647 if(self.splitView.isInFullScreenMode)
648 [self.splitView exitFullScreenModeWithOptions:nil];
Alexandre Lisionef6333a2015-03-24 12:30:31 -0400649 QObject::disconnect(videoHolder.frameUpdated);
650 QObject::disconnect(videoHolder.started);
651 QObject::disconnect(videoHolder.stopped);
652 QObject::disconnect(previewHolder.frameUpdated);
653 QObject::disconnect(previewHolder.stopped);
654 QObject::disconnect(previewHolder.started);
Anthony Léonardc0013bb2018-02-20 16:23:02 -0500655 QObject::disconnect(self.messageConnection);
Alexandre Lisionc5148052015-03-04 15:10:35 -0500656 [previewView.layer setContents:nil];
Kateryna Kostiuk81afce72018-10-30 16:40:50 -0400657 [previewView setHidden: YES];
Kateryna Kostiukc17db8d2018-10-23 09:48:03 -0400658 [videoView setLayer:[CALayer layer]];
659 [videoView.layer setBackgroundColor:[[NSColor blackColor] CGColor]];
Alexandre Lision883719f2015-10-22 17:37:45 -0400660
Alexandre Lision89edc6a2015-11-09 11:30:47 -0500661 [self.chatButton setHidden:YES];
Alexandre Lision4baba4c2016-02-11 13:00:57 -0500662 [self.chatButton setPressed:NO];
Alexandre Lision883719f2015-10-22 17:37:45 -0400663 [self collapseRightView];
Alexandre Lision57227772016-01-15 17:19:36 -0500664
665 [personLabel setStringValue:@""];
666 [timeSpentLabel setStringValue:@""];
667 [stateLabel setStringValue:@""];
Kateryna Kostiuk81afce72018-10-30 16:40:50 -0400668 //audio view
669 [audioCallTime setStringValue:@""];
670 [audioCallPersonId setStringValue:@""];
671 [audioCallPersonLabel setStringValue:@""];
672 [audioCallView setHidden:YES];
673 [audioCallPhoto setImage:nil];
674 //background view
675 [bluerBackgroundEffect setHidden:NO];
676 [backgroundImage setHidden:NO];
Kateryna Kostiuk81afce72018-10-30 16:40:50 -0400677 //outgoing view
Andreas Traczyk77a50d52018-05-08 17:47:31 -0400678 [outgoingPersonLabel setStringValue:@""];
679 [outgoingStateLabel setStringValue:@""];
Alexandre Lisionc5148052015-03-04 15:10:35 -0500680}
681
Andreas Traczykecdc4fa2018-03-22 16:11:47 -0400682-(void) setupCallView
683{
684 if (accountInfo_ == nil)
685 return;
686
687 auto* callModel = accountInfo_->callModel.get();
688 auto* convModel = accountInfo_->conversationModel.get();
689
690 // when call comes in we want to show the controls/header
Kateryna Kostiuk1004caa2018-03-29 13:48:07 -0400691 [self mouseIsMoving: YES];
692 [videoView setShouldAcceptInteractions: NO];
Andreas Traczykecdc4fa2018-03-22 16:11:47 -0400693
694 [self connectVideoSignals];
695 /* check if text media is already present */
696 if(not callModel->hasCall(callUid_))
697 return;
698
699 [loadingIndicator setAnimates:YES];
Andreas Traczyk77a50d52018-05-08 17:47:31 -0400700 [self updateCall];
Andreas Traczykecdc4fa2018-03-22 16:11:47 -0400701
702 /* monitor media for messaging text messaging */
703 QObject::disconnect(self.messageConnection);
704 self.messageConnection = QObject::connect(convModel,
705 &lrc::api::ConversationModel::interactionStatusUpdated,
706 [self] (std::string convUid,
707 uint64_t msgId,
708 lrc::api::interaction::Info msg) {
709 if (msg.type == lrc::api::interaction::Type::TEXT) {
710 if(not [[self splitView] isSubviewCollapsed:[[[self splitView] subviews] objectAtIndex: 1]]){
711 return;
712 }
713 [self uncollapseRightView];
714 }
715 });
Kateryna Kostiuk133364b2018-10-31 11:36:08 -0400716 //monitor for updated profile
717 QObject::disconnect(self.profileUpdatedConnection);
718 self.profileUpdatedConnection =
719 QObject::connect(accountInfo_->contactModel.get(),
720 &lrc::api::ContactModel::contactAdded,
721 [self](const std::string &contactUri) {
722 auto convIt = getConversationFromUid(convUid_, *accountInfo_->conversationModel.get());
723 if (convIt == accountInfo_->conversationModel->allFilteredConversations().end()) {
724 return;
725 }
726 if (convIt->participants.empty()) {
727 return;
728
729 }
730 auto& contact = accountInfo_->contactModel->getContact(convIt->participants[0]);
731 if (contact.profileInfo.type == lrc::api::profile::Type::RING && contact.profileInfo.uri == contactUri)
732 accountInfo_->conversationModel->makePermanent(convUid_);
733 [incomingPersonPhoto setImage: [self getContactImageOfSize:120.0 withDefaultAvatar:YES]];
734 [outgoingPhoto setImage: [self getContactImageOfSize:120.0 withDefaultAvatar:YES]];
Kateryna Kostiuk732b4062018-11-28 09:59:28 -0500735 [self.delegate conversationInfoUpdatedFor:convUid_];
Kateryna Kostiuk133364b2018-10-31 11:36:08 -0400736 if(accountInfo_->callModel.get()->getCall(callUid_).isAudioOnly) {
737 [audioCallPhoto setImage: [self getContactImageOfSize:120.0 withDefaultAvatar:YES]];
738 [self setBackground];
739 return;
740 }
741 [personPhoto setImage: [self getContactImageOfSize:120.0 withDefaultAvatar:YES]];
742 });
Andreas Traczykecdc4fa2018-03-22 16:11:47 -0400743}
744
745-(void) showWithAnimation:(BOOL)animate
746{
747 if (!animate) {
748 [self.view setHidden:NO];
749 [self setupCallView];
750 return;
751 }
752
753 CGRect frame = CGRectOffset(self.view.superview.bounds, -self.view.superview.bounds.size.width, 0);
754 [self.view setHidden:NO];
755
756 [CATransaction begin];
757 CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"position"];
758 [animation setFromValue:[NSValue valueWithPoint:frame.origin]];
759 [animation setToValue:[NSValue valueWithPoint:self.view.superview.bounds.origin]];
760 [animation setDuration:0.2f];
761 [animation setTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseIn]];
762 [CATransaction setCompletionBlock:^{
763 [self setupCallView];
764 }];
765
766 [self.view.layer addAnimation:animation forKey:animation.keyPath];
767 [CATransaction commit];
768}
769
770-(void) hideWithAnimation:(BOOL)animate
Alexandre Lisionc5148052015-03-04 15:10:35 -0500771{
Alexandre Lisionc5148052015-03-04 15:10:35 -0500772 if(self.view.frame.origin.x < 0) {
Alexandre Lisionc5148052015-03-04 15:10:35 -0500773 return;
774 }
775
Andreas Traczykecdc4fa2018-03-22 16:11:47 -0400776 if (!animate) {
777 [self.view setHidden:YES];
778 return;
779 }
780
Alexandre Lisionc5148052015-03-04 15:10:35 -0500781 CGRect frame = CGRectOffset(self.view.frame, -self.view.frame.size.width, 0);
782 [CATransaction begin];
783 CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"position"];
784 [animation setFromValue:[NSValue valueWithPoint:self.view.frame.origin]];
785 [animation setToValue:[NSValue valueWithPoint:frame.origin]];
786 [animation setDuration:0.2f];
Alexandre Lision22d615b2016-01-25 12:54:34 -0500787 [animation setTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut]];
Alexandre Lisionc5148052015-03-04 15:10:35 -0500788
789 [CATransaction setCompletionBlock:^{
790 [self.view setHidden:YES];
Alexandre Lisionef6333a2015-03-24 12:30:31 -0400791 // first make sure everything is disconnected
Alexandre Lisionc5148052015-03-04 15:10:35 -0500792 [self cleanUp];
Olivier Soldano994971f2017-12-05 16:30:12 -0500793// if (currentCall_) {
794// [self animateIn];
795// }
Alexandre Lisionc5148052015-03-04 15:10:35 -0500796 }];
797 [self.view.layer addAnimation:animation forKey:animation.keyPath];
Alexandre Lisiona1c6d752015-06-23 12:27:38 -0400798
799 [self.view.layer setPosition:frame.origin];
Alexandre Lisionc5148052015-03-04 15:10:35 -0500800 [CATransaction commit];
801}
802
803/**
804 * Debug purpose
805 */
806-(void) dumpFrame:(CGRect) frame WithName:(NSString*) name
807{
808 NSLog(@"frame %@ : %f %f %f %f \n\n",name ,frame.origin.x, frame.origin.y, frame.size.width, frame.size.height);
809}
810
Alexandre Lision58cab672015-06-09 15:25:40 -0400811#pragma mark - Button methods
812
Alexandre Lisionc5148052015-03-04 15:10:35 -0500813- (IBAction)hangUp:(id)sender {
Olivier Soldano994971f2017-12-05 16:30:12 -0500814 if (accountInfo_ == nil)
815 return;
816
817 auto* callModel = accountInfo_->callModel.get();
818
819 callModel->hangUp(callUid_);
Alexandre Lisionc5148052015-03-04 15:10:35 -0500820}
821
822- (IBAction)accept:(id)sender {
Olivier Soldano994971f2017-12-05 16:30:12 -0500823 if (accountInfo_ == nil)
824 return;
825
Anthony Léonardf584cc52018-01-19 12:00:19 -0500826 // If we accept a conversation with a non trusted contact, we first accept it
827 auto convIt = getConversationFromUid(convUid_, *accountInfo_->conversationModel.get());
828 if (convIt != accountInfo_->conversationModel->allFilteredConversations().end()) {
829 auto& contact = accountInfo_->contactModel->getContact(convIt->participants[0]);
830 if (contact.profileInfo.type == lrc::api::profile::Type::PENDING)
831 accountInfo_->conversationModel->makePermanent(convUid_);
832 }
833
Olivier Soldano994971f2017-12-05 16:30:12 -0500834 auto* callModel = accountInfo_->callModel.get();
835
836 callModel->accept(callUid_);
Alexandre Lisionc5148052015-03-04 15:10:35 -0500837}
838
839- (IBAction)toggleRecording:(id)sender {
Olivier Soldano994971f2017-12-05 16:30:12 -0500840 if (accountInfo_ == nil)
841 return;
842
843 auto* callModel = accountInfo_->callModel.get();
844
845 callModel->toggleAudioRecord(callUid_);
Kateryna Kostiuk54d76972018-06-07 15:45:07 -0400846 [recordOnOffButton setPressed:!recordOnOffButton.isPressed];
Alexandre Lisionc5148052015-03-04 15:10:35 -0500847}
848
849- (IBAction)toggleHold:(id)sender {
Olivier Soldano994971f2017-12-05 16:30:12 -0500850 if (accountInfo_ == nil)
851 return;
852
853 auto* callModel = accountInfo_->callModel.get();
Kateryna Kostiuk54d76972018-06-07 15:45:07 -0400854 auto currentCall = callModel->getCall(callUid_);
855 if (currentCall.status != lrc::api::call::Status::PAUSED) {
856 holdOnOffButton.image = [NSImage imageNamed:@"ic_action_holdoff.png"];
857 } else {
858 holdOnOffButton.image = [NSImage imageNamed:@"ic_action_hold.png"];
859 }
Olivier Soldano994971f2017-12-05 16:30:12 -0500860
861 callModel->togglePause(callUid_);
Alexandre Lisionc5148052015-03-04 15:10:35 -0500862}
863
Alexandre Lision62005312016-01-28 15:55:16 -0500864- (IBAction)toggleAdvancedControls:(id)sender {
865 [advancedButton setPressed:!advancedButton.isPressed];
866 [advancedPanel setHidden:![advancedButton isPressed]];
867}
868
869- (IBAction)showDialpad:(id)sender {
870 AppDelegate* appDelegate = (AppDelegate *)[[NSApplication sharedApplication] delegate];
871 [appDelegate showDialpad];
872}
873
Alexandre Lision58cab672015-06-09 15:25:40 -0400874-(IBAction)toggleChat:(id)sender;
875{
876 BOOL rightViewCollapsed = [[self splitView] isSubviewCollapsed:[[[self splitView] subviews] objectAtIndex: 1]];
877 if (rightViewCollapsed) {
878 [self uncollapseRightView];
Alexandre Lision58cab672015-06-09 15:25:40 -0400879 } else {
880 [self collapseRightView];
881 }
Alexandre Lision62005312016-01-28 15:55:16 -0500882 [chatButton setPressed:rightViewCollapsed];
Alexandre Lision58cab672015-06-09 15:25:40 -0400883}
884
Alexandre Lisiond18fa272015-06-15 11:18:03 -0400885- (IBAction)muteAudio:(id)sender
886{
Olivier Soldano994971f2017-12-05 16:30:12 -0500887 if (accountInfo_ == nil)
888 return;
889
890 auto* callModel = accountInfo_->callModel.get();
Kateryna Kostiuk54d76972018-06-07 15:45:07 -0400891 auto currentCall = callModel->getCall(callUid_);
892 if (currentCall.audioMuted) {
893 muteAudioButton.image = [NSImage imageNamed:@"ic_action_audio.png"];
894 } else {
895 muteAudioButton.image = [NSImage imageNamed:@"ic_action_mute_audio.png"];
896 }
Olivier Soldano994971f2017-12-05 16:30:12 -0500897 callModel->toggleMedia(callUid_, lrc::api::NewCallModel::Media::AUDIO);
Alexandre Lisiond18fa272015-06-15 11:18:03 -0400898}
899
900- (IBAction)muteVideo:(id)sender
901{
Olivier Soldano994971f2017-12-05 16:30:12 -0500902 if (accountInfo_ == nil)
903 return;
Olivier Soldano994971f2017-12-05 16:30:12 -0500904 auto* callModel = accountInfo_->callModel.get();
Kateryna Kostiuk54d76972018-06-07 15:45:07 -0400905 auto currentCall = callModel->getCall(callUid_);
906 if(!currentCall.isAudioOnly) {
907 if (currentCall.videoMuted) {
908 muteVideoButton.image = [NSImage imageNamed:@"ic_action_video.png"];
909 } else {
910 muteVideoButton.image = [NSImage imageNamed:@"ic_action_mute_video.png"];
911 }
912 }
Olivier Soldano994971f2017-12-05 16:30:12 -0500913 callModel->toggleMedia(callUid_, lrc::api::NewCallModel::Media::VIDEO);
Alexandre Lisiond18fa272015-06-15 11:18:03 -0400914}
Alexandre Lision883719f2015-10-22 17:37:45 -0400915
916- (IBAction)toggleTransferView:(id)sender {
Kateryna Kostiuk1ba5a012018-12-12 17:07:38 -0500917
Alexandre Lisionf23ec5a2015-07-16 11:24:06 -0400918}
Alexandre Lisiond18fa272015-06-15 11:18:03 -0400919
Alexandre Lision89edc6a2015-11-09 11:30:47 -0500920- (IBAction)toggleAddParticipantView:(id)sender {
Kateryna Kostiuk1ba5a012018-12-12 17:07:38 -0500921
Alexandre Lision89edc6a2015-11-09 11:30:47 -0500922}
923
Alexandre Lision2db8f472015-07-22 15:05:46 -0400924#pragma mark - NSPopOverDelegate
925
Alexandre Lision266fca02015-09-28 14:47:05 -0400926- (void)popoverWillClose:(NSNotification *)notification
927{
Alexandre Lision89edc6a2015-11-09 11:30:47 -0500928 if (_brokerPopoverVC != nullptr) {
929 [_brokerPopoverVC performClose:self];
930 _brokerPopoverVC = NULL;
Alexandre Lision883719f2015-10-22 17:37:45 -0400931 }
Alexandre Lision266fca02015-09-28 14:47:05 -0400932
Alexandre Lision2db8f472015-07-22 15:05:46 -0400933 if (self.addToContactPopover != nullptr) {
934 [self.addToContactPopover performClose:self];
935 self.addToContactPopover = NULL;
936 }
Alexandre Lision883719f2015-10-22 17:37:45 -0400937
Alexandre Lision62005312016-01-28 15:55:16 -0500938 [self.transferButton setPressed:NO];
Alexandre Lision89edc6a2015-11-09 11:30:47 -0500939 [self.addParticipantButton setState:NSOffState];
Alexandre Lision883719f2015-10-22 17:37:45 -0400940}
941
942- (void)popoverDidClose:(NSNotification *)notification
943{
944 [videoView setCallDelegate:self];
Alexandre Lision2db8f472015-07-22 15:05:46 -0400945}
946
947#pragma mark - ContactLinkedDelegate
948
949- (void)contactLinked
950{
951 if (self.addToContactPopover != nullptr) {
952 [self.addToContactPopover performClose:self];
953 self.addToContactPopover = NULL;
954 }
955}
956
Alexandre Lision58cab672015-06-09 15:25:40 -0400957#pragma mark - NSSplitViewDelegate
958
959/* 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.
960 */
961- (BOOL)splitView:(NSSplitView *)splitView shouldCollapseSubview:(NSView *)subview forDoubleClickOnDividerAtIndex:(NSInteger)dividerIndex;
962{
963 NSView* rightView = [[splitView subviews] objectAtIndex:1];
964 return ([subview isEqual:rightView]);
965}
966
967
968- (BOOL)splitView:(NSSplitView *)splitView canCollapseSubview:(NSView *)subview;
969{
970 NSView* rightView = [[splitView subviews] objectAtIndex:1];
971 return ([subview isEqual:rightView]);
972}
973
974
Alexandre Lisiona1eee3c2015-08-10 13:44:51 -0400975# pragma mark - CallnDelegate
Alexandre Lision58cab672015-06-09 15:25:40 -0400976
977- (void) callShouldToggleFullScreen
978{
979 if(self.splitView.isInFullScreenMode)
980 [self.splitView exitFullScreenModeWithOptions:nil];
981 else {
982 NSApplicationPresentationOptions options = NSApplicationPresentationDefault +NSApplicationPresentationAutoHideDock +
983 NSApplicationPresentationAutoHideMenuBar + NSApplicationPresentationAutoHideToolbar;
984 NSDictionary *opts = [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithUnsignedInt:options],
985 NSFullScreenModeApplicationPresentationOptions, nil];
986
987 [self.splitView enterFullScreenMode:[NSScreen mainScreen] withOptions:opts];
988 }
989}
990
Alexandre Lisiona1eee3c2015-08-10 13:44:51 -0400991-(void) mouseIsMoving:(BOOL) move
992{
993 [[controlsPanel animator] setAlphaValue:move]; // fade out
994 [[headerContainer animator] setAlphaValue:move];
995}
996
Kateryna Kostiuk89398b62018-11-20 16:45:04 -0500997- (BOOL)splitView:(NSSplitView *)splitView shouldHideDividerAtIndex:(NSInteger)dividerIndex
998{
999 return YES;
1000}
1001
Alexandre Lisionc5148052015-03-04 15:10:35 -05001002@end