CurrentCallVC.mm 45.6 KB
Newer Older
1
/*
Alexandre Lision's avatar
Alexandre Lision committed
2
 *  Copyright (C) 2015-2016 Savoir-faire Linux Inc.
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
 *  Author: Alexandre Lision <alexandre.lision@savoirfairelinux.com>
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA.
 */
#import "CurrentCallVC.h"
Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
20 21 22 23
extern "C" {
#import "libavutil/frame.h"
#import "libavutil/display.h"
}
24 25 26

#import <QuartzCore/QuartzCore.h>

Alexandre Lision's avatar
Alexandre Lision committed
27 28 29 30 31 32 33
///Qt
#import <QMimeData>
#import <QtMacExtras/qmacfunctions.h>
#import <QtCore/qabstractitemmodel.h>
#import <QPixmap>

///LRC
34 35
#import <video/previewmanager.h>
#import <video/renderer.h>
36 37 38
#import <api/newcallmodel.h>
#import <api/call.h>
#import <api/conversationmodel.h>
Alexandre Lision's avatar
Alexandre Lision committed
39
#import <globalinstances.h>
40

Alexandre Lision's avatar
Alexandre Lision committed
41
#import "AppDelegate.h"
42
#import "views/ITProgressIndicator.h"
Alexandre Lision's avatar
Alexandre Lision committed
43
#import "views/CallView.h"
Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
44
#import "views/NSColor+RingTheme.h"
Alexandre Lision's avatar
Alexandre Lision committed
45
#import "delegates/ImageManipulationDelegate.h"
Alexandre Lision's avatar
Alexandre Lision committed
46
#import "ChatVC.h"
47
#import "views/IconButton.h"
48
#import "utils.h"
Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
49
#import "views/CallMTKView.h"
Alexandre Lision's avatar
Alexandre Lision committed
50

51 52 53 54 55 56 57 58 59 60 61 62
@interface RendererConnectionsHolder : NSObject

@property QMetaObject::Connection frameUpdated;
@property QMetaObject::Connection started;
@property QMetaObject::Connection stopped;

@end

@implementation RendererConnectionsHolder

@end

63
@interface CurrentCallVC () <NSPopoverDelegate> {
64 65 66
    std::string convUid_;
    std::string callUid_;
    const lrc::api::account::Info *accountInfo_;
67
    NSTimer* refreshDurationTimer;
68
}
69

Alexandre Lision's avatar
Alexandre Lision committed
70 71
// Main container
@property (unsafe_unretained) IBOutlet NSSplitView* splitView;
Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
72
@property (unsafe_unretained) IBOutlet NSView* backgroundImage;
Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
73
@property (unsafe_unretained) IBOutlet NSBox* bluerBackgroundEffect;
Alexandre Lision's avatar
Alexandre Lision committed
74

75 76
// Header info
@property (unsafe_unretained) IBOutlet NSView* headerContainer;
Alexandre Lision's avatar
Alexandre Lision committed
77 78
@property (unsafe_unretained) IBOutlet NSTextField* personLabel;
@property (unsafe_unretained) IBOutlet NSTextField* stateLabel;
79
@property (unsafe_unretained) IBOutlet NSTextField* timeSpentLabel;
Alexandre Lision's avatar
Alexandre Lision committed
80
@property (unsafe_unretained) IBOutlet NSImageView* personPhoto;
81

Andreas Traczyk's avatar
Andreas Traczyk committed
82 83 84 85 86
@property (unsafe_unretained) IBOutlet NSImageView* outgoingPhoto;
@property (unsafe_unretained) IBOutlet NSTextField* outgoingPersonLabel;
@property (unsafe_unretained) IBOutlet NSTextField* outgoingStateLabel;
@property (unsafe_unretained) IBOutlet NSTextField* outgoingId;

87 88
// Call Controls
@property (unsafe_unretained) IBOutlet NSView* controlsPanel;
Alexandre Lision's avatar
Alexandre Lision committed
89

90 91 92 93 94 95 96 97 98
@property (unsafe_unretained) IBOutlet IconButton* holdOnOffButton;
@property (unsafe_unretained) IBOutlet IconButton* hangUpButton;
@property (unsafe_unretained) IBOutlet IconButton* recordOnOffButton;
@property (unsafe_unretained) IBOutlet IconButton* pickUpButton;
@property (unsafe_unretained) IBOutlet IconButton* muteAudioButton;
@property (unsafe_unretained) IBOutlet IconButton* muteVideoButton;
@property (unsafe_unretained) IBOutlet IconButton* transferButton;
@property (unsafe_unretained) IBOutlet IconButton* addParticipantButton;
@property (unsafe_unretained) IBOutlet IconButton* chatButton;
Andreas Traczyk's avatar
Andreas Traczyk committed
99
@property (unsafe_unretained) IBOutlet IconButton* qualityButton;
100

Alexandre Lision's avatar
Alexandre Lision committed
101 102
@property (unsafe_unretained) IBOutlet NSView* advancedPanel;
@property (unsafe_unretained) IBOutlet IconButton* advancedButton;
Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
103
@property (assign) IBOutlet NSLayoutConstraint* callRecordButtonMarginLeft;
104

105
@property (strong) NSPopover* addToContactPopover;
106
@property (strong) NSPopover* brokerPopoverVC;
Alexandre Lision's avatar
Alexandre Lision committed
107
@property (strong) IBOutlet ChatVC* chatVC;
108

Alexandre Lision's avatar
Alexandre Lision committed
109 110 111 112 113 114 115 116
// Ringing call panel
@property (unsafe_unretained) IBOutlet NSView* ringingPanel;
@property (unsafe_unretained) IBOutlet NSImageView* incomingPersonPhoto;
@property (unsafe_unretained) IBOutlet NSTextField* incomingDisplayName;

// Outgoing call panel
@property (unsafe_unretained) IBOutlet NSView* outgoingPanel;
@property (unsafe_unretained) IBOutlet ITProgressIndicator *loadingIndicator;
117

Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
118 119 120 121 122 123 124
// audio only view
@property (unsafe_unretained) IBOutlet NSView* audioCallView;
@property (unsafe_unretained) IBOutlet NSImageView* audioCallPhoto;
@property (unsafe_unretained) IBOutlet NSTextField* audioCallTime;
@property (unsafe_unretained) IBOutlet NSTextField* audioCallPersonLabel;
@property (unsafe_unretained) IBOutlet NSTextField* audioCallPersonId;

125
// Video
Alexandre Lision's avatar
Alexandre Lision committed
126
@property (unsafe_unretained) IBOutlet CallView *videoView;
Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
127 128 129
@property (unsafe_unretained) IBOutlet CallMTKView *previewView;

