blob: ba5c5af792c8dabb871b8270ca56a17a35abb71e [file] [log] [blame]
Alexandre Lisionc5148052015-03-04 15:10:35 -05001/*
Alexandre Lision9fe374b2016-01-06 10:17:31 -05002 * Copyright (C) 2015-2016 Savoir-faire Linux Inc.
Alexandre Lisionc5148052015-03-04 15:10:35 -05003 * Author: Alexandre Lision <alexandre.lision@savoirfairelinux.com>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
Alexandre Lisionc5148052015-03-04 15:10:35 -050018 */
19#import "CurrentCallVC.h"
Kateryna Kostiuk00dcbff2019-07-11 15:42:13 -040020extern "C" {
21#import "libavutil/frame.h"
22#import "libavutil/display.h"
23}
Alexandre Lisionc5148052015-03-04 15:10:35 -050024
25#import <QuartzCore/QuartzCore.h>
26
Alexandre Lision210fe212016-01-27 11:15:13 -050027///Qt
28#import <QMimeData>
29#import <QtMacExtras/qmacfunctions.h>
30#import <QtCore/qabstractitemmodel.h>
Alexandre Lision210fe212016-01-27 11:15:13 -050031#import <QPixmap>
Kateryna Kostiuk5d90c3b2019-07-18 12:03:52 -040032#import <QUrl>
Alexandre Lision210fe212016-01-27 11:15:13 -050033
34///LRC
Alexandre Lisionc5148052015-03-04 15:10:35 -050035#import <video/renderer.h>
Olivier Soldano994971f2017-12-05 16:30:12 -050036#import <api/newcallmodel.h>
37#import <api/call.h>
38#import <api/conversationmodel.h>
Kateryna Kostiuk5d90c3b2019-07-18 12:03:52 -040039#import <api/avmodel.h>
Alexandre Lision210fe212016-01-27 11:15:13 -050040#import <globalinstances.h>
Alexandre Lisionc5148052015-03-04 15:10:35 -050041
Alexandre Lision62005312016-01-28 15:55:16 -050042#import "AppDelegate.h"
Alexandre Lisionf47a2562015-06-15 15:48:29 -040043#import "views/ITProgressIndicator.h"
Alexandre Lision74dd47f2015-04-14 13:47:42 -040044#import "views/CallView.h"
Kateryna Kostiuk81afce72018-10-30 16:40:50 -040045#import "views/NSColor+RingTheme.h"
Alexandre Lision210fe212016-01-27 11:15:13 -050046#import "delegates/ImageManipulationDelegate.h"
Alexandre Lision16d9c0a2015-08-10 12:05:15 -040047#import "ChatVC.h"
Alexandre Lision4baba4c2016-02-11 13:00:57 -050048#import "views/IconButton.h"
Anthony Léonard6f819752018-01-05 09:53:40 -050049#import "utils.h"
Kateryna Kostiuk00dcbff2019-07-11 15:42:13 -040050#import "views/CallMTKView.h"
Kateryna Kostiukc5fd3b92019-07-24 10:12:44 -040051#import "VideoCommon.h"
Kateryna Kostiuk88a6ee82019-09-10 17:55:08 -040052#import "views/GradientView.h"
Kateryna Kostiuk6891d4f2019-09-19 17:44:33 -040053#import "views/MovableView.h"
Alexandre Lision74dd47f2015-04-14 13:47:42 -040054
Kateryna Kostiuk1ba5a012018-12-12 17:07:38 -050055@interface CurrentCallVC () <NSPopoverDelegate> {
Olivier Soldano994971f2017-12-05 16:30:12 -050056 std::string convUid_;
57 std::string callUid_;
58 const lrc::api::account::Info *accountInfo_;
Anthony Léonard66a050c2018-01-03 16:32:34 -050059 NSTimer* refreshDurationTimer;
Olivier Soldano994971f2017-12-05 16:30:12 -050060}
Alexandre Lisionc5148052015-03-04 15:10:35 -050061
Alexandre Lision210fe212016-01-27 11:15:13 -050062// Main container
63@property (unsafe_unretained) IBOutlet NSSplitView* splitView;
Kateryna Kostiuk89398b62018-11-20 16:45:04 -050064@property (unsafe_unretained) IBOutlet NSView* backgroundImage;
Kateryna Kostiuk81afce72018-10-30 16:40:50 -040065@property (unsafe_unretained) IBOutlet NSBox* bluerBackgroundEffect;
Alexandre Lision210fe212016-01-27 11:15:13 -050066
Alexandre Lision89edc6a2015-11-09 11:30:47 -050067// Header info
68@property (unsafe_unretained) IBOutlet NSView* headerContainer;
Kateryna Kostiuk2aa39232019-10-10 11:55:11 -040069@property (unsafe_unretained) IBOutlet NSView* headerGradientView;
Alexandre Lision89edc6a2015-11-09 11:30:47 -050070@property (unsafe_unretained) IBOutlet NSTextField* timeSpentLabel;
71
Kateryna Kostiuk9e9f7822019-09-27 11:29:45 -040072// info
73@property (unsafe_unretained) IBOutlet NSStackView* infoContainer;
74@property (unsafe_unretained) IBOutlet NSImageView* contactPhoto;
75@property (unsafe_unretained) IBOutlet NSTextField* contactNameLabel;
76@property (unsafe_unretained) IBOutlet NSTextField* callStateLabel;
77@property (unsafe_unretained) IBOutlet NSTextField* contactIdLabel;
78@property (unsafe_unretained) IBOutlet IconButton* cancelCallButton;
79@property (unsafe_unretained) IBOutlet IconButton* pickUpButton;
80@property (unsafe_unretained) IBOutlet ITProgressIndicator *loadingIndicator;
Andreas Traczyk77a50d52018-05-08 17:47:31 -040081
Alexandre Lision89edc6a2015-11-09 11:30:47 -050082// Call Controls
Kateryna Kostiuk88a6ee82019-09-10 17:55:08 -040083@property (unsafe_unretained) IBOutlet GradientView* controlsPanel;
Kateryna Kostiuk2aa39232019-10-10 11:55:11 -040084@property (unsafe_unretained) IBOutlet NSStackView* controlsStackView;
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;
Alexandre Lision4baba4c2016-02-11 13:00:57 -050089@property (unsafe_unretained) IBOutlet IconButton* muteAudioButton;
90@property (unsafe_unretained) IBOutlet IconButton* muteVideoButton;
Alexandre Lision4baba4c2016-02-11 13:00:57 -050091@property (unsafe_unretained) IBOutlet IconButton* transferButton;
92@property (unsafe_unretained) IBOutlet IconButton* addParticipantButton;
93@property (unsafe_unretained) IBOutlet IconButton* chatButton;
Andreas Traczyk77a50d52018-05-08 17:47:31 -040094@property (unsafe_unretained) IBOutlet IconButton* qualityButton;
Alexandre Lisiond18fa272015-06-15 11:18:03 -040095
Alexandre Lisionc5148052015-03-04 15:10:35 -050096// Video
Alexandre Lision58cab672015-06-09 15:25:40 -040097@property (unsafe_unretained) IBOutlet CallView *videoView;
Kateryna Kostiuk00dcbff2019-07-11 15:42:13 -040098@property (unsafe_unretained) IBOutlet CallMTKView *previewView;
Kateryna Kostiuk6891d4f2019-09-19 17:44:33 -040099@property (unsafe_unretained) IBOutlet MovableView *movableBaseForView;
100@property (unsafe_unretained) IBOutlet NSView* hidePreviewBackground;
101@property (unsafe_unretained) IBOutlet NSButton* hidePreviewButton;
Kateryna Kostiuk00dcbff2019-07-11 15:42:13 -0400102
103@property (unsafe_unretained) IBOutlet CallMTKView *videoMTKView;
Alexandre Lisionc5148052015-03-04 15:10:35 -0500104
Kateryna Kostiuk5d90c3b2019-07-18 12:03:52 -0400105@property RendererConnectionsHolder* renderConnections;
Alexandre Lisionef6333a2015-03-24 12:30:31 -0400106@property QMetaObject::Connection videoStarted;
Alexandre Lision4d9905e2016-01-22 12:01:36 -0500107@property QMetaObject::Connection selectedCallChanged;
Alexandre Lisionb65c0272015-07-22 15:51:29 -0400108@property QMetaObject::Connection messageConnection;
109@property QMetaObject::Connection mediaAddedConnection;
Kateryna Kostiuk133364b2018-10-31 11:36:08 -0400110@property QMetaObject::Connection profileUpdatedConnection;
Alexandre Lisionc5148052015-03-04 15:10:35 -0500111
Kateryna Kostiuk9e9f7822019-09-27 11:29:45 -0400112@property (strong) IBOutlet ChatVC* chatVC;
113
Alexandre Lisionc5148052015-03-04 15:10:35 -0500114@end
115
116@implementation CurrentCallVC
Kateryna Kostiuk5d90c3b2019-07-18 12:03:52 -0400117lrc::api::AVModel* mediaModel;
Kateryna Kostiuk6891d4f2019-09-19 17:44:33 -0400118
119NSInteger const PREVIEW_WIDTH = 185;
120NSInteger const PREVIEW_HEIGHT = 130;
Kateryna Kostiuk2aa39232019-10-10 11:55:11 -0400121NSInteger const HIDE_PREVIEW_BUTTON_SIZE = 25;
Kateryna Kostiuk6891d4f2019-09-19 17:44:33 -0400122NSInteger const PREVIEW_MARGIN = 20;
123
Kateryna Kostiuk2aa39232019-10-10 11:55:11 -0400124@synthesize holdOnOffButton, hangUpButton, recordOnOffButton, pickUpButton, chatButton, transferButton, addParticipantButton, timeSpentLabel, muteVideoButton, muteAudioButton, controlsPanel, headerContainer, videoView, previewView, splitView, loadingIndicator, backgroundImage, bluerBackgroundEffect, hidePreviewButton, hidePreviewBackground, movableBaseForView, infoContainer, contactPhoto, contactNameLabel, callStateLabel, contactIdLabel, cancelCallButton, headerGradientView, controlsStackView;
Alexandre Lisionc5148052015-03-04 15:10:35 -0500125
Kateryna Kostiuk5d90c3b2019-07-18 12:03:52 -0400126@synthesize renderConnections;
Kateryna Kostiuk00dcbff2019-07-11 15:42:13 -0400127CVPixelBufferPoolRef pixelBufferPoolDistantView;
128CVPixelBufferRef pixelBufferDistantView;
129CVPixelBufferPoolRef pixelBufferPoolPreview;
130CVPixelBufferRef pixelBufferPreview;
Alexandre Lisionc5148052015-03-04 15:10:35 -0500131
Olivier Soldano994971f2017-12-05 16:30:12 -0500132-(void) setCurrentCall:(const std::string&)callUid
133 conversation:(const std::string&)convUid
Kateryna Kostiuk5d90c3b2019-07-18 12:03:52 -0400134 account:(const lrc::api::account::Info*)account
135 avModel:(lrc::api::AVModel *)avModel;
Alexandre Lisionc5148052015-03-04 15:10:35 -0500136{
Olivier Soldano994971f2017-12-05 16:30:12 -0500137 if(account == nil)
138 return;
Alexandre Lisionef6333a2015-03-24 12:30:31 -0400139
Kateryna Kostiuk5d90c3b2019-07-18 12:03:52 -0400140 mediaModel = avModel;
Olivier Soldano994971f2017-12-05 16:30:12 -0500141 auto* callModel = account->callModel.get();
Alexandre Lisionc5148052015-03-04 15:10:35 -0500142
Olivier Soldano994971f2017-12-05 16:30:12 -0500143 if (not callModel->hasCall(callUid)){
144 callUid_.clear();
Alexandre Lision2db8f472015-07-22 15:05:46 -0400145 return;
146 }
Olivier Soldano994971f2017-12-05 16:30:12 -0500147 callUid_ = callUid;
148 convUid_ = convUid;
149 accountInfo_ = account;
Anthony Léonard4b0935f2017-12-26 12:05:22 -0500150 [self.chatVC setConversationUid:convUid model:account->conversationModel.get()];
Kateryna Kostiuk54d76972018-06-07 15:45:07 -0400151 auto currentCall = callModel->getCall(callUid_);
Kateryna Kostiuk88a6ee82019-09-10 17:55:08 -0400152 [self setUpButtons: currentCall isRecording: (callModel->isRecording(callUid_) || avModel->getAlwaysRecord())];
Kateryna Kostiuk81afce72018-10-30 16:40:50 -0400153 [previewView setHidden: YES];
Kateryna Kostiukd76f6202018-09-11 17:15:28 -0400154 videoView.callId = callUid;
Kateryna Kostiuk6891d4f2019-09-19 17:44:33 -0400155 [self setUpPrewviewFrame];
Alexandre Lisionc5148052015-03-04 15:10:35 -0500156}
157
Kateryna Kostiuk88a6ee82019-09-10 17:55:08 -0400158-(void) setUpButtons:(lrc::api::call::Info&)callInfo isRecording:(BOOL) isRecording {
159 muteAudioButton.image = callInfo.audioMuted ? [NSImage imageNamed:@"ic_action_mute_audio.png"] :
160 [NSImage imageNamed:@"ic_action_audio.png"];
161 muteVideoButton.image = callInfo.videoMuted ? [NSImage imageNamed:@"ic_action_mute_video.png"] :
162 [NSImage imageNamed:@"ic_action_video.png"];
163 [muteVideoButton setHidden: callInfo.isAudioOnly ? YES: NO];
164 if (isRecording) {
165 [recordOnOffButton startBlinkAnimationfrom:[NSColor buttonBlinkColorColor] to:[NSColor whiteColor] scaleFactor: 1 duration: 1.5];
166 } else {
167 [recordOnOffButton stopBlinkAnimation];
168 }
169}
170
Kateryna Kostiuk6891d4f2019-09-19 17:44:33 -0400171- (void) setUpPrewviewFrame {
172 CGPoint previewOrigin = CGPointMake(self.videoView.frame.size.width - PREVIEW_WIDTH - PREVIEW_MARGIN, PREVIEW_MARGIN);
173 movableBaseForView.frame = CGRectMake(previewOrigin.x, previewOrigin.y, PREVIEW_WIDTH, PREVIEW_HEIGHT);
Kateryna Kostiuk6891d4f2019-09-19 17:44:33 -0400174 self.movableBaseForView.movable = true;
175 previewView.frame = movableBaseForView.bounds;
Kateryna Kostiuk2aa39232019-10-10 11:55:11 -0400176 hidePreviewBackground.frame = [self frameForExpendPreviewButton];
177 hidePreviewButton.contentTintColor = [NSColor blackColor];
Kateryna Kostiuk6891d4f2019-09-19 17:44:33 -0400178}
179
Alexandre Lisionc5148052015-03-04 15:10:35 -0500180- (void)awakeFromNib
181{
Alexandre Lisionef6333a2015-03-24 12:30:31 -0400182 [self.view setWantsLayer:YES];
Alexandre Lisionc5148052015-03-04 15:10:35 -0500183
Kateryna Kostiuk5d90c3b2019-07-18 12:03:52 -0400184 renderConnections = [[RendererConnectionsHolder alloc] init];
Alexandre Lisionef6333a2015-03-24 12:30:31 -0400185
Alexandre Lisionf47a2562015-06-15 15:48:29 -0400186 [loadingIndicator setColor:[NSColor whiteColor]];
Andreas Traczyk77a50d52018-05-08 17:47:31 -0400187 [loadingIndicator setNumberOfLines:200];
188 [loadingIndicator setWidthOfLine:4];
189 [loadingIndicator setLengthOfLine:4];
190 [loadingIndicator setInnerMargin:59];
Alexandre Lisionf47a2562015-06-15 15:48:29 -0400191
Alexandre Lisiona1eee3c2015-08-10 13:44:51 -0400192 [self.videoView setCallDelegate:self];
Kateryna Kostiuk81afce72018-10-30 16:40:50 -0400193 [bluerBackgroundEffect setWantsLayer:YES];
Kateryna Kostiuk89398b62018-11-20 16:45:04 -0500194 [backgroundImage setWantsLayer: YES];
195 backgroundImage.layer.contentsGravity = kCAGravityResizeAspectFill;
Kateryna Kostiuk9e9f7822019-09-27 11:29:45 -0400196 movableBaseForView.wantsLayer = YES;
Kateryna Kostiuk2aa39232019-10-10 11:55:11 -0400197 movableBaseForView.shadow = [[NSShadow alloc] init];
198 movableBaseForView.layer.shadowOpacity = 0.6;
199 movableBaseForView.layer.shadowColor = [[NSColor blackColor] CGColor];
200 movableBaseForView.layer.shadowOffset = NSMakeSize(0, -3);
201 movableBaseForView.layer.shadowRadius = 10;
202 previewView.wantsLayer = YES;
203 previewView.layer.cornerRadius = 5;
204 previewView.layer.masksToBounds = true;
205 hidePreviewBackground.wantsLayer = YES;
206 hidePreviewBackground.layer.cornerRadius = 5;
207 hidePreviewBackground.layer.maskedCorners = kCALayerMinXMinYCorner;
208 hidePreviewBackground.layer.masksToBounds = true;
Kateryna Kostiuk9e9f7822019-09-27 11:29:45 -0400209 movableBaseForView.hostingView = self.videoView;
210 [movableBaseForView setAutoresizingMask: NSViewNotSizable | NSViewMaxXMargin | NSViewMaxYMargin | NSViewMinXMargin | NSViewMinYMargin];
211 [previewView setAutoresizingMask: NSViewNotSizable | NSViewMaxXMargin | NSViewMaxYMargin | NSViewMinXMargin | NSViewMinYMargin];
Alexandre Lisionc5148052015-03-04 15:10:35 -0500212}
213
Anthony Léonard66a050c2018-01-03 16:32:34 -0500214-(void) updateDurationLabel
215{
216 if (accountInfo_ != nil) {
217 auto* callModel = accountInfo_->callModel.get();
218 if (callModel->hasCall(callUid_)) {
219 auto& callStatus = callModel->getCall(callUid_).status;
220 if (callStatus != lrc::api::call::Status::ENDED &&
221 callStatus != lrc::api::call::Status::TERMINATING &&
222 callStatus != lrc::api::call::Status::INVALID) {
Kateryna Kostiuk9e9f7822019-09-27 11:29:45 -0400223 [timeSpentLabel setStringValue:@(callModel->getFormattedCallDuration(callUid_).c_str())];
Anthony Léonard66a050c2018-01-03 16:32:34 -0500224 return;
225 }
226 }
227 }
228
229 // If call is not running anymore or accountInfo_ is not set for any reason
230 // we stop the refresh loop
231 [refreshDurationTimer invalidate];
232 refreshDurationTimer = nil;
Anthony Léonard66a050c2018-01-03 16:32:34 -0500233}
234
Andreas Traczyk77a50d52018-05-08 17:47:31 -0400235-(void) updateCall
Alexandre Lisionc5148052015-03-04 15:10:35 -0500236{
Olivier Soldano994971f2017-12-05 16:30:12 -0500237 if (accountInfo_ == nil)
238 return;
Alexandre Lisionbb5dbcd2015-07-09 16:36:47 -0400239
Olivier Soldano994971f2017-12-05 16:30:12 -0500240 auto* callModel = accountInfo_->callModel.get();
241 if (not callModel->hasCall(callUid_)) {
242 return;
243 }
Alexandre Lision89edc6a2015-11-09 11:30:47 -0500244
Olivier Soldano994971f2017-12-05 16:30:12 -0500245 auto currentCall = callModel->getCall(callUid_);
246 NSLog(@"\n status %@ \n",@(lrc::api::call::to_string(currentCall.status).c_str()));
Anthony Léonard8ee26782018-01-05 10:23:04 -0500247 auto convIt = getConversationFromUid(convUid_, *accountInfo_->conversationModel);
Andreas Traczyk77a50d52018-05-08 17:47:31 -0400248 if (convIt != accountInfo_->conversationModel->allFilteredConversations().end()) {
249 NSString* bestName = bestNameForConversation(*convIt, *accountInfo_->conversationModel);
Kateryna Kostiuk9e9f7822019-09-27 11:29:45 -0400250 [contactNameLabel setStringValue:bestName];
Andreas Traczyk77a50d52018-05-08 17:47:31 -0400251 NSString* ringID = bestIDForConversation(*convIt, *accountInfo_->conversationModel);
252 if([bestName isEqualToString:ringID]) {
253 ringID = @"";
254 }
Kateryna Kostiuk9e9f7822019-09-27 11:29:45 -0400255 [contactIdLabel setStringValue:ringID];
Andreas Traczyk77a50d52018-05-08 17:47:31 -0400256 }
Kateryna Kostiuk9e9f7822019-09-27 11:29:45 -0400257 [self setupContactInfo:contactPhoto];
Andreas Traczyk77a50d52018-05-08 17:47:31 -0400258
Anthony Léonard66a050c2018-01-03 16:32:34 -0500259 [timeSpentLabel setStringValue:@(callModel->getFormattedCallDuration(callUid_).c_str())];
Anthony Léonard66a050c2018-01-03 16:32:34 -0500260 if (refreshDurationTimer == nil)
261 refreshDurationTimer = [NSTimer scheduledTimerWithTimeInterval:1.0
262 target:self
263 selector:@selector(updateDurationLabel)
264 userInfo:nil
265 repeats:YES];
Kateryna Kostiuk81afce72018-10-30 16:40:50 -0400266 [self setBackground];
Alexandre Lisionc5148052015-03-04 15:10:35 -0500267
Olivier Soldano994971f2017-12-05 16:30:12 -0500268 using Status = lrc::api::call::Status;
Kateryna Kostiuk88a6ee82019-09-10 17:55:08 -0400269 if (currentCall.status == Status::PAUSED) {
270 [holdOnOffButton startBlinkAnimationfrom:[NSColor buttonBlinkColorColor] to:[NSColor whiteColor] scaleFactor: 1.0 duration: 1.5];
271 } else {
272 [holdOnOffButton stopBlinkAnimation];
273 }
Kateryna Kostiuk9e9f7822019-09-27 11:29:45 -0400274 callStateLabel.stringValue = currentCall.status == Status::INCOMING_RINGING ? @"wants to talk to you" : @(to_string(currentCall.status).c_str());
275 loadingIndicator.hidden = (currentCall.status == Status::SEARCHING ||
276 currentCall.status == Status::CONNECTING ||
277 currentCall.status == Status::OUTGOING_RINGING) ? NO : YES;
278 pickUpButton.hidden = (currentCall.status == Status::INCOMING_RINGING) ? NO : YES;
279 callStateLabel.hidden = (currentCall.status == Status::IN_PROGRESS) ? YES : NO;
280 cancelCallButton.hidden = (currentCall.status == Status::IN_PROGRESS ||
281 currentCall.status == Status::PAUSED) ? YES : NO;
282
Olivier Soldano994971f2017-12-05 16:30:12 -0500283 switch (currentCall.status) {
284 case Status::SEARCHING:
Olivier Soldano994971f2017-12-05 16:30:12 -0500285 case Status::CONNECTING:
Olivier Soldano994971f2017-12-05 16:30:12 -0500286 case Status::OUTGOING_RINGING:
Kateryna Kostiuk9e9f7822019-09-27 11:29:45 -0400287 case Status::INCOMING_RINGING:
288 [infoContainer setHidden: NO];
Andreas Traczyk77a50d52018-05-08 17:47:31 -0400289 [headerContainer setHidden:YES];
Kateryna Kostiuk2aa39232019-10-10 11:55:11 -0400290 [headerGradientView setHidden:YES];
Kateryna Kostiuk9e9f7822019-09-27 11:29:45 -0400291 [controlsPanel setHidden:YES];
Kateryna Kostiuk2aa39232019-10-10 11:55:11 -0400292 [controlsStackView setHidden:YES];
Olivier Soldano994971f2017-12-05 16:30:12 -0500293 break;
Andreas Traczyk77a50d52018-05-08 17:47:31 -0400294 /*case Status::CONFERENCE:
295 [self setupConference:currentCall];
296 break;*/
Olivier Soldano994971f2017-12-05 16:30:12 -0500297 case Status::PAUSED:
Kateryna Kostiuk9e9f7822019-09-27 11:29:45 -0400298 [infoContainer setHidden: NO];
299 [headerContainer setHidden:NO];
Kateryna Kostiuk2aa39232019-10-10 11:55:11 -0400300 [headerGradientView setHidden:NO];
Kateryna Kostiuk9e9f7822019-09-27 11:29:45 -0400301 [controlsPanel setHidden:NO];
Kateryna Kostiuk2aa39232019-10-10 11:55:11 -0400302 [controlsStackView setHidden:NO];
Kateryna Kostiuk00dcbff2019-07-11 15:42:13 -0400303 [backgroundImage setHidden:NO];
Kateryna Kostiuk9e9f7822019-09-27 11:29:45 -0400304 [bluerBackgroundEffect setHidden:NO];
305 if(!currentCall.isAudioOnly) {
306 [self.videoMTKView fillWithBlack];
307 [self.previewView fillWithBlack];
308 [hidePreviewBackground setHidden:YES];
309 [self.previewView setHidden: YES];
310 [self.videoMTKView setHidden: YES];
311 self.previewView.stopRendering = true;
312 self.videoMTKView.stopRendering = true;
313 }
Kateryna Kostiuk00dcbff2019-07-11 15:42:13 -0400314 break;
Olivier Soldano994971f2017-12-05 16:30:12 -0500315 case Status::INACTIVE:
Kateryna Kostiuk00dcbff2019-07-11 15:42:13 -0400316 if(currentCall.isAudioOnly) {
317 [self setUpAudioOnlyView];
318 } else {
319 [self setUpVideoCallView];
320 }
321 break;
Olivier Soldano994971f2017-12-05 16:30:12 -0500322 case Status::IN_PROGRESS:
Kateryna Kostiuk9e9f7822019-09-27 11:29:45 -0400323 [headerContainer setHidden:NO];
Kateryna Kostiuk2aa39232019-10-10 11:55:11 -0400324 [headerGradientView setHidden:NO];
Kateryna Kostiuk9e9f7822019-09-27 11:29:45 -0400325 [controlsPanel setHidden:NO];
Kateryna Kostiuk2aa39232019-10-10 11:55:11 -0400326 [controlsStackView setHidden:NO];
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 {
Kateryna Kostiuk9e9f7822019-09-27 11:29:45 -0400343 [previewView fillWithBlack];
344 [self.videoMTKView fillWithBlack];
Kateryna Kostiuk81afce72018-10-30 16:40:50 -0400345 [previewView setHidden: NO];
Kateryna Kostiuk00dcbff2019-07-11 15:42:13 -0400346 [self.videoMTKView setHidden:NO];
Kateryna Kostiuk0ee5cbd2019-10-08 12:14:33 -0400347 [hidePreviewBackground setHidden: self.previewView.stopRendering];
Kateryna Kostiuk81afce72018-10-30 16:40:50 -0400348 [bluerBackgroundEffect setHidden:YES];
Kateryna Kostiuk81afce72018-10-30 16:40:50 -0400349 [backgroundImage setHidden:YES];
Kateryna Kostiuk0ee5cbd2019-10-08 12:14:33 -0400350 self.previewView.stopRendering = false;
351 self.videoMTKView.stopRendering = false;
Kateryna Kostiuk81afce72018-10-30 16:40:50 -0400352}
353
354-(void) setUpAudioOnlyView {
Kateryna Kostiuk00dcbff2019-07-11 15:42:13 -0400355 [self.previewView setHidden: YES];
356 [self.videoMTKView setHidden: YES];
Kateryna Kostiuk9e9f7822019-09-27 11:29:45 -0400357 [hidePreviewBackground setHidden: YES];
358 [bluerBackgroundEffect setHidden:NO];
359 [backgroundImage setHidden:NO];
Kateryna Kostiuk81afce72018-10-30 16:40:50 -0400360}
361
362-(void) setBackground {
Andreas Traczyk77a50d52018-05-08 17:47:31 -0400363 auto* convModel = accountInfo_->conversationModel.get();
364 auto it = getConversationFromUid(convUid_, *convModel);
Kateryna Kostiuk81afce72018-10-30 16:40:50 -0400365 NSImage *image= [self getContactImageOfSize:120.0 withDefaultAvatar:NO];
366 if(image) {
Kateryna Kostiuk89398b62018-11-20 16:45:04 -0500367 CIImage * ciImage = [[CIImage alloc] initWithData:[image TIFFRepresentation]];
368 CIContext *context = [[CIContext alloc] init];
369 CIFilter *clamp = [CIFilter filterWithName:@"CIAffineClamp"];
370 [clamp setValue:[NSAffineTransform transform] forKey:@"inputTransform"];
371 [clamp setValue:ciImage forKey: kCIInputImageKey];
Kateryna Kostiuk81afce72018-10-30 16:40:50 -0400372 CIFilter* bluerFilter = [CIFilter filterWithName:@"CIGaussianBlur"];
373 [bluerFilter setDefaults];
Kateryna Kostiuk89398b62018-11-20 16:45:04 -0500374 [bluerFilter setValue:[NSNumber numberWithFloat: 9] forKey:@"inputRadius"];
375 [bluerFilter setValue:[clamp valueForKey:kCIOutputImageKey] forKey: kCIInputImageKey];
376 CIImage *result = [bluerFilter valueForKey:kCIOutputImageKey];
377 CGRect extent = [result extent];
378 CGImageRef cgImage = [context createCGImage:result fromRect: [ciImage extent]];
379 NSImage *bluredImage = [[NSImage alloc] initWithCGImage:cgImage size:NSSizeFromCGSize(CGSizeMake(image.size.width, image.size.height))];
380 backgroundImage.layer.contents = bluredImage;
Kateryna Kostiuk81afce72018-10-30 16:40:50 -0400381 [backgroundImage setHidden:NO];
382 } else {
Kateryna Kostiuk2aa39232019-10-10 11:55:11 -0400383 contactNameLabel.textColor = [NSColor textColor];
384 contactNameLabel.textColor = [NSColor textColor];
385 contactIdLabel.textColor = [NSColor textColor];
386 callStateLabel.textColor = [NSColor textColor];
Kateryna Kostiuk9e9f7822019-09-27 11:29:45 -0400387 backgroundImage.layer.contents = nil;
Kateryna Kostiuk2aa39232019-10-10 11:55:11 -0400388 [bluerBackgroundEffect setFillColor:[NSColor windowBackgroundColor]];
Kateryna Kostiuk9e9f7822019-09-27 11:29:45 -0400389 [bluerBackgroundEffect setAlphaValue:1];
Kateryna Kostiuk81afce72018-10-30 16:40:50 -0400390 [backgroundImage setHidden:YES];
Andreas Traczyk77a50d52018-05-08 17:47:31 -0400391 }
392}
393
Kateryna Kostiuk133364b2018-10-31 11:36:08 -0400394-(NSImage *) getContactImageOfSize: (double) size withDefaultAvatar:(BOOL) shouldDrawDefault {
Kateryna Kostiuk81afce72018-10-30 16:40:50 -0400395 auto* convModel = accountInfo_->conversationModel.get();
396 auto convIt = getConversationFromUid(convUid_, *convModel);
397 if (convIt == convModel->allFilteredConversations().end()) {
398 return nil;
399 }
400 if(shouldDrawDefault) {
401 auto& imgManip = reinterpret_cast<Interfaces::ImageManipulationDelegate&>(GlobalInstances::pixmapManipulator());
402 QVariant photo = imgManip.conversationPhoto(*convIt, *accountInfo_, QSize(size, size), NO);
403 return QtMac::toNSImage(qvariant_cast<QPixmap>(photo));
404 }
405 auto contact = accountInfo_->contactModel->getContact(convIt->participants[0]);
406 NSData *imageData = [[NSData alloc] initWithBase64EncodedString:@(contact.profileInfo.avatar.c_str()) options:NSDataBase64DecodingIgnoreUnknownCharacters];
407 return [[NSImage alloc] initWithData:imageData];
408}
409
Kateryna Kostiuk81afce72018-10-30 16:40:50 -0400410-(void) setupContactInfo:(NSImageView*)imageView
411{
412 [imageView setImage: [self getContactImageOfSize:120.0 withDefaultAvatar:YES]];
413}
414
Kateryna Kostiukbe7e2e42019-07-26 17:44:12 -0400415-(void) setupConference
Olivier Soldano994971f2017-12-05 16:30:12 -0500416{
417 [videoView setShouldAcceptInteractions:YES];
418 [self.chatButton setHidden:NO];
Olivier Soldano994971f2017-12-05 16:30:12 -0500419 [self.addParticipantButton setHidden:NO];
420 [self.transferButton setHidden:YES];
421}
422
423-(void)collapseRightView
424{
425 NSView *right = [[splitView subviews] objectAtIndex:1];
426 NSView *left = [[splitView subviews] objectAtIndex:0];
427 NSRect leftFrame = [left frame];
428 [right setHidden:YES];
429 [splitView display];
430}
431
432- (void) changeCallSelection:(std::string&)callUid
433{
434 if (accountInfo_ == nil)
435 return;
436
437 auto* callModel = accountInfo_->callModel.get();
438 if (not callModel->hasCall(callUid)) {
439 return;
440 }
441
Alexandre Lision4d9905e2016-01-22 12:01:36 -0500442 QObject::disconnect(self.selectedCallChanged);
Olivier Soldano994971f2017-12-05 16:30:12 -0500443 self.selectedCallChanged = QObject::connect(callModel,
444 &lrc::api::NewCallModel::callStatusChanged,
445 [self](const std::string callId) {
Andreas Traczyk77a50d52018-05-08 17:47:31 -0400446 [self updateCall];
Alexandre Lision4d9905e2016-01-22 12:01:36 -0500447 });
Alexandre Lisionc5148052015-03-04 15:10:35 -0500448}
449
450-(void) connectVideoSignals
451{
Olivier Soldano994971f2017-12-05 16:30:12 -0500452 if (accountInfo_ == nil)
453 return;
Kateryna Kostiuk5d90c3b2019-07-18 12:03:52 -0400454 [self connectRenderer];
Alexandre Lisionc5148052015-03-04 15:10:35 -0500455}
456
Kateryna Kostiuk5d90c3b2019-07-18 12:03:52 -0400457-(void) connectRenderer
Alexandre Lisionc5148052015-03-04 15:10:35 -0500458{
Kateryna Kostiuk5d90c3b2019-07-18 12:03:52 -0400459 QObject::disconnect(renderConnections.frameUpdated);
460 QObject::disconnect(renderConnections.stopped);
461 QObject::disconnect(renderConnections.started);
462 renderConnections.started =
463 QObject::connect(mediaModel,
464 &lrc::api::AVModel::rendererStarted,
465 [=](const std::string& id) {
466 if (id == lrc::api::video::PREVIEW_RENDERER_ID) {
467 [self.previewView setHidden:NO];
468 self.previewView.stopRendering = false;
469 } else {
470 [self mouseIsMoving: NO];
471 self.videoMTKView.stopRendering = false;
472 [self.videoMTKView setHidden:NO];
473 [bluerBackgroundEffect setHidden:YES];
474 [backgroundImage setHidden:YES];
475 [videoView setShouldAcceptInteractions:YES];
476 }
477 QObject::disconnect(renderConnections.frameUpdated);
478 renderConnections.frameUpdated =
479 QObject::connect(mediaModel,
480 &lrc::api::AVModel::frameUpdated,
481 [=](const std::string& id) {
482 if (id == lrc::api::video::PREVIEW_RENDERER_ID) {
483 auto renderer = &mediaModel->getRenderer(lrc::api::video::PREVIEW_RENDERER_ID);
484 if(!renderer->isRendering()) {
485 return;
486 }
Kateryna Kostiuk6891d4f2019-09-19 17:44:33 -0400487 [hidePreviewBackground setHidden: NO];
Kateryna Kostiuk5d90c3b2019-07-18 12:03:52 -0400488 [self renderer: renderer renderFrameForPreviewView:previewView];
489
490 } else {
491 auto renderer = &mediaModel->getRenderer(id);
492 if(!renderer->isRendering()) {
493 return;
494 }
495 [self renderer:renderer renderFrameForDistantView: self.videoMTKView];
Kateryna Kostiuk00dcbff2019-07-11 15:42:13 -0400496 }
Kateryna Kostiuk5d90c3b2019-07-18 12:03:52 -0400497 });
Alexandre Lisionc5148052015-03-04 15:10:35 -0500498 });
Kateryna Kostiuk5d90c3b2019-07-18 12:03:52 -0400499 renderConnections.stopped =
500 QObject::connect(mediaModel,
501 &lrc::api::AVModel::rendererStopped,
502 [=](const std::string& id) {
503 QObject::disconnect(renderConnections.frameUpdated);
504 if (id == lrc::api::video::PREVIEW_RENDERER_ID) {
505 [self.previewView setHidden:YES];
506 self.previewView.stopRendering = true;
507 } else {
508 [self mouseIsMoving: YES];
509 self.videoMTKView.stopRendering = true;
510 [self.videoMTKView setHidden:YES];
511 [bluerBackgroundEffect setHidden:NO];
512 [backgroundImage setHidden:NO];
513 [videoView setShouldAcceptInteractions:NO];
Kateryna Kostiuk00dcbff2019-07-11 15:42:13 -0400514 }
Kateryna Kostiuk5d90c3b2019-07-18 12:03:52 -0400515 });
516 renderConnections.frameUpdated =
517 QObject::connect(mediaModel,
518 &lrc::api::AVModel::frameUpdated,
519 [=](const std::string& id) {
520 if (id == lrc::api::video::PREVIEW_RENDERER_ID) {
521 auto renderer = &mediaModel->getRenderer(lrc::api::video::PREVIEW_RENDERER_ID);
522 if(!renderer->isRendering()) {
523 return;
524 }
525 [self renderer: renderer renderFrameForPreviewView:previewView];
Alexandre Lisionc5148052015-03-04 15:10:35 -0500526
Kateryna Kostiuk5d90c3b2019-07-18 12:03:52 -0400527 } else {
528 auto renderer = &mediaModel->getRenderer(id);
529 if(!renderer->isRendering()) {
530 return;
531 }
532 [self renderer:renderer renderFrameForDistantView: self.videoMTKView];
Kateryna Kostiuk732b4062018-11-28 09:59:28 -0500533 }
Alexandre Lisionc5148052015-03-04 15:10:35 -0500534 });
535}
536
Kateryna Kostiuk5d90c3b2019-07-18 12:03:52 -0400537-(void) renderer: (const lrc::api::video::Renderer*)renderer renderFrameForPreviewView:(CallMTKView*) view
Alexandre Lisionc5148052015-03-04 15:10:35 -0500538{
Kateryna Kostiuk00dcbff2019-07-11 15:42:13 -0400539 @autoreleasepool {
540 auto framePtr = renderer->currentAVFrame();
541 auto frame = framePtr.get();
542 if(!frame || !frame->width || !frame->height) {
543 return;
544 }
545 auto frameSize = CGSizeMake(frame->width, frame->height);
546 auto rotation = 0;
547 if (frame->data[3] != NULL && (CVPixelBufferRef)frame->data[3]) {
548 [view renderWithPixelBuffer:(CVPixelBufferRef)frame->data[3]
549 size: frameSize
550 rotation: rotation
551 fillFrame: true];
552 return;
553 }
554 else if (CVPixelBufferRef pixelBuffer = [self getBufferForPreviewFromFrame:frame]) {
555 [view renderWithPixelBuffer: pixelBuffer
556 size: frameSize
557 rotation: rotation
558 fillFrame: true];
559 }
560 }
Alexandre Lisionc5148052015-03-04 15:10:35 -0500561}
562
Kateryna Kostiuk5d90c3b2019-07-18 12:03:52 -0400563-(void) renderer: (const lrc::api::video::Renderer*)renderer renderFrameForDistantView:(CallMTKView*) view
Anthony Léonard14e7bf32017-06-08 08:13:16 -0400564{
Kateryna Kostiuk00dcbff2019-07-11 15:42:13 -0400565 @autoreleasepool {
566 auto framePtr = renderer->currentAVFrame();
567 auto frame = framePtr.get();
568 if(!frame || !frame->width || !frame->height) {
569 return;
570 }
571 auto frameSize = CGSizeMake(frame->width, frame->height);
572 auto rotation = 0;
573 if (auto matrix = av_frame_get_side_data(frame, AV_FRAME_DATA_DISPLAYMATRIX)) {
574 const int32_t* data = reinterpret_cast<int32_t*>(matrix->data);
575 rotation = av_display_rotation_get(data);
576 }
577 if (frame->data[3] != NULL && (CVPixelBufferRef)frame->data[3]) {
578 [view renderWithPixelBuffer: (CVPixelBufferRef)frame->data[3]
579 size: frameSize
580 rotation: rotation
581 fillFrame: false];
582 }
583 if (CVPixelBufferRef pixelBuffer = [self getBufferForDistantViewFromFrame:frame]) {
584 [view renderWithPixelBuffer: pixelBuffer
585 size: frameSize
586 rotation: rotation
587 fillFrame: false];
588 }
Kateryna Kostiukbcf24e92018-11-20 10:09:06 -0500589 }
Anthony Léonard14e7bf32017-06-08 08:13:16 -0400590}
591
Kateryna Kostiuk00dcbff2019-07-11 15:42:13 -0400592-(CVPixelBufferRef) getBufferForPreviewFromFrame:(const AVFrame*)frame {
Kateryna Kostiukc5fd3b92019-07-24 10:12:44 -0400593 [VideoCommon fillPixelBuffr:&pixelBufferPreview fromFrame:frame bufferPool:&pixelBufferPoolPreview];
594 CVPixelBufferRef buffer = pixelBufferPreview;
595 return buffer;
Kateryna Kostiuk00dcbff2019-07-11 15:42:13 -0400596}
597
598-(CVPixelBufferRef) getBufferForDistantViewFromFrame:(const AVFrame*)frame {
Kateryna Kostiukc5fd3b92019-07-24 10:12:44 -0400599 [VideoCommon fillPixelBuffr:&pixelBufferDistantView fromFrame:frame bufferPool:&pixelBufferPoolDistantView];
600 CVPixelBufferRef buffer = pixelBufferDistantView;
601 return buffer;
Kateryna Kostiuk00dcbff2019-07-11 15:42:13 -0400602}
603
Alexandre Lisionc5148052015-03-04 15:10:35 -0500604- (void) initFrame
605{
606 [self.view setFrame:self.view.superview.bounds];
607 [self.view setHidden:YES];
608 self.view.layer.position = self.view.frame.origin;
Alexandre Lisionbb5dbcd2015-07-09 16:36:47 -0400609 [self collapseRightView];
Alexandre Lisionc5148052015-03-04 15:10:35 -0500610}
611
612# pragma private IN/OUT animations
613
Olivier Soldano994971f2017-12-05 16:30:12 -0500614-(void)uncollapseRightView
615{
616 NSView *left = [[splitView subviews] objectAtIndex:0];
617 NSView *right = [[splitView subviews] objectAtIndex:1];
618 [right setHidden:NO];
619
620 CGFloat dividerThickness = [splitView dividerThickness];
621
622 // get the different frames
623 NSRect leftFrame = [left frame];
624 NSRect rightFrame = [right frame];
625
626 leftFrame.size.width = (leftFrame.size.width - rightFrame.size.width - dividerThickness);
627 rightFrame.origin.x = leftFrame.size.width + dividerThickness;
628 [left setFrameSize:leftFrame.size];
629 [right setFrame:rightFrame];
630 [splitView display];
631
632 [self.chatVC takeFocus];
633}
634
Alexandre Lisionc5148052015-03-04 15:10:35 -0500635-(void) cleanUp
636{
Anthony Léonard5c287642018-02-21 15:54:19 -0500637 if(self.splitView.isInFullScreenMode)
638 [self.splitView exitFullScreenModeWithOptions:nil];
Kateryna Kostiuk5d90c3b2019-07-18 12:03:52 -0400639 QObject::disconnect(renderConnections.frameUpdated);
640 QObject::disconnect(renderConnections.started);
641 QObject::disconnect(renderConnections.stopped);
Anthony Léonardc0013bb2018-02-20 16:23:02 -0500642 QObject::disconnect(self.messageConnection);
Alexandre Lision883719f2015-10-22 17:37:45 -0400643
Alexandre Lision4baba4c2016-02-11 13:00:57 -0500644 [self.chatButton setPressed:NO];
Alexandre Lision883719f2015-10-22 17:37:45 -0400645 [self collapseRightView];
Alexandre Lision57227772016-01-15 17:19:36 -0500646
Alexandre Lision57227772016-01-15 17:19:36 -0500647 [timeSpentLabel setStringValue:@""];
Kateryna Kostiuk9e9f7822019-09-27 11:29:45 -0400648 [contactIdLabel setStringValue:@""];
649 [contactNameLabel setStringValue:@""];
650 [contactPhoto setImage:nil];
Kateryna Kostiuk81afce72018-10-30 16:40:50 -0400651 //background view
652 [bluerBackgroundEffect setHidden:NO];
Kateryna Kostiuk9e9f7822019-09-27 11:29:45 -0400653 [bluerBackgroundEffect setFillColor:[NSColor darkGrayColor]];
654 [bluerBackgroundEffect setAlphaValue:0.6];
Kateryna Kostiuk81afce72018-10-30 16:40:50 -0400655 [backgroundImage setHidden:NO];
Kateryna Kostiuk9e9f7822019-09-27 11:29:45 -0400656 backgroundImage.layer.contents = nil;
Kateryna Kostiuk00dcbff2019-07-11 15:42:13 -0400657 [self.previewView setHidden:YES];
658 [self.videoMTKView setHidden:YES];
Kateryna Kostiuk9e9f7822019-09-27 11:29:45 -0400659
660 contactNameLabel.textColor = [NSColor highlightColor];
661 contactNameLabel.textColor = [NSColor highlightColor];
662 contactIdLabel.textColor = [NSColor highlightColor];
663 callStateLabel.textColor = [NSColor highlightColor];
Alexandre Lisionc5148052015-03-04 15:10:35 -0500664}
665
Andreas Traczykecdc4fa2018-03-22 16:11:47 -0400666-(void) setupCallView
667{
668 if (accountInfo_ == nil)
669 return;
670
671 auto* callModel = accountInfo_->callModel.get();
672 auto* convModel = accountInfo_->conversationModel.get();
673
674 // when call comes in we want to show the controls/header
Kateryna Kostiuk1004caa2018-03-29 13:48:07 -0400675 [self mouseIsMoving: YES];
676 [videoView setShouldAcceptInteractions: NO];
Andreas Traczykecdc4fa2018-03-22 16:11:47 -0400677
678 [self connectVideoSignals];
679 /* check if text media is already present */
680 if(not callModel->hasCall(callUid_))
681 return;
682
683 [loadingIndicator setAnimates:YES];
Andreas Traczyk77a50d52018-05-08 17:47:31 -0400684 [self updateCall];
Andreas Traczykecdc4fa2018-03-22 16:11:47 -0400685
686 /* monitor media for messaging text messaging */
687 QObject::disconnect(self.messageConnection);
688 self.messageConnection = QObject::connect(convModel,
689 &lrc::api::ConversationModel::interactionStatusUpdated,
690 [self] (std::string convUid,
691 uint64_t msgId,
692 lrc::api::interaction::Info msg) {
693 if (msg.type == lrc::api::interaction::Type::TEXT) {
694 if(not [[self splitView] isSubviewCollapsed:[[[self splitView] subviews] objectAtIndex: 1]]){
695 return;
696 }
697 [self uncollapseRightView];
698 }
699 });
Kateryna Kostiuk133364b2018-10-31 11:36:08 -0400700 //monitor for updated profile
701 QObject::disconnect(self.profileUpdatedConnection);
702 self.profileUpdatedConnection =
703 QObject::connect(accountInfo_->contactModel.get(),
704 &lrc::api::ContactModel::contactAdded,
705 [self](const std::string &contactUri) {
706 auto convIt = getConversationFromUid(convUid_, *accountInfo_->conversationModel.get());
707 if (convIt == accountInfo_->conversationModel->allFilteredConversations().end()) {
708 return;
709 }
710 if (convIt->participants.empty()) {
711 return;
712
713 }
714 auto& contact = accountInfo_->contactModel->getContact(convIt->participants[0]);
715 if (contact.profileInfo.type == lrc::api::profile::Type::RING && contact.profileInfo.uri == contactUri)
716 accountInfo_->conversationModel->makePermanent(convUid_);
Kateryna Kostiuk9e9f7822019-09-27 11:29:45 -0400717 [contactPhoto setImage: [self getContactImageOfSize:120.0 withDefaultAvatar:YES]];
Kateryna Kostiuk732b4062018-11-28 09:59:28 -0500718 [self.delegate conversationInfoUpdatedFor:convUid_];
Kateryna Kostiuk9e9f7822019-09-27 11:29:45 -0400719 [self setBackground];
Kateryna Kostiuk133364b2018-10-31 11:36:08 -0400720 });
Andreas Traczykecdc4fa2018-03-22 16:11:47 -0400721}
722
723-(void) showWithAnimation:(BOOL)animate
724{
725 if (!animate) {
726 [self.view setHidden:NO];
727 [self setupCallView];
728 return;
729 }
730
731 CGRect frame = CGRectOffset(self.view.superview.bounds, -self.view.superview.bounds.size.width, 0);
732 [self.view setHidden:NO];
733
734 [CATransaction begin];
735 CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"position"];
736 [animation setFromValue:[NSValue valueWithPoint:frame.origin]];
737 [animation setToValue:[NSValue valueWithPoint:self.view.superview.bounds.origin]];
738 [animation setDuration:0.2f];
739 [animation setTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseIn]];
740 [CATransaction setCompletionBlock:^{
741 [self setupCallView];
742 }];
743
744 [self.view.layer addAnimation:animation forKey:animation.keyPath];
745 [CATransaction commit];
746}
747
748-(void) hideWithAnimation:(BOOL)animate
Alexandre Lisionc5148052015-03-04 15:10:35 -0500749{
Alexandre Lisionc5148052015-03-04 15:10:35 -0500750 if(self.view.frame.origin.x < 0) {
Alexandre Lisionc5148052015-03-04 15:10:35 -0500751 return;
752 }
753
Andreas Traczykecdc4fa2018-03-22 16:11:47 -0400754 if (!animate) {
755 [self.view setHidden:YES];
756 return;
757 }
758
Alexandre Lisionc5148052015-03-04 15:10:35 -0500759 CGRect frame = CGRectOffset(self.view.frame, -self.view.frame.size.width, 0);
760 [CATransaction begin];
761 CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"position"];
762 [animation setFromValue:[NSValue valueWithPoint:self.view.frame.origin]];
763 [animation setToValue:[NSValue valueWithPoint:frame.origin]];
764 [animation setDuration:0.2f];
Alexandre Lision22d615b2016-01-25 12:54:34 -0500765 [animation setTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut]];
Alexandre Lisionc5148052015-03-04 15:10:35 -0500766
767 [CATransaction setCompletionBlock:^{
768 [self.view setHidden:YES];
Alexandre Lisionef6333a2015-03-24 12:30:31 -0400769 // first make sure everything is disconnected
Alexandre Lisionc5148052015-03-04 15:10:35 -0500770 [self cleanUp];
Olivier Soldano994971f2017-12-05 16:30:12 -0500771// if (currentCall_) {
772// [self animateIn];
773// }
Alexandre Lisionc5148052015-03-04 15:10:35 -0500774 }];
775 [self.view.layer addAnimation:animation forKey:animation.keyPath];
Alexandre Lisiona1c6d752015-06-23 12:27:38 -0400776
777 [self.view.layer setPosition:frame.origin];
Alexandre Lisionc5148052015-03-04 15:10:35 -0500778 [CATransaction commit];
779}
780
781/**
782 * Debug purpose
783 */
784-(void) dumpFrame:(CGRect) frame WithName:(NSString*) name
785{
786 NSLog(@"frame %@ : %f %f %f %f \n\n",name ,frame.origin.x, frame.origin.y, frame.size.width, frame.size.height);
787}
788
Alexandre Lision58cab672015-06-09 15:25:40 -0400789#pragma mark - Button methods
790
Alexandre Lisionc5148052015-03-04 15:10:35 -0500791- (IBAction)hangUp:(id)sender {
Olivier Soldano994971f2017-12-05 16:30:12 -0500792 if (accountInfo_ == nil)
793 return;
794
795 auto* callModel = accountInfo_->callModel.get();
Olivier Soldano994971f2017-12-05 16:30:12 -0500796 callModel->hangUp(callUid_);
Alexandre Lisionc5148052015-03-04 15:10:35 -0500797}
798
799- (IBAction)accept:(id)sender {
Olivier Soldano994971f2017-12-05 16:30:12 -0500800 if (accountInfo_ == nil)
801 return;
802
Anthony Léonardf584cc52018-01-19 12:00:19 -0500803 // If we accept a conversation with a non trusted contact, we first accept it
804 auto convIt = getConversationFromUid(convUid_, *accountInfo_->conversationModel.get());
805 if (convIt != accountInfo_->conversationModel->allFilteredConversations().end()) {
806 auto& contact = accountInfo_->contactModel->getContact(convIt->participants[0]);
807 if (contact.profileInfo.type == lrc::api::profile::Type::PENDING)
808 accountInfo_->conversationModel->makePermanent(convUid_);
809 }
810
Olivier Soldano994971f2017-12-05 16:30:12 -0500811 auto* callModel = accountInfo_->callModel.get();
812
813 callModel->accept(callUid_);
Alexandre Lisionc5148052015-03-04 15:10:35 -0500814}
815
816- (IBAction)toggleRecording:(id)sender {
Olivier Soldano994971f2017-12-05 16:30:12 -0500817 if (accountInfo_ == nil)
818 return;
819
820 auto* callModel = accountInfo_->callModel.get();
821
822 callModel->toggleAudioRecord(callUid_);
Kateryna Kostiuk88a6ee82019-09-10 17:55:08 -0400823 if (callModel->isRecording(callUid_)) {
824 [recordOnOffButton startBlinkAnimationfrom:[NSColor buttonBlinkColorColor] to:[NSColor whiteColor] scaleFactor: 1 duration: 1.5];
825 } else {
826 [recordOnOffButton stopBlinkAnimation];
827 }
Alexandre Lisionc5148052015-03-04 15:10:35 -0500828}
829
830- (IBAction)toggleHold:(id)sender {
Olivier Soldano994971f2017-12-05 16:30:12 -0500831 if (accountInfo_ == nil)
832 return;
833
834 auto* callModel = accountInfo_->callModel.get();
Kateryna Kostiuk54d76972018-06-07 15:45:07 -0400835 auto currentCall = callModel->getCall(callUid_);
Olivier Soldano994971f2017-12-05 16:30:12 -0500836
837 callModel->togglePause(callUid_);
Alexandre Lisionc5148052015-03-04 15:10:35 -0500838}
839
Alexandre Lision62005312016-01-28 15:55:16 -0500840- (IBAction)showDialpad:(id)sender {
841 AppDelegate* appDelegate = (AppDelegate *)[[NSApplication sharedApplication] delegate];
842 [appDelegate showDialpad];
843}
844
Alexandre Lision58cab672015-06-09 15:25:40 -0400845-(IBAction)toggleChat:(id)sender;
846{
847 BOOL rightViewCollapsed = [[self splitView] isSubviewCollapsed:[[[self splitView] subviews] objectAtIndex: 1]];
848 if (rightViewCollapsed) {
849 [self uncollapseRightView];
Alexandre Lision58cab672015-06-09 15:25:40 -0400850 } else {
851 [self collapseRightView];
852 }
Alexandre Lision62005312016-01-28 15:55:16 -0500853 [chatButton setPressed:rightViewCollapsed];
Alexandre Lision58cab672015-06-09 15:25:40 -0400854}
855
Alexandre Lisiond18fa272015-06-15 11:18:03 -0400856- (IBAction)muteAudio:(id)sender
857{
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.audioMuted) {
864 muteAudioButton.image = [NSImage imageNamed:@"ic_action_audio.png"];
865 } else {
866 muteAudioButton.image = [NSImage imageNamed:@"ic_action_mute_audio.png"];
867 }
Olivier Soldano994971f2017-12-05 16:30:12 -0500868 callModel->toggleMedia(callUid_, lrc::api::NewCallModel::Media::AUDIO);
Alexandre Lisiond18fa272015-06-15 11:18:03 -0400869}
870
871- (IBAction)muteVideo:(id)sender
872{
Olivier Soldano994971f2017-12-05 16:30:12 -0500873 if (accountInfo_ == nil)
874 return;
Olivier Soldano994971f2017-12-05 16:30:12 -0500875 auto* callModel = accountInfo_->callModel.get();
Kateryna Kostiuk54d76972018-06-07 15:45:07 -0400876 auto currentCall = callModel->getCall(callUid_);
877 if(!currentCall.isAudioOnly) {
878 if (currentCall.videoMuted) {
879 muteVideoButton.image = [NSImage imageNamed:@"ic_action_video.png"];
880 } else {
881 muteVideoButton.image = [NSImage imageNamed:@"ic_action_mute_video.png"];
882 }
883 }
Olivier Soldano994971f2017-12-05 16:30:12 -0500884 callModel->toggleMedia(callUid_, lrc::api::NewCallModel::Media::VIDEO);
Alexandre Lisiond18fa272015-06-15 11:18:03 -0400885}
Alexandre Lision883719f2015-10-22 17:37:45 -0400886
887- (IBAction)toggleTransferView:(id)sender {
Kateryna Kostiuk1ba5a012018-12-12 17:07:38 -0500888
Alexandre Lisionf23ec5a2015-07-16 11:24:06 -0400889}
Alexandre Lisiond18fa272015-06-15 11:18:03 -0400890
Alexandre Lision89edc6a2015-11-09 11:30:47 -0500891- (IBAction)toggleAddParticipantView:(id)sender {
Kateryna Kostiuk9e9f7822019-09-27 11:29:45 -0400892
Alexandre Lision89edc6a2015-11-09 11:30:47 -0500893}
Kateryna Kostiuk6891d4f2019-09-19 17:44:33 -0400894- (IBAction)hidePreview:(id)sender {
895 CGRect previewFrame = previewView.frame;
Kateryna Kostiuk2aa39232019-10-10 11:55:11 -0400896 CGRect newPreviewFrame;//, bcHidePreviewFrame;
897 if (previewFrame.size.width > HIDE_PREVIEW_BUTTON_SIZE) {
Kateryna Kostiuk6891d4f2019-09-19 17:44:33 -0400898 self.movableBaseForView.movable = false;
899 newPreviewFrame = self.getVideoPreviewCollapsedSize;
Kateryna Kostiuk2aa39232019-10-10 11:55:11 -0400900 // bcHidePreviewFrame = [self frameForExpendPreviewButton: true];
Kateryna Kostiuk6891d4f2019-09-19 17:44:33 -0400901 hidePreviewButton.image = [NSImage imageNamed: NSImageNameTouchBarEnterFullScreenTemplate];
902 } else {
903 self.movableBaseForView.movable = true;
904 newPreviewFrame = CGRectMake(0, 0, PREVIEW_WIDTH, PREVIEW_HEIGHT);
Kateryna Kostiuk2aa39232019-10-10 11:55:11 -0400905 // bcHidePreviewFrame = [self frameForExpendPreviewButton: false];
Kateryna Kostiuk6891d4f2019-09-19 17:44:33 -0400906 hidePreviewButton.image = [NSImage imageNamed: NSImageNameTouchBarExitFullScreenTemplate];
907 }
908 [NSAnimationContext runAnimationGroup:^(NSAnimationContext * _Nonnull context) {
909 context.duration = 0.2f;
910 context.timingFunction = [CAMediaTimingFunction functionWithName: kCAMediaTimingFunctionEaseOut];
911 previewView.animator.frame = newPreviewFrame;
912 } completionHandler: nil];
Kateryna Kostiuk2aa39232019-10-10 11:55:11 -0400913 // hidePreviewBackground.frame = bcHidePreviewFrame;
Kateryna Kostiuk6891d4f2019-09-19 17:44:33 -0400914}
Alexandre Lision89edc6a2015-11-09 11:30:47 -0500915
Alexandre Lision58cab672015-06-09 15:25:40 -0400916#pragma mark - NSSplitViewDelegate
917
918/* 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.
919 */
920- (BOOL)splitView:(NSSplitView *)splitView shouldCollapseSubview:(NSView *)subview forDoubleClickOnDividerAtIndex:(NSInteger)dividerIndex;
921{
922 NSView* rightView = [[splitView subviews] objectAtIndex:1];
923 return ([subview isEqual:rightView]);
924}
925
926
927- (BOOL)splitView:(NSSplitView *)splitView canCollapseSubview:(NSView *)subview;
928{
929 NSView* rightView = [[splitView subviews] objectAtIndex:1];
930 return ([subview isEqual:rightView]);
931}
932
933
Alexandre Lisiona1eee3c2015-08-10 13:44:51 -0400934# pragma mark - CallnDelegate
Alexandre Lision58cab672015-06-09 15:25:40 -0400935
936- (void) callShouldToggleFullScreen
937{
938 if(self.splitView.isInFullScreenMode)
939 [self.splitView exitFullScreenModeWithOptions:nil];
940 else {
941 NSApplicationPresentationOptions options = NSApplicationPresentationDefault +NSApplicationPresentationAutoHideDock +
942 NSApplicationPresentationAutoHideMenuBar + NSApplicationPresentationAutoHideToolbar;
943 NSDictionary *opts = [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithUnsignedInt:options],
944 NSFullScreenModeApplicationPresentationOptions, nil];
945
946 [self.splitView enterFullScreenMode:[NSScreen mainScreen] withOptions:opts];
947 }
948}
949
Alexandre Lisiona1eee3c2015-08-10 13:44:51 -0400950-(void) mouseIsMoving:(BOOL) move
951{
Kateryna Kostiuk2aa39232019-10-10 11:55:11 -0400952 [[controlsPanel animator] setAlphaValue:move];// fade out
953 [[controlsStackView animator] setAlphaValue:move];
Alexandre Lisiona1eee3c2015-08-10 13:44:51 -0400954 [[headerContainer animator] setAlphaValue:move];
Kateryna Kostiuk2aa39232019-10-10 11:55:11 -0400955 [[headerGradientView animator] setAlphaValue:move];
Alexandre Lisiona1eee3c2015-08-10 13:44:51 -0400956}
957
Kateryna Kostiuk89398b62018-11-20 16:45:04 -0500958- (BOOL)splitView:(NSSplitView *)splitView shouldHideDividerAtIndex:(NSInteger)dividerIndex
959{
960 return YES;
961}
962
Kateryna Kostiuk5d90c3b2019-07-18 12:03:52 -0400963-(void) screenShare {
964 NSScreen *mainScreen = [NSScreen mainScreen];
965 NSRect screenFrame = mainScreen.frame;
966 QRect captureRect = QRect(screenFrame.origin.x, screenFrame.origin.y, screenFrame.size.width, screenFrame.size.height);
967 mediaModel->setDisplay(0, screenFrame.origin.x, screenFrame.origin.y, screenFrame.size.width, screenFrame.size.height);
968}
969-(void) switchToDevice:(int)deviceID {
970 auto devices = mediaModel->getDevices();
971 auto device = devices[deviceID];
972 mediaModel->switchInputTo(device);
973}
974
975-(std::vector<std::string>) getDeviceList {
976 return mediaModel->getDevices();
977}
978
979-(void) switchToFile:(std::string)uri {
980 mediaModel->setInputFile(QUrl::fromLocalFile(uri.c_str()).toLocalFile().toStdString());
981}
982
Kateryna Kostiuk6891d4f2019-09-19 17:44:33 -0400983-(CGRect) getVideoPreviewCollapsedSize {
984 CGPoint origin;
985 switch (movableBaseForView.closestCorner) {
986 case TOP_LEFT:
Kateryna Kostiuk2aa39232019-10-10 11:55:11 -0400987 origin = CGPointMake(0, movableBaseForView.frame.size.height - HIDE_PREVIEW_BUTTON_SIZE);
Kateryna Kostiuk6891d4f2019-09-19 17:44:33 -0400988 break;
989 case BOTTOM_LEFT:
990 origin = CGPointMake(0, 0);
991 break;
992 case TOP_RIGHT:
Kateryna Kostiuk2aa39232019-10-10 11:55:11 -0400993 origin = CGPointMake(movableBaseForView.frame.size.width - HIDE_PREVIEW_BUTTON_SIZE, movableBaseForView.frame.size.height - HIDE_PREVIEW_BUTTON_SIZE);
Kateryna Kostiuk6891d4f2019-09-19 17:44:33 -0400994 break;
995 case BOTTOM_RIGHT:
Kateryna Kostiuk2aa39232019-10-10 11:55:11 -0400996 origin = CGPointMake(movableBaseForView.frame.size.width - HIDE_PREVIEW_BUTTON_SIZE, 0);
Kateryna Kostiuk6891d4f2019-09-19 17:44:33 -0400997 break;
998 }
Kateryna Kostiuk2aa39232019-10-10 11:55:11 -0400999 return CGRectMake(origin.x, origin.y, HIDE_PREVIEW_BUTTON_SIZE, HIDE_PREVIEW_BUTTON_SIZE);
Kateryna Kostiuk6891d4f2019-09-19 17:44:33 -04001000}
1001
Kateryna Kostiuk2aa39232019-10-10 11:55:11 -04001002-(CGRect) frameForExpendPreviewButton {
1003 CGPoint origin = CGPointMake(self.previewView.frame.size.width - HIDE_PREVIEW_BUTTON_SIZE,
1004 self.previewView.frame.size.height - HIDE_PREVIEW_BUTTON_SIZE);
1005 return CGRectMake(origin.x, origin.y, HIDE_PREVIEW_BUTTON_SIZE, HIDE_PREVIEW_BUTTON_SIZE);
Kateryna Kostiuk6891d4f2019-09-19 17:44:33 -04001006}
1007
Alexandre Lisionc5148052015-03-04 15:10:35 -05001008@end