Commit ccbf2202 authored by Christophe Deschamps's avatar Christophe Deschamps
Browse files

- Use the new conference call API

- Displays participants when joining a conference call as a guest
parent ef744eb0
Pipeline #34178 failed with stage
in 4 minutes and 42 seconds
This diff is collapsed.
......@@ -21,6 +21,7 @@
#import "UICallConferenceCell.h"
#import "LinphoneManager.h"
#import "Utils.h"
#import "linphoneapp-Swift.h"
@implementation CallConferenceTableView
......@@ -30,21 +31,6 @@
[self.tableView reloadData];
}
#pragma mark - UITableViewDataSource Functions
- (LinphoneCall *)conferenceCallForRow:(NSInteger)row {
const MSList *calls = linphone_core_get_calls(LC);
int i = -1;
while (calls) {
if (linphone_call_params_get_local_conference_mode(linphone_call_get_current_params(calls->data))) {
i++;
if (i == row)
break;
}
calls = calls->next;
}
return (calls ? calls->data : NULL);
}
#pragma mark - UITableViewDataSource Functions
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
......@@ -53,21 +39,16 @@
if (cell == nil) {
cell = [[UICallConferenceCell alloc] initWithIdentifier:kCellId];
}
[cell setCall:[self conferenceCallForRow:indexPath.row]];
cell.contentView.userInteractionEnabled = false;
LinphoneConference *c = [CallManager.instance getConference];
if (c != nil) {
[cell setParticipant:bctbx_list_nth_data(linphone_conference_get_participant_list(c),(int)indexPath.row)];
}
cell.contentView.userInteractionEnabled = NO;
return cell;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
const MSList *calls = linphone_core_get_calls(LC);
int count = 0;
while (calls) {
if (linphone_call_params_get_local_conference_mode(linphone_call_get_current_params(calls->data))) {
count++;
}
calls = calls->next;
}
return count;
return [CallManager.instance getConference] ? linphone_conference_get_participant_count([CallManager.instance getConference]) : 0;
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
......
......@@ -45,6 +45,9 @@ import AVFoundation
var endCallkit: Bool = false
var globalState : GlobalState = .Off
var actionsToPerformOnceWhenCoreIsOn : [(()->Void)] = []
var conference: Conference?
var backgroundContextCall : Call?
@objc var backgroundContextCameraIsEnabled : Bool = false
......@@ -266,7 +269,7 @@ import AVFoundation
}
let sAddr = Address.getSwiftObject(cObject: addr!)
if (CallManager.callKitEnabled() && !CallManager.instance().nextCallIsTransfer) {
if (CallManager.callKitEnabled() && !CallManager.instance().nextCallIsTransfer && !isInConference()) {
let uuid = UUID()
let name = FastAddressBook.displayName(for: addr) ?? "unknow"
let handle = CXHandle(type: .generic, value: sAddr.asStringUriOnly())
......@@ -475,6 +478,12 @@ import AVFoundation
CallManager.instance().endCallkit = false
}
}
func onConferenceStateChanged(core: Core, conference: Conference, state: Conference.State) {
if (state == .Terminated) {
CallManager.instance().conference = nil
}
}
func onCallStateChanged(core: Core, call: Call, state cstate: Call.State, message: String) {
let callLog = call.callLog
......@@ -633,12 +642,124 @@ import AVFoundation
}
}
// post Notification kLinphoneCallUpdate
NotificationCenter.default.post(name: Notification.Name("LinphoneCallUpdate"), object: self, userInfo: [
AnyHashable("call"): NSValue.init(pointer:UnsafeRawPointer(call.getCobject)),
AnyHashable("state"): NSNumber(value: cstate.rawValue),
AnyHashable("message"): message
])
DispatchQueue.main.async {
NotificationCenter.default.post(name: Notification.Name("LinphoneCallUpdate"), object: self, userInfo: [
AnyHashable("call"): NSValue.init(pointer:UnsafeRawPointer(call.getCobject)),
AnyHashable("state"): NSNumber(value: cstate.rawValue),
AnyHashable("message"): message
])
}
}
// Conference
@objc func hostConference() -> Bool {
return conference != nil
}
func addAllToConference() {
if (conference == nil) {
guard let cp = try?lc?.createConferenceParams() else {
Log.directLog(BCTBX_LOG_ERROR, text: "Unable to create conference parameters")
return
}
if let currentCall = lc?.currentCall, let currentParams = currentCall.currentParams {
cp.videoEnabled = currentParams.videoEnabled
}
conference = try?lc?.createConferenceWithParams(params: cp)
}
lc?.calls.forEach { call in
if (call.conference == nil || call.conference?.participantCount == 1) {
try?conference?.addParticipant(call: call)
}
}
}
@objc func getConference() -> OpaquePointer? {
guard let core = lc else {
return nil
}
return (core.conference != nil) ? core.conference?.getCobject : (core.currentCall?.conference != nil) ? core.currentCall!.conference!.getCobject : nil
}
func getConference() -> Conference? {
guard let core = lc else {
return nil
}
return (core.conference != nil) ? core.conference : (core.currentCall?.conference != nil) ? core.currentCall!.conference : nil
}
@objc func isInConference() -> Bool {
return isInConferenceAsHost()||isInConferenceAsGuest()
}
@objc func isInConferenceAsGuest() -> Bool {
guard let core = lc else {
return false
}
return !isInConferenceAsHost() && core.currentCall != nil && core.currentCall?.conference != nil && (core.currentCall?.conference!.participantCount)! > 1
}
@objc func isInConferenceAsHost() -> Bool {
guard let core = lc else {
return false
}
return core.conference?.isIn == true
}
@objc func hasConferenceAsGuest() -> Bool {
guard let core = lc else {
return false
}
if (core.callsNb<=1) {
return false
}
var found = false
core.calls.forEach {
let c = $0.conference
if (c != nil && c!.participantCount > 1 && hostConference()) {
found = true
return
}
}
return found
}
@objc func getCallFor(participant : OpaquePointer) -> OpaquePointer? {
let p = Participant.getSwiftObject(cObject: participant)
guard let core = lc else {
return nil
}
var call:Call? = nil
core.calls.forEach { (callIt) in
let c = callIt.conference
c?.participantList.forEach { (p2) in
if (p2.address?.asStringUriOnly() == p.address?.asStringUriOnly()) {
call = callIt
return
}
}
}
return call?.getCobject
}
@objc func inVideoConf() -> Bool {
guard let core = lc else {
return false
}
return isInConference() && (getConference()?.currentParams?.isVideoEnabled == true || core.currentCall?.currentParams?.videoEnabled == true)
}
@objc func inAudioConf() -> Bool {
guard let core = lc else {
return false
}
return core.conference?.isIn == true && core.conference != nil && core.currentCall?.conference?.currentParams?.isVideoEnabled == false
}
}
......@@ -24,6 +24,7 @@
#import <QuartzCore/CAAnimation.h>
#import <QuartzCore/QuartzCore.h>
#import <UserNotifications/UserNotifications.h>
#import "UICallConferenceCell.h"
#import "CallView.h"
#import "CallSideMenuView.h"
......@@ -170,12 +171,25 @@ static UICompositeViewDescription *compositeDescription = nil;
selector:@selector(callUpdateEvent:)
name:kLinphoneCallUpdate
object:nil];
[NSNotificationCenter.defaultCenter addObserver:self
selector:@selector(participantListChanged:)
name:kLinphoneConfStateParticipantListChanged
object:nil];
[NSNotificationCenter.defaultCenter addObserver:self
selector:@selector(confStateChanged:)
name:kLinphoneConfStateChanged
object:nil];
[NSTimer scheduledTimerWithTimeInterval:1
target:self
selector:@selector(callDurationUpdate)
userInfo:nil
repeats:YES];
}
- (void)viewDidAppear:(BOOL)animated {
......@@ -263,7 +277,8 @@ static UICompositeViewDescription *compositeDescription = nil;
_pausedByRemoteView.hidden = NO;
[self updateInfoView:TRUE];
}
_conferenceView.hidden = !linphone_core_is_in_conference(LC);
_conferenceView.hidden = ![CallManager.instance isInConference];
[self onCurrentCallChange];
}
#pragma mark - UI modification
......@@ -312,9 +327,8 @@ static void hideSpinner(LinphoneCall *call, void *user_data) {
_optionsButton.enabled = (!call || !linphone_core_sound_resources_locked(LC));
_optionsTransferButton.enabled = call && !linphone_core_sound_resources_locked(LC);
// enable conference button if 2 calls are presents and at least one is not in the conference
int confSize = linphone_core_get_conference_size(LC) - (linphone_core_is_in_conference(LC) ? 1 : 0);
_optionsConferenceButton.enabled =
((linphone_core_get_calls_nb(LC) > 1) && (linphone_core_get_calls_nb(LC) != confSize));
int confSize = linphone_core_get_conference_size(LC) - ([CallManager.instance isInConference] ? 1 : 0);
_optionsConferenceButton.enabled = (linphone_core_get_calls_nb(LC) > 1) && (linphone_core_get_calls_nb(LC) != confSize) && !CallManager.instance.hasConferenceAsGuest;
// Disable transfert in conference
if (linphone_core_get_current_call(LC) == NULL) {
......@@ -359,8 +373,18 @@ static void hideSpinner(LinphoneCall *call, void *user_data) {
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:0.35];
_pausedCallsTable.tableView.alpha = _videoCameraSwitch.alpha = _callPauseButton.alpha = _routesView.alpha =
_optionsView.alpha = _numpadView.alpha = _bottomBar.alpha = (hidden ? 0 : 1);
_optionsView.alpha = _numpadView.alpha = _bottomBar.alpha = _conferenceView.alpha = (hidden ? 0 : 1);
_infoView.alpha = (hidden ? 0 : .8f);
if ([CallManager.instance inVideoConf]) {
_videoCameraSwitch.frame = CGRectMake(_videoCameraSwitch.frame.origin.x, _bottomBar.frame.origin.y - 75, _videoCameraSwitch.frame.size.width,_videoCameraSwitch.frame.size.height);
}
if (CallManager.instance.isInConference) {
_callPauseButton.hidden = true;
_conferenceView.frame = CGRectMake(_conferenceView.frame.origin.x,_conferenceView.frame.origin.y,_conferenceView.frame.size.width,_conferenceCallsTable.tableView.frame.origin.y+[_conferenceCallsTable.tableView numberOfRowsInSection:0]*CONFERENCE_CELL_HEIGHT+10);
}
[UIView commitAnimations];
......@@ -426,12 +450,25 @@ static void hideSpinner(LinphoneCall *call, void *user_data) {
[_videoWaitingForFirstImage setHidden:NO];
[_videoWaitingForFirstImage startAnimating];
LinphoneCall *call = linphone_core_get_current_call(LC);
// linphone_call_params_get_used_video_codec return 0 if no video stream enabled
if (call != NULL && linphone_call_params_get_used_video_codec(linphone_call_get_current_params(call))) {
linphone_call_set_next_video_frame_decoded_callback(call, hideSpinner, (__bridge void *)(self));
if ([CallManager.instance inVideoConf])
[self hideSpinnerIndicator:nil];
else {
LinphoneCall *call = linphone_core_get_current_call(LC);
// linphone_call_params_get_used_video_codec return 0 if no video stream enabled
if (call != NULL && linphone_call_params_get_used_video_codec(linphone_call_get_current_params(call))) {
linphone_call_set_next_video_frame_decoded_callback(call, hideSpinner, (__bridge void *)(self));
}
}
}
if ([CallManager.instance isInConference]) {
[_conferenceView removeFromSuperview];
[_callView addSubview:_conferenceView];
} else {
[_conferenceView removeFromSuperview];
[self.view addSubview:_conferenceView];
[self.view sendSubviewToBack:_conferenceView];
}
}
- (void)displayVideoCall:(BOOL)animated {
......@@ -454,15 +491,17 @@ static void hideSpinner(LinphoneCall *call, void *user_data) {
- (void)onCurrentCallChange {
LinphoneCall *call = linphone_core_get_current_call(LC);
_noActiveCallView.hidden = (call || linphone_core_is_in_conference(LC));
_callView.hidden = !call;
_conferenceView.hidden = !linphone_core_is_in_conference(LC);
_callPauseButton.hidden = !call && !linphone_core_is_in_conference(LC);
_noActiveCallView.hidden = (call || CallManager.instance.isInConference);
_callView.hidden = !call && !CallManager.instance.isInConference;
_conferenceView.hidden = !CallManager.instance.isInConference;
_conferenceView.hidden = !CallManager.instance.isInConference;
_callPauseButton.hidden = !call;
[_callPauseButton setType:UIPauseButtonType_CurrentCall call:call];
[_conferencePauseButton setType:UIPauseButtonType_Conference call:call];
if (!_callView.hidden) {
if (call) {
const LinphoneAddress *addr = linphone_call_get_remote_address(call);
[ContactDisplay setDisplayNameLabel:_nameLabel forAddress:addr];
char *uri = linphone_address_as_string_uri_only(addr);
......@@ -551,19 +590,13 @@ static void hideSpinner(LinphoneCall *call, void *user_data) {
// Update tables
[_pausedCallsTable update];
[_conferenceCallsTable update];
static LinphoneCall *currentCall = NULL;
if (!currentCall || linphone_core_get_current_call(LC) != currentCall) {
currentCall = linphone_core_get_current_call(LC);
[self onCurrentCallChange];
}
// Fake call update
if (call == NULL) {
return;
}
BOOL shouldDisableVideo = !currentCall || !linphone_call_params_video_enabled(linphone_call_get_current_params(currentCall));
[self onCurrentCallChange];
LinphoneCall *currentCall = linphone_core_get_current_call(LC);
BOOL shouldDisableVideo = currentCall ? !linphone_call_params_video_enabled(linphone_call_get_current_params(currentCall)): ![CallManager.instance inVideoConf];
if (videoHidden != shouldDisableVideo) {
if (!shouldDisableVideo) {
[self displayVideoCall:animated];
......@@ -571,12 +604,11 @@ static void hideSpinner(LinphoneCall *call, void *user_data) {
[self displayAudioCall:animated];
}
}
if (!shouldDisableVideo && !linphone_core_is_in_conference(LC) && // camera is diabled duiring conference, it must be activated after leaving conference.
[UIApplication sharedApplication].applicationState == UIApplicationStateActive) { // Camera should not be enabled when in background)
linphone_call_enable_camera(call, TRUE);
}
[self updateCallView];
// Fake call update
if (call == NULL) {
return;
}
if (state != LinphoneCallPausedByRemote) {
_pausedByRemoteView.hidden = YES;
......@@ -623,7 +655,7 @@ static void hideSpinner(LinphoneCall *call, void *user_data) {
floor(NSFoundationVersionNumber) > NSFoundationVersionNumber_iOS_9_x_Max))) {
linphone_core_defer_call_update(LC, call);
[self displayAskToEnableVideoCall:call];
} else if (linphone_call_params_video_enabled(current) && !linphone_call_params_video_enabled(remote)) {
} else if (linphone_call_params_video_enabled(current) && !linphone_call_params_video_enabled(remote) && ![CallManager.instance inVideoConf]) {
[self displayAudioCall:animated];
}
break;
......@@ -633,10 +665,12 @@ static void hideSpinner(LinphoneCall *call, void *user_data) {
[self displayAudioCall:animated];
break;
case LinphoneCallPausedByRemote:
[self displayAudioCall:animated];
if (call == linphone_core_get_current_call(LC)) {
_pausedByRemoteView.hidden = NO;
[self updateInfoView:TRUE];
if (![CallManager.instance inVideoConf]) {
[self displayAudioCall:animated];
if (call == linphone_core_get_current_call(LC)) {
_pausedByRemoteView.hidden = NO;
[self updateInfoView:TRUE];
}
}
break;
case LinphoneCallEnd:
......@@ -651,7 +685,13 @@ static void hideSpinner(LinphoneCall *call, void *user_data) {
- (void)displayAskToEnableVideoCall:(LinphoneCall *)call {
if (linphone_call_params_get_local_conference_mode(linphone_call_get_current_params(call))) {
return;
} else if (CallManager.instance.inVideoConf) {
LinphoneCallParams *params = linphone_core_create_call_params(LC, call);
linphone_call_params_enable_video(params, TRUE);
linphone_call_accept_update(call, params);
return;
}
if (linphone_core_get_video_policy(LC)->automatically_accept &&
!([UIApplication sharedApplication].applicationState != UIApplicationStateActive))
return;
......@@ -825,6 +865,9 @@ static void hideSpinner(LinphoneCall *call, void *user_data) {
}
- (IBAction)onOptionsClick:(id)sender {
int confSize = linphone_core_get_conference_size(LC) - (CallManager.instance.isInConference ? 1 : 0);
_optionsConferenceButton.enabled = (linphone_core_get_calls_nb(LC) > 1) && (linphone_core_get_calls_nb(LC) != confSize) && !CallManager.instance.hasConferenceAsGuest;
if ([_optionsView isHidden]) {
[self hideOptions:FALSE animated:ANIMATED];
} else {
......@@ -909,4 +952,56 @@ static void hideSpinner(LinphoneCall *call, void *user_data) {
[_chatNotificationView stopAnimating:appear];
}
}
#pragma mark - Conference
- (void)participantListChanged:(NSNotification *)notif {
[self confStateChanged:nil];
[_conferenceCallsTable update];
_conferenceView.frame = CGRectMake(_conferenceView.frame.origin.x,_conferenceView.frame.origin.y,_conferenceView.frame.size.width,_conferenceCallsTable.tableView.frame.origin.y+[_conferenceCallsTable.tableView numberOfRowsInSection:0]*CONFERENCE_CELL_HEIGHT+10);
[self onCurrentCallChange];
_conferenceView.hidden = !CallManager.instance.isInConference;
}
- (void)confStateChanged:(NSNotification *)notif {
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
if ([CallManager.instance inVideoConf]) {
[self displayVideoCall:true];
} else if (CallManager.instance.isInConference) {
[self displayAudioConference];
} else {
[self displayAudioCall:true];
_callPauseButton.hidden = NO;
_nameLabel.hidden = NO;
_durationLabel.hidden = NO;
_avatarImage.hidden = NO;
}
[_conferenceCallsTable update];
_conferenceView.frame = CGRectMake(_conferenceView.frame.origin.x,_conferenceView.frame.origin.y,_conferenceView.frame.size.width,_conferenceCallsTable.tableView.frame.origin.y+[_conferenceCallsTable.tableView numberOfRowsInSection:0]*CONFERENCE_CELL_HEIGHT+10);
});
}
-(void) displayAudioConference {
_callPauseButton.hidden = true;
_nameLabel.hidden = true;
_conferenceView.frame = CGRectMake(_conferenceView.frame.origin.x,_conferenceView.frame.origin.y,_conferenceView.frame.size.width,_conferenceCallsTable.tableView.frame.origin.y+[_conferenceCallsTable.tableView numberOfRowsInSection:0]*CONFERENCE_CELL_HEIGHT+10);
_durationLabel.hidden = true;
_avatarImage.hidden = true;
[_conferenceView removeFromSuperview];
[_callView addSubview:_conferenceView];
if ([CallManager.instance isInConference]) {
[_conferenceView removeFromSuperview];
[_callView addSubview:_conferenceView];
_conferenceView.hidden = NO;
} else {
[_conferenceView removeFromSuperview];
[self.view addSubview:_conferenceView];
[self.view sendSubviewToBack:_conferenceView];
}
}
@end
......@@ -60,6 +60,9 @@ extern NSString *const kLinphoneFileTransferRecvUpdate;
extern NSString *const kLinphoneQRCodeFound;
extern NSString *const kLinphoneChatCreateViewChange;
extern NSString *const kLinphoneEphemeralMessageDeletedInRoom;
extern NSString *const kLinphoneConfStateParticipantListChanged;
extern NSString *const kLinphoneConfStateChanged;
extern NSString *const kLinphoneMsgNotificationAppGroupId;
......@@ -188,6 +191,7 @@ typedef struct _LinphoneManagerSounds {
- (BOOL)isCTCallCenterExist;
- (void) checkLocalNetworkPermission;
@property (readonly) BOOL isTesting;
@property(readonly, strong) FastAddressBook *fastAddressBook;
@property (readonly) NetworkType network;
......
......@@ -75,7 +75,8 @@ NSString *const kLinphoneFileTransferRecvUpdate = @"LinphoneFileTransferRecvUpda
NSString *const kLinphoneQRCodeFound = @"LinphoneQRCodeFound";
NSString *const kLinphoneChatCreateViewChange = @"LinphoneChatCreateViewChange";
NSString *const kLinphoneEphemeralMessageDeletedInRoom = @"LinphoneEphemeralMessageDeletedInRoom";
NSString *const kLinphoneConfStateChanged = @"kLinphoneConfStateChanged";
NSString *const kLinphoneConfStateParticipantListChanged = @"kLinphoneConfStateParticipantListChanged";
NSString *const kLinphoneMsgNotificationAppGroupId = @"group.org.linphone.phone.msgNotification";
......@@ -1374,6 +1375,8 @@ void popup_link_account_cb(LinphoneAccountCreator *creator, LinphoneAccountCreat
linphone_core_cbs_set_call_id_updated(cbs, linphone_iphone_call_id_updated);
linphone_core_cbs_set_user_data(cbs, (__bridge void *)(self));
linphone_core_cbs_set_chat_room_ephemeral_message_deleted(cbs, linphone_iphone_ephemeral_message_deleted);
linphone_core_cbs_set_conference_state_changed(cbs, linphone_iphone_conference_state_changed);
theLinphoneCore = linphone_factory_create_shared_core_with_config(factory, _configDb, NULL, [kLinphoneMsgNotificationAppGroupId UTF8String], true);
......@@ -2252,4 +2255,36 @@ static int comp_call_state_paused(const LinphoneCall *call, const void *param) {
_avatar = ret;
}
#pragma mark - Conference
void conference_participant_changed(LinphoneConference *conference, const LinphoneParticipant *participant) {
[NSNotificationCenter.defaultCenter postNotificationName:kLinphoneConfStateParticipantListChanged object:nil];
}
void conference_device_changed(LinphoneConference *conference, const LinphoneParticipantDevice *participant) {
[NSNotificationCenter.defaultCenter postNotificationName:kLinphoneConfStateParticipantListChanged object:nil];
}
void linphone_iphone_conference_state_changed(LinphoneCore *lc, LinphoneConference *conf,LinphoneConferenceState state) {
if (state == LinphoneConferenceStateCreated) {
LinphoneConferenceCbs * cbs = linphone_conference_get_current_callbacks(conf);
if (!cbs) {
cbs = linphone_factory_create_conference_cbs(linphone_factory_get());
}
linphone_conference_cbs_set_participant_added(cbs, conference_participant_changed);
linphone_conference_cbs_set_participant_device_added(cbs, conference_device_changed);
linphone_conference_cbs_set_participant_device_removed(cbs, conference_device_changed);
linphone_conference_cbs_set_participant_removed(cbs, conference_participant_changed);
linphone_conference_add_callbacks(conf, cbs);
}
NSMutableDictionary *dict = [NSMutableDictionary dictionary];
[dict setObject:[NSNumber numberWithInt:state] forKey:@"state"];
[NSNotificationCenter.defaultCenter postNotificationName:kLinphoneConfStateChanged object:nil userInfo:dict];
}
@end
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="15702" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" colorMatched="YES">
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="18122" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" colorMatched="YES">
<device id="retina6_1" orientation="portrait" appearance="light"/>
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="15704"/>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="18093"/>
<capability name="System colors in document resources" minToolsVersion="11.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
......@@ -9,6 +12,7 @@
<connections>
<outlet property="avatarImage" destination="PjC-yS-i03" id="srY-K7-Ajk"/>
<outlet property="durationLabel" destination="Jgc-Z9-ItD" id="EGs-SW-dRc"/>
<outlet property="kickButton" destination="nOf-6W-BeC" id="NR8-NM-f5V"/>
<outlet property="nameLabel" destination="63x-tV-T6U" id="rBP-rS-gbi"/>
</connections>
</placeholder>
......@@ -55,7 +59,7 @@
</connections>
</button>
</subviews>
<color key="backgroundColor" systemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
<color key="backgroundColor" systemColor="systemBackgroundColor"/>
<nil key="simulatedStatusBarMetrics"/>
<freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
<point key="canvasLocation" x="490.57971014492756" y="28.794642857142854"/>
......@@ -65,5 +69,8 @@
<image name="avatar.png" width="414.39999389648438" height="414.39999389648438"/>
<image name="conference_exit_default.png" width="55.200000762939453" height="46.400001525878906"/>
<image name="conference_exit_over.png" width="55.200000762939453" height="46.400001525878906"/>