@property (unsafe_unretained) IBOutlet CallMTKView *videoMTKView;
130 131 132

@property RendererConnectionsHolder* previewHolder;
@property RendererConnectionsHolder* videoHolder;
133
@property QMetaObject::Connection videoStarted;
Alexandre Lision's avatar
Alexandre Lision committed
134
@property QMetaObject::Connection selectedCallChanged;
135 136
@property QMetaObject::Connection messageConnection;
@property QMetaObject::Connection mediaAddedConnection;
Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
137
@property QMetaObject::Connection profileUpdatedConnection;
Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
138
@property NSImageView *testView;
139 140 141 142

@end

@implementation CurrentCallVC
143
@synthesize personLabel, personPhoto, stateLabel, holdOnOffButton, hangUpButton,
144
            recordOnOffButton, pickUpButton, chatButton, transferButton, addParticipantButton, timeSpentLabel,
145
            muteVideoButton, muteAudioButton, controlsPanel, advancedPanel, advancedButton, headerContainer, videoView,
Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
146 147 148
            incomingDisplayName, incomingPersonPhoto, previewView, splitView, loadingIndicator, ringingPanel,
            outgoingPanel, outgoingPersonLabel, outgoingStateLabel, outgoingPhoto, outgoingId,
            callRecordButtonMarginLeft, audioCallView, audioCallPhoto, audioCallTime, audioCallPersonLabel, audioCallPersonId, backgroundImage, bluerBackgroundEffect;
149 150 151

@synthesize previewHolder;
@synthesize videoHolder;
Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
152 153 154 155
CVPixelBufferPoolRef pixelBufferPoolDistantView;
CVPixelBufferRef pixelBufferDistantView;
CVPixelBufferPoolRef pixelBufferPoolPreview;
CVPixelBufferRef pixelBufferPreview;
156

157 158 159
-(void) setCurrentCall:(const std::string&)callUid
          conversation:(const std::string&)convUid
               account:(const lrc::api::account::Info*)account;
160
{
161 162 163 164 165 166 167 168
    if(account == nil)
        return;

    auto* callModel = account->callModel.get();

    if (not callModel->hasCall(callUid)){
        callUid_.clear();
        return;
169
    }
170 171 172
    callUid_ = callUid;
    convUid_ = convUid;
    accountInfo_ = account;
Anthony Léonard's avatar
Anthony Léonard committed
173
    [self.chatVC setConversationUid:convUid model:account->conversationModel.get()];
Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
174
    auto currentCall = callModel->getCall(callUid_);
Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
175 176 177
    [muteVideoButton setHidden: currentCall.isAudioOnly ? YES: NO];
    callRecordButtonMarginLeft.constant = currentCall.isAudioOnly ? -40.0f: 10.0f;
    [previewView setHidden: YES];
Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
178
    videoView.callId = callUid;
179 180
}

181
- (void)awakeFromNib
182
{
183 184 185 186 187 188 189 190 191
    [self.view setWantsLayer:YES];
    [controlsPanel setWantsLayer:YES];
    [controlsPanel.layer setBackgroundColor:[NSColor clearColor].CGColor];
    [controlsPanel.layer setFrame:controlsPanel.frame];

    previewHolder = [[RendererConnectionsHolder alloc] init];
    videoHolder = [[RendererConnectionsHolder alloc] init];

    [loadingIndicator setColor:[NSColor whiteColor]];
Andreas Traczyk's avatar
Andreas Traczyk committed
192 193 194 195
    [loadingIndicator setNumberOfLines:200];
    [loadingIndicator setWidthOfLine:4];
    [loadingIndicator setLengthOfLine:4];
    [loadingIndicator setInnerMargin:59];
196 197

    [self.videoView setCallDelegate:self];
Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
198 199
    CGColor* color = [[[NSColor blackColor] colorWithAlphaComponent:0.2] CGColor];
    [headerContainer.layer setBackgroundColor:color];
Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
200 201 202 203
    [bluerBackgroundEffect setWantsLayer:YES];
    bluerBackgroundEffect.alphaValue = 0.6;
    [audioCallView setWantsLayer:YES];
    [audioCallView.layer setBackgroundColor: [[NSColor clearColor] CGColor]];
Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
204 205
    [backgroundImage setWantsLayer: YES];
    backgroundImage.layer.contentsGravity = kCAGravityResizeAspectFill;
206 207
}

208 209 210 211 212 213 214 215 216
-(void) updateDurationLabel
{
    if (accountInfo_ != nil) {
        auto* callModel = accountInfo_->callModel.get();
        if (callModel->hasCall(callUid_)) {
            auto& callStatus = callModel->getCall(callUid_).status;
            if (callStatus != lrc::api::call::Status::ENDED &&
                callStatus != lrc::api::call::Status::TERMINATING &&
                callStatus != lrc::api::call::Status::INVALID) {
Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
217 218 219 220 221
                if(callModel->getCall(callUid_).isAudioOnly) {
                    [audioCallTime setStringValue:@(callModel->getFormattedCallDuration(callUid_).c_str())];
                } else {
                    [timeSpentLabel setStringValue:@(callModel->getFormattedCallDuration(callUid_).c_str())];
                }
222 223 224 225 226 227 228 229 230 231
                return;
            }
        }
    }

    // If call is not running anymore or accountInfo_ is not set for any reason
    // we stop the refresh loop
    [refreshDurationTimer invalidate];
    refreshDurationTimer = nil;
    [timeSpentLabel setHidden:YES];
Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
232
    [audioCallView setHidden:YES];
233 234
}

Andreas Traczyk's avatar
Andreas Traczyk committed
235
-(void) updateCall
236
{
237 238 239 240 241
    if (accountInfo_ == nil)
        return;

    auto* callModel = accountInfo_->callModel.get();
    if (not callModel->hasCall(callUid_)) {
242 243
        return;
    }
244

245 246
    auto currentCall = callModel->getCall(callUid_);
    NSLog(@"\n status %@ \n",@(lrc::api::call::to_string(currentCall.status).c_str()));
247
    auto convIt = getConversationFromUid(convUid_, *accountInfo_->conversationModel);
Andreas Traczyk's avatar
Andreas Traczyk committed
248 249 250 251
    if (convIt != accountInfo_->conversationModel->allFilteredConversations().end()) {
        NSString* bestName = bestNameForConversation(*convIt, *accountInfo_->conversationModel);
        [personLabel setStringValue:bestName];
        [outgoingPersonLabel setStringValue:bestName];
Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
252
        [audioCallPersonLabel setStringValue:bestName];
Andreas Traczyk's avatar
Andreas Traczyk committed
253 254 255 256 257
        NSString* ringID = bestIDForConversation(*convIt, *accountInfo_->conversationModel);
        if([bestName isEqualToString:ringID]) {
            ringID = @"";
        }
        [outgoingId setStringValue:ringID];
Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
258
        [audioCallPersonId setStringValue:ringID];
Andreas Traczyk's avatar
Andreas Traczyk committed
259 260
    }

261
    [timeSpentLabel setStringValue:@(callModel->getFormattedCallDuration(callUid_).c_str())];
Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
262
    [audioCallTime setStringValue:@(callModel->getFormattedCallDuration(callUid_).c_str())];
263 264 265 266 267 268
    if (refreshDurationTimer == nil)
        refreshDurationTimer = [NSTimer scheduledTimerWithTimeInterval:1.0
                                                                target:self
                                                              selector:@selector(updateDurationLabel)
                                                              userInfo:nil
                                                               repeats:YES];
269
    [stateLabel setStringValue:@(to_string(currentCall.status).c_str())];
Andreas Traczyk's avatar
Andreas Traczyk committed
270
    [outgoingStateLabel setStringValue:@(to_string(currentCall.status).c_str())];
Alexandre Lision's avatar
Alexandre Lision committed
271

Alexandre Lision's avatar
Alexandre Lision committed
272
    // Default values for this views
273
    [loadingIndicator setHidden:YES];
Alexandre Lision's avatar
Alexandre Lision committed
274 275 276 277
    [ringingPanel setHidden:YES];
    [outgoingPanel setHidden:YES];
    [controlsPanel setHidden:NO];
    [headerContainer setHidden:NO];
Anthony Léonard's avatar
Anthony Léonard committed
278
    [chatButton setHidden:NO];
Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
279
    [self setBackground];
Alexandre Lision's avatar
Alexandre Lision committed
280

281
    using Status = lrc::api::call::Status;
Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
282 283
    holdOnOffButton.image = currentCall.status == lrc::api::call::Status::PAUSED ?
    [NSImage imageNamed:@"ic_action_holdoff.png"] : [NSImage imageNamed:@"ic_action_hold.png"];
284 285 286
    switch (currentCall.status) {
        case Status::SEARCHING:
        case Status::CONNECTING:
Andreas Traczyk's avatar
Andreas Traczyk committed
287 288 289 290
            [headerContainer setHidden:YES];
            [outgoingPanel setHidden:NO];
            [outgoingPhoto setHidden:NO];
            [self setupContactInfo:outgoingPhoto];
291
            [loadingIndicator setHidden:NO];
Andreas Traczyk's avatar
Andreas Traczyk committed
292
            [controlsPanel setHidden:YES];
293
            break;
294
        case Status::INCOMING_RINGING:
Andreas Traczyk's avatar
Andreas Traczyk committed
295
            [outgoingPhoto setHidden:YES];
296 297 298
            [controlsPanel setHidden:YES];
            [outgoingPanel setHidden:YES];
            [self setupIncoming:callUid_];
299
            break;
300 301 302
        case Status::OUTGOING_RINGING:
            [controlsPanel setHidden:YES];
            [outgoingPanel setHidden:NO];
Andreas Traczyk's avatar
Andreas Traczyk committed
303 304
            [loadingIndicator setHidden:NO];
            [headerContainer setHidden:YES];
305
            break;
Andreas Traczyk's avatar
Andreas Traczyk committed
306 307 308
        /*case Status::CONFERENCE:
            [self setupConference:currentCall];
            break;*/
309
        case Status::PAUSED:
Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
310 311 312 313 314 315 316 317 318
            [self.videoMTKView fillWithBlack];
            [self.previewView fillWithBlack];
            [bluerBackgroundEffect setHidden:NO];
            [backgroundImage setHidden:NO];
            [self.previewView setHidden: YES];
            [self.videoMTKView setHidden: YES];
            self.previewView.stopRendering = true;
            self.videoMTKView.stopRendering = true;
            break;
319
        case Status::INACTIVE:
Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
320 321 322 323 324 325
            if(currentCall.isAudioOnly) {
                [self setUpAudioOnlyView];
            } else {
                [self setUpVideoCallView];
            }
            break;
326
        case Status::IN_PROGRESS:
Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
327 328 329 330 331 332
            self.previewView.stopRendering = false;
            self.videoMTKView.stopRendering = false;
            [previewView fillWithBlack];
            [self.videoMTKView fillWithBlack];
            [self.previewView setHidden: NO];
            [self.videoMTKView setHidden: NO];
Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
333 334 335 336 337
            if(currentCall.isAudioOnly) {
                [self setUpAudioOnlyView];
            } else {
                [self setUpVideoCallView];
            }
Andreas Traczyk's avatar
Andreas Traczyk committed
338
            break;
339
        case Status::CONNECTED:
340
            break;
341 342 343
        case Status::ENDED:
        case Status::TERMINATING:
        case Status::INVALID:
344 345 346 347
            break;
    }
}

Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
348 349 350 351 352 353
-(void) setUpVideoCallView {
    [self setupContactInfo:personPhoto];
    [timeSpentLabel setHidden:NO];
    [outgoingPhoto setHidden:YES];
    [headerContainer setHidden:NO];
    [previewView setHidden: NO];
Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
354
    [self.videoMTKView setHidden:NO];
Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
355 356
    [bluerBackgroundEffect setHidden:YES];
    [backgroundImage setHidden:YES];
Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
357
    [audioCallView setHidden:YES];
Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
358 359 360 361 362
}

-(void) setUpAudioOnlyView {
    [audioCallView setHidden:NO];
    [headerContainer setHidden:YES];
Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
363 364
    [self.previewView setHidden: YES];
    [self.videoMTKView setHidden: YES];
Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
365 366 367 368
    [audioCallPhoto setImage: [self getContactImageOfSize:120.0 withDefaultAvatar:YES]];
}

-(void) setBackground {
Andreas Traczyk's avatar
Andreas Traczyk committed
369 370
    auto* convModel = accountInfo_->conversationModel.get();
    auto it = getConversationFromUid(convUid_, *convModel);
Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
371 372
    NSImage *image= [self getContactImageOfSize:120.0 withDefaultAvatar:NO];
    if(image) {
Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
373 374 375 376 377
        CIImage * ciImage = [[CIImage alloc] initWithData:[image TIFFRepresentation]];
        CIContext *context = [[CIContext alloc] init];
        CIFilter *clamp = [CIFilter filterWithName:@"CIAffineClamp"];
        [clamp setValue:[NSAffineTransform transform] forKey:@"inputTransform"];
        [clamp setValue:ciImage forKey: kCIInputImageKey];
Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
378 379
        CIFilter* bluerFilter = [CIFilter filterWithName:@"CIGaussianBlur"];
        [bluerFilter setDefaults];
Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
380 381 382 383 384 385 386
        [bluerFilter setValue:[NSNumber numberWithFloat: 9] forKey:@"inputRadius"];
        [bluerFilter setValue:[clamp valueForKey:kCIOutputImageKey] forKey: kCIInputImageKey];
        CIImage *result = [bluerFilter valueForKey:kCIOutputImageKey];
        CGRect extent = [result extent];
        CGImageRef cgImage = [context createCGImage:result fromRect: [ciImage extent]];
        NSImage *bluredImage = [[NSImage alloc] initWithCGImage:cgImage size:NSSizeFromCGSize(CGSizeMake(image.size.width, image.size.height))];
        backgroundImage.layer.contents = bluredImage;
Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
387 388 389 390 391 392 393
        [backgroundImage setHidden:NO];
    } else {
        [bluerBackgroundEffect setFillColor:[NSColor ringDarkGrey]];
        [backgroundImage setHidden:YES];
    }
}

Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
394
-(NSImage *) getContactImageOfSize: (double) size withDefaultAvatar:(BOOL) shouldDrawDefault {
Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
395 396 397 398 399 400
    auto* convModel = accountInfo_->conversationModel.get();
    auto convIt = getConversationFromUid(convUid_, *convModel);
    if (convIt == convModel->allFilteredConversations().end()) {
        return nil;
    }
    if(shouldDrawDefault) {
Andreas Traczyk's avatar
Andreas Traczyk committed
401
        auto& imgManip = reinterpret_cast<Interfaces::ImageManipulationDelegate&>(GlobalInstances::pixmapManipulator());
Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
402 403
        QVariant photo = imgManip.conversationPhoto(*convIt, *accountInfo_, QSize(size, size), NO);
        return QtMac::toNSImage(qvariant_cast<QPixmap>(photo));
Andreas Traczyk's avatar
Andreas Traczyk committed
404
    }
Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
405 406 407 408 409 410 411 412
    auto contact = accountInfo_->contactModel->getContact(convIt->participants[0]);
    NSData *imageData = [[NSData alloc] initWithBase64EncodedString:@(contact.profileInfo.avatar.c_str()) options:NSDataBase64DecodingIgnoreUnknownCharacters];
    return [[NSImage alloc] initWithData:imageData];
}

-(void) setupContactInfo:(NSImageView*)imageView
{
    [imageView setImage: [self getContactImageOfSize:120.0 withDefaultAvatar:YES]];
Andreas Traczyk's avatar
Andreas Traczyk committed
413 414
}

415
-(void) setupIncoming:(const std::string&) callId
Alexandre Lision's avatar
Alexandre Lision committed
416
{
417 418 419 420 421 422 423 424 425 426 427
    if (accountInfo_ == nil)
        return;

    auto* callModel = accountInfo_->callModel.get();
    if (not callModel->hasCall(callUid_)) {
        return;
    }

    auto call = callModel->getCall(callUid_);
    auto* convModel = accountInfo_->conversationModel.get();

Alexandre Lision's avatar
Alexandre Lision committed
428 429 430
    [ringingPanel setHidden:NO];
    [controlsPanel setHidden:YES];
    [headerContainer setHidden:YES];
431
    auto it = getConversationFromUid(convUid_, *convModel);
432
    if (it != convModel->allFilteredConversations().end()) {
Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
433
        [incomingPersonPhoto setImage: [self getContactImageOfSize:120.0 withDefaultAvatar:YES]];
434
        [incomingDisplayName setStringValue:bestNameForConversation(*it, *convModel)];
435
    }
Alexandre Lision's avatar
Alexandre Lision committed
436 437 438 439 440 441 442 443 444 445
}

-(void) setupConference:(Call*) c
{
    [videoView setShouldAcceptInteractions:YES];
    [self.chatButton setHidden:NO];
    [self.addParticipantButton setHidden:NO];
    [self.transferButton setHidden:YES];
}

446
-(void)collapseRightView
447
{
448 449 450 451 452
    NSView *right = [[splitView subviews] objectAtIndex:1];
    NSView *left  = [[splitView subviews] objectAtIndex:0];
    NSRect leftFrame = [left frame];
    [right setHidden:YES];
    [splitView display];
453 454
}

455
- (void) changeCallSelection:(std::string&)callUid
456
{
457 458
    if (accountInfo_ == nil)
        return;
Alexandre Lision's avatar
Alexandre Lision committed
459

460 461 462 463
    auto* callModel = accountInfo_->callModel.get();
    if (not callModel->hasCall(callUid)) {
        return;
    }
Alexandre Lision's avatar
Alexandre Lision committed
464 465

    QObject::disconnect(self.selectedCallChanged);
466 467 468
    self.selectedCallChanged = QObject::connect(callModel,
                                                &lrc::api::NewCallModel::callStatusChanged,
                                                [self](const std::string callId) {
Andreas Traczyk's avatar
Andreas Traczyk committed
469
                                                    [self updateCall];
Alexandre Lision's avatar
Alexandre Lision committed
470
                                                });
471 472 473 474
}

-(void) connectVideoSignals
{
475 476
    if (accountInfo_ == nil)
        return;
477

478 479 480 481 482
    auto* callModel = accountInfo_->callModel.get();
    QObject::disconnect(self.videoStarted);
    self.videoStarted = QObject::connect(callModel,
                                         &lrc::api::NewCallModel::remotePreviewStarted,
                                         [self](const std::string& callId, Video::Renderer* renderer) {
Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
483
                                             [videoView setShouldAcceptInteractions:YES];
Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
484
                                             [self.videoMTKView setHidden:NO];
Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
485
                                             [self mouseIsMoving: NO];
486 487 488 489 490 491
                                             [self connectVideoRenderer:renderer];
                                         });

    if (callModel->hasCall(callUid_)) {
        if (auto renderer = callModel->getRenderer(callUid_)) {
            QObject::disconnect(self.videoStarted);
Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
492
            [self.videoMTKView setHidden:NO];
493 494
            [self connectVideoRenderer: renderer];
        }
495 496 497 498 499 500
    }
    [self connectPreviewRenderer];
}

-(void) connectPreviewRenderer
{
501 502 503
    QObject::disconnect(previewHolder.frameUpdated);
    QObject::disconnect(previewHolder.stopped);
    QObject::disconnect(previewHolder.started);
Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
504 505
    previewHolder.started =
    QObject::connect(&Video::PreviewManager::instance(),
506 507
                     &Video::PreviewManager::previewStarted,
                     [=](Video::Renderer* renderer) {
Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
508 509
                         [self.previewView setHidden:NO];
                         self.previewView.stopRendering = false;
510
                         QObject::disconnect(previewHolder.frameUpdated);
Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
511 512 513 514 515 516 517 518 519 520
                         previewHolder.frameUpdated =
                         QObject::connect(renderer,
                                          &Video::Renderer::frameUpdated,
                                          [=]() {
                                              if(!renderer->isRendering()) {
                                                  return;
                                              }
                                              [self renderer:renderer renderFrameForPreviewView: self.previewView];
                         });
    });
Alexandre Lision's avatar
Alexandre Lision committed
521
    previewHolder.stopped = QObject::connect(&Video::PreviewManager::instance(),
522 523
                     &Video::PreviewManager::previewStopped,
                     [=](Video::Renderer* renderer) {
Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
524
                        QObject::disconnect(previewHolder.frameUpdated);
Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
525 526
                        [self.previewView setHidden:YES];
                        self.previewView.stopRendering = true;
527 528
                     });

Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
529 530 531 532 533 534 535 536 537 538 539
    previewHolder.frameUpdated =
    QObject::connect(Video::PreviewManager::instance().previewRenderer(),
                     &Video::Renderer::frameUpdated,
                     [=]() {
                         if(!Video::PreviewManager::instance()
                            .previewRenderer()->isRendering()) {
                             return;
                         }
                         [self renderer:Video::PreviewManager::instance()
                          .previewRenderer() renderFrameForPreviewView: self.previewView];
    });
540 541 542 543
}

-(void) connectVideoRenderer: (Video::Renderer*)renderer
{
544 545 546
    QObject::disconnect(videoHolder.frameUpdated);
    QObject::disconnect(videoHolder.started);
    QObject::disconnect(videoHolder.stopped);
547 548 549 550

    if(renderer == nil)
        return;

551 552 553
    videoHolder.frameUpdated = QObject::connect(renderer,
                     &Video::Renderer::frameUpdated,
                     [=]() {
Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
554 555 556
                         if(!renderer->isRendering()) {
                             return;
                         }
Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
557
                         [self renderer:renderer renderFrameForDistantView:self.videoMTKView];
558 559
                     });

Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
560 561
    videoHolder.started =
    QObject::connect(renderer,
562 563
                     &Video::Renderer::started,
                     [=]() {
Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
564
                         [self mouseIsMoving: NO];
Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
565 566 567 568
                         self.videoMTKView.stopRendering = false;
                         [self.videoMTKView setHidden:NO];
                         [bluerBackgroundEffect setHidden:YES];
                         [backgroundImage setHidden:YES];
Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
569
                         [videoView setShouldAcceptInteractions:YES];
570
                         QObject::disconnect(videoHolder.frameUpdated);
Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
571 572 573 574 575 576 577 578 579
                         videoHolder.frameUpdated
                         = QObject::connect(renderer,
                                            &Video::Renderer::frameUpdated,
                                            [=]() {
                                                if(!renderer->isRendering()) {
                                                    return;
                                                }
                                                [self renderer:renderer renderFrameForDistantView:self.videoMTKView];
                                            });
580 581 582 583 584
                     });

    videoHolder.stopped = QObject::connect(renderer,
                     &Video::Renderer::stopped,
                     [=]() {
Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
585
                         [self mouseIsMoving: YES];
Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
586 587 588 589
                         self.videoMTKView.stopRendering = true;
                         [self.videoMTKView setHidden:YES];
                         [bluerBackgroundEffect setHidden:NO];
                         [backgroundImage setHidden:NO];
Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
590
                         [videoView setShouldAcceptInteractions:NO];
591 592 593 594
                         QObject::disconnect(videoHolder.frameUpdated);
                     });
}

Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
595
-(void) renderer: (Video::Renderer*)renderer renderFrameForPreviewView:(CallMTKView*) view
596
{
Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618
    @autoreleasepool {
        auto framePtr = renderer->currentAVFrame();
        auto frame = framePtr.get();
        if(!frame || !frame->width || !frame->height) {
            return;
        }
        auto frameSize = CGSizeMake(frame->width, frame->height);
        auto rotation = 0;
        if (frame->data[3] != NULL && (CVPixelBufferRef)frame->data[3]) {
            [view renderWithPixelBuffer:(CVPixelBufferRef)frame->data[3]
                                   size: frameSize
                               rotation: rotation
                              fillFrame: true];
            return;
        }
        else if (CVPixelBufferRef pixelBuffer = [self getBufferForPreviewFromFrame:frame]) {
            [view renderWithPixelBuffer: pixelBuffer
                                   size: frameSize
                               rotation: rotation
                              fillFrame: true];
        }
    }
619 620
}

Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
621
-(void) renderer: (Video::Renderer*)renderer renderFrameForDistantView:(CallMTKView*) view
622
{
Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703
    @autoreleasepool {
        auto framePtr = renderer->currentAVFrame();
        auto frame = framePtr.get();
        if(!frame || !frame->width || !frame->height)  {
            return;
        }
        auto frameSize = CGSizeMake(frame->width, frame->height);
        auto rotation = 0;
        if (auto matrix = av_frame_get_side_data(frame, AV_FRAME_DATA_DISPLAYMATRIX)) {
            const int32_t* data = reinterpret_cast<int32_t*>(matrix->data);
            rotation = av_display_rotation_get(data);
        }
        if (frame->data[3] != NULL && (CVPixelBufferRef)frame->data[3]) {
            [view renderWithPixelBuffer: (CVPixelBufferRef)frame->data[3]
                                   size: frameSize
                               rotation: rotation
                              fillFrame: false];
        }
        if (CVPixelBufferRef pixelBuffer = [self getBufferForDistantViewFromFrame:frame]) {
            [view renderWithPixelBuffer: pixelBuffer
                                   size: frameSize
                               rotation: rotation
                              fillFrame: false];
        }
    }
}

-(CVPixelBufferRef) getBufferForPreviewFromFrame:(const AVFrame*)frame {
    if(!frame || !frame->data[0] || !frame->data[1]) {
        return nil;
    }
    CVReturn theError;
    bool createPool = false;
    if (!pixelBufferPoolPreview) {
        createPool = true;
    } else {
        NSDictionary* atributes = (__bridge NSDictionary*)CVPixelBufferPoolGetAttributes(pixelBufferPoolPreview);
        int width = [[atributes objectForKey:(NSString*)kCVPixelBufferWidthKey] intValue];
        int height = [[atributes objectForKey:(NSString*)kCVPixelBufferHeightKey] intValue];
        if (width != frame->width || height != frame->height) {
            createPool = true;
        }
    }
    if (createPool) {
        CVPixelBufferPoolRelease(pixelBufferPoolPreview);
        CVPixelBufferRelease(pixelBufferPreview);
        pixelBufferPreview = nil;
        pixelBufferPoolPreview = nil;
        NSMutableDictionary* attributes = [NSMutableDictionary dictionary];
        [attributes setObject:[NSNumber numberWithInt:kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange] forKey:(NSString*)kCVPixelBufferPixelFormatTypeKey];
        [attributes setObject:[NSNumber numberWithInt:frame->width] forKey: (NSString*)kCVPixelBufferWidthKey];
        [attributes setObject:[NSNumber numberWithInt:frame->height] forKey: (NSString*)kCVPixelBufferHeightKey];
        [attributes setObject:@(frame->linesize[0]) forKey:(NSString*)kCVPixelBufferBytesPerRowAlignmentKey];
        [attributes setObject:[NSDictionary dictionary] forKey:(NSString*)kCVPixelBufferIOSurfacePropertiesKey];
        theError = CVPixelBufferPoolCreate(kCFAllocatorDefault, NULL, (__bridge CFDictionaryRef) attributes, &pixelBufferPoolPreview);
        if (theError != kCVReturnSuccess) {
            NSLog(@"CVPixelBufferPoolCreate Failed");
            return nil;
        }
    }
    if(!pixelBufferPreview) {
        theError = CVPixelBufferPoolCreatePixelBuffer(NULL, pixelBufferPoolPreview, &pixelBufferPreview);
        if(theError != kCVReturnSuccess) {
            NSLog(@"CVPixelBufferPoolCreatePixelBuffer Failed");
            return nil;
        }
    }
    theError = CVPixelBufferLockBaseAddress(pixelBufferPreview, 0);
    if (theError != kCVReturnSuccess) {
        NSLog(@"lock error");
        return nil;
    }
    size_t bytePerRowY = CVPixelBufferGetBytesPerRowOfPlane(pixelBufferPreview, 0);
    size_t bytesPerRowUV = CVPixelBufferGetBytesPerRowOfPlane(pixelBufferPreview, 1);
    void* base = CVPixelBufferGetBaseAddressOfPlane(pixelBufferPreview, 0);
    memcpy(base, frame->data[0], bytePerRowY * frame->height);
    base = CVPixelBufferGetBaseAddressOfPlane(pixelBufferPreview, 1);
    memcpy(base, frame->data[1], bytesPerRowUV * frame->height/2);
    CVPixelBufferUnlockBaseAddress(pixelBufferPreview, 0);
    return pixelBufferPreview;
}
704

Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
705 706 707 708 709 710 711 712 713 714 715 716 717 718
-(CVPixelBufferRef) getBufferForDistantViewFromFrame:(const AVFrame*)frame {
    if(!frame || !frame->data[0] || !frame->data[1]) {
        return nil;
    }
    CVReturn theError;
    bool createPool = false;
    if (!pixelBufferPoolDistantView){
        createPool = true;
    }
    NSDictionary* atributes = (__bridge NSDictionary*)CVPixelBufferPoolGetPixelBufferAttributes(pixelBufferPoolDistantView);
    int width = [[atributes objectForKey:(NSString*)kCVPixelBufferWidthKey] intValue];
    int height = [[atributes objectForKey:(NSString*)kCVPixelBufferHeightKey] intValue];
    if (width != frame->width || height != frame->height) {
        createPool = true;
Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
719
    }
Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772
    if (createPool) {
        CVPixelBufferPoolRelease(pixelBufferPoolDistantView);
        CVPixelBufferRelease(pixelBufferDistantView);
        pixelBufferDistantView = nil;
        pixelBufferPoolDistantView = nil;
        NSMutableDictionary* attributes = [NSMutableDictionary dictionary];
        [attributes setObject:[NSNumber numberWithInt:kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange] forKey:(NSString*)kCVPixelBufferPixelFormatTypeKey];
        [attributes setObject:[NSNumber numberWithInt:frame->width] forKey: (NSString*)kCVPixelBufferWidthKey];
        [attributes setObject:[NSNumber numberWithInt:frame->height] forKey: (NSString*)kCVPixelBufferHeightKey];
        [attributes setObject:@(frame->linesize[0]) forKey:(NSString*)kCVPixelBufferBytesPerRowAlignmentKey];
        [attributes setObject:[NSDictionary dictionary] forKey:(NSString*)kCVPixelBufferIOSurfacePropertiesKey];
        theError = CVPixelBufferPoolCreate(kCFAllocatorDefault, NULL, (__bridge CFDictionaryRef) attributes, &pixelBufferPoolDistantView);
        if (theError != kCVReturnSuccess) {
            return nil;
            NSLog(@"CVPixelBufferPoolCreate Failed");
        }
    }
    if(!pixelBufferDistantView) {
        theError = CVPixelBufferPoolCreatePixelBuffer(NULL, pixelBufferPoolDistantView, &pixelBufferDistantView);
        if(theError != kCVReturnSuccess) {
            return nil;
            NSLog(@"CVPixelBufferPoolCreatePixelBuffer Failed");
        }
    }
    theError = CVPixelBufferLockBaseAddress(pixelBufferDistantView, 0);
    if (theError != kCVReturnSuccess) {
        return nil;
        NSLog(@"lock error");
    }
    size_t bytePerRowY = CVPixelBufferGetBytesPerRowOfPlane(pixelBufferDistantView, 0);
    size_t bytesPerRowUV = CVPixelBufferGetBytesPerRowOfPlane(pixelBufferDistantView, 1);
    void* base = CVPixelBufferGetBaseAddressOfPlane(pixelBufferDistantView, 0);
    memcpy(base, frame->data[0], bytePerRowY * frame->height);
    base = CVPixelBufferGetBaseAddressOfPlane(pixelBufferDistantView, 1);
    uint32_t size = frame->linesize[1] * frame->height / 2;
    uint8_t* dstData = new uint8_t[2 * size];
    uint8_t * firstData = new uint8_t[size];
    memcpy(firstData, frame->data[1], size);
    uint8_t * secondData  = new uint8_t[size];
    memcpy(secondData, frame->data[2], size);
    for (int i = 0; i < 2 * size; i++){
        if (i % 2 == 0){
            dstData[i] = firstData[i/2];
        }else {
            dstData[i] = secondData[i/2];
        }
    }
    memcpy(base, dstData, bytesPerRowUV * frame->height/2);
    CVPixelBufferUnlockBaseAddress(pixelBufferDistantView, 0);
    free(dstData);
    free(firstData);
    free(secondData);
    return pixelBufferDistantView;
773 774
}

775 776 777 778 779
- (void) initFrame
{
    [self.view setFrame:self.view.superview.bounds];
    [self.view setHidden:YES];
    self.view.layer.position = self.view.frame.origin;
780
    [self collapseRightView];
Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
781 782
    self.testView = [[NSImageView alloc] initWithFrame:self.view.frame];
    [self.view addSubview:self.testView];
783 784 785 786
}

# pragma private IN/OUT animations

787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807
-(void)uncollapseRightView
{
    NSView *left  = [[splitView subviews] objectAtIndex:0];
    NSView *right = [[splitView subviews] objectAtIndex:1];
    [right setHidden:NO];

    CGFloat dividerThickness = [splitView dividerThickness];

    // get the different frames
    NSRect leftFrame = [left frame];
    NSRect rightFrame = [right frame];

    leftFrame.size.width = (leftFrame.size.width - rightFrame.size.width - dividerThickness);
    rightFrame.origin.x = leftFrame.size.width + dividerThickness;
    [left setFrameSize:leftFrame.size];
    [right setFrame:rightFrame];
    [splitView display];

    [self.chatVC takeFocus];
}

808 809
-(void) cleanUp
{
810 811
    if(self.splitView.isInFullScreenMode)
        [self.splitView exitFullScreenModeWithOptions:nil];
812 813 814 815 816 817
    QObject::disconnect(videoHolder.frameUpdated);
    QObject::disconnect(videoHolder.started);
    QObject::disconnect(videoHolder.stopped);
    QObject::disconnect(previewHolder.frameUpdated);
    QObject::disconnect(previewHolder.stopped);
    QObject::disconnect(previewHolder.started);
818
    QObject::disconnect(self.messageConnection);
Alexandre Lision's avatar
Alexandre Lision committed
819

820
    [self.chatButton setHidden:YES];
821
    [self.chatButton setPressed:NO];
Alexandre Lision's avatar
Alexandre Lision committed
822
    [self collapseRightView];
Alexandre Lision's avatar
Alexandre Lision committed
823 824 825 826

    [personLabel setStringValue:@""];
    [timeSpentLabel setStringValue:@""];
    [stateLabel setStringValue:@""];
Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
827 828 829 830 831 832 833 834 835 836
    //audio view
    [audioCallTime setStringValue:@""];
    [audioCallPersonId setStringValue:@""];
    [audioCallPersonLabel setStringValue:@""];
    [audioCallView setHidden:YES];
    [audioCallPhoto setImage:nil];
    //background view
    [bluerBackgroundEffect setHidden:NO];
    [backgroundImage setHidden:NO];
    //outgoing view
Andreas Traczyk's avatar
Andreas Traczyk committed
837 838
    [outgoingPersonLabel setStringValue:@""];
    [outgoingStateLabel setStringValue:@""];
Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
839 840
    [self.previewView setHidden:YES];
    [self.videoMTKView setHidden:YES];
841 842
}

843 844 845 846 847 848 849 850 851
-(void) setupCallView
{
    if (accountInfo_ == nil)
        return;

    auto* callModel = accountInfo_->callModel.get();
    auto* convModel = accountInfo_->conversationModel.get();

    // when call comes in we want to show the controls/header
Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
852 853
    [self mouseIsMoving: YES];
    [videoView setShouldAcceptInteractions: NO];
854 855 856 857 858 859 860

    [self connectVideoSignals];
    /* check if text media is already present */
    if(not callModel->hasCall(callUid_))
        return;

    [loadingIndicator setAnimates:YES];
Andreas Traczyk's avatar
Andreas Traczyk committed
861
    [self updateCall];
862 863 864 865 866 867 868 869 870 871 872 873 874 875 876

    /* monitor media for messaging text messaging */
    QObject::disconnect(self.messageConnection);
    self.messageConnection = QObject::connect(convModel,
                                              &lrc::api::ConversationModel::interactionStatusUpdated,
                                              [self] (std::string convUid,
                                                      uint64_t msgId,
                                                      lrc::api::interaction::Info msg) {
                                                  if (msg.type == lrc::api::interaction::Type::TEXT) {
                                                      if(not [[self splitView] isSubviewCollapsed:[[[self splitView] subviews] objectAtIndex: 1]]){
                                                          return;
                                                      }
                                                      [self uncollapseRightView];
                                                  }
                                              });
Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895
    //monitor for updated profile
    QObject::disconnect(self.profileUpdatedConnection);
    self.profileUpdatedConnection =
    QObject::connect(accountInfo_->contactModel.get(),
                     &lrc::api::ContactModel::contactAdded,
                     [self](const std::string &contactUri) {
                         auto convIt = getConversationFromUid(convUid_, *accountInfo_->conversationModel.get());
                         if (convIt == accountInfo_->conversationModel->allFilteredConversations().end()) {
                             return;
                         }
                         if (convIt->participants.empty()) {
                             return;

                         }
                         auto& contact = accountInfo_->contactModel->getContact(convIt->participants[0]);
                         if (contact.profileInfo.type == lrc::api::profile::Type::RING && contact.profileInfo.uri == contactUri)
                             accountInfo_->conversationModel->makePermanent(convUid_);
                         [incomingPersonPhoto setImage: [self getContactImageOfSize:120.0 withDefaultAvatar:YES]];
                         [outgoingPhoto setImage: [self getContactImageOfSize:120.0 withDefaultAvatar:YES]];
Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
896
                         [self.delegate conversationInfoUpdatedFor:convUid_];
Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
897 898 899 900 901 902 903
                         if(accountInfo_->callModel.get()->getCall(callUid_).isAudioOnly) {
                         [audioCallPhoto setImage: [self getContactImageOfSize:120.0 withDefaultAvatar:YES]];
                             [self setBackground];
                             return;
                         }
                         [personPhoto setImage: [self getContactImageOfSize:120.0 withDefaultAvatar:YES]];
                     });
904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931
}

-(void) showWithAnimation:(BOOL)animate
{
    if (!animate) {
        [self.view setHidden:NO];
        [self setupCallView];
        return;
    }

    CGRect frame = CGRectOffset(self.view.superview.bounds, -self.view.superview.bounds.size.width, 0);
    [self.view setHidden:NO];

    [CATransaction begin];
    CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"position"];
    [animation setFromValue:[NSValue valueWithPoint:frame.origin]];
    [animation setToValue:[NSValue valueWithPoint:self.view.superview.bounds.origin]];
    [animation setDuration:0.2f];
    [animation setTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseIn]];
    [CATransaction setCompletionBlock:^{
        [self setupCallView];
    }];

    [self.view.layer addAnimation:animation forKey:animation.keyPath];
    [CATransaction commit];
}

-(void) hideWithAnimation:(BOOL)animate
932 933 934 935 936
{
    if(self.view.frame.origin.x < 0) {
        return;
    }

937 938 939 940 941
    if (!animate) {
        [self.view setHidden:YES];
        return;
    }

942 943 944 945 946 947
    CGRect frame = CGRectOffset(self.view.frame, -self.view.frame.size.width, 0);
    [CATransaction begin];
    CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"position"];
    [animation setFromValue:[NSValue valueWithPoint:self.view.frame.origin]];
    [animation setToValue:[NSValue valueWithPoint:frame.origin]];
    [animation setDuration:0.2f];
948
    [animation setTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut]];
949 950 951

    [CATransaction setCompletionBlock:^{
        [self.view setHidden:YES];
952
        // first make sure everything is disconnected
953
        [self cleanUp];
954 955 956
//        if (currentCall_) {
//            [self animateIn];
//        }
957 958
    }];
    [self.view.layer addAnimation:animation forKey:animation.keyPath];
959 960

    [self.view.layer setPosition:frame.origin];
961 962 963 964 965 966 967 968 969 970 971
    [CATransaction commit];
}

/**
 *  Debug purpose
 */
-(void) dumpFrame:(CGRect) frame WithName:(NSString*) name
{
    NSLog(@"frame %@ : %f %f %f %f \n\n",name ,frame.origin.x, frame.origin.y, frame.size.width, frame.size.height);
}

972
#pragma mark - Button methods
Alexandre Lision's avatar
Alexandre Lision committed
973

974 975 976
- (IBAction)hangUp:(id)sender {
    if (accountInfo_ == nil)
        return;
Alexandre Lision's avatar
Alexandre Lision committed
977

978 979
    auto* callModel = accountInfo_->callModel.get();
    callModel->hangUp(callUid_);
Alexandre Lision's avatar
Alexandre Lision committed
980 981
}

982 983 984
- (IBAction)accept:(id)sender {
    if (accountInfo_ == nil)
        return;
Alexandre Lision's avatar
Alexandre Lision committed
985

986 987 988 989 990 991 992 993
    // If we accept a conversation with a non trusted contact, we first accept it
    auto convIt = getConversationFromUid(convUid_, *accountInfo_->conversationModel.get());
    if (convIt != accountInfo_->conversationModel->allFilteredConversations().end()) {
        auto& contact = accountInfo_->contactModel->getContact(convIt->participants[0]);
        if (contact.profileInfo.type == lrc::api::profile::Type::PENDING)
            accountInfo_->conversationModel->makePermanent(convUid_);
    }

994
    auto* callModel = accountInfo_->callModel.get();
Alexandre Lision's avatar
Alexandre Lision committed
995

996
    callModel->accept(callUid_);
997 998
}

999 1000 1001
- (IBAction)toggleRecording:(id)sender {
    if (accountInfo_ == nil)
        return;
1002

1003
    auto* callModel = accountInfo_->callModel.get();
1004

1005
    callModel->toggleAudioRecord(callUid_);
Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
1006
    [recordOnOffButton setPressed:!recordOnOffButton.isPressed];
1007 1008 1009
}

- (IBAction)toggleHold:(id)sender {
1010 1011 1012 1013
    if (accountInfo_ == nil)
        return;

    auto* callModel = accountInfo_->callModel.get();
Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
1014 1015 1016 1017 1018 1019
    auto currentCall = callModel->getCall(callUid_);
    if (currentCall.status != lrc::api::call::Status::PAUSED) {
        holdOnOffButton.image = [NSImage imageNamed:@"ic_action_holdoff.png"];
    } else {
        holdOnOffButton.image = [NSImage imageNamed:@"ic_action_hold.png"];
    }
1020 1021

    callModel->togglePause(callUid_);
1022 1023
}

Alexandre Lision's avatar
Alexandre Lision committed
1024 1025 1026 1027 1028 1029 1030 1031 1032 1033
- (IBAction)toggleAdvancedControls:(id)sender {
    [advancedButton setPressed:!advancedButton.isPressed];
    [advancedPanel setHidden:![advancedButton isPressed]];
}

- (IBAction)showDialpad:(id)sender {
    AppDelegate* appDelegate = (AppDelegate *)[[NSApplication sharedApplication] delegate];
    [appDelegate showDialpad];
}

Alexandre Lision's avatar
Alexandre Lision committed
1034 1035 1036 1037 1038 1039 1040 1041
-(IBAction)toggleChat:(id)sender;
{
    BOOL rightViewCollapsed = [[self splitView] isSubviewCollapsed:[[[self splitView] subviews] objectAtIndex: 1]];
    if (rightViewCollapsed) {
        [self uncollapseRightView];
    } else {
        [self collapseRightView];
    }
Alexandre Lision's avatar
Alexandre Lision committed
1042
    [chatButton setPressed:rightViewCollapsed];
Alexandre Lision's avatar
Alexandre Lision committed
1043 1044
}

1045 1046
- (IBAction)muteAudio:(id)sender
{
1047 1048 1049 1050
    if (accountInfo_ == nil)
        return;

    auto* callModel = accountInfo_->callModel.get();
Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
1051 1052 1053 1054 1055 1056
    auto currentCall = callModel->getCall(callUid_);
    if (currentCall.audioMuted) {
        muteAudioButton.image = [NSImage imageNamed:@"ic_action_audio.png"];
    } else {
       muteAudioButton.image = [NSImage imageNamed:@"ic_action_mute_audio.png"];
    }
1057
    callModel->toggleMedia(callUid_, lrc::api::NewCallModel::Media::AUDIO);
1058 1059 1060 1061
}

- (IBAction)muteVideo:(id)sender
{
1062 1063 1064
    if (accountInfo_ == nil)
        return;
    auto* callModel = accountInfo_->callModel.get();
Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
1065 1066 1067 1068 1069 1070 1071 1072
    auto currentCall = callModel->getCall(callUid_);
    if(!currentCall.isAudioOnly) {
        if (currentCall.videoMuted) {
            muteVideoButton.image = [NSImage imageNamed:@"ic_action_video.png"];
        } else {
            muteVideoButton.image = [NSImage imageNamed:@"ic_action_mute_video.png"];
        }
    }
1073
    callModel->toggleMedia(callUid_, lrc::api::NewCallModel::Media::VIDEO);
1074
}
Alexandre Lision's avatar
Alexandre Lision committed
1075 1076

- (IBAction)toggleTransferView:(id)sender {
1077

1078 1079 1080
}

- (IBAction)toggleAddParticipantView:(id)sender {
1081
    
1082
}
1083

1084 1085
#pragma mark - NSPopOverDelegate

Alexandre Lision's avatar
Alexandre Lision committed
1086 1087
- (void)popoverWillClose:(NSNotification *)notification
{