Compare commits

...
Sign in to create a new pull request.

224 commits

Author SHA1 Message Date
QuentinArguillere
29bcbf64c0 Remove mistaken line from changeling 2022-12-06 17:43:49 +01:00
QuentinArguillere
b99327dbe5 Update Changelog and Podfile for 5.0.0 release 2022-12-06 14:28:22 +01:00
Benoit Martins
952b27c94f Minor changes of VFS_enabled_mode 2022-12-06 12:04:20 +01:00
Benoit Martins
5407a18b57 Save VFS and Auto_download_mode changes 2022-12-06 12:04:20 +01:00
Benoit Martins
9e52922fff In chat setting, when user enable VFS or set auto_download_mode on Never, disable auto_write_to_gallery_mode item.
When enable auto_write_to_gallery_mode, app disable VFS and auto_download_mode items.
2022-12-06 12:04:20 +01:00
Christophe Deschamps
4ade9f2c60 Automatically initiate video call (if remote party is in auti accept mode) 2022-12-06 11:40:54 +01:00
QuentinArguillere
abf598b654 Use linphone_core_search_chat_room rather than linphone_core_get_chat_room, in order to fix chatrooms being empty when using multiple accounts 2022-12-05 16:40:31 +01:00
Christophe Deschamps
0a05baea1e Fix pause screen not showing in some situation in Paused conferences 2022-12-05 10:33:34 +01:00
QuentinArguillere
4cd9da44de Add security to participant_imdn_status callback to avoid potential crash 2022-12-05 10:08:00 +01:00
Christophe Deschamps
a341bb6aec - Allow date picker to select today's date in the first place
- supports pre-ios14 device for date/time picking
2022-12-05 09:32:17 +01:00
Christophe Deschamps
ff5bafb7c0 When adding calls to a conference - enter the conference again if it was paused 2022-12-04 18:38:59 +01:00
Christophe Deschamps
9ba95b024c Disable call merge into conference if not admin of conference 2022-12-04 18:20:36 +01:00
Christophe Deschamps
05b905a3f1 Hide audio route selection view when clicked outsize 2022-12-02 17:09:11 +01:00
Christophe Deschamps
e8c2e92fc9 Added call back when bluetooth device is added or removed 2022-12-02 16:50:23 +01:00
Christophe Deschamps
82289f4523 Fix user interaction on send via chat switch on conference scheduling 2022-12-02 16:32:22 +01:00
Christophe Deschamps
6365c66d55 Fix ConferenceParticipantDeviceData not updated in certain circonstances 2022-12-02 16:14:17 +01:00
QuentinArguillere
61e645c238 Add post_quantum_secure shield icon to the project resources (file was actually already there but not included in the project) 2022-12-02 16:08:15 +01:00
Christophe Deschamps
9d5836b991 Hide video stats in conference call with video sending disabled 2022-12-02 15:28:54 +01:00
Christophe Deschamps
b78b4a1e6b Fix navigation when hanging up conference after editing participants list 2022-12-02 11:12:57 +01:00
Christophe Deschamps
6850282208 Conference waiting room : Dismiss layout picked if view is clicked elsewhere 2022-12-02 11:03:50 +01:00
Christophe Deschamps
9803678d6a Avoid unecessary setting of outgoing/incoming mutable value 2022-12-02 10:30:12 +01:00
QuentinArguillere
f2b36ed7f8 When Poping views, make sure we add the targeted one if we couldn’t already find it in the stack 2022-12-02 10:16:33 +01:00
QuentinArguillere
d39fb0661b Update version to 5.0.0 for incoming release 2022-12-01 17:44:46 +01:00
QuentinArguillere
b378682d0c Enable/disable phone’s idle timer when entering/leaving a conference 2022-12-01 17:39:53 +01:00
Benoit Martins
7da10d500f Change "VIEW(ChatConversationView).sharingMedia = nil" to "_sharingMedia = nil" in CharConversationView 2022-12-01 16:32:49 +01:00
Benoit Martins
4b5eaa3bc5 Fix image sharing from gallery, and fix view navigation 2022-12-01 16:32:49 +01:00
Benoit Martins
78c21a5c2b Change job iOS Tag to run on : macos-xcode13 2022-12-01 16:18:27 +01:00
Benoit Martins
fecbb85a81 Fix imdmIcon and ephemeral (icon/timer) in conversation when user want to transfer or reply messages 2022-12-01 16:02:02 +01:00
Benoit Martins
8582d93555 Fix for iPhone SE, the Security Badge of UIConfirmationDialog not displaying 2022-11-30 09:27:44 +01:00
Christophe Deschamps
a74d042a6a Restart video previow upon rotation to avoid crash 2022-11-28 16:42:40 +00:00
Christophe Deschamps
ad9163320c Landcape handling of video self view in 1-1 calls 2022-11-28 16:42:40 +00:00
Benoit Martins
5ab6a7ce1b Remove DTMF sound in Dialer View 2022-11-28 17:06:20 +01:00
QuentinArguillere
7ccdcbebcf Ensure that the result of linphone_event_log_get_chat_message(_event) is not nil before trying to use it 2022-11-28 17:00:29 +01:00
Benoit Martins
0fe94354d7 Fix ChatConversationView having no message after playing video received in chat 2022-11-28 16:52:59 +01:00
QuentinArguillere
3dc7d4fb15 Make sure we have callbacks before proceeding with chatroom deletions (could cause a crash when deleting chatrooms quickly after login and/or changing views) 2022-11-28 14:19:31 +01:00
Benoit Martins
a98aa42ca4 Disable features (Group chat and Security Chat) without Linphone account 2022-11-28 12:01:49 +01:00
Benoit Martins
84a817783d Removed IASKPSTitleValueSpecifierViewCell to align each cell of SettingView 2022-11-28 11:45:57 +01:00
Benoit Martins
6e846f092c Hide record button in conference views 2022-11-23 15:33:08 +01:00
Christophe Deschamps
fd9afd66fa Fix crash on marking chatroom as read 2022-11-23 13:34:10 +01:00
Christophe Deschamps
e5505a836c Merge branch 'fix/various_refacto_sync_android' into fix/various_refacto_sync_android2 2022-11-23 10:43:00 +01:00
Christophe Deschamps
781a9b07d1 Fix video freeze when alone in conference A/S mode 2022-11-23 09:21:21 +01:00
Christophe Deschamps
6a4ef41ab5 Various fixes in view model update propagation 2022-11-23 08:59:42 +01:00
Christophe Deschamps
c09519bcc1 Fixed dual spinner joining conference + set color to make it visible 2022-11-22 23:51:40 +01:00
Christophe Deschamps
9ab5a0f106 Enable full screen joining video conference with video 2022-11-22 23:51:40 +01:00
Christophe Deschamps
eff9964b12 Refactored to ConferenceWaitingRoomView 2022-11-22 23:51:40 +01:00
Christophe Deschamps
0202c7aa6b UTF8 handling in conference names in history 2022-11-22 23:51:40 +01:00
Christophe Deschamps
4cb99a62a0 Added local group call subject when merging calls 2022-11-22 23:51:40 +01:00
Christophe Deschamps
fc3f48363f Avoid getting back to waiting room after conf ends 2022-11-22 23:51:40 +01:00
Christophe Deschamps
3800aafd1f Full screen toggle 2022-11-22 23:51:40 +01:00
Christophe Deschamps
3a3c04bb23 Fix resuming call putting other calls on hold 2022-11-22 23:51:40 +01:00
Christophe Deschamps
8607469f68 Refactorisation of call views + various fixes 2022-11-22 23:51:15 +01:00
Christophe Deschamps
987ea21e05 Multi call various fixes 2022-11-22 23:50:58 +01:00
QuentinArguillere
b6ff317551 Check that contact address has a valid username when trying to match phone numbers 2022-11-22 17:25:23 +01:00
QuentinArguillere
9977644872 Add small 1px margin to make sure we scroll when Composing message appear while we just opened a chatroom 2022-11-22 17:24:44 +01:00
QuentinArguillere
d447158a25 Set _chatRoom to NULL when ChatConversationView disappears to avoir keeping invalid references 2022-11-22 17:24:10 +01:00
Benoit Martins
c3746801ac Remove Incoming condition in IncomingOutgoingCommonView for reset outgoing call timer 2022-11-22 15:50:35 +01:00
Benoit Martins
dbd56b5c38 Merge remote-tracking branch 'refs/remotes/origin/release/4.7' 2022-11-22 15:41:56 +01:00
Simon Morlat
c49013aa2b Fix an issue where audio does not work when a VoIP Call is resumed after a GSM call has been terminated by the remote party. 2022-11-22 14:34:20 +01:00
Benoit Martins
fc9c9b9508 Set fullscreen when start or join a conference 2022-11-22 14:28:22 +01:00
Benoit Martins
6cb4ea55f6 Fix scrollBadge and markAsRead in chat conversation 2022-11-22 11:12:27 +01:00
QuentinArguillere
54fb56d93c Fix crash when entering a chat conversation with audio messages that were not properly downloaded 2022-11-22 11:01:28 +01:00
Benoit Martins
ac49ee5d56 Merge remote-tracking branch 'refs/remotes/origin/release/4.7' 2022-11-22 09:57:43 +01:00
QuentinArguillere
03af1afac1 When calling setComposingVisible, check that the “is composing” message is visible as well as blocking text before scrolling to the bottom of the chat conversation view 2022-11-21 17:26:38 +01:00
Christophe Deschamps
233865747e Reworked IMDN cell calculation to make it independent from EventList 2022-11-21 15:56:30 +01:00
Christophe Deschamps
1291e7dee4 Protect _chatRoom dependent sub function calls 2022-11-21 14:54:19 +01:00
Benoit Martins
61ec8a37dd Merge remote-tracking branch 'refs/remotes/origin/release/4.7' 2022-11-21 13:20:55 +01:00
Benoit Martins
bd42b8855c Add hide_link_phone_number in RC setting for hide link_account view 2022-11-21 13:16:05 +01:00
Benoit Martins
7a78a14bbf Add hide_link_phone_number in RC setting for hide link_account view 2022-11-21 12:07:18 +01:00
Benoit Martins
236a8ee52f Fix recording list. Change of cell background color 2022-11-21 11:43:31 +01:00
Christophe Deschamps
62f7d8a620 0010414: Ios Build 2.1 (3) Probléme d'affichage de la ligne de notification d'écriture du correspondant. 2022-11-21 11:25:44 +01:00
Christophe Deschamps
8a47583130 set composig visible when _chatRoom is resolved 2022-11-21 11:23:01 +01:00
Christophe Deschamps
59098c6db0 Texts update 2022-11-18 09:29:23 +01:00
Christophe Deschamps
3043fc5c7a Do not force speaker on video activation/deactivation in conference 2022-11-18 09:27:26 +01:00
Christophe Deschamps
3e6bffd723 Added further missing translations 2022-11-18 09:00:50 +01:00
Christophe Deschamps
0cd72844b6 Missing translation 2022-11-18 08:46:19 +01:00
Christophe Deschamps
d5a6a233af Fix freeze in call by swiping screen right 2022-11-17 18:20:30 +01:00
Christophe Deschamps
65573ba1be Switch from audio only to active speaker using toggle video button (instead of disabling it) 2022-11-17 17:59:14 +01:00
Benoit Martins
669851065a Fix scrollToBottom for ChatConversation TableView 2022-11-17 17:24:22 +01:00
Benoit Martins
6fa24fff6d ScrollToBottom the ChatConversation TableView when composeLabel is visible 2022-11-17 17:24:22 +01:00
Benoit Martins
670dabaf77 Update popup ZRTP 2022-11-17 17:15:59 +01:00
Christophe Deschamps
93eecbc304 Appearance (dark/light) handling 2022-11-17 16:08:34 +00:00
Christophe Deschamps
e02580bade Removed redundant post of kLinphoneMessageReceived notification when chat conversation is opened 2022-11-17 13:44:27 +01:00
Christophe Deschamps
581d2dbf50 Removed ChatConversationTableView entire reloading upon entry addition 2022-11-17 13:44:27 +01:00
Christophe Deschamps
f7ad67390a Fix PQ Call Stats 2022-11-17 13:20:39 +01:00
Benoit Martins
c72d8913b8 Add a new button in the account link popup to never ask again user to link his account 2022-11-17 10:43:30 +01:00
Christophe Deschamps
490f9e96db Hide remote video view in 1-1 call when it is paused by remote 2022-11-15 18:11:02 +01:00
QuentinArguillere
5fb42ce09f When configuring a linphone account, set the core media encryption to SRTP by default 2022-11-15 16:30:07 +01:00
Christophe Deschamps
736059f699 Fix compilation 2022-11-15 07:35:11 +01:00
Christophe Deschamps
b63779b65e Enable camera in background context when app goes in background 2022-11-15 07:30:37 +01:00
Christophe Deschamps
0fc1f024d3 Enable camera on conference calls when app comes to foreground 2022-11-14 21:19:11 +01:00
QuentinArguillere
8b5668e8ce Prevent from from going to sleep when video is active in call 2022-11-14 16:46:55 +01:00
Christophe Deschamps
8909ae7279 Fix meeting icon in size menu 2022-11-10 17:56:49 +01:00
Christophe Deschamps
be5e303b09 Handle pause in A/S view when 1 or 2 participants 2022-11-10 17:30:27 +01:00
Christophe Deschamps
82704471ac Fix layout change when reaching more than 6 participants 2022-11-10 08:31:56 +01:00
Christophe Deschamps
988840d9a9 Fix compilation 2022-11-09 10:24:45 +01:00
Christophe Deschamps
a120a067c9 Revert "Fix crash upon device rotation when playing back voice memo"
This reverts commit d326b7f56e.
2022-11-08 18:22:56 +01:00
Christophe Deschamps
555428d62d Fix layout of chat conversation upon rotation 2022-11-08 18:09:40 +01:00
Christophe Deschamps
d326b7f56e Fix crash upon device rotation when playing back voice memo 2022-11-08 18:07:53 +01:00
Christophe Deschamps
59c90fe65a Handle meeting invite notification creation/update/cancellation 2022-11-08 15:25:12 +01:00
QuentinArguillere
3edcfb4222 Fix contact list in chat/conference creation view in order to include contacts from ldap/remote provisioning that have a sip uri 2022-11-08 11:55:54 +01:00
QuentinArguillere
77c4fb1747 Fix potential background crash when link popup was supposed to appear 2022-11-04 19:43:45 +01:00
QuentinArguillere
50f656d1ee Rework of non-native contacts: fix bug that would not display sip adresses in detail view, and fix bug that would lose the “createdFromLdapOrProvisioning” tag of non-native contacts after first launch. 2022-11-04 15:22:55 +01:00
Christophe Deschamps
06cbe9fa1d Fix avatars light/dark mode 2022-11-03 17:04:28 +01:00
QuentinArguillere
f3bc7add35 Check for nil chatroom in ChatConversationView.m::CellForRowAtIndexPath to avoid recurring crashes in background. Temporary fix. 2022-11-03 15:29:28 +01:00
Christophe Deschamps
9fcfe2b8de Cosmetic on chat contextual menu 2022-11-03 14:50:31 +01:00
Christophe Deschamps
d04f961559 Handle ICSs in chat list last message 2022-11-03 14:14:08 +01:00
QuentinArguillere
1e1c0fd3d3 Fix magic search to also display contacts that were created through remote provisioning, not just LDAP or native 2022-11-03 13:51:13 +01:00
Christophe Deschamps
7ddfff9c32 Unified avatar with VoIP 2022-11-03 13:35:38 +01:00
Christophe Deschamps
ecdb22dc5d (Ugly) work around to display differentiated avatars for numbers and usernames, until rest of the app is re-written in swift 2022-11-03 12:56:44 +01:00
Christophe Deschamps
d340db5680 Fix removal of UIChatCreateCollectionViewCell.xib 2022-11-03 12:05:24 +01:00
Christophe Deschamps
960046b87e Coscmetic on group chat info view 2022-11-03 10:13:14 +01:00
Christophe Deschamps
79a2235368 Cosmetic on conference list display 2022-11-03 10:09:47 +01:00
Christophe Deschamps
df53cc73e9 Avatar icon colour fixes 2022-11-03 08:39:02 +01:00
Christophe Deschamps
44cf121007 Cosmetic fixes on conference summary view 2022-11-03 08:37:37 +01:00
Christophe Deschamps
14ddbaa58a New message indicator icon in orange background 2022-11-03 00:28:44 +01:00
Christophe Deschamps
0c687d1dce Removed separator in popup menus (chat conversation, and chat bubble) 2022-11-03 00:09:41 +01:00
Christophe Deschamps
f00daf1997 Do not hide keyboard upon contact selection in group chat/call creation 2022-11-02 23:58:02 +01:00
Christophe Deschamps
c80ad228ef Reworked UICollectionView for displaying selected contacts in group chat and group calls 2022-11-02 23:47:49 +01:00
Christophe Deschamps
70b2671f19 No default date/time when scheduling a conference 2022-11-02 21:51:46 +01:00
Christophe Deschamps
3c8af14495 Conference subject hint changing if groupcall or meeting 2022-11-02 21:51:26 +01:00
Christophe Deschamps
51f23e91bb Implement onActiveSpeakerParticipantDevice api on conference 2022-11-01 22:29:06 +01:00
Christophe Deschamps
cd7951e590 Fix chat room creation view split 2022-10-21 17:21:20 +02:00
Christophe Deschamps
a6638d3c9b Update new class header 2022-10-21 10:57:31 +02:00
Christophe Deschamps
0e947803ce Set unique file name per message to avoid local display conflicts 2022-10-21 10:53:05 +02:00
Christophe Deschamps
666b2c7f48 Fix conference creation button state in conference list 2022-10-19 10:31:30 +02:00
Christophe Deschamps
ac32d80b51 Voipdialog anchor on iPads 2022-10-19 10:27:14 +02:00
Christophe Deschamps
e2dc5d4ad3 Show tabbar in chat on iPads (no back button) 2022-10-19 10:23:53 +02:00
Christophe Deschamps
eda3fa114a Merge branch 'fix/conf_fixes' into release/4.7 2022-10-19 09:59:24 +02:00
Christophe Deschamps
fd3afda60c Bulk bug fixes conference 2022-10-19 09:57:52 +02:00
Christophe Deschamps
670ad21770 Conference creation on iPad 2022-10-19 09:53:58 +02:00
Christophe Deschamps
0353180bd0 Conference creation on iPad 2022-10-19 09:53:01 +02:00
Christophe Deschamps
b402f08006 Send cancel conference iCS 2022-10-19 08:55:38 +02:00
QuentinArguillere
b495e4b8bb Disable basic chat button in contact history when force_lime_chat_rooms=1 2022-10-17 13:25:35 +02:00
Christophe Deschamps
a0a1e1a417 Bulk bug fixes conference 2022-10-14 18:31:23 +02:00
QuentinArguillere
821f2e30b7 Rename : “only_secure_chatrooms”=>”force_lime_chat_rooms” and “fetch_native_contacts”=>”enable_native_address_book” to match android 2022-10-14 10:13:48 +02:00
Christophe Deschamps
d0f83d6d1d Fix freeze tapping settings in waiting room 2022-10-12 17:30:20 +02:00
Christophe Deschamps
f705e14664 Removed double tap listener on audio click / landscape 2022-10-12 07:27:14 +02:00
Christophe Deschamps
35c772db3a Meeting edition fixes 2022-10-11 17:10:55 +02:00
QuentinArguillere
a57da3d499 Add [rtp]accept_any_encryption=1 and [video]max_conference_size=vga to linphonerc-factory 2022-10-11 16:41:18 +02:00
QuentinArguillere
b9ef7a83b5 Go to chatrooms after receiving “on_conference_joined” callback rather than when chatroom is created 2022-10-07 16:03:26 +02:00
Christophe Deschamps
559a9d8bc3 Handle video/no video updates from Active Speaker 2022-10-03 14:56:21 +02:00
Christophe Deschamps
f8d3a196f5 Fix display of conferences in history on ipads 2022-09-29 13:41:51 +02:00
Christophe Deschamps
298778e20d Fix AS layout not updating when joining a conf with more than 2 participants already 2022-09-29 13:23:36 +02:00
QuentinArguillere
b4103d2f4f Add “only_secure_chatrooms” parameter to rc. When set to “1”, only encrypted chatrooms can be created 2022-09-27 17:21:16 +02:00
QuentinArguillere
4c75c4222d Add “fetch_native_contacts” parameters to the rc-factory. If set to 0, we won’t ask for permission to use the phone native contacts 2022-09-26 10:46:58 +02:00
Christophe Deschamps
d8163fb55b Option to schedule meeting from group chat 2022-09-23 00:45:02 +02:00
Christophe Deschamps
68597369ed Send cancel information when deleting a meeting self-organized 2022-09-23 00:40:20 +02:00
Christophe Deschamps
388e3482a9 Multiple deletion of meetings (function activated by long click on meeting cell) 2022-09-22 23:58:36 +02:00
Christophe Deschamps
811a859430 Swipe to delete on conferences view 2022-09-22 23:58:25 +02:00
Christophe Deschamps
96543cdaca Fix (for good) background color for Me participant + repositionned the Me sticker bottom right in AS Landscape iso top right 2022-09-19 16:05:02 +02:00
Christophe Deschamps
23633d1ea4 Send conference invitation via secure chat when possible 2022-09-19 15:43:27 +02:00
Christophe Deschamps
cf78ae8ff9 Display a message when attempting to launch cancelled conference from history 2022-09-19 15:18:15 +02:00
Christophe Deschamps
2b252a04ff Added handling of meeting cancellation and edition 2022-09-19 11:37:55 +02:00
Christophe Deschamps
d54c9ef5a9 Set special color for local participants in conference 2022-09-19 11:37:42 +02:00
Christophe Deschamps
aaea844c4c Added remote mute indicator in AS Conference view with 2 participants 2022-09-19 11:37:36 +02:00
Christophe Deschamps
507060fb31 Video conference : use video preview when alone and in active speaker view 2022-09-08 17:38:36 +02:00
Christophe Deschamps
863731f775 Wrap display name of participants in conference grid mode 2022-09-08 13:16:10 +02:00
Christophe Deschamps
6a8c5a4440 Tidy up Avatar 2022-09-08 13:16:03 +02:00
QuentinArguillere
360c88b764 Change uri of video conference factory to “sip:videoconference-factory@sip.linphone.org” 2022-09-01 18:01:31 +02:00
QuentinArguillere
a4bfa37454 Remove line committed by mistake 2022-08-30 15:32:13 +02:00
QuentinArguillere
68bece526b Put set LDAP search loading view to be semi transparent, and add a message when more results are available 2022-08-30 15:32:08 +02:00
QuentinArguillere
8efce6e446 Do not apply international prefix when parsing uri that will not be used for a chat/call 2022-08-30 15:06:09 +02:00
Christophe Deschamps
4333e97932 Add more space to call view in landscape mode 2022-08-24 09:34:40 +02:00
Christophe Deschamps
77b9287195 Conference Active Speaker adjustments :
- Leave local participant always first (portrait) or top (landscape)
- Layout for 0, 1 and more participants
2022-08-24 09:17:11 +02:00
QuentinArguillere
a4bc64f2b3 Use linphone_core_interpret_url_2 and use the account params useInternationalPrefixForCallsAndChat 2022-08-23 16:30:17 +02:00
QuentinArguillere
2a54eae049 Add Post quantum ZRTP support, encryption section to the call stats. Add zrtp_key_agreements_suites for post quantum in the factory rc 2022-08-23 16:01:43 +02:00
Christophe Deschamps
2279a23db1 Added dropdown patch into Podfile 2022-08-19 16:01:37 +02:00
Christophe Deschamps
8743a01b88 Filter for terminated/scheduled conferences 2022-08-19 12:23:31 +02:00
Christophe Deschamps
8ed7ba7bee IMDM cell cosmetics 2022-08-19 12:21:48 +02:00
Christophe Deschamps
240d28e234 Display Meeting in recording list instead of conference server address 2022-08-19 12:21:40 +02:00
Christophe Deschamps
d352034e5e Display avatar icon instead of initials when username is a phone number 2022-08-19 12:21:28 +02:00
Christophe Deschamps
3c12854dc6 Conference participants list cosmetics 2022-08-19 12:21:19 +02:00
Christophe Deschamps
47b7820b58 Meeting invitations bubble :
- Fix description displaying (dynamic height)
- Fix reply/forward header overlap
- Remove copy text option from contextual menu of ICS
- Improved chat bubble context menu icons quality
- Do not show IMDM option menu in 1-1 chat room
- Remove extra space at bottom of contextual menu in chat bubbles
2022-08-19 12:21:10 +02:00
Christophe Deschamps
ed56de2527 Send conference invitations only if scheduled for laster 2022-08-19 12:20:32 +02:00
Christophe Deschamps
57399ec722 Conference scheduling graphical fixes :
- mandatory indicator icon truncated
- text entry colors (hint/text)
- Send via option moved inside scheduling form
- mandatory indicator placement
- conference summary cosmetics
2022-08-19 12:20:24 +02:00
Christophe Deschamps
57b676c775 - Ability to mute/unmute when outside a conference
- Call stats & Numpad views above controls
- Group call icon in call list cosmetic
- Calls List : fix icon size & layout issue on some iOS Devices
- Black background on video full screen conference
2022-08-19 12:20:14 +02:00
Christophe Deschamps
07e0f0ca80 Fix conference resume button 2022-08-19 12:20:06 +02:00
Christophe Deschamps
981a7c5a60 Cosmetic on dots menu & micro icon on conversation view 2022-08-19 12:19:56 +02:00
Christophe Deschamps
8ba4230c19 Conference Active Speaker view landscape mode 2022-08-19 12:19:48 +02:00
Christophe Deschamps
12f9de3384 Landscape layout in conference waiting room
If video is activated in waiting room with audio only mode selected, active speaker mode becomes selected
2022-08-19 12:19:39 +02:00
Christophe Deschamps
6af2602639 Fix local video content mode in 1-1 video calls 2022-08-19 12:19:25 +02:00
Christophe Deschamps
81964daa0c Prevent infinite loop on displaying ICS invitation 2022-08-19 12:19:06 +02:00
Quentin Monnier
7cdcdd4a4d bug fix :
-display/hide call stats when clicking on the network quality icon in the status bar of the Active Call View
2022-08-19 11:13:14 +02:00
Quentin Monnier
be1e726a0c bug fix:
-resume the active call by clicking on the play button and not by clicking anywhere on the screen when it is paused
2022-08-19 11:13:08 +02:00
Quentin Monnier
8d3fead61d bug fix:
-shows the "backToCallButton" when accessing the "TransferCallView" and the "AddCallView" from the "ExtraButtonMenu" in active calls
-delete the first blank character in the entered DTMF
-disable the hide action of the "ExtraButtonMenu" when clicking between buttons, in the background
-closes the "NumpadView" and the "CallStatsView" when the call ends
-displays the "StatusBar" in "Incoming/OutgoingCallView"
-define the height of the "EneteredDtmfTextView" (no longer automatic since the blank character has been removed)
2022-08-19 11:13:02 +02:00
QuentinArguillere
5dda082a75 Do not display accounts that have the “hidden” custom parameters enabled 2022-08-11 09:39:27 +02:00
QuentinArguillere
a77d6bb1b6 Fix chatroom when entering background then re-entering foreground during a call 2022-07-28 15:36:45 +02:00
QuentinArguillere
dcc832436e Allow the removal of account international prefix when it has been set 2022-07-28 15:36:28 +02:00
Christophe Deschamps
1559bf46c7 Activate CallKit & Audio Session upon immediate conf locally created 2022-07-27 17:43:41 +02:00
QuentinArguillere
4ec4723fbb When destroying the linphone core, also destroy the magic search singleton This fixes bugs that would break the LDAP contact search after a remote provisioning. 2022-07-27 15:23:05 +02:00
QuentinArguillere
d25cbbf609 To avoid possible race condition, reload contacts in the global state change callbacks 2022-07-27 15:22:59 +02:00
Christophe Deschamps
90c367ccca Workarounds for active speaker: do not display 'me' device name when speaking + showing ourselves when alone 2022-07-22 12:09:49 +02:00
Christophe Deschamps
3e22ea3a80 Fix Toast not dismissing 2022-07-21 12:22:21 +02:00
Christophe Deschamps
49bee7c6a0 ICS Reply 2022-07-11 23:44:22 +02:00
Christophe Deschamps
907cc94fc6 Conference side menu icon 2022-07-11 23:44:13 +02:00
Christophe Deschamps
b04a306443 Fix locally paused state in view model 2022-07-11 23:44:06 +02:00
Christophe Deschamps
ffe795bf02 Merge branch 'release/4.7' of gitlab.linphone.org:BC/public/linphone-iphone into release/4.7 2022-07-11 20:52:24 +02:00
Christophe Deschamps
5ee6695cdc Adapt to latest sdk change related to conference + implemented joining spinner + refactored conference miniatures update 2022-07-11 20:52:15 +02:00
QuentinArguillere
d14a0d3c10 In group chatrooms, added the “add to contact” contextual action when interacting with a message coming from someone that is not registered in your phone contacts 2022-07-08 17:21:31 +02:00
QuentinArguillere
9612e21162 Fix “Add to contact” action for encrypted chatrooms 2022-07-08 17:11:12 +02:00
QuentinArguillere
81688b416b Do not display “Infos” action for messages in 1-1 chatrooms 2022-07-05 13:51:40 +02:00
QuentinArguillere
da08af7e3f “Back” button in chatrooms should now behave more in line with what is expected : return to the chatlist view in most cases 2022-07-05 13:50:15 +02:00
QuentinArguillere
9ab70c60fe In chat conversation, hide keyboard when the action pop-up bubble (reply, transfer, etc) appears, and show keyboard when “reply’ is selected. This matches the usual behaviours of other messaging apps 2022-07-04 17:24:33 +02:00
Christophe Deschamps
e3ed160c54 Dropdown picker more user-friendly 2022-07-03 19:00:52 +02:00
Christophe Deschamps
7d71bd500e Merge branch 'release/4.7' of gitlab.linphone.org:BC/public/linphone-iphone into release/4.7 2022-07-03 19:00:47 +02:00
QuentinArguillere
6ca276b718 Update iOS version on Podfile from 9.0 to 10.0 2022-06-30 15:57:52 +02:00
Christophe Deschamps
0d0c0225f1 Fix calendar event pre-set fields 2022-06-30 15:21:13 +02:00
Christophe Deschamps
6adeb43fdc Added calendar sharing of ICS from bubble chat 2022-06-30 12:27:41 +02:00
Christophe Deschamps
761a47ad87 Prevent keyboard from hiding description field in conference scheduling view 2022-06-29 13:12:50 +02:00
Christophe Deschamps
343a30d93b Admin set/unset notification in conference 2022-06-29 08:53:00 +02:00
QuentinArguillere
54071f9d15 Avoid crash in settings menu for iOS < 13.0 2022-06-28 17:21:01 +02:00
QuentinArguillere
924b499496 Disable voip & remote push notification by default when using the assistant to login with an external sip account 2022-06-28 17:20:17 +02:00
QuentinArguillere
10b28cd6cf Fix canAdminEphemeral function in ChatConversationViews so that only encrypted chatrooms can possibly return “true”. Also fix bug causing the wrong row to be called in chatroom popup action menu 2022-06-27 16:50:38 +02:00
QuentinArguillere
33b07703ca Always display LDAP contacts in priority. If a local contact matches, partially or totally, the LDAP contact, still display both. 2022-06-27 11:43:33 +02:00
QuentinArguillere
74c69b3779 Always Hide edit/delete buttons for LDAP contact details view (could still appear if accessed through a “previous” button) 2022-06-27 11:43:27 +02:00
QuentinArguillere
ef8e1baa92 Fix typo in LDAP.plist file 2022-06-24 16:54:55 +02:00
QuentinArguillere
6e93601508 Fix typos in LDAP settings menu that would cause a partial loss of the configuration when exiting the menu 2022-06-24 16:18:15 +02:00
QuentinArguillere
0e31b555c1 Implement notification mute for group & encrypted chatrooms 2022-06-23 14:48:51 +02:00
QuentinArguillere
930dd74c89 Fix bugs when rotating from portrait to landscape mode : edit/delete action shown as available for LDAP contacts, or Contact Name appearing despite being currently in edit mode 2022-06-23 13:33:16 +02:00
QuentinArguillere
f847d8cfe3 Fix bug that could cause a clipping/hiding of a contact name depending on the phone orientation 2022-06-23 13:33:06 +02:00
QuentinArguillere
263b460372 When hitting the “back” button on a contact details view, redirect to the ContactListView by default. Only return to the history details view if we specifically came from there. 2022-06-23 13:33:00 +02:00
QuentinArguillere
4c690f9274 Now allow duplicate contacts to be displayed (useful if you have a local contact AND the contact exists with more informations in a LDAP server) 2022-06-23 13:32:54 +02:00
QuentinArguillere
ff14c18f1c Fix GUI when remote provisioning fails due to the user already having accounts 2022-06-23 13:32:49 +02:00
Christophe Deschamps
97708e9096 Fallback to AS if MaxPart > 6 only if Grid is active 2022-06-23 09:49:02 +02:00
189 changed files with 4379 additions and 2008 deletions

View file

@ -7,11 +7,12 @@ variables:
job-ios: job-ios:
stage: build stage: build
tags: [ "macmini-m1-xcode13" ] tags: [ "macos-xcode13" ]
script: script:
- pod install --repo-update - pod install --repo-update
- pwd - pwd
- sed 's/fileprivate let tableView =/public let tableView =/g' ./Pods/DropDown/DropDown/src/DropDown.swift > tmp.swift && mv -f tmp.swift ./Pods/DropDown/DropDown/src/DropDown.swift
- xcodebuild archive -scheme $archive_scheme -archivePath ./$archive_path -configuration Release -workspace ./linphone.xcworkspace -UseModernBuildSystem=YES -destination 'generic/platform=iOS' - xcodebuild archive -scheme $archive_scheme -archivePath ./$archive_path -configuration Release -workspace ./linphone.xcworkspace -UseModernBuildSystem=YES -destination 'generic/platform=iOS'
- xcodebuild -exportArchive -archivePath ./$archive_path -exportPath ./$export_path -exportOptionsPlist ./$export_options_plist -allowProvisioningUpdates -UseModernBuildSystem=YES -destination 'generic/platform=iOS' - xcodebuild -exportArchive -archivePath ./$archive_path -exportPath ./$export_path -exportOptionsPlist ./$export_options_plist -allowProvisioningUpdates -UseModernBuildSystem=YES -destination 'generic/platform=iOS'

View file

@ -9,7 +9,33 @@ Group changes to describe their impact on the project, as follows:
Removed for deprecated features removed in this release. Removed for deprecated features removed in this release.
Fixed for any bug fixes. Fixed for any bug fixes.
Security to invite users to upgrade in case of vulnerabilities. Security to invite users to upgrade in case of vulnerabilities.
## [5.0.0] - 2021-12-06
### Added
- Post Quantum encryption when using ZRTP
- Conference creation with scheduling, video, different layouts, showing who is speaking and who is muted, etc...
- Group calls directly from group chat rooms
- Chat rooms can be individually muted (no notification when receiving a chat message)
- Outgoing call video in early-media if requested by callee
- Call recordings can be exported
- Setting to prevent international prefix from account to be applied to call & chat
- Add a "Never ask again" option to the "Link my account" pop-up when starting the app
### Changed
- In-call views have been re-designed
- Improved how contact avatars are generated
- 3-dots menu even for basic chat rooms with more options
- Update linphone SDK to 5.2.0
### Fixed
- Chatroom appearing as empty when being logged on multiple accounts
- Chatroom appearing as empty after playing a video file inside it
- Fix potential crash when entering a chatroom
- Fix potential crash when accessing to the delivery infos of a message in a group chat.
- IMDN logo not properly displayed when transfering or replying to a message with media (voice message, photo...)
- Clarified view when sending an image from the galery
- Various audio route fixes for CallKit and IOS 16
## [4.6.4] - 2021-08-06 ## [4.6.4] - 2021-08-06
### Changed ### Changed

View file

@ -127,7 +127,9 @@ static UICompositeViewDescription *compositeDescription = nil;
if (!mustRestoreView) { if (!mustRestoreView) {
new_account = NULL; new_account = NULL;
number_of_accounts_before = bctbx_list_size(linphone_core_get_account_list(LC)); MSList *accounts = [LinphoneManager.instance createAccountsNotHiddenList];
number_of_accounts_before = bctbx_list_size(accounts);
bctbx_free(accounts);
[self resetTextFields]; [self resetTextFields];
[self changeView:_welcomeView back:FALSE animation:FALSE]; [self changeView:_welcomeView back:FALSE animation:FALSE];
} }
@ -519,6 +521,10 @@ static UICompositeViewDescription *compositeDescription = nil;
#endif #endif
linphone_push_notification_config_set_provider(pushConfig, PROVIDER_NAME); linphone_push_notification_config_set_provider(pushConfig, PROVIDER_NAME);
if (strcmp(creatorDomain, "sip.linphone.org")==0) {
linphone_core_set_media_encryption(LC, LinphoneMediaEncryptionSRTP);
}
new_account = linphone_core_create_account(LC, accountParams); new_account = linphone_core_create_account(LC, accountParams);
linphone_account_params_unref(accountParams); linphone_account_params_unref(accountParams);
@ -705,7 +711,7 @@ static UICompositeViewDescription *compositeDescription = nil;
LinphoneAccount *default_account = linphone_core_create_account(LC, default_account_params); LinphoneAccount *default_account = linphone_core_create_account(LC, default_account_params);
const char *identity = linphone_account_params_get_identity(linphone_account_get_params(default_account)); const char *identity = linphone_account_params_get_identity(linphone_account_get_params(default_account));
if (identity) { if (identity) {
LinphoneAddress *default_addr = linphone_core_interpret_url(LC, identity); LinphoneAddress *default_addr = linphone_core_interpret_url_2(LC, identity, false);
if (default_addr) { if (default_addr) {
const char *domain = linphone_address_get_domain(default_addr); const char *domain = linphone_address_get_domain(default_addr);
const char *username = linphone_address_get_username(default_addr); const char *username = linphone_address_get_username(default_addr);
@ -1011,10 +1017,13 @@ static UICompositeViewDescription *compositeDescription = nil;
[LinphoneManager.instance lpConfigSetInt:[NSDate new].timeIntervalSince1970 [LinphoneManager.instance lpConfigSetInt:[NSDate new].timeIntervalSince1970
forKey:@"must_link_account_time"]; forKey:@"must_link_account_time"];
[LinphoneManager.instance configurePushProviderForAccounts]; [LinphoneManager.instance configurePushProviderForAccounts];
if (number_of_accounts_before < bctbx_list_size(linphone_core_get_account_list(LC))) {
MSList *accounts = [LinphoneManager.instance createAccountsNotHiddenList];
if (number_of_accounts_before < bctbx_list_size(accounts)) {
LOGI(@"A proxy config was set up with the remote provisioning, skip assistant"); LOGI(@"A proxy config was set up with the remote provisioning, skip assistant");
[self onDialerClick:nil]; [self onDialerClick:nil];
} }
bctbx_free(accounts);
_waitView.hidden = true; _waitView.hidden = true;
if (nextView == nil) { if (nextView == nil) {
@ -1149,7 +1158,11 @@ static UICompositeViewDescription *compositeDescription = nil;
_outgoingView = DialerView.compositeViewDescription; _outgoingView = DialerView.compositeViewDescription;
[self configureAccount]; [self configureAccount];
} else if (status == LinphoneAccountCreatorStatusAccountExist) { } else if (status == LinphoneAccountCreatorStatusAccountExist) {
_outgoingView = AssistantLinkView.compositeViewDescription; if([LinphoneManager.instance lpConfigIntForKey:@"hide_link_phone_number"]){
_outgoingView = DialerView.compositeViewDescription;
}else{
_outgoingView = AssistantLinkView.compositeViewDescription;
}
[self configureAccount]; [self configureAccount];
} else { } else {
if (resp) { if (resp) {
@ -1584,10 +1597,11 @@ UIColor *previousColor = (UIColor*)[sender backgroundColor]; \
UIAlertAction* defaultAction = [UIAlertAction actionWithTitle:@"OK" UIAlertAction* defaultAction = [UIAlertAction actionWithTitle:@"OK"
style:UIAlertActionStyleDefault style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action) {}]; handler:^(UIAlertAction * action) {}];
[errView addAction:defaultAction]; [errView addAction:defaultAction];
[self presentViewController:errView animated:YES completion:nil]; [self presentViewController:errView animated:YES completion:nil];
_waitView.hidden = TRUE;
} else { } else {
linphone_core_set_provisioning_uri(LC, [self addSchemeToProvisiionninUriIMissing:[self findTextField:ViewElement_URL].text].UTF8String); linphone_core_set_provisioning_uri(LC, [self addSchemeToProvisiionninUriIMissing:[self findTextField:ViewElement_URL].text].UTF8String);
[self resetLiblinphone:TRUE]; [self resetLiblinphone:TRUE];

View file

@ -1,9 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="20037" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" colorMatched="YES"> <document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="21225" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" colorMatched="YES">
<device id="retina6_1" orientation="portrait" appearance="light"/> <device id="retina6_1" orientation="portrait" appearance="light"/>
<dependencies> <dependencies>
<deployment identifier="iOS"/> <deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="20020"/> <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="21207"/>
<capability name="System colors in document resources" minToolsVersion="11.0"/> <capability name="System colors in document resources" minToolsVersion="11.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/> <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies> </dependencies>
@ -175,13 +175,13 @@
<color key="backgroundColor" systemColor="secondarySystemBackgroundColor"/> <color key="backgroundColor" systemColor="secondarySystemBackgroundColor"/>
</view> </view>
<view contentMode="scaleToFill" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="KRQ-Fm-3cQ" userLabel="addedContacts" customClass="UICollectionView"> <view contentMode="scaleToFill" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="KRQ-Fm-3cQ" userLabel="addedContacts" customClass="UICollectionView">
<rect key="frame" x="8" y="110" width="398" height="70"/> <rect key="frame" x="8" y="110" width="398" height="50"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxY="YES"/> <autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxY="YES"/>
<color key="backgroundColor" systemColor="systemBackgroundColor"/> <color key="backgroundColor" systemColor="systemBackgroundColor"/>
<accessibility key="accessibilityConfiguration" label="addedContacts"/> <accessibility key="accessibilityConfiguration" label="addedContacts"/>
</view> </view>
<tableView clipsSubviews="YES" contentMode="scaleToFill" fixedFrame="YES" alwaysBounceVertical="YES" style="plain" separatorStyle="default" allowsSelectionDuringEditing="YES" allowsMultipleSelectionDuringEditing="YES" rowHeight="44" sectionHeaderHeight="22" sectionFooterHeight="22" translatesAutoresizingMaskIntoConstraints="NO" id="6"> <tableView clipsSubviews="YES" contentMode="scaleToFill" fixedFrame="YES" alwaysBounceVertical="YES" style="plain" separatorStyle="default" allowsSelectionDuringEditing="YES" allowsMultipleSelectionDuringEditing="YES" rowHeight="44" sectionHeaderHeight="22" sectionFooterHeight="22" translatesAutoresizingMaskIntoConstraints="NO" id="6">
<rect key="frame" x="5" y="178" width="403" height="610"/> <rect key="frame" x="5" y="158" width="403" height="610"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" widthSizable="YES" flexibleMaxX="YES" heightSizable="YES" flexibleMaxY="YES"/> <autoresizingMask key="autoresizingMask" flexibleMinX="YES" widthSizable="YES" flexibleMaxX="YES" heightSizable="YES" flexibleMaxY="YES"/>
<color key="backgroundColor" systemColor="systemBackgroundColor"/> <color key="backgroundColor" systemColor="systemBackgroundColor"/>
<color key="separatorColor" red="0.67030966281890869" green="0.71867996454238892" blue="0.75078284740447998" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> <color key="separatorColor" red="0.67030966281890869" green="0.71867996454238892" blue="0.75078284740447998" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>

View file

@ -1,9 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="18122" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES"> <document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="21225" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES">
<device id="retina6_1" orientation="portrait" appearance="light"/> <device id="retina6_1" orientation="portrait" appearance="light"/>
<dependencies> <dependencies>
<deployment identifier="iOS"/> <deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="18093"/> <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="21207"/>
<capability name="System colors in document resources" minToolsVersion="11.0"/> <capability name="System colors in document resources" minToolsVersion="11.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/> <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies> </dependencies>
@ -97,7 +97,7 @@
<nil key="highlightedColor"/> <nil key="highlightedColor"/>
</label> </label>
<button opaque="NO" contentMode="scaleToFill" fixedFrame="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" adjustsImageWhenHighlighted="NO" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="XSI-9T-NtW" userLabel="addButton" customClass="UIInterfaceStyleButton"> <button opaque="NO" contentMode="scaleToFill" fixedFrame="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" adjustsImageWhenHighlighted="NO" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="XSI-9T-NtW" userLabel="addButton" customClass="UIInterfaceStyleButton">
<rect key="frame" x="382" y="4" width="24" height="21"/> <rect key="frame" x="382" y="2" width="24" height="24"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMinY="YES" flexibleMaxY="YES"/> <autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMinY="YES" flexibleMaxY="YES"/>
<accessibility key="accessibilityConfiguration" label="Back"/> <accessibility key="accessibilityConfiguration" label="Back"/>
<fontDescription key="fontDescription" type="system" pointSize="14"/> <fontDescription key="fontDescription" type="system" pointSize="14"/>

View file

@ -1,9 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="20037" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" colorMatched="YES"> <document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="21507" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" colorMatched="YES">
<device id="retina6_1" orientation="portrait" appearance="light"/> <device id="retina6_1" orientation="portrait" appearance="light"/>
<dependencies> <dependencies>
<deployment identifier="iOS"/> <deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="20020"/> <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="21505"/>
<capability name="System colors in document resources" minToolsVersion="11.0"/> <capability name="System colors in document resources" minToolsVersion="11.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/> <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies> </dependencies>
@ -218,7 +218,7 @@
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/> <autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
<subviews> <subviews>
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" tag="15" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" text="%@ is composing..." lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumScaleFactor="0.5" translatesAutoresizingMaskIntoConstraints="NO" id="fpY-Fv-ht2" userLabel="composeLabel"> <label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" tag="15" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" text="%@ is composing..." lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumScaleFactor="0.5" translatesAutoresizingMaskIntoConstraints="NO" id="fpY-Fv-ht2" userLabel="composeLabel">
<rect key="frame" x="0.0" y="1" width="414" height="22"/> <rect key="frame" x="10" y="-5" width="406" height="21"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<accessibility key="accessibilityConfiguration" label=""/> <accessibility key="accessibilityConfiguration" label=""/>
<fontDescription key="fontDescription" type="system" pointSize="17"/> <fontDescription key="fontDescription" type="system" pointSize="17"/>
@ -608,7 +608,6 @@
</state> </state>
<state key="selected" image="vr_on.png"/> <state key="selected" image="vr_on.png"/>
<connections> <connections>
<action selector="onPictureClick:" destination="-1" eventType="touchUpInside" id="qhP-B0-dkG"/>
<action selector="onVrStart:" destination="-1" eventType="touchUpInside" id="yWJ-st-yz2"/> <action selector="onVrStart:" destination="-1" eventType="touchUpInside" id="yWJ-st-yz2"/>
</connections> </connections>
</button> </button>

View file

@ -12,6 +12,7 @@
<connections> <connections>
<outlet property="addButton" destination="6" id="91"/> <outlet property="addButton" destination="6" id="91"/>
<outlet property="allButton" destination="4" id="27"/> <outlet property="allButton" destination="4" id="27"/>
<outlet property="ldapMoreResultsLabel" destination="cDH-mL-cHP" id="3d9-gp-Hog"/>
<outlet property="linphoneButton" destination="5" id="31"/> <outlet property="linphoneButton" destination="5" id="31"/>
<outlet property="loadingLabel" destination="qSa-Ba-dq9" id="CPa-pO-OQD"/> <outlet property="loadingLabel" destination="qSa-Ba-dq9" id="CPa-pO-OQD"/>
<outlet property="loadingView" destination="CM2-Aq-Q3g" id="uie-SJ-TKf"/> <outlet property="loadingView" destination="CM2-Aq-Q3g" id="uie-SJ-TKf"/>
@ -179,6 +180,14 @@
<outlet property="delegate" destination="TJG-JZ-YRR" id="V1N-gI-U4J"/> <outlet property="delegate" destination="TJG-JZ-YRR" id="V1N-gI-U4J"/>
</connections> </connections>
</tableView> </tableView>
<label hidden="YES" opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" text="More results are available, refine search to see them" textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="3" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="cDH-mL-cHP" userLabel="ldapMoreResultsLabel">
<rect key="frame" x="8" y="110" width="359" height="18"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" widthSizable="YES" flexibleMaxX="YES" heightSizable="YES" flexibleMaxY="YES"/>
<color key="backgroundColor" systemColor="systemBackgroundColor"/>
<fontDescription key="fontDescription" type="system" pointSize="13"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
<label hidden="YES" opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" text="No contact found in your address book" textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="3" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="JR3-k7-gVP" userLabel="emptyTableLabel"> <label hidden="YES" opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" text="No contact found in your address book" textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="3" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="JR3-k7-gVP" userLabel="emptyTableLabel">
<rect key="frame" x="0.0" y="110" width="375" height="449"/> <rect key="frame" x="0.0" y="110" width="375" height="449"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" widthSizable="YES" flexibleMaxX="YES" heightSizable="YES" flexibleMaxY="YES"/> <autoresizingMask key="autoresizingMask" flexibleMinX="YES" widthSizable="YES" flexibleMaxX="YES" heightSizable="YES" flexibleMaxY="YES"/>
@ -187,7 +196,7 @@
<nil key="textColor"/> <nil key="textColor"/>
<nil key="highlightedColor"/> <nil key="highlightedColor"/>
</label> </label>
<view hidden="YES" contentMode="scaleToFill" id="CM2-Aq-Q3g"> <view hidden="YES" alpha="0.80000000000000004" contentMode="scaleToFill" id="CM2-Aq-Q3g">
<rect key="frame" x="0.0" y="110" width="375" height="449"/> <rect key="frame" x="0.0" y="110" width="375" height="449"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES" flexibleMaxY="YES"/> <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES" flexibleMaxY="YES"/>
<subviews> <subviews>
@ -209,7 +218,7 @@
</view> </view>
</subviews> </subviews>
<color key="backgroundColor" red="1" green="1" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> <color key="backgroundColor" red="1" green="1" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<point key="canvasLocation" x="6.5217391304347831" y="142.29910714285714"/> <point key="canvasLocation" x="5.7971014492753632" y="141.96428571428569"/>
</view> </view>
<tableViewController id="TJG-JZ-YRR" userLabel="tableController" customClass="ContactsListTableView"> <tableViewController id="TJG-JZ-YRR" userLabel="tableController" customClass="ContactsListTableView">
<connections> <connections>

View file

@ -1,7 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?> <?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="21225" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" colorMatched="YES">
<device id="retina6_0" orientation="portrait" appearance="light"/>
<dependencies> <dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="15704"/> <deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="21207"/>
<capability name="System colors in document resources" minToolsVersion="11.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/> <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies> </dependencies>
<objects> <objects>
@ -11,6 +14,7 @@
<outlet property="addressLabel" destination="EoB-ux-sD7" id="Ajw-2s-M6X"/> <outlet property="addressLabel" destination="EoB-ux-sD7" id="Ajw-2s-M6X"/>
<outlet property="avatarImage" destination="23" id="43"/> <outlet property="avatarImage" destination="23" id="43"/>
<outlet property="backButton" destination="9" id="Pqj-y9-hqc"/> <outlet property="backButton" destination="9" id="Pqj-y9-hqc"/>
<outlet property="chatButton" destination="obZ-W7-q8P" id="96n-Oe-Gm0"/>
<outlet property="contactLabel" destination="25" id="rTL-Ut-42o"/> <outlet property="contactLabel" destination="25" id="rTL-Ut-42o"/>
<outlet property="emptyLabel" destination="hvz-CS-NME" id="Qws-r1-XMh"/> <outlet property="emptyLabel" destination="hvz-CS-NME" id="Qws-r1-XMh"/>
<outlet property="encryptedChatView" destination="JU4-bf-tVI" id="j6f-qz-VKd"/> <outlet property="encryptedChatView" destination="JU4-bf-tVI" id="j6f-qz-VKd"/>
@ -26,19 +30,19 @@
</placeholder> </placeholder>
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/> <placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
<view contentMode="scaleToFill" id="HKr-sq-hGv" userLabel="iphone6MetricsView"> <view contentMode="scaleToFill" id="HKr-sq-hGv" userLabel="iphone6MetricsView">
<rect key="frame" x="0.0" y="0.0" width="667" height="375"/> <rect key="frame" x="0.0" y="0.0" width="390" height="844"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/> <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<subviews> <subviews>
<view tag="1" contentMode="scaleToFill" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="4"> <view tag="1" contentMode="scaleToFill" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="4">
<rect key="frame" x="0.0" y="42" width="667" height="267"/> <rect key="frame" x="-1" y="41" width="389" height="735"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews> <subviews>
<view alpha="0.90000000000000002" tag="2" contentMode="scaleToFill" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="6" userLabel="topBar"> <view alpha="0.90000000000000002" tag="2" contentMode="scaleToFill" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="6" userLabel="topBar">
<rect key="frame" x="0.0" y="0.0" width="667" height="66"/> <rect key="frame" x="0.0" y="0.0" width="389" height="66"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxY="YES"/> <autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxY="YES"/>
<subviews> <subviews>
<button opaque="NO" tag="4" contentMode="scaleToFill" fixedFrame="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" adjustsImageWhenHighlighted="NO" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="9" userLabel="backButton" customClass="UIInterfaceStyleButton"> <button opaque="NO" tag="4" contentMode="scaleToFill" fixedFrame="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" adjustsImageWhenHighlighted="NO" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="9" userLabel="backButton" customClass="UIInterfaceStyleButton">
<rect key="frame" x="0.0" y="0.0" width="128" height="66"/> <rect key="frame" x="0.0" y="0.0" width="74" height="65"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" widthSizable="YES" flexibleMaxX="YES" heightSizable="YES"/> <autoresizingMask key="autoresizingMask" flexibleMinX="YES" widthSizable="YES" flexibleMaxX="YES" heightSizable="YES"/>
<accessibility key="accessibilityConfiguration" label="Back"/> <accessibility key="accessibilityConfiguration" label="Back"/>
<inset key="titleEdgeInsets" minX="0.0" minY="18" maxX="0.0" maxY="0.0"/> <inset key="titleEdgeInsets" minX="0.0" minY="18" maxX="0.0" maxY="0.0"/>
@ -50,7 +54,7 @@
</connections> </connections>
</button> </button>
<button opaque="NO" tag="5" contentMode="scaleToFill" fixedFrame="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" adjustsImageWhenHighlighted="NO" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="50" userLabel="addButton" customClass="UIInterfaceStyleButton"> <button opaque="NO" tag="5" contentMode="scaleToFill" fixedFrame="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" adjustsImageWhenHighlighted="NO" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="50" userLabel="addButton" customClass="UIInterfaceStyleButton">
<rect key="frame" x="539" y="0.0" width="128" height="66"/> <rect key="frame" x="314" y="0.0" width="75" height="65"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" widthSizable="YES" flexibleMaxX="YES" heightSizable="YES"/> <autoresizingMask key="autoresizingMask" flexibleMinX="YES" widthSizable="YES" flexibleMaxX="YES" heightSizable="YES"/>
<accessibility key="accessibilityConfiguration" label="Add to contact"/> <accessibility key="accessibilityConfiguration" label="Add to contact"/>
<inset key="titleEdgeInsets" minX="0.0" minY="18" maxX="0.0" maxY="0.0"/> <inset key="titleEdgeInsets" minX="0.0" minY="18" maxX="0.0" maxY="0.0"/>
@ -62,14 +66,14 @@
</connections> </connections>
</button> </button>
</subviews> </subviews>
<color key="backgroundColor" systemColor="secondarySystemBackgroundColor" red="0.94901960780000005" green="0.94901960780000005" blue="0.96862745100000003" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> <color key="backgroundColor" systemColor="secondarySystemBackgroundColor"/>
</view> </view>
<view tag="7" contentMode="scaleToFill" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="33" userLabel="headerView"> <view tag="7" contentMode="scaleToFill" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="33" userLabel="headerView">
<rect key="frame" x="0.0" y="66" width="667" height="250"/> <rect key="frame" x="0.0" y="66" width="389" height="250"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" widthSizable="YES" flexibleMaxX="YES" flexibleMaxY="YES"/> <autoresizingMask key="autoresizingMask" flexibleMinX="YES" widthSizable="YES" flexibleMaxX="YES" flexibleMaxY="YES"/>
<subviews> <subviews>
<imageView userInteractionEnabled="NO" tag="8" contentMode="scaleAspectFit" fixedFrame="YES" image="avatar.png" translatesAutoresizingMaskIntoConstraints="NO" id="23" userLabel="avatarImage" customClass="UIRoundedImageView"> <imageView userInteractionEnabled="NO" tag="8" contentMode="scaleAspectFit" fixedFrame="YES" image="avatar.png" translatesAutoresizingMaskIntoConstraints="NO" id="23" userLabel="avatarImage" customClass="UIRoundedImageView">
<rect key="frame" x="244" y="8" width="178" height="100"/> <rect key="frame" x="141" y="8" width="104" height="100"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" widthSizable="YES" flexibleMaxX="YES" flexibleMaxY="YES"/> <autoresizingMask key="autoresizingMask" flexibleMinX="YES" widthSizable="YES" flexibleMaxX="YES" flexibleMaxY="YES"/>
<accessibility key="accessibilityConfiguration" label="Contact avatar"> <accessibility key="accessibilityConfiguration" label="Contact avatar">
<accessibilityTraits key="traits" image="YES" notEnabled="YES"/> <accessibilityTraits key="traits" image="YES" notEnabled="YES"/>
@ -77,7 +81,7 @@
</accessibility> </accessibility>
</imageView> </imageView>
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" tag="9" contentMode="left" fixedFrame="YES" text="John Doe" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="25" userLabel="contactLabel"> <label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" tag="9" contentMode="left" fixedFrame="YES" text="John Doe" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="25" userLabel="contactLabel">
<rect key="frame" x="0.0" y="110" width="667" height="40"/> <rect key="frame" x="0.0" y="110" width="389" height="40"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" widthSizable="YES" flexibleMaxX="YES" flexibleMaxY="YES"/> <autoresizingMask key="autoresizingMask" flexibleMinX="YES" widthSizable="YES" flexibleMaxX="YES" flexibleMaxY="YES"/>
<accessibility key="accessibilityConfiguration" label="Contact name"/> <accessibility key="accessibilityConfiguration" label="Contact name"/>
<fontDescription key="fontDescription" type="system" pointSize="33"/> <fontDescription key="fontDescription" type="system" pointSize="33"/>
@ -85,7 +89,7 @@
<nil key="highlightedColor"/> <nil key="highlightedColor"/>
</label> </label>
<imageView userInteractionEnabled="NO" tag="10" contentMode="scaleAspectFit" fixedFrame="YES" image="linphone_user.png" translatesAutoresizingMaskIntoConstraints="NO" id="mfN-Ai-9RX" userLabel="linphoneImage" customClass="UIRoundedImageView"> <imageView userInteractionEnabled="NO" tag="10" contentMode="scaleAspectFit" fixedFrame="YES" image="linphone_user.png" translatesAutoresizingMaskIntoConstraints="NO" id="mfN-Ai-9RX" userLabel="linphoneImage" customClass="UIRoundedImageView">
<rect key="frame" x="642" y="124" width="15" height="15"/> <rect key="frame" x="363" y="124" width="16" height="15"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxY="YES"/> <autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxY="YES"/>
<accessibility key="accessibilityConfiguration" label="Contact avatar"> <accessibility key="accessibilityConfiguration" label="Contact avatar">
<accessibilityTraits key="traits" image="YES" notEnabled="YES"/> <accessibilityTraits key="traits" image="YES" notEnabled="YES"/>
@ -93,7 +97,7 @@
</accessibility> </accessibility>
</imageView> </imageView>
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" tag="11" contentMode="left" fixedFrame="YES" text="johndoe@sip.linphone.org" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumFontSize="10" translatesAutoresizingMaskIntoConstraints="NO" id="EoB-ux-sD7" userLabel="addressLabel"> <label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" tag="11" contentMode="left" fixedFrame="YES" text="johndoe@sip.linphone.org" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumFontSize="10" translatesAutoresizingMaskIntoConstraints="NO" id="EoB-ux-sD7" userLabel="addressLabel">
<rect key="frame" x="0.0" y="158" width="667" height="23"/> <rect key="frame" x="0.0" y="158" width="389" height="23"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" widthSizable="YES" flexibleMaxX="YES" flexibleMaxY="YES"/> <autoresizingMask key="autoresizingMask" flexibleMinX="YES" widthSizable="YES" flexibleMaxX="YES" flexibleMaxY="YES"/>
<accessibility key="accessibilityConfiguration" label="Contact name"/> <accessibility key="accessibilityConfiguration" label="Contact name"/>
<fontDescription key="fontDescription" type="system" pointSize="18"/> <fontDescription key="fontDescription" type="system" pointSize="18"/>
@ -101,11 +105,11 @@
<nil key="highlightedColor"/> <nil key="highlightedColor"/>
</label> </label>
<view contentMode="scaleToFill" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="k0D-99-OKO" userLabel="optionsView"> <view contentMode="scaleToFill" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="k0D-99-OKO" userLabel="optionsView">
<rect key="frame" x="0.0" y="189" width="667" height="44"/> <rect key="frame" x="0.0" y="189" width="389" height="44"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" widthSizable="YES" flexibleMaxX="YES" flexibleMaxY="YES"/> <autoresizingMask key="autoresizingMask" flexibleMinX="YES" widthSizable="YES" flexibleMaxX="YES" flexibleMaxY="YES"/>
<subviews> <subviews>
<button opaque="NO" tag="13" contentMode="scaleAspectFit" fixedFrame="YES" contentHorizontalAlignment="left" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="5eX-W0-T4B" userLabel="callButton"> <button opaque="NO" tag="13" contentMode="scaleAspectFit" fixedFrame="YES" contentHorizontalAlignment="left" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="5eX-W0-T4B" userLabel="callButton">
<rect key="frame" x="179" y="0.0" width="51" height="51"/> <rect key="frame" x="98" y="0.0" width="51" height="51"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMaxY="YES"/> <autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMaxY="YES"/>
<state key="normal" image="call_start_body_default.png"> <state key="normal" image="call_start_body_default.png">
<color key="titleShadowColor" red="0.5" green="0.5" blue="0.5" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> <color key="titleShadowColor" red="0.5" green="0.5" blue="0.5" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
@ -117,7 +121,7 @@
</connections> </connections>
</button> </button>
<button opaque="NO" tag="12" contentMode="scaleAspectFit" fixedFrame="YES" contentHorizontalAlignment="left" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="obZ-W7-q8P" userLabel="chatButton"> <button opaque="NO" tag="12" contentMode="scaleAspectFit" fixedFrame="YES" contentHorizontalAlignment="left" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="obZ-W7-q8P" userLabel="chatButton">
<rect key="frame" x="317" y="0.0" width="50" height="51"/> <rect key="frame" x="174" y="0.0" width="50" height="51"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMaxY="YES"/> <autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMaxY="YES"/>
<state key="normal" image="chat_start_body_default.png"> <state key="normal" image="chat_start_body_default.png">
<color key="titleShadowColor" red="0.5" green="0.5" blue="0.5" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> <color key="titleShadowColor" red="0.5" green="0.5" blue="0.5" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
@ -129,11 +133,11 @@
</connections> </connections>
</button> </button>
<view contentMode="scaleToFill" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="JU4-bf-tVI" userLabel="encryptedChatView"> <view contentMode="scaleToFill" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="JU4-bf-tVI" userLabel="encryptedChatView">
<rect key="frame" x="456" y="-1" width="50" height="51"/> <rect key="frame" x="251" y="-2" width="49" height="50"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMinY="YES"/> <autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMinY="YES"/>
<subviews> <subviews>
<button opaque="NO" contentMode="scaleAspectFit" fixedFrame="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="E2n-mF-saI" userLabel="encryptedChatButton" customClass="UIIconButton"> <button opaque="NO" contentMode="scaleAspectFit" fixedFrame="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="E2n-mF-saI" userLabel="encryptedChatButton" customClass="UIIconButton">
<rect key="frame" x="-1" y="0.0" width="51" height="51"/> <rect key="frame" x="0.0" y="-2" width="51" height="53"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMinY="YES"/> <autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMinY="YES"/>
<accessibility key="accessibilityConfiguration" label="Chat"/> <accessibility key="accessibilityConfiguration" label="Chat"/>
<state key="normal" image="chat_start_body_default.png"> <state key="normal" image="chat_start_body_default.png">
@ -154,35 +158,35 @@
</subviews> </subviews>
</view> </view>
</subviews> </subviews>
<color key="backgroundColor" systemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/> <color key="backgroundColor" systemColor="systemBackgroundColor"/>
</view> </view>
<tableView clipsSubviews="YES" tag="6" contentMode="scaleToFill" fixedFrame="YES" alwaysBounceVertical="YES" style="plain" separatorStyle="none" allowsSelection="NO" rowHeight="30" sectionHeaderHeight="28" sectionFooterHeight="28" translatesAutoresizingMaskIntoConstraints="NO" id="k6N-Av-eOu"> <tableView clipsSubviews="YES" tag="6" contentMode="scaleToFill" fixedFrame="YES" alwaysBounceVertical="YES" style="plain" separatorStyle="none" allowsSelection="NO" rowHeight="30" sectionHeaderHeight="28" sectionFooterHeight="28" translatesAutoresizingMaskIntoConstraints="NO" id="k6N-Av-eOu">
<rect key="frame" x="0.0" y="316" width="667" height="0.0"/> <rect key="frame" x="0.0" y="316" width="389" height="419"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" widthSizable="YES" flexibleMaxX="YES" heightSizable="YES" flexibleMaxY="YES"/> <autoresizingMask key="autoresizingMask" flexibleMinX="YES" widthSizable="YES" flexibleMaxX="YES" heightSizable="YES" flexibleMaxY="YES"/>
<color key="backgroundColor" systemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/> <color key="backgroundColor" systemColor="systemBackgroundColor"/>
<connections> <connections>
<outlet property="dataSource" destination="baU-d4-eu3" id="p7o-Mx-Kmc"/> <outlet property="dataSource" destination="baU-d4-eu3" id="p7o-Mx-Kmc"/>
<outlet property="delegate" destination="baU-d4-eu3" id="iS5-xg-0C2"/> <outlet property="delegate" destination="baU-d4-eu3" id="iS5-xg-0C2"/>
</connections> </connections>
</tableView> </tableView>
<label hidden="YES" opaque="NO" userInteractionEnabled="NO" tag="40" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" text="No log selected" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="hvz-CS-NME" userLabel="emptyLabel"> <label hidden="YES" opaque="NO" userInteractionEnabled="NO" tag="40" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" text="No log selected" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="hvz-CS-NME" userLabel="emptyLabel">
<rect key="frame" x="0.0" y="66" width="667" height="201"/> <rect key="frame" x="0.0" y="65" width="389" height="670"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" systemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/> <color key="backgroundColor" systemColor="systemBackgroundColor"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/> <fontDescription key="fontDescription" type="system" pointSize="17"/>
<nil key="textColor"/> <nil key="textColor"/>
<nil key="highlightedColor"/> <nil key="highlightedColor"/>
</label> </label>
<view hidden="YES" tag="8" contentMode="scaleToFill" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="dEJ-xc-518" userLabel="waitView"> <view hidden="YES" tag="8" contentMode="scaleToFill" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="dEJ-xc-518" userLabel="waitView">
<rect key="frame" x="0.0" y="0.0" width="667" height="267"/> <rect key="frame" x="0.0" y="0.0" width="389" height="735"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews> <subviews>
<activityIndicatorView opaque="NO" tag="9" contentMode="scaleToFill" fixedFrame="YES" animating="YES" style="gray" translatesAutoresizingMaskIntoConstraints="NO" id="NK3-ME-9jd" userLabel="activityIndicatorView"> <activityIndicatorView opaque="NO" tag="9" contentMode="scaleToFill" fixedFrame="YES" animating="YES" style="gray" translatesAutoresizingMaskIntoConstraints="NO" id="NK3-ME-9jd" userLabel="activityIndicatorView">
<rect key="frame" x="326" y="122" width="20" height="20"/> <rect key="frame" x="186" y="352" width="20" height="21"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMinY="YES" flexibleMaxY="YES"/> <autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMinY="YES" flexibleMaxY="YES"/>
</activityIndicatorView> </activityIndicatorView>
</subviews> </subviews>
<color key="backgroundColor" systemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/> <color key="backgroundColor" systemColor="systemBackgroundColor"/>
<gestureRecognizers/> <gestureRecognizers/>
</view> </view>
</subviews> </subviews>
@ -193,23 +197,23 @@
<point key="canvasLocation" x="-3.2000000000000002" y="22.488755622188908"/> <point key="canvasLocation" x="-3.2000000000000002" y="22.488755622188908"/>
</view> </view>
<view contentMode="scaleToFill" id="LBc-mh-ozk" userLabel="iphone6MetricsView"> <view contentMode="scaleToFill" id="LBc-mh-ozk" userLabel="iphone6MetricsView">
<rect key="frame" x="0.0" y="0.0" width="667" height="375"/> <rect key="frame" x="0.0" y="0.0" width="390" height="844"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/> <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<subviews> <subviews>
<view tag="1" contentMode="scaleToFill" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="NHC-7w-48z"> <view tag="1" contentMode="scaleToFill" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="NHC-7w-48z">
<rect key="frame" x="0.0" y="42" width="667" height="333"/> <rect key="frame" x="-2" y="42" width="391" height="800"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews> <subviews>
<view tag="2" contentMode="scaleToFill" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Rtv-hu-bCz" userLabel="topBar"> <view tag="2" contentMode="scaleToFill" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Rtv-hu-bCz" userLabel="topBar">
<rect key="frame" x="0.0" y="0.0" width="667" height="66"/> <rect key="frame" x="0.0" y="0.0" width="391" height="66"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxY="YES"/> <autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxY="YES"/>
<subviews> <subviews>
<imageView userInteractionEnabled="NO" tag="3" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" image="imageView:JOe-5t-C7f:image" translatesAutoresizingMaskIntoConstraints="NO" id="JOe-5t-C7f" userLabel="backgroundColor"> <imageView userInteractionEnabled="NO" tag="3" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" image="imageView:JOe-5t-C7f:image" translatesAutoresizingMaskIntoConstraints="NO" id="JOe-5t-C7f" userLabel="backgroundColor">
<rect key="frame" x="0.0" y="0.0" width="667" height="66"/> <rect key="frame" x="0.0" y="0.0" width="391" height="65"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" widthSizable="YES" flexibleMaxX="YES" heightSizable="YES"/> <autoresizingMask key="autoresizingMask" flexibleMinX="YES" widthSizable="YES" flexibleMaxX="YES" heightSizable="YES"/>
</imageView> </imageView>
<button opaque="NO" tag="4" contentMode="scaleToFill" fixedFrame="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" adjustsImageWhenHighlighted="NO" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="NJl-Lb-CU6" userLabel="backButton" customClass="UIIconButton"> <button opaque="NO" tag="4" contentMode="scaleToFill" fixedFrame="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" adjustsImageWhenHighlighted="NO" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="NJl-Lb-CU6" userLabel="backButton" customClass="UIIconButton">
<rect key="frame" x="0.0" y="0.0" width="71" height="66"/> <rect key="frame" x="0.0" y="0.0" width="41" height="65"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" widthSizable="YES" flexibleMaxX="YES" heightSizable="YES"/> <autoresizingMask key="autoresizingMask" flexibleMinX="YES" widthSizable="YES" flexibleMaxX="YES" heightSizable="YES"/>
<accessibility key="accessibilityConfiguration" label="Back"/> <accessibility key="accessibilityConfiguration" label="Back"/>
<inset key="titleEdgeInsets" minX="0.0" minY="18" maxX="0.0" maxY="0.0"/> <inset key="titleEdgeInsets" minX="0.0" minY="18" maxX="0.0" maxY="0.0"/>
@ -221,7 +225,7 @@
</connections> </connections>
</button> </button>
<button opaque="NO" tag="5" contentMode="scaleToFill" fixedFrame="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" adjustsImageWhenHighlighted="NO" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="O7r-6t-b7w" userLabel="addButton" customClass="UIIconButton"> <button opaque="NO" tag="5" contentMode="scaleToFill" fixedFrame="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" adjustsImageWhenHighlighted="NO" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="O7r-6t-b7w" userLabel="addButton" customClass="UIIconButton">
<rect key="frame" x="594" y="0.0" width="73" height="66"/> <rect key="frame" x="346" y="0.0" width="45" height="65"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" widthSizable="YES" flexibleMaxX="YES" heightSizable="YES"/> <autoresizingMask key="autoresizingMask" flexibleMinX="YES" widthSizable="YES" flexibleMaxX="YES" heightSizable="YES"/>
<accessibility key="accessibilityConfiguration" label="Add to contact"/> <accessibility key="accessibilityConfiguration" label="Add to contact"/>
<inset key="titleEdgeInsets" minX="0.0" minY="18" maxX="0.0" maxY="0.0"/> <inset key="titleEdgeInsets" minX="0.0" minY="18" maxX="0.0" maxY="0.0"/>
@ -235,7 +239,7 @@
</subviews> </subviews>
</view> </view>
<tableView clipsSubviews="YES" tag="6" contentMode="scaleToFill" fixedFrame="YES" alwaysBounceVertical="YES" style="plain" separatorStyle="none" allowsSelection="NO" rowHeight="30" sectionHeaderHeight="44" sectionFooterHeight="22" translatesAutoresizingMaskIntoConstraints="NO" id="2jK-gw-ULv"> <tableView clipsSubviews="YES" tag="6" contentMode="scaleToFill" fixedFrame="YES" alwaysBounceVertical="YES" style="plain" separatorStyle="none" allowsSelection="NO" rowHeight="30" sectionHeaderHeight="44" sectionFooterHeight="22" translatesAutoresizingMaskIntoConstraints="NO" id="2jK-gw-ULv">
<rect key="frame" x="0.0" y="168" width="667" height="165"/> <rect key="frame" x="0.0" y="167" width="391" height="633"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" widthSizable="YES" flexibleMaxX="YES" heightSizable="YES"/> <autoresizingMask key="autoresizingMask" flexibleMinX="YES" widthSizable="YES" flexibleMaxX="YES" heightSizable="YES"/>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> <color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<connections> <connections>
@ -244,11 +248,11 @@
</connections> </connections>
</tableView> </tableView>
<view tag="7" contentMode="scaleToFill" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Mwp-y3-g1b" userLabel="headerView"> <view tag="7" contentMode="scaleToFill" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Mwp-y3-g1b" userLabel="headerView">
<rect key="frame" x="0.0" y="66" width="667" height="102"/> <rect key="frame" x="0.0" y="66" width="391" height="102"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" widthSizable="YES" flexibleMaxX="YES" flexibleMaxY="YES"/> <autoresizingMask key="autoresizingMask" flexibleMinX="YES" widthSizable="YES" flexibleMaxX="YES" flexibleMaxY="YES"/>
<subviews> <subviews>
<imageView userInteractionEnabled="NO" tag="8" contentMode="scaleAspectFit" fixedFrame="YES" image="avatar.png" translatesAutoresizingMaskIntoConstraints="NO" id="d9m-G0-1u3" userLabel="avatarImage" customClass="UIRoundedImageView"> <imageView userInteractionEnabled="NO" tag="8" contentMode="scaleAspectFit" fixedFrame="YES" image="avatar.png" translatesAutoresizingMaskIntoConstraints="NO" id="d9m-G0-1u3" userLabel="avatarImage" customClass="UIRoundedImageView">
<rect key="frame" x="28" y="8" width="88" height="86"/> <rect key="frame" x="16" y="8" width="51" height="86"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" widthSizable="YES" flexibleMaxX="YES" flexibleMaxY="YES"/> <autoresizingMask key="autoresizingMask" flexibleMinX="YES" widthSizable="YES" flexibleMaxX="YES" flexibleMaxY="YES"/>
<accessibility key="accessibilityConfiguration" label="Contact avatar"> <accessibility key="accessibilityConfiguration" label="Contact avatar">
<accessibilityTraits key="traits" image="YES" notEnabled="YES"/> <accessibilityTraits key="traits" image="YES" notEnabled="YES"/>
@ -256,7 +260,7 @@
</accessibility> </accessibility>
</imageView> </imageView>
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" tag="9" contentMode="left" fixedFrame="YES" text="John Doe" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="Qbg-hm-bd7" userLabel="contactLabel"> <label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" tag="9" contentMode="left" fixedFrame="YES" text="John Doe" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="Qbg-hm-bd7" userLabel="contactLabel">
<rect key="frame" x="160" y="8" width="356" height="50"/> <rect key="frame" x="94" y="8" width="207" height="48"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" widthSizable="YES" flexibleMaxX="YES" heightSizable="YES" flexibleMaxY="YES"/> <autoresizingMask key="autoresizingMask" flexibleMinX="YES" widthSizable="YES" flexibleMaxX="YES" heightSizable="YES" flexibleMaxY="YES"/>
<accessibility key="accessibilityConfiguration" label="Contact name"/> <accessibility key="accessibilityConfiguration" label="Contact name"/>
<fontDescription key="fontDescription" type="system" pointSize="33"/> <fontDescription key="fontDescription" type="system" pointSize="33"/>
@ -264,7 +268,7 @@
<nil key="highlightedColor"/> <nil key="highlightedColor"/>
</label> </label>
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" tag="11" contentMode="left" fixedFrame="YES" text="johndoe@sip.linphone.org" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumFontSize="10" translatesAutoresizingMaskIntoConstraints="NO" id="XJa-f6-K0y" userLabel="addressLabel"> <label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" tag="11" contentMode="left" fixedFrame="YES" text="johndoe@sip.linphone.org" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumFontSize="10" translatesAutoresizingMaskIntoConstraints="NO" id="XJa-f6-K0y" userLabel="addressLabel">
<rect key="frame" x="160" y="56" width="356" height="38"/> <rect key="frame" x="94" y="54" width="207" height="39"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" widthSizable="YES" flexibleMaxX="YES" heightSizable="YES" flexibleMaxY="YES"/> <autoresizingMask key="autoresizingMask" flexibleMinX="YES" widthSizable="YES" flexibleMaxX="YES" heightSizable="YES" flexibleMaxY="YES"/>
<accessibility key="accessibilityConfiguration" label="Contact name"/> <accessibility key="accessibilityConfiguration" label="Contact name"/>
<fontDescription key="fontDescription" type="system" pointSize="18"/> <fontDescription key="fontDescription" type="system" pointSize="18"/>
@ -272,11 +276,11 @@
<nil key="highlightedColor"/> <nil key="highlightedColor"/>
</label> </label>
<view contentMode="scaleToFill" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="teU-AB-8hO" userLabel="optionsView"> <view contentMode="scaleToFill" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="teU-AB-8hO" userLabel="optionsView">
<rect key="frame" x="0.0" y="29" width="667" height="44"/> <rect key="frame" x="0.0" y="29" width="391" height="44"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" widthSizable="YES" flexibleMaxX="YES" flexibleMaxY="YES"/> <autoresizingMask key="autoresizingMask" flexibleMinX="YES" widthSizable="YES" flexibleMaxX="YES" flexibleMaxY="YES"/>
<subviews> <subviews>
<button opaque="NO" tag="13" contentMode="scaleAspectFit" fixedFrame="YES" contentHorizontalAlignment="left" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="pBo-Oo-bAW" userLabel="callButton"> <button opaque="NO" tag="13" contentMode="scaleAspectFit" fixedFrame="YES" contentHorizontalAlignment="left" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="pBo-Oo-bAW" userLabel="callButton">
<rect key="frame" x="491" y="0.0" width="51" height="51"/> <rect key="frame" x="271" y="0.0" width="51" height="51"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMaxY="YES"/> <autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMaxY="YES"/>
<state key="normal" image="call_start_body_default.png"> <state key="normal" image="call_start_body_default.png">
<color key="titleShadowColor" red="0.5" green="0.5" blue="0.5" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> <color key="titleShadowColor" red="0.5" green="0.5" blue="0.5" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
@ -288,7 +292,7 @@
</connections> </connections>
</button> </button>
<button opaque="NO" tag="12" contentMode="scaleAspectFit" fixedFrame="YES" contentHorizontalAlignment="left" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="iDG-Mn-jm2" userLabel="chatButton"> <button opaque="NO" tag="12" contentMode="scaleAspectFit" fixedFrame="YES" contentHorizontalAlignment="left" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="iDG-Mn-jm2" userLabel="chatButton">
<rect key="frame" x="551" y="0.0" width="51" height="51"/> <rect key="frame" x="303" y="0.0" width="51" height="51"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMaxY="YES"/> <autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMaxY="YES"/>
<state key="normal" image="chat_start_body_default.png"> <state key="normal" image="chat_start_body_default.png">
<color key="titleShadowColor" red="0.5" green="0.5" blue="0.5" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> <color key="titleShadowColor" red="0.5" green="0.5" blue="0.5" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
@ -300,11 +304,11 @@
</connections> </connections>
</button> </button>
<view contentMode="scaleToFill" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="m90-u6-x3J" userLabel="encryptedChatView"> <view contentMode="scaleToFill" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="m90-u6-x3J" userLabel="encryptedChatView">
<rect key="frame" x="611" y="0.0" width="51" height="51"/> <rect key="frame" x="336" y="-1" width="51" height="51"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMinY="YES"/> <autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMinY="YES"/>
<subviews> <subviews>
<button opaque="NO" contentMode="scaleAspectFit" fixedFrame="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="8fY-hz-ECC" userLabel="encryptedChatButton" customClass="UIIconButton"> <button opaque="NO" contentMode="scaleAspectFit" fixedFrame="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="8fY-hz-ECC" userLabel="encryptedChatButton" customClass="UIIconButton">
<rect key="frame" x="0.0" y="0.0" width="51" height="51"/> <rect key="frame" x="0.0" y="-2" width="51" height="53"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMinY="YES"/> <autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMinY="YES"/>
<accessibility key="accessibilityConfiguration" label="Chat"/> <accessibility key="accessibilityConfiguration" label="Chat"/>
<state key="normal" image="chat_start_body_default.png"> <state key="normal" image="chat_start_body_default.png">
@ -325,7 +329,7 @@
</subviews> </subviews>
</view> </view>
<imageView hidden="YES" userInteractionEnabled="NO" tag="10" contentMode="scaleAspectFit" fixedFrame="YES" image="linphone_user.png" translatesAutoresizingMaskIntoConstraints="NO" id="G2O-Yh-fZA" userLabel="linphoneImage"> <imageView hidden="YES" userInteractionEnabled="NO" tag="10" contentMode="scaleAspectFit" fixedFrame="YES" image="linphone_user.png" translatesAutoresizingMaskIntoConstraints="NO" id="G2O-Yh-fZA" userLabel="linphoneImage">
<rect key="frame" x="123" y="8" width="30" height="25"/> <rect key="frame" x="71" y="8" width="19" height="24"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" widthSizable="YES" flexibleMaxX="YES" heightSizable="YES" flexibleMaxY="YES"/> <autoresizingMask key="autoresizingMask" flexibleMinX="YES" widthSizable="YES" flexibleMaxX="YES" heightSizable="YES" flexibleMaxY="YES"/>
<accessibility key="accessibilityConfiguration" label="Contact avatar"> <accessibility key="accessibilityConfiguration" label="Contact avatar">
<accessibilityTraits key="traits" image="YES" notEnabled="YES"/> <accessibilityTraits key="traits" image="YES" notEnabled="YES"/>
@ -336,7 +340,7 @@
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> <color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</view> </view>
<label hidden="YES" opaque="NO" userInteractionEnabled="NO" tag="40" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" text="No log selected" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="IHY-Yg-pkN" userLabel="emptyLabel"> <label hidden="YES" opaque="NO" userInteractionEnabled="NO" tag="40" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" text="No log selected" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="IHY-Yg-pkN" userLabel="emptyLabel">
<rect key="frame" x="0.0" y="66" width="667" height="267"/> <rect key="frame" x="0.0" y="65" width="391" height="735"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> <color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/> <fontDescription key="fontDescription" type="system" pointSize="17"/>
@ -344,11 +348,11 @@
<nil key="highlightedColor"/> <nil key="highlightedColor"/>
</label> </label>
<view hidden="YES" tag="8" contentMode="scaleToFill" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="X29-vB-VIz" userLabel="waitView"> <view hidden="YES" tag="8" contentMode="scaleToFill" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="X29-vB-VIz" userLabel="waitView">
<rect key="frame" x="0.0" y="0.0" width="667" height="333"/> <rect key="frame" x="0.0" y="0.0" width="391" height="800"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews> <subviews>
<activityIndicatorView opaque="NO" tag="9" contentMode="scaleToFill" fixedFrame="YES" animating="YES" style="gray" translatesAutoresizingMaskIntoConstraints="NO" id="7l5-ZU-CbW" userLabel="activityIndicatorView"> <activityIndicatorView opaque="NO" tag="9" contentMode="scaleToFill" fixedFrame="YES" animating="YES" style="gray" translatesAutoresizingMaskIntoConstraints="NO" id="7l5-ZU-CbW" userLabel="activityIndicatorView">
<rect key="frame" x="326" y="155" width="20" height="20"/> <rect key="frame" x="185" y="385" width="21" height="20"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMinY="YES" flexibleMaxY="YES"/> <autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMinY="YES" flexibleMaxY="YES"/>
</activityIndicatorView> </activityIndicatorView>
</subviews> </subviews>
@ -384,106 +388,113 @@
<image name="contact_add_disabled.png" width="55.200000762939453" height="47.200000762939453"/> <image name="contact_add_disabled.png" width="55.200000762939453" height="47.200000762939453"/>
<image name="imageView:JOe-5t-C7f:image" width="2" height="2"> <image name="imageView:JOe-5t-C7f:image" width="2" height="2">
<mutableData key="keyedArchiveRepresentation"> <mutableData key="keyedArchiveRepresentation">
YnBsaXN0MDDUAQIDBAUGUlNYJHZlcnNpb25YJG9iamVjdHNZJGFyY2hpdmVyVCR0b3ASAAGGoK8QEQcI YnBsaXN0MDDUAQIDBAUGBwpYJHZlcnNpb25ZJGFyY2hpdmVyVCR0b3BYJG9iamVjdHMSAAGGoF8QD05T
ERYbHCAhKCsuOEBESExPVSRudWxs1AkKCwwNDg8QViRjbGFzc1xOU0ltYWdlRmxhZ3NWTlNSZXBzV05T S2V5ZWRBcmNoaXZlctEICVRyb290gAGvEBALDBccEyEmJy4xND5GR0tOVSRudWxs1Q0ODxAREhMUFRZW
Q29sb3KAEBIAwAAAgAKACtISCRMVWk5TLm9iamVjdHOhFIADgAnSEgkXGqIYGYAEgAWACBAA0h0JHh9f JGNsYXNzXk5TUmVzaXppbmdNb2RlXE5TSW1hZ2VGbGFnc1ZOU1JlcHNXTlNDb2xvcoAPEAASAMAAAIAC
EBROU1RJRkZSZXByZXNlbnRhdGlvboAGgAdPEQI+TU0AKgAAAAzh4eHhAA8BAAADAAAAAQACAAABAQAD gArSGA0ZG1pOUy5vYmplY3RzoRqAA4AJ0hgNHSCiHh+ABIAFgAjTDSIjJCUTXxAUTlNUSUZGUmVwcmVz
AAAAAQACAAABAgADAAAAAQAIAAABAwADAAAAAQABAAABBgADAAAAAQABAAABCgADAAAAAQABAAABEQAE ZW50YXRpb25fEBlOU0ludGVybmFsTGF5b3V0RGlyZWN0aW9ugAeABk8RAj5NTQAqAAAADOHh4eEADwEA
AAAAAQAAAAgBEgADAAAAAQABAAABFQADAAAAAQABAAABFgADAAAAAQACAAABFwAEAAAAAQAAAAQBHAAD AAMAAAABAAIAAAEBAAMAAAABAAIAAAECAAMAAAABAAgAAAEDAAMAAAABAAEAAAEGAAMAAAABAAEAAAEK
AAAAAQABAAABKAADAAAAAQACAAABUwADAAAAAQABAACHcwAHAAABeAAAAMYAAAAAAAABeGFwcGwCEAAA AAMAAAABAAEAAAERAAQAAAABAAAACAESAAMAAAABAAEAAAEVAAMAAAABAAEAAAEWAAMAAAABAAIAAAEX
bW50ckdSQVlYWVogB9UABwABAAAAAAAAYWNzcEFQUEwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPbW AAQAAAABAAAABAEcAAMAAAABAAEAAAEoAAMAAAABAAIAAAFTAAMAAAABAAEAAIdzAAcAAAF4AAAAxgAA
AAEAAAAA0y1hcHBsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAE AAAAAAF4YXBwbAIQAABtbnRyR1JBWVhZWiAH1QAHAAEAAAAAAABhY3NwQVBQTAAAAAAAAAAAAAAAAAAA
ZGVzYwAAALQAAAB1Y3BydAAAASwAAAAnd3RwdAAAAVQAAAAUa1RSQwAAAWgAAAAOZGVzYwAAAAAAAAAb AAAAAAAAAAAAAAAA9tYAAQAAAADTLWFwcGwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
Q2FsaWJyYXRlZCBHcmF5IENvbG9yc3BhY2UAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAARkZXNjAAAAtAAAAHVjcHJ0AAABLAAAACd3dHB0AAABVAAAABRrVFJDAAABaAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAdGV4dAAAAABDb3B5 AA5kZXNjAAAAAAAAABtDYWxpYnJhdGVkIEdyYXkgQ29sb3JzcGFjZQAAAAAAAAAAAAAAAAAAAAAAAAAA
cmlnaHQgQXBwbGUgQ29tcHV0ZXIsIEluYy4AAFhZWiAAAAAAAADzUQABAAAAARbMY3VydgAAAAAAAAAB
AjMAANIiIyQlWiRjbGFzc25hbWVYJGNsYXNzZXNfEBBOU0JpdG1hcEltYWdlUmVwoyQmJ1pOU0ltYWdl
UmVwWE5TT2JqZWN00iIjKSpXTlNBcnJheaIpJ9IiIywtXk5TTXV0YWJsZUFycmF5oywpJ9UvMDEyCTM0
NTY3V05TV2hpdGVcTlNDb21wb25lbnRzXE5TQ29sb3JTcGFjZV8QEk5TQ3VzdG9tQ29sb3JTcGFjZUQw
IDAAQzAgMBADgAuAD9Q5OjsJPD0+P1ROU0lEVU5TSUNDV05TTW9kZWwQCYAMEACADtJBCUJDV05TLmRh
dGFPERFoAAARaGFwcGwCAAAAbW50ckdSQVlYWVogB9wACAAXAA8ALgAPYWNzcEFQUEwAAAAAbm9uZQAA
AAAAAAAAAAAAAAAAAAAAAPbWAAEAAAAA0y1hcHBsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAFZGVzYwAAAMAAAAB5ZHNjbQAAATwAAAfoY3BydAAACSQAAAAjd3RwdAAA
CUgAAAAUa1RSQwAACVwAAAgMZGVzYwAAAAAAAAAfR2VuZXJpYyBHcmF5IEdhbW1hIDIuMiBQcm9maWxl
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAG1sdWMAAAAAAAAAHwAAAAxza1NLAAAALgAAAYRkYURLAAAAOAAA AAB0ZXh0AAAAAENvcHlyaWdodCBBcHBsZSBDb21wdXRlciwgSW5jLgAAWFlaIAAAAAAAAPNRAAEAAAAB
AbJjYUVTAAAAOAAAAep2aVZOAAAAQAAAAiJwdEJSAAAASgAAAmJ1a1VBAAAALAAAAqxmckZVAAAAPgAA FsxjdXJ2AAAAAAAAAAECMwAA0igpKitaJGNsYXNzbmFtZVgkY2xhc3Nlc18QEE5TQml0bWFwSW1hZ2VS
AthodUhVAAAANAAAAxZ6aFRXAAAAHgAAA0puYk5PAAAAOgAAA2hjc0NaAAAAKAAAA6JoZUlMAAAAJAAA ZXCjKiwtWk5TSW1hZ2VSZXBYTlNPYmplY3TSKCkvMFdOU0FycmF5oi8t0igpMjNeTlNNdXRhYmxlQXJy
A8ppdElUAAAATgAAA+5yb1JPAAAAKgAABDxkZURFAAAATgAABGZrb0tSAAAAIgAABLRzdlNFAAAAOAAA YXmjMi8t1TU2NzgNOTo7PD1XTlNXaGl0ZVxOU0NvbXBvbmVudHNcTlNDb2xvclNwYWNlXxASTlNDdXN0
AbJ6aENOAAAAHgAABNZqYUpQAAAAJgAABPRlbEdSAAAAKgAABRpwdFBPAAAAUgAABURubE5MAAAAQAAA b21Db2xvclNwYWNlRDAgMABDMCAwEAOAC4AO1D9AQQ1CQ0RFVE5TSURVTlNJQ0NXTlNNb2RlbBAJgAwQ
BZZlc0VTAAAATAAABdZ0aFRIAAAAMgAABiJ0clRSAAAAJAAABlRmaUZJAAAARgAABnhockhSAAAAPgAA AIANTxERnAAAEZxhcHBsAgAAAG1udHJHUkFZWFlaIAfcAAgAFwAPAC4AD2Fjc3BBUFBMAAAAAG5vbmUA
Br5wbFBMAAAASgAABvxydVJVAAAAOgAAB0ZlblVTAAAAPAAAB4BhckVHAAAALAAAB7wAVgFhAGUAbwBi AAAAAAAAAAAAAAAAAAAAAAD21gABAAAAANMtYXBwbAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AGUAYwBuAOEAIABzAGkAdgDhACAAZwBhAG0AYQAgADIALAAyAEcAZQBuAGUAcgBpAHMAawAgAGcAcgDl AAAAAAAAAAAAAAAAAAAAAAAABWRlc2MAAADAAAAAeWRzY20AAAE8AAAIGmNwcnQAAAlYAAAAI3d0cHQA
ACAAMgAsADIAIABnAGEAbQBtAGEAcAByAG8AZgBpAGwARwBhAG0AbQBhACAAZABlACAAZwByAGkAcwBv AAl8AAAAFGtUUkMAAAmQAAAIDGRlc2MAAAAAAAAAH0dlbmVyaWMgR3JheSBHYW1tYSAyLjIgUHJvZmls
AHMAIABnAGUAbgDoAHIAaQBjAGEAIAAyAC4AMgBDHqUAdQAgAGgA7ABuAGgAIABNAOAAdQAgAHgA4QBt ZQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
ACAAQwBoAHUAbgBnACAARwBhAG0AbQBhACAAMgAuADIAUABlAHIAZgBpAGwAIABHAGUAbgDpAHIAaQBj AAAAAAAAAAAAAAAAAAAAAAAAAAAAAABtbHVjAAAAAAAAAB8AAAAMc2tTSwAAAC4AAAGEZGFESwAAADoA
AG8AIABkAGEAIABHAGEAbQBhACAAZABlACAAQwBpAG4AegBhAHMAIAAyACwAMgQXBDAEMwQwBDsETAQ9 AAGyY2FFUwAAADgAAAHsdmlWTgAAAEAAAAIkcHRCUgAAAEoAAAJkdWtVQQAAACwAAAKuZnJGVQAAAD4A
BDAAIABHAHIAYQB5AC0EMwQwBDwEMAAgADIALgAyAFAAcgBvAGYAaQBsACAAZwDpAG4A6QByAGkAcQB1 AALaaHVIVQAAADQAAAMYemhUVwAAABoAAANMa29LUgAAACIAAANmbmJOTwAAADoAAAOIY3NDWgAAACgA
AGUAIABnAHIAaQBzACAAZwBhAG0AbQBhACAAMgAsADIAwQBsAHQAYQBsAOEAbgBvAHMAIABzAHoA/ABy AAPCaGVJTAAAACQAAAPqcm9STwAAACoAAAQOZGVERQAAAE4AAAQ4aXRJVAAAAE4AAASGc3ZTRQAAADgA
AGsAZQAgAGcAYQBtAG0AYQAgADIALgAykBp1KHBwlo5RSV6mACAAMgAuADIAIIJyX2ljz4/wAEcAZQBu AATUemhDTgAAABoAAAUMamFKUAAAACYAAAUmZWxHUgAAACoAAAVMcHRQTwAAAFIAAAV2bmxOTAAAAEAA
AGUAcgBpAHMAawAgAGcAcgDlACAAZwBhAG0AbQBhACAAMgAsADIALQBwAHIAbwBmAGkAbABPAGIAZQBj AAXIZXNFUwAAAEwAAAYIdGhUSAAAADIAAAZUdHJUUgAAACQAAAaGZmlGSQAAAEYAAAaqaHJIUgAAAD4A
AG4A4QAgAWEAZQBkAOEAIABnAGEAbQBhACAAMgAuADIF0gXQBd4F1AAgBdAF5AXVBegAIAXbBdwF3AXZ AAbwcGxQTAAAAEoAAAcuYXJFRwAAACwAAAd4cnVSVQAAADoAAAekZW5VUwAAADwAAAfeAFYBYQBlAG8A
ACAAMgAuADIAUAByAG8AZgBpAGwAbwAgAGcAcgBpAGcAaQBvACAAZwBlAG4AZQByAGkAYwBvACAAZABl YgBlAGMAbgDhACAAcwBpAHYA4QAgAGcAYQBtAGEAIAAyACwAMgBHAGUAbgBlAHIAaQBzAGsAIABnAHIA
AGwAbABhACAAZwBhAG0AbQBhACAAMgAsADIARwBhAG0AYQAgAGcAcgBpACAAZwBlAG4AZQByAGkAYwED 5QAgADIALAAyACAAZwBhAG0AbQBhAC0AcAByAG8AZgBpAGwARwBhAG0AbQBhACAAZABlACAAZwByAGkA
ACAAMgAsADIAQQBsAGwAZwBlAG0AZQBpAG4AZQBzACAARwByAGEAdQBzAHQAdQBmAGUAbgAtAFAAcgBv cwBvAHMAIABnAGUAbgDoAHIAaQBjAGEAIAAyAC4AMgBDHqUAdQAgAGgA7ABuAGgAIABNAOAAdQAgAHgA
AGYAaQBsACAARwBhAG0AbQBhACAAMgAsADLHfLwYACDWjMDJACCsELnIACAAMgAuADIAINUEuFzTDMd8 4QBtACAAQwBoAHUAbgBnACAARwBhAG0AbQBhACAAMgAuADIAUABlAHIAZgBpAGwAIABHAGUAbgDpAHIA
Zm6QGnBwXqZ8+2VwACAAMgAuADIAIGPPj/Blh072TgCCLDCwMOwwpDCsMPMw3gAgADIALgAyACAw1zDt aQBjAG8AIABkAGEAIABHAGEAbQBhACAAZABlACAAQwBpAG4AegBhAHMAIAAyACwAMgQXBDAEMwQwBDsE
MNUwoTCkMOsDkwO1A70DuQO6A8wAIAOTA7oDwQO5ACADkwOsA7wDvAOxACAAMgAuADIAUABlAHIAZgBp TAQ9BDAAIABHAHIAYQB5AC0EMwQwBDwEMAAgADIALgAyAFAAcgBvAGYAaQBsACAAZwDpAG4A6QByAGkA
AGwAIABnAGUAbgDpAHIAaQBjAG8AIABkAGUAIABjAGkAbgB6AGUAbgB0AG8AcwAgAGQAYQAgAEcAYQBt cQB1AGUAIABnAHIAaQBzACAAZwBhAG0AbQBhACAAMgAsADIAwQBsAHQAYQBsAOEAbgBvAHMAIABzAHoA
AG0AYQAgADIALAAyAEEAbABnAGUAbQBlAGUAbgAgAGcAcgBpAGoAcwAgAGcAYQBtAG0AYQAgADIALAAy /AByAGsAZQAgAGcAYQBtAG0AYQAgADIALgAykBp1KHBwlo5RSV6mADIALgAygnJfaWPPj/DHfLwYACDW
AC0AcAByAG8AZgBpAGUAbABQAGUAcgBmAGkAbAAgAGcAZQBuAOkAcgBpAGMAbwAgAGQAZQAgAGcAYQBt jMDJACCsELnIACAAMgAuADIAINUEuFzTDMd8AEcAZQBuAGUAcgBpAHMAawAgAGcAcgDlACAAZwBhAG0A
AG0AYQAgAGQAZQAgAGcAcgBpAHMAZQBzACAAMgAsADIOIw4xDgcOKg41DkEOAQ4hDiEOMg5ADgEOIw4i bQBhACAAMgAsADIALQBwAHIAbwBmAGkAbABPAGIAZQBjAG4A4QAgAWEAZQBkAOEAIABnAGEAbQBhACAA
DkwOFw4xDkgOJw5EDhsAIAAyAC4AMgBHAGUAbgBlAGwAIABHAHIAaQAgAEcAYQBtAGEAIAAyACwAMgBZ MgAuADIF0gXQBd4F1AAgBdAF5AXVBegAIAXbBdwF3AXZACAAMgAuADIARwBhAG0AYQAgAGcAcgBpACAA
AGwAZQBpAG4AZQBuACAAaABhAHIAbQBhAGEAbgAgAGcAYQBtAG0AYQAgADIALAAyACAALQBwAHIAbwBm ZwBlAG4AZQByAGkAYwEDACAAMgAsADIAQQBsAGwAZwBlAG0AZQBpAG4AZQBzACAARwByAGEAdQBzAHQA
AGkAaQBsAGkARwBlAG4AZQByAGkBDQBrAGkAIABHAHIAYQB5ACAARwBhAG0AbQBhACAAMgAuADIAIABw dQBmAGUAbgAtAFAAcgBvAGYAaQBsACAARwBhAG0AbQBhACAAMgAsADIAUAByAG8AZgBpAGwAbwAgAGcA
AHIAbwBmAGkAbABVAG4AaQB3AGUAcgBzAGEAbABuAHkAIABwAHIAbwBmAGkAbAAgAHMAegBhAHIAbwFb cgBpAGcAaQBvACAAZwBlAG4AZQByAGkAYwBvACAAZABlAGwAbABhACAAZwBhAG0AbQBhACAAMgAsADIA
AGMAaQAgAGcAYQBtAG0AYQAgADIALAAyBB4EMQRJBDAETwAgBEEENQRABDAETwAgBDMEMAQ8BDwEMAAg RwBlAG4AZQByAGkAcwBrACAAZwByAOUAIAAyACwAMgAgAGcAYQBtAG0AYQBwAHIAbwBmAGkAbGZukBpw
ADIALAAyAC0EPwRABD4ERAQ4BDsETABHAGUAbgBlAHIAaQBjACAARwByAGEAeQAgAEcAYQBtAG0AYQAg cF6mfPtlcAAyAC4AMmPPj/Blh072TgCCLDCwMOwwpDCsMPMw3gAgADIALgAyACAw1zDtMNUwoTCkMOsD
ADIALgAyACAAUAByAG8AZgBpAGwAZQY6BicGRQYnACAAMgAuADIAIAZEBkgGRgAgBjEGRQYnBi8GSgAg kwO1A70DuQO6A8wAIAOTA7oDwQO5ACADkwOsA7wDvAOxACAAMgAuADIAUABlAHIAZgBpAGwAIABnAGUA
BjkGJwZFdGV4dAAAAABDb3B5cmlnaHQgQXBwbGUgSW5jLiwgMjAxMgAAWFlaIAAAAAAAAPNRAAEAAAAB bgDpAHIAaQBjAG8AIABkAGUAIABjAGkAbgB6AGUAbgB0AG8AcwAgAGQAYQAgAEcAYQBtAG0AYQAgADIA
FsxjdXJ2AAAAAAAABAAAAAAFAAoADwAUABkAHgAjACgALQAyADcAOwBAAEUASgBPAFQAWQBeAGMAaABt LAAyAEEAbABnAGUAbQBlAGUAbgAgAGcAcgBpAGoAcwAgAGcAYQBtAG0AYQAgADIALAAyAC0AcAByAG8A
AHIAdwB8AIEAhgCLAJAAlQCaAJ8ApACpAK4AsgC3ALwAwQDGAMsA0ADVANsA4ADlAOsA8AD2APsBAQEH ZgBpAGUAbABQAGUAcgBmAGkAbAAgAGcAZQBuAOkAcgBpAGMAbwAgAGQAZQAgAGcAYQBtAG0AYQAgAGQA
AQ0BEwEZAR8BJQErATIBOAE+AUUBTAFSAVkBYAFnAW4BdQF8AYMBiwGSAZoBoQGpAbEBuQHBAckB0QHZ ZQAgAGcAcgBpAHMAZQBzACAAMgAsADIOIw4xDgcOKg41DkEOAQ4hDiEOMg5ADgEOIw4iDkwOFw4xDkgO
AeEB6QHyAfoCAwIMAhQCHQImAi8COAJBAksCVAJdAmcCcQJ6AoQCjgKYAqICrAK2AsECywLVAuAC6wL1 Jw5EDhsAIAAyAC4AMgBHAGUAbgBlAGwAIABHAHIAaQAgAEcAYQBtAGEAIAAyACwAMgBZAGwAZQBpAG4A
AwADCwMWAyEDLQM4A0MDTwNaA2YDcgN+A4oDlgOiA64DugPHA9MD4APsA/kEBgQTBCAELQQ7BEgEVQRj ZQBuACAAaABhAHIAbQBhAGEAbgAgAGcAYQBtAG0AYQAgADIALAAyACAALQBwAHIAbwBmAGkAaQBsAGkA
BHEEfgSMBJoEqAS2BMQE0wThBPAE/gUNBRwFKwU6BUkFWAVnBXcFhgWWBaYFtQXFBdUF5QX2BgYGFgYn RwBlAG4AZQByAGkBDQBrAGkAIABHAHIAYQB5ACAARwBhAG0AbQBhACAAMgAuADIAIABwAHIAbwBmAGkA
BjcGSAZZBmoGewaMBp0GrwbABtEG4wb1BwcHGQcrBz0HTwdhB3QHhgeZB6wHvwfSB+UH+AgLCB8IMghG bABVAG4AaQB3AGUAcgBzAGEAbABuAHkAIABwAHIAbwBmAGkAbAAgAHMAegBhAHIAbwFbAGMAaQAgAGcA
CFoIbgiCCJYIqgi+CNII5wj7CRAJJQk6CU8JZAl5CY8JpAm6Cc8J5Qn7ChEKJwo9ClQKagqBCpgKrgrF YQBtAG0AYQAgADIALAAyBjoGJwZFBicAIAAyAC4AMgAgBkQGSAZGACAGMQZFBicGLwZKACAGOQYnBkUE
CtwK8wsLCyILOQtRC2kLgAuYC7ALyAvhC/kMEgwqDEMMXAx1DI4MpwzADNkM8w0NDSYNQA1aDXQNjg2p HgQxBEkEMARPACAEQQQ1BEAEMARPACAEMwQwBDwEPAQwACAAMgAsADIALQQ/BEAEPgREBDgEOwRMAEcA
DcMN3g34DhMOLg5JDmQOfw6bDrYO0g7uDwkPJQ9BD14Peg+WD7MPzw/sEAkQJhBDEGEQfhCbELkQ1xD1 ZQBuAGUAcgBpAGMAIABHAHIAYQB5ACAARwBhAG0AbQBhACAAMgAuADIAIABQAHIAbwBmAGkAbABlAAB0
ERMRMRFPEW0RjBGqEckR6BIHEiYSRRJkEoQSoxLDEuMTAxMjE0MTYxODE6QTxRPlFAYUJxRJFGoUixSt ZXh0AAAAAENvcHlyaWdodCBBcHBsZSBJbmMuLCAyMDEyAABYWVogAAAAAAAA81EAAQAAAAEWzGN1cnYA
FM4U8BUSFTQVVhV4FZsVvRXgFgMWJhZJFmwWjxayFtYW+hcdF0EXZReJF64X0hf3GBsYQBhlGIoYrxjV AAAAAAAEAAAAAAUACgAPABQAGQAeACMAKAAtADIANwA7AEAARQBKAE8AVABZAF4AYwBoAG0AcgB3AHwA
GPoZIBlFGWsZkRm3Gd0aBBoqGlEadxqeGsUa7BsUGzsbYxuKG7Ib2hwCHCocUhx7HKMczBz1HR4dRx1w gQCGAIsAkACVAJoAnwCkAKkArgCyALcAvADBAMYAywDQANUA2wDgAOUA6wDwAPYA+wEBAQcBDQETARkB
HZkdwx3sHhYeQB5qHpQevh7pHxMfPh9pH5Qfvx/qIBUgQSBsIJggxCDwIRwhSCF1IaEhziH7IiciVSKC HwElASsBMgE4AT4BRQFMAVIBWQFgAWcBbgF1AXwBgwGLAZIBmgGhAakBsQG5AcEByQHRAdkB4QHpAfIB
Iq8i3SMKIzgjZiOUI8Ij8CQfJE0kfCSrJNolCSU4JWgllyXHJfcmJyZXJocmtyboJxgnSSd6J6sn3CgN +gIDAgwCFAIdAiYCLwI4AkECSwJUAl0CZwJxAnoChAKOApgCogKsArYCwQLLAtUC4ALrAvUDAAMLAxYD
KD8ocSiiKNQpBik4KWspnSnQKgIqNSpoKpsqzysCKzYraSudK9EsBSw5LG4soizXLQwtQS12Last4S4W IQMtAzgDQwNPA1oDZgNyA34DigOWA6IDrgO6A8cD0wPgA+wD+QQGBBMEIAQtBDsESARVBGMEcQR+BIwE
Lkwugi63Lu4vJC9aL5Evxy/+MDUwbDCkMNsxEjFKMYIxujHyMioyYzKbMtQzDTNGM38zuDPxNCs0ZTSe mgSoBLYExATTBOEE8AT+BQ0FHAUrBToFSQVYBWcFdwWGBZYFpgW1BcUF1QXlBfYGBgYWBicGNwZIBlkG
NNg1EzVNNYc1wjX9Njc2cjauNuk3JDdgN5w31zgUOFA4jDjIOQU5Qjl/Obw5+To2OnQ6sjrvOy07azuq agZ7BowGnQavBsAG0QbjBvUHBwcZBysHPQdPB2EHdAeGB5kHrAe/B9IH5Qf4CAsIHwgyCEYIWghuCIII
O+g8JzxlPKQ84z0iPWE9oT3gPiA+YD6gPuA/IT9hP6I/4kAjQGRApkDnQSlBakGsQe5CMEJyQrVC90M6 lgiqCL4I0gjnCPsJEAklCToJTwlkCXkJjwmkCboJzwnlCfsKEQonCj0KVApqCoEKmAquCsUK3ArzCwsL
Q31DwEQDREdEikTORRJFVUWaRd5GIkZnRqtG8Ec1R3tHwEgFSEtIkUjXSR1JY0mpSfBKN0p9SsRLDEtT Igs5C1ELaQuAC5gLsAvIC+EL+QwSDCoMQwxcDHUMjgynDMAM2QzzDQ0NJg1ADVoNdA2ODakNww3eDfgO
S5pL4kwqTHJMuk0CTUpNk03cTiVObk63TwBPSU+TT91QJ1BxULtRBlFQUZtR5lIxUnxSx1MTU19TqlP2 Ew4uDkkOZA5/DpsOtg7SDu4PCQ8lD0EPXg96D5YPsw/PD+wQCRAmEEMQYRB+EJsQuRDXEPURExExEU8R
VEJUj1TbVShVdVXCVg9WXFapVvdXRFeSV+BYL1h9WMtZGllpWbhaB1pWWqZa9VtFW5Vb5Vw1XIZc1l0n bRGMEaoRyRHoEgcSJhJFEmQShBKjEsMS4xMDEyMTQxNjE4MTpBPFE+UUBhQnFEkUahSLFK0UzhTwFRIV
XXhdyV4aXmxevV8PX2Ffs2AFYFdgqmD8YU9homH1YklinGLwY0Njl2PrZEBklGTpZT1lkmXnZj1mkmbo NBVWFXgVmxW9FeAWAxYmFkkWbBaPFrIW1hb6Fx0XQRdlF4kXrhfSF/cYGxhAGGUYihivGNUY+hkgGUUZ
Zz1nk2fpaD9olmjsaUNpmmnxakhqn2r3a09rp2v/bFdsr20IbWBtuW4SbmtuxG8eb3hv0XArcIZw4HE6 axmRGbcZ3RoEGioaURp3Gp4axRrsGxQbOxtjG4obshvaHAIcKhxSHHscoxzMHPUdHh1HHXAdmR3DHewe
cZVx8HJLcqZzAXNdc7h0FHRwdMx1KHWFdeF2Pnabdvh3VnezeBF4bnjMeSp5iXnnekZ6pXsEe2N7wnwh Fh5AHmoelB6+HukfEx8+H2kflB+/H+ogFSBBIGwgmCDEIPAhHCFIIXUhoSHOIfsiJyJVIoIiryLdIwoj
fIF84X1BfaF+AX5ifsJ/I3+Ef+WAR4CogQqBa4HNgjCCkoL0g1eDuoQdhICE44VHhauGDoZyhteHO4ef OCNmI5QjwiPwJB8kTSR8JKsk2iUJJTglaCWXJccl9yYnJlcmhya3JugnGCdJJ3onqyfcKA0oPyhxKKIo
iASIaYjOiTOJmYn+imSKyoswi5aL/IxjjMqNMY2Yjf+OZo7OjzaPnpAGkG6Q1pE/kaiSEZJ6kuOTTZO2 1CkGKTgpaymdKdAqAio1KmgqmyrPKwIrNitpK50r0SwFLDksbiyiLNctDC1BLXYtqy3hLhYuTC6CLrcu
lCCUipT0lV+VyZY0lp+XCpd1l+CYTJi4mSSZkJn8mmia1ZtCm6+cHJyJnPedZJ3SnkCerp8dn4uf+qBp 7i8kL1ovkS/HL/4wNTBsMKQw2zESMUoxgjG6MfIyKjJjMpsy1DMNM0YzfzO4M/E0KzRlNJ402DUTNU01
oNihR6G2oiailqMGo3aj5qRWpMelOKWpphqmi6b9p26n4KhSqMSpN6mpqhyqj6sCq3Wr6axcrNCtRK24 hzXCNf02NzZyNq426TckN2A3nDfXOBQ4UDiMOMg5BTlCOX85vDn5OjY6dDqyOu87LTtrO6o76DwnPGU8
ri2uoa8Wr4uwALB1sOqxYLHWskuywrM4s660JbSctRO1irYBtnm28Ldot+C4WbjRuUq5wro7urW7Lrun pDzjPSI9YT2hPeA+ID5gPqA+4D8hP2E/oj/iQCNAZECmQOdBKUFqQaxB7kIwQnJCtUL3QzpDfUPARANE
vCG8m70VvY++Cr6Evv+/er/1wHDA7MFnwePCX8Lbw1jD1MRRxM7FS8XIxkbGw8dBx7/IPci8yTrJuco4 R0SKRM5FEkVVRZpF3kYiRmdGq0bwRzVHe0fASAVIS0iRSNdJHUljSalJ8Eo3Sn1KxEsMS1NLmkviTCpM
yrfLNsu2zDXMtc01zbXONs62zzfPuNA50LrRPNG+0j/SwdNE08bUSdTL1U7V0dZV1tjXXNfg2GTY6Nls cky6TQJNSk2TTdxOJU5uTrdPAE9JT5NP3VAnUHFQu1EGUVBRm1HmUjFSfFLHUxNTX1OqU/ZUQlSPVNtV
2fHadtr724DcBdyK3RDdlt4c3qLfKd+v4DbgveFE4cziU+Lb42Pj6+Rz5PzlhOYN5pbnH+ep6DLovOlG KFV1VcJWD1ZcVqlW91dEV5JX4FgvWH1Yy1kaWWlZuFoHWlZaplr1W0VblVvlXDVchlzWXSddeF3JXhpe
6dDqW+rl63Dr++yG7RHtnO4o7rTvQO/M8Fjw5fFy8f/yjPMZ86f0NPTC9VD13vZt9vv3ivgZ+Kj5OPnH bF69Xw9fYV+zYAVgV2CqYPxhT2GiYfViSWKcYvBjQ2OXY+tkQGSUZOllPWWSZedmPWaSZuhnPWeTZ+lo
+lf65/t3/Af8mP0p/br+S/7c/23//4AN0iIjRUZdTlNNdXRhYmxlRGF0YaNFRydWTlNEYXRh0iIjSUpc P2iWaOxpQ2maafFqSGqfavdrT2una/9sV2yvbQhtYG25bhJua27Ebx5veG/RcCtwhnDgcTpxlXHwckty
TlNDb2xvclNwYWNloksnXE5TQ29sb3JTcGFjZdIiI01OV05TQ29sb3KiTSfSIiNQUVdOU0ltYWdlolAn pnMBc11zuHQUdHB0zHUodYV14XY+dpt2+HdWd7N4EXhueMx5KnmJeed6RnqlewR7Y3vCfCF8gXzhfUF9
XxAPTlNLZXllZEFyY2hpdmVy0VRVVHJvb3SAAQAIABEAGgAjAC0AMgA3AEsAUQBaAGEAbgB1AH0AfwCE oX4BfmJ+wn8jf4R/5YBHgKiBCoFrgc2CMIKSgvSDV4O6hB2EgITjhUeFq4YOhnKG14c7h5+IBIhpiM6J
AIYAiACNAJgAmgCcAJ4AowCmAKgAqgCsAK4AswDKAMwAzgMQAxUDIAMpAzwDQANLA1QDWQNhA2QDaQN4 M4mZif6KZIrKizCLlov8jGOMyo0xjZiN/45mjs6PNo+ekAaQbpDWkT+RqJIRknqS45NNk7aUIJSKlPSV
A3wDhwOPA5wDqQO+A8MDxwPJA8sDzQPWA9sD4QPpA+sD7QPvA/ED9gP+FWoVbBVxFX8VgxWKFY8VnBWf X5XJljSWn5cKl3WX4JhMmLiZJJmQmfyaaJrVm0Kbr5wcnImc951kndKeQJ6unx2fi5/6oGmg2KFHobai
FawVsRW5FbwVwRXJFcwV3hXhFeYAAAAAAAACAQAAAAAAAABWAAAAAAAAAAAAAAAAAAAV6A JqKWowajdqPmpFakx6U4pammGqaLpv2nbqfgqFKoxKk3qamqHKqPqwKrdavprFys0K1ErbiuLa6hrxav
i7AAsHWw6rFgsdayS7LCszizrrQltJy1E7WKtgG2ebbwt2i34LhZuNG5SrnCuju6tbsuu6e8IbybvRW9
j74KvoS+/796v/XAcMDswWfB48JfwtvDWMPUxFHEzsVLxcjGRsbDx0HHv8g9yLzJOsm5yjjKt8s2y7bM
Ncy1zTXNtc42zrbPN8+40DnQutE80b7SP9LB00TTxtRJ1MvVTtXR1lXW2Ndc1+DYZNjo2WzZ8dp22vvb
gNwF3IrdEN2W3hzeot8p36/gNuC94UThzOJT4tvjY+Pr5HPk/OWE5g3mlucf56noMui86Ubp0Opb6uXr
cOv77IbtEe2c7ijutO9A78zwWPDl8XLx//KM8xnzp/Q09ML1UPXe9m32+/eK+Bn4qPk4+cf6V/rn+3f8
B/yY/Sn9uv5L/tz/bf//0igpSElcTlNDb2xvclNwYWNlokotXE5TQ29sb3JTcGFjZdIoKUxNV05TQ29s
b3KiTC3SKClPUFdOU0ltYWdlok8tAAgAEQAaACQAKQAyADcASQBMAFEAUwBmAGwAdwB+AI0AmgChAKkA
qwCtALIAtAC2ALsAxgDIAMoAzADRANQA1gDYANoA4QD4ARQBFgEYA1oDXwNqA3MDhgOKA5UDngOjA6sD
rgOzA8IDxgPRA9kD5gPzBAgEDQQRBBMEFQQXBCAEJQQrBDMENQQ3BDkEOxXbFeAV7RXwFf0WAhYKFg0W
EhYaAAAAAAAAAgEAAAAAAAAAUQAAAAAAAAAAAAAAAAAAFh0
</mutableData> </mutableData>
</image> </image>
<image name="linphone_user.png" width="41.599998474121094" height="42.400001525878906"/> <image name="linphone_user.png" width="41.599998474121094" height="42.400001525878906"/>
<image name="security_toogle_icon_green.png" width="33.599998474121094" height="38.400001525878906"/> <image name="security_toogle_icon_green.png" width="33.599998474121094" height="38.400001525878906"/>
<systemColor name="secondarySystemBackgroundColor">
<color red="0.94901960784313721" green="0.94901960784313721" blue="0.96862745098039216" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</systemColor>
<systemColor name="systemBackgroundColor">
<color white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
</systemColor>
</resources> </resources>
</document> </document>

View file

@ -29,15 +29,15 @@
@property(nonatomic, strong) NSMutableArray *addresses; @property(nonatomic, strong) NSMutableArray *addresses;
@property(nonatomic, strong) NSMutableArray *phoneOrAddr; @property(nonatomic, strong) NSMutableArray *phoneOrAddr;
@property(nonatomic, strong) NSMutableArray *addressesCached; @property(nonatomic, strong) NSMutableArray *addressesCached;
@property(readonly, nonatomic) NSMutableDictionary *ldapContactAddressBookMap; @property(readonly, nonatomic) NSMutableDictionary *ldapAndProvisioningContactAddressBookMap;
@end @end
@implementation ChatConversationCreateTableView @implementation ChatConversationCreateTableView
- (void)viewWillAppear:(BOOL)animated { - (void)viewWillAppear:(BOOL)animated {
if (!_ldapContactAddressBookMap) { if (!_ldapAndProvisioningContactAddressBookMap) {
_ldapContactAddressBookMap = [NSMutableDictionary dictionary]; _ldapAndProvisioningContactAddressBookMap = [NSMutableDictionary dictionary];
} }
[super viewWillAppear:animated]; [super viewWillAppear:animated];
@ -57,7 +57,7 @@
_addresses = [[NSMutableArray alloc] initWithCapacity:LinphoneManager.instance.fastAddressBook.addressBookMap.allKeys.count]; _addresses = [[NSMutableArray alloc] initWithCapacity:LinphoneManager.instance.fastAddressBook.addressBookMap.allKeys.count];
_phoneOrAddr = [[NSMutableArray alloc] initWithCapacity:LinphoneManager.instance.fastAddressBook.addressBookMap.allKeys.count]; _phoneOrAddr = [[NSMutableArray alloc] initWithCapacity:LinphoneManager.instance.fastAddressBook.addressBookMap.allKeys.count];
_addressesCached = [[NSMutableArray alloc] initWithCapacity:LinphoneManager.instance.fastAddressBook.addressBookMap.allKeys.count]; _addressesCached = [[NSMutableArray alloc] initWithCapacity:LinphoneManager.instance.fastAddressBook.addressBookMap.allKeys.count];
[[NSNotificationCenter defaultCenter] [[NSNotificationCenter defaultCenter]
addObserver:self addObserver:self
@ -104,25 +104,27 @@
LinphoneSearchResult *result = results->data; LinphoneSearchResult *result = results->data;
const LinphoneAddress *addr = linphone_search_result_get_address(result); const LinphoneAddress *addr = linphone_search_result_get_address(result);
const LinphoneFriend* friend = linphone_search_result_get_friend(result);
const char *phoneNumber = linphone_search_result_get_phone_number(result);
const char *phoneNumber = NULL;
Contact *contact = nil; Contact *contact = nil;
char *uri = nil; char *uri = nil;
NSString *address = nil; NSString *address = nil;
if (addr) { if (addr) {
uri = linphone_address_as_string_uri_only(addr); uri = linphone_address_as_string_uri_only(addr);
address = [NSString stringWithUTF8String:uri]; address = [NSString stringWithUTF8String:uri];
contact = [LinphoneManager.instance.fastAddressBook.addressBookMap objectForKey:[FastAddressBook normalizeSipURI:address]]; contact = [LinphoneManager.instance.fastAddressBook.addressBookMap objectForKey:[FastAddressBook normalizeSipURI:address use_prefix:[CallManager.instance applyInternationalPrefix]]];
}
if (!contact && friend) {
const LinphoneFriend* friend = linphone_search_result_get_friend(result); contact = [[Contact alloc] initWithFriend:friend];
if (!addr || (!contact && friend)) { [contact setCreatedFromLdapOrProvisioning:TRUE];
phoneNumber = linphone_search_result_get_phone_number(result); [_ldapAndProvisioningContactAddressBookMap setObject:contact forKey:address];
}
} else if (friend){
if (!phoneNumber) { if (!phoneNumber) {
results = results->next; results = results->next;
continue; continue;
} }
LinphoneAccount *account = linphone_core_get_default_account(LC); LinphoneAccount *account = linphone_core_get_default_account(LC);
if (account) { if (account) {
const char *normalizedPhoneNumber = linphone_account_normalize_phone_number(account, phoneNumber); const char *normalizedPhoneNumber = linphone_account_normalize_phone_number(account, phoneNumber);
@ -136,10 +138,9 @@
address = [NSString stringWithUTF8String:uri]; address = [NSString stringWithUTF8String:uri];
contact = [[Contact alloc] initWithFriend:friend]; contact = [[Contact alloc] initWithFriend:friend];
[contact setCreatedFromLdap:TRUE]; [contact setCreatedFromLdapOrProvisioning:TRUE];
[_ldapContactAddressBookMap setObject:contact forKey:address]; [_ldapAndProvisioningContactAddressBookMap setObject:contact forKey:address];
} }
} }
if (!addr) { if (!addr) {
@ -168,7 +169,7 @@
[_addresses removeAllObjects]; [_addresses removeAllObjects];
[_phoneOrAddr removeAllObjects]; [_phoneOrAddr removeAllObjects];
[_addressesCached removeAllObjects]; [_addressesCached removeAllObjects];
[_ldapContactAddressBookMap removeAllObjects]; [_ldapAndProvisioningContactAddressBookMap removeAllObjects];
[self.tableView reloadData]; [self.tableView reloadData];
_reloadMagicSearch = _reloadMagicSearch || [filter length]==0 || ![[MagicSearchSingleton.instance currentFilter] isEqualToString:filter]; _reloadMagicSearch = _reloadMagicSearch || [filter length]==0 || ![[MagicSearchSingleton.instance currentFilter] isEqualToString:filter];
@ -192,7 +193,7 @@
} }
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(nonnull NSIndexPath *)indexPath { - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(nonnull NSIndexPath *)indexPath {
return 60.0; return 60.0;
} }
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
@ -203,12 +204,12 @@
NSString *key = [_addresses objectAtIndex:indexPath.row]; NSString *key = [_addresses objectAtIndex:indexPath.row];
NSString *phoneOrAddr = [_phoneOrAddr objectAtIndex:indexPath.row]; NSString *phoneOrAddr = [_phoneOrAddr objectAtIndex:indexPath.row];
Contact *contact = [LinphoneManager.instance.fastAddressBook.addressBookMap objectForKey:[FastAddressBook normalizeSipURI:key]]; Contact *contact = [LinphoneManager.instance.fastAddressBook.addressBookMap objectForKey:[FastAddressBook normalizeSipURI:key use_prefix:[CallManager.instance applyInternationalPrefix]]];
if (!contact) { if (!contact) {
contact = [_ldapContactAddressBookMap objectForKey:key]; contact = [_ldapAndProvisioningContactAddressBookMap objectForKey:key];
} }
const LinphonePresenceModel *model = contact.friend ? linphone_friend_get_presence_model(contact.friend) : NULL; const LinphonePresenceModel *model = contact.friend ? linphone_friend_get_presence_model(contact.friend) : NULL;
Boolean linphoneContact = [FastAddressBook contactHasValidSipDomain:contact] Boolean linphoneContact = [FastAddressBook contactHasValidSipDomain:contact]
|| (model && linphone_presence_model_get_basic_status(model) == LinphonePresenceBasicStatusOpen); || (model && linphone_presence_model_get_basic_status(model) == LinphonePresenceBasicStatusOpen);
LinphoneAddress *addr = [LinphoneUtils normalizeSipOrPhoneAddress:key]; LinphoneAddress *addr = [LinphoneUtils normalizeSipOrPhoneAddress:key];
@ -216,30 +217,32 @@
return cell; return cell;
cell.linphoneImage.hidden = [LinphoneManager.instance lpConfigBoolForKey:@"hide_linphone_contacts" inSection:@"app"] || !linphoneContact; cell.linphoneImage.hidden = [LinphoneManager.instance lpConfigBoolForKey:@"hide_linphone_contacts" inSection:@"app"] || !linphoneContact;
cell.securityImage.hidden = !(model && linphone_presence_model_has_capability(model, LinphoneFriendCapabilityLimeX3dh)); cell.securityImage.hidden = !(model && linphone_presence_model_has_capability(model, LinphoneFriendCapabilityLimeX3dh));
int capabilities = [[_addressesCached objectAtIndex:indexPath.row] intValue]; int capabilities = [[_addressesCached objectAtIndex:indexPath.row] intValue];
BOOL greyCellForEncryptedChat = _isEncrypted ? capabilities > 1 : TRUE; BOOL greyCellForEncryptedChat = _isEncrypted ? capabilities > 1 : TRUE;
BOOL greyCellForGroupChat = _isGroupChat ? capabilities > 0 : TRUE; BOOL greyCellForGroupChat = _isGroupChat ? capabilities > 0 : TRUE;
cell.userInteractionEnabled = cell.greyView.hidden = greyCellForEncryptedChat && greyCellForGroupChat; cell.userInteractionEnabled = cell.greyView.hidden = greyCellForEncryptedChat && greyCellForGroupChat;
cell.displayNameLabel.text = [contact createdFromLdap] ? [contact displayName] : [FastAddressBook displayNameForAddress:addr]; cell.displayNameLabel.text = [contact createdFromLdapOrProvisioning] ? [contact displayName] : [FastAddressBook displayNameForAddress:addr];
char *str = linphone_address_as_string(addr); char *str = linphone_address_as_string(addr);
cell.addressLabel.text = linphoneContact ? [NSString stringWithUTF8String:str] : phoneOrAddr; cell.addressLabel.text = linphoneContact ? [NSString stringWithUTF8String:str] : phoneOrAddr;
ms_free(str); ms_free(str);
cell.selectedImage.hidden = ![_contactsGroup containsObject:cell.addressLabel.text]; cell.selectedImage.hidden = ![_contactsGroup containsObject:cell.addressLabel.text];
[cell.avatarImage setImage:[FastAddressBook imageForAddress:addr] bordered:NO withRoundedRadius:YES]; [cell.avatarImage setImage:[FastAddressBook imageForAddress:addr] bordered:NO withRoundedRadius:YES];
cell.contentView.userInteractionEnabled = false; cell.contentView.userInteractionEnabled = false;
cell.contentView.backgroundColor = UIColor.clearColor;
cell.backgroundColor = UIColor.clearColor;
return cell; return cell;
} }
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
UIChatCreateCell *cell = [tableView cellForRowAtIndexPath:indexPath]; UIChatCreateCell *cell = [tableView cellForRowAtIndexPath:indexPath];
if (!cell.userInteractionEnabled) if (!cell.userInteractionEnabled)
return; return;
LinphoneAccount *defaultAccount = linphone_core_get_default_account(LC); LinphoneAccount *defaultAccount = linphone_core_get_default_account(LC);
if (!(defaultAccount && linphone_account_params_get_conference_factory_uri(linphone_account_get_params(defaultAccount))) || !_isGroupChat) { if (!(defaultAccount && linphone_account_params_get_conference_factory_uri(linphone_account_get_params(defaultAccount))) || !_isGroupChat) {
LinphoneAddress *addr = linphone_address_new(cell.addressLabel.text.UTF8String); LinphoneAddress *addr = linphone_address_new(cell.addressLabel.text.UTF8String);
[PhoneMainView.instance getOrCreateOneToOneChatRoom:addr waitView:_waitView isEncrypted:_isEncrypted]; [PhoneMainView.instance getOrCreateOneToOneChatRoom:addr waitView:_waitView isEncrypted:_isEncrypted];
if (!addr) { if (!addr) {
LOGE(@"Chat room could not be created on server, because null address."); LOGE(@"Chat room could not be created on server, because null address.");
[ChatConversationInfoView displayCreationError]; [ChatConversationInfoView displayCreationError];
@ -251,8 +254,6 @@
[tableView deselectRowAtIndexPath:indexPath animated:YES]; [tableView deselectRowAtIndexPath:indexPath animated:YES];
NSInteger index = 0; NSInteger index = 0;
_searchBar.text = @"";
[self searchBar:_searchBar textDidChange:@""];
if(cell.selectedImage.hidden) { if(cell.selectedImage.hidden) {
if(![_contactsGroup containsObject:cell.addressLabel.text]) { if(![_contactsGroup containsObject:cell.addressLabel.text]) {
[_contactsGroup addObject:cell.addressLabel.text]; [_contactsGroup addObject:cell.addressLabel.text];

View file

@ -55,6 +55,9 @@
- (IBAction)onNextClick:(id)sender; - (IBAction)onNextClick:(id)sender;
- (IBAction)onChiffreClick:(id)sender; - (IBAction)onChiffreClick:(id)sender;
-(void) unfragmentCompositeDescription;
-(void) fragmentCompositeDescription;
@end @end
#endif /* ChatConversationCreateView_h */ #endif /* ChatConversationCreateView_h */

View file

@ -45,6 +45,21 @@ static UICompositeViewDescription *compositeDescription = nil;
return self.class.compositeViewDescription; return self.class.compositeViewDescription;
} }
-(void) unfragmentCompositeDescription {
if (!IPAD)
return;
compositeDescription.isLeftFragment = true;
compositeDescription.otherFragment = nil;
}
-(void) fragmentCompositeDescription {
if (!IPAD)
return;
compositeDescription.otherFragment = IPAD ? NSStringFromClass(ChatsListView.class) : nil;
compositeDescription.isLeftFragment = false;
}
- (void)viewDidLoad { - (void)viewDidLoad {
[super viewDidLoad]; [super viewDidLoad];
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc]
@ -54,7 +69,7 @@ static UICompositeViewDescription *compositeDescription = nil;
[self.view addGestureRecognizer:tap]; [self.view addGestureRecognizer:tap];
UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init]; UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init];
layout.scrollDirection = UICollectionViewScrollDirectionHorizontal; layout.scrollDirection = UICollectionViewScrollDirectionHorizontal;
layout.itemSize = CGSizeMake(100.0 , 50.0); layout.estimatedItemSize = UICollectionViewFlowLayoutAutomaticSize;
_collectionController.collectionView = _collectionView; _collectionController.collectionView = _collectionView;
_collectionController = (ChatConversationCreateCollectionViewController *)[[UICollectionViewController alloc] initWithCollectionViewLayout:layout]; _collectionController = (ChatConversationCreateCollectionViewController *)[[UICollectionViewController alloc] initWithCollectionViewLayout:layout];
_collectionView.dataSource = self; _collectionView.dataSource = self;
@ -75,6 +90,10 @@ static UICompositeViewDescription *compositeDescription = nil;
selector:@selector(viewUpdateEvent:) selector:@selector(viewUpdateEvent:)
name:kLinphoneChatCreateViewChange name:kLinphoneChatCreateViewChange
object:nil]; object:nil];
[NSNotificationCenter.defaultCenter addObserver:self
selector:@selector(displayModeChanged)
name:kDisplayModeChanged
object:nil];
LinphoneAccount *defaultAccount = linphone_core_get_default_account(LC); LinphoneAccount *defaultAccount = linphone_core_get_default_account(LC);
_chiffreOptionView.hidden = !(defaultAccount && linphone_account_params_get_conference_factory_uri(linphone_account_get_params(defaultAccount))); _chiffreOptionView.hidden = !(defaultAccount && linphone_account_params_get_conference_factory_uri(linphone_account_get_params(defaultAccount)));
if ([LinphoneManager.instance lpConfigBoolForKey:@"hide_linphone_contacts" inSection:@"app"]) { if ([LinphoneManager.instance lpConfigBoolForKey:@"hide_linphone_contacts" inSection:@"app"]) {
@ -83,8 +102,15 @@ static UICompositeViewDescription *compositeDescription = nil;
CGRect frame = _allButton.frame; CGRect frame = _allButton.frame;
frame.origin.x = _linphoneButton.frame.origin.x; frame.origin.x = _linphoneButton.frame.origin.x;
_allButton.frame = frame; _allButton.frame = frame;
} }
if ([LinphoneManager.instance lpConfigBoolForKey:@"force_lime_chat_rooms"]) {
_chiffreOptionView.hidden = true;
_isEncrypted = true;
_tableController.isEncrypted = true;
_allButton.hidden = true;
}
if (_isForVoipConference) { if (_isForVoipConference) {
_switchView.hidden = true; _switchView.hidden = true;
@ -95,13 +121,28 @@ static UICompositeViewDescription *compositeDescription = nil;
} else { } else {
[_nextButton setImage:[UIImage imageNamed:@"next_default"] forState:UIControlStateNormal]; [_nextButton setImage:[UIImage imageNamed:@"next_default"] forState:UIControlStateNormal];
} }
_topBar.backgroundColor = VoipTheme.toolbar_color;
} else { } else {
_voipTitle.hidden = true; _voipTitle.hidden = true;
[_nextButton setImage:[UIImage imageNamed:@"next_default"] forState:UIControlStateNormal]; [_nextButton setImage:[UIImage imageNamed:@"next_default"] forState:UIControlStateNormal];
_topBar.backgroundColor = UIColor.secondarySystemBackgroundColor;
} }
[self displayModeChanged];
}
- (void)displayModeChanged{
[self.tableController.tableView reloadData];
if (_isForVoipConference) {
_topBar.backgroundColor = [VoipTheme.voipToolbarBackgroundColor get];
self.view.backgroundColor = [VoipTheme.voipBackgroundBWColor get];
_tableController.tableView.backgroundColor = [VoipTheme.voipBackgroundBWColor get];
_tableController.searchBar.backgroundColor = [VoipTheme.voipBackgroundBWColor get];
_tableController.collectionView.backgroundColor = [VoipTheme.voipBackgroundBWColor get];
} else {
_topBar.backgroundColor = UIColor.secondarySystemBackgroundColor;
self.view.backgroundColor = [VoipTheme.backgroundWhiteBlack get];
_tableController.tableView.backgroundColor = [VoipTheme.backgroundWhiteBlack get];
_tableController.searchBar.backgroundColor = [VoipTheme.backgroundWhiteBlack get];
_tableController.collectionView.backgroundColor = [VoipTheme.backgroundWhiteBlack get];
}
} }
- (void)viewUpdateEvent:(NSNotification *)notif { - (void)viewUpdateEvent:(NSNotification *)notif {
@ -116,17 +157,17 @@ static UICompositeViewDescription *compositeDescription = nil;
frame.origin.x = self.view.frame.size.width * 0.192; frame.origin.x = self.view.frame.size.width * 0.192;
} }
_chiffreOptionView.frame = frame; _chiffreOptionView.frame = frame;
_isEncrypted = FALSE; _isEncrypted = [LinphoneManager.instance lpConfigBoolForKey:@"force_lime_chat_rooms"]; // false by default
CGRect buttonFrame = _chiffreButton.frame; CGRect buttonFrame = _chiffreButton.frame;
_tableController.isEncrypted = _isEncrypted;
// no encrypted by default if (!_isEncrypted) {
buttonFrame.origin.x = 2; buttonFrame.origin.x = 2;
[_chiffreImage setImage:[UIImage imageNamed:@"security_toogle_background_grey.png"]]; [_chiffreImage setImage:[UIImage imageNamed:@"security_toogle_background_grey.png"]];
_chiffreButton.frame = buttonFrame; _chiffreButton.frame = buttonFrame;
}
_waitView.hidden = YES; _waitView.hidden = YES;
_backButton.hidden = IPAD; _backButton.hidden = IPAD && !(_isForVoipConference||_isForOngoingVoipConference);
if(_tableController.contactsGroup.count == 0) { if(_tableController.contactsGroup.count == 0) {
if (!_isForEditing) if (!_isForEditing)
_nextButton.enabled = FALSE; _nextButton.enabled = FALSE;
@ -149,9 +190,8 @@ static UICompositeViewDescription *compositeDescription = nil;
} }
- (void)viewWillDisappear:(BOOL)animated { - (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated]; [super viewWillDisappear:animated];
if (IPAD) [NSNotificationCenter.defaultCenter removeObserver:self];
[NSNotificationCenter.defaultCenter removeObserver:self];
} }
#pragma mark - Chat room functions #pragma mark - Chat room functions
@ -168,7 +208,12 @@ static UICompositeViewDescription *compositeDescription = nil;
- (IBAction)onBackClick:(id)sender { - (IBAction)onBackClick:(id)sender {
[_tableController.contactsGroup removeAllObjects]; [_tableController.contactsGroup removeAllObjects];
if (_isForVoipConference) { if (_isForVoipConference) {
[PhoneMainView.instance popToView:ConferenceSchedulingView.compositeViewDescription]; if (_isForOngoingVoipConference) {
[PhoneMainView.instance popToView:VIEW(ConferenceCallView).compositeViewDescription];
[ControlsViewModelBridge showParticipants];
} else {
[PhoneMainView.instance popToView:ConferenceSchedulingView.compositeViewDescription];
}
} else { } else {
if (_tableController.isForEditing) if (_tableController.isForEditing)
[PhoneMainView.instance popToView:ChatConversationInfoView.compositeViewDescription]; [PhoneMainView.instance popToView:ChatConversationInfoView.compositeViewDescription];
@ -180,7 +225,7 @@ static UICompositeViewDescription *compositeDescription = nil;
- (IBAction)onNextClick:(id)sender { - (IBAction)onNextClick:(id)sender {
if (_isForVoipConference) { if (_isForVoipConference) {
if (_isForOngoingVoipConference) { if (_isForOngoingVoipConference) {
[PhoneMainView.instance changeCurrentView:VIEW(ActiveCallOrConferenceView).compositeViewDescription]; [PhoneMainView.instance popToView:VIEW(ConferenceCallView).compositeViewDescription];
[ConferenceViewModelBridge updateParticipantsListWithAddresses:_tableController.contactsGroup]; [ConferenceViewModelBridge updateParticipantsListWithAddresses:_tableController.contactsGroup];
} else { } else {
[PhoneMainView.instance changeCurrentView:VIEW(ConferenceSchedulingSummaryView).compositeViewDescription]; [PhoneMainView.instance changeCurrentView:VIEW(ConferenceSchedulingSummaryView).compositeViewDescription];
@ -260,7 +305,7 @@ typedef enum { ContactsAll, ContactsLinphone, ContactsMAX } ContactsCategory;
return NO; return NO;
} }
#pragma mark - UICollectionViewDataSource #pragma mark - UICollectionViewDataSource & Delegate
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section { - (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
return _tableController.contactsGroup.count; return _tableController.contactsGroup.count;
} }
@ -282,9 +327,10 @@ typedef enum { ContactsAll, ContactsLinphone, ContactsMAX } ContactsCategory;
ms_free(phone); ms_free(phone);
} else } else
addr = linphone_address_new(uri.UTF8String); addr = linphone_address_new(uri.UTF8String);
cell = [cell initWithName:[FastAddressBook displayNameForAddress:addr]]; [cell.nameLabel setText:[FastAddressBook displayNameForAddress:addr]];
linphone_address_unref(addr); linphone_address_unref(addr);
return cell; return cell;
} }
@end @end

View file

@ -32,7 +32,7 @@
NSString *messageText; NSString *messageText;
} }
@property(nonatomic) LinphoneChatMessage *msg; @property(nonatomic) LinphoneEventLog *event;
@property(nonatomic) bctbx_list_t *displayedList; @property(nonatomic) bctbx_list_t *displayedList;
@property(nonatomic) bctbx_list_t *receivedList; @property(nonatomic) bctbx_list_t *receivedList;
@property(nonatomic) bctbx_list_t *notReceivedList; @property(nonatomic) bctbx_list_t *notReceivedList;

View file

@ -48,17 +48,12 @@ static UICompositeViewDescription *compositeDescription = nil;
- (void)viewDidLoad { - (void)viewDidLoad {
[super viewDidLoad]; [super viewDidLoad];
_msg = NULL;
} }
- (void)viewWillAppear:(BOOL)animated { - (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated]; [super viewWillAppear:animated];
int index = [VIEW(ChatConversationView).tableController indexOfMesssage:_msg]; _cell = [VIEW(ChatConversationView).tableController buildMessageCell:_event];
if (index < 0)
[PhoneMainView.instance popToView:ChatConversationView.compositeViewDescription];
_cell = (UIChatBubbleTextCell *)[VIEW(ChatConversationView).tableController tableView:VIEW(ChatConversationView).tableController.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:index inSection:0]];
_cell.frame = CGRectMake(-10,0,_msgView.frame.size.width,_msgView.frame.size.height); _cell.frame = CGRectMake(-10,0,_msgView.frame.size.width,_msgView.frame.size.height);
_cell.isFirst = true; _cell.isFirst = true;
_cell.isLast = true; _cell.isLast = true;
@ -90,18 +85,21 @@ static UICompositeViewDescription *compositeDescription = nil;
} }
- (void)updateImdnList { - (void)updateImdnList {
if (_msg && linphone_chat_message_get_chat_room(_msg)) { if (_event) {
_displayedList = linphone_chat_message_get_participants_by_imdn_state(_msg, LinphoneChatMessageStateDisplayed); LinphoneChatMessage *_msg = linphone_event_log_get_chat_message(_event);
_receivedList = linphone_chat_message_get_participants_by_imdn_state(_msg, LinphoneChatMessageStateDeliveredToUser); if (_msg) {
_notReceivedList = linphone_chat_message_get_participants_by_imdn_state(_msg, LinphoneChatMessageStateDelivered); _displayedList = linphone_chat_message_get_participants_by_imdn_state(_msg, LinphoneChatMessageStateDisplayed);
_errorList = linphone_chat_message_get_participants_by_imdn_state(_msg, LinphoneChatMessageStateNotDelivered); _receivedList = linphone_chat_message_get_participants_by_imdn_state(_msg, LinphoneChatMessageStateDeliveredToUser);
_notReceivedList = linphone_chat_message_get_participants_by_imdn_state(_msg, LinphoneChatMessageStateDelivered);
[_tableView reloadData]; _errorList = linphone_chat_message_get_participants_by_imdn_state(_msg, LinphoneChatMessageStateNotDelivered);
[_tableView reloadData];
}
} }
} }
- (void)fitContent { - (void)fitContent {
LinphoneChatMessage *_msg = linphone_event_log_get_chat_message(_event);
CGSize messageSize = [UIChatBubbleTextCell ViewHeightForMessage:_msg withWidth:self.view.frame.size.width]; CGSize messageSize = [UIChatBubbleTextCell ViewHeightForMessage:_msg withWidth:self.view.frame.size.width];
[_msgView setFrame:CGRectMake(_msgView.frame.origin.x, [_msgView setFrame:CGRectMake(_msgView.frame.origin.x,
_msgView.frame.origin.y, _msgView.frame.origin.y,
@ -299,7 +297,8 @@ static UICompositeViewDescription *compositeDescription = nil;
f.unitsStyle = NSDateComponentsFormatterUnitsStylePositional; f.unitsStyle = NSDateComponentsFormatterUnitsStylePositional;
f.zeroFormattingBehavior = NSDateComponentsFormatterZeroFormattingBehaviorPad; f.zeroFormattingBehavior = NSDateComponentsFormatterZeroFormattingBehaviorPad;
if (linphone_chat_message_is_ephemeral(_msg)) { LinphoneChatMessage *_msg = _event ? linphone_event_log_get_chat_message(_event) : nil;
if (_msg && linphone_chat_message_is_ephemeral(_msg)) {
long duration = linphone_chat_message_get_ephemeral_expire_time(_msg) == 0 ? long duration = linphone_chat_message_get_ephemeral_expire_time(_msg) == 0 ?
linphone_chat_room_get_ephemeral_lifetime(linphone_chat_message_get_chat_room(_msg)) : linphone_chat_room_get_ephemeral_lifetime(linphone_chat_message_get_chat_room(_msg)) :
linphone_chat_message_get_ephemeral_expire_time(_msg)-[NSDate date].timeIntervalSince1970; linphone_chat_message_get_ephemeral_expire_time(_msg)-[NSDate date].timeIntervalSince1970;

View file

@ -35,6 +35,7 @@
@property(nonatomic) LinphoneChatRoom *room; @property(nonatomic) LinphoneChatRoom *room;
@property(nonatomic) LinphoneChatRoomCbs *chatRoomCbs; @property(nonatomic) LinphoneChatRoomCbs *chatRoomCbs;
@property(nonatomic) const char *peerAddress; @property(nonatomic) const char *peerAddress;
@property(nonatomic) const char *localAddress;
@property (weak, nonatomic) IBOutlet UIIconButton *nextButton; @property (weak, nonatomic) IBOutlet UIIconButton *nextButton;
@property (weak, nonatomic) IBOutlet UIRoundBorderedButton *quitButton; @property (weak, nonatomic) IBOutlet UIRoundBorderedButton *quitButton;

View file

@ -84,6 +84,7 @@ static UICompositeViewDescription *compositeDescription = nil;
_room = NULL; _room = NULL;
_chatRoomCbs = NULL; _chatRoomCbs = NULL;
_peerAddress = NULL; _peerAddress = NULL;
_localAddress = NULL;
} }
- (void)viewWillAppear:(BOOL)animated { - (void)viewWillAppear:(BOOL)animated {
@ -153,10 +154,11 @@ static UICompositeViewDescription *compositeDescription = nil;
- (void)onLinphoneCoreReady:(NSNotification *)notif { - (void)onLinphoneCoreReady:(NSNotification *)notif {
if ((LinphoneGlobalState)[[[notif userInfo] valueForKey:@"state"] integerValue] == LinphoneGlobalOn) { if ((LinphoneGlobalState)[[[notif userInfo] valueForKey:@"state"] integerValue] == LinphoneGlobalOn) {
if (!_create && _peerAddress) { if (!_create && _peerAddress && _localAddress) {
LinphoneAddress *peerAddr = linphone_core_create_address([LinphoneManager getLc], _peerAddress); LinphoneAddress *peerAddr = linphone_core_create_address([LinphoneManager getLc], _peerAddress);
if (peerAddr) { LinphoneAddress *localAddr = linphone_core_create_address([LinphoneManager getLc], _localAddress);
_room = linphone_core_get_chat_room([LinphoneManager getLc], peerAddr); if (peerAddr && localAddr) {
_room = linphone_core_search_chat_room([LinphoneManager getLc], NULL, localAddr, peerAddr, NULL);
} }
[self configure]; [self configure];
} }
@ -270,6 +272,7 @@ static UICompositeViewDescription *compositeDescription = nil;
- (IBAction)onBackClick:(id)sender { - (IBAction)onBackClick:(id)sender {
if(_create) { if(_create) {
ChatConversationCreateView *view = VIEW(ChatConversationCreateView); ChatConversationCreateView *view = VIEW(ChatConversationCreateView);
[view fragmentCompositeDescription];
view.tableController.contactsGroup = [_contacts mutableCopy]; view.tableController.contactsGroup = [_contacts mutableCopy];
view.tableController.notFirstTime = TRUE; view.tableController.notFirstTime = TRUE;
view.isForEditing = FALSE; view.isForEditing = FALSE;
@ -296,6 +299,7 @@ static UICompositeViewDescription *compositeDescription = nil;
- (IBAction)onAddClick:(id)sender { - (IBAction)onAddClick:(id)sender {
if (_create || _imAdmin) { if (_create || _imAdmin) {
ChatConversationCreateView *view = VIEW(ChatConversationCreateView); ChatConversationCreateView *view = VIEW(ChatConversationCreateView);
[view fragmentCompositeDescription];
view.tableController.notFirstTime = TRUE; view.tableController.notFirstTime = TRUE;
view.isForEditing = !_create; view.isForEditing = !_create;
view.isGroupChat = TRUE; view.isGroupChat = TRUE;

View file

@ -25,6 +25,7 @@
#import "UICheckBoxTableView.h" #import "UICheckBoxTableView.h"
@interface FileContext : NSObject @interface FileContext : NSObject
@property NSMutableArray <NSString *> *typesArray; @property NSMutableArray <NSString *> *typesArray;
@property NSMutableArray <NSData *> *datasArray; @property NSMutableArray <NSData *> *datasArray;
@ -70,5 +71,6 @@
- (void) dismissMessagesPopups; - (void) dismissMessagesPopups;
- (void) scrollToMessage:(LinphoneChatMessage *)message; - (void) scrollToMessage:(LinphoneChatMessage *)message;
- (int) indexOfMesssage:(LinphoneChatMessage *)message; - (int) indexOfMesssage:(LinphoneChatMessage *)message;
- (void *)buildMessageCell:(LinphoneEventLog *) event;
@end @end

View file

@ -20,7 +20,6 @@
#import "LinphoneManager.h" #import "LinphoneManager.h"
#import "ChatConversationTableView.h" #import "ChatConversationTableView.h"
#import "ChatConversationImdnView.h" #import "ChatConversationImdnView.h"
#import "UIChatBubbleTextCell.h"
#import "UIChatBubblePhotoCell.h" #import "UIChatBubblePhotoCell.h"
#import "UIChatNotifiedEventCell.h" #import "UIChatNotifiedEventCell.h"
#import "PhoneMainView.h" #import "PhoneMainView.h"
@ -84,7 +83,6 @@
LinphoneChatRoomCapabilitiesMask capabilities = linphone_chat_room_get_capabilities(_chatRoom); LinphoneChatRoomCapabilitiesMask capabilities = linphone_chat_room_get_capabilities(_chatRoom);
bool oneToOne = capabilities & LinphoneChatRoomCapabilitiesOneToOne; bool oneToOne = capabilities & LinphoneChatRoomCapabilitiesOneToOne;
bctbx_list_t *chatRoomEvents = linphone_chat_room_get_history_events(_chatRoom, 0); bctbx_list_t *chatRoomEvents = linphone_chat_room_get_history_events(_chatRoom, 0);
int unread_count = 0; int unread_count = 0;
bctbx_list_t *head = chatRoomEvents; bctbx_list_t *head = chatRoomEvents;
@ -142,12 +140,11 @@
- (void)addEventEntry:(LinphoneEventLog *)event { - (void)addEventEntry:(LinphoneEventLog *)event {
[eventList addObject:[NSValue valueWithPointer:linphone_event_log_ref(event)]]; [eventList addObject:[NSValue valueWithPointer:linphone_event_log_ref(event)]];
[totalEventList addObject:[NSValue valueWithPointer:linphone_event_log_ref(event)]]; [totalEventList addObject:[NSValue valueWithPointer:linphone_event_log_ref(event)]];
int pos = (int)eventList.count - 1; int pos = (int)eventList.count - 1;
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:pos inSection:0]; NSIndexPath *indexPath = [NSIndexPath indexPathForRow:pos inSection:0];
[self.tableView beginUpdates]; [self.tableView beginUpdates];
[self.tableView insertRowsAtIndexPaths:@[ indexPath ] withRowAnimation:UITableViewRowAnimationFade]; [self.tableView insertRowsAtIndexPaths:@[ indexPath ] withRowAnimation:UITableViewRowAnimationFade];
[self.tableView reloadData];
[self.tableView endUpdates]; [self.tableView endUpdates];
} }
@ -331,28 +328,32 @@ static const int BASIC_EVENT_LIST=15;
} }
} }
-(UIChatBubbleTextCell *)buildMessageCell:(LinphoneEventLog *) event {
NSString *kCellId = nil;
LinphoneChatMessage *chat = linphone_event_log_get_chat_message(event);
BOOL isConferenceIcs = [ICSBubbleView isConferenceInvitationMessageWithCmessage:chat];
if (!isConferenceIcs && (linphone_chat_message_get_file_transfer_information(chat) || linphone_chat_message_get_external_body_url(chat)))
kCellId = NSStringFromClass(UIChatBubblePhotoCell.class);
else
kCellId = NSStringFromClass(UIChatBubbleTextCell.class);
// To use less memory and to avoid overlapping. To be improved.
UIChatBubbleTextCell *cell = [self.tableView dequeueReusableCellWithIdentifier:kCellId];
cell = [[NSClassFromString(kCellId) alloc] initWithIdentifier:kCellId];
[cell setEvent:event];
return cell;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
NSString *kCellId = nil; NSString *kCellId = nil;
LinphoneEventLog *event = [[eventList objectAtIndex:indexPath.row] pointerValue]; LinphoneEventLog *event = [[eventList objectAtIndex:indexPath.row] pointerValue];
if (linphone_event_log_get_type(event) == LinphoneEventLogTypeConferenceChatMessage) { if (linphone_event_log_get_type(event) == LinphoneEventLogTypeConferenceChatMessage) {
UIChatBubbleTextCell *cell = [self buildMessageCell:event];
LinphoneChatMessage *chat = linphone_event_log_get_chat_message(event); LinphoneChatMessage *chat = linphone_event_log_get_chat_message(event);
BOOL isConferenceIcs = [ICSBubbleView isConferenceInvitationMessageWithCmessage:chat]; if (chat) {
if (!isConferenceIcs && (linphone_chat_message_get_file_transfer_information(chat) || linphone_chat_message_get_external_body_url(chat))) cell.isFirst = [self isFirstIndexInTableView:indexPath chat:chat];
kCellId = NSStringFromClass(UIChatBubblePhotoCell.class); cell.isLast = [self isLastIndexInTableView:indexPath chat:chat];
else [cell update];
kCellId = NSStringFromClass(UIChatBubbleTextCell.class); }
// To use less memory and to avoid overlapping. To be improved.
UIChatBubbleTextCell *cell = [tableView dequeueReusableCellWithIdentifier:kCellId];
cell = [[NSClassFromString(kCellId) alloc] initWithIdentifier:kCellId];
[cell setEvent:event];
if (chat) {
cell.isFirst = [self isFirstIndexInTableView:indexPath chat:chat];
cell.isLast = [self isLastIndexInTableView:indexPath chat:chat];
[cell update];
}
[cell setChatRoomDelegate:_chatRoomDelegate]; [cell setChatRoomDelegate:_chatRoomDelegate];
[super accessoryForCell:cell atPath:indexPath]; [super accessoryForCell:cell atPath:indexPath];
cell.selectionStyle = UITableViewCellSelectionStyleNone; cell.selectionStyle = UITableViewCellSelectionStyleNone;
@ -378,6 +379,13 @@ static const int BASIC_EVENT_LIST=15;
[_chatRoomDelegate tableViewIsScrolling]; [_chatRoomDelegate tableViewIsScrolling];
} }
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
if (scrollView.contentOffset.y >= (scrollView.contentSize.height - scrollView.frame.size.height)) {
if ([UIApplication sharedApplication].applicationState == UIApplicationStateActive)
[ChatConversationView markAsRead:_chatRoom];
}
}
static const CGFloat MESSAGE_SPACING_PERCENTAGE = 1.f; static const CGFloat MESSAGE_SPACING_PERCENTAGE = 1.f;
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath { - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
@ -443,12 +451,11 @@ static const CGFloat MESSAGE_SPACING_PERCENTAGE = 1.f;
LinphoneEventLog *event = [[eventList objectAtIndex:indexPath.row] pointerValue]; LinphoneEventLog *event = [[eventList objectAtIndex:indexPath.row] pointerValue];
UIContextualAction *imdnAction = [UIContextualAction contextualActionWithStyle:UIContextualActionStyleNormal UIContextualAction *imdnAction = [UIContextualAction contextualActionWithStyle:UIContextualActionStyleNormal
title:NSLocalizedString(@"Info", nil) title:NSLocalizedString(@"Info", nil)
handler:^(UIContextualAction * _Nonnull action, __kindof UIView * _Nonnull sourceView, void (^ _Nonnull completionHandler)(BOOL)) { handler:^(UIContextualAction * _Nonnull action, __kindof UIView * _Nonnull sourceView, void (^ _Nonnull completionHandler)(BOOL)) {
LinphoneChatMessage *msg = linphone_event_log_get_chat_message(event); ChatConversationImdnView *view = VIEW(ChatConversationImdnView);
ChatConversationImdnView *view = VIEW(ChatConversationImdnView); view.event = event;
view.msg = msg; [PhoneMainView.instance changeCurrentView:view.compositeViewDescription];
[PhoneMainView.instance changeCurrentView:view.compositeViewDescription];
}]; }];
UISwipeActionsConfiguration *swipeActionConfig; UISwipeActionsConfiguration *swipeActionConfig;

View file

@ -63,6 +63,7 @@
@property(nonatomic) LinphoneChatRoomCbs *chatRoomCbs; @property(nonatomic) LinphoneChatRoomCbs *chatRoomCbs;
@property(nonatomic) Boolean markAsRead; @property(nonatomic) Boolean markAsRead;
@property(nonatomic) const char *peerAddress; @property(nonatomic) const char *peerAddress;
@property(nonatomic) const char *localAddress;
@property (strong, nonatomic) FileDataSource *FileDataSource; @property (strong, nonatomic) FileDataSource *FileDataSource;
@ -112,6 +113,7 @@
@property LinphonePlayer *sharedVoicePlayer; @property LinphonePlayer *sharedVoicePlayer;
@property BOOL showVoiceRecorderView; @property BOOL showVoiceRecorderView;
@property BOOL preservePendingActions; @property BOOL preservePendingActions;
@property BOOL *sharingMedia;
// Reply // Reply
@property (weak, nonatomic) IBOutlet UIView *replyView; @property (weak, nonatomic) IBOutlet UIView *replyView;

View file

@ -139,7 +139,7 @@ static UICompositeViewDescription *compositeDescription = nil;
if (compositeDescription == nil) { if (compositeDescription == nil) {
compositeDescription = [[UICompositeViewDescription alloc] init:self.class compositeDescription = [[UICompositeViewDescription alloc] init:self.class
statusBar:StatusBarView.class statusBar:StatusBarView.class
tabBar:nil tabBar:IPAD ? TabBarView.class :nil
sideMenu:SideMenuView.class sideMenu:SideMenuView.class
fullscreen:false fullscreen:false
isLeftFragment:NO isLeftFragment:NO
@ -196,7 +196,8 @@ static UICompositeViewDescription *compositeDescription = nil;
_vrInnerView.layer.masksToBounds = YES; _vrInnerView.layer.masksToBounds = YES;
_vrWaveMaskPlayer.backgroundColor = [UIColor colorWithPatternImage:[UIImage imageNamed:@"color_L"]]; // rgba(1,88,7,0.2); _vrWaveMaskPlayer.backgroundColor = [UIColor colorWithPatternImage:[UIImage imageNamed:@"color_L"]]; // rgba(1,88,7,0.2);
_showVoiceRecorderView = false; _showVoiceRecorderView = false;
_toggleMenuButton.imageView.contentMode = UIViewContentModeScaleAspectFit;
_toggleRecord.imageView.contentMode = UIViewContentModeScaleAspectFit;
} }
- (void)refreshData { - (void)refreshData {
@ -212,10 +213,17 @@ static UICompositeViewDescription *compositeDescription = nil;
- (void)viewWillAppear:(BOOL)animated { - (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated]; [super viewWillAppear:animated];
if (!_chatRoom)
[self onLinphoneCoreReady:nil];
[NSNotificationCenter.defaultCenter addObserver:self [NSNotificationCenter.defaultCenter addObserver:self
selector:@selector(applicationWillEnterBackground) selector:@selector(applicationDidEnterBackground)
name:UIApplicationDidEnterBackgroundNotification name:UIApplicationDidEnterBackgroundNotification
object:nil]; object:nil];
[NSNotificationCenter.defaultCenter addObserver:self
selector:@selector(applicationWillEnterForeground)
name:UIApplicationWillEnterForegroundNotification
object:nil];
[NSNotificationCenter.defaultCenter addObserver:self [NSNotificationCenter.defaultCenter addObserver:self
selector:@selector(keyboardWillShow:) selector:@selector(keyboardWillShow:)
name:UIKeyboardWillShowNotification name:UIKeyboardWillShowNotification
@ -232,10 +240,10 @@ static UICompositeViewDescription *compositeDescription = nil;
selector:@selector(callUpdateEvent:) selector:@selector(callUpdateEvent:)
name:kLinphoneCallUpdate name:kLinphoneCallUpdate
object:nil]; object:nil];
[NSNotificationCenter.defaultCenter addObserver:self [NSNotificationCenter.defaultCenter addObserver:self
selector:@selector(onLinphoneCoreReady:) selector:@selector(onLinphoneCoreReady:)
name:kLinphoneGlobalStateUpdate name:kLinphoneGlobalStateUpdate
object:nil]; object:nil];
[NSNotificationCenter.defaultCenter addObserver:self [NSNotificationCenter.defaultCenter addObserver:self
selector:@selector(endVoicePlayingIfDoingSO:) selector:@selector(endVoicePlayingIfDoingSO:)
@ -246,24 +254,23 @@ static UICompositeViewDescription *compositeDescription = nil;
selector:@selector(endVoicePlayingIfDoingSO:) selector:@selector(endVoicePlayingIfDoingSO:)
name:kLinphoneVoiceMessagePlayerEOF name:kLinphoneVoiceMessagePlayerEOF
object:nil]; object:nil];
if ([_fileContext count] > 0) { if ([_fileContext count] > 0) {
[UIView animateWithDuration:0 [UIView animateWithDuration:0
delay:0 delay:0
options:UIViewAnimationOptionBeginFromCurrentState options:UIViewAnimationOptionBeginFromCurrentState
animations:^{ animations:^{
// resizing imagesView // resizing imagesView
CGRect imagesFrame = [_imagesView frame]; CGRect imagesFrame = [_imagesView frame];
imagesFrame.origin.y = [_messageView frame].origin.y - 120; imagesFrame.origin.y = [_messageView frame].origin.y - 120;
imagesFrame.size.height = 120; imagesFrame.size.height = 120;
[_imagesView setFrame:imagesFrame]; [_imagesView setFrame:imagesFrame];
// resizing chatTable // resizing chatTable
CGRect tableViewFrame = [_tableController.tableView frame]; CGRect tableViewFrame = [_tableController.tableView frame];
tableViewFrame.size.height -= 120; tableViewFrame.size.height -= 120;
[_tableController.tableView setFrame:tableViewFrame]; [_tableController.tableView setFrame:tableViewFrame];
[self updateFramesInclRecordingAndReplyView]; [self updateFramesInclRecordingAndReplyView];
} } completion:nil];
completion:nil]; }
}
[self configureForRoom:self.editing]; [self configureForRoom:self.editing];
// Resize the popup table depending on wether ephemeral messages are enabled or not. // Resize the popup table depending on wether ephemeral messages are enabled or not.
@ -273,12 +280,13 @@ static UICompositeViewDescription *compositeDescription = nil;
// Voice recording & Replies // Voice recording & Replies
_vrView.hidden = true; _vrView.hidden = true;
_toggleRecord.enabled = linphone_core_get_calls_nb(LC) == 0; _toggleRecord.enabled = linphone_core_get_calls_nb(LC) == 0;
_replyView.hidden = true; _replyView.hidden = true;
_preservePendingActions = false; _preservePendingActions = false;
_toggleRecord.enabled = linphone_core_get_calls_nb(LC) == 0; _toggleRecord.enabled = linphone_core_get_calls_nb(LC) == 0;
[PhoneMainView.instance hideTabBar:!IPAD];
} }
- (void)viewWillDisappear:(BOOL)animated { - (void)viewWillDisappear:(BOOL)animated {
@ -307,6 +315,8 @@ static UICompositeViewDescription *compositeDescription = nil;
[NSNotificationCenter.defaultCenter removeObserver:self]; [NSNotificationCenter.defaultCenter removeObserver:self];
PhoneMainView.instance.currentRoom = NULL; PhoneMainView.instance.currentRoom = NULL;
[[UIApplication sharedApplication] setIdleTimerDisabled:false]; [[UIApplication sharedApplication] setIdleTimerDisabled:false];
_chatRoom = NULL;
_tableController.chatRoom = nil;
} }
- (void)removeCallBacks { - (void)removeCallBacks {
@ -323,27 +333,31 @@ static UICompositeViewDescription *compositeDescription = nil;
return; return;
} }
composingVisible = !composingVisible; composingVisible = !composingVisible;
[self setComposingVisible:!composingVisible withDelay:0];
// force offset recomputing // force offset recomputing
[_messageField refreshHeight]; [_messageField refreshHeight];
LinphoneAddress *peerAddr = linphone_core_create_address([LinphoneManager getLc], _peerAddress); LinphoneAddress *peerAddr = linphone_core_create_address([LinphoneManager getLc], _peerAddress);
if (peerAddr) { LinphoneAddress *localAddr = linphone_core_create_address([LinphoneManager getLc], _localAddress);
_chatRoom = linphone_core_get_chat_room([LinphoneManager getLc], peerAddr); if (peerAddr && localAddr) {
isOneToOne = linphone_chat_room_get_capabilities(_chatRoom) & LinphoneChatRoomCapabilitiesOneToOne; _chatRoom = linphone_core_search_chat_room([LinphoneManager getLc], NULL, localAddr, peerAddr, NULL);
isEncrypted = linphone_chat_room_get_capabilities(_chatRoom) & LinphoneChatRoomCapabilitiesEncrypted; if (_chatRoom) {
isOneToOne = linphone_chat_room_get_capabilities(_chatRoom) & LinphoneChatRoomCapabilitiesOneToOne;
isEncrypted = linphone_chat_room_get_capabilities(_chatRoom) & LinphoneChatRoomCapabilitiesEncrypted;
[self setComposingVisible:!composingVisible withDelay:0];
[self configureForRoom:true];
[_tableController scrollToBottom:true];
}
} }
[self configureForRoom:true];
_backButton.hidden = _tableController.isEditing;
[_tableController scrollToBottom:true];
[self refreshImageDrawer];
[self stopAllPlays];
_backButton.hidden = _tableController.isEditing;
[self refreshImageDrawer];
[self stopAllPlays];
[self keyboardWillHide:nil];
} }
#pragma mark - #pragma mark -
- (void)applicationWillEnterBackground{ - (void)applicationDidEnterBackground{
if (!_preservePendingActions) if (!_preservePendingActions)
[self cancelVoiceRecording]; [self cancelVoiceRecording];
else if (_isVoiceRecording) else if (_isVoiceRecording)
@ -351,9 +365,18 @@ static UICompositeViewDescription *compositeDescription = nil;
if (!_preservePendingActions) if (!_preservePendingActions)
[self closePendingReply]; [self closePendingReply];
[self stopAllPlays]; [self stopAllPlays];
_chatRoom = nil;
[_messageField resignFirstResponder];
} }
- (void)applicationWillEnterForeground{
if (_chatRoom == nil) {
if (linphone_core_get_calls_nb(LC) == 0)
[SVProgressHUD show];
else
[self onLinphoneCoreReady:nil];
}
}
- (void)configureForRoom:(BOOL)editing { - (void)configureForRoom:(BOOL)editing {
if (!_chatRoom) { if (!_chatRoom) {
@ -426,7 +449,7 @@ static UICompositeViewDescription *compositeDescription = nil;
if (!room) if (!room)
return true; return true;
LinphoneChatRoomCapabilitiesMask capabilities = linphone_chat_room_get_capabilities(room); LinphoneChatRoomCapabilitiesMask capabilities = linphone_chat_room_get_capabilities(room);
return capabilities & LinphoneChatRoomCapabilitiesBasic; return capabilities & LinphoneChatRoomCapabilitiesEncrypted;
} }
@ -466,7 +489,7 @@ static UICompositeViewDescription *compositeDescription = nil;
//file shared from photo lib //file shared from photo lib
NSString *fileName = dict[@"url"]; NSString *fileName = dict[@"url"];
[_messageField setText:dict[@"message"]]; [_messageField setText:dict[@"message"]];
[self confirmShare:[self nsDataRead] url:nil fileName:fileName]; [self confirmShare:[self nsDataRead] url:nil fileName:fileName];
[defaults removeObjectForKey:@"photoData"]; [defaults removeObjectForKey:@"photoData"];
} else if (dictFile) { } else if (dictFile) {
NSString *fileName = dictFile[@"url"]; NSString *fileName = dictFile[@"url"];
@ -483,23 +506,26 @@ static UICompositeViewDescription *compositeDescription = nil;
// reload the chatroom after the core starts // reload the chatroom after the core starts
- (void)onLinphoneCoreReady:(NSNotification *)notif { - (void)onLinphoneCoreReady:(NSNotification *)notif {
if ((LinphoneGlobalState)[[[notif userInfo] valueForKey:@"state"] integerValue] == LinphoneGlobalOn) { if (linphone_core_get_global_state(LC) == LinphoneGlobalOn) {
LinphoneAddress *peerAddr = linphone_core_create_address([LinphoneManager getLc], _peerAddress); LinphoneAddress *peerAddr = linphone_core_create_address([LinphoneManager getLc], _peerAddress);
if (peerAddr) { LinphoneAddress *localAddr = linphone_core_create_address([LinphoneManager getLc], _localAddress);
_chatRoom = linphone_core_get_chat_room([LinphoneManager getLc], peerAddr); if (peerAddr && localAddr) {
isOneToOne = linphone_chat_room_get_capabilities(_chatRoom) & LinphoneChatRoomCapabilitiesOneToOne; _chatRoom = linphone_core_search_chat_room([LinphoneManager getLc], NULL, localAddr, peerAddr, NULL);
isEncrypted = linphone_chat_room_get_capabilities(_chatRoom) & LinphoneChatRoomCapabilitiesEncrypted; if (_chatRoom) {
} isOneToOne = linphone_chat_room_get_capabilities(_chatRoom) & LinphoneChatRoomCapabilitiesOneToOne;
[self configureForRoom:self.editing]; isEncrypted = linphone_chat_room_get_capabilities(_chatRoom) & LinphoneChatRoomCapabilitiesEncrypted;
if (_chatRoom && _markAsRead) { [self configureForRoom:self.editing];
if (IPAD) { if (_chatRoom && _markAsRead) {
[VIEW(ChatsListView).tableController loadData]; if (IPAD) {
[VIEW(ChatsListView).tableController loadData];
}
[ChatConversationView markAsRead:_chatRoom];
}
_markAsRead = TRUE;
} }
}
[ChatConversationView markAsRead:_chatRoom]; [SVProgressHUD dismiss];
} }
_markAsRead = TRUE;
}
} }
- (void)callUpdateEvent:(NSNotification *)notif { - (void)callUpdateEvent:(NSNotification *)notif {
@ -598,9 +624,9 @@ static UICompositeViewDescription *compositeDescription = nil;
} }
- (void)confirmShare:(NSData *)data url:(NSString *)url fileName:(NSString *)fileName { - (void)confirmShare:(NSData *)data url:(NSString *)url fileName:(NSString *)fileName {
DTActionSheet *sheet = [[DTActionSheet alloc] initWithTitle:@""]; DTActionSheet *sheet = [[DTActionSheet alloc] initWithTitle:NSLocalizedString(@"Select or create a conversation to share the file(s)", nil)];
dispatch_async(dispatch_get_main_queue(), ^{ dispatch_async(dispatch_get_main_queue(), ^{
[sheet addButtonWithTitle:NSLocalizedString(@"send to this conversation", nil) [sheet addButtonWithTitle:NSLocalizedString(@"Send to this conversation", nil)
block:^() { block:^() {
if (![[self.messageField text] isEqualToString:@""]) { if (![[self.messageField text] isEqualToString:@""]) {
[self sendMessageInMessageField:linphone_chat_room_create_empty_message(_chatRoom)]; [self sendMessageInMessageField:linphone_chat_room_create_empty_message(_chatRoom)];
@ -657,6 +683,11 @@ static UICompositeViewDescription *compositeDescription = nil;
completion:^(BOOL finished) { completion:^(BOOL finished) {
_composeIndicatorView.hidden = !visible; _composeIndicatorView.hidden = !visible;
}]; }];
if (visible) {
if (_tableController.tableView.contentOffset.y + newComposingFrame.size.height >= (_tableController.tableView.contentSize.height - _tableController.tableView.frame.size.height - 1)) {
[_tableController scrollToBottom:TRUE];
}
}
} }
- (BOOL) groupCallAvailable { - (BOOL) groupCallAvailable {
@ -771,9 +802,9 @@ static UICompositeViewDescription *compositeDescription = nil;
// if we're showing the compose message, update it position // if we're showing the compose message, update it position
if (![_composeLabel isHidden]) { if (![_composeLabel isHidden]) {
CGRect frame = [_composeLabel frame]; CGRect frame = [_composeIndicatorView frame];
frame.origin.y -= diff; frame.origin.y -= diff;
[_composeLabel setFrame:frame]; [_composeIndicatorView setFrame:frame];
} }
} }
} }
@ -781,7 +812,15 @@ static UICompositeViewDescription *compositeDescription = nil;
#pragma mark - Action Functions #pragma mark - Action Functions
- (IBAction)onBackClick:(id)event { - (IBAction)onBackClick:(id)event {
[PhoneMainView.instance popCurrentView]; NSString *previousViewName = [PhoneMainView.instance getPreviousViewName];
_sharingMedia = nil;
if ([previousViewName isEqualToString:@"ContactDetailsView"]) {
ContactDetailsView *view = VIEW(ContactDetailsView);
[PhoneMainView.instance popToView:view.compositeViewDescription];
} else {
ChatsListView *view = VIEW(ChatsListView);
[PhoneMainView.instance popToView:view.compositeViewDescription];
}
} }
- (IBAction)onEditClick:(id)event { - (IBAction)onEditClick:(id)event {
@ -937,6 +976,7 @@ static UICompositeViewDescription *compositeDescription = nil;
view.oldSubject = [NSString stringWithUTF8String:linphone_chat_room_get_subject(_chatRoom) ?: LINPHONE_DUMMY_SUBJECT]; view.oldSubject = [NSString stringWithUTF8String:linphone_chat_room_get_subject(_chatRoom) ?: LINPHONE_DUMMY_SUBJECT];
view.room = _chatRoom; view.room = _chatRoom;
view.peerAddress = _peerAddress; view.peerAddress = _peerAddress;
view.localAddress = _localAddress;
[PhoneMainView.instance changeCurrentView:view.compositeViewDescription]; [PhoneMainView.instance changeCurrentView:view.compositeViewDescription];
} }
@ -1415,12 +1455,11 @@ void on_chat_room_chat_message_received(LinphoneChatRoom *cr, const LinphoneEven
bool isDisplayingBottomOfTable = [view.tableController.tableView indexPathsForVisibleRows].lastObject.row == [view.tableController totalNumberOfItems] - 1; bool isDisplayingBottomOfTable = [view.tableController.tableView indexPathsForVisibleRows].lastObject.row == [view.tableController totalNumberOfItems] - 1;
[view.tableController addEventEntry:(LinphoneEventLog *)event_log]; [view.tableController addEventEntry:(LinphoneEventLog *)event_log];
[NSNotificationCenter.defaultCenter postNotificationName:kLinphoneMessageReceived object:view];
if (isDisplayingBottomOfTable) { if (isDisplayingBottomOfTable) {
[view.tableController scrollToBottom:TRUE]; [view.tableController scrollToBottom:TRUE];
} else { } else {
[[view.tableController scrollBadge] setHidden:FALSE];
int unread_msg = linphone_chat_room_get_unread_messages_count(cr); int unread_msg = linphone_chat_room_get_unread_messages_count(cr);
[[view.tableController scrollBadge] setText:[NSString stringWithFormat:@"%d", unread_msg]]; [[view.tableController scrollBadge] setText:[NSString stringWithFormat:@"%d", unread_msg]];
} }
@ -1721,13 +1760,6 @@ void on_chat_room_conference_alert(LinphoneChatRoom *cr, const LinphoneEventLog
} }
} }
-(BOOL) isConversationMuted {
return FALSE; // TODO
}
-(void) toggleMuteConversation {
// TODO
}
-(void) showAddressAndIdentityPopup { -(void) showAddressAndIdentityPopup {
char *localAddress = linphone_address_as_string(linphone_chat_room_get_local_address(_chatRoom)); char *localAddress = linphone_address_as_string(linphone_chat_room_get_local_address(_chatRoom));
@ -1754,8 +1786,7 @@ void on_chat_room_conference_alert(LinphoneChatRoom *cr, const LinphoneEventLog
} }
-(BOOL) canAdminEphemeral:(const LinphoneChatRoom *)cr { -(BOOL) canAdminEphemeral:(const LinphoneChatRoom *)cr {
if (!cr) return FALSE; if (!cr || !isEncrypted) return FALSE;
if ([ChatConversationView isBasicChatRoom:_chatRoom]) return FALSE;
// If ephemeral mode is DeviceManaged, then we don't need to check anything else // If ephemeral mode is DeviceManaged, then we don't need to check anything else
return (linphone_chat_room_params_get_ephemeral_mode(linphone_chat_room_get_current_params(cr)) == LinphoneChatRoomEphemeralModeDeviceManaged) return (linphone_chat_room_params_get_ephemeral_mode(linphone_chat_room_get_current_params(cr)) == LinphoneChatRoomEphemeralModeDeviceManaged)
@ -1773,42 +1804,57 @@ void on_chat_room_conference_alert(LinphoneChatRoom *cr, const LinphoneEventLog
_popupMenu.layer.masksToBounds = false; _popupMenu.layer.masksToBounds = false;
_toggleMenuButton.hidden = false; _toggleMenuButton.hidden = false;
_popupMenu.tableFooterView = [UIView new]; _popupMenu.tableFooterView = [UIView new];
_popupMenu.separatorStyle = UITableViewCellSeparatorStyleNone;
[_popupMenu reloadData]; [_popupMenu reloadData];
} }
-(void) tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { -(void) tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
[self onToggleMenu:nil]; [self onToggleMenu:nil];
BOOL isEncrypted = ![ChatConversationView isBasicChatRoom:_chatRoom];
if (indexPath.row == 0) { int firstIndex = isOneToOne ? 0 : 1;
if (!isOneToOne && indexPath.row == 0) { //Schedule meeting
[ConferenceViewModelBridge scheduleFromGroupChatWithCChatRoom:_chatRoom];
[PhoneMainView.instance popToView:ConferenceSchedulingView.compositeViewDescription];
}
if (indexPath.row == firstIndex) {
if (isOneToOne) { if (isOneToOne) {
[self addOrGoToContact:linphone_chat_room_get_peer_address(_chatRoom)]; if (isEncrypted) {
LinphoneAddress* contactAddress = linphone_address_clone(linphone_participant_get_address(bctbx_list_nth_data(linphone_chat_room_get_participants(_chatRoom), 0)));
linphone_address_clean(contactAddress);
[self addOrGoToContact:contactAddress];
linphone_address_unref(contactAddress);
} else {
[self addOrGoToContact:linphone_chat_room_get_peer_address(_chatRoom)];
}
} else { } else {
[self displayGroupInfo]; [self displayGroupInfo];
} }
} }
if (isEncrypted && indexPath.row == 1) { if (isEncrypted && indexPath.row == 1+firstIndex) {
[self goToDeviceListView]; [self goToDeviceListView];
} }
BOOL canEphemeral = [self canAdminEphemeral:_chatRoom]; BOOL canEphemeral = [self canAdminEphemeral:_chatRoom];
if (canEphemeral && indexPath.row == 2) { if (canEphemeral && indexPath.row == 2+firstIndex) {
EphemeralSettingsView *view = VIEW(EphemeralSettingsView); EphemeralSettingsView *view = VIEW(EphemeralSettingsView);
view.room = _chatRoom; view.room = _chatRoom;
[PhoneMainView.instance popToView:view.compositeViewDescription]; [PhoneMainView.instance popToView:view.compositeViewDescription];
} }
if ((!isEncrypted && indexPath.row == 1) || (isEncrypted && indexPath.row == 3)) { if ((!isEncrypted && indexPath.row == 1+firstIndex) || (isEncrypted && indexPath.row == 3+firstIndex)) {
[self toggleMuteConversation]; [LinphoneManager setChatroomPushEnabled:_chatRoom withPushEnabled:![LinphoneManager getChatroomPushEnabled:_chatRoom]];
[_popupMenu reloadData];
} }
if ((!isEncrypted && indexPath.row == 2) || (isEncrypted && indexPath.row == 4)) { if ((!isEncrypted && indexPath.row == 2+firstIndex) || (isEncrypted && indexPath.row == 4+firstIndex)) {
[_tableController onEditClick:nil]; [_tableController onEditClick:nil];
[self onEditionChangeClick:nil]; [self onEditionChangeClick:nil];
} }
if ((isEncrypted && ((!canEphemeral && indexPath.row == 4)||(canEphemeral && indexPath.row == 5))) if ((isEncrypted && ((!canEphemeral && indexPath.row == 4+firstIndex)||(canEphemeral && indexPath.row == 5+firstIndex)))
|| (!isEncrypted && indexPath.row == 3)) { || (!isEncrypted && indexPath.row == 3+firstIndex)) {
[self showAddressAndIdentityPopup]; [self showAddressAndIdentityPopup];
} }
} }
@ -1831,61 +1877,89 @@ void on_chat_room_conference_alert(LinphoneChatRoom *cr, const LinphoneEventLog
if ([self canAdminEphemeral:_chatRoom]) if ([self canAdminEphemeral:_chatRoom])
++nbRows; ++nbRows;
if (!isOneToOne) // schedule meeting
++nbRows;
return nbRows; return nbRows;
} }
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [[UITableViewCell alloc] init]; UITableViewCell *cell = [[UITableViewCell alloc] init];
if (indexPath.row == 0) { if (!_chatRoom) {
// Workaround to avoid crash in background for release 4.7. This shouldn't happen though, so there may be a deeper issue not found yet
return cell;
}
int firstIndex = isOneToOne ? 0 : 1;
if (!isOneToOne && indexPath.row == 0) {
cell.imageView.image = [UIImage imageNamed:@"menu_voip_meeting_schedule"];
cell.textLabel.text = NSLocalizedString(@"Schedule a meeting",nil);
}
if (indexPath.row == firstIndex) {
if (isOneToOne) { if (isOneToOne) {
Contact *contact = [FastAddressBook getContactWithAddress:linphone_chat_room_get_peer_address(_chatRoom)]; Contact *contact;
if (isEncrypted) {
LinphoneAddress * contactAddress = linphone_address_clone(linphone_participant_get_address(bctbx_list_nth_data(linphone_chat_room_get_participants(_chatRoom), 0)));
linphone_address_clean(contactAddress);
contact = [FastAddressBook getContactWithAddress:contactAddress];
linphone_address_unref(contactAddress);
} else {
contact = [FastAddressBook getContactWithAddress:linphone_chat_room_get_peer_address(_chatRoom)];
}
if (contact == nil) { if (contact == nil) {
cell.imageView.image = [LinphoneUtils resizeImage:[UIImage imageNamed:@"contact_add_default.png"] newSize:CGSizeMake(20, 25)]; cell.imageView.image = [UIImage imageNamed:@"contact_add_default.png"];
cell.textLabel.text = NSLocalizedString(@"Add to contacts",nil); cell.textLabel.text = NSLocalizedString(@"Add to contacts",nil);
} else { } else {
cell.imageView.image = [LinphoneUtils resizeImage:[UIImage imageNamed:@"contacts_all_default.png"] newSize:CGSizeMake(20, 25)]; cell.imageView.image = [UIImage imageNamed:@"contacts_all_default.png"];
cell.textLabel.text = NSLocalizedString(@"Go to contact",nil); cell.textLabel.text = NSLocalizedString(@"Go to contact",nil);
} }
} else { } else {
cell.imageView.image = [LinphoneUtils resizeImage:[UIImage imageNamed:@"chat_group_informations.png"] newSize:CGSizeMake(25, 25)]; cell.imageView.image = [UIImage imageNamed:@"chat_group_informations.png"];
cell.textLabel.text = NSLocalizedString(@"Group infos",nil); cell.textLabel.text = NSLocalizedString(@"Group infos",nil);
} }
} }
if (isEncrypted && indexPath.row == 1) { if (isEncrypted && indexPath.row == 1+firstIndex) {
cell.imageView.image = [LinphoneUtils resizeImage:[UIImage imageNamed:@"menu_security_default.png"] newSize:CGSizeMake(20, 25)]; cell.imageView.image = [UIImage imageNamed:@"menu_security_default.png"];
cell.textLabel.text = NSLocalizedString(@"Conversation's devices",nil); cell.textLabel.text = NSLocalizedString(@"Conversation's devices",nil);
} }
bool canEphemeral = [self canAdminEphemeral:_chatRoom]; bool canEphemeral = [self canAdminEphemeral:_chatRoom];
if (canEphemeral && indexPath.row == 2) { if (canEphemeral && indexPath.row == 2+firstIndex) {
cell.imageView.image = [LinphoneUtils resizeImage:[UIImage imageNamed:@"ephemeral_messages_default.png"] newSize:CGSizeMake(20, 25)]; cell.imageView.image = [UIImage imageNamed:@"ephemeral_messages_default.png"];
cell.textLabel.text = NSLocalizedString(@"Ephemeral messages",nil); cell.textLabel.text = NSLocalizedString(@"Ephemeral messages",nil);
} }
if ((isEncrypted && indexPath.row == 3) || (!isEncrypted && indexPath.row == 1)) { if ((isEncrypted && indexPath.row == 3+firstIndex) || (!isEncrypted && indexPath.row == 1+firstIndex)) {
if ([self isConversationMuted]) { if ([LinphoneManager getChatroomPushEnabled:_chatRoom]) {
cell.imageView.image = [LinphoneUtils resizeImage:[UIImage imageNamed:@"menu_notifications_off.png"] newSize:CGSizeMake(20, 25)]; cell.imageView.image = [UIImage imageNamed:@"menu_notifications_off.png"];
cell.textLabel.text = NSLocalizedString(@"NOT IMPLEMENTED",nil); cell.textLabel.text = NSLocalizedString(@"Mute notifications",nil);
} else { } else {
cell.imageView.image = [LinphoneUtils resizeImage:[UIImage imageNamed:@"menu_notifications_on.png"] newSize:CGSizeMake(20, 25)]; cell.imageView.image = [UIImage imageNamed:@"menu_notifications_on.png"];
cell.textLabel.text = NSLocalizedString(@"NOT IMPLEMENTED",nil); cell.textLabel.text = NSLocalizedString(@"Un-mute notifications",nil);
} }
} }
if ((isEncrypted && indexPath.row == 4) || (!isEncrypted && indexPath.row == 2)) { if ((isEncrypted && indexPath.row == 4+firstIndex) || (!isEncrypted && indexPath.row == 2+firstIndex)) {
cell.imageView.image = [LinphoneUtils resizeImage:[UIImage imageNamed:@"delete_default.png"] newSize:CGSizeMake(20, 25)]; cell.imageView.image = [UIImage imageNamed:@"delete_default.png"];
cell.textLabel.text = NSLocalizedString(@"Delete messages",nil); cell.textLabel.text = NSLocalizedString(@"Delete messages",nil);
} }
if ((isEncrypted && ((!canEphemeral && indexPath.row == 4)||(canEphemeral && indexPath.row == 5))) if ((isEncrypted && ((!canEphemeral && indexPath.row == 4+firstIndex)||(canEphemeral && indexPath.row == 5+firstIndex)))
|| (!isEncrypted && indexPath.row == 3)) { || (!isEncrypted && indexPath.row == 3+firstIndex)) {
cell.imageView.image = [LinphoneUtils resizeImage:[UIImage imageNamed:@"chat_group_informations.png"] newSize:CGSizeMake(25, 25)]; cell.imageView.image = [UIImage imageNamed:@"chat_group_informations.png"];
cell.textLabel.text = NSLocalizedString(@"Show address and identity",nil); cell.textLabel.text = NSLocalizedString(@"Debug infos",nil);
} }
cell.imageView.contentMode = UIViewContentModeScaleAspectFit; UIImageView * icon = [[UIImageView alloc] initWithFrame:CGRectMake(tableView.frame.size.width-37, 7, 30, 30)];
icon.contentMode = UIViewContentModeScaleAspectFit;
icon.image = cell.imageView.image;
[cell.contentView addSubview:icon];
cell.imageView.image = nil;
return cell; return cell;
} }
- (IBAction)onToggleMenu:(id)sender { - (IBAction)onToggleMenu:(id)sender {
@ -2194,6 +2268,7 @@ void on_shared_player_eof_reached(LinphonePlayer *p) {
_showReplyView = true; _showReplyView = true;
[self updateFramesInclRecordingAndReplyView]; [self updateFramesInclRecordingAndReplyView];
[self.tableController scrollToMessage:message]; [self.tableController scrollToMessage:message];
[self.messageField becomeFirstResponder];
} }
-(void) handlePendingTransferIfAny { -(void) handlePendingTransferIfAny {

View file

@ -125,6 +125,7 @@ static int sorted_history_comparison(LinphoneChatRoom *to_insert, LinphoneChatRo
[LinphoneManager.instance lpConfigSetBool:FALSE forKey:@"create_chat"]; [LinphoneManager.instance lpConfigSetBool:FALSE forKey:@"create_chat"];
} else if (![self selectFirstRow]) { } else if (![self selectFirstRow]) {
ChatConversationCreateView *view = VIEW(ChatConversationCreateView); ChatConversationCreateView *view = VIEW(ChatConversationCreateView);
[view fragmentCompositeDescription];
view.tableController.notFirstTime = FALSE; view.tableController.notFirstTime = FALSE;
view.isForVoipConference = FALSE; view.isForVoipConference = FALSE;
[PhoneMainView.instance changeCurrentView:view.compositeViewDescription]; [PhoneMainView.instance changeCurrentView:view.compositeViewDescription];
@ -190,7 +191,7 @@ static int sorted_history_comparison(LinphoneChatRoom *to_insert, LinphoneChatRo
void deletion_chat_room_state_changed(LinphoneChatRoom *cr, LinphoneChatRoomState newState) { void deletion_chat_room_state_changed(LinphoneChatRoom *cr, LinphoneChatRoomState newState) {
LinphoneChatRoomCbs *cbs = linphone_chat_room_get_current_callbacks(cr); LinphoneChatRoomCbs *cbs = linphone_chat_room_get_current_callbacks(cr);
ChatsListTableView *view = (__bridge ChatsListTableView *)linphone_chat_room_cbs_get_user_data(cbs) ?: NULL; ChatsListTableView *view =cbs ? ((__bridge ChatsListTableView *)linphone_chat_room_cbs_get_user_data(cbs) ?: NULL) : NULL;
if (!view) if (!view)
return; return;
@ -231,7 +232,9 @@ void deletion_chat_room_state_changed(LinphoneChatRoom *cr, LinphoneChatRoomStat
} }
} }
[ftdToDelete cancel]; [ftdToDelete cancel];
// Re-enable push notification after deleting the chatroom, in order to get the notification if we are re-invited, or for secure 1-to-1 chatrooms.
[LinphoneManager setChatroomPushEnabled:chatRoom withPushEnabled:TRUE];
linphone_core_delete_chat_room(LC, chatRoom); linphone_core_delete_chat_room(LC, chatRoom);
chatRooms = chatRooms->next; chatRooms = chatRooms->next;
} }

View file

@ -43,4 +43,6 @@
- (IBAction)onEditionChangeClick:(id)sender; - (IBAction)onEditionChangeClick:(id)sender;
- (IBAction)onDeleteClick:(id)sender; - (IBAction)onDeleteClick:(id)sender;
-(void) mediaSharing;
@end @end

View file

@ -39,6 +39,10 @@
selector:@selector(ephemeralDeleted:) selector:@selector(ephemeralDeleted:)
name:kLinphoneEphemeralMessageDeletedInRoom name:kLinphoneEphemeralMessageDeletedInRoom
object:nil]; object:nil];
[NSNotificationCenter.defaultCenter addObserver:self
selector:@selector(displayModeChanged)
name:kDisplayModeChanged
object:nil];
[_backToCallButton update]; [_backToCallButton update];
self.tableController.waitView = _waitView; self.tableController.waitView = _waitView;
[self setEditing:NO]; [self setEditing:NO];
@ -54,20 +58,43 @@
forControlEvents:UIControlEventTouchUpInside]; forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:button];*/ [self.view addSubview:button];*/
BOOL forwardMode = VIEW(ChatConversationView).pendingForwardMessage != nil; [self mediaSharing];
}
- (void)mediaSharing{
BOOL forwardMode;
NSString* groupName = [NSString stringWithFormat:@"group.%@.linphoneExtension",[[NSBundle mainBundle] bundleIdentifier]];
NSUserDefaults *defaults = [[NSUserDefaults alloc] initWithSuiteName:groupName];
NSDictionary *dict = [defaults valueForKey:@"photoData"];
NSDictionary *dictFile = [defaults valueForKey:@"icloudData"];
NSDictionary *dictUrl = [defaults valueForKey:@"url"];
if(dict||dictFile||dictUrl) VIEW(ChatConversationView).sharingMedia = TRUE;
if(VIEW(ChatConversationView).sharingMedia == nil){
forwardMode = VIEW(ChatConversationView).pendingForwardMessage != nil;
}else{
forwardMode = VIEW(ChatConversationView).sharingMedia != nil;
}
_tableController.editButton.hidden = forwardMode; _tableController.editButton.hidden = forwardMode;
_forwardTitle.text = NSLocalizedString(@"Select a discussion or create a new one",nil); if(VIEW(ChatConversationView).sharingMedia == nil){
_forwardTitle.text = NSLocalizedString(@"Select a discussion or create a new one",nil);
}
else{
_forwardTitle.text = NSLocalizedString(@"Select or create a conversation to share the file(s)",nil);
}
_forwardTitle.hidden = !forwardMode; _forwardTitle.hidden = !forwardMode;
_cancelForwardButton.hidden = !forwardMode; _cancelForwardButton.hidden = !forwardMode;
_tableController.tableView.frame = CGRectMake(0, 66 + (forwardMode ? _forwardTitle.frame.size.height : 0), _tableController.tableView.frame.size.width, self.view.frame.size.height - 66 - ( forwardMode ? _forwardTitle.frame.size.height : 0 )); _tableController.tableView.frame = CGRectMake(0, 66 + (forwardMode ? _forwardTitle.frame.size.height : 0), _tableController.tableView.frame.size.width, self.view.frame.size.height - 66 - ( forwardMode ? _forwardTitle.frame.size.height : 0 ));
_addButton.frame = CGRectMake(forwardMode ? 82 : 0 , _addButton.frame.origin.y, _addButton.frame.size.width, _addButton.frame.size.height); _addButton.frame = CGRectMake(forwardMode ? 82 : 0 , _addButton.frame.origin.y, _addButton.frame.size.width, _addButton.frame.size.height);
_addGroupChatButton.frame = CGRectMake(forwardMode ? 164 : 82 , _addGroupChatButton.frame.origin.y, _addGroupChatButton.frame.size.width, _addGroupChatButton.frame.size.height); _addGroupChatButton.frame = CGRectMake(forwardMode ? 164 : 82 , _addGroupChatButton.frame.origin.y, _addGroupChatButton.frame.size.width, _addGroupChatButton.frame.size.height);
} }
- (void)displayModeChanged{
[self.tableController.tableView reloadData];
}
- (void)ephemeralDeleted:(NSNotification *)notif { - (void)ephemeralDeleted:(NSNotification *)notif {
//LinphoneChatRoom *r =[[notif.userInfo objectForKey:@"room"] intValue]; //LinphoneChatRoom *r =[[notif.userInfo objectForKey:@"room"] intValue];
@ -117,6 +144,7 @@ static UICompositeViewDescription *compositeDescription = nil;
- (void)newChatCreate:(BOOL)isGroup { - (void)newChatCreate:(BOOL)isGroup {
ChatConversationCreateView *view = VIEW(ChatConversationCreateView); ChatConversationCreateView *view = VIEW(ChatConversationCreateView);
[view fragmentCompositeDescription];
view.isForEditing = false; view.isForEditing = false;
view.isGroupChat = isGroup; view.isGroupChat = isGroup;
view.tableController.notFirstTime = FALSE; view.tableController.notFirstTime = FALSE;
@ -172,8 +200,16 @@ static UICompositeViewDescription *compositeDescription = nil;
} }
- (IBAction)onCancelForwardClicked:(id)sender { - (IBAction)onCancelForwardClicked:(id)sender {
VIEW(ChatConversationView).sharingMedia = nil;
VIEW(ChatConversationView).pendingForwardMessage = nil; VIEW(ChatConversationView).pendingForwardMessage = nil;
NSString* groupName = [NSString stringWithFormat:@"group.%@.linphoneExtension",[[NSBundle mainBundle] bundleIdentifier]];
NSUserDefaults *defaults = [[NSUserDefaults alloc] initWithSuiteName:groupName];
[defaults removeObjectForKey:@"photoData"];
[defaults removeObjectForKey:@"icloudData"];
[defaults removeObjectForKey:@"url"];
[PhoneMainView.instance popCurrentView]; [PhoneMainView.instance popCurrentView];
} }
@end @end

View file

@ -35,7 +35,7 @@
@property(nonatomic, strong) NSMutableArray *sipAddresses; @property(nonatomic, strong) NSMutableArray *sipAddresses;
@property(nonatomic, strong) NSMutableArray *emails; @property(nonatomic, strong) NSMutableArray *emails;
@property(nonatomic, strong) NSMutableArray *phones; @property(nonatomic, strong) NSMutableArray *phones;
@property BOOL createdFromLdap; @property BOOL createdFromLdapOrProvisioning;
@property BOOL added; @property BOOL added;
- (void)setAvatar:(UIImage *)avatar; - (void)setAvatar:(UIImage *)avatar;
@ -58,4 +58,6 @@
- (BOOL)removeSipAddressAtIndex:(NSInteger)index; - (BOOL)removeSipAddressAtIndex:(NSInteger)index;
- (BOOL)removePhoneNumberAtIndex:(NSInteger)index; - (BOOL)removePhoneNumberAtIndex:(NSInteger)index;
- (BOOL)removeEmailAtIndex:(NSInteger)index; - (BOOL)removeEmailAtIndex:(NSInteger)index;
- (NSMutableArray*)getSipAddressesWithoutDuplicatePhoneNumbers;
@end @end

View file

@ -36,7 +36,7 @@
_person = acncontact; _person = acncontact;
_friend = afriend ? linphone_friend_ref(afriend) : NULL; _friend = afriend ? linphone_friend_ref(afriend) : NULL;
_added = FALSE; _added = FALSE;
_createdFromLdap = FALSE; _createdFromLdapOrProvisioning = FALSE;
_phones = [[NSMutableArray alloc] init]; _phones = [[NSMutableArray alloc] init];
_sipAddresses = [[NSMutableArray alloc] init]; _sipAddresses = [[NSMutableArray alloc] init];
_emails = [[NSMutableArray alloc] init]; _emails = [[NSMutableArray alloc] init];
@ -70,7 +70,7 @@
linphone_friend_set_ref_key(_friend, key); linphone_friend_set_ref_key(_friend, key);
linphone_friend_set_name(_friend, [NSString stringWithFormat:@"%@%@", _firstName ? _firstName : @"", _lastName ? [_firstName ? @" " : @"" stringByAppendingString:_lastName] : @""] .UTF8String); linphone_friend_set_name(_friend, [NSString stringWithFormat:@"%@%@", _firstName ? _firstName : @"", _lastName ? [_firstName ? @" " : @"" stringByAppendingString:_lastName] : @""] .UTF8String);
for (NSString *sipAddr in _sipAddresses) { for (NSString *sipAddr in _sipAddresses) {
LinphoneAddress *addr = linphone_core_interpret_url(LC, sipAddr.UTF8String); LinphoneAddress *addr = linphone_core_interpret_url_2(LC, sipAddr.UTF8String, true);
if (addr) { if (addr) {
linphone_address_set_display_name(addr, [self displayName].UTF8String); linphone_address_set_display_name(addr, [self displayName].UTF8String);
linphone_friend_add_address(_friend, addr); linphone_friend_add_address(_friend, addr);
@ -287,7 +287,7 @@
[_person setValue:tmpSipAddresses forKey:CNContactInstantMessageAddressesKey]; [_person setValue:tmpSipAddresses forKey:CNContactInstantMessageAddressesKey];
ret = TRUE; ret = TRUE;
} else { } else {
LinphoneAddress *addr = linphone_core_interpret_url(LC, sip.UTF8String) ?: linphone_address_new(sip.UTF8String); LinphoneAddress *addr = linphone_core_interpret_url_2(LC, sip.UTF8String, true) ?: linphone_address_new(sip.UTF8String);
if (!addr) if (!addr)
return FALSE; return FALSE;
@ -370,7 +370,7 @@
} }
ret = TRUE; ret = TRUE;
} else { } else {
LinphoneAddress *addr = linphone_core_interpret_url(LC, ((NSString *)_sipAddresses[index]).UTF8String); LinphoneAddress *addr = linphone_core_interpret_url_2(LC, ((NSString *)_sipAddresses[index]).UTF8String, true);
if (!addr) if (!addr)
return FALSE; return FALSE;
@ -465,7 +465,7 @@
linphone_friend_set_ref_key(_friend, key); linphone_friend_set_ref_key(_friend, key);
linphone_friend_set_name(_friend, [NSString stringWithFormat:@"%@%@", _firstName ? _firstName : @"", _lastName ? [_firstName ? @" " : @"" stringByAppendingString:_lastName] : @""] .UTF8String); linphone_friend_set_name(_friend, [NSString stringWithFormat:@"%@%@", _firstName ? _firstName : @"", _lastName ? [_firstName ? @" " : @"" stringByAppendingString:_lastName] : @""] .UTF8String);
for (NSString *sipAddr in _sipAddresses) { for (NSString *sipAddr in _sipAddresses) {
LinphoneAddress *addr = linphone_core_interpret_url(LC, sipAddr.UTF8String); LinphoneAddress *addr = linphone_core_interpret_url_2(LC, sipAddr.UTF8String, true);
if (addr) { if (addr) {
linphone_address_set_display_name(addr, [self displayName].UTF8String); linphone_address_set_display_name(addr, [self displayName].UTF8String);
linphone_friend_add_address(_friend, addr); linphone_friend_add_address(_friend, addr);
@ -488,4 +488,23 @@
_friend = NULL; _friend = NULL;
} }
- (NSMutableArray*)getSipAddressesWithoutDuplicatePhoneNumbers {
NSMutableArray* resAdresses = [[NSMutableArray alloc] init];
for (NSString *address in _sipAddresses) {
LinphoneAddress *addr = linphone_core_interpret_url_2(LC, [address UTF8String], YES);
bool isFoundInPhones = false;
if (addr && linphone_address_get_username(addr)) {
for (NSString *phoneNb in _phones) {
if ([phoneNb isEqualToString:[NSString stringWithUTF8String:linphone_address_get_username(addr)]]) {
isFoundInPhones = true;
break;
}
}
}
if (!isFoundInPhones) [resAdresses addObject:address];
}
return resAdresses;
}
@end @end

View file

@ -160,15 +160,15 @@
/*first and last name only when editting */ /*first and last name only when editting */
return (self.tableView.isEditing) ? 1 : 0; return (self.tableView.isEditing) ? 1 : 0;
} else if (section == ContactSections_Sip) { } else if (section == ContactSections_Sip) {
return _contact.createdFromLdap ? 0 : _contact.sipAddresses.count; return [_contact getSipAddressesWithoutDuplicatePhoneNumbers].count;
} else if (section == ContactSections_Number) { } else if (section == ContactSections_Number) {
return _contact.phones.count; return _contact.phones.count;
} else if (section == ContactSections_Email) { } else if (section == ContactSections_Email) {
BOOL showEmails = [LinphoneManager.instance BOOL showEmails = [LinphoneManager.instance
lpConfigBoolForKey:@"show_contacts_emails_preference"]; lpConfigBoolForKey:@"show_contacts_emails_preference"];
return showEmails ? _contact.emails.count : 0; return showEmails ? _contact.emails.count : 0;
} }
return 0; return 0;
} }
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
@ -200,7 +200,7 @@
LinphoneAddress *addr = NULL; LinphoneAddress *addr = NULL;
if ([LinphoneManager.instance if ([LinphoneManager.instance
lpConfigBoolForKey:@"contact_display_username_only"] && lpConfigBoolForKey:@"contact_display_username_only"] &&
(addr = linphone_core_interpret_url(LC, [value UTF8String]))) { (addr = linphone_core_interpret_url_2(LC, [value UTF8String], YES))) {
value = value =
[NSString stringWithCString:linphone_address_get_username(addr) [NSString stringWithCString:linphone_address_get_username(addr)
encoding:[NSString defaultCStringEncoding]]; encoding:[NSString defaultCStringEncoding]];
@ -283,7 +283,7 @@
if (section == ContactSections_Number) { if (section == ContactSections_Number) {
text = NSLocalizedString(@"Phone numbers", nil); text = NSLocalizedString(@"Phone numbers", nil);
addEntryName = NSLocalizedString(@"Add new phone number", nil); addEntryName = NSLocalizedString(@"Add new phone number", nil);
} else if (section == ContactSections_Sip && !_contact.createdFromLdap) { } else if (section == ContactSections_Sip && !_contact.createdFromLdapOrProvisioning) {
text = NSLocalizedString(@"SIP addresses", nil); text = NSLocalizedString(@"SIP addresses", nil);
addEntryName = NSLocalizedString(@"Add new SIP address", nil); addEntryName = NSLocalizedString(@"Add new SIP address", nil);
} else if (section == ContactSections_Email && } else if (section == ContactSections_Email &&

View file

@ -102,7 +102,7 @@
} }
for (NSString *sipAddr in _contact.sipAddresses) { for (NSString *sipAddr in _contact.sipAddresses) {
LinphoneAddress *addr = linphone_core_interpret_url(LC, sipAddr.UTF8String); LinphoneAddress *addr = linphone_core_interpret_url_2(LC, sipAddr.UTF8String, true);
if (addr) { if (addr) {
linphone_friend_add_address(_contact.friend, addr); linphone_friend_add_address(_contact.friend, addr);
linphone_address_destroy(addr); linphone_address_destroy(addr);
@ -119,8 +119,8 @@
_contact = acontact; _contact = acontact;
_emptyLabel.hidden = (_contact != NULL); _emptyLabel.hidden = (_contact != NULL);
_avatarImage.hidden = !_emptyLabel.hidden; _avatarImage.hidden = !_emptyLabel.hidden;
_deleteButton.hidden = !_emptyLabel.hidden || [_contact createdFromLdap]; _deleteButton.hidden = !_emptyLabel.hidden || [_contact createdFromLdapOrProvisioning];
_editButton.hidden = !_emptyLabel.hidden || [_contact createdFromLdap]; _editButton.hidden = !_emptyLabel.hidden || [_contact createdFromLdapOrProvisioning];
[_avatarImage setImage:[FastAddressBook imageForContact:_contact] bordered:NO withRoundedRadius:YES]; [_avatarImage setImage:[FastAddressBook imageForContact:_contact] bordered:NO withRoundedRadius:YES];
[ContactDisplay setDisplayNameLabel:_nameLabel forContact:_contact]; [ContactDisplay setDisplayNameLabel:_nameLabel forContact:_contact];
@ -146,7 +146,7 @@
} }
- (void)addCurrentContactContactField:(NSString *)address { - (void)addCurrentContactContactField:(NSString *)address {
LinphoneAddress *linphoneAddress = linphone_core_interpret_url(LC, address.UTF8String); LinphoneAddress *linphoneAddress = linphone_core_interpret_url_2(LC, address.UTF8String, true);
NSString *username = NSString *username =
linphoneAddress ? [NSString stringWithUTF8String:linphone_address_get_username(linphoneAddress)] : address; linphoneAddress ? [NSString stringWithUTF8String:linphone_address_get_username(linphoneAddress)] : address;
@ -284,6 +284,9 @@
if (IPAD && self.contact == NULL) { if (IPAD && self.contact == NULL) {
_editButton.hidden = TRUE; _editButton.hidden = TRUE;
_deleteButton.hidden = TRUE; _deleteButton.hidden = TRUE;
} else if (self.contact != NULL && self.contact.createdFromLdapOrProvisioning) {
_editButton.hidden = TRUE;
_deleteButton.hidden = TRUE;
} }
PhoneMainView.instance.currentName = _nameLabel.text; PhoneMainView.instance.currentName = _nameLabel.text;
// Update presence for contact // Update presence for contact
@ -296,6 +299,7 @@
[_editButton setImage:[UIImage imageNamed:@"valid_default.png"] forState:UIControlStateSelected]; [_editButton setImage:[UIImage imageNamed:@"valid_default.png"] forState:UIControlStateSelected];
[self updateBackOrCancelButton]; [self updateBackOrCancelButton];
[self recomputeTableViewSize:FALSE];
} }
- (void)deviceOrientationDidChange:(NSNotification*)notif { - (void)deviceOrientationDidChange:(NSNotification*)notif {
@ -310,9 +314,13 @@
} }
} }
if (self.contact != NULL && self.contact.createdFromLdapOrProvisioning) {
_editButton.hidden = TRUE;
_deleteButton.hidden = TRUE;
}
_nameLabel.hidden = self.tableController.isEditing;
[self updateBackOrCancelButton]; [self updateBackOrCancelButton];
[self recomputeTableViewSize:self.tableController.isEditing];
[self recomputeTableViewSize:_editButton.hidden];
} }
- (void)viewWillDisappear:(BOOL)animated { - (void)viewWillDisappear:(BOOL)animated {
@ -408,10 +416,11 @@ static UICompositeViewDescription *compositeDescription = nil;
- (void)recomputeTableViewSize:(BOOL)editing { - (void)recomputeTableViewSize:(BOOL)editing {
CGRect frame = _tableController.tableView.frame; CGRect frame = _tableController.tableView.frame;
frame.origin.y = _avatarImage.frame.size.height + _avatarImage.frame.origin.y;
if ([self viewIsCurrentlyPortrait] && !editing) { if ([self viewIsCurrentlyPortrait] && !editing) {
frame.origin.y += _nameLabel.frame.size.height; frame.origin.y = _nameLabel.frame.origin.y + _nameLabel.frame.size.height;
} } else {
frame.origin.y = _avatarImage.frame.size.height + _avatarImage.frame.origin.y;
}
frame.size.height = _tableController.tableView.contentSize.height; frame.size.height = _tableController.tableView.contentSize.height;
_tableController.tableView.frame = frame; _tableController.tableView.frame = frame;
@ -514,11 +523,11 @@ static UICompositeViewDescription *compositeDescription = nil;
} }
NSString* previous = [PhoneMainView.instance getPreviousViewName]; NSString* previous = [PhoneMainView.instance getPreviousViewName];
if ([previous isEqualToString:@"ContactsListView"] || [previous isEqualToString:@"ImagePickerView"]) { if ([previous isEqualToString:@"HistoryDetailsView"]) {
ContactsListView *view = VIEW(ContactsListView); HistoryDetailsView *view = VIEW(HistoryDetailsView);
[PhoneMainView.instance popToView:view.compositeViewDescription]; [PhoneMainView.instance popToView:view.compositeViewDescription];
} else { } else {
HistoryDetailsView *view = VIEW(HistoryDetailsView); ContactsListView *view = VIEW(ContactsListView);
[PhoneMainView.instance popToView:view.compositeViewDescription]; [PhoneMainView.instance popToView:view.compositeViewDescription];
} }
} }
@ -588,7 +597,7 @@ static UICompositeViewDescription *compositeDescription = nil;
[[store unifiedContactWithIdentifier:contactToCheck.identifier keysToFetch:keysToFetch error:nil] mutableCopy]; [[store unifiedContactWithIdentifier:contactToCheck.identifier keysToFetch:keysToFetch error:nil] mutableCopy];
if(mCNContact == NULL){ if(mCNContact == NULL){
for(NSString *address in contactToCheck.sipAddresses){ for(NSString *address in contactToCheck.sipAddresses){
NSString *name = [FastAddressBook normalizeSipURI:address]; NSString *name = [FastAddressBook normalizeSipURI:address use_prefix:TRUE];
if([LinphoneManager.instance.fastAddressBook.addressBookMap objectForKey:name]){ if([LinphoneManager.instance.fastAddressBook.addressBookMap objectForKey:name]){
return TRUE; return TRUE;
} }

View file

@ -58,6 +58,7 @@ typedef enum _ContactSelectionMode { ContactSelectionModeNone, ContactSelectionM
@property (weak, nonatomic) IBOutlet UIInterfaceStyleButton *toggleSelectionButton; @property (weak, nonatomic) IBOutlet UIInterfaceStyleButton *toggleSelectionButton;
@property (weak, nonatomic) IBOutlet UILabel *loadingLabel; @property (weak, nonatomic) IBOutlet UILabel *loadingLabel;
@property (weak, nonatomic) IBOutlet UIView *loadingView; @property (weak, nonatomic) IBOutlet UIView *loadingView;
@property (weak, nonatomic) IBOutlet UILabel *ldapMoreResultsLabel;
- (IBAction)onAllClick:(id)event; - (IBAction)onAllClick:(id)event;
- (IBAction)onLinphoneClick:(id)event; - (IBAction)onLinphoneClick:(id)event;

View file

@ -142,6 +142,15 @@ static UICompositeViewDescription *compositeDescription = nil;
selector:@selector(onMagicSearchFinished:) selector:@selector(onMagicSearchFinished:)
name:kLinphoneMagicSearchFinished name:kLinphoneMagicSearchFinished
object:nil]; object:nil];
[[NSNotificationCenter defaultCenter]
addObserver:self
selector:@selector(onMagicSearchMoreAvailable:)
name:kLinphoneMagicSearchMoreAvailable
object:nil];
[NSNotificationCenter.defaultCenter addObserver:self
selector:@selector(displayModeChanged)
name:kDisplayModeChanged
object:nil];
} }
- (void)onMagicSearchStarted:(NSNotification *)k { - (void)onMagicSearchStarted:(NSNotification *)k {
@ -150,6 +159,9 @@ static UICompositeViewDescription *compositeDescription = nil;
- (void)onMagicSearchFinished:(NSNotification *)k { - (void)onMagicSearchFinished:(NSNotification *)k {
_loadingView.hidden = TRUE; _loadingView.hidden = TRUE;
} }
- (void)onMagicSearchMoreAvailable:(NSNotification *)k {
_ldapMoreResultsLabel.hidden = FALSE;
}
- (void)viewDidAppear:(BOOL)animated { - (void)viewDidAppear:(BOOL)animated {
NSLog(@"Debuglog viewDidAppear"); NSLog(@"Debuglog viewDidAppear");
@ -225,7 +237,7 @@ static UICompositeViewDescription *compositeDescription = nil;
} }
- (void)refreshButtons { - (void)refreshButtons {
[addButton setHidden:FALSE]; [addButton setHidden:![LinphoneManager.instance lpConfigBoolForKey:@"enable_native_address_book"]];
[self changeView:[ContactSelection getSipFilterEnabled] ? ContactsLinphone : ContactsAll]; [self changeView:[ContactSelection getSipFilterEnabled] ? ContactsLinphone : ContactsAll];
} }
@ -250,6 +262,10 @@ static UICompositeViewDescription *compositeDescription = nil;
} }
} }
- (void)displayModeChanged{
[self.tableController.tableView reloadData];
}
- (IBAction)onDeleteClick:(id)sender { - (IBAction)onDeleteClick:(id)sender {
NSString *msg = [NSString stringWithFormat:NSLocalizedString(@"Do you want to delete selected contacts?\nThey will also be deleted from your phone's address book.", nil)]; NSString *msg = [NSString stringWithFormat:NSLocalizedString(@"Do you want to delete selected contacts?\nThey will also be deleted from your phone's address book.", nil)];
[LinphoneManager.instance setContactsUpdated:TRUE]; [LinphoneManager.instance setContactsUpdated:TRUE];
@ -268,6 +284,9 @@ static UICompositeViewDescription *compositeDescription = nil;
- (IBAction)onEditionChangeClick:(id)sender { - (IBAction)onEditionChangeClick:(id)sender {
allButton.hidden = linphoneButton.hidden = _selectedButtonImage.hidden = addButton.hidden = self.tableController.isEditing; allButton.hidden = linphoneButton.hidden = _selectedButtonImage.hidden = addButton.hidden = self.tableController.isEditing;
if ([LinphoneManager.instance lpConfigBoolForKey:@"enable_native_address_book"]) {
addButton.hidden = self.tableController.isEditing;
}
} }
- (void)searchBarCancelButtonClicked:(UISearchBar *)searchBar { - (void)searchBarCancelButtonClicked:(UISearchBar *)searchBar {
@ -292,6 +311,7 @@ static UICompositeViewDescription *compositeDescription = nil;
if (searchText.length == 0) { if (searchText.length == 0) {
[LinphoneManager.instance setContactsUpdated:TRUE]; [LinphoneManager.instance setContactsUpdated:TRUE];
} }
_ldapMoreResultsLabel.hidden = TRUE;
[tableController loadDataWithFilter:searchText]; [tableController loadDataWithFilter:searchText];
} }
} }

View file

@ -145,9 +145,17 @@ static UICompositeViewDescription *compositeDescription = nil;
[_videoCameraSwitch setHidden:FALSE]; [_videoCameraSwitch setHidden:FALSE];
} }
} }
[_addContactButton setImage:[UIImage imageNamed:@"voip_conference_new"] forState:UIControlStateNormal];
_addContactButton.imageView.contentMode = UIViewContentModeScaleAspectFit; LinphoneAccount *defaultAccount = linphone_core_get_default_account(LC);
_addContactButton.enabled = true; if (!(defaultAccount && linphone_account_params_get_conference_factory_uri(linphone_account_get_params(defaultAccount)))){
[_addContactButton setImage:[UIImage imageNamed:@"contact_add_default"] forState:UIControlStateNormal];
_addContactButton.imageView.contentMode = UIViewContentModeScaleAspectFit;
_addContactButton.enabled = true;
}else{
[_addContactButton setImage:[UIImage imageNamed:@"voip_conference_new"] forState:UIControlStateNormal];
_addContactButton.imageView.contentMode = UIViewContentModeScaleAspectFit;
_addContactButton.enabled = true;
}
} }
- (void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation - (void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation
@ -175,10 +183,11 @@ static UICompositeViewDescription *compositeDescription = nil;
_padView.hidden = !IPAD && UIInterfaceOrientationIsLandscape(toInterfaceOrientation); _padView.hidden = !IPAD && UIInterfaceOrientationIsLandscape(toInterfaceOrientation);
if (linphone_core_get_calls_nb(LC)) { if (linphone_core_get_calls_nb(LC)) {
_backButton.hidden = FALSE; _backButton.hidden = FALSE;
_addContactButton.hidden = TRUE;
} else { } else {
_backButton.hidden = TRUE; _backButton.hidden = TRUE;
_addContactButton.hidden = FALSE;
} }
_addContactButton.hidden = FALSE;
} }
- (void)viewDidAppear:(BOOL)animated { - (void)viewDidAppear:(BOOL)animated {
@ -392,19 +401,38 @@ static UICompositeViewDescription *compositeDescription = nil;
#pragma mark - Action Functions #pragma mark - Action Functions
- (IBAction)onAddContactClick:(id)event { - (IBAction)onAddContactClick:(id)event {
ConferenceSchedulingView *view = VIEW(ConferenceSchedulingView); LinphoneAccount *defaultAccount = linphone_core_get_default_account(LC);
[view resetViewModel]; if (!(defaultAccount && linphone_account_params_get_conference_factory_uri(linphone_account_get_params(defaultAccount)))){
[PhoneMainView.instance changeCurrentView:ConferenceSchedulingView.compositeViewDescription]; [ContactSelection setSelectionMode:ContactSelectionModeEdit];
[ContactSelection setAddAddress:[_addressField text]];
[ContactSelection enableSipFilter:FALSE];
[PhoneMainView.instance changeCurrentView:ContactsListView.compositeViewDescription];
}else{
ConferenceSchedulingView *view = VIEW(ConferenceSchedulingView);
[view resetViewModel];
[PhoneMainView.instance changeCurrentView:ConferenceSchedulingView.compositeViewDescription];
}
} }
- (IBAction)onBackClick:(id)event { - (IBAction)onBackClick:(id)event {
[PhoneMainView.instance popToView:ActiveCallOrConferenceView.compositeViewDescription]; [PhoneMainView.instance popToView:[CallsViewModelBridge callViewToDisplay]];
} }
- (IBAction)onAddressChange:(id)sender { - (IBAction)onAddressChange:(id)sender {
if ([self displayDebugPopup:_addressField.text]) { if ([self displayDebugPopup:_addressField.text]) {
_addressField.text = @""; _addressField.text = @"";
} }
LinphoneAccount *defaultAccount = linphone_core_get_default_account(LC);
if (!(defaultAccount && linphone_account_params_get_conference_factory_uri(linphone_account_get_params(defaultAccount)))){
[_addContactButton setImage:[UIImage imageNamed:@"contact_add_default"] forState:UIControlStateNormal];
_addContactButton.enabled = ([[_addressField text] length] > 0);
if ([_addressField.text length] == 0) {
[self.view endEditing:YES];
}
}else{
[_addContactButton setImage:[UIImage imageNamed:@"voip_conference_new"] forState:UIControlStateNormal];
_addContactButton.enabled = true;
}
} }
- (IBAction)onBackspaceClick:(id)sender { - (IBAction)onBackspaceClick:(id)sender {

View file

@ -69,14 +69,15 @@ static UICompositeViewDescription *compositeDescription = nil;
object:nil]; object:nil];
// Update on show // Update on show
const MSList *list = linphone_core_get_account_list([LinphoneManager getLc]); MSList *list = [LinphoneManager.instance createAccountsNotHiddenList];
if (list != NULL) { if (list != NULL) {
LinphoneAccount *account = (LinphoneAccount *)list->data; LinphoneAccount *account = (LinphoneAccount *)list->data;
if (account) { if (account) {
[self registrationUpdate:linphone_account_get_state(account)]; [self registrationUpdate:linphone_account_get_state(account)];
} }
} }
bctbx_list_free(list);
if (account_creator) { if (account_creator) {
linphone_account_creator_unref(account_creator); linphone_account_creator_unref(account_creator);
} }

View file

@ -41,6 +41,7 @@
@property (weak, nonatomic) IBOutlet UIView *waitView; @property (weak, nonatomic) IBOutlet UIView *waitView;
@property (weak, nonatomic) IBOutlet UIRoundedImageView *linphoneImage; @property (weak, nonatomic) IBOutlet UIRoundedImageView *linphoneImage;
@property (weak, nonatomic) IBOutlet UIView *optionsView; @property (weak, nonatomic) IBOutlet UIView *optionsView;
@property(weak, nonatomic) IBOutlet UIButton *chatButton;
@property (weak, nonatomic) IBOutlet UIView *encryptedChatView; @property (weak, nonatomic) IBOutlet UIView *encryptedChatView;
- (IBAction)onBackClick:(id)event; - (IBAction)onBackClick:(id)event;

View file

@ -68,6 +68,12 @@ static UICompositeViewDescription *compositeDescription = nil;
[_headerView addGestureRecognizer:headerTapGesture]; [_headerView addGestureRecognizer:headerTapGesture];
} }
- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation {
_chatButton.hidden = [LinphoneManager.instance lpConfigBoolForKey:@"force_lime_chat_rooms"];
[super didRotateFromInterfaceOrientation:fromInterfaceOrientation];
[self update];
}
- (void)viewWillAppear:(BOOL)animated { - (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated]; [super viewWillAppear:animated];
_waitView.hidden = YES; _waitView.hidden = YES;

View file

@ -268,9 +268,13 @@
} else { } else {
if (linphone_call_log_was_conference(callLog)) { if (linphone_call_log_was_conference(callLog)) {
LinphoneConferenceInfo *confInfo = linphone_call_log_get_conference_info(callLog); LinphoneConferenceInfo *confInfo = linphone_call_log_get_conference_info(callLog);
ConferenceWaitingRoomFragment *view = VIEW(ConferenceWaitingRoomFragment); if (linphone_conference_info_get_state(confInfo) == LinphoneConferenceInfoStateCancelled) {
[ConferenceViewModelBridge showCancelledMeetingWithCConferenceInfo:confInfo];
return;
}
ConferenceWaitingRoomView *view = VIEW(ConferenceWaitingRoomView);
[view setDetailsWithSubject:[NSString stringWithUTF8String:linphone_conference_info_get_subject(confInfo)] url:[NSString stringWithUTF8String:linphone_address_as_string(linphone_conference_info_get_uri(confInfo))]]; [view setDetailsWithSubject:[NSString stringWithUTF8String:linphone_conference_info_get_subject(confInfo)] url:[NSString stringWithUTF8String:linphone_address_as_string(linphone_conference_info_get_uri(confInfo))]];
[PhoneMainView.instance changeCurrentView:ConferenceWaitingRoomFragment.compositeViewDescription]; [PhoneMainView.instance changeCurrentView:ConferenceWaitingRoomView.compositeViewDescription];
} else { } else {
const LinphoneAddress *addr = linphone_call_log_get_remote_address(callLog); const LinphoneAddress *addr = linphone_call_log_get_remote_address(callLog);
[LinphoneManager.instance call:addr]; [LinphoneManager.instance call:addr];

View file

@ -67,10 +67,15 @@ static UICompositeViewDescription *compositeDescription = nil;
// Fake event // Fake event
[NSNotificationCenter.defaultCenter postNotificationName:kLinphoneCallUpdate object:self]; [NSNotificationCenter.defaultCenter postNotificationName:kLinphoneCallUpdate object:self];
[_toggleSelectionButton setImage:[UIImage imageNamed:@"select_all_default.png"] forState:UIControlStateSelected]; [_toggleSelectionButton setImage:[UIImage imageNamed:@"select_all_default.png"] forState:UIControlStateSelected];
[NSNotificationCenter.defaultCenter addObserver:self
selector:@selector(displayModeChanged)
name:kDisplayModeChanged
object:nil];
} }
- (void) viewWillDisappear:(BOOL)animated { - (void) viewWillDisappear:(BOOL)animated {
self.view = NULL; self.view = NULL;
[NSNotificationCenter.defaultCenter removeObserver:self];
} }
#pragma mark - #pragma mark -
@ -100,6 +105,10 @@ static UICompositeViewDescription *compositeDescription = nil;
_selectedButtonImage.frame = frame; _selectedButtonImage.frame = frame;
} }
- (void)displayModeChanged{
[self.tableController.tableView reloadData];
}
#pragma m ~ark - Action Functions #pragma m ~ark - Action Functions
- (IBAction)onAllClick:(id)event { - (IBAction)onAllClick:(id)event {

View file

@ -35,6 +35,8 @@
#import <IntentsUI/IntentsUI.h> #import <IntentsUI/IntentsUI.h>
#import "linphoneapp-Swift.h" #import "linphoneapp-Swift.h"
#import "SVProgressHUD.h"
#ifdef USE_CRASHLYTICS #ifdef USE_CRASHLYTICS
#include "FIRApp.h" #include "FIRApp.h"
@ -85,6 +87,7 @@
[LinphoneManager.instance.fastAddressBook reloadFriends]; [LinphoneManager.instance.fastAddressBook reloadFriends];
[NSNotificationCenter.defaultCenter postNotificationName:kLinphoneMessageReceived object:nil]; [NSNotificationCenter.defaultCenter postNotificationName:kLinphoneMessageReceived object:nil];
} }
- (void)applicationWillResignActive:(UIApplication *)application { - (void)applicationWillResignActive:(UIApplication *)application {
@ -137,7 +140,6 @@
if ((floor(NSFoundationVersionNumber) <= NSFoundationVersionNumber_iOS_9_x_Max)) { if ((floor(NSFoundationVersionNumber) <= NSFoundationVersionNumber_iOS_9_x_Max)) {
if ([LinphoneManager.instance lpConfigBoolForKey:@"autoanswer_notif_preference"]) { if ([LinphoneManager.instance lpConfigBoolForKey:@"autoanswer_notif_preference"]) {
linphone_call_accept(call); linphone_call_accept(call);
[PhoneMainView.instance changeCurrentView:ActiveCallOrConferenceView.compositeViewDescription];
} else { } else {
[PhoneMainView.instance displayIncomingCall:call]; [PhoneMainView.instance displayIncomingCall:call];
} }
@ -156,7 +158,9 @@
[self handleShortcut:_shortcutItem]; [self handleShortcut:_shortcutItem];
_shortcutItem = nil; _shortcutItem = nil;
} }
#if TARGET_IPHONE_SIMULATOR
#else
[[UNUserNotificationCenter currentNotificationCenter] requestAuthorizationWithOptions:(UNAuthorizationOptionAlert | UNAuthorizationOptionSound | UNAuthorizationOptionBadge) [[UNUserNotificationCenter currentNotificationCenter] requestAuthorizationWithOptions:(UNAuthorizationOptionAlert | UNAuthorizationOptionSound | UNAuthorizationOptionBadge)
completionHandler:^(BOOL granted, NSError *_Nullable error) { completionHandler:^(BOOL granted, NSError *_Nullable error) {
if (error) if (error)
@ -182,6 +186,16 @@
} }
}]; }];
#endif
if ([UIDeviceBridge switchedDisplayMode]) {
[AvatarBridge prepareIt];
[NSNotificationCenter.defaultCenter postNotificationName:kDisplayModeChanged object:nil];
[PhoneMainView.instance.mainViewController removeEntryFromCache:ChatConversationCreateView.compositeViewDescription.name];
[PhoneMainView.instance.mainViewController changeView:PhoneMainView.instance.currentView];
[UIDeviceBridge notifyDisplayModeSwitch];
}
} }
@ -331,8 +345,9 @@
return NO; return NO;
} }
[PhoneMainView.instance.mainViewController getCachedController:ActiveCallOrConferenceView.compositeViewDescription.name]; // This will create the single instance of the ActiveCallOrConferenceView including listeneres [PhoneMainView.instance.mainViewController getCachedController:SingleCallView.compositeViewDescription.name]; // This will create the single instance of the SingleCallView including listeneres
[PhoneMainView.instance.mainViewController getCachedController:ConferenceCallView.compositeViewDescription.name]; // This will create the single instance of the ConferenceCallView including listeneres
[CallsViewModelBridge setupCallsViewNavigation];
return YES; return YES;
} }
@ -352,6 +367,7 @@
- (BOOL)handleShortcut:(UIApplicationShortcutItem *)shortcutItem { - (BOOL)handleShortcut:(UIApplicationShortcutItem *)shortcutItem {
BOOL success = NO; BOOL success = NO;
if ([shortcutItem.type isEqualToString:@"linphone.phone.action.newMessage"]) { if ([shortcutItem.type isEqualToString:@"linphone.phone.action.newMessage"]) {
[VIEW(ChatConversationCreateView) fragmentCompositeDescription];
[PhoneMainView.instance changeCurrentView:ChatConversationCreateView.compositeViewDescription]; [PhoneMainView.instance changeCurrentView:ChatConversationCreateView.compositeViewDescription];
success = YES; success = YES;
} }
@ -388,7 +404,15 @@
[PhoneMainView.instance presentViewController:errView animated:YES completion:nil]; [PhoneMainView.instance presentViewController:errView animated:YES completion:nil];
} else if([[url scheme] isEqualToString:@"message-linphone"]) { } else if([[url scheme] isEqualToString:@"message-linphone"]) {
[PhoneMainView.instance popToView:ChatsListView.compositeViewDescription]; if ([[PhoneMainView.instance currentView] equal:ChatsListView.compositeViewDescription]) {
VIEW(ChatConversationView).sharingMedia = TRUE;
ChatsListView *view = VIEW(ChatsListView);
[view mediaSharing];
}else{
[SVProgressHUD dismiss];
VIEW(ChatConversationView).sharingMedia = TRUE;
[PhoneMainView.instance popToView:ChatsListView.compositeViewDescription];
}
} else if ([scheme isEqualToString:@"sip"]||[scheme isEqualToString:@"sips"]) { } else if ([scheme isEqualToString:@"sip"]||[scheme isEqualToString:@"sips"]) {
// remove "sip://" from the URI, and do it correctly by taking resourceSpecifier and removing leading and // remove "sip://" from the URI, and do it correctly by taking resourceSpecifier and removing leading and
// trailing "/" // trailing "/"
@ -503,15 +527,15 @@
- (void) userNotificationCenter:(UNUserNotificationCenter *)center willPresentNotification:(UNNotification *)notification withCompletionHandler:(void (^)(UNNotificationPresentationOptions))completionHandler { - (void) userNotificationCenter:(UNUserNotificationCenter *)center willPresentNotification:(UNNotification *)notification withCompletionHandler:(void (^)(UNNotificationPresentationOptions))completionHandler {
// If an app extension launch a user notif while app is in fg, it is catch by the app // If an app extension launch a user notif while app is in fg, it is catch by the app
NSString *category = [[[notification request] content] categoryIdentifier]; NSString *category = [[[notification request] content] categoryIdentifier];
if (category && [category isEqualToString:@"app_active"]) { if (category && [category isEqualToString:@"app_active"]) {
return; return;
} }
if (category && [category isEqualToString:@"msg_cat"] && [UIApplication sharedApplication].applicationState == UIApplicationStateActive) { if (category && [category isEqualToString:@"msg_cat"] && [UIApplication sharedApplication].applicationState == UIApplicationStateActive) {
if ((PhoneMainView.instance.currentView == ChatsListView.compositeViewDescription)) if ((PhoneMainView.instance.currentView == ChatsListView.compositeViewDescription))
return; return;
if (PhoneMainView.instance.currentView == ChatConversationView.compositeViewDescription) { if (PhoneMainView.instance.currentView == ChatConversationView.compositeViewDescription) {
NSDictionary *userInfo = [[[notification request] content] userInfo]; NSDictionary *userInfo = [[[notification request] content] userInfo];
NSString *peerAddress = userInfo[@"peer_addr"]; NSString *peerAddress = userInfo[@"peer_addr"];
@ -519,12 +543,12 @@
if (peerAddress && localAddress) { if (peerAddress && localAddress) {
LinphoneAddress *peer = linphone_core_create_address([LinphoneManager getLc], peerAddress.UTF8String); LinphoneAddress *peer = linphone_core_create_address([LinphoneManager getLc], peerAddress.UTF8String);
LinphoneAddress *local = linphone_core_create_address([LinphoneManager getLc], localAddress.UTF8String); LinphoneAddress *local = linphone_core_create_address([LinphoneManager getLc], localAddress.UTF8String);
LinphoneChatRoom *room = linphone_core_find_chat_room([LinphoneManager getLc], peer, local); LinphoneChatRoom *room = linphone_core_search_chat_room([LinphoneManager getLc], NULL, local, peer, NULL);
if (room == PhoneMainView.instance.currentRoom) return; if (room == PhoneMainView.instance.currentRoom) return;
} }
} }
} }
completionHandler(UNNotificationPresentationOptionAlert); completionHandler(UNNotificationPresentationOptionAlert);
} }
@ -616,7 +640,6 @@
} }
} else if ([response.notification.request.content.categoryIdentifier isEqual:@"video_request"]) { } else if ([response.notification.request.content.categoryIdentifier isEqual:@"video_request"]) {
if (!call) return; if (!call) return;
[PhoneMainView.instance changeCurrentView:ActiveCallOrConferenceView.compositeViewDescription];
NSTimer *videoDismissTimer = nil; NSTimer *videoDismissTimer = nil;
UIConfirmationDialog *sheet = [UIConfirmationDialog ShowWithMessage:response.notification.request.content.body UIConfirmationDialog *sheet = [UIConfirmationDialog ShowWithMessage:response.notification.request.content.body
cancelMessage:nil cancelMessage:nil

View file

@ -27,6 +27,7 @@
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
@implementation LinphoneCoreSettingsStore @implementation LinphoneCoreSettingsStore
- (id)init { - (id)init {
@ -141,7 +142,9 @@
} }
- (void)transformAccountToKeys:(NSString *)username { - (void)transformAccountToKeys:(NSString *)username {
const MSList *accountList = linphone_core_get_account_list(LC); //const MSList *accountList = linphone_core_get_account_list(LC);
MSList *accountListToBeFreed = [LinphoneManager.instance createAccountsNotHiddenList];
MSList *accountList = accountListToBeFreed;
while (username && accountList && while (username && accountList &&
strcmp(username.UTF8String, strcmp(username.UTF8String,
linphone_address_get_username(linphone_account_params_get_identity_address(linphone_account_get_params(accountList->data)))) != 0) { linphone_address_get_username(linphone_account_params_get_identity_address(linphone_account_get_params(accountList->data)))) != 0) {
@ -168,6 +171,7 @@
[self setInteger:-1 forKey:@"account_expire_preference"]; [self setInteger:-1 forKey:@"account_expire_preference"];
[self setInteger:-1 forKey:@"current_proxy_config_preference"]; [self setInteger:-1 forKey:@"current_proxy_config_preference"];
[self setCString:"" forKey:@"account_prefix_preference"]; [self setCString:"" forKey:@"account_prefix_preference"];
[self setBool:YES forKey:@"apply_international_prefix_for_calls_and_chats"];
[self setBool:NO forKey:@"account_substitute_+_by_00_preference"]; [self setBool:NO forKey:@"account_substitute_+_by_00_preference"];
[self setBool:NO forKey:@"account_ice_preference"]; [self setBool:NO forKey:@"account_ice_preference"];
[self setCString:"" forKey:@"account_stun_preference"]; [self setCString:"" forKey:@"account_stun_preference"];
@ -183,7 +187,7 @@
const LinphoneAddress *identity_addr = linphone_account_params_get_identity_address(accountParams); const LinphoneAddress *identity_addr = linphone_account_params_get_identity_address(accountParams);
const char *server_addr = linphone_account_params_get_server_addr(accountParams); const char *server_addr = linphone_account_params_get_server_addr(accountParams);
LinphoneAddress *proxy_addr = linphone_core_interpret_url(LC, server_addr); LinphoneAddress *proxy_addr = linphone_core_interpret_url_2(LC, server_addr, false);
if (identity_addr && proxy_addr) { if (identity_addr && proxy_addr) {
int port = linphone_address_get_port(proxy_addr); int port = linphone_address_get_port(proxy_addr);
@ -234,9 +238,11 @@
[self setCString:linphone_auth_info_get_algorithm(ai) forKey:@"ha1_algo_preference"]; [self setCString:linphone_auth_info_get_algorithm(ai) forKey:@"ha1_algo_preference"];
} }
MSList *accountsList = [LinphoneManager.instance createAccountsNotHiddenList];
int idx = (int)bctbx_list_index(linphone_core_get_account_list(LC), account); int idx = (int)bctbx_list_index(linphone_core_get_account_list(LC), account);
[self setInteger:idx forKey:@"current_proxy_config_preference"]; [self setInteger:idx forKey:@"current_proxy_config_preference"];
bctbx_list_free(accountsList);
int expires = linphone_account_params_get_expires(accountParams); int expires = linphone_account_params_get_expires(accountParams);
[self setInteger:expires forKey:@"account_expire_preference"]; [self setInteger:expires forKey:@"account_expire_preference"];
@ -251,10 +257,13 @@
{ {
const char *dial_prefix = linphone_account_params_get_international_prefix(accountParams); const char *dial_prefix = linphone_account_params_get_international_prefix(accountParams);
[self setCString:dial_prefix forKey:@"account_prefix_preference"]; [self setCString:dial_prefix forKey:@"account_prefix_preference"];
BOOL apply_prefix = linphone_account_params_get_use_international_prefix_for_calls_and_chats(accountParams);
[self setBool:apply_prefix forKey:@"apply_international_prefix_for_calls_and_chats"];
BOOL dial_escape_plus = linphone_account_params_get_dial_escape_plus_enabled(accountParams); BOOL dial_escape_plus = linphone_account_params_get_dial_escape_plus_enabled(accountParams);
[self setBool:dial_escape_plus forKey:@"account_substitute_+_by_00_preference"]; [self setBool:dial_escape_plus forKey:@"account_substitute_+_by_00_preference"];
} }
} }
bctbx_list_free(accountListToBeFreed);
} }
@ -343,15 +352,17 @@
// root section // root section
{ {
const bctbx_list_t *accounts = linphone_core_get_account_list(LC); MSList *accountsListToBeFreed = [lm createAccountsNotHiddenList];
size_t count = bctbx_list_size(accounts); MSList *accountsList = accountsListToBeFreed;
for (size_t i = 1; i <= count; i++, accounts = accounts->next) { size_t count = bctbx_list_size(accountsList);
for (size_t i = 1; i <= count; i++, accountsList = accountsList->next) {
NSString *key = [NSString stringWithFormat:@"menu_account_%lu", i]; NSString *key = [NSString stringWithFormat:@"menu_account_%lu", i];
LinphoneAccount *account = (LinphoneAccount *)accounts->data; LinphoneAccount *account = (LinphoneAccount *)accountsList->data;
[self setCString:linphone_address_get_username(linphone_account_params_get_identity_address(linphone_account_get_params(account))) [self setCString:linphone_address_get_username(linphone_account_params_get_identity_address(linphone_account_get_params(account)))
forKey:key]; forKey:key];
} }
bctbx_free(accountsListToBeFreed);
[self setBool:linphone_core_video_display_enabled(LC) forKey:@"enable_video_preference"]; [self setBool:linphone_core_video_display_enabled(LC) forKey:@"enable_video_preference"];
[self setBool:[LinphoneManager.instance lpConfigBoolForKey:@"auto_answer"] [self setBool:[LinphoneManager.instance lpConfigBoolForKey:@"auto_answer"]
forKey:@"enable_auto_answer_preference"]; forKey:@"enable_auto_answer_preference"];
@ -431,10 +442,10 @@
{ {
[self setCString:linphone_core_get_file_transfer_server(LC) forKey:@"file_transfer_server_url_preference"]; [self setCString:linphone_core_get_file_transfer_server(LC) forKey:@"file_transfer_server_url_preference"];
int maxSize = linphone_core_get_max_size_for_auto_download_incoming_files(LC); int maxSize = linphone_core_get_max_size_for_auto_download_incoming_files(LC);
[self setObject:maxSize==0 ? @"Always" : (maxSize==-1 ? @"Nerver" : @"Customize") forKey:@"auto_download_mode"]; [self setObject:maxSize==0 ? @"Always" : (maxSize==-1 ? @"Never" : @"Customize") forKey:@"auto_download_mode"];
[self setInteger:maxSize forKey:@"auto_download_incoming_files_max_size"]; [self setInteger:maxSize forKey:@"auto_download_incoming_files_max_size"];
[self setBool:[VFSUtil vfsEnabledWithGroupName:kLinphoneMsgNotificationAppGroupId] forKey:@"vfs_enabled_mode"]; [self setBool:[VFSUtil vfsEnabledWithGroupName:kLinphoneMsgNotificationAppGroupId] forKey:@"vfs_enabled_mode"];
[self setBool:[lm lpConfigBoolForKey:@"auto_write_to_gallery_preference" withDefault:YES] forKey:@"auto_write_to_gallery_mode"]; [self setBool:[lm lpConfigBoolForKey:@"auto_write_to_gallery_preference" withDefault:NO] forKey:@"auto_write_to_gallery_mode"];
} }
// network section // network section
@ -486,7 +497,7 @@
val = "None"; val = "None";
break; break;
} }
[self setCString:val forKey:@"media_encryption_preference"]; [self setCString:val forKey:linphone_core_get_post_quantum_available() ? @"media_encryption_preference_pq_enabled" : @"media_encryption_preference"];
[self setInteger:linphone_core_get_upload_bandwidth(LC) forKey:@"upload_bandwidth_preference"]; [self setInteger:linphone_core_get_upload_bandwidth(LC) forKey:@"upload_bandwidth_preference"];
[self setInteger:linphone_core_get_download_bandwidth(LC) forKey:@"download_bandwidth_preference"]; [self setInteger:linphone_core_get_download_bandwidth(LC) forKey:@"download_bandwidth_preference"];
[self setBool:linphone_core_adaptive_rate_control_enabled(LC) forKey:@"adaptive_rate_control_preference"]; [self setBool:linphone_core_adaptive_rate_control_enabled(LC) forKey:@"adaptive_rate_control_preference"];
@ -604,6 +615,7 @@
int expire = [self integerForKey:@"account_expire_preference"]; int expire = [self integerForKey:@"account_expire_preference"];
BOOL pushnotification = [self boolForKey:@"account_pushnotification_preference"]; BOOL pushnotification = [self boolForKey:@"account_pushnotification_preference"];
NSString *prefix = [self stringForKey:@"account_prefix_preference"]; NSString *prefix = [self stringForKey:@"account_prefix_preference"];
BOOL use_prefix = [self boolForKey:@"apply_international_prefix_for_calls_and_chats"];
NSString *proxyAddress = [self stringForKey:@"account_proxy_preference"]; NSString *proxyAddress = [self stringForKey:@"account_proxy_preference"];
if ((!proxyAddress || [proxyAddress length] < 1) && domain) { if ((!proxyAddress || [proxyAddress length] < 1) && domain) {
@ -614,7 +626,7 @@
proxyAddress = [NSString stringWithFormat:@"sip:%@", proxyAddress]; proxyAddress = [NSString stringWithFormat:@"sip:%@", proxyAddress];
} }
LinphoneAddress *proxy_addr = linphone_core_interpret_url(LC, proxyAddress.UTF8String); LinphoneAddress *proxy_addr = linphone_core_interpret_url_2(LC, proxyAddress.UTF8String, false);
if (proxy_addr) { if (proxy_addr) {
LinphoneTransportType type = LinphoneTransportUdp; LinphoneTransportType type = LinphoneTransportUdp;
@ -625,9 +637,11 @@
linphone_address_set_transport(proxy_addr, type); linphone_address_set_transport(proxy_addr, type);
} }
account = bctbx_list_nth_data(linphone_core_get_account_list(LC), MSList *accountList= [LinphoneManager.instance createAccountsNotHiddenList];
account = bctbx_list_nth_data(accountList,
[self integerForKey:@"current_proxy_config_preference"]); [self integerForKey:@"current_proxy_config_preference"]);
bctbx_free(accountList);
// if account was deleted, it is not present anymore // if account was deleted, it is not present anymore
if (account == NULL) if (account == NULL)
@ -670,10 +684,8 @@
linphone_nat_policy_set_stun_server(policy, stun_preference.UTF8String); linphone_nat_policy_set_stun_server(policy, stun_preference.UTF8String);
linphone_account_params_set_nat_policy(newAccountParams, policy); linphone_account_params_set_nat_policy(newAccountParams, policy);
if ([prefix length] > 0) { linphone_account_params_set_international_prefix(newAccountParams, [prefix UTF8String]);
linphone_account_params_set_international_prefix(newAccountParams, [prefix UTF8String]); linphone_account_params_set_use_international_prefix_for_calls_and_chats(newAccountParams, use_prefix);
}
if ([self objectForKey:@"account_substitute_+_by_00_preference"]) { if ([self objectForKey:@"account_substitute_+_by_00_preference"]) {
bool substitute_plus_by_00 = [self boolForKey:@"account_substitute_+_by_00_preference"]; bool substitute_plus_by_00 = [self boolForKey:@"account_substitute_+_by_00_preference"];
linphone_account_params_set_dial_escape_plus_enabled(newAccountParams, substitute_plus_by_00); linphone_account_params_set_dial_escape_plus_enabled(newAccountParams, substitute_plus_by_00);
@ -713,7 +725,7 @@
} }
char *identity = linphone_address_as_string(linphoneAddress); char *identity = linphone_address_as_string(linphoneAddress);
LinphoneAddress *from = linphone_core_interpret_url(LC, identity); LinphoneAddress *from = linphone_core_interpret_url_2(LC, identity, false);
ms_free(identity); ms_free(identity);
if (from) { if (from) {
const char *userid_str = (userID != nil) ? [userID UTF8String] : NULL; const char *userid_str = (userID != nil) ? [userID UTF8String] : NULL;
@ -805,7 +817,7 @@
linphone_ldap_params_set_bind_dn(newLdapParams, [self stringForKey:@"ldap_bind_dn"].UTF8String); linphone_ldap_params_set_bind_dn(newLdapParams, [self stringForKey:@"ldap_bind_dn"].UTF8String);
linphone_ldap_params_set_password(newLdapParams, [self stringForKey:@"ldap_password"].UTF8String); linphone_ldap_params_set_password(newLdapParams, [self stringForKey:@"ldap_password"].UTF8String);
LinphoneLdapAuthMethod authMethod = [[self stringForKey:@"ldap_verification_method"] isEqualToString:@"simple"] ? LinphoneLdapAuthMethodSimple : LinphoneLdapAuthMethodAnonymous; LinphoneLdapAuthMethod authMethod = [[self stringForKey:@"ldap_auth_method"] isEqualToString:@"simple"] ? LinphoneLdapAuthMethodSimple : LinphoneLdapAuthMethodAnonymous;
linphone_ldap_params_set_auth_method(newLdapParams, authMethod); linphone_ldap_params_set_auth_method(newLdapParams, authMethod);
linphone_ldap_params_enable_tls(newLdapParams, [self boolForKey:@"ldap_tls_enabled"]); linphone_ldap_params_enable_tls(newLdapParams, [self boolForKey:@"ldap_tls_enabled"]);
@ -829,8 +841,8 @@
// Analysis parameters // Analysis parameters
linphone_ldap_params_set_name_attribute(newLdapParams, [self stringForKey:@"ldap_name_attributes"].UTF8String); linphone_ldap_params_set_name_attribute(newLdapParams, [self stringForKey:@"ldap_name_attribute"].UTF8String);
linphone_ldap_params_set_sip_attribute(newLdapParams, [self stringForKey:@"ldap_sip_attributes"].UTF8String); linphone_ldap_params_set_sip_attribute(newLdapParams, [self stringForKey:@"ldap_sip_attribute"].UTF8String);
linphone_ldap_params_set_sip_domain(newLdapParams, [self stringForKey:@"ldap_sip_domain"].UTF8String); linphone_ldap_params_set_sip_domain(newLdapParams, [self stringForKey:@"ldap_sip_domain"].UTF8String);
// Miscellaneous parameters // Miscellaneous parameters
@ -1020,7 +1032,7 @@
[LinphoneCoreSettingsStore parsePortRange:video_port_preference minPort:&videoMinPort maxPort:&videoMaxPort]; [LinphoneCoreSettingsStore parsePortRange:video_port_preference minPort:&videoMinPort maxPort:&videoMaxPort];
linphone_core_set_video_port_range(LC, videoMinPort, videoMaxPort); linphone_core_set_video_port_range(LC, videoMinPort, videoMaxPort);
NSString *menc = [self stringForKey:@"media_encryption_preference"]; NSString *menc = [self stringForKey:linphone_core_get_post_quantum_available() ? @"media_encryption_preference_pq_enabled" : @"media_encryption_preference"];
if (menc && [menc compare:@"SRTP"] == NSOrderedSame) if (menc && [menc compare:@"SRTP"] == NSOrderedSame)
linphone_core_set_media_encryption(LC, LinphoneMediaEncryptionSRTP); linphone_core_set_media_encryption(LC, LinphoneMediaEncryptionSRTP);
else if (menc && [menc compare:@"ZRTP"] == NSOrderedSame) else if (menc && [menc compare:@"ZRTP"] == NSOrderedSame)
@ -1096,7 +1108,9 @@
NSString *rls_uri = [lm lpConfigStringForKey:@"rls_uri" inSection:@"sip" withDefault:@"sips:rls@sip.linphone.org"]; NSString *rls_uri = [lm lpConfigStringForKey:@"rls_uri" inSection:@"sip" withDefault:@"sips:rls@sip.linphone.org"];
LinphoneAddress *rls_addr = linphone_address_new(rls_uri.UTF8String); LinphoneAddress *rls_addr = linphone_address_new(rls_uri.UTF8String);
const char *rls_domain = linphone_address_get_domain(rls_addr); const char *rls_domain = linphone_address_get_domain(rls_addr);
const MSList *accounts = linphone_core_get_account_list(LC);
MSList *accountListToBeFreed = [LinphoneManager.instance createAccountsNotHiddenList];
const MSList *accounts = accountListToBeFreed;
if (!accounts) // Enable it if no proxy config for first launch of app if (!accounts) // Enable it if no proxy config for first launch of app
[self setInteger:1 forKey:@"use_rls_presence"]; [self setInteger:1 forKey:@"use_rls_presence"];
else { else {
@ -1110,6 +1124,7 @@
} }
} }
linphone_address_unref(rls_addr); linphone_address_unref(rls_addr);
bctbx_free(accountListToBeFreed);
} }
[lm lpConfigSetInt:[self integerForKey:@"use_rls_presence"] forKey:@"use_rls_presence"]; [lm lpConfigSetInt:[self integerForKey:@"use_rls_presence"] forKey:@"use_rls_presence"];
@ -1151,9 +1166,12 @@
} }
- (void)removeAccount { - (void)removeAccount {
LinphoneAccount *account = bctbx_list_nth_data(linphone_core_get_account_list(LC),
MSList *accountList = [LinphoneManager.instance createAccountsNotHiddenList];
LinphoneAccount *account = bctbx_list_nth_data(accountList,
[self integerForKey:@"current_proxy_config_preference"]); [self integerForKey:@"current_proxy_config_preference"]);
const MSList *lists = linphone_core_get_friends_lists(LC); const MSList *lists = linphone_core_get_friends_lists(LC);
while (lists) { while (lists) {
linphone_friend_list_enable_subscriptions(lists->data, FALSE); linphone_friend_list_enable_subscriptions(lists->data, FALSE);
@ -1174,11 +1192,12 @@
if (isDefault) { if (isDefault) {
// if we removed the default proxy config, set another one instead // if we removed the default proxy config, set another one instead
if (linphone_core_get_account_list(LC) != NULL) { if (accountList != NULL) {
linphone_core_set_default_account(LC, (LinphoneAccount *)(linphone_core_get_account_list(LC)->data)); linphone_core_set_default_account(LC, (LinphoneAccount *)(accountList->data));
} }
} }
[self transformLinphoneCoreToKeys]; [self transformLinphoneCoreToKeys];
bctbx_free(accountList);
} }
- (void)removeLdap { - (void)removeLdap {

View file

@ -65,6 +65,8 @@ extern NSString *const kLinphoneConfStateParticipantListChanged;
extern NSString *const kLinphoneConfStateChanged; extern NSString *const kLinphoneConfStateChanged;
extern NSString *const kLinphoneMagicSearchStarted; extern NSString *const kLinphoneMagicSearchStarted;
extern NSString *const kLinphoneMagicSearchFinished; extern NSString *const kLinphoneMagicSearchFinished;
extern NSString *const kLinphoneMagicSearchMoreAvailable;
extern NSString *const kDisplayModeChanged;
extern NSString *const kLinphoneMsgNotificationAppGroupId; extern NSString *const kLinphoneMsgNotificationAppGroupId;
@ -174,6 +176,7 @@ typedef struct _LinphoneManagerSounds {
- (void)silentPushFailed:(NSTimer*)timer; - (void)silentPushFailed:(NSTimer*)timer;
- (MSList *) createAccountsNotHiddenList; // needs to be unref
- (void)removeAllAccounts; - (void)removeAllAccounts;
+ (BOOL)isMyself:(const LinphoneAddress *)addr; + (BOOL)isMyself:(const LinphoneAddress *)addr;
@ -192,6 +195,8 @@ typedef struct _LinphoneManagerSounds {
- (BOOL)isCTCallCenterExist; - (BOOL)isCTCallCenterExist;
- (void) checkLocalNetworkPermission; - (void) checkLocalNetworkPermission;
+ (BOOL) getChatroomPushEnabled:(LinphoneChatRoom *)chatroom;
+ (void) setChatroomPushEnabled:(LinphoneChatRoom *)chatroom withPushEnabled:(BOOL)enabled;
@property (readonly) BOOL isTesting; @property (readonly) BOOL isTesting;
@property(readonly, strong) FastAddressBook *fastAddressBook; @property(readonly, strong) FastAddressBook *fastAddressBook;

View file

@ -81,6 +81,8 @@ NSString *const kLinphoneConfStateChanged = @"kLinphoneConfStateChanged";
NSString *const kLinphoneConfStateParticipantListChanged = @"kLinphoneConfStateParticipantListChanged"; NSString *const kLinphoneConfStateParticipantListChanged = @"kLinphoneConfStateParticipantListChanged";
NSString *const kLinphoneMagicSearchStarted = @"LinphoneMagicSearchStarted"; NSString *const kLinphoneMagicSearchStarted = @"LinphoneMagicSearchStarted";
NSString *const kLinphoneMagicSearchFinished = @"LinphoneMagicSearchFinished"; NSString *const kLinphoneMagicSearchFinished = @"LinphoneMagicSearchFinished";
NSString *const kLinphoneMagicSearchMoreAvailable = @"LinphoneMagicSearchMoreAvailable";
NSString *const kDisplayModeChanged = @"DisplayModeChanged";
NSString *const kLinphoneMsgNotificationAppGroupId = @"group.org.linphone.phone.msgNotification"; NSString *const kLinphoneMsgNotificationAppGroupId = @"group.org.linphone.phone.msgNotification";
@ -276,6 +278,14 @@ struct codec_name_pref_table codec_pref_table[] = {{"speex", 8000, "speex_8k_pre
[self lpConfigSetString:@"conflate" forKey:@"handle_content_encoding" inSection:@"misc"]; [self lpConfigSetString:@"conflate" forKey:@"handle_content_encoding" inSection:@"misc"];
#endif #endif
} }
if ([self lpConfigStringForKey:@"display_link_account_popup"] == nil) {
[self lpConfigSetBool:true forKey:@"display_link_account_popup"];
}
if ([self lpConfigStringForKey:@"hide_link_phone_number"] == nil) {
[self lpConfigSetInt:1 forKey:@"hide_link_phone_number"];
}
[self migrateFromUserPrefs]; [self migrateFromUserPrefs];
[self loadAvatar]; [self loadAvatar];
@ -637,6 +647,11 @@ static void linphone_iphone_global_state_changed(LinphoneCore *lc, LinphoneGloba
if (theLinphoneCore && linphone_core_get_global_state(theLinphoneCore) != LinphoneGlobalOff) if (theLinphoneCore && linphone_core_get_global_state(theLinphoneCore) != LinphoneGlobalOff)
[NSNotificationCenter.defaultCenter postNotificationName:kLinphoneGlobalStateUpdate object:self userInfo:dict]; [NSNotificationCenter.defaultCenter postNotificationName:kLinphoneGlobalStateUpdate object:self userInfo:dict];
}); });
if (state == LinphoneGlobalOn) {
// reload friends
[self.fastAddressBook fetchContactsInBackGroundThread];
}
} }
- (void)globalStateChangedNotificationHandler:(NSNotification *)notif { - (void)globalStateChangedNotificationHandler:(NSNotification *)notif {
@ -1254,7 +1269,7 @@ void popup_link_account_cb(LinphoneAccountCreator *creator, LinphoneAccountCreat
[LinphoneManager.instance lpConfigSetInt:0 forKey:@"must_link_account_time"]; [LinphoneManager.instance lpConfigSetInt:0 forKey:@"must_link_account_time"];
} else { } else {
LinphoneAccount *account = linphone_core_get_default_account(LC); LinphoneAccount *account = linphone_core_get_default_account(LC);
LinphoneAccountParams const *accountParams = linphone_account_get_params(account); LinphoneAccountParams const *accountParams = account ? linphone_account_get_params(account) : NULL;
if (account && if (account &&
strcmp(linphone_account_params_get_domain(accountParams), strcmp(linphone_account_params_get_domain(accountParams),
[LinphoneManager.instance lpConfigStringForKey:@"domain_name" [LinphoneManager.instance lpConfigStringForKey:@"domain_name"
@ -1275,7 +1290,14 @@ void popup_link_account_cb(LinphoneAccountCreator *creator, LinphoneAccountCreat
handler:^(UIAlertAction * action) { handler:^(UIAlertAction * action) {
[PhoneMainView.instance changeCurrentView:AssistantLinkView.compositeViewDescription]; [PhoneMainView.instance changeCurrentView:AssistantLinkView.compositeViewDescription];
}]; }];
UIAlertAction* otherAction = [UIAlertAction actionWithTitle:NSLocalizedString(@"Never ask again", nil)
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action) {
[LinphoneManager.instance lpConfigSetBool:false forKey:@"display_link_account_popup"];
}];
defaultAction.accessibilityLabel = @"Later"; defaultAction.accessibilityLabel = @"Later";
[errView addAction:otherAction];
[errView addAction:defaultAction]; [errView addAction:defaultAction];
[errView addAction:continueAction]; [errView addAction:continueAction];
[PhoneMainView.instance presentViewController:errView animated:YES completion:nil]; [PhoneMainView.instance presentViewController:errView animated:YES completion:nil];
@ -1294,7 +1316,7 @@ void popup_link_account_cb(LinphoneAccountCreator *creator, LinphoneAccountCreat
NSDate *nextTime = NSDate *nextTime =
[NSDate dateWithTimeIntervalSince1970:[self lpConfigIntForKey:@"must_link_account_time" withDefault:1]]; [NSDate dateWithTimeIntervalSince1970:[self lpConfigIntForKey:@"must_link_account_time" withDefault:1]];
NSDate *now = [NSDate date]; NSDate *now = [NSDate date];
if (nextTime.timeIntervalSince1970 > 0 && [now earlierDate:nextTime] == nextTime) { if (nextTime.timeIntervalSince1970 > 0 && [now earlierDate:nextTime] == nextTime && [LinphoneManager.instance lpConfigBoolForKey:@"display_link_account_popup"] && ![LinphoneManager.instance lpConfigIntForKey:@"hide_link_phone_number"]) {
LinphoneAccount *account = linphone_core_get_default_account(LC); LinphoneAccount *account = linphone_core_get_default_account(LC);
if (account) { if (account) {
const char *username = linphone_address_get_username(linphone_account_params_get_identity_address(linphone_account_get_params(account))); const char *username = linphone_address_get_username(linphone_account_params_get_identity_address(linphone_account_get_params(account)));
@ -1459,6 +1481,7 @@ void popup_link_account_cb(LinphoneAccountCreator *creator, LinphoneAccountCreat
- (void)destroyLinphoneCore { - (void)destroyLinphoneCore {
// just in case // just in case
[self removeCTCallCenterCb]; [self removeCTCallCenterCb];
[MagicSearchSingleton destroyInstance];
if (theLinphoneCore != nil) { // just in case application terminate before linphone core initialization if (theLinphoneCore != nil) { // just in case application terminate before linphone core initialization
@ -1488,8 +1511,6 @@ void popup_link_account_cb(LinphoneAccountCreator *creator, LinphoneAccountCreat
- (void)resetLinphoneCore { - (void)resetLinphoneCore {
[self destroyLinphoneCore]; [self destroyLinphoneCore];
[self createLinphoneCore]; [self createLinphoneCore];
// reload friends
[self.fastAddressBook fetchContactsInBackGroundThread];
} }
static int comp_call_id(const LinphoneCall *call, const char *callid) { static int comp_call_id(const LinphoneCall *call, const char *callid) {
@ -1884,7 +1905,8 @@ static int comp_call_state_paused(const LinphoneCall *call, const void *param) {
} }
[self checkLocalNetworkPermission]; [self checkLocalNetworkPermission];
// For OutgoingCall, show CallOutgoingView // For OutgoingCall, show CallOutgoingView
[CallManager.instance startCallWithAddr:iaddr isSas:FALSE isVideo:false isConference:false]; BOOL initiateVideoCall = linphone_core_get_video_activation_policy(LC) && linphone_video_activation_policy_get_automatically_initiate(linphone_core_get_video_activation_policy(LC));
[CallManager.instance startCallWithAddr:iaddr isSas:FALSE isVideo:initiateVideoCall isConference:false];
} }
#pragma mark - Misc Functions #pragma mark - Misc Functions
@ -2210,6 +2232,23 @@ static int comp_call_state_paused(const LinphoneCall *call, const void *param) {
#pragma mark - #pragma mark -
- (MSList *) createAccountsNotHiddenList {
MSList *list = NULL;
const MSList *accounts = linphone_core_get_account_list(LC);
while (accounts) {
const char *isHidden = linphone_account_get_custom_param(accounts->data, "hidden");
if (isHidden == NULL || strcmp(linphone_account_get_custom_param(accounts->data, "hidden"), "1") != 0) {
if (!list) {
list = bctbx_list_new(accounts->data);
} else {
bctbx_list_append(list, accounts->data);
}
}
accounts = accounts->next;
}
return list;
}
- (void)removeAllAccounts { - (void)removeAllAccounts {
linphone_core_clear_accounts(LC); linphone_core_clear_accounts(LC);
linphone_core_clear_all_auth_info(LC); linphone_core_clear_all_auth_info(LC);
@ -2303,6 +2342,31 @@ void linphone_iphone_conference_state_changed(LinphoneCore *lc, LinphoneConferen
[NSNotificationCenter.defaultCenter postNotificationName:kLinphoneConfStateChanged object:nil userInfo:dict]; [NSNotificationCenter.defaultCenter postNotificationName:kLinphoneConfStateChanged object:nil userInfo:dict];
} }
+ (BOOL) getChatroomPushEnabled:(LinphoneChatRoom *)chatroom {
bool currently_enabled = true;
NSUserDefaults *defaults = [[NSUserDefaults alloc] initWithSuiteName:kLinphoneMsgNotificationAppGroupId];
NSDictionary *chatroomsPushStatus = [defaults dictionaryForKey:@"chatroomsPushStatus"];
if (chatroomsPushStatus != nil && chatroom) {
char *uri = linphone_address_as_string_uri_only(linphone_chat_room_get_peer_address(chatroom));
NSString* pushStatus = [chatroomsPushStatus objectForKey:[NSString stringWithUTF8String:uri]];
currently_enabled = (pushStatus == nil) || [pushStatus isEqualToString:@"enabled"];
ms_free(uri);
}
return currently_enabled;
}
+ (void) setChatroomPushEnabled:(LinphoneChatRoom *)chatroom withPushEnabled:(BOOL)enabled {
if (!chatroom) return;
NSUserDefaults *defaults = [[NSUserDefaults alloc] initWithSuiteName:kLinphoneMsgNotificationAppGroupId];
NSMutableDictionary *chatroomsPushStatus = [[NSMutableDictionary alloc] initWithDictionary:[defaults dictionaryForKey:@"chatroomsPushStatus"]];
if (chatroomsPushStatus == nil) chatroomsPushStatus = [[NSMutableDictionary dictionary] init];
char *uri = linphone_address_as_string_uri_only(linphone_chat_room_get_peer_address(chatroom));
[chatroomsPushStatus setValue:(enabled ? @"enabled" : @"disabled") forKey:[NSString stringWithUTF8String:uri]];
ms_free(uri);
[defaults setObject:chatroomsPushStatus forKey:@"chatroomsPushStatus"];
}
@end @end

View file

@ -1,9 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="19529" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" colorMatched="YES"> <document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="20037" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" colorMatched="YES">
<device id="retina6_1" orientation="portrait" appearance="light"/> <device id="retina6_1" orientation="portrait" appearance="light"/>
<dependencies> <dependencies>
<deployment identifier="iOS"/> <deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="19519"/> <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="20020"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/> <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies> </dependencies>
<objects> <objects>
@ -57,7 +57,7 @@
<color key="titleShadowColor" red="0.5" green="0.5" blue="0.5" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> <color key="titleShadowColor" red="0.5" green="0.5" blue="0.5" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</state> </state>
<connections> <connections>
<action selector="onSideMenuClick:" destination="-1" eventType="touchUpInside" id="iOC-wy-MPP"/> <action selector="onQualityClick:" destination="-1" eventType="touchUpInside" id="Pir-Ib-ywJ"/>
</connections> </connections>
</button> </button>
</subviews> </subviews>

View file

@ -1,9 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="19529" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" colorMatched="YES"> <document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="21507" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" colorMatched="YES">
<device id="retina4_7" orientation="portrait" appearance="light"/> <device id="retina4_7" orientation="portrait" appearance="light"/>
<dependencies> <dependencies>
<deployment identifier="iOS"/> <deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="19519"/> <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="21505"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/> <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies> </dependencies>
<objects> <objects>
@ -81,6 +81,22 @@
<rect key="frame" x="0.0" y="0.0" width="365" height="357"/> <rect key="frame" x="0.0" y="0.0" width="365" height="357"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES" flexibleMaxY="YES"/> <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES" flexibleMaxY="YES"/>
</imageView> </imageView>
<imageView hidden="YES" userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" image="chat_read.png" translatesAutoresizingMaskIntoConstraints="NO" id="LPj-VT-0fH" userLabel="imdmIcon">
<rect key="frame" x="372" y="342" width="10" height="10"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMinY="YES"/>
<accessibility key="accessibilityConfiguration" label="Delivery failed"/>
</imageView>
<imageView hidden="YES" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="ephemeral_messages_color_A.png" id="7JB-ZL-0lZ" userLabel="ephemeralIcon">
<rect key="frame" x="352" y="346" width="10" height="10"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMinY="YES"/>
</imageView>
<label hidden="YES" opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="00:00:00" textAlignment="right" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="IRV-qN-sRj" userLabel="ephemeralTime">
<rect key="frame" x="285" y="346" width="65" height="10"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMinY="YES"/>
<fontDescription key="fontDescription" type="system" pointSize="9"/>
<color key="textColor" red="1" green="0.36862745099999999" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/>
</label>
<view contentMode="scaleToFill" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="VAJ-tE-fsa"> <view contentMode="scaleToFill" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="VAJ-tE-fsa">
<rect key="frame" x="0.0" y="10" width="382" height="347"/> <rect key="frame" x="0.0" y="10" width="382" height="347"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
@ -185,22 +201,6 @@
<textInputTraits key="textInputTraits" autocapitalizationType="sentences"/> <textInputTraits key="textInputTraits" autocapitalizationType="sentences"/>
<dataDetectorType key="dataDetectorTypes" link="YES"/> <dataDetectorType key="dataDetectorTypes" link="YES"/>
</textView> </textView>
<imageView hidden="YES" userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" image="chat_read.png" translatesAutoresizingMaskIntoConstraints="NO" id="LPj-VT-0fH" userLabel="imdmIcon">
<rect key="frame" x="372" y="337" width="10" height="10"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMinY="YES"/>
<accessibility key="accessibilityConfiguration" label="Delivery failed"/>
</imageView>
<imageView hidden="YES" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="ephemeral_messages_color_A.png" id="7JB-ZL-0lZ" userLabel="ephemeralIcon">
<rect key="frame" x="351" y="336" width="10" height="10"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMinY="YES"/>
</imageView>
<label hidden="YES" opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="00:00:00" textAlignment="right" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="IRV-qN-sRj" userLabel="ephemeralTime">
<rect key="frame" x="282" y="336" width="65" height="10"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMinY="YES"/>
<fontDescription key="fontDescription" type="system" pointSize="9"/>
<color key="textColor" red="1" green="0.36862745099999999" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/>
</label>
<view tag="28021" contentMode="scaleToFill" id="bhq-9n-zYF" userLabel="voiceRecording"> <view tag="28021" contentMode="scaleToFill" id="bhq-9n-zYF" userLabel="voiceRecording">
<rect key="frame" x="7" y="252" width="351" height="60"/> <rect key="frame" x="7" y="252" width="351" height="60"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES"/> <autoresizingMask key="autoresizingMask" widthSizable="YES"/>
@ -261,7 +261,7 @@
<image name="color_M.png" width="2" height="2"/> <image name="color_M.png" width="2" height="2"/>
<image name="ephemeral_messages_color_A.png" width="136" height="158.39999389648438"/> <image name="ephemeral_messages_color_A.png" width="136" height="158.39999389648438"/>
<image name="linphone_logo.png" width="41.599998474121094" height="42.400001525878906"/> <image name="linphone_logo.png" width="41.599998474121094" height="42.400001525878906"/>
<image name="menu_reply_default.png" width="25" height="25"/> <image name="menu_reply_default.png" width="60" height="60"/>
<image name="vr_play.png" width="200" height="200"/> <image name="vr_play.png" width="200" height="200"/>
<image name="vr_wave.png" width="1078" height="90"/> <image name="vr_wave.png" width="1078" height="90"/>
</resources> </resources>

View file

@ -1,9 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="18122" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" colorMatched="YES"> <document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="21507" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" colorMatched="YES">
<device id="retina4_7" orientation="portrait" appearance="light"/> <device id="retina4_7" orientation="portrait" appearance="light"/>
<dependencies> <dependencies>
<deployment identifier="iOS"/> <deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="18093"/> <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="21505"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/> <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies> </dependencies>
<objects> <objects>
@ -66,7 +66,7 @@
<dataDetectorType key="dataDetectorTypes" link="YES"/> <dataDetectorType key="dataDetectorTypes" link="YES"/>
</textView> </textView>
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" image="chat_read.png" translatesAutoresizingMaskIntoConstraints="NO" id="Nod-GX-0kg" userLabel="imdmIcon"> <imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" image="chat_read.png" translatesAutoresizingMaskIntoConstraints="NO" id="Nod-GX-0kg" userLabel="imdmIcon">
<rect key="frame" x="133" y="30" width="10" height="10"/> <rect key="frame" x="133" y="28" width="10" height="10"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMinY="YES"/> <autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMinY="YES"/>
</imageView> </imageView>
<label hidden="YES" opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" text="00:00:00" textAlignment="right" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="GDJ-O8-m6J" userLabel="ephemeralTime"> <label hidden="YES" opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" text="00:00:00" textAlignment="right" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="GDJ-O8-m6J" userLabel="ephemeralTime">
@ -93,6 +93,6 @@
<image name="chat_read.png" width="20" height="20"/> <image name="chat_read.png" width="20" height="20"/>
<image name="color_A.png" width="2" height="2"/> <image name="color_A.png" width="2" height="2"/>
<image name="ephemeral_messages_color_A.png" width="136" height="158.39999389648438"/> <image name="ephemeral_messages_color_A.png" width="136" height="158.39999389648438"/>
<image name="menu_reply_default.png" width="25" height="25"/> <image name="menu_reply_default.png" width="60" height="60"/>
</resources> </resources>
</document> </document>

View file

@ -1,9 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="18122" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" colorMatched="YES"> <document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="21225" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" colorMatched="YES">
<device id="retina4_7" orientation="portrait" appearance="light"/> <device id="retina4_7" orientation="portrait" appearance="light"/>
<dependencies> <dependencies>
<deployment identifier="iOS"/> <deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="18093"/> <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="21207"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/> <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies> </dependencies>
<objects> <objects>
@ -66,15 +66,11 @@
<nil key="highlightedColor"/> <nil key="highlightedColor"/>
</label> </label>
<view hidden="YES" autoresizesSubviews="NO" userInteractionEnabled="NO" tag="7" contentMode="scaleToFill" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="7DE-KJ-9Q3" userLabel="unreadCountView" customClass="UIBouncingView"> <view hidden="YES" autoresizesSubviews="NO" userInteractionEnabled="NO" tag="7" contentMode="scaleToFill" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="7DE-KJ-9Q3" userLabel="unreadCountView" customClass="UIBouncingView">
<rect key="frame" x="338" y="12" width="21" height="21"/> <rect key="frame" x="338" y="12" width="20" height="20"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxY="YES"/> <autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxY="YES"/>
<subviews> <subviews>
<imageView userInteractionEnabled="NO" tag="8" contentMode="scaleAspectFit" fixedFrame="YES" image="chat_list_indicator.png" translatesAutoresizingMaskIntoConstraints="NO" id="NXj-A8-YLh" userLabel="unreadCountImage">
<rect key="frame" x="0.0" y="0.0" width="21" height="21"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMinY="YES" flexibleMaxY="YES"/>
</imageView>
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" tag="9" contentMode="left" fixedFrame="YES" text="99" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumFontSize="7" translatesAutoresizingMaskIntoConstraints="NO" id="ZXq-Do-7Ua" userLabel="unreadCountLabel"> <label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" tag="9" contentMode="left" fixedFrame="YES" text="99" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumFontSize="7" translatesAutoresizingMaskIntoConstraints="NO" id="ZXq-Do-7Ua" userLabel="unreadCountLabel">
<rect key="frame" x="0.0" y="0.0" width="21" height="21"/> <rect key="frame" x="0.0" y="0.0" width="20" height="20"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMinY="YES" flexibleMaxY="YES"/> <autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMinY="YES" flexibleMaxY="YES"/>
<accessibility key="accessibilityConfiguration"> <accessibility key="accessibilityConfiguration">
<accessibilityTraits key="traits" none="YES"/> <accessibilityTraits key="traits" none="YES"/>
@ -88,11 +84,11 @@
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/> <color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
</view> </view>
<imageView hidden="YES" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="forward_message_default.png" id="rbY-QS-6QH" userLabel="transferIcon"> <imageView hidden="YES" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="forward_message_default.png" id="rbY-QS-6QH" userLabel="transferIcon">
<rect key="frame" x="338" y="33" width="21" height="21"/> <rect key="frame" x="338" y="33" width="20" height="20"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/> <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
</imageView> </imageView>
<imageView hidden="YES" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" image="ephemeral_messages_color_A.png" translatesAutoresizingMaskIntoConstraints="NO" id="q18-yi-ol3"> <imageView hidden="YES" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" image="ephemeral_messages_color_A.png" translatesAutoresizingMaskIntoConstraints="NO" id="q18-yi-ol3">
<rect key="frame" x="338" y="54" width="21" height="21"/> <rect key="frame" x="338" y="54" width="20" height="20"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMinY="YES" flexibleMaxY="YES"/> <autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMinY="YES" flexibleMaxY="YES"/>
</imageView> </imageView>
</subviews> </subviews>
@ -103,7 +99,6 @@
</objects> </objects>
<resources> <resources>
<image name="avatar.png" width="414.39999389648438" height="414.39999389648438"/> <image name="avatar.png" width="414.39999389648438" height="414.39999389648438"/>
<image name="chat_list_indicator.png" width="28" height="28"/>
<image name="chat_read.png" width="20" height="20"/> <image name="chat_read.png" width="20" height="20"/>
<image name="ephemeral_messages_color_A.png" width="136" height="158.39999389648438"/> <image name="ephemeral_messages_color_A.png" width="136" height="158.39999389648438"/>
<image name="forward_message_default.png" width="187" height="148"/> <image name="forward_message_default.png" width="187" height="148"/>

View file

@ -1,9 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?> <?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" useTraitCollections="YES" colorMatched="YES"> <document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="20037" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES">
<device id="retina4_7" orientation="portrait" appearance="light"/> <device id="retina4_7" orientation="portrait" appearance="light"/>
<dependencies> <dependencies>
<deployment identifier="iOS"/> <deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="15704"/> <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="20020"/>
<capability name="System colors in document resources" minToolsVersion="11.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/> <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies> </dependencies>
<objects> <objects>
@ -23,18 +24,18 @@
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMinY="YES" flexibleMaxY="YES"/> <autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMinY="YES" flexibleMaxY="YES"/>
<subviews> <subviews>
<imageView userInteractionEnabled="NO" contentMode="scaleAspectFit" fixedFrame="YES" insetsLayoutMarginsFromSafeArea="NO" image="avatar.png" translatesAutoresizingMaskIntoConstraints="NO" id="Z2U-vm-azg" userLabel="avatarImage" customClass="UIRoundedImageView"> <imageView userInteractionEnabled="NO" contentMode="scaleAspectFit" fixedFrame="YES" insetsLayoutMarginsFromSafeArea="NO" image="avatar.png" translatesAutoresizingMaskIntoConstraints="NO" id="Z2U-vm-azg" userLabel="avatarImage" customClass="UIRoundedImageView">
<rect key="frame" x="0.0" y="5" width="44" height="28"/> <rect key="frame" x="0.0" y="8" width="44" height="28"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxX="YES" heightSizable="YES"/> <autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxX="YES" heightSizable="YES"/>
</imageView> </imageView>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" insetsLayoutMarginsFromSafeArea="NO" text="John Doe" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="f3q-pd-EF5" userLabel="displayNameLabel"> <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" insetsLayoutMarginsFromSafeArea="NO" text="John Doe" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="f3q-pd-EF5" userLabel="displayNameLabel">
<rect key="frame" x="43" y="11" width="185" height="16"/> <rect key="frame" x="43" y="0.0" width="185" height="44"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" widthSizable="YES" flexibleMaxX="YES" heightSizable="YES"/> <autoresizingMask key="autoresizingMask" flexibleMinX="YES" widthSizable="YES" flexibleMaxX="YES" heightSizable="YES"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/> <fontDescription key="fontDescription" type="system" pointSize="17"/>
<nil key="textColor"/> <nil key="textColor"/>
<nil key="highlightedColor"/> <nil key="highlightedColor"/>
</label> </label>
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" fixedFrame="YES" insetsLayoutMarginsFromSafeArea="NO" text="Today - 18h42" textAlignment="right" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="hEV-7I-B0v" userLabel="contactDateLabel"> <label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" fixedFrame="YES" insetsLayoutMarginsFromSafeArea="NO" text="Today - 18h42" textAlignment="right" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="hEV-7I-B0v" userLabel="contactDateLabel">
<rect key="frame" x="200" y="13" width="159" height="12"/> <rect key="frame" x="229" y="16" width="130" height="12"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" widthSizable="YES" heightSizable="YES"/> <autoresizingMask key="autoresizingMask" flexibleMinX="YES" widthSizable="YES" heightSizable="YES"/>
<accessibility key="accessibilityConfiguration" label="Contact name"/> <accessibility key="accessibilityConfiguration" label="Contact name"/>
<fontDescription key="fontDescription" type="system" pointSize="12"/> <fontDescription key="fontDescription" type="system" pointSize="12"/>
@ -42,12 +43,15 @@
<nil key="highlightedColor"/> <nil key="highlightedColor"/>
</label> </label>
</subviews> </subviews>
<color key="backgroundColor" systemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/> <color key="backgroundColor" systemColor="systemBackgroundColor"/>
</tableViewCellContentView> </tableViewCellContentView>
<point key="canvasLocation" x="61.5" y="52"/> <point key="canvasLocation" x="60" y="51.27436281859071"/>
</tableViewCell> </tableViewCell>
</objects> </objects>
<resources> <resources>
<image name="avatar.png" width="414.39999389648438" height="414.39999389648438"/> <image name="avatar.png" width="414.39999389648438" height="414.39999389648438"/>
<systemColor name="systemBackgroundColor">
<color white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
</systemColor>
</resources> </resources>
</document> </document>

View file

@ -1,9 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="20037" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" colorMatched="YES"> <document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="21507" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" colorMatched="YES">
<device id="retina4_7" orientation="portrait" appearance="light"/> <device id="retina4_7" orientation="portrait" appearance="light"/>
<dependencies> <dependencies>
<deployment identifier="iOS"/> <deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="20020"/> <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="21505"/>
<capability name="System colors in document resources" minToolsVersion="11.0"/> <capability name="System colors in document resources" minToolsVersion="11.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/> <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies> </dependencies>
@ -61,7 +61,7 @@
<color key="backgroundColor" systemColor="systemBackgroundColor"/> <color key="backgroundColor" systemColor="systemBackgroundColor"/>
</view> </view>
</subviews> </subviews>
<color key="backgroundColor" systemColor="systemBackgroundColor"/> <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<nil key="simulatedStatusBarMetrics"/> <nil key="simulatedStatusBarMetrics"/>
<freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/> <freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
<point key="canvasLocation" x="890.39999999999998" y="192.50374812593705"/> <point key="canvasLocation" x="890.39999999999998" y="192.50374812593705"/>

View file

@ -1,41 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="19162" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES">
<device id="retina4_7" orientation="portrait" appearance="light"/>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="19144"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
<collectionViewCell opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" id="gTV-IL-0wX" customClass="UIChatCreateCollectionViewCell">
<rect key="frame" x="0.0" y="0.0" width="100" height="50"/>
<autoresizingMask key="autoresizingMask"/>
<view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center">
<rect key="frame" x="0.0" y="0.0" width="100" height="50"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" text="John Doe" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="fHV-en-AZD" userLabel="displayNameLabel">
<rect key="frame" x="14" y="18" width="92" height="20"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" widthSizable="YES" flexibleMaxX="YES" flexibleMinY="YES" heightSizable="YES" flexibleMaxY="YES"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<nil key="highlightedColor"/>
</label>
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" fixedFrame="YES" image="conference_delete.png" translatesAutoresizingMaskIntoConstraints="NO" id="yfP-hQ-SXb" userLabel="selectedImage">
<rect key="frame" x="1" y="23" width="10" height="10"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES" flexibleMaxY="YES"/>
</imageView>
</subviews>
</view>
<size key="customSize" width="170" height="45"/>
<connections>
<outlet property="nameLabel" destination="fHV-en-AZD" id="gOU-sp-v0V"/>
</connections>
<point key="canvasLocation" x="2" y="86"/>
</collectionViewCell>
</objects>
<resources>
<image name="conference_delete.png" width="17.600000381469727" height="17.600000381469727"/>
</resources>
</document>

View file

@ -1,9 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="19162" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES"> <document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="20037" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
<device id="retina6_1" orientation="portrait" appearance="light"/> <device id="retina6_1" orientation="portrait" appearance="light"/>
<dependencies> <dependencies>
<deployment identifier="iOS"/> <deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="19144"/> <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="20020"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/> <capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="System colors in document resources" minToolsVersion="11.0"/> <capability name="System colors in document resources" minToolsVersion="11.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/> <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
@ -13,6 +13,7 @@
<connections> <connections>
<outlet property="contentCollection" destination="ZgY-Ye-8DD" id="hlD-Fw-g1z"/> <outlet property="contentCollection" destination="ZgY-Ye-8DD" id="hlD-Fw-g1z"/>
<outlet property="dismissButton" destination="KrT-j6-YOy" id="K5m-Mw-Cjx"/> <outlet property="dismissButton" destination="KrT-j6-YOy" id="K5m-Mw-Cjx"/>
<outlet property="icsIcon" destination="gkH-jk-DYU" id="Xkl-Ip-skO"/>
<outlet property="leftBar" destination="Iov-AL-8Xz" id="eKb-4B-unZ"/> <outlet property="leftBar" destination="Iov-AL-8Xz" id="eKb-4B-unZ"/>
<outlet property="originalMessageGone" destination="B26-sw-o4w" id="Gwh-dh-GRN"/> <outlet property="originalMessageGone" destination="B26-sw-o4w" id="Gwh-dh-GRN"/>
<outlet property="senderName" destination="uuW-tW-1Sj" id="Yao-6A-SBh"/> <outlet property="senderName" destination="uuW-tW-1Sj" id="Yao-6A-SBh"/>
@ -68,6 +69,10 @@
<autoresizingMask key="autoresizingMask" flexibleMinX="YES"/> <autoresizingMask key="autoresizingMask" flexibleMinX="YES"/>
<state key="normal" image="reply_cancel.png"/> <state key="normal" image="reply_cancel.png"/>
</button> </button>
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" image="voip_meeting_schedule.png" translatesAutoresizingMaskIntoConstraints="NO" id="gkH-jk-DYU">
<rect key="frame" x="16" y="23" width="35" height="35"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
</imageView>
</subviews> </subviews>
<viewLayoutGuide key="safeArea" id="fnl-2z-Ty3"/> <viewLayoutGuide key="safeArea" id="fnl-2z-Ty3"/>
<color key="backgroundColor" systemColor="systemBackgroundColor"/> <color key="backgroundColor" systemColor="systemBackgroundColor"/>
@ -77,6 +82,7 @@
</objects> </objects>
<resources> <resources>
<image name="reply_cancel.png" width="60" height="60"/> <image name="reply_cancel.png" width="60" height="60"/>
<image name="voip_meeting_schedule.png" width="20" height="20"/>
<systemColor name="systemBackgroundColor"> <systemColor name="systemBackgroundColor">
<color white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/> <color white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
</systemColor> </systemColor>

View file

@ -1,9 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="20037" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" colorMatched="YES"> <document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="21507" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" colorMatched="YES">
<device id="retina4_7" orientation="portrait" appearance="light"/> <device id="retina4_7" orientation="portrait" appearance="light"/>
<dependencies> <dependencies>
<deployment identifier="iOS"/> <deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="20020"/> <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="21505"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/> <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies> </dependencies>
<objects> <objects>
@ -11,8 +11,10 @@
<connections> <connections>
<outlet property="authButton" destination="lWW-wB-FMR" id="C7W-JM-WFQ"/> <outlet property="authButton" destination="lWW-wB-FMR" id="C7W-JM-WFQ"/>
<outlet property="authView" destination="CCn-Oz-I0M" id="fSM-6k-paN"/> <outlet property="authView" destination="CCn-Oz-I0M" id="fSM-6k-paN"/>
<outlet property="backgroundColor" destination="cqN-1f-6SE" id="gjg-LB-xLT"/>
<outlet property="cancelButton" destination="B1K-CB-3of" id="KKi-Xc-ldA"/> <outlet property="cancelButton" destination="B1K-CB-3of" id="KKi-Xc-ldA"/>
<outlet property="confirmationButton" destination="SbQ-re-fGQ" id="yiv-a9-o8E"/> <outlet property="confirmationButton" destination="SbQ-re-fGQ" id="yiv-a9-o8E"/>
<outlet property="firstView" destination="ef9-Iu-Bcb" id="hKx-op-r7Z"/>
<outlet property="forwardImage" destination="1Wh-Yi-cUe" id="YQq-bt-pk1"/> <outlet property="forwardImage" destination="1Wh-Yi-cUe" id="YQq-bt-pk1"/>
<outlet property="groupCallImage" destination="SVn-4k-9yc" id="sAP-8V-ttn"/> <outlet property="groupCallImage" destination="SVn-4k-9yc" id="sAP-8V-ttn"/>
<outlet property="securityImage" destination="bbo-g3-bGy" id="qZa-li-yrl"/> <outlet property="securityImage" destination="bbo-g3-bGy" id="qZa-li-yrl"/>
@ -25,27 +27,32 @@
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/> <rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews> <subviews>
<imageView userInteractionEnabled="NO" alpha="0.89999999999999991" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" image="color_C.png" translatesAutoresizingMaskIntoConstraints="NO" id="cqN-1f-6SE" userLabel="backgroundColor"> <view contentMode="scaleToFill" id="ef9-Iu-Bcb" userLabel="firstView">
<rect key="frame" x="0.0" y="0.0" width="377" height="667"/> <rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
</imageView> </view>
<view contentMode="scaleToFill" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="2BQ-o9-xv2"> <view contentMode="scaleToFill" id="2BQ-o9-xv2">
<rect key="frame" x="28" y="139" width="320" height="365"/> <rect key="frame" x="25" y="114" width="325" height="440"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMinY="YES" flexibleMaxY="YES"/> <autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMinY="YES" flexibleMaxY="YES"/>
<subviews> <subviews>
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" image="color_C.png" translatesAutoresizingMaskIntoConstraints="NO" id="cqN-1f-6SE" userLabel="backgroundColor">
<rect key="frame" x="-17" y="-17" width="360" height="474"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" red="0.26666668059999998" green="0.26666668059999998" blue="0.26666668059999998" alpha="1" colorSpace="custom" customColorSpace="displayP3"/>
</imageView>
<imageView hidden="YES" userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" insetsLayoutMarginsFromSafeArea="NO" image="security_2_indicator.png" translatesAutoresizingMaskIntoConstraints="NO" id="bbo-g3-bGy" userLabel="securityImage"> <imageView hidden="YES" userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" insetsLayoutMarginsFromSafeArea="NO" image="security_2_indicator.png" translatesAutoresizingMaskIntoConstraints="NO" id="bbo-g3-bGy" userLabel="securityImage">
<rect key="frame" x="130" y="0.0" width="56" height="68"/> <rect key="frame" x="130" y="15" width="64" height="68"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMaxY="YES"/> <autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMinY="YES" flexibleMaxY="YES"/>
</imageView> </imageView>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" text="Are you sure you want to delete all your selection?" textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" minimumScaleFactor="0.5" translatesAutoresizingMaskIntoConstraints="NO" id="jLz-g1-cTe" userLabel="titleLabel"> <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" text="Are you sure you want to delete all your selection?" textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" minimumScaleFactor="0.5" translatesAutoresizingMaskIntoConstraints="NO" id="jLz-g1-cTe" userLabel="titleLabel">
<rect key="frame" x="-10" y="15" width="336" height="279"/> <rect key="frame" x="-8" y="15" width="347" height="350"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" widthSizable="YES" flexibleMaxX="YES" flexibleMinY="YES" heightSizable="YES" flexibleMaxY="YES"/> <autoresizingMask key="autoresizingMask" flexibleMinX="YES" widthSizable="YES" flexibleMaxX="YES" flexibleMinY="YES" heightSizable="YES" flexibleMaxY="YES"/>
<fontDescription key="fontDescription" type="system" pointSize="21"/> <fontDescription key="fontDescription" type="system" pointSize="21"/>
<color key="textColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> <color key="textColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/> <nil key="highlightedColor"/>
</label> </label>
<button opaque="NO" contentMode="scaleToFill" fixedFrame="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="B1K-CB-3of" userLabel="cancelButton" customClass="UIRoundBorderedButton"> <button opaque="NO" contentMode="scaleToFill" fixedFrame="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="B1K-CB-3of" userLabel="cancelButton" customClass="UIRoundBorderedButton">
<rect key="frame" x="8" y="308" width="139" height="42"/> <rect key="frame" x="16" y="383" width="139" height="36"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMinY="YES" flexibleMaxY="YES"/> <autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMinY="YES" flexibleMaxY="YES"/>
<state key="normal" title="CANCEL" backgroundImage="color_H.png"> <state key="normal" title="CANCEL" backgroundImage="color_H.png">
<color key="titleColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> <color key="titleColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
@ -57,11 +64,11 @@
</connections> </connections>
</button> </button>
<view hidden="YES" contentMode="scaleToFill" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="CCn-Oz-I0M" userLabel="authView"> <view hidden="YES" contentMode="scaleToFill" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="CCn-Oz-I0M" userLabel="authView">
<rect key="frame" x="61" y="273" width="240" height="27"/> <rect key="frame" x="65" y="345" width="243" height="25"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMinY="YES" flexibleMaxY="YES"/> <autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMinY="YES" flexibleMaxY="YES"/>
<subviews> <subviews>
<button opaque="NO" contentMode="scaleToFill" fixedFrame="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="lWW-wB-FMR" userLabel="authButton"> <button opaque="NO" contentMode="scaleToFill" fixedFrame="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="lWW-wB-FMR" userLabel="authButton">
<rect key="frame" x="26" y="2" width="17" height="22"/> <rect key="frame" x="24" y="0.0" width="22" height="24"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMinY="YES" flexibleMaxY="YES"/> <autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMinY="YES" flexibleMaxY="YES"/>
<state key="normal" image="checkbox_unchecked.png"/> <state key="normal" image="checkbox_unchecked.png"/>
<state key="selected" image="checkbox_checked.png"/> <state key="selected" image="checkbox_checked.png"/>
@ -70,7 +77,7 @@
</connections> </connections>
</button> </button>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" text="Do not show again" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="dQL-Sf-slc"> <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" text="Do not show again" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="dQL-Sf-slc">
<rect key="frame" x="55" y="2" width="176" height="21"/> <rect key="frame" x="57" y="10" width="173" height="15"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMinY="YES" flexibleMaxY="YES"/> <autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMinY="YES" flexibleMaxY="YES"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/> <fontDescription key="fontDescription" type="system" pointSize="17"/>
<color key="textColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/> <color key="textColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
@ -79,7 +86,7 @@
</subviews> </subviews>
</view> </view>
<button opaque="NO" contentMode="scaleToFill" fixedFrame="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="SbQ-re-fGQ" userLabel="confirmationButton" customClass="UIRoundBorderedButton"> <button opaque="NO" contentMode="scaleToFill" fixedFrame="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="SbQ-re-fGQ" userLabel="confirmationButton" customClass="UIRoundBorderedButton">
<rect key="frame" x="169" y="308" width="143" height="42"/> <rect key="frame" x="177" y="383" width="136" height="36"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMinY="YES" flexibleMaxY="YES"/> <autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMinY="YES" flexibleMaxY="YES"/>
<state key="normal" title="DELETE" backgroundImage="color_I.png"> <state key="normal" title="DELETE" backgroundImage="color_I.png">
<color key="titleColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> <color key="titleColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
@ -91,17 +98,16 @@
</connections> </connections>
</button> </button>
<imageView hidden="YES" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" image="forward_message_default.png" translatesAutoresizingMaskIntoConstraints="NO" id="1Wh-Yi-cUe"> <imageView hidden="YES" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" image="forward_message_default.png" translatesAutoresizingMaskIntoConstraints="NO" id="1Wh-Yi-cUe">
<rect key="frame" x="89" y="50" width="138" height="54"/> <rect key="frame" x="100" y="50" width="136" height="54"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMaxY="YES"/> <autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMaxY="YES"/>
</imageView> </imageView>
<imageView hidden="YES" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" image="voip_conference_new.png" translatesAutoresizingMaskIntoConstraints="NO" id="SVn-4k-9yc"> <imageView hidden="YES" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" image="voip_conference_new.png" translatesAutoresizingMaskIntoConstraints="NO" id="SVn-4k-9yc">
<rect key="frame" x="89" y="50" width="138" height="54"/> <rect key="frame" x="100" y="50" width="136" height="54"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMaxY="YES"/> <autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMaxY="YES"/>
</imageView> </imageView>
</subviews> </subviews>
</view> </view>
</subviews> </subviews>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="simulatedStatusBarMetrics"/> <nil key="simulatedStatusBarMetrics"/>
<point key="canvasLocation" x="871.20000000000005" y="261.31934032983509"/> <point key="canvasLocation" x="871.20000000000005" y="261.31934032983509"/>
</view> </view>

View file

@ -35,6 +35,7 @@
@property(weak, nonatomic) IBOutlet UIView *outcallView; @property(weak, nonatomic) IBOutlet UIView *outcallView;
- (IBAction)onSecurityClick:(id)sender; - (IBAction)onSecurityClick:(id)sender;
- (IBAction)onQualityClick:(id)sender;
- (IBAction)onSideMenuClick:(id)sender; - (IBAction)onSideMenuClick:(id)sender;
- (IBAction)onRegistrationStateClick:(id)sender; - (IBAction)onRegistrationStateClick:(id)sender;
+ (UIImage *)imageForState:(LinphoneRegistrationState)state; + (UIImage *)imageForState:(LinphoneRegistrationState)state;

View file

@ -21,6 +21,7 @@
#import "LinphoneManager.h" #import "LinphoneManager.h"
#import "PhoneMainView.h" #import "PhoneMainView.h"
#import <UserNotifications/UserNotifications.h> #import <UserNotifications/UserNotifications.h>
#import "linphoneapp-Swift.h"
@implementation StatusBarView { @implementation StatusBarView {
@ -193,11 +194,13 @@
message = NSLocalizedString(@"Fetching remote configuration", nil); message = NSLocalizedString(@"Fetching remote configuration", nil);
} else if (account == NULL) { } else if (account == NULL) {
state = LinphoneRegistrationNone; state = LinphoneRegistrationNone;
if (linphone_core_get_account_list(LC) != NULL) { MSList *accounts = [LinphoneManager.instance createAccountsNotHiddenList];
if (accounts != NULL) {
message = NSLocalizedString(@"No default account", nil); message = NSLocalizedString(@"No default account", nil);
} else { } else {
message = NSLocalizedString(@"No account configured", nil); message = NSLocalizedString(@"No account configured", nil);
} }
bctbx_free(accounts);
} else { } else {
state = linphone_account_get_state(account); state = linphone_account_get_state(account);
@ -329,14 +332,9 @@
correspondantCode = [code substringToIndex:2]; correspondantCode = [code substringToIndex:2];
myCode = [code substringFromIndex:2]; myCode = [code substringFromIndex:2];
} }
NSString *message = NSString *message = [NSString stringWithFormat:NSLocalizedString(@"\nCommunication security:\n\nTo raise the security level, you can check the following codes with your correspondent.\n\nSay: %1$@\n\nYour correspondent must say: %2$@", nil),
[NSString stringWithFormat:NSLocalizedString(@"\nConfirmation security\n\n" myCode.uppercaseString, correspondantCode.uppercaseString];
@"Say: %@\n"
@"Confirm that your interlocutor\n"
@"says: %@",
nil),
myCode.uppercaseString, correspondantCode.uppercaseString];
if ([UIApplication sharedApplication].applicationState != UIApplicationStateActive && if ([UIApplication sharedApplication].applicationState != UIApplicationStateActive &&
floor(NSFoundationVersionNumber) > NSFoundationVersionNumber_iOS_9_x_Max) { floor(NSFoundationVersionNumber) > NSFoundationVersionNumber_iOS_9_x_Max) {
UNMutableNotificationContent *content = [[UNMutableNotificationContent alloc] init]; UNMutableNotificationContent *content = [[UNMutableNotificationContent alloc] init];
@ -368,14 +366,14 @@
UIFont *baseFont = [UIFont systemFontOfSize:21.0]; UIFont *baseFont = [UIFont systemFontOfSize:21.0];
[attrString addAttribute:NSFontAttributeName value:baseFont range:NSMakeRange(0, length)]; [attrString addAttribute:NSFontAttributeName value:baseFont range:NSMakeRange(0, length)];
UIFont *boldFont = [UIFont boldSystemFontOfSize:23.0]; UIFont *boldFont = [UIFont boldSystemFontOfSize:23.0];
[attrString addAttribute:NSFontAttributeName value:boldFont range:[message rangeOfString:@"Confirmation security"]]; [attrString addAttribute:NSFontAttributeName value:boldFont range:[message rangeOfString:@"Communication security"]];
UIColor *color = [UIColor colorWithRed:(150 / 255.0) green:(193 / 255.0) blue:(31 / 255.0) alpha:1.0]; UIColor *color = [UIColor colorWithRed:(150 / 255.0) green:(193 / 255.0) blue:(31 / 255.0) alpha:1.0];
[attrString addAttribute:NSForegroundColorAttributeName value:color range:[message rangeOfString:myCode.uppercaseString]]; [attrString addAttribute:NSForegroundColorAttributeName value:color range:[message rangeOfString:myCode.uppercaseString]];
[attrString addAttribute:NSForegroundColorAttributeName value:color range:[message rangeOfString:correspondantCode.uppercaseString]]; [attrString addAttribute:NSForegroundColorAttributeName value:color range:[message rangeOfString:correspondantCode.uppercaseString]];
securityDialog = [UIConfirmationDialog ShowWithAttributedMessage:attrString securityDialog = [UIConfirmationDialog ShowWithAttributedMessage:attrString
cancelMessage:NSLocalizedString(@"DENY", nil) cancelMessage:NSLocalizedString(@"Later", nil)
confirmMessage:NSLocalizedString(@"ACCEPT", nil) confirmMessage:NSLocalizedString(@"Correct", nil)
onCancelClick:^() { onCancelClick:^() {
if (linphone_core_get_current_call(LC) == call) { if (linphone_core_get_current_call(LC) == call) {
linphone_call_set_authentication_token_verified(call, NO); linphone_call_set_authentication_token_verified(call, NO);
@ -393,6 +391,7 @@
securityDialog.securityImage.hidden = FALSE; securityDialog.securityImage.hidden = FALSE;
[securityDialog setSpecialColor]; [securityDialog setSpecialColor];
[securityDialog setWhiteCancel];
} }
} }
} }
@ -400,6 +399,10 @@
} }
} }
- (IBAction)onQualityClick:(id)sender {
[ControlsViewModelBridge toggleStatsVisibility];
}
- (IBAction)onSideMenuClick:(id)sender { - (IBAction)onSideMenuClick:(id)sender {
UICompositeView *cvc = PhoneMainView.instance.mainViewController; UICompositeView *cvc = PhoneMainView.instance.mainViewController;
[cvc hideSideMenu:(cvc.sideMenuView.frame.origin.x == 0)]; [cvc hideSideMenu:(cvc.sideMenuView.frame.origin.x == 0)];
@ -408,10 +411,15 @@
- (IBAction)onRegistrationStateClick:(id)sender { - (IBAction)onRegistrationStateClick:(id)sender {
if (linphone_core_get_default_account(LC)) { if (linphone_core_get_default_account(LC)) {
linphone_core_refresh_registers(LC); linphone_core_refresh_registers(LC);
} else if (linphone_core_get_account_list(LC)) {
[PhoneMainView.instance changeCurrentView:SettingsView.compositeViewDescription];
} else { } else {
[PhoneMainView.instance changeCurrentView:AssistantView.compositeViewDescription];
MSList *accounts = [LinphoneManager.instance createAccountsNotHiddenList];
if (accounts) {
[PhoneMainView.instance changeCurrentView:SettingsView.compositeViewDescription];
} else {
[PhoneMainView.instance changeCurrentView:AssistantView.compositeViewDescription];
}
bctbx_free(accounts);
} }
} }

View file

@ -48,7 +48,7 @@
} }
- (IBAction)onBackToCallClick:(id)sender { - (IBAction)onBackToCallClick:(id)sender {
[PhoneMainView.instance popToView:ActiveCallOrConferenceView.compositeViewDescription]; [PhoneMainView.instance popToView:[CallsViewModelBridge callViewToDisplay]];
} }
@end @end

View file

@ -192,14 +192,20 @@
_voiceRecordingFile = nil; _voiceRecordingFile = nil;
LinphoneContent *voiceContent = [UIChatBubbleTextCell voiceContent:self.message]; LinphoneContent *voiceContent = [UIChatBubbleTextCell voiceContent:self.message];
if (voiceContent) { if (voiceContent) {
_voiceRecordingFile = [NSString stringWithUTF8String:[VFSUtil vfsEnabledWithGroupName:kLinphoneMsgNotificationAppGroupId] ? linphone_content_get_plain_file_path(voiceContent) : linphone_content_get_file_path(voiceContent)]; const char *fileName = ([VFSUtil vfsEnabledWithGroupName:kLinphoneMsgNotificationAppGroupId] ? linphone_content_get_plain_file_path(voiceContent) : linphone_content_get_file_path(voiceContent));
if ([VFSUtil vfsEnabledWithGroupName:kLinphoneMsgNotificationAppGroupId]) if (fileName == nil) {
linphone_content_set_file_path(voiceContent, [[LinphoneManager imagesDirectory] stringByAppendingPathComponent:[[NSUUID UUID] UUIDString]].UTF8String);
linphone_chat_message_download_content(self.message, voiceContent);
}
_voiceRecordingFile = fileName ? [NSString stringWithUTF8String:fileName] : nil;
if (fileName && [VFSUtil vfsEnabledWithGroupName:kLinphoneMsgNotificationAppGroupId]) {
[encrptedFilePaths setValue:_voiceRecordingFile forKey:[NSString stringWithUTF8String:linphone_content_get_name(voiceContent)]]; [encrptedFilePaths setValue:_voiceRecordingFile forKey:[NSString stringWithUTF8String:linphone_content_get_name(voiceContent)]];
}
_vrTimerLabel.text = [self formattedDuration:linphone_content_get_file_duration(voiceContent)/1000]; _vrTimerLabel.text = [self formattedDuration:linphone_content_get_file_duration(voiceContent)/1000];
_vrWaveMaskPlayback.frame = CGRectZero; _vrWaveMaskPlayback.frame = CGRectZero;
_vrWaveMaskPlayback.backgroundColor = linphone_chat_message_is_outgoing(self.message) ? UIColor.orangeColor : UIColor.grayColor; _vrWaveMaskPlayback.backgroundColor = linphone_chat_message_is_outgoing(self.message) ? UIColor.orangeColor : UIColor.grayColor;
} }
const bctbx_list_t *contents = linphone_chat_message_get_contents(self.message); const bctbx_list_t *contents = linphone_chat_message_get_contents(self.message);
size_t contentCount = bctbx_list_size(contents); size_t contentCount = bctbx_list_size(contents);
@ -234,7 +240,8 @@
if (strcmp(cPath, "") != 0) { if (strcmp(cPath, "") != 0) {
NSString *tempPath = [NSString stringWithUTF8String:cPath]; NSString *tempPath = [NSString stringWithUTF8String:cPath];
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES); NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
filePath = [paths objectAtIndex:0]; filePath = [NSString stringWithFormat:@"%@/%s", [paths objectAtIndex:0],linphone_chat_message_get_message_id(super.message)];
[FileUtil ensureDirectoryExistsWithPath:filePath];
filePath = [filePath stringByAppendingPathComponent:name]; filePath = [filePath stringByAppendingPathComponent:name];
[[NSFileManager defaultManager] moveItemAtPath:tempPath toPath:filePath error:nil]; [[NSFileManager defaultManager] moveItemAtPath:tempPath toPath:filePath error:nil];
} }
@ -300,7 +307,8 @@
if (strcmp(cPath, "") != 0) { if (strcmp(cPath, "") != 0) {
NSString *tempPath = [NSString stringWithUTF8String:cPath]; NSString *tempPath = [NSString stringWithUTF8String:cPath];
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES); NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
filePath = [paths objectAtIndex:0]; filePath = [NSString stringWithFormat:@"%@/%s", [paths objectAtIndex:0],linphone_chat_message_get_message_id(super.message)];
[FileUtil ensureDirectoryExistsWithPath:filePath];
filePath = [filePath stringByAppendingPathComponent:fileName]; filePath = [filePath stringByAppendingPathComponent:fileName];
[[NSFileManager defaultManager] moveItemAtPath:tempPath toPath:filePath error:nil]; [[NSFileManager defaultManager] moveItemAtPath:tempPath toPath:filePath error:nil];
} }

View file

@ -46,10 +46,8 @@
[self setFrame:CGRectMake(0, 0, sub.frame.size.width, sub.frame.size.height)]; [self setFrame:CGRectMake(0, 0, sub.frame.size.width, sub.frame.size.height)];
[self addSubview:sub]; [self addSubview:sub];
self.icsBubbleView = [[ICSBubbleView alloc] init]; self.icsBubbleView = [[ICSBubbleView alloc] init];
self.icsBubbleView.frame = CGRectMake(_messageText.frame.origin.x, _messageText.frame.origin.y+25, CONFERENCE_INVITATION_WIDTH-80, CONFERENCE_INVITATION_HEIGHT-20);
[self.innerView addSubview:self.icsBubbleView]; [self.innerView addSubview:self.icsBubbleView];
[(ICSBubbleView*)self.icsBubbleView setLayoutConstraintsWithView:self.backgroundColorImage]; [(ICSBubbleView*)self.icsBubbleView setLayoutConstraintsWithView:self.backgroundColorImage];
} }
} }
@ -168,7 +166,7 @@
return; return;
} }
if (_messageText && ![LinphoneManager getMessageAppDataForKey:@"localvideo" inMessage:_message]) { if (_messageText && ![LinphoneManager getMessageAppDataForKey:@"localvideo" inMessage:_message] && ![ICSBubbleView isConferenceInvitationMessageWithCmessage:self.message]) {
[_messageText setHidden:FALSE]; [_messageText setHidden:FALSE];
/* We need to use an attributed string here so that data detector don't mess /* We need to use an attributed string here so that data detector don't mess
* with the text style. See http://stackoverflow.com/a/20669356 */ * with the text style. See http://stackoverflow.com/a/20669356 */
@ -346,8 +344,10 @@ static void message_status(LinphoneChatMessage *msg, LinphoneChatMessageState st
} }
static void participant_imdn_status(LinphoneChatMessage* msg, const LinphoneParticipantImdnState *state) { static void participant_imdn_status(LinphoneChatMessage* msg, const LinphoneParticipantImdnState *state) {
ChatConversationImdnView *imdnView = VIEW(ChatConversationImdnView); dispatch_async(dispatch_get_main_queue(), ^{
[imdnView updateImdnList]; ChatConversationImdnView *imdnView = VIEW(ChatConversationImdnView);
[imdnView updateImdnList];
});
} }
- (void)displayImdmStatus:(LinphoneChatMessageState)state { - (void)displayImdmStatus:(LinphoneChatMessageState)state {
@ -399,7 +399,7 @@ static const CGFloat REPLY_OR_FORWARD_TAG_HEIGHT = 18;
return cached; return cached;
} }
CGSize size = [self ViewHeightForMessageText:chat withWidth:width textForImdn:nil]; CGSize size = [self ViewHeightForMessageText:chat withWidth:width textForImdn:nil];
size.height += linphone_chat_message_is_forward(chat) || linphone_chat_message_is_reply(chat) ? REPLY_OR_FORWARD_TAG_HEIGHT : 0; size.height += linphone_chat_message_is_forward(chat) || linphone_chat_message_is_reply(chat) ? REPLY_OR_FORWARD_TAG_HEIGHT : 0;
size.height += linphone_chat_message_is_reply(chat) ? REPLY_CHAT_BUBBLE_HEIGHT+5 : 0; size.height += linphone_chat_message_is_reply(chat) ? REPLY_CHAT_BUBBLE_HEIGHT+5 : 0;
@ -491,7 +491,7 @@ static const CGFloat REPLY_OR_FORWARD_TAG_HEIGHT = 18;
+ (CGSize)ViewHeightForMessageText:(LinphoneChatMessage *)chat withWidth:(int)width textForImdn:(NSString *)imdnText { + (CGSize)ViewHeightForMessageText:(LinphoneChatMessage *)chat withWidth:(int)width textForImdn:(NSString *)imdnText {
if ([ICSBubbleView isConferenceInvitationMessageWithCmessage:chat]) { if ([ICSBubbleView isConferenceInvitationMessageWithCmessage:chat]) {
return CGSizeMake(CONFERENCE_INVITATION_WIDTH, CONFERENCE_INVITATION_HEIGHT); return CGSizeMake(CONFERENCE_INVITATION_WIDTH, CONFERENCE_INVITATION_HEIGHT+[ICSBubbleView getDescriptionHeightFromContentWithCmessage:chat]);
} }
NSString *messageText = [UIChatBubbleTextCell TextMessageForChat:chat]; NSString *messageText = [UIChatBubbleTextCell TextMessageForChat:chat];
@ -791,6 +791,8 @@ static const CGFloat REPLY_OR_FORWARD_TAG_HEIGHT = 18;
_replyTransferIcon.hidden = ! linphone_chat_message_is_reply(_message) && !linphone_chat_message_is_forward(_message); _replyTransferIcon.hidden = ! linphone_chat_message_is_reply(_message) && !linphone_chat_message_is_forward(_message);
_replyTransferLabel.hidden = ! linphone_chat_message_is_reply(_message) && !linphone_chat_message_is_forward(_message); _replyTransferLabel.hidden = ! linphone_chat_message_is_reply(_message) && !linphone_chat_message_is_forward(_message);
[(ICSBubbleView*)self.icsBubbleView updateTopLayoutConstraintsWithView:self.backgroundColorImage replyOrForward:linphone_chat_message_is_reply(_message)||linphone_chat_message_is_forward(_message)];
if (linphone_chat_message_is_reply(_message)) { if (linphone_chat_message_is_reply(_message)) {
CGRect replyFrame = CGRectMake(10, _replyTransferLabel.frame.origin.y+_replyTransferLabel.frame.size.height+5,MAX(self.contactDateLabel.frame.size.width-20,180), REPLY_CHAT_BUBBLE_HEIGHT); CGRect replyFrame = CGRectMake(10, _replyTransferLabel.frame.origin.y+_replyTransferLabel.frame.size.height+5,MAX(self.contactDateLabel.frame.size.width-20,180), REPLY_CHAT_BUBBLE_HEIGHT);
@ -848,10 +850,13 @@ static const CGFloat REPLY_OR_FORWARD_TAG_HEIGHT = 18;
-(void) buildActions { -(void) buildActions {
LinphoneChatMessage *message = self.message; LinphoneChatMessage *message = self.message;
LinphoneEventLog *event = self.event;
_messageActionsTitles = [[NSMutableArray alloc] init]; _messageActionsTitles = [[NSMutableArray alloc] init];
_messageActionsBlocks = [[NSMutableArray alloc] init]; _messageActionsBlocks = [[NSMutableArray alloc] init];
_messageActionsIcons = [[NSMutableArray alloc] init]; _messageActionsIcons = [[NSMutableArray alloc] init];
[VIEW(ChatConversationView).messageField resignFirstResponder];
UIChatBubbleTextCell *thiz = self; UIChatBubbleTextCell *thiz = self;
LinphoneChatMessageState state = linphone_chat_message_get_state(self.message); LinphoneChatMessageState state = linphone_chat_message_get_state(self.message);
@ -869,7 +874,7 @@ static const CGFloat REPLY_OR_FORWARD_TAG_HEIGHT = 18;
} }
if (linphone_chat_message_get_utf8_text(message)) { if (linphone_chat_message_get_utf8_text(message) && ![ICSBubbleView isConferenceInvitationMessageWithCmessage:message]) {
[_messageActionsTitles addObject:NSLocalizedString(@"Copy text", nil)]; [_messageActionsTitles addObject:NSLocalizedString(@"Copy text", nil)];
[_messageActionsIcons addObject:@"menu_copy_text_default"]; [_messageActionsIcons addObject:@"menu_copy_text_default"];
[_messageActionsBlocks addObject:^{ [_messageActionsBlocks addObject:^{
@ -896,16 +901,43 @@ static const CGFloat REPLY_OR_FORWARD_TAG_HEIGHT = 18;
[VIEW(ChatConversationView) initiateReplyViewForMessage:message]; [VIEW(ChatConversationView) initiateReplyViewForMessage:message];
}]; }];
if (linphone_chat_message_is_outgoing(self.message) || linphone_chat_room_get_nb_participants(linphone_chat_message_get_chat_room(self.message)) > 1) { LinphoneChatRoom *chatroom = linphone_chat_message_get_chat_room(self.message);
if (linphone_chat_room_get_nb_participants(chatroom) > 1) {
[_messageActionsTitles addObject:NSLocalizedString(@"Infos", nil)]; [_messageActionsTitles addObject:NSLocalizedString(@"Infos", nil)];
[_messageActionsIcons addObject:@"menu_info"]; [_messageActionsIcons addObject:@"menu_info"];
[_messageActionsBlocks addObject:^{ [_messageActionsBlocks addObject:^{
[thiz dismissPopup]; [thiz dismissPopup];
ChatConversationImdnView *view = VIEW(ChatConversationImdnView); ChatConversationImdnView *view = VIEW(ChatConversationImdnView);
view.msg = message; view.event = event;
[PhoneMainView.instance changeCurrentView:view.compositeViewDescription]; [PhoneMainView.instance changeCurrentView:view.compositeViewDescription];
}]; }];
} }
if (!linphone_chat_message_is_outgoing(self.message)
&& [FastAddressBook getContactWithAddress:linphone_chat_message_get_from_address(self.message)] == nil
&& !(linphone_chat_room_get_capabilities(chatroom) & LinphoneChatRoomCapabilitiesOneToOne) ) {
LinphoneAddress *fromAddress = linphone_address_clone(linphone_chat_message_get_from_address(self.message));
[_messageActionsTitles addObject:NSLocalizedString(@"Add to contact", nil)];
[_messageActionsIcons addObject:@"contact_add_default"];
[_messageActionsBlocks addObject:^{
[thiz dismissPopup];
linphone_address_clean(fromAddress);
char *lAddress = linphone_address_as_string_uri_only(fromAddress);
if (lAddress != NULL) {
NSString *normSip = [NSString stringWithUTF8String:lAddress];
normSip = [normSip hasPrefix:@"sip:"] ? [normSip substringFromIndex:4] : normSip;
normSip = [normSip hasPrefix:@"sips:"] ? [normSip substringFromIndex:5] : normSip;
[ContactSelection setAddAddress:normSip];
[ContactSelection setSelectionMode:ContactSelectionModeEdit];
[ContactSelection enableSipFilter:FALSE];
[PhoneMainView.instance changeCurrentView:ContactsListView.compositeViewDescription];
ms_free(lAddress);
}
linphone_address_unref(fromAddress);
}];
}
[_messageActionsTitles addObject:NSLocalizedString(@"Delete", nil)]; [_messageActionsTitles addObject:NSLocalizedString(@"Delete", nil)];
[_messageActionsIcons addObject:@"menu_delete"]; [_messageActionsIcons addObject:@"menu_delete"];
@ -930,7 +962,7 @@ static const CGFloat REPLY_OR_FORWARD_TAG_HEIGHT = 18;
int cellHeight = 45; int cellHeight = 45;
int numberOfItems = (int) _messageActionsTitles.count; int numberOfItems = (int) _messageActionsTitles.count;
CGRect screenRect = UIScreen.mainScreen.bounds; CGRect screenRect = UIScreen.mainScreen.bounds;
int menuHeight = numberOfItems * cellHeight + 15; int menuHeight = numberOfItems * cellHeight;
CGRect frame = CGRectMake( CGRect frame = CGRectMake(
linphone_chat_message_is_outgoing(self.message) ? screenRect.size.width - width - 10 : 10, linphone_chat_message_is_outgoing(self.message) ? screenRect.size.width - width - 10 : 10,
@ -942,7 +974,8 @@ static const CGFloat REPLY_OR_FORWARD_TAG_HEIGHT = 18;
_popupMenu.scrollEnabled = false; _popupMenu.scrollEnabled = false;
_popupMenu.dataSource = self; _popupMenu.dataSource = self;
_popupMenu.delegate = self; _popupMenu.delegate = self;
_popupMenu.separatorStyle = UITableViewCellSeparatorStyleNone;
_popupMenu.layer.masksToBounds = false; _popupMenu.layer.masksToBounds = false;
_popupMenu.layer.shadowColor = [UIColor darkGrayColor].CGColor; _popupMenu.layer.shadowColor = [UIColor darkGrayColor].CGColor;

View file

@ -21,6 +21,7 @@
#import "PhoneMainView.h" #import "PhoneMainView.h"
#import "LinphoneManager.h" #import "LinphoneManager.h"
#import "Utils.h" #import "Utils.h"
#import "linphoneapp-Swift.h"
@implementation UIChatCell @implementation UIChatCell
@ -38,6 +39,10 @@
[self addSubview:sub]; [self addSubview:sub];
} }
[_imdmIcon setHidden:TRUE]; [_imdmIcon setHidden:TRUE];
_unreadCountView.backgroundColor = VoipTheme.primary_color;
_unreadCountView.layer.cornerRadius = 10;
_unreadCountView.clipsToBounds = true;
_unreadCountLabel.textAlignment = NSTextAlignmentCenter;
return self; return self;
} }
@ -132,7 +137,8 @@
_chatContentLabel.frame = newFrame; _chatContentLabel.frame = newFrame;
} }
} else { } else {
NSString *text = [[FastAddressBook displayNameForAddress:linphone_chat_message_get_from_address(last_msg)] NSString *conferenceInfo = [ICSBubbleView getConferenceSummaryWithCmessage:last_msg];
NSString *text = conferenceInfo != nil ? conferenceInfo : [[FastAddressBook displayNameForAddress:linphone_chat_message_get_from_address(last_msg)]
stringByAppendingFormat:@" : %@", [UIChatBubbleTextCell TextMessageForChat:last_msg]]; stringByAppendingFormat:@" : %@", [UIChatBubbleTextCell TextMessageForChat:last_msg]];
// shorten long messages // shorten long messages
/*if ([text length] > 50) /*if ([text length] > 50)

View file

@ -21,9 +21,8 @@
#import "ChatConversationCreateView.h" #import "ChatConversationCreateView.h"
@interface UIChatCreateCollectionViewCell : UICollectionViewCell @interface UIChatCreateCollectionViewCell : UICollectionViewCell
@property (weak, nonatomic) IBOutlet UILabel *nameLabel; @property UILabel *nameLabel;
@property (strong, nonatomic) ChatConversationCreateView *controller; @property (strong, nonatomic) ChatConversationCreateView *controller;
@property (strong, nonatomic) NSString *uri; @property (strong, nonatomic) NSString *uri;
- (id)initWithName:(NSString *)identifier;
- (void)onDelete; - (void)onDelete;
@end @end

View file

@ -18,27 +18,31 @@
*/ */
#import "UIChatCreateCollectionViewCell.h" #import "UIChatCreateCollectionViewCell.h"
#import "linphoneapp-Swift.h"
@implementation UIChatCreateCollectionViewCell @implementation UIChatCreateCollectionViewCell
- (void)awakeFromNib {
[super awakeFromNib];
}
- (id)initWithName:(NSString *)identifier {
if (self != nil) {
NSArray *arrayOfViews =
[[NSBundle mainBundle] loadNibNamed:NSStringFromClass(self.class) owner:self options:nil];
if ([arrayOfViews count] >= 1) {
UIChatCreateCollectionViewCell *sub = ((UIChatCreateCollectionViewCell *)[arrayOfViews objectAtIndex:0]);
[self addSubview:sub];
_nameLabel = sub.nameLabel;
}
}
[_nameLabel setText:identifier];
- (id)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
self.contentView.translatesAutoresizingMaskIntoConstraints = false;
[SnapkitBridge matchParentDimensionsWithView:self.contentView topInset:10];
self.nameLabel = [[UILabel alloc] initWithFrame:CGRectZero];
self.nameLabel.numberOfLines = 1;
[self.contentView addSubview:self.nameLabel];
[SnapkitBridge matchParentDimensionsWithView:self.nameLabel leftInset:20];
[SnapkitBridge heightWithView:self heiht:50];
UIImageView *image = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"conference_delete"]];
image.contentMode = UIViewContentModeScaleAspectFit;
[self.contentView addSubview:image];
[SnapkitBridge squareWithView:image size:15];
[SnapkitBridge alignParentLeftWithView:image];
[SnapkitBridge centerYWithView:image];
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(onDelete)]; UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(onDelete)];
tap.numberOfTouchesRequired = 1; tap.numberOfTouchesRequired = 1;
[self addGestureRecognizer:tap]; [image addGestureRecognizer:tap];
image.userInteractionEnabled = true;
return self; return self;
} }
@ -60,4 +64,6 @@
[_controller.tableController.tableView reloadData]; [_controller.tableController.tableView reloadData];
_controller.nextButton.enabled = (_controller.tableController.contactsGroup.count > 0) || _controller.isForEditing; _controller.nextButton.enabled = (_controller.tableController.contactsGroup.count > 0) || _controller.isForEditing;
} }
@end @end

View file

@ -33,6 +33,7 @@ NS_ASSUME_NONNULL_BEGIN
@property (weak, nonatomic) IBOutlet UICollectionView *contentCollection; @property (weak, nonatomic) IBOutlet UICollectionView *contentCollection;
@property NSArray *dataContent; @property NSArray *dataContent;
@property (weak, nonatomic) IBOutlet UILabel *originalMessageGone; @property (weak, nonatomic) IBOutlet UILabel *originalMessageGone;
@property (weak, nonatomic) IBOutlet UIImageView *icsIcon;
-(void) configureForMessage:(LinphoneChatMessage *)message withDimissBlock:(void (^)(void))dismissBlock hideDismiss:(BOOL)hideDismiss withClickBlock:(void (^)(void))clickBlock; -(void) configureForMessage:(LinphoneChatMessage *)message withDimissBlock:(void (^)(void))dismissBlock hideDismiss:(BOOL)hideDismiss withClickBlock:(void (^)(void))clickBlock;
@end @end

View file

@ -40,6 +40,7 @@
-(void) viewDidLoad { -(void) viewDidLoad {
_contentCollection.dataSource = self; _contentCollection.dataSource = self;
[_icsIcon setImageNamed:@"voip_meeting_schedule" tintColor:VoipTheme.voip_dark_gray];
[_contentCollection registerClass:UICollectionViewCell.class forCellWithReuseIdentifier:@"dataContent"]; [_contentCollection registerClass:UICollectionViewCell.class forCellWithReuseIdentifier:@"dataContent"];
} }
@ -51,6 +52,7 @@
_contentCollection.hidden = true; _contentCollection.hidden = true;
_senderName.hidden = true; _senderName.hidden = true;
_originalMessageGone.hidden = false; _originalMessageGone.hidden = false;
_icsIcon.hidden = true;
return; return;
} }
if (hideDismiss) { if (hideDismiss) {
@ -60,9 +62,12 @@
_originalMessageGone.hidden = true; _originalMessageGone.hidden = true;
self.message = message; self.message = message;
self.dataContent = [self loadDataContent]; self.dataContent = [self loadDataContent];
BOOL isIcal = [ICSBubbleView isConferenceInvitationMessageWithCmessage:message];
_icsIcon.hidden = !isIcal;
NSString *sender = [FastAddressBook displayNameForAddress:linphone_chat_message_get_from_address(message)]; NSString *sender = [FastAddressBook displayNameForAddress:linphone_chat_message_get_from_address(message)];
_senderName.text = sender; _senderName.text = sender;
const char * text = linphone_chat_message_get_text_content(message); const char * text = isIcal ? [ICSBubbleView getSubjectFromContentWithCmessage:message].UTF8String : linphone_chat_message_get_text_content(message);
if (text && strlen(text) == 0) if (text && strlen(text) == 0)
text = nil; text = nil;
_textContent.text = text ? [NSString stringWithUTF8String:text] : @""; _textContent.text = text ? [NSString stringWithUTF8String:text] : @"";

View file

@ -91,8 +91,8 @@
#pragma mark - #pragma mark -
- (void)accessoryForCell:(UITableViewCell *)cell atPath:(NSIndexPath *)indexPath { - (void)accessoryForCell:(UITableViewCell *)cell atPath:(NSIndexPath *)indexPath {
cell.selectionStyle = UITableViewCellSelectionStyleGray;
if ([self isEditing]) { if ([self isEditing]) {
cell.selectionStyle = UITableViewCellSelectionStyleGray;
UIButton *checkBoxButton = [UIButton buttonWithType:UIButtonTypeCustom]; UIButton *checkBoxButton = [UIButton buttonWithType:UIButtonTypeCustom];
UIImage *image = nil; UIImage *image = nil;
if ([_selectedItems containsObject:indexPath]) { if ([_selectedItems containsObject:indexPath]) {

View file

@ -84,6 +84,7 @@
- (UIViewController *)getCurrentViewController; - (UIViewController *)getCurrentViewController;
- (UIInterfaceOrientation)currentOrientation; - (UIInterfaceOrientation)currentOrientation;
- (void)clearCache:(NSArray *)exclude; - (void)clearCache:(NSArray *)exclude;
- (void)removeEntryFromCache:(NSString *)key;
- (IBAction)onRightSwipe:(id)sender; - (IBAction)onRightSwipe:(id)sender;

View file

@ -305,6 +305,9 @@
return nil; return nil;
} }
- (void)removeEntryFromCache:(NSString *)key {
[viewControllerCache removeObjectForKey:key];
}
- (void)clearCache:(NSArray *)exclude { - (void)clearCache:(NSArray *)exclude {
@ -313,7 +316,7 @@
bool remove = true; bool remove = true;
/*ImagePickerView can be used as popover and we do NOT want to free it*/; /*ImagePickerView can be used as popover and we do NOT want to free it*/;
if ([key isEqualToString:ImagePickerView.compositeViewDescription.name] || [key isEqualToString:ActiveCallOrConferenceView.compositeViewDescription.name]) { if ([key isEqualToString:ImagePickerView.compositeViewDescription.name] || [key isEqualToString:SingleCallView.compositeViewDescription.name] || [key isEqualToString:ConferenceCallView.compositeViewDescription.name]) {
remove = false; remove = false;
} else if (exclude != nil) { } else if (exclude != nil) {
for (UICompositeViewDescription *description in exclude) { for (UICompositeViewDescription *description in exclude) {
@ -336,6 +339,9 @@
} }
- (IBAction)onRightSwipe:(id)sender { - (IBAction)onRightSwipe:(id)sender {
if (linphone_core_get_calls_nb(LC) > 0) {
return;
}
[self hideSideMenu:NO]; [self hideSideMenu:NO];
} }

View file

@ -17,11 +17,14 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include "UIRoundBorderedButton.h" #import <UIKit/UIKit.h>
#import "UICompositeView.h"
#import "UIRoundBorderedButton.h"
typedef void (^UIConfirmationBlock)(void); typedef void (^UIConfirmationBlock)(void);
@interface UIConfirmationDialog : UIViewController { @interface UIConfirmationDialog : UIViewController <UICompositeViewDelegate, UIGestureRecognizerDelegate, UITextFieldDelegate, UITableViewDelegate, UITableViewDataSource>{
UIConfirmationBlock onCancelCb; UIConfirmationBlock onCancelCb;
UIConfirmationBlock onConfirmCb; UIConfirmationBlock onConfirmCb;
} }
@ -49,7 +52,9 @@ typedef void (^UIConfirmationBlock)(void);
@property (weak, nonatomic) IBOutlet UIImageView *groupCallImage; @property (weak, nonatomic) IBOutlet UIImageView *groupCallImage;
@property(weak, nonatomic) IBOutlet UIRoundBorderedButton *confirmationButton; @property(weak, nonatomic) IBOutlet UIRoundBorderedButton *confirmationButton;
@property (weak, nonatomic) IBOutlet UIView *authView; @property (weak, nonatomic) IBOutlet UIView *authView;
@property (weak, nonatomic) IBOutlet UIImageView *backgroundColor;
@property(weak, nonatomic) IBOutlet UILabel *titleLabel; @property(weak, nonatomic) IBOutlet UILabel *titleLabel;
@property(weak, nonatomic) IBOutlet UIView *firstView;
@property (weak, nonatomic) IBOutlet UIButton *authButton; @property (weak, nonatomic) IBOutlet UIButton *authButton;
- (void)setSpecialColor; - (void)setSpecialColor;

View file

@ -19,7 +19,7 @@
#import "UIConfirmationDialog.h" #import "UIConfirmationDialog.h"
#import "PhoneMainView.h" #import "PhoneMainView.h"
#import "linphoneapp-Swift.h"" #import "linphoneapp-Swift.h"
@implementation UIConfirmationDialog @implementation UIConfirmationDialog
+ (UIConfirmationDialog *)initDialog:(NSString *)cancel + (UIConfirmationDialog *)initDialog:(NSString *)cancel
@ -33,6 +33,8 @@
dialog.view.frame = PhoneMainView.instance.mainViewController.view.frame; dialog.view.frame = PhoneMainView.instance.mainViewController.view.frame;
[controller.view addSubview:dialog.view]; [controller.view addSubview:dialog.view];
[controller addChildViewController:dialog]; [controller addChildViewController:dialog];
dialog.backgroundColor.layer.cornerRadius = 10;
dialog.backgroundColor.layer.masksToBounds = true;
dialog->onCancelCb = onCancel; dialog->onCancelCb = onCancel;
dialog->onConfirmCb = onConfirm; dialog->onConfirmCb = onConfirm;
@ -48,9 +50,21 @@
[[UIColor colorWithPatternImage:[UIImage imageNamed:@"color_A.png"]] CGColor]; [[UIColor colorWithPatternImage:[UIImage imageNamed:@"color_A.png"]] CGColor];
dialog.cancelButton.layer.borderColor = dialog.cancelButton.layer.borderColor =
[[UIColor colorWithPatternImage:[UIImage imageNamed:@"color_F.png"]] CGColor]; [[UIColor colorWithPatternImage:[UIImage imageNamed:@"color_F.png"]] CGColor];
if (linphone_core_get_post_quantum_available()) {
[dialog.securityImage setImage:[UIImage imageNamed:@"post_quantum_secure.png"]];
}
return dialog; return dialog;
} }
- (void)viewDidLoad {
[super viewDidLoad];
UITapGestureRecognizer *tapGestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self
action:@selector(onCancelClick:)];
tapGestureRecognizer.delegate = self;
[self.firstView addGestureRecognizer:tapGestureRecognizer];
}
+ (UIConfirmationDialog *)ShowWithMessage:(NSString *)message + (UIConfirmationDialog *)ShowWithMessage:(NSString *)message
cancelMessage:(NSString *)cancel cancelMessage:(NSString *)cancel
confirmMessage:(NSString *)confirm confirmMessage:(NSString *)confirm

View file

@ -50,28 +50,34 @@
normAddr = linphone_account_normalize_phone_number(account, normAddr = linphone_account_normalize_phone_number(account,
_addressLabel.text.UTF8String); _addressLabel.text.UTF8String);
} }
LinphoneAddress *addr = linphone_core_interpret_url(LC, normAddr); LinphoneAddress *addr = linphone_core_interpret_url_2(LC, normAddr, true);
_chatButton.enabled = _callButton.enabled = _encryptedChatButton.enabled = (addr != NULL); _chatButton.enabled = _callButton.enabled = _encryptedChatButton.enabled = (addr != NULL);
_chatButton.accessibilityLabel = _chatButton.accessibilityLabel =
[NSString stringWithFormat:NSLocalizedString(@"Chat with %@", nil), _addressLabel.text]; [NSString stringWithFormat:NSLocalizedString(@"Chat with %@", nil), _addressLabel.text];
_callButton.accessibilityLabel = [NSString stringWithFormat:NSLocalizedString(@"Call %@", nil), _addressLabel.text]; _callButton.accessibilityLabel = [NSString stringWithFormat:NSLocalizedString(@"Call %@", nil), _addressLabel.text];
// Test presence // Test presence
Contact *contact; Contact *contact = addr ? [FastAddressBook getContactWithAddress:(addr)] : NULL;
contact = addr ? [FastAddressBook getContactWithAddress:(addr)] : NULL; LinphoneFriend *contactFriend = NULL;
if (contact && contact.friend) {
contactFriend = contact.friend;
} else if (addr) {
contactFriend = linphone_core_find_friend(LC, addr);
}
ContactDetailsView *contactDetailsView = VIEW(ContactDetailsView);
_linphoneImage.hidden = TRUE; _linphoneImage.hidden = TRUE;
if (contact) { if (contactFriend) {
const LinphonePresenceModel *model = contact.friend ? linphone_friend_get_presence_model_for_uri_or_tel(contact.friend, _addressLabel.text.UTF8String) : NULL; const LinphonePresenceModel *model = contactFriend ? linphone_friend_get_presence_model_for_uri_or_tel(contactFriend, _addressLabel.text.UTF8String) : NULL;
self.linphoneImage.hidden = [LinphoneManager.instance lpConfigBoolForKey:@"hide_linphone_contacts" inSection:@"app"] || self.linphoneImage.hidden = [LinphoneManager.instance lpConfigBoolForKey:@"hide_linphone_contacts" inSection:@"app"] ||
!((model && linphone_presence_model_get_basic_status(model) == LinphonePresenceBasicStatusOpen) || !((model && linphone_presence_model_get_basic_status(model) == LinphonePresenceBasicStatusOpen) ||
(account && !linphone_account_is_phone_number(account, (account && !linphone_account_is_phone_number(account,
_addressLabel.text.UTF8String) && _addressLabel.text.UTF8String) &&
[FastAddressBook isSipURIValid:_addressLabel.text])); [FastAddressBook isSipURIValid:_addressLabel.text]));
ContactDetailsView *contactDetailsView = VIEW(ContactDetailsView); self.inviteButton.hidden = !ENABLE_SMS_INVITE || [[contactDetailsView.contact sipAddresses] count] > 0 || !self.linphoneImage.hidden;
self.inviteButton.hidden = !ENABLE_SMS_INVITE || [[contactDetailsView.contact sipAddresses] count] > 0 || !self.linphoneImage.hidden;
[self shouldHideEncryptedChatView:account && linphone_account_params_get_conference_factory_uri(linphone_account_get_params(account)) && model && linphone_presence_model_has_capability(model, LinphoneFriendCapabilityLimeX3dh)]; [self shouldHideEncryptedChatView:account && linphone_account_params_get_conference_factory_uri(linphone_account_get_params(account)) && model && linphone_presence_model_has_capability(model, LinphoneFriendCapabilityLimeX3dh)];
_chatButton.hidden = [LinphoneManager.instance lpConfigBoolForKey:@"force_lime_chat_rooms"];
} }
if (addr) { if (addr) {
@ -103,7 +109,7 @@
normAddr = linphone_account_normalize_phone_number(account, normAddr = linphone_account_normalize_phone_number(account,
_addressLabel.text.UTF8String); _addressLabel.text.UTF8String);
} }
LinphoneAddress *addr = linphone_core_interpret_url(LC, normAddr); LinphoneAddress *addr = linphone_core_interpret_url_2(LC, normAddr, true);
// Test presence // Test presence
Contact *contact = [FastAddressBook getContactWithAddress:(addr)]; Contact *contact = [FastAddressBook getContactWithAddress:(addr)];

View file

@ -67,7 +67,6 @@
if (addressField && (!dtmf || !linphone_core_in_call(LC))) { if (addressField && (!dtmf || !linphone_core_in_call(LC))) {
NSString *newAddress = [NSString stringWithFormat:@"%@%c", addressField.text, digit]; NSString *newAddress = [NSString stringWithFormat:@"%@%c", addressField.text, digit];
[addressField setText:newAddress]; [addressField setText:newAddress];
linphone_core_play_dtmf(LC, digit, -1);
} else { } else {
linphone_call_send_dtmf(linphone_core_get_current_call(LC), digit); linphone_call_send_dtmf(linphone_core_get_current_call(LC), digit);
linphone_core_play_dtmf(LC, digit, 100); linphone_core_play_dtmf(LC, digit, 100);

View file

@ -91,7 +91,7 @@
// Set up the cell... // Set up the cell...
if (linphone_call_log_was_conference(callLog)) { if (linphone_call_log_was_conference(callLog)) {
const char *subject = linphone_conference_info_get_subject(linphone_call_log_get_conference_info(callLog)); const char *subject = linphone_conference_info_get_subject(linphone_call_log_get_conference_info(callLog));
displayNameLabel.text = [NSString stringWithFormat:@"%s",subject]; displayNameLabel.text = [NSString stringWithUTF8String:subject];
[_avatarImage setImage:[UIImage imageNamed:@"voip_multiple_contacts_avatar"]]; [_avatarImage setImage:[UIImage imageNamed:@"voip_multiple_contacts_avatar"]];
_stateImage.hidden = true; _stateImage.hidden = true;
} else { } else {

View file

@ -25,8 +25,6 @@
@property (weak, nonatomic) IBOutlet UILabel *nameLabel; @property (weak, nonatomic) IBOutlet UILabel *nameLabel;
@property (strong, nonatomic) IBOutlet UIToolbar *toolbar; @property (strong, nonatomic) IBOutlet UIToolbar *toolbar;
@property (weak, nonatomic) IBOutlet UIBarButtonItem *shareButton; @property (weak, nonatomic) IBOutlet UIBarButtonItem *shareButton;
@property(nonatomic, assign) __block NSString *recording; @property(nonatomic, assign) __block NSString *recording;
- (id)initWithIdentifier:(NSString*)identifier; - (id)initWithIdentifier:(NSString*)identifier;

View file

@ -64,7 +64,8 @@ static UILinphoneAudioPlayer *player;
NSArray *parsedRecording = [LinphoneUtils parseRecordingName:_recording]; NSArray *parsedRecording = [LinphoneUtils parseRecordingName:_recording];
NSDateFormatter *dateFormat = [[NSDateFormatter alloc] init]; NSDateFormatter *dateFormat = [[NSDateFormatter alloc] init];
[dateFormat setDateFormat:@"HH:mm:ss"]; [dateFormat setDateFormat:@"HH:mm:ss"];
_nameLabel.text = [[[parsedRecording objectAtIndex:0] stringByAppendingString:@" @ "] stringByAppendingString:[dateFormat stringFromDate:[parsedRecording objectAtIndex:1]]]; NSString *b = [[parsedRecording objectAtIndex:0] containsString:@"conf-id"] ? NSLocalizedString(@"Meeting", nil) : [parsedRecording objectAtIndex:0];
_nameLabel.text = [[b stringByAppendingString:@" @ "] stringByAppendingString:[dateFormat stringFromDate:[parsedRecording objectAtIndex:1]]];
} }
} }
@ -89,7 +90,8 @@ static UILinphoneAudioPlayer *player;
} }
- (void)setHighlighted:(BOOL)highlighted animated:(BOOL)animated { - (void)setHighlighted:(BOOL)highlighted animated:(BOOL)animated {
self.selectionStyle = UITableViewCellSelectionStyleNone; if (!VIEW(RecordingsListView).tableController.isEditing)
self.selectionStyle = UITableViewCellSelectionStyleNone;
} }
- (void)updateFrame { - (void)updateFrame {
@ -104,10 +106,11 @@ static UILinphoneAudioPlayer *player;
-(void)setSelected:(BOOL)selected animated:(BOOL)animated{ -(void)setSelected:(BOOL)selected animated:(BOOL)animated{
[super setSelected:selected animated:animated]; [super setSelected:selected animated:animated];
_toolbar.hidden = !selected;
if (!selected) { if (!selected || (selected && VIEW(RecordingsListView).tableController.isEditing)) {
return; _toolbar.hidden = true;
} return;
}
if (player && [player isCreated]) { if (player && [player isCreated]) {
[player close]; [player close];
} }
@ -121,6 +124,7 @@ static UILinphoneAudioPlayer *player;
player.view.frame = _playerView.frame; player.view.frame = _playerView.frame;
player.view.bounds = _playerView.bounds; player.view.bounds = _playerView.bounds;
[player open]; [player open];
_toolbar.hidden = false;
} }
- (void)onShareButtonPressed { - (void)onShareButtonPressed {

View file

@ -34,6 +34,7 @@ import linphonesw
NotificationCenter.default.post(name: Notification.Name(kLinphoneMagicSearchFinished), object: self) NotificationCenter.default.post(name: Notification.Name(kLinphoneMagicSearchFinished), object: self)
}, onLdapHaveMoreResults: { (magicSearch: MagicSearch, ldap: Ldap) in }, onLdapHaveMoreResults: { (magicSearch: MagicSearch, ldap: Ldap) in
Log.directLog(BCTBX_LOG_MESSAGE, text: "Ldap have more result") Log.directLog(BCTBX_LOG_MESSAGE, text: "Ldap have more result")
NotificationCenter.default.post(name: Notification.Name(kLinphoneMagicSearchMoreAvailable), object: self)
}) })
magicSearch.addDelegate(delegate: magicSearchDelegate!) magicSearch.addDelegate(delegate: magicSearchDelegate!)
@ -47,17 +48,26 @@ import linphonesw
return theMagicSearchSingleton! return theMagicSearchSingleton!
} }
@objc static func destroyInstance() {
theMagicSearchSingleton = nil
}
func getContactFromAddr(addr: Address) -> Contact? { func getContactFromAddr(addr: Address) -> Contact? {
return LinphoneManager.instance().fastAddressBook.addressBookMap.object(forKey: addr.asStringUriOnly() as Any) as? Contact return LinphoneManager.instance().fastAddressBook.addressBookMap.object(forKey: addr.asStringUriOnly() as Any) as? Contact
} }
func getContactFromPhoneNb(phoneNb: String) -> Contact? { func getContactFromPhoneNb(phoneNb: String) -> Contact? {
let contactKey = FastAddressBook.localizedLabel(FastAddressBook.normalizeSipURI( lc?.defaultAccount?.normalizePhoneNumber(username: phoneNb) ?? phoneNb)) let contactKey = FastAddressBook.localizedLabel(FastAddressBook.normalizeSipURI(lc?.defaultAccount?.normalizePhoneNumber(username: phoneNb) ?? phoneNb, use_prefix: true))
return LinphoneManager.instance().fastAddressBook.addressBookMap.object(forKey: contactKey as Any) as? Contact return LinphoneManager.instance().fastAddressBook.addressBookMap.object(forKey: contactKey as Any) as? Contact
} }
func searchAndAddMatchingContact(searchResult: SearchResult) -> Contact? { func searchAndAddMatchingContact(searchResult: SearchResult) -> Contact? {
if let friend = searchResult.friend { if let friend = searchResult.friend {
if (searchResult.sourceFlags == MagicSearchSource.LdapServers.rawValue), let newContact = Contact(friend: friend.getCobject) {
// Contact comes from LDAP, creating a new one
newContact.createdFromLdapOrProvisioning = true
return newContact
}
if let addr = friend.address, let foundContact = getContactFromAddr(addr: addr) { if let addr = friend.address, let foundContact = getContactFromAddr(addr: addr) {
return foundContact return foundContact
} }
@ -66,11 +76,6 @@ import linphonesw
return foundContact return foundContact
} }
} }
// No contacts found (searchResult likely comes from LDAP), creating a new one
if let newContact = Contact(friend: friend.getCobject) {
newContact.createdFromLdap = true
return newContact
}
} }
if let addr = searchResult.address, let foundContact = getContactFromAddr(addr: addr) { if let addr = searchResult.address, let foundContact = getContactFromAddr(addr: addr) {
@ -81,6 +86,11 @@ import linphonesw
return foundContact return foundContact
} }
// Friend comes from provisioning
if let addr = searchResult.address, let friend = searchResult.friend, let newContact = Contact(friend: friend.getCobject) {
newContact.createdFromLdapOrProvisioning = true
return newContact
}
return nil return nil
} }
@ -100,13 +110,9 @@ import linphonesw
@objc func getLastSearchContacts() -> [Contact] { @objc func getLastSearchContacts() -> [Contact] {
if (needUpdateLastSearchContacts) { if (needUpdateLastSearchContacts) {
lastSearchContacts = [] lastSearchContacts = []
var addedContactNames : [String] = []
for res in magicSearch.lastSearch { for res in magicSearch.lastSearch {
if let contact = searchAndAddMatchingContact(searchResult: res) { if let contact = searchAndAddMatchingContact(searchResult: res) {
if (!addedContactNames.contains(contact.displayName)) { lastSearchContacts.append(contact)
addedContactNames.append(contact.displayName)
lastSearchContacts.append(contact)
}
} }
} }
needUpdateLastSearchContacts = false needUpdateLastSearchContacts = false

View file

@ -123,3 +123,4 @@
@end @end
void main_view_chat_room_state_changed(LinphoneChatRoom *cr, LinphoneChatRoomState newState); void main_view_chat_room_state_changed(LinphoneChatRoom *cr, LinphoneChatRoomState newState);
void main_view_chat_room_conference_joined(LinphoneChatRoom *cr, const LinphoneEventLog *event_log);

View file

@ -374,7 +374,6 @@ static RootViewManager *rootViewManagerInstance = nil;
} }
break; break;
} }
case LinphoneCallOutgoingInit:
case LinphoneCallOutgoingEarlyMedia: case LinphoneCallOutgoingEarlyMedia:
case LinphoneCallOutgoingProgress: case LinphoneCallOutgoingProgress:
case LinphoneCallOutgoingRinging: { case LinphoneCallOutgoingRinging: {
@ -386,7 +385,7 @@ static RootViewManager *rootViewManagerInstance = nil;
} }
break; break;
} }
case LinphoneCallPausedByRemote: case LinphoneCallPausedByRemote:break;
case LinphoneCallConnected: { case LinphoneCallConnected: {
if (![LinphoneManager.instance isCTCallCenterExist]) { if (![LinphoneManager.instance isCTCallCenterExist]) {
/*only register CT call center CB for connected call*/ /*only register CT call center CB for connected call*/
@ -418,6 +417,7 @@ static RootViewManager *rootViewManagerInstance = nil;
} }
case LinphoneCallUpdating: case LinphoneCallUpdating:
break; break;
} }
if (state == LinphoneCallEnd || state == LinphoneCallError || floor(NSFoundationVersionNumber) <= NSFoundationVersionNumber_iOS_9_x_Max) if (state == LinphoneCallEnd || state == LinphoneCallError || floor(NSFoundationVersionNumber) <= NSFoundationVersionNumber_iOS_9_x_Max)
[self updateApplicationBadgeNumber]; [self updateApplicationBadgeNumber];
@ -453,9 +453,19 @@ static RootViewManager *rootViewManagerInstance = nil;
LinphoneManager *lm = LinphoneManager.instance; LinphoneManager *lm = LinphoneManager.instance;
LOGI(@"%s", linphone_global_state_to_string(linphone_core_get_global_state(LC))); LOGI(@"%s", linphone_global_state_to_string(linphone_core_get_global_state(LC)));
NSString* groupName = [NSString stringWithFormat:@"group.%@.linphoneExtension",[[NSBundle mainBundle] bundleIdentifier]];
NSUserDefaults *defaults = [[NSUserDefaults alloc] initWithSuiteName:groupName];
NSDictionary *dict = [defaults valueForKey:@"photoData"];
NSDictionary *dictFile = [defaults valueForKey:@"icloudData"];
NSDictionary *dictUrl = [defaults valueForKey:@"url"];
// If we've been started by a remote push notification, // If we've been started by a remote push notification,
// we'll already be on the corresponding chat conversation view, no need to go anywhere else // we'll already be on the corresponding chat conversation view, no need to go anywhere else
if (![[self currentView].name isEqualToString:@"ChatConversationView"]) { if (dict||dictFile||dictUrl){
[self changeCurrentView:ChatsListView.compositeViewDescription];
}else if (![[self currentView].name isEqualToString:@"ChatConversationView"]) {
if (linphone_core_get_global_state(LC) != LinphoneGlobalOn) { if (linphone_core_get_global_state(LC) != LinphoneGlobalOn) {
[self changeCurrentView:DialerView.compositeViewDescription]; [self changeCurrentView:DialerView.compositeViewDescription];
@ -607,7 +617,8 @@ static RootViewManager *rootViewManagerInstance = nil;
} }
[self _changeCurrentView:viewStack.lastObject ?: DialerView.compositeViewDescription [self _changeCurrentView:viewStack.lastObject ?: DialerView.compositeViewDescription
transition:[PhoneMainView getBackwardTransition] transition:[PhoneMainView getBackwardTransition]
animated:ANIMATED]; animated:ANIMATED
addViewToStack:FALSE];
return [mainViewController getCurrentViewController]; return [mainViewController getCurrentViewController];
} }
@ -621,18 +632,19 @@ static RootViewManager *rootViewManagerInstance = nil;
- (void)changeCurrentView:(UICompositeViewDescription *)view { - (void)changeCurrentView:(UICompositeViewDescription *)view {
[self _changeCurrentView:view transition:nil animated:ANIMATED]; [self _changeCurrentView:view transition:nil animated:ANIMATED addViewToStack:TRUE];
} }
- (UIViewController *)_changeCurrentView:(UICompositeViewDescription *)view - (UIViewController *)_changeCurrentView:(UICompositeViewDescription *)view
transition:(CATransition *)transition transition:(CATransition *)transition
animated:(BOOL)animated { animated:(BOOL)animated
addViewToStack:(BOOL)addViewToStack {
PhoneMainView *vc = [[RootViewManager instance] setViewControllerForDescription:view]; PhoneMainView *vc = [[RootViewManager instance] setViewControllerForDescription:view];
if (![view equal:vc.currentView] || vc != self) { if (![view equal:vc.currentView] || vc != self) {
LOGI(@"Change current view to %@", view.name); LOGI(@"Change current view to %@", view.name);
[self setPreviousViewName:vc.currentView.name]; [self setPreviousViewName:vc.currentView.name];
NSMutableArray *viewStack = [RootViewManager instance].viewDescriptionStack; NSMutableArray *viewStack = [RootViewManager instance].viewDescriptionStack;
[viewStack addObject:view]; if (addViewToStack) [viewStack addObject:view];
if (animated && transition == nil) if (animated && transition == nil)
transition = [PhoneMainView getTransition:vc.currentView new:view]; transition = [PhoneMainView getTransition:vc.currentView new:view];
[vc.mainViewController setViewTransition:(animated ? transition : nil)]; [vc.mainViewController setViewTransition:(animated ? transition : nil)];
@ -653,7 +665,8 @@ static RootViewManager *rootViewManagerInstance = nil;
while (viewStack.count > 0 && ![[viewStack lastObject] equal:view]) { while (viewStack.count > 0 && ![[viewStack lastObject] equal:view]) {
[viewStack removeLastObject]; [viewStack removeLastObject];
} }
return [self _changeCurrentView:view transition:[PhoneMainView getBackwardTransition] animated:ANIMATED]; BOOL addView = (viewStack.count == 0); // if we couldn't find the view in the stack, we need to add it
return [self _changeCurrentView:view transition:[PhoneMainView getBackwardTransition] animated:ANIMATED addViewToStack:addView];
} }
- (void) setPreviousViewName:(NSString*)previous{ - (void) setPreviousViewName:(NSString*)previous{
@ -884,6 +897,7 @@ static RootViewManager *rootViewManagerInstance = nil;
LinphoneChatRoomCbs *cbs = linphone_factory_create_chat_room_cbs(linphone_factory_get()); LinphoneChatRoomCbs *cbs = linphone_factory_create_chat_room_cbs(linphone_factory_get());
linphone_chat_room_cbs_set_state_changed(cbs, main_view_chat_room_state_changed); linphone_chat_room_cbs_set_state_changed(cbs, main_view_chat_room_state_changed);
linphone_chat_room_cbs_set_conference_joined(cbs, main_view_chat_room_conference_joined);
linphone_chat_room_add_callbacks(room, cbs); linphone_chat_room_add_callbacks(room, cbs);
return room; return room;
@ -901,31 +915,34 @@ static RootViewManager *rootViewManagerInstance = nil;
[view clearMessageView]; [view clearMessageView];
view.chatRoom = cr; view.chatRoom = cr;
view.peerAddress = linphone_address_as_string(linphone_chat_room_get_peer_address(cr)); view.peerAddress = linphone_address_as_string(linphone_chat_room_get_peer_address(cr));
view.localAddress = linphone_address_as_string(linphone_chat_room_get_local_address(cr));
self.currentRoom = view.chatRoom; self.currentRoom = view.chatRoom;
if (PhoneMainView.instance.currentView == view.compositeViewDescription) if (PhoneMainView.instance.currentView == view.compositeViewDescription)
[view configureForRoom:FALSE]; [view configureForRoom:FALSE];
else else
[PhoneMainView.instance changeCurrentView:view.compositeViewDescription]; [PhoneMainView.instance changeCurrentView:view.compositeViewDescription];
} }
void main_view_chat_room_conference_joined(LinphoneChatRoom *cr, const LinphoneEventLog *event_log) {
PhoneMainView *view = PhoneMainView.instance;
LOGI(@"Chat room [%p] conference joined.", cr);
linphone_chat_room_remove_callbacks(cr, linphone_chat_room_get_current_callbacks(cr));
[view goToChatRoom:cr];
if (!IPAD)
return;
if (PhoneMainView.instance.currentView != ChatsListView.compositeViewDescription && PhoneMainView.instance.currentView != ChatConversationView.compositeViewDescription)
return;
ChatsListView *mainView = VIEW(ChatsListView);
[mainView.tableController loadData];
[mainView.tableController selectFirstRow];
}
void main_view_chat_room_state_changed(LinphoneChatRoom *cr, LinphoneChatRoomState newState) { void main_view_chat_room_state_changed(LinphoneChatRoom *cr, LinphoneChatRoomState newState) {
PhoneMainView *view = PhoneMainView.instance; PhoneMainView *view = PhoneMainView.instance;
switch (newState) { switch (newState) {
case LinphoneChatRoomStateCreated: {
LOGI(@"Chat room [%p] created on server.", cr);
linphone_chat_room_remove_callbacks(cr, linphone_chat_room_get_current_callbacks(cr));
[view goToChatRoom:cr];
if (!IPAD)
break;
if (PhoneMainView.instance.currentView != ChatsListView.compositeViewDescription && PhoneMainView.instance.currentView != ChatConversationView.compositeViewDescription)
break;
ChatsListView *mainView = VIEW(ChatsListView);
[mainView.tableController loadData];
[mainView.tableController selectFirstRow];
break;
}
case LinphoneChatRoomStateCreationFailed: case LinphoneChatRoomStateCreationFailed:
LOGE(@"Chat room [%p] could not be created on server.", cr); LOGE(@"Chat room [%p] could not be created on server.", cr);
linphone_chat_room_remove_callbacks(cr, linphone_chat_room_get_current_callbacks(cr)); linphone_chat_room_remove_callbacks(cr, linphone_chat_room_get_current_callbacks(cr));
@ -958,4 +975,8 @@ void main_view_chat_room_state_changed(LinphoneChatRoom *cr, LinphoneChatRoomSta
} }
} }
-(void)traitCollectionDidChange:(UITraitCollection *)previousTraitCollection {
[UIDeviceBridge notifyDisplayModeSwitch];
}
@end @end

View file

@ -147,8 +147,6 @@
NSString *recordingPath = subAr[indexPath.row]; NSString *recordingPath = subAr[indexPath.row];
[cell setRecording:recordingPath]; [cell setRecording:recordingPath];
[super accessoryForCell:cell atPath:indexPath]; [super accessoryForCell:cell atPath:indexPath];
//accessoryForCell set it to gray but we don't want it
cell.selectionStyle = UITableViewCellSelectionStyleNone;
[cell updateFrame]; [cell updateFrame];
cell.contentView.userInteractionEnabled = false; cell.contentView.userInteractionEnabled = false;
return cell; return cell;

View file

@ -22,12 +22,12 @@
#import "LinphoneAppDelegate.h" #import "LinphoneAppDelegate.h"
#import "PhoneMainView.h" #import "PhoneMainView.h"
#import "Utils.h" #import "Utils.h"
#import "linphoneapp-Swift.h"
#import "DCRoundSwitch.h" #import "DCRoundSwitch.h"
#import "IASKSpecifierValuesViewController.h" #import "IASKSpecifierValuesViewController.h"
#import "IASKPSTextFieldSpecifierViewCell.h" #import "IASKPSTextFieldSpecifierViewCell.h"
#import "IASKPSTitleValueSpecifierViewCell.h"
#import "IASKSpecifier.h" #import "IASKSpecifier.h"
#import "IASKTextField.h" #import "IASKTextField.h"
#include "linphone/lpconfig.h" #include "linphone/lpconfig.h"
@ -247,7 +247,7 @@
[field setTextColor:LINPHONE_MAIN_COLOR]; [field setTextColor:LINPHONE_MAIN_COLOR];
} }
if ([cell isKindOfClass:[IASKPSTitleValueSpecifierViewCell class]]) { if ([cell isKindOfClass:[UITableViewCell class]]) {
cell.detailTextLabel.textColor = [UIColor grayColor]; cell.detailTextLabel.textColor = [UIColor grayColor];
} else { } else {
cell.detailTextLabel.textColor = LINPHONE_MAIN_COLOR; cell.detailTextLabel.textColor = LINPHONE_MAIN_COLOR;
@ -445,8 +445,11 @@ void update_hash_cbs(LinphoneAccountCreator *creator, LinphoneAccountCreatorStat
linphone_account_creator_set_password(creator, _tmpPwd.UTF8String); linphone_account_creator_set_password(creator, _tmpPwd.UTF8String);
[settingsStore setObject:_tmpPwd forKey:@"account_mandatory_password_preference"]; [settingsStore setObject:_tmpPwd forKey:@"account_mandatory_password_preference"];
MSList *accountList = [LinphoneManager.instance createAccountsNotHiddenList];
LinphoneAccount *account = bctbx_list_nth_data(linphone_core_get_account_list(LC), LinphoneAccount *account = bctbx_list_nth_data(linphone_core_get_account_list(LC),
[settingsStore integerForKey:@"current_proxy_config_preference"]); [settingsStore integerForKey:@"current_proxy_config_preference"]);
bctbx_free(accountList);
if (account != NULL) { if (account != NULL) {
const LinphoneAuthInfo *auth = linphone_account_find_auth_info(account); const LinphoneAuthInfo *auth = linphone_account_find_auth_info(account);
if (auth) { if (auth) {
@ -521,11 +524,50 @@ void update_hash_cbs(LinphoneAccountCreator *creator, LinphoneAccountCreatorStat
[keys addObject:@"download_bandwidth_preference"]; [keys addObject:@"download_bandwidth_preference"];
} else if ([@"auto_download_mode" compare:notif.object] == NSOrderedSame) { } else if ([@"auto_download_mode" compare:notif.object] == NSOrderedSame) {
NSString *download_mode = [notif.userInfo objectForKey:@"auto_download_mode"]; NSString *download_mode = [notif.userInfo objectForKey:@"auto_download_mode"];
removeFromHiddenKeys = [download_mode isEqualToString:@"Customize"]; if([download_mode isEqualToString:@"Customize"]){
if (removeFromHiddenKeys) [LinphoneManager.instance lpConfigSetBool:FALSE forKey:@"auto_download_mode_is_never"];
[LinphoneManager.instance lpConfigSetInt:10000000 forKey:@"auto_download_incoming_files_max_size"]; removeFromHiddenKeys = [download_mode isEqualToString:@"Customize"];
[keys addObject:@"auto_download_incoming_files_max_size"]; [LinphoneManager.instance lpConfigSetInt:10000000 forKey:@"auto_download_incoming_files_max_size"];
} [keys addObject:@"auto_download_incoming_files_max_size"];
[hiddenKeys addObject:@"auto_write_to_gallery_mode"];
} else if([download_mode isEqualToString:@"Never"]){
[LinphoneManager.instance lpConfigSetBool:TRUE forKey:@"auto_download_mode_is_never"];
removeFromHiddenKeys = [download_mode isEqualToString:@"Never"];
if(![LinphoneManager.instance lpConfigBoolForKey:@"vfs_enabled_mode"])
[keys addObject:@"auto_write_to_gallery_mode"];
else
[hiddenKeys addObject:@"auto_write_to_gallery_mode"];
[hiddenKeys addObject:@"auto_download_incoming_files_max_size"];
} else {
[LinphoneManager.instance lpConfigSetBool:FALSE forKey:@"auto_download_mode_is_never"];
[hiddenKeys addObject:@"auto_write_to_gallery_mode"];
[hiddenKeys addObject:@"auto_download_incoming_files_max_size"];
}
}else if ([@"vfs_enabled_mode" compare:notif.object] == NSOrderedSame) {
removeFromHiddenKeys = [[notif.userInfo objectForKey:@"vfs_enabled_mode"] boolValue];
if(removeFromHiddenKeys){
[LinphoneManager.instance lpConfigSetBool:TRUE forKey:@"vfs_enabled_mode"];
[hiddenKeys addObject:@"auto_write_to_gallery_mode"];
[hiddenKeys addObject:@"vfs_enabled_mode"];
[keys addObject:@"vfs_enabled"];
}else{
if(![LinphoneManager.instance lpConfigBoolForKey:@"auto_download_mode_is_never"]){
[hiddenKeys addObject:@"auto_write_to_gallery_mode"];
}
}
}
else if ([@"auto_write_to_gallery_mode" compare:notif.object] == NSOrderedSame) {
removeFromHiddenKeys = ![[notif.userInfo objectForKey:@"auto_write_to_gallery_mode"] boolValue];
if(![LinphoneManager.instance lpConfigBoolForKey:@"vfs_enabled_mode"]){
if(!removeFromHiddenKeys){
[LinphoneManager.instance lpConfigSetBool:TRUE forKey:@"auto_write_to_gallery_mode"];
[hiddenKeys addObject:@"auto_download_mode"];
}else{
[LinphoneManager.instance lpConfigSetBool:FALSE forKey:@"auto_write_to_gallery_mode"];
[keys addObject:@"auto_download_mode"];
}
}
}
for (NSString *key in keys) { for (NSString *key in keys) {
if (removeFromHiddenKeys) if (removeFromHiddenKeys)
@ -552,11 +594,12 @@ void update_hash_cbs(LinphoneAccountCreator *creator, LinphoneAccountCreatorStat
return [[IASKSpecifier alloc] initWithSpecifier:dict]; return [[IASKSpecifier alloc] initWithSpecifier:dict];
} }
} else { } else {
if ([[specifier key] isEqualToString:@"media_encryption_preference"]) { BOOL pq_available = linphone_core_get_post_quantum_available();
if ([[specifier key] isEqualToString:pq_available ? @"media_encryption_preference_pq_enabled" : @"media_encryption_preference"]) {
NSMutableDictionary *dict = [NSMutableDictionary dictionaryWithDictionary:[specifier specifierDict]]; NSMutableDictionary *dict = [NSMutableDictionary dictionaryWithDictionary:[specifier specifierDict]];
if (!linphone_core_media_encryption_supported(LC, LinphoneMediaEncryptionZRTP)) { if (!linphone_core_media_encryption_supported(LC, LinphoneMediaEncryptionZRTP)) {
NSMutableArray *titles = [NSMutableArray arrayWithArray:[dict objectForKey:@"Titles"]]; NSMutableArray *titles = [NSMutableArray arrayWithArray:[dict objectForKey:@"Titles"]];
[titles removeObject:@"ZRTP"]; [titles removeObject:pq_available ? @"ZRTP" : @"ZRTP Post Quantum"];
[dict setObject:titles forKey:@"Titles"]; [dict setObject:titles forKey:@"Titles"];
NSMutableArray *values = [NSMutableArray arrayWithArray:[dict objectForKey:@"Values"]]; NSMutableArray *values = [NSMutableArray arrayWithArray:[dict objectForKey:@"Values"]];
[values removeObject:@"ZRTP"]; [values removeObject:@"ZRTP"];
@ -583,7 +626,7 @@ void update_hash_cbs(LinphoneAccountCreator *creator, LinphoneAccountCreatorStat
} }
if ([specifier.key hasPrefix:@"menu_account_"]) { if ([specifier.key hasPrefix:@"menu_account_"]) {
const bctbx_list_t *accounts = linphone_core_get_account_list(LC); MSList *accounts = [LinphoneManager.instance createAccountsNotHiddenList];
int index = [specifier.key substringFromIndex:@"menu_account_".length].intValue - 1; int index = [specifier.key substringFromIndex:@"menu_account_".length].intValue - 1;
if (index < bctbx_list_size(accounts)) { if (index < bctbx_list_size(accounts)) {
LinphoneAccount *account = (LinphoneAccount *)bctbx_list_nth_data(accounts, index); LinphoneAccount *account = (LinphoneAccount *)bctbx_list_nth_data(accounts, index);
@ -591,6 +634,7 @@ void update_hash_cbs(LinphoneAccountCreator *creator, LinphoneAccountCreatorStat
stringWithUTF8String:linphone_address_get_username(linphone_account_params_get_identity_address(linphone_account_get_params(account)))]; stringWithUTF8String:linphone_address_get_username(linphone_account_params_get_identity_address(linphone_account_get_params(account)))];
[specifier.specifierDict setValue:name forKey:kIASKTitle]; [specifier.specifierDict setValue:name forKey:kIASKTitle];
} }
bctbx_free(accounts);
} }
if ([specifier.key hasPrefix:@"ldap_"]) { if ([specifier.key hasPrefix:@"ldap_"]) {
@ -625,18 +669,25 @@ void update_hash_cbs(LinphoneAccountCreator *creator, LinphoneAccountCreatorStat
- (NSSet *)findHiddenKeys { - (NSSet *)findHiddenKeys {
LinphoneManager *lm = LinphoneManager.instance; LinphoneManager *lm = LinphoneManager.instance;
NSMutableSet *hiddenKeys = [NSMutableSet set]; NSMutableSet *hiddenKeys = [NSMutableSet set];
const MSList *accounts = linphone_core_get_account_list(LC); MSList *accounts = [LinphoneManager.instance createAccountsNotHiddenList];
for (size_t i = bctbx_list_size(accounts) + 1; i <= 5; i++) { for (size_t i = bctbx_list_size(accounts) + 1; i <= 5; i++) {
[hiddenKeys addObject:[NSString stringWithFormat:@"menu_account_%lu", i]]; [hiddenKeys addObject:[NSString stringWithFormat:@"menu_account_%lu", i]];
} }
bctbx_free(accounts);
const MSList *ldaps = linphone_core_get_ldap_list(LC); const MSList *ldaps = linphone_core_get_ldap_list(LC);
for (size_t i = bctbx_list_size(ldaps) + 1; i <= 5; i++) { for (size_t i = bctbx_list_size(ldaps) + 1; i <= 5; i++) {
[hiddenKeys addObject:[NSString stringWithFormat:@"ldap_%lu", i]]; [hiddenKeys addObject:[NSString stringWithFormat:@"ldap_%lu", i]];
} }
if (!linphone_core_sip_transport_supported(LC, LinphoneTransportTls)) { if (!linphone_core_sip_transport_supported(LC, LinphoneTransportTls)) {
[hiddenKeys addObject:@"media_encryption_preference"]; [hiddenKeys addObject:@"media_encryption_preference"];
[hiddenKeys addObject:@"media_encryption_preference_pq_enabled"];
} else {
if (linphone_core_get_post_quantum_available()) {
[hiddenKeys addObject:@"media_encryption_preference"];
} else {
[hiddenKeys addObject:@"media_encryption_preference_pq_enabled"];
}
} }
if (!linphone_core_ldap_available(LC)) { if (!linphone_core_ldap_available(LC)) {
@ -762,6 +813,21 @@ void update_hash_cbs(LinphoneAccountCreator *creator, LinphoneAccountCreatorStat
[hiddenKeys addObject:@"auto_download_incoming_files_max_size"]; [hiddenKeys addObject:@"auto_download_incoming_files_max_size"];
} }
if (![[lm lpConfigStringForKey:@"auto_download_mode"] isEqualToString:@"Never"]) {
[hiddenKeys addObject:@"auto_write_to_gallery_mode"];
}
if ([lm lpConfigBoolForKey:@"vfs_enabled_mode"]) {
[hiddenKeys addObject:@"auto_write_to_gallery_mode"];
[hiddenKeys addObject:@"vfs_enabled_mode"];
}else{
[hiddenKeys addObject:@"vfs_enabled"];
}
if ([lm lpConfigBoolForKey:@"auto_write_to_gallery_mode"]) {
[hiddenKeys addObject:@"auto_write_to_gallery_mode"];
}
return hiddenKeys; return hiddenKeys;
} }
@ -901,8 +967,11 @@ void update_hash_cbs(LinphoneAccountCreator *creator, LinphoneAccountCreatorStat
if (pwd && ![pwd isEqualToString:@""]) { if (pwd && ![pwd isEqualToString:@""]) {
if ([pwd isEqualToString:conf_pwd]) { if ([pwd isEqualToString:conf_pwd]) {
_tmpPwd = pwd; _tmpPwd = pwd;
LinphoneAccount *account = bctbx_list_nth_data(linphone_core_get_account_list(LC),
[settingsStore integerForKey:@"current_proxy_config_preference"]); MSList *accounts = [LinphoneManager.instance createAccountsNotHiddenList];
LinphoneAccount *account = bctbx_list_nth_data(accounts,
[settingsStore integerForKey:@"current_proxy_config_preference"]);
bctbx_free(accounts);
const LinphoneAuthInfo *ai = linphone_account_find_auth_info(account); const LinphoneAuthInfo *ai = linphone_account_find_auth_info(account);
LinphoneAccountCreator *account_creator = linphone_account_creator_new( LinphoneAccountCreator *account_creator = linphone_account_creator_new(

View file

@ -66,8 +66,11 @@
changeCurrentView:AssistantView.compositeViewDescription]; changeCurrentView:AssistantView.compositeViewDescription];
}]]; }]];
BOOL mustLink = ([LinphoneManager.instance lpConfigIntForKey:@"must_link_account_time"] > 0); BOOL mustLink = ([LinphoneManager.instance lpConfigIntForKey:@"must_link_account_time"] > 0);
BOOL hasAccount = linphone_core_get_account_list(LC) != NULL;
if (mustLink && hasAccount) { MSList *accounts = [LinphoneManager.instance createAccountsNotHiddenList];
BOOL hasAccount = accounts != NULL;
bctbx_free(accounts);
if (mustLink && hasAccount && ![LinphoneManager.instance lpConfigIntForKey:@"hide_link_phone_number"]) {
[_sideMenuEntries [_sideMenuEntries
addObject:[[SideMenuEntry alloc] initWithTitle:NSLocalizedString(@"Link my account", nil) addObject:[[SideMenuEntry alloc] initWithTitle:NSLocalizedString(@"Link my account", nil)
image:[UIImage imageNamed:@"menu_link_account.png"] image:[UIImage imageNamed:@"menu_link_account.png"]
@ -104,7 +107,7 @@
} }
[_sideMenuEntries addObject:[[SideMenuEntry alloc] initWithTitle:VoipTexts.conference_scheduled [_sideMenuEntries addObject:[[SideMenuEntry alloc] initWithTitle:VoipTexts.conference_scheduled
image:[UIImage imageNamed:@"voip_conference_new.png"] image:[UIImage imageNamed:@"side_menu_voip_meeting_schedule"]
tapBlock:^() { tapBlock:^() {
[PhoneMainView.instance [PhoneMainView.instance
changeCurrentView:ScheduledConferencesView.compositeViewDescription]; changeCurrentView:ScheduledConferencesView.compositeViewDescription];
@ -127,9 +130,12 @@
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
if (section == 0) { if (section == 0) {
BOOL hasDefault = (linphone_core_get_default_account(LC) != NULL); BOOL hasDefault = (linphone_core_get_default_account(LC) != NULL);
// default account is shown in the header already // default account is shown in the header already
size_t count = bctbx_list_size(linphone_core_get_account_list(LC)); MSList *accounts = [LinphoneManager.instance createAccountsNotHiddenList];
size_t count = bctbx_list_size(accounts);
bctbx_free(accounts);
return MAX(0, (int)count - (hasDefault ? 1 : 0)); return MAX(0, (int)count - (hasDefault ? 1 : 0));
} else { } else {
return [_sideMenuEntries count]; return [_sideMenuEntries count];
@ -142,12 +148,14 @@
// isLcInitialized called here because this is called when going in bg after LC destroy // isLcInitialized called here because this is called when going in bg after LC destroy
if (indexPath.section == 0 && [LinphoneManager isLcInitialized]) { if (indexPath.section == 0 && [LinphoneManager isLcInitialized]) {
// do not display default account here, it is already in header view // do not display default account here, it is already in header view
MSList *accounts = [LinphoneManager.instance createAccountsNotHiddenList];
int idx = int idx =
linphone_core_get_default_account(LC) linphone_core_get_default_account(LC)
? bctbx_list_index(linphone_core_get_account_list(LC), linphone_core_get_default_account(LC)) ? bctbx_list_index(accounts, linphone_core_get_default_account(LC))
: HUGE_VAL; : HUGE_VAL;
LinphoneAccount *account = bctbx_list_nth_data(linphone_core_get_account_list(LC), LinphoneAccount *account = bctbx_list_nth_data(accounts,
(int)indexPath.row + (idx <= indexPath.row ? 1 : 0)); (int)indexPath.row + (idx <= indexPath.row ? 1 : 0));
bctbx_free(accounts);
if (account) { if (account) {
cell.textLabel.text = [NSString stringWithUTF8String:linphone_account_params_get_identity(linphone_account_get_params(account))]; cell.textLabel.text = [NSString stringWithUTF8String:linphone_account_params_get_identity(linphone_account_get_params(account))];
cell.imageView.image = [StatusBarView imageForState:linphone_account_get_state(account)]; cell.imageView.image = [StatusBarView imageForState:linphone_account_get_state(account)];

View file

@ -70,7 +70,9 @@
_addressLabel.text = str ? [NSString stringWithUTF8String:str] : NSLocalizedString(@"No address", nil); _addressLabel.text = str ? [NSString stringWithUTF8String:str] : NSLocalizedString(@"No address", nil);
if (str) ms_free(str); if (str) ms_free(str);
} else { } else {
_nameLabel.text = linphone_core_get_account_list(LC) ? NSLocalizedString(@"No default account", nil) : NSLocalizedString(@"No account", nil); MSList *accounts = [LinphoneManager.instance createAccountsNotHiddenList];
_nameLabel.text = accounts ? NSLocalizedString(@"No default account", nil) : NSLocalizedString(@"No account", nil);
bctbx_free(accounts);
// display direct IP:port address so that we can be reached // display direct IP:port address so that we can be reached
LinphoneAddress *addr = linphone_core_get_primary_contact_parsed(LC); LinphoneAddress *addr = linphone_core_get_primary_contact_parsed(LC);
if (addr) { if (addr) {

View file

@ -27,7 +27,7 @@ import AVFoundation
@objc class CallAppData: NSObject { @objc class CallAppData: NSObject {
@objc var batteryWarningShown = false @objc var batteryWarningShown = false
@objc var videoRequested = false /*set when user has requested for video*/ @objc var videoRequested = false /*set when user has requested for video*/
@objc var isConference = true @objc var isConference = false
} }
@ -49,6 +49,7 @@ import AVFoundation
var actionsToPerformOnceWhenCoreIsOn : [(()->Void)] = [] var actionsToPerformOnceWhenCoreIsOn : [(()->Void)] = []
var conference: Conference? var conference: Conference?
var callkitAudioSessionActivated : Bool? = nil // if "nil", ignore. var callkitAudioSessionActivated : Bool? = nil // if "nil", ignore.
var actionToFulFill : CXCallAction? = nil;
var backgroundContextCall : Call? var backgroundContextCall : Call?
@objc var backgroundContextCameraIsEnabled : Bool = false @objc var backgroundContextCameraIsEnabled : Bool = false
@ -556,7 +557,7 @@ import AVFoundation
switch cstate { switch cstate {
case .IncomingReceived: case .IncomingReceived:
let addr = call.remoteAddress let addr = call.remoteAddress
var displayName = incomingDisplayName(call: call) var displayName = incomingDisplayName(call: call)
if (CallManager.callKitEnabled()) { if (CallManager.callKitEnabled()) {
let isConference = isConferenceCall(call: call) let isConference = isConferenceCall(call: call)
@ -608,7 +609,13 @@ import AVFoundation
CallManager.instance().speakerBeforePause = false CallManager.instance().speakerBeforePause = false
CallManager.instance().changeRouteToSpeaker() CallManager.instance().changeRouteToSpeaker()
} }
actionToFulFill?.fulfill()
actionToFulFill = nil
break break
case .Paused:
actionToFulFill?.fulfill()
actionToFulFill = nil
break
case .OutgoingInit, case .OutgoingInit,
.OutgoingProgress, .OutgoingProgress,
.OutgoingRinging, .OutgoingRinging,
@ -625,7 +632,16 @@ import AVFoundation
Log.directLog(BCTBX_LOG_MESSAGE, text: "CallKit: outgoing call started connecting with uuid \(uuid!) and callId \(callId!)") Log.directLog(BCTBX_LOG_MESSAGE, text: "CallKit: outgoing call started connecting with uuid \(uuid!) and callId \(callId!)")
CallManager.instance().providerDelegate.reportOutgoingCallStartedConnecting(uuid: uuid!) CallManager.instance().providerDelegate.reportOutgoingCallStartedConnecting(uuid: uuid!)
} else { } else {
CallManager.instance().referedToCall = callId if CallManager.instance().isConferenceCall(call: call) {
let uuid = UUID()
let callInfo = CallInfo.newOutgoingCallInfo(addr: call.remoteAddress!, isSas: call.params?.mediaEncryption == .ZRTP, displayName: VoipTexts.conference_default_title, isVideo: call.params?.videoEnabled == true, isConference:true)
CallManager.instance().providerDelegate.callInfos.updateValue(callInfo, forKey: uuid)
CallManager.instance().providerDelegate.uuids.updateValue(uuid, forKey: "")
CallManager.instance().providerDelegate.reportOutgoingCallStartedConnecting(uuid: uuid)
Core.get().activateAudioSession(actived: true)
} else {
CallManager.instance().referedToCall = callId
}
} }
} }
break break
@ -706,7 +722,7 @@ import AVFoundation
let readyForRoutechange = CallManager.instance().callkitAudioSessionActivated == nil || (CallManager.instance().callkitAudioSessionActivated == true) let readyForRoutechange = CallManager.instance().callkitAudioSessionActivated == nil || (CallManager.instance().callkitAudioSessionActivated == true)
if (readyForRoutechange && (cstate == .IncomingReceived || cstate == .OutgoingInit || cstate == .Connected || cstate == .StreamsRunning)) { if (readyForRoutechange && (cstate == .IncomingReceived || cstate == .OutgoingInit || cstate == .Connected || cstate == .StreamsRunning)) {
if ((call.currentParams?.videoEnabled ?? false) && CallManager.instance().isReceiverEnabled()) { if ((call.currentParams?.videoEnabled ?? false) && CallManager.instance().isReceiverEnabled() && call.conference == nil) {
CallManager.instance().changeRouteToSpeaker() CallManager.instance().changeRouteToSpeaker()
} else if (isBluetoothAvailable()) { } else if (isBluetoothAvailable()) {
// Use bluetooth device by default if one is available // Use bluetooth device by default if one is available
@ -775,6 +791,7 @@ import AVFoundation
do { do {
if let core = lc, let params = try? core.createConferenceParams(conference: nil) { if let core = lc, let params = try? core.createConferenceParams(conference: nil) {
params.videoEnabled = false // We disable video for local conferencing (cf Android) params.videoEnabled = false // We disable video for local conferencing (cf Android)
params.subject = VoipTexts.conference_local_title
let conference = core.conference != nil ? core.conference : try core.createConferenceWithParams(params: params) let conference = core.conference != nil ? core.conference : try core.createConferenceWithParams(params: params)
try conference?.addParticipants(calls: core.calls) try conference?.addParticipants(calls: core.calls)
} }
@ -783,8 +800,12 @@ import AVFoundation
} }
} }
@objc func applyInternationalPrefix() -> Bool {
if let account = lc?.defaultAccount, let params = account.params {
return params.useInternationalPrefixForCallsAndChats
}
return true; // Legacy behavior
}
} }

View file

@ -36,10 +36,16 @@ class ScheduledConferenceData {
let participantsShort = MutableLiveData<String>() let participantsShort = MutableLiveData<String>()
let participantsExpanded = MutableLiveData<String>() let participantsExpanded = MutableLiveData<String>()
let rawDate : Date let rawDate : Date
let isConferenceCancelled = MutableLiveData(false)
let canEdit = MutableLiveData(false)
let isFinished : Bool
let selectedForDeletion = MutableLiveData(false)
private var conferenceSchedulerDelegate : ConferenceSchedulerDelegateStub? = nil
private var conferenceScheduler : ConferenceScheduler? = nil
init (conferenceInfo: ConferenceInfo, isFinished: Bool = false) {
init (conferenceInfo: ConferenceInfo) {
self.conferenceInfo = conferenceInfo self.conferenceInfo = conferenceInfo
self.isFinished = isFinished
expanded.value = false expanded.value = false
address.value = conferenceInfo.uri?.asStringUriOnly() address.value = conferenceInfo.uri?.asStringUriOnly()
@ -51,14 +57,24 @@ class ScheduledConferenceData {
rawDate = Date(timeIntervalSince1970:TimeInterval(conferenceInfo.dateTime)) rawDate = Date(timeIntervalSince1970:TimeInterval(conferenceInfo.dateTime))
let durationFormatter = DateComponentsFormatter() let durationFormatter = DateComponentsFormatter()
durationFormatter.unitsStyle = .positional durationFormatter.unitsStyle = .abbreviated
durationFormatter.allowedUnits = [.minute, .second ] duration.value = conferenceInfo.duration > 0 ? durationFormatter.string(from: TimeInterval(conferenceInfo.duration*60)) : nil
durationFormatter.zeroFormattingBehavior = [ .pad ]
duration.value = conferenceInfo.duration > 0 ? durationFormatter.string(from: TimeInterval(conferenceInfo.duration)) : nil
organizer.value = conferenceInfo.organizer?.addressBookEnhancedDisplayName() organizer.value = conferenceInfo.organizer?.addressBookEnhancedDisplayName()
computeParticipantsLists() computeParticipantsLists()
isConferenceCancelled.value = conferenceInfo.state == .Cancelled
if let organizerAddress = conferenceInfo.organizer {
let localAccount = Core.get().accountList.filter { account in
account.params?.identityAddress != nil && organizerAddress.weakEqual(address2: account.params!.identityAddress!)
}.first
canEdit.value = localAccount != nil
} else {
canEdit.value = false
Log.e("[Scheduled Conference] No organizer SIP URI found for: \(conferenceInfo.uri?.asStringUriOnly())")
}
} }
func destroy() { func destroy() {
@ -73,6 +89,10 @@ class ScheduledConferenceData {
String(describing: participant.addressBookEnhancedDisplayName()) String(describing: participant.addressBookEnhancedDisplayName())
}.joined(separator: ", ") }.joined(separator: ", ")
if (participantsShort.value?.count == 0) {
participantsShort.value = " "
}
participantsExpanded.value = conferenceInfo.participants.map {(participant) in participantsExpanded.value = conferenceInfo.participants.map {(participant) in
String(describing: participant.addressBookEnhancedDisplayName())+" ("+String(describing: participant.asStringUriOnly())+")" String(describing: participant.addressBookEnhancedDisplayName())+" ("+String(describing: participant.asStringUriOnly())+")"
}.joined(separator: "\n") }.joined(separator: "\n")
@ -81,4 +101,30 @@ class ScheduledConferenceData {
func gotoAssociatedChat() { func gotoAssociatedChat() {
} }
func deleteConference() {
conferenceSchedulerDelegate = ConferenceSchedulerDelegateStub(
onStateChanged: { scheduler, state in
Log.i("[Conference Deletion] Conference scheduler state is \(state)")
if (state == .Ready) {
if let chatRoomParams = ConferenceSchedulingViewModel.shared.getConferenceInvitationsChatRoomParams() {
scheduler.sendInvitations(chatRoomParams: chatRoomParams) // Send cancel ICS
Log.e("[Conference Deletion] sent cancel ICS.")
}
}
})
if (conferenceInfo.state != .Cancelled && canEdit.value == true) {
Log.i("[Scheduled Conferences] Cancelling conference \(conferenceInfo.subject)")
self.conferenceScheduler = try? Core.get().createConferenceScheduler()
if (self.conferenceScheduler != nil) {
self.conferenceScheduler?.addDelegate(delegate: conferenceSchedulerDelegate!)
self.conferenceScheduler?.cancelConference(conferenceInfo: conferenceInfo)
}
} else {
Core.get().deleteConferenceInformation(conferenceInfo: conferenceInfo)
}
}
} }

View file

@ -31,8 +31,9 @@ class ConferenceSchedulingViewModel {
let description = MutableLiveData<String>() let description = MutableLiveData<String>()
let scheduleForLater = MutableLiveData<Bool>() let scheduleForLater = MutableLiveData<Bool>()
let scheduledDateTime = MutableLiveData<Date>() let scheduledDate = MutableLiveData<Date>()
let scheduledTime = MutableLiveData<Date>()
var scheduledTimeZone = MutableLiveData<Int>() var scheduledTimeZone = MutableLiveData<Int>()
static let timeZones: [TimeZoneData] = computeTimeZonesList() static let timeZones: [TimeZoneData] = computeTimeZonesList()
@ -64,7 +65,7 @@ class ConferenceSchedulingViewModel {
private var chatRooomDelegate : ChatRoomDelegate? = nil private var chatRooomDelegate : ChatRoomDelegate? = nil
private var conferenceSchedulerDelegate : ConferenceSchedulerDelegateStub? = nil private var conferenceSchedulerDelegate : ConferenceSchedulerDelegateStub? = nil
var existingConfInfo:ConferenceInfo? = nil var existingConfInfo:MutableLiveData<ConferenceInfo?> = MutableLiveData()
init () { init () {
@ -79,14 +80,10 @@ class ConferenceSchedulingViewModel {
} }
self.address.value = conferenceAddress self.address.value = conferenceAddress
if (self.sendInviteViaChat.value == true) { if (self.scheduleForLater.value == true && self.sendInviteViaChat.value == true) {
// Send conference info even when conf is not scheduled for later // Send conference info even when conf is not scheduled for later
// as the conference server doesn't invite participants automatically // as the conference server doesn't invite participants automatically
if let chatRoomParams = try?self.core.createDefaultChatRoomParams() { if let chatRoomParams = self.getConferenceInvitationsChatRoomParams() {
chatRoomParams.backend = ChatRoomBackend.FlexisipChat
chatRoomParams.groupEnabled = false
chatRoomParams.encryptionEnabled = true
chatRoomParams.subject = self.subject.value!
scheduler.sendInvitations(chatRoomParams: chatRoomParams) scheduler.sendInvitations(chatRoomParams: chatRoomParams)
} }
} else { } else {
@ -133,13 +130,23 @@ class ConferenceSchedulingViewModel {
scheduleForLater.observe { _ in scheduleForLater.observe { _ in
self.continueEnabled.value = self.allMandatoryFieldsFilled() self.continueEnabled.value = self.allMandatoryFieldsFilled()
} }
scheduledDateTime.observe { _ in scheduledDate.observe { _ in
self.continueEnabled.value = self.allMandatoryFieldsFilled()
}
scheduledTime.observe { _ in
self.continueEnabled.value = self.allMandatoryFieldsFilled() self.continueEnabled.value = self.allMandatoryFieldsFilled()
} }
} }
func isEndToEndEncryptedChatAvailable() -> Bool {
let core = Core.get()
return core.limeX3DhEnabled &&
((core.limeX3DhServerUrl != nil && core.limeX3DhServerUrl.count > 0) || core.defaultAccount?.params?.limeServerUrl != nil) &&
core.defaultAccount?.params?.conferenceFactoryUri != nil
}
func reset() { func reset() {
subject.value = "" subject.value = ""
@ -147,8 +154,9 @@ class ConferenceSchedulingViewModel {
isEncrypted.value = false isEncrypted.value = false
sendInviteViaChat.value = true sendInviteViaChat.value = true
sendInviteViaEmail.value = false sendInviteViaEmail.value = false
scheduledDateTime.value = Date() scheduledDate.value = nil
scheduledTime.value = nil
scheduledTimeZone.value = ConferenceSchedulingViewModel.timeZones.indices.filter { scheduledTimeZone.value = ConferenceSchedulingViewModel.timeZones.indices.filter {
ConferenceSchedulingViewModel.timeZones[$0].timeZone.identifier == NSTimeZone.default.identifier ConferenceSchedulingViewModel.timeZones[$0].timeZone.identifier == NSTimeZone.default.identifier
}.first }.first
@ -158,7 +166,7 @@ class ConferenceSchedulingViewModel {
}.first }.first
continueEnabled.value = false continueEnabled.value = false
selectedAddresses.value = [] selectedAddresses.value = []
existingConfInfo = nil existingConfInfo.value = nil
description.value = "" description.value = ""
} }
@ -191,7 +199,7 @@ class ConferenceSchedulingViewModel {
conferenceScheduler = try? Core.get().createConferenceScheduler() conferenceScheduler = try? Core.get().createConferenceScheduler()
conferenceScheduler?.addDelegate(delegate: conferenceSchedulerDelegate!) conferenceScheduler?.addDelegate(delegate: conferenceSchedulerDelegate!)
guard let conferenceInfo = existingConfInfo != nil ? existingConfInfo : try Factory.Instance.createConferenceInfo() else { guard let conferenceInfo = existingConfInfo.value != nil ? existingConfInfo.value??.clone() : try Factory.Instance.createConferenceInfo() else {
Log.e("[Conference Creation/Update] Failed, unable to get conf info.") Log.e("[Conference Creation/Update] Failed, unable to get conf info.")
return return
} }
@ -206,7 +214,7 @@ class ConferenceSchedulingViewModel {
} }
conferenceScheduler?.account = localAccount conferenceScheduler?.account = localAccount
conferenceScheduler?.info = conferenceInfo // Will trigger the conference creation automatically conferenceScheduler?.info = conferenceInfo // Will trigger the conference creation automatically
existingConfInfo = conferenceInfo existingConfInfo.value = conferenceInfo
} catch { } catch {
Log.e("[Conference Creation] Failed \(error)") Log.e("[Conference Creation] Failed \(error)")
@ -216,14 +224,15 @@ class ConferenceSchedulingViewModel {
private func allMandatoryFieldsFilled() -> Bool { private func allMandatoryFieldsFilled() -> Bool {
return subject.value != nil && subject.value!.count > 0 && (scheduleForLater.value != true || scheduledDateTime.value != nil); return subject.value != nil && subject.value!.count > 0 && (scheduleForLater.value != true || (scheduledDate.value != nil && scheduledTime.value != nil) );
} }
private func getConferenceStartTimestamp() -> Double { private func getConferenceStartTimestamp() -> Double {
return scheduleForLater.value == true ? let days = Int32(scheduledDate.value!.timeIntervalSince1970)/86400
scheduledDateTime.value!.timeIntervalSince1970 + let time = Int32(scheduledTime.value!.timeIntervalSince1970)%86400
Double(ConferenceSchedulingViewModel.timeZones[scheduledTimeZone.value!].timeZone.secondsFromGMT()-TimeZone.current.secondsFromGMT())
return scheduleForLater.value == true ? TimeInterval(days * 86400 + time) + Double(ConferenceSchedulingViewModel.timeZones[scheduledTimeZone.value!].timeZone.secondsFromGMT()-TimeZone.current.secondsFromGMT())
: Date().timeIntervalSince1970 : Date().timeIntervalSince1970
} }
@ -238,5 +247,16 @@ class ConferenceSchedulingViewModel {
return [Duration(value: 30, display: "30min"), Duration(value: 60, display: "1h"), Duration(value: 120, display: "2h")] return [Duration(value: 30, display: "30min"), Duration(value: 60, display: "1h"), Duration(value: 120, display: "2h")]
} }
func getConferenceInvitationsChatRoomParams() -> ChatRoomParams? {
if let chatRoomParams = try?self.core.createDefaultChatRoomParams() {
chatRoomParams.encryptionEnabled = self.isEndToEndEncryptedChatAvailable()
chatRoomParams.groupEnabled = false
chatRoomParams.backend = chatRoomParams.encryptionEnabled ? .FlexisipChat : .Basic
chatRoomParams.subject = self.subject.value!
return chatRoomParams
}
return nil
}
} }

View file

@ -31,13 +31,17 @@ class ScheduledConferencesViewModel {
var conferences : MutableLiveData<[ScheduledConferenceData]> = MutableLiveData([]) var conferences : MutableLiveData<[ScheduledConferenceData]> = MutableLiveData([])
var daySplitted : [Date : [ScheduledConferenceData]] = [:] var daySplitted : [Date : [ScheduledConferenceData]] = [:]
var coreDelegate: CoreDelegateStub? var coreDelegate: CoreDelegateStub?
var showTerminated = MutableLiveData(false)
let editionEnabled = MutableLiveData(false)
let conferenceScheduler = try? Core.get().createConferenceScheduler()
init () { init () {
coreDelegate = CoreDelegateStub( coreDelegate = CoreDelegateStub(
onConferenceInfoReceived: { (core, conferenceInfo) in onConferenceInfoReceived: { (core, conferenceInfo) in
Log.i("[Scheduled Conferences] New conference info received") Log.i("[Scheduled Conferences] New conference info received")
self.conferences.value!.append(ScheduledConferenceData(conferenceInfo: conferenceInfo)) self.conferences.value!.append(ScheduledConferenceData(conferenceInfo: conferenceInfo,isFinished: false))
self.conferences.notifyValue() self.conferences.notifyValue()
} }
@ -48,11 +52,20 @@ class ScheduledConferencesViewModel {
func computeConferenceInfoList() { func computeConferenceInfoList() {
conferences.value!.removeAll() conferences.value!.removeAll()
let now = Date().timeIntervalSince1970 // Linphone uses time_t in seconds let now = Date().timeIntervalSince1970 // Linphone uses time_t in seconds
let oneHourAgo = now - 3600 // Show all conferences from 1 hour ago and forward
core.getConferenceInformationListAfterTime(time: time_t(oneHourAgo)).filter{$0.duration != 0}.forEach { conferenceInfo in if (showTerminated.value == true) {
conferences.value!.append(ScheduledConferenceData(conferenceInfo: conferenceInfo)) core.conferenceInformationList.filter{$0.duration != 0 && (TimeInterval($0.dateTime) + TimeInterval($0.duration) < now)}.forEach { conferenceInfo in
conferences.value!.append(ScheduledConferenceData(conferenceInfo: conferenceInfo,isFinished: true))
}
} else {
let twoHoursAgo = now - 7200 // Show all conferences from 2 hour ago and forward
core.getConferenceInformationListAfterTime(time: time_t(twoHoursAgo)).filter{$0.duration != 0}.forEach { conferenceInfo in
conferences.value!.append(ScheduledConferenceData(conferenceInfo: conferenceInfo,isFinished: false))
}
} }
daySplitted = [:] daySplitted = [:]
conferences.value!.forEach { (conferenceInfo) in conferences.value!.forEach { (conferenceInfo) in
let startDateDay = dateAtBeginningOfDay(for: conferenceInfo.rawDate) let startDateDay = dateAtBeginningOfDay(for: conferenceInfo.rawDate)

View file

@ -50,7 +50,7 @@ import linphonesw
} }
static let compositeDescription = UICompositeViewDescription(ConferenceHistoryDetailsView.self, statusBar: StatusBarView.self, tabBar: nil, sideMenu: SideMenuView.self, fullscreen: false, isLeftFragment: false,fragmentWith: nil) static let compositeDescription = UICompositeViewDescription(ConferenceHistoryDetailsView.self, statusBar: StatusBarView.self, tabBar: TabBarView.classForCoder(), sideMenu: SideMenuView.self, fullscreen: false, isLeftFragment: false,fragmentWith: HistoryListView.classForCoder())
static func compositeViewDescription() -> UICompositeViewDescription! { return compositeDescription } static func compositeViewDescription() -> UICompositeViewDescription! { return compositeDescription }
func compositeViewDescription() -> UICompositeViewDescription! { return type(of: self).compositeDescription } func compositeViewDescription() -> UICompositeViewDescription! { return type(of: self).compositeDescription }
@ -65,6 +65,7 @@ import linphonesw
title:"") title:"")
super.nextButton.isHidden = true super.nextButton.isHidden = true
super.backButton.isHidden = UIDevice.ipad()
let schedulingStack = UIStackView() let schedulingStack = UIStackView()
schedulingStack.axis = .vertical schedulingStack.axis = .vertical
@ -106,7 +107,6 @@ import linphonesw
// Organiser // Organiser
organiserLabel.backgroundColor = VoipTheme.voipFormBackgroundColor.get()
contentView.addSubview(organiserLabel) contentView.addSubview(organiserLabel)
organiserLabel.matchParentSideBorders().height(form_input_height).alignUnder(view: schedulingStack,withMargin: form_margin*2).done() organiserLabel.matchParentSideBorders().height(form_input_height).alignUnder(view: schedulingStack,withMargin: form_margin*2).done()
organiserLabel.textAlignment = .left organiserLabel.textAlignment = .left
@ -120,12 +120,10 @@ import linphonesw
organizerTableView.allowsFocus = false organizerTableView.allowsFocus = false
} }
organizerTableView.separatorStyle = .singleLine organizerTableView.separatorStyle = .singleLine
organizerTableView.separatorColor = VoipTheme.light_grey_color
organizerTableView.tag = 1; organizerTableView.tag = 1;
organizerTableView.matchParentSideBorders().height(VoipParticipantCell.cell_height).alignUnder(view: organiserLabel,withMargin: form_margin).done() organizerTableView.matchParentSideBorders().height(VoipParticipantCell.cell_height).alignUnder(view: organiserLabel,withMargin: form_margin).done()
// Participants // Participants
participantsLabel.backgroundColor = VoipTheme.voipFormBackgroundColor.get()
contentView.addSubview(participantsLabel) contentView.addSubview(participantsLabel)
participantsLabel.matchParentSideBorders().height(form_input_height).alignUnder(view: organizerTableView,withMargin: form_margin).done() participantsLabel.matchParentSideBorders().height(form_input_height).alignUnder(view: organizerTableView,withMargin: form_margin).done()
participantsLabel.textAlignment = .left participantsLabel.textAlignment = .left
@ -139,7 +137,6 @@ import linphonesw
participantsListTableView.allowsFocus = false participantsListTableView.allowsFocus = false
} }
participantsListTableView.separatorStyle = .singleLine participantsListTableView.separatorStyle = .singleLine
participantsListTableView.separatorColor = VoipTheme.light_grey_color
// Goto chat - v2 // Goto chat - v2
/* /*
@ -152,6 +149,14 @@ import linphonesw
chatButton.centerX().alignParentBottom(withMargin: 3*self.form_margin).alignUnder(view: participantsListTableView,withMargin: 3*self.form_margin).done() chatButton.centerX().alignParentBottom(withMargin: 3*self.form_margin).alignUnder(view: participantsListTableView,withMargin: 3*self.form_margin).done()
*/ */
UIDeviceBridge.displayModeSwitched.readCurrentAndObserve { _ in
self.organiserLabel.backgroundColor = VoipTheme.voipFormBackgroundColor.get()
self.organizerTableView.separatorColor = VoipTheme.separatorColor.get()
self.participantsLabel.backgroundColor = VoipTheme.voipFormBackgroundColor.get()
self.participantsListTableView.separatorColor = VoipTheme.separatorColor.get()
self.view.backgroundColor = VoipTheme.voipBackgroundBWColor.get()
}
} }

View file

@ -29,13 +29,16 @@ import SVProgressHUD
let participantsListTableView = UITableView() let participantsListTableView = UITableView()
let datePicker = StyledDatePicker(liveValue: ConferenceSchedulingViewModel.shared.scheduledDateTime,pickerMode: .date, readOnly:true) let datePicker = StyledDatePicker(liveValue: ConferenceSchedulingViewModel.shared.scheduledDate,pickerMode: .date, readOnly:true)
let timeZoneValue = StyledValuePicker(liveIndex: ConferenceSchedulingViewModel.shared.scheduledTimeZone,options: ConferenceSchedulingViewModel.timeZones.map({ (tzd: TimeZoneData) -> String in tzd.descWithOffset()}), readOnly:true) let timeZoneValue = StyledValuePicker(liveIndex: ConferenceSchedulingViewModel.shared.scheduledTimeZone,options: ConferenceSchedulingViewModel.timeZones.map({ (tzd: TimeZoneData) -> String in tzd.descWithOffset()}), readOnly:true)
let durationValue = StyledValuePicker(liveIndex: ConferenceSchedulingViewModel.shared.scheduledDuration,options: ConferenceSchedulingViewModel.durationList.map({ (duration: Duration) -> String in duration.display}), readOnly:true) let durationValue = StyledValuePicker(liveIndex: ConferenceSchedulingViewModel.shared.scheduledDuration,options: ConferenceSchedulingViewModel.durationList.map({ (duration: Duration) -> String in duration.display}), readOnly:true)
let timePicker = StyledDatePicker(liveValue: ConferenceSchedulingViewModel.shared.scheduledDateTime,pickerMode: .time, readOnly:true) let timePicker = StyledDatePicker(liveValue: ConferenceSchedulingViewModel.shared.scheduledTime,pickerMode: .time, readOnly:true)
let descriptionLabel = StyledLabel(VoipTheme.conference_scheduling_font, VoipTexts.conference_schedule_description_hint)
let descriptionInput = StyledTextView(VoipTheme.conference_scheduling_font, placeHolder:VoipTexts.conference_schedule_description_hint,liveValue: ConferenceSchedulingViewModel.shared.description, readOnly:true) let descriptionInput = StyledTextView(VoipTheme.conference_scheduling_font, placeHolder:VoipTexts.conference_schedule_description_hint,liveValue: ConferenceSchedulingViewModel.shared.description, readOnly:true)
let createButton = FormButton(backgroundStateColors: VoipTheme.primary_colors_background) let createButton = FormButton(backgroundStateColors: VoipTheme.primary_colors_background)
let leftColumn = UIView()
let rightColumn = UIView()
let scheduleForm = UIView()
static let compositeDescription = UICompositeViewDescription(ConferenceSchedulingSummaryView.self, statusBar: StatusBarView.self, tabBar: nil, sideMenu: SideMenuView.self, fullscreen: false, isLeftFragment: false,fragmentWith: nil) static let compositeDescription = UICompositeViewDescription(ConferenceSchedulingSummaryView.self, statusBar: StatusBarView.self, tabBar: nil, sideMenu: SideMenuView.self, fullscreen: false, isLeftFragment: false,fragmentWith: nil)
static func compositeViewDescription() -> UICompositeViewDescription! { return compositeDescription } static func compositeViewDescription() -> UICompositeViewDescription! { return compositeDescription }
@ -76,15 +79,12 @@ import SVProgressHUD
schedulingStack.alignUnder(view: subjectInput,withMargin: 3*form_margin).matchParentSideBorders(insetedByDx: form_margin).done() schedulingStack.alignUnder(view: subjectInput,withMargin: 3*form_margin).matchParentSideBorders(insetedByDx: form_margin).done()
let scheduleForm = UIView()
schedulingStack.addArrangedSubview(scheduleForm) schedulingStack.addArrangedSubview(scheduleForm)
scheduleForm.matchParentSideBorders().done() ConferenceSchedulingViewModel.shared.scheduleForLater.readCurrentAndObserve { (forLater) in self.scheduleForm.isHidden = forLater != true }
ConferenceSchedulingViewModel.shared.scheduleForLater.readCurrentAndObserve { (forLater) in scheduleForm.isHidden = forLater != true }
// Left column (Date & Time) // Left column (Date & Time)
let leftColumn = UIView()
scheduleForm.addSubview(leftColumn) scheduleForm.addSubview(leftColumn)
leftColumn.matchParentWidthDividedBy(2.2).alignParentLeft(withMargin: form_margin).alignParentTop(withMargin: form_margin).done() leftColumn.matchParentWidthDividedBy(2.2).alignParentLeft().alignParentTop(withMargin: form_margin).done()
let dateLabel = StyledLabel(VoipTheme.conference_scheduling_font, VoipTexts.conference_schedule_date) let dateLabel = StyledLabel(VoipTheme.conference_scheduling_font, VoipTexts.conference_schedule_date)
leftColumn.addSubview(dateLabel) leftColumn.addSubview(dateLabel)
@ -104,9 +104,8 @@ import SVProgressHUD
// Right column (Duration & Timezone) // Right column (Duration & Timezone)
let rightColumn = UIView()
scheduleForm.addSubview(rightColumn) scheduleForm.addSubview(rightColumn)
rightColumn.matchParentWidthDividedBy(2.2).alignParentRight(withMargin: form_margin).alignParentTop().done() rightColumn.matchParentWidthDividedBy(2.2).alignParentRight().alignParentTop().done()
let durationLabel = StyledLabel(VoipTheme.conference_scheduling_font, VoipTexts.conference_schedule_duration) let durationLabel = StyledLabel(VoipTheme.conference_scheduling_font, VoipTexts.conference_schedule_duration)
rightColumn.addSubview(durationLabel) rightColumn.addSubview(durationLabel)
@ -125,31 +124,26 @@ import SVProgressHUD
rightColumn.wrapContentY().done() rightColumn.wrapContentY().done()
// Description // Description
let descriptionLabel = StyledLabel(VoipTheme.conference_scheduling_font, VoipTexts.conference_schedule_description_title)
scheduleForm.addSubview(descriptionLabel) scheduleForm.addSubview(descriptionLabel)
descriptionLabel.alignUnder(view: leftColumn,withMargin: form_margin).alignUnder(view: rightColumn,withMargin: form_margin).matchParentSideBorders(insetedByDx: form_margin).done()
descriptionInput.textContainer.maximumNumberOfLines = 5 descriptionInput.textContainer.maximumNumberOfLines = 5
descriptionInput.textContainer.lineBreakMode = .byWordWrapping descriptionInput.textContainer.lineBreakMode = .byWordWrapping
scheduleForm.addSubview(descriptionInput) scheduleForm.addSubview(descriptionInput)
descriptionInput.alignUnder(view: descriptionLabel,withMargin: form_margin).matchParentSideBorders(insetedByDx: form_margin).height(description_height).alignParentBottom(withMargin: form_margin*2).done()
scheduleForm.wrapContentY().done()
// Sending method // Sending method
let viaChatLabel = StyledLabel(VoipTheme.conference_scheduling_font, VoipTexts.conference_schedule_send_invite_chat_summary) let viaChatLabel = StyledLabel(VoipTheme.conference_scheduling_font, VoipTexts.conference_schedule_send_invite_chat_summary)
contentView.addSubview(viaChatLabel) contentView.addSubview(viaChatLabel)
viaChatLabel.matchParentSideBorders(insetedByDx: form_margin).alignUnder(view: schedulingStack,withMargin: 2*form_margin).done() viaChatLabel.matchParentSideBorders(insetedByDx: form_margin).alignUnder(view: schedulingStack,withMargin: 2*form_margin).done()
viaChatLabel.numberOfLines = 2
ConferenceSchedulingViewModel.shared.sendInviteViaChat.readCurrentAndObserve { (sendChat) in ConferenceSchedulingViewModel.shared.sendInviteViaChat.readCurrentAndObserve { (sendChat) in
viaChatLabel.isHidden = sendChat != true || ConferenceSchedulingViewModel.shared.scheduleForLater.value != true viaChatLabel.isHidden = sendChat != true || ConferenceSchedulingViewModel.shared.scheduleForLater.value != true
} }
// Participants // Participants
let participantsLabel = StyledLabel(VoipTheme.conference_scheduling_font, VoipTexts.conference_schedule_participants_list) let participantsLabel = StyledLabel(VoipTheme.conference_scheduling_font, " "+VoipTexts.conference_schedule_participants_list)
participantsLabel.backgroundColor = VoipTheme.voipFormBackgroundColor.get()
contentView.addSubview(participantsLabel) contentView.addSubview(participantsLabel)
participantsLabel.matchParentSideBorders().height(form_input_height).alignUnder(view: viaChatLabel,withMargin: form_margin*2).done() participantsLabel.matchParentSideBorders().height(form_input_height).alignUnder(view: viaChatLabel,withMargin: form_margin).done()
participantsLabel.textAlignment = .center participantsLabel.textAlignment = .left
contentView.addSubview(participantsListTableView) contentView.addSubview(participantsListTableView)
@ -161,7 +155,7 @@ import SVProgressHUD
participantsListTableView.allowsFocus = false participantsListTableView.allowsFocus = false
} }
participantsListTableView.separatorStyle = .singleLine participantsListTableView.separatorStyle = .singleLine
participantsListTableView.separatorColor = VoipTheme.light_grey_color participantsListTableView.backgroundColor = .clear
ConferenceSchedulingViewModel.shared.selectedAddresses.readCurrentAndObserve { (addresses) in ConferenceSchedulingViewModel.shared.selectedAddresses.readCurrentAndObserve { (addresses) in
self.participantsListTableView.reloadData() self.participantsListTableView.reloadData()
@ -172,8 +166,13 @@ import SVProgressHUD
// Create / Schedule // Create / Schedule
contentView.addSubview(createButton) contentView.addSubview(createButton)
createButton.centerX().alignParentBottom(withMargin: 3*self.form_margin).alignUnder(view: participantsListTableView,withMargin: 3*self.form_margin).width(0).done()
ConferenceSchedulingViewModel.shared.scheduleForLater.readCurrentAndObserve { _ in ConferenceSchedulingViewModel.shared.scheduleForLater.readCurrentAndObserve { _ in
self.createButton.title = ConferenceSchedulingViewModel.shared.scheduleForLater.value == true ? VoipTexts.conference_schedule_start.uppercased() : VoipTexts.conference_group_call_create.uppercased() self.createButton.title = ConferenceSchedulingViewModel.shared.scheduleForLater.value == true ? ConferenceSchedulingViewModel.shared.existingConfInfo.value != nil ? VoipTexts.conference_schedule_edit.uppercased() : VoipTexts.conference_schedule_start.uppercased() : VoipTexts.conference_group_call_create.uppercased()
self.createButton.addSidePadding()
}
ConferenceSchedulingViewModel.shared.existingConfInfo.readCurrentAndObserve { _ in
self.createButton.title = ConferenceSchedulingViewModel.shared.scheduleForLater.value == true ? ConferenceSchedulingViewModel.shared.existingConfInfo.value != nil ? VoipTexts.conference_schedule_edit.uppercased() : VoipTexts.conference_schedule_start.uppercased() : VoipTexts.conference_group_call_create.uppercased()
self.createButton.addSidePadding() self.createButton.addSidePadding()
} }
@ -210,21 +209,40 @@ import SVProgressHUD
} }
} }
ConferenceSchedulingViewModel.shared.scheduleForLater.readCurrentAndObserve { (later) in ConferenceSchedulingViewModel.shared.scheduleForLater.readCurrentAndObserve { (later) in
self.createButton.title = ConferenceSchedulingViewModel.shared.scheduleForLater.value == true ? VoipTexts.conference_schedule_start.uppercased() : VoipTexts.conference_group_call_create.uppercased() self.createButton.title = ConferenceSchedulingViewModel.shared.scheduleForLater.value == true ? ConferenceSchedulingViewModel.shared.existingConfInfo.value != nil ? VoipTexts.conference_schedule_edit.uppercased() : VoipTexts.conference_schedule_start.uppercased() : VoipTexts.conference_group_call_create.uppercased()
viaChatLabel.isHidden = later != true || ConferenceSchedulingViewModel.shared.sendInviteViaChat.value != true viaChatLabel.isHidden = later != true || ConferenceSchedulingViewModel.shared.sendInviteViaChat.value != true
viaChatLabel.removeConstraints().matchParentSideBorders(insetedByDx: self.form_margin).alignUnder(view: schedulingStack,withMargin: (viaChatLabel.isHidden ? 0 : 1)*self.form_margin).done()
if (viaChatLabel.isHidden) {
viaChatLabel.height(0).done()
}
self.createButton.addSidePadding() self.createButton.addSidePadding()
} }
createButton.centerX().alignParentBottom(withMargin: 3*self.form_margin).alignUnder(view: participantsListTableView,withMargin: 3*self.form_margin).done() UIDeviceBridge.displayModeSwitched.readCurrentAndObserve { _ in
self.view.backgroundColor = VoipTheme.voipBackgroundBWColor.get()
participantsLabel.backgroundColor = VoipTheme.voipFormBackgroundColor.get()
self.participantsListTableView.separatorColor = VoipTheme.separatorColor.get()
}
} }
override func viewWillAppear(_ animated: Bool) { override func viewWillAppear(_ animated: Bool) {
datePicker.liveValue = ConferenceSchedulingViewModel.shared.scheduledDateTime datePicker.liveValue = ConferenceSchedulingViewModel.shared.scheduledDate
timeZoneValue.setIndex(index: ConferenceSchedulingViewModel.shared.scheduledTimeZone.value!) timeZoneValue.setIndex(index: ConferenceSchedulingViewModel.shared.scheduledTimeZone.value!)
durationValue.setIndex(index: ConferenceSchedulingViewModel.shared.scheduledDuration.value!) durationValue.setIndex(index: ConferenceSchedulingViewModel.shared.scheduledDuration.value!)
timePicker.liveValue = ConferenceSchedulingViewModel.shared.scheduledDateTime timePicker.liveValue = ConferenceSchedulingViewModel.shared.scheduledTime
descriptionInput.text = ConferenceSchedulingViewModel.shared.description.value descriptionInput.text = ConferenceSchedulingViewModel.shared.description.value
descriptionLabel.removeConstraints().alignUnder(view: leftColumn,withMargin: form_margin).alignUnder(view: rightColumn,withMargin: form_margin).matchParentSideBorders().done()
descriptionInput.removeConstraints().alignUnder(view: descriptionLabel,withMargin: form_margin).matchParentSideBorders().height(description_height).alignParentBottom(withMargin: form_margin*2).done()
if (ConferenceSchedulingViewModel.shared.description.value == nil || ConferenceSchedulingViewModel.shared.description.value!.count == 0) {
descriptionLabel.height(0).done()
descriptionInput.height(0).done()
}
// Wrap form
scheduleForm.removeConstraints().matchParentSideBorders().wrapContentY().done()
createButton.addSidePadding() createButton.addSidePadding()
super.viewWillAppear(animated) super.viewWillAppear(animated)
} }
@ -233,6 +251,7 @@ import SVProgressHUD
func goBackParticipantsListSelection() { func goBackParticipantsListSelection() {
let view: ChatConversationCreateView = VIEW(ChatConversationCreateView.compositeViewDescription()) let view: ChatConversationCreateView = VIEW(ChatConversationCreateView.compositeViewDescription())
view.unfragmentCompositeDescription()
let addresses = ConferenceSchedulingViewModel.shared.selectedAddresses.value!.map { (address) in String(address.asStringUriOnly()) } let addresses = ConferenceSchedulingViewModel.shared.selectedAddresses.value!.map { (address) in String(address.asStringUriOnly()) }
view.tableController.contactsGroup = (addresses as NSArray).mutableCopy() as? NSMutableArray view.tableController.contactsGroup = (addresses as NSArray).mutableCopy() as? NSMutableArray
view.tableController.notFirstTime = true view.tableController.notFirstTime = true

View file

@ -21,6 +21,7 @@
import UIKit import UIKit
import Foundation import Foundation
import linphonesw import linphonesw
import IQKeyboardManager
@objc class ConferenceSchedulingView: BackNextNavigationView, UICompositeViewDelegate { @objc class ConferenceSchedulingView: BackNextNavigationView, UICompositeViewDelegate {
@ -29,17 +30,19 @@ import linphonesw
static func compositeViewDescription() -> UICompositeViewDescription! { return compositeDescription } static func compositeViewDescription() -> UICompositeViewDescription! { return compositeDescription }
func compositeViewDescription() -> UICompositeViewDescription! { return type(of: self).compositeDescription } func compositeViewDescription() -> UICompositeViewDescription! { return type(of: self).compositeDescription }
let datePicker = StyledDatePicker(liveValue: ConferenceSchedulingViewModel.shared.scheduledDateTime,pickerMode: .date) let datePicker = StyledDatePicker(liveValue: ConferenceSchedulingViewModel.shared.scheduledDate,pickerMode: .date)
let timeZoneValue = StyledValuePicker(liveIndex: ConferenceSchedulingViewModel.shared.scheduledTimeZone,options: ConferenceSchedulingViewModel.timeZones.map({ (tzd: TimeZoneData) -> String in tzd.descWithOffset()})) let timeZoneValue = StyledValuePicker(liveIndex: ConferenceSchedulingViewModel.shared.scheduledTimeZone,options: ConferenceSchedulingViewModel.timeZones.map({ (tzd: TimeZoneData) -> String in tzd.descWithOffset()}))
let durationValue = StyledValuePicker(liveIndex: ConferenceSchedulingViewModel.shared.scheduledDuration,options: ConferenceSchedulingViewModel.durationList.map({ (duration: Duration) -> String in duration.display})) let durationValue = StyledValuePicker(liveIndex: ConferenceSchedulingViewModel.shared.scheduledDuration,options: ConferenceSchedulingViewModel.durationList.map({ (duration: Duration) -> String in duration.display}))
let timePicker = StyledDatePicker(liveValue: ConferenceSchedulingViewModel.shared.scheduledDateTime,pickerMode: .time) let timePicker = StyledDatePicker(liveValue: ConferenceSchedulingViewModel.shared.scheduledTime,pickerMode: .time)
let descriptionInput = StyledTextView(VoipTheme.conference_scheduling_font, placeHolder:VoipTexts.conference_schedule_description_hint,liveValue: ConferenceSchedulingViewModel.shared.description) let descriptionInput = StyledTextView(VoipTheme.conference_scheduling_font, placeHolder:VoipTexts.conference_schedule_description_hint,liveValue: ConferenceSchedulingViewModel.shared.description)
let subjectInput = StyledTextView(VoipTheme.conference_scheduling_font, placeHolder:VoipTexts.conference_schedule_subject_hint, liveValue: ConferenceSchedulingViewModel.shared.subject,maxLines:1)
override func viewDidLoad() { override func viewDidLoad() {
super.viewDidLoad( super.viewDidLoad(
backAction: { backAction: {
ConferenceSchedulingViewModel.shared.reset()
PhoneMainView.instance().popView(self.compositeViewDescription()) PhoneMainView.instance().popView(self.compositeViewDescription())
},nextAction: { },nextAction: {
self.gotoParticipantsListSelection() self.gotoParticipantsListSelection()
@ -52,13 +55,11 @@ import linphonesw
contentView.addSubview(subjectLabel) contentView.addSubview(subjectLabel)
subjectLabel.alignParentLeft(withMargin: form_margin).alignParentTop().done() subjectLabel.alignParentLeft(withMargin: form_margin).alignParentTop().done()
let subjectInput = StyledTextView(VoipTheme.conference_scheduling_font, placeHolder:VoipTexts.conference_schedule_subject_hint, liveValue: ConferenceSchedulingViewModel.shared.subject,maxLines:1)
contentView.addSubview(subjectInput) contentView.addSubview(subjectInput)
subjectInput.alignUnder(view: subjectLabel,withMargin: form_margin).matchParentSideBorders(insetedByDx: form_margin).height(form_input_height).done() subjectInput.alignUnder(view: subjectLabel,withMargin: form_margin).matchParentSideBorders(insetedByDx: form_margin).height(form_input_height).done()
let schedulingStack = UIStackView() let schedulingStack = UIStackView()
schedulingStack.axis = .vertical schedulingStack.axis = .vertical
schedulingStack.backgroundColor = VoipTheme.voipFormBackgroundColor.get()
contentView.addSubview(schedulingStack) contentView.addSubview(schedulingStack)
schedulingStack.alignUnder(view: subjectInput,withMargin: form_margin).matchParentSideBorders(insetedByDx: form_margin).done() schedulingStack.alignUnder(view: subjectInput,withMargin: form_margin).matchParentSideBorders(insetedByDx: form_margin).done()
@ -132,19 +133,25 @@ import linphonesw
descriptionInput.textContainer.maximumNumberOfLines = 5 descriptionInput.textContainer.maximumNumberOfLines = 5
descriptionInput.textContainer.lineBreakMode = .byWordWrapping descriptionInput.textContainer.lineBreakMode = .byWordWrapping
scheduleForm.addSubview(descriptionInput) scheduleForm.addSubview(descriptionInput)
descriptionInput.alignUnder(view: descriptionLabel,withMargin: form_margin).matchParentSideBorders(insetedByDx: form_margin).height(description_height).alignParentBottom(withMargin: form_margin*2).done() descriptionInput.alignUnder(view: descriptionLabel,withMargin: 2*form_margin).matchParentSideBorders(insetedByDx: form_margin).height(description_height).done()
scheduleForm.wrapContentY().done()
// Sending methods // Sending methods
let viaChatSwitch = StyledCheckBox(liveValue: ConferenceSchedulingViewModel.shared.sendInviteViaChat)
contentView.addSubview(viaChatSwitch) let viaChatView = UIView()
viaChatSwitch.alignParentLeft(withMargin: form_margin).alignUnder(view: schedulingStack,withMargin: 2*form_margin).done() scheduleForm.addSubview(viaChatView)
viaChatView.alignUnder(view: descriptionInput,withMargin: form_margin).matchParentSideBorders(insetedByDx: form_margin).alignParentBottom(withMargin: form_margin*4).done()
let viaChatSwitch = StyledCheckBox()
viaChatSwitch.liveValue = ConferenceSchedulingViewModel.shared.sendInviteViaChat
viaChatView.addSubview(viaChatSwitch)
viaChatSwitch.alignParentLeft().done()
let viaChatLabel = StyledLabel(VoipTheme.conference_scheduling_font, VoipTexts.conference_schedule_send_invite_chat) let viaChatLabel = StyledLabel(VoipTheme.conference_scheduling_font, VoipTexts.conference_schedule_send_invite_chat)
contentView.addSubview(viaChatLabel) viaChatView.addSubview(viaChatLabel)
viaChatLabel.toRightOf(viaChatSwitch,withLeftMargin: form_margin).alignUnder(view: schedulingStack,withMargin: 2*form_margin).alignHorizontalCenterWith(viaChatSwitch).done() viaChatLabel.toRightOf(viaChatSwitch,withLeftMargin: form_margin).alignHorizontalCenterWith(viaChatSwitch).done()
viaChatView.wrapContentY().done()
/* Hidden as in Android 9.6.2022 /* Hidden as in Android 9.6.2022
let viaMailSwitch = StyledCheckBox(liveValue: ConferenceSchedulingViewModel.shared.sendInviteViaEmail) let viaMailSwitch = StyledCheckBox(liveValue: ConferenceSchedulingViewModel.shared.sendInviteViaEmail)
@ -183,34 +190,51 @@ import linphonesw
let mandatoryLabel = StyledLabel(VoipTheme.conference_scheduling_font, VoipTexts.conference_schedule_mandatory_field) let mandatoryLabel = StyledLabel(VoipTheme.conference_scheduling_font, VoipTexts.conference_schedule_mandatory_field)
mandatoryLabel.addIndicatorIcon(iconName: "voip_mandatory", trailing: false) mandatoryLabel.addIndicatorIcon(iconName: "voip_mandatory", trailing: false)
contentView.addSubview(mandatoryLabel) contentView.addSubview(mandatoryLabel)
mandatoryLabel.alignUnder(view: viaChatSwitch,withMargin: 2*form_margin).centerX().matchParentSideBorders().done()
mandatoryLabel.textAlignment = .center mandatoryLabel.textAlignment = .center
mandatoryLabel.alignParentBottom().done() let lastView = UIView()
contentView.addSubview(lastView)
lastView.alignUnder(view: mandatoryLabel).alignParentBottom().done()
// Schedule for later observer // Schedule for later observer
ConferenceSchedulingViewModel.shared.scheduleForLater.readCurrentAndObserve { (forLater) in ConferenceSchedulingViewModel.shared.scheduleForLater.readCurrentAndObserve { (forLater) in
self.subjectInput.setPlaceHolder(phText: forLater == true ? VoipTexts.conference_schedule_subject_hint : VoipTexts.conference_group_call_subject_hint)
scheduleForm.isHidden = forLater != true scheduleForm.isHidden = forLater != true
super.titleLabel.text = forLater == true ? VoipTexts.conference_schedule_title : VoipTexts.conference_group_call_title super.titleLabel.text = forLater == true ? ConferenceSchedulingViewModel.shared.existingConfInfo.value != nil ? VoipTexts.conference_schedule_edit : VoipTexts.conference_schedule_title : VoipTexts.conference_group_call_title
viaChatSwitch.isHidden = forLater != true mandatoryLabel.removeConstraints().done()
viaChatLabel.isHidden = forLater != true mandatoryLabel.alignUnder(view: forLater == true ? scheduleForm : scheduleForLater,withMargin: 2*self.form_margin).centerX().matchParentSideBorders().done()
} }
ConferenceSchedulingViewModel.shared.existingConfInfo.readCurrentAndObserve { (confInfo) in
super.titleLabel.text = ConferenceSchedulingViewModel.shared.scheduleForLater.value == true ? ConferenceSchedulingViewModel.shared.existingConfInfo.value != nil ? VoipTexts.conference_schedule_edit : VoipTexts.conference_schedule_title : VoipTexts.conference_group_call_title
}
UIDeviceBridge.displayModeSwitched.readCurrentAndObserve { _ in
self.view.backgroundColor = VoipTheme.voipBackgroundBWColor.get()
schedulingStack.backgroundColor = VoipTheme.voipFormBackgroundColor.get()
}
} }
override func viewWillAppear(_ animated: Bool) { override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated) super.viewWillAppear(animated)
datePicker.liveValue = ConferenceSchedulingViewModel.shared.scheduledDateTime datePicker.liveValue = ConferenceSchedulingViewModel.shared.scheduledDate
timeZoneValue.setIndex(index: ConferenceSchedulingViewModel.shared.scheduledTimeZone.value!) timeZoneValue.setIndex(index: ConferenceSchedulingViewModel.shared.scheduledTimeZone.value!)
durationValue.setIndex(index: ConferenceSchedulingViewModel.shared.scheduledDuration.value!) durationValue.setIndex(index: ConferenceSchedulingViewModel.shared.scheduledDuration.value!)
timePicker.liveValue = ConferenceSchedulingViewModel.shared.scheduledDateTime timePicker.liveValue = ConferenceSchedulingViewModel.shared.scheduledTime
descriptionInput.text = ConferenceSchedulingViewModel.shared.description.value descriptionInput.text = ConferenceSchedulingViewModel.shared.description.value
IQKeyboardManager.shared().isEnabled = true
} }
override func viewWillDisappear(_ animated: Bool) {
IQKeyboardManager.shared().isEnabled = false
super.viewWillDisappear(animated)
}
func gotoParticipantsListSelection() { func gotoParticipantsListSelection() {
let view: ChatConversationCreateView = self.VIEW(ChatConversationCreateView.compositeViewDescription()); let view: ChatConversationCreateView = self.VIEW(ChatConversationCreateView.compositeViewDescription())
view.unfragmentCompositeDescription()
let addresses = ConferenceSchedulingViewModel.shared.selectedAddresses.value!.map { (address) in String(address.asStringUriOnly()) } let addresses = ConferenceSchedulingViewModel.shared.selectedAddresses.value!.map { (address) in String(address.asStringUriOnly()) }
view.tableController.contactsGroup = (addresses as NSArray).mutableCopy() as? NSMutableArray view.tableController.contactsGroup = (addresses as NSArray).mutableCopy() as? NSMutableArray
view.isForEditing = false view.isForEditing = false

View file

@ -22,16 +22,17 @@ import UIKit
import linphonesw import linphonesw
@objc class ConferenceWaitingRoomFragment: UIViewController, UICompositeViewDelegate { // Replaces CallView @objc class ConferenceWaitingRoomView: UIViewController, UICompositeViewDelegate { // Replaces CallView
// Layout constants // Layout constants
let common_margin = 17.0 let common_margin = 17.0
let switch_camera_button_size = 50 let switch_camera_button_size = 35
let switch_camera_button_margins = 7.0 let switch_camera_button_margins = 7.0
let content_inset = 12.0 let content_inset = 12.0
let button_spacing = 15.0 let button_spacing = 15.0
let center_view_corner_radius = 20.0 let center_view_corner_radius = 20.0
let button_width = 150 let button_width = 150
let layout_picker_inset = UIEdgeInsets(top: 8, left: 8, bottom: 8, right: 8)
var audioRoutesView : AudioRoutesView? = nil var audioRoutesView : AudioRoutesView? = nil
@ -47,15 +48,19 @@ import linphonesw
var conferenceUrl : String? = nil var conferenceUrl : String? = nil
let conferenceSubject = MutableLiveData<String>() let conferenceSubject = MutableLiveData<String>()
let controlsView = ControlsView(showVideo: true, controlsViewModel: ConferenceWaitingRoomViewModel.sharedModel)
var layoutPicker : CallControlButton? = nil
let layoutPickerView = ConferenceLayoutPickerView(orientation: UIDevice.current.orientation)
static let compositeDescription = UICompositeViewDescription(ConferenceWaitingRoomFragment.self, statusBar: StatusBarView.self, tabBar: nil, sideMenu: nil, fullscreen: false, isLeftFragment: false,fragmentWith: nil)
static let compositeDescription = UICompositeViewDescription(ConferenceWaitingRoomView.self, statusBar: StatusBarView.self, tabBar: nil, sideMenu: SideMenuView.self, fullscreen: false, isLeftFragment: false,fragmentWith: nil)
static func compositeViewDescription() -> UICompositeViewDescription! { return compositeDescription } static func compositeViewDescription() -> UICompositeViewDescription! { return compositeDescription }
func compositeViewDescription() -> UICompositeViewDescription! { return type(of: self).compositeDescription } func compositeViewDescription() -> UICompositeViewDescription! { return type(of: self).compositeDescription }
override func viewDidLoad() { override func viewDidLoad() {
super.viewDidLoad() super.viewDidLoad()
view.backgroundColor = VoipTheme.voipBackgroundColor.get()
view.addSubview(subject) view.addSubview(subject)
subject.centerX().alignParentTop(withMargin: common_margin).done() subject.centerX().alignParentTop(withMargin: common_margin).done()
@ -64,16 +69,15 @@ import linphonesw
} }
// Controls // Controls
let controlsView = ControlsView(showVideo: true, controlsViewModel: ConferenceWaitingRoomViewModel.sharedModel)
view.addSubview(controlsView) view.addSubview(controlsView)
controlsView.alignParentBottom(withMargin:SharedLayoutConstants.buttons_bottom_margin).centerX().done() controlsView.alignParentBottom(withMargin:SharedLayoutConstants.buttons_bottom_margin).centerX().done()
// Layoout picker // Layoout picker
let layoutPicker = CallControlButton(imageInset : UIEdgeInsets(top: 8, left: 8, bottom: 8, right: 8),buttonTheme: VoipTheme.conf_waiting_room_layout_picker, onClickAction: { layoutPicker = CallControlButton(imageInset : layout_picker_inset,buttonTheme: VoipTheme.conf_waiting_room_layout_picker, onClickAction: {
ConferenceWaitingRoomViewModel.sharedModel.showLayoutPicker.value = ConferenceWaitingRoomViewModel.sharedModel.showLayoutPicker.value != true ConferenceWaitingRoomViewModel.sharedModel.showLayoutPicker.value = ConferenceWaitingRoomViewModel.sharedModel.showLayoutPicker.value != true
}) })
view.addSubview(layoutPicker) view.addSubview(layoutPicker!)
layoutPicker.alignParentBottom(withMargin:SharedLayoutConstants.buttons_bottom_margin).alignParentRight(withMargin:SharedLayoutConstants.buttons_bottom_margin).done() layoutPicker!.alignParentBottom(withMargin:SharedLayoutConstants.buttons_bottom_margin).alignParentRight(withMargin:SharedLayoutConstants.buttons_bottom_margin).done()
ConferenceWaitingRoomViewModel.sharedModel.joinLayout.readCurrentAndObserve { layout in ConferenceWaitingRoomViewModel.sharedModel.joinLayout.readCurrentAndObserve { layout in
var icon = "" var icon = ""
@ -85,18 +89,28 @@ import linphonesw
ConferenceWaitingRoomViewModel.sharedModel.isVideoEnabled.value = false ConferenceWaitingRoomViewModel.sharedModel.isVideoEnabled.value = false
break break
} }
layoutPicker.applyTintedIcons(tintedIcons: [UIButton.State.normal.rawValue : TintableIcon(name: icon ,tintColor: LightDarkColor(.white,.white))]) self.layoutPicker?.applyTintedIcons(tintedIcons: [UIButton.State.normal.rawValue : TintableIcon(name: icon ,tintColor: LightDarkColor(.white,.white))])
}
ConferenceWaitingRoomViewModel.sharedModel.isVideoEnabled.observe { video in
if (video == true && ConferenceWaitingRoomViewModel.sharedModel.joinLayout.value == .AudioOnly) {
ConferenceWaitingRoomViewModel.sharedModel.joinLayout.value = .ActiveSpeaker
}
} }
let layoutPickerView = ConferenceLayoutPickerView()
view.addSubview(layoutPickerView) view.addSubview(layoutPickerView)
layoutPickerView.alignAbove(view:layoutPicker,withMargin:button_spacing).alignVerticalCenterWith(layoutPicker).done()
ConferenceWaitingRoomViewModel.sharedModel.showLayoutPicker.readCurrentAndObserve { show in ConferenceWaitingRoomViewModel.sharedModel.showLayoutPicker.readCurrentAndObserve { show in
layoutPicker.isSelected = show == true self.layoutPicker?.isSelected = show == true
layoutPickerView.isHidden = show != true self.layoutPickerView.isHidden = show != true
if (show == true) { if (show == true) {
self.view.bringSubviewToFront(layoutPickerView) self.view.bringSubviewToFront(self.layoutPickerView)
}
}
self.view.onClick{
if (ConferenceWaitingRoomViewModel.sharedModel.showLayoutPicker.value == true) {
ConferenceWaitingRoomViewModel.sharedModel.showLayoutPicker.value = false
} }
} }
@ -131,16 +145,16 @@ import linphonesw
self.start.isEnabled = joining != true self.start.isEnabled = joining != true
//self.localVideo.isHidden = joining == true (UX question as video window goes black by the core, better black or hidden ?) //self.localVideo.isHidden = joining == true (UX question as video window goes black by the core, better black or hidden ?)
self.noVideoLabel.isHidden = joining == true self.noVideoLabel.isHidden = joining == true
layoutPicker.isHidden = joining == true self.layoutPicker?.isHidden = joining == true
if (joining == true) { if (joining == true) {
self.view.addSubview(self.conferenceJoinSpinner) self.view.addSubview(self.conferenceJoinSpinner)
self.conferenceJoinSpinner.square(IncomingOutgoingCommonView.spinner_size).center().done() self.conferenceJoinSpinner.square(AbstractIncomingOutgoingCallView.spinner_size).center().done()
self.conferenceJoinSpinner.startRotation() self.conferenceJoinSpinner.startRotation()
controlsView.isHidden = true self.controlsView.isHidden = true
} else { } else {
self.conferenceJoinSpinner.stopRotation() self.conferenceJoinSpinner.stopRotation()
self.conferenceJoinSpinner.removeFromSuperview() self.conferenceJoinSpinner.removeFromSuperview()
controlsView.isHidden = false self.controlsView.isHidden = false
} }
} }
@ -151,9 +165,7 @@ import linphonesw
localVideo.contentMode = .scaleAspectFill localVideo.contentMode = .scaleAspectFill
localVideo.backgroundColor = .black localVideo.backgroundColor = .black
self.view.addSubview(localVideo) self.view.addSubview(localVideo)
localVideo.matchParentSideBorders(insetedByDx: content_inset).alignAbove(view:buttonsView,withMargin:SharedLayoutConstants.buttons_bottom_margin).alignUnder(view: subject,withMargin: common_margin).done()
localVideo.addSubview(switchCamera) localVideo.addSubview(switchCamera)
switchCamera.alignParentTop(withMargin: switch_camera_button_margins).alignParentRight(withMargin: switch_camera_button_margins).square(switch_camera_button_size).done()
switchCamera.contentMode = .scaleAspectFit switchCamera.contentMode = .scaleAspectFit
switchCamera.onClick { switchCamera.onClick {
Core.get().videoPreviewEnabled = false Core.get().videoPreviewEnabled = false
@ -182,7 +194,41 @@ import linphonesw
} }
audioRoutesView!.alignAbove(view:controlsView,withMargin:SharedLayoutConstants.buttons_bottom_margin).centerX().done() audioRoutesView!.alignAbove(view:controlsView,withMargin:SharedLayoutConstants.buttons_bottom_margin).centerX().done()
layoutRotatableElements()
UIDeviceBridge.displayModeSwitched.readCurrentAndObserve { _ in
self.view.backgroundColor = VoipTheme.voipBackgroundColor.get()
}
}
func layoutRotatableElements() {
layoutPickerView.removeConstraints().done()
localVideo.removeConstraints().done()
switchCamera.removeConstraints().done()
if ([.landscapeLeft, .landscapeRight].contains( UIDevice.current.orientation)) {
layoutPickerView.alignAbove(view:layoutPicker!,withMargin:button_spacing).alignVerticalCenterWith(layoutPicker!).done()
localVideo.matchParentSideBorders().alignParentBottom().alignParentTop().done()
localVideo.layer.cornerRadius = 0
switchCamera.alignParentTop(withMargin: switch_camera_button_margins).alignParentRight(withMargin: switch_camera_button_margins + (UIDevice.hasNotch() && UIDevice.current.orientation == .landscapeRight ? 30.0 : 0.0)).square(switch_camera_button_size).done()
} else {
layoutPickerView.alignAbove(view:layoutPicker!,withMargin:button_spacing).alignVerticalCenterWith(layoutPicker!).done()
localVideo.matchParentSideBorders(insetedByDx: content_inset).alignAbove(view:buttonsView,withMargin:SharedLayoutConstants.buttons_bottom_margin).alignUnder(view: subject,withMargin: common_margin).done()
localVideo.layer.cornerRadius = center_view_corner_radius
switchCamera.alignParentTop(withMargin: switch_camera_button_margins).alignParentRight(withMargin: switch_camera_button_margins).square(switch_camera_button_size).done()
}
view.sendSubviewToBack(localVideo)
}
override func didRotate(from fromInterfaceOrientation: UIInterfaceOrientation) {
super.didRotate(from: fromInterfaceOrientation)
self.layoutRotatableElements()
Core.get().videoPreviewEnabled = ConferenceWaitingRoomViewModel.sharedModel.isVideoEnabled.value == true
}
override func willRotate(to toInterfaceOrientation: UIInterfaceOrientation, duration: TimeInterval) {
Core.get().videoPreviewEnabled = false
} }
override func viewWillAppear(_ animated: Bool) { override func viewWillAppear(_ animated: Bool) {

View file

@ -20,19 +20,25 @@
import UIKit import UIKit
import Foundation import Foundation
import linphonesw import linphonesw
import EventKit
import EventKitUI
@objc class ICSBubbleView: UIView { @objc class ICSBubbleView: UIView, EKEventEditViewDelegate {
let corner_radius = 7.0 let corner_radius = 7.0
let border_width = 2.0 let border_width = 2.0
let rows_spacing = 6.0 let rows_spacing = 6.0
let inner_padding = 8.0 let inner_padding = 8.0
let forward_reply_title_height = 10.0
let indicator_y = 3.0 let indicator_y = 3.0
let share_size = 25 let share_size = 25
let join_share_width = 150.0 let join_share_width = 150.0
let inviteTitle = StyledLabel(VoipTheme.conference_invite_title_font, VoipTexts.conference_invite_title) let inviteTitle = StyledLabel(VoipTheme.conference_invite_title_font, VoipTexts.conference_invite_title)
let inviteCancelled = StyledLabel(VoipTheme.conference_cancelled_title_font, VoipTexts.conference_cancel_title)
let inviteUpdated = StyledLabel(VoipTheme.conference_updated_title_font, VoipTexts.conference_update_title)
let subject = StyledLabel(VoipTheme.conference_invite_subject_font) let subject = StyledLabel(VoipTheme.conference_invite_subject_font)
let participants = StyledLabel(VoipTheme.conference_invite_desc_font) let participants = StyledLabel(VoipTheme.conference_invite_desc_font)
let date = StyledLabel(VoipTheme.conference_invite_desc_font) let date = StyledLabel(VoipTheme.conference_invite_desc_font)
@ -42,40 +48,46 @@ import linphonesw
let joinShare = UIStackView() let joinShare = UIStackView()
let join = FormButton(title:VoipTexts.conference_invite_join.uppercased(), backgroundStateColors: VoipTheme.button_green_background) let join = FormButton(title:VoipTexts.conference_invite_join.uppercased(), backgroundStateColors: VoipTheme.button_green_background)
let share = UIImageView(image:UIImage(named:"voip_export")?.tinted(with: VoipTheme.primaryTextColor.get())) let share = UIImageView(image:UIImage(named:"voip_export")?.tinted(with: VoipTheme.primaryTextColor.get()))
var icsFile : String? = nil
var conferenceData: ScheduledConferenceData? = nil { var conferenceData: ScheduledConferenceData? = nil {
didSet { didSet {
if let data = conferenceData { if let data = conferenceData {
subject.text = data.subject.value subject.text = data.subject.value
participants.text = VoipTexts.conference_invite_participants_count.replacingOccurrences(of: "%d", with: String(data.conferenceInfo.participants.count+1)) participants.text = VoipTexts.conference_invite_participants_count.replacingOccurrences(of: "%d", with: String(data.conferenceInfo.participants.count+1))
participants.addIndicatorIcon(iconName: "conference_schedule_participants_default",padding : 0.0, y: -indicator_y, trailing: false) participants.addIndicatorIcon(iconName: "conference_schedule_participants_default",padding : 0.0, y: -indicator_y, trailing: false)
date.text = " "+TimestampUtils.dateToString(date: data.rawDate) date.text = TimestampUtils.dateToString(date: data.rawDate)
date.addIndicatorIcon(iconName: "conference_schedule_calendar_default", padding: 0.0, y:-indicator_y, trailing:false) date.addIndicatorIcon(iconName: "conference_schedule_calendar_default", padding: 0.0, y:-indicator_y, trailing:false)
timeDuration.text = " \(data.time.value) ( \(data.duration.value) )" timeDuration.text = "\(data.time.value)" + (data.duration.value != nil ? " ( \(data.duration.value) )" : "")
timeDuration.addIndicatorIcon(iconName: "conference_schedule_time_default",padding : 0.0, y: -indicator_y, trailing: false) timeDuration.addIndicatorIcon(iconName: "conference_schedule_time_default",padding : 0.0, y: -indicator_y, trailing: false)
descriptionTitle.isHidden = data.description.value == nil || data.description.value!.count == 0 descriptionTitle.isHidden = data.description.value == nil || data.description.value!.count == 0
descriptionValue.isHidden = descriptionTitle.isHidden descriptionValue.isHidden = descriptionTitle.isHidden
descriptionValue.text = data.description.value descriptionValue.text = data.description.value
inviteTitle.isHidden = [.Cancelled,.Updated].contains(data.conferenceInfo.state)
inviteCancelled.isHidden = data.conferenceInfo.state != .Cancelled
inviteUpdated.isHidden = data.conferenceInfo.state != .Updated
join.isEnabled = data.isConferenceCancelled.value != true
} }
} }
} }
init() { init() {
super.init(frame:.zero) super.init(frame:.zero)
layer.cornerRadius = corner_radius layer.cornerRadius = corner_radius
clipsToBounds = true clipsToBounds = true
backgroundColor = VoipTheme.voip_light_gray UIDeviceBridge.displayModeSwitched.readCurrentAndObserve { _ in
self.backgroundColor = VoipTheme.chatBubbleBGColor.get()
}
let rows = UIStackView() let rows = UIStackView()
rows.axis = .vertical rows.axis = .vertical
rows.spacing = rows_spacing rows.spacing = rows_spacing
addSubview(rows) addSubview(rows)
rows.addArrangedSubview(inviteTitle) rows.addArrangedSubview(inviteTitle)
rows.addArrangedSubview(inviteCancelled)
rows.addArrangedSubview(inviteUpdated)
rows.addArrangedSubview(subject) rows.addArrangedSubview(subject)
rows.addArrangedSubview(participants) rows.addArrangedSubview(participants)
rows.addArrangedSubview(date) rows.addArrangedSubview(date)
@ -83,6 +95,8 @@ import linphonesw
rows.addArrangedSubview(descriptionTitle) rows.addArrangedSubview(descriptionTitle)
rows.addArrangedSubview(descriptionValue) rows.addArrangedSubview(descriptionValue)
descriptionValue.numberOfLines = 5
addSubview(joinShare) addSubview(joinShare)
joinShare.axis = .horizontal joinShare.axis = .horizontal
@ -92,19 +106,48 @@ import linphonesw
joinShare.addArrangedSubview(join) joinShare.addArrangedSubview(join)
rows.matchParentSideBorders(insetedByDx: inner_padding).alignParentTop(withMargin: inner_padding).done() rows.matchParentSideBorders(insetedByDx: inner_padding).alignParentTop(withMargin: inner_padding).done()
joinShare.alignParentBottom(withMargin: inner_padding).width(join_share_width).alignParentRight(withMargin: inner_padding).done() joinShare.alignParentBottom(withMargin: inner_padding).width(join_share_width).alignParentRight(withMargin: inner_padding).done()
join.onClick { join.onClick {
let view : ConferenceWaitingRoomFragment = self.VIEW(ConferenceWaitingRoomFragment.compositeViewDescription()) let view : ConferenceWaitingRoomView = self.VIEW(ConferenceWaitingRoomView.compositeViewDescription())
PhoneMainView.instance().changeCurrentView(view.compositeViewDescription()) PhoneMainView.instance().changeCurrentView(view.compositeViewDescription())
view.setDetails(subject: (self.conferenceData?.subject.value)!, url: (self.conferenceData?.address.value)!) view.setDetails(subject: (self.conferenceData?.subject.value)!, url: (self.conferenceData?.address.value)!)
} }
share.onClick { share.onClick {
let ics = URL(string: "file://"+self.icsFile!) let eventStore = EKEventStore()
UIApplication.shared.open(ics!) eventStore.requestAccess( to: EKEntityType.event, completion:{(granted, error) in
DispatchQueue.main.async {
if (granted) && (error == nil) {
let event = EKEvent(eventStore: eventStore)
event.title = self.conferenceData?.subject.value
event.startDate = self.conferenceData?.rawDate
if let duration = self.conferenceData?.conferenceInfo.duration, duration > 0 {
event.endDate = event.startDate.addingTimeInterval(TimeInterval(duration*60))
} else {
event.endDate = event.startDate.addingTimeInterval(TimeInterval(3600))
}
event.calendar = eventStore.defaultCalendarForNewEvents
if let description = self.conferenceData?.description.value, description.count > 0 {
event.notes = description + "\n\n"
}
event.notes = (event.notes != nil ? event.notes! : "") + "\(VoipTexts.call_action_participants_list):\n\(self.conferenceData?.participantsExpanded.value)"
if let urlString = self.conferenceData?.conferenceInfo.uri?.asStringUriOnly() {
event.url = URL(string:urlString)
}
let addController = EKEventEditViewController()
addController.event = event
addController.eventStore = eventStore
PhoneMainView.instance().present(addController, animated: false)
addController.editViewDelegate = self;
} else {
VoipDialog.toast(message: VoipTexts.conference_unable_to_share_via_calendar)
}
}
})
} }
} }
required init?(coder: NSCoder) { required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented") fatalError("init(coder:) has not been implemented")
} }
@ -115,7 +158,6 @@ import linphonesw
if (content.isIcalendar) { if (content.isIcalendar) {
if let conferenceInfo = try? Factory.Instance.createConferenceInfoFromIcalendarContent(content: content) { if let conferenceInfo = try? Factory.Instance.createConferenceInfoFromIcalendarContent(content: content) {
self.conferenceData = ScheduledConferenceData(conferenceInfo: conferenceInfo) self.conferenceData = ScheduledConferenceData(conferenceInfo: conferenceInfo)
self.icsFile = content.filePath
} }
} }
} }
@ -132,7 +174,76 @@ import linphonesw
} }
@objc func setLayoutConstraints(view:UIView) { @objc func setLayoutConstraints(view:UIView) {
matchDimensionsWith(view: view, insetedByDx: inner_padding).done() matchBordersWith(view: view, insetedByDx: inner_padding).done()
}
@objc func updateTopLayoutConstraints(view:UIView, replyOrForward: Bool) {
updateTopBorderWith(view: view, inset: inner_padding + (replyOrForward ? forward_reply_title_height : 0.0)).done()
}
func eventEditViewController(_ controller: EKEventEditViewController, didCompleteWith action: EKEventEditViewAction) {
controller.dismiss(animated: true, completion: nil)
}
@objc static func getSubjectFromContent(cmessage: OpaquePointer) -> String {
let message = ChatMessage.getSwiftObject(cObject: cmessage)
var subject = ""
message.contents.forEach { content in
if (content.isIcalendar) {
if let conferenceInfo = try? Factory.Instance.createConferenceInfoFromIcalendarContent(content: content) {
subject = conferenceInfo.subject
}
}
}
return subject
}
@objc static func getConferenceInfo(cmessage: OpaquePointer) -> OpaquePointer? {
let message = ChatMessage.getSwiftObject(cObject: cmessage)
var result : OpaquePointer? = nil
message.contents.forEach { content in
if (content.isIcalendar) {
if let conferenceInfo = try? Factory.Instance.createConferenceInfoFromIcalendarContent(content: content) {
result = conferenceInfo.getCobject
}
}
}
return result
}
@objc static func getConferenceSummary(cmessage: OpaquePointer) -> String? {
let message = ChatMessage.getSwiftObject(cObject: cmessage)
var subject:String? = nil
message.contents.forEach { content in
if (content.isIcalendar) {
if let conferenceInfo = try? Factory.Instance.createConferenceInfoFromIcalendarContent(content: content) {
subject = conferenceInfo.state == .New ? VoipTexts.conference_invite_title + conferenceInfo.subject :
conferenceInfo.state == .Updated ? VoipTexts.conference_update_title + conferenceInfo.subject :
VoipTexts.conference_cancel_title + conferenceInfo.subject
}
}
}
return subject
}
@objc static func getDescriptionHeightFromContent(cmessage: OpaquePointer) -> CGFloat {
let message = ChatMessage.getSwiftObject(cObject: cmessage)
var height = 0.0
message.contents.forEach { content in
if (content.isIcalendar) {
if let conferenceInfo = try? Factory.Instance.createConferenceInfoFromIcalendarContent(content: content) {
let description = NSString(string: conferenceInfo.description)
if (description.length > 0) {
let dummyTitle = StyledLabel(VoipTheme.conference_invite_desc_title_font, VoipTexts.conference_description_title)
let dummyLabel = StyledLabel(VoipTheme.conference_invite_desc_font)
let rect = CGSize(width: CGFloat(CONFERENCE_INVITATION_WIDTH-80), height: CGFloat.greatestFiniteMagnitude)
height = dummyTitle.intrinsicContentSize.height + description.boundingRect(with: rect, options: [.usesLineFragmentOrigin, .usesFontLeading], attributes: [NSAttributedString.Key.font: dummyLabel.font!], context: nil).height
}
}
}
}
return height
} }
} }

View file

@ -27,18 +27,20 @@ class ScheduledConferencesCell: UITableViewCell {
let corner_radius = 7.0 let corner_radius = 7.0
let border_width = 2.0 let border_width = 2.0
static let button_size = 40 static let button_size = 40
let delete_checkbox_margin = 5
let clockIcon = UIImageView(image: UIImage(named: "conference_schedule_time_default")) let clockIcon = UIImageView()
let timeDuration = StyledLabel(VoipTheme.conference_invite_desc_font) let timeDuration = StyledLabel(VoipTheme.conference_invite_desc_font)
let organiser = StyledLabel(VoipTheme.conference_invite_desc_font) let organiser = StyledLabel(VoipTheme.conference_invite_desc_font)
let subject = StyledLabel(VoipTheme.conference_invite_subject_font) let subject = StyledLabel(VoipTheme.conference_list_subject_font)
let participantsIcon = UIImageView(image: UIImage(named: "conference_schedule_participants_default")) let cancelledLabel = StyledLabel(VoipTheme.conference_cancelled_title_font)
let participantsIcon = UIImageView()
let participants = StyledLabel(VoipTheme.conference_invite_desc_font) let participants = StyledLabel(VoipTheme.conference_invite_desc_font)
let infoConf = UIButton() let infoConf = UIButton()
let descriptionTitle = StyledLabel(VoipTheme.conference_invite_desc_font, VoipTexts.conference_description_title) let descriptionTitle = StyledLabel(VoipTheme.conference_list_address_desc_font, VoipTexts.conference_description_title)
let descriptionValue = StyledLabel(VoipTheme.conference_invite_desc_font) let descriptionValue = StyledLabel(VoipTheme.conference_list_address_desc_font)
let urlTitle = StyledLabel(VoipTheme.conference_invite_desc_font, VoipTexts.conference_schedule_address_title) let urlTitle = StyledLabel(VoipTheme.conference_list_address_desc_font, VoipTexts.conference_schedule_address_title)
let urlValue = StyledLabel(VoipTheme.conference_scheduling_font) let urlValue = StyledLabel(VoipTheme.conference_scheduling_font)
let copyLink = CallControlButton(width:button_size,height:button_size,buttonTheme: VoipTheme.scheduled_conference_action("voip_copy")) let copyLink = CallControlButton(width:button_size,height:button_size,buttonTheme: VoipTheme.scheduled_conference_action("voip_copy"))
let joinConf = FormButton(title:VoipTexts.conference_invite_join.uppercased(), backgroundStateColors: VoipTheme.button_green_background) let joinConf = FormButton(title:VoipTexts.conference_invite_join.uppercased(), backgroundStateColors: VoipTheme.button_green_background)
@ -47,17 +49,29 @@ class ScheduledConferencesCell: UITableViewCell {
var owningTableView : UITableView? = nil var owningTableView : UITableView? = nil
let joinEditDelete = UIStackView() let joinEditDelete = UIStackView()
let expandedRows = UIStackView() let expandedRows = UIStackView()
let selectionCheckBox = StyledCheckBox()
let myContentView = UIView()
var conferenceData: ScheduledConferenceData? = nil { var conferenceData: ScheduledConferenceData? = nil {
didSet { didSet {
if let data = conferenceData { if let data = conferenceData {
timeDuration.text = "\(data.time.value)"+(data.duration.value != nil ? " ( \(data.duration.value) )" : "") timeDuration.text = "\(data.time.value)"+(data.duration.value != nil ? " (\(data.duration.value))" : "")
organiser.text = VoipTexts.conference_schedule_organizer+data.organizer.value! organiser.text = VoipTexts.conference_schedule_organizer+data.organizer.value!
subject.text = data.subject.value! subject.text = data.subject.value!
cancelledLabel.text = data.isConferenceCancelled.value == true ? ( data.canEdit.value == true ? VoipTexts.conference_scheduled_cancelled_by_me: VoipTexts.conference_scheduled_cancelled_by_organizer) : nil
cancelledLabel.isHidden = data.isConferenceCancelled.value != true
descriptionValue.text = data.description.value! descriptionValue.text = data.description.value!
urlValue.text = data.address.value! urlValue.text = data.address.value!
self.joinConf.isHidden = data.isConferenceCancelled.value == true
self.editConf.isHidden = data.canEdit.value != true || data.isConferenceCancelled.value == true
self.urlTitle.isHidden = data.isConferenceCancelled.value == true
self.urlValue.isHidden = data.isConferenceCancelled.value == true
self.copyLink.isHidden = data.isConferenceCancelled.value == true
data.expanded.readCurrentAndObserve { expanded in data.expanded.readCurrentAndObserve { expanded in
self.contentView.layer.borderWidth = expanded == true ? 2.0 : 0.0 self.myContentView.backgroundColor =
data.conferenceInfo.state == .Cancelled ? VoipTheme.voip_conference_cancelled_bg_color :
data.isFinished ? VoipTheme.backgroundColor3.get() : VoipTheme.backgroundColor4.get()
self.myContentView.layer.borderWidth = expanded == true ? 2.0 : 0.0
self.descriptionTitle.isHidden = expanded != true || self.descriptionValue.text?.count == 0 self.descriptionTitle.isHidden = expanded != true || self.descriptionValue.text?.count == 0
self.descriptionValue.isHidden = expanded != true || self.descriptionValue.text?.count == 0 self.descriptionValue.isHidden = expanded != true || self.descriptionValue.text?.count == 0
self.infoConf.isSelected = expanded == true self.infoConf.isSelected = expanded == true
@ -66,18 +80,18 @@ class ScheduledConferencesCell: UITableViewCell {
self.expandedRows.isHidden = expanded != true self.expandedRows.isHidden = expanded != true
self.joinEditDelete.isHidden = expanded != true self.joinEditDelete.isHidden = expanded != true
if let myAddress = Core.get().defaultAccount?.params?.identityAddress { if let myAddress = Core.get().defaultAccount?.params?.identityAddress {
self.editConf.isHidden = expanded != true || data.conferenceInfo.organizer?.weakEqual(address2: myAddress) != true self.editConf.isHidden = expanded != true || data.conferenceInfo.organizer?.weakEqual(address2: myAddress) != true || data.conferenceInfo.state == .Cancelled
} else { } else {
self.editConf.isHidden = true self.editConf.isHidden = true
} }
self.participants.removeConstraints().alignUnder(view: self.subject,withMargin: 15).toRightOf(self.participantsIcon,withLeftMargin:10).toRightOf(self.participantsIcon,withLeftMargin:10).toLeftOf(self.infoConf,withRightMargin: 15).done() self.participants.removeConstraints().alignUnder(view: self.subject,withMargin: 10).toRightOf(self.participantsIcon,withLeftMargin:10).toRightOf(self.participantsIcon,withLeftMargin:10).toLeftOf(self.infoConf,withRightMargin: 15).done()
self.joinEditDelete.removeConstraints().alignUnder(view: self.expandedRows,withMargin: 15).alignParentRight(withMargin: 10).done() self.joinEditDelete.removeConstraints().alignUnder(view: self.expandedRows,withMargin: 10).alignParentRight(withMargin: 10).done()
if (expanded == true) { if (expanded == true) {
self.joinEditDelete.alignParentBottom(withMargin: 10).done() self.joinEditDelete.alignParentBottom(withMargin: 10).done()
} else { } else {
self.participants.alignParentBottom(withMargin: 10).done() self.participants.alignParentBottom(withMargin: 10).done()
} }
self.selectionCheckBox.liveValue = data.selectedForDeletion
} }
} }
} }
@ -86,46 +100,55 @@ class ScheduledConferencesCell: UITableViewCell {
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier) super.init(style: style, reuseIdentifier: reuseIdentifier)
contentView.layer.cornerRadius = corner_radius contentView.addSubview(myContentView)
contentView.clipsToBounds = true contentView.backgroundColor = .clear
contentView.backgroundColor = VoipTheme.header_background_color backgroundColor = .clear
contentView.layer.borderColor = VoipTheme.primary_color.cgColor myContentView.layer.cornerRadius = corner_radius
myContentView.clipsToBounds = true
myContentView.backgroundColor = VoipTheme.header_background_color
myContentView.layer.borderColor = VoipTheme.primary_color.cgColor
myContentView.matchParentDimmensions(insetedBy: UIEdgeInsets(top: 5,left: 0,bottom: 5,right: 0)).done()
myContentView.addSubview(clockIcon)
clockIcon.alignParentTop(withMargin: 10).square(20).alignParentLeft(withMargin: 10).done()
myContentView.addSubview(timeDuration)
timeDuration.alignParentTop(withMargin: 10).toRightOf(clockIcon,withLeftMargin:10).alignHorizontalCenterWith(clockIcon).done()
myContentView.addSubview(organiser)
organiser.alignParentTop(withMargin: 10).toRightOf(timeDuration, withLeftMargin:10).alignParentRight(withMargin:10).alignHorizontalCenterWith(clockIcon).done()
contentView.addSubview(clockIcon) let subjectCancel = UIStackView()
clockIcon.alignParentTop(withMargin: 15).square(15).alignParentLeft(withMargin: 10).done() subjectCancel.axis = .vertical
myContentView.addSubview(subjectCancel)
contentView.addSubview(timeDuration) subjectCancel.alignUnder(view: timeDuration,withMargin: 10).matchParentSideBorders(insetedByDx: 10.0).done()
timeDuration.alignParentTop(withMargin: 15).toRightOf(clockIcon,withLeftMargin:10).alignHorizontalCenterWith(clockIcon).done()
contentView.addSubview(organiser) subjectCancel.addArrangedSubview(cancelledLabel)
organiser.alignParentTop(withMargin: 15).toRightOf(timeDuration, withLeftMargin:10).alignParentRight(withMargin:10).alignHorizontalCenterWith(clockIcon).done() subjectCancel.addArrangedSubview(subject)
contentView.addSubview(subject) myContentView.addSubview(participantsIcon)
subject.alignUnder(view: timeDuration,withMargin: 15).alignParentLeft(withMargin: 10).done() participantsIcon.alignUnder(view: subject,withMargin: 5).square(25).alignParentLeft(withMargin: 10).done()
contentView.addSubview(participantsIcon)
participantsIcon.alignUnder(view: subject,withMargin: 15).square(15).alignParentLeft(withMargin: 10).done()
//infoConf.onClick { //infoConf.onClick {
contentView.onClick { contentView.onClick {
self.conferenceData?.toggleExpand() self.conferenceData?.toggleExpand()
self.owningTableView?.reloadData() self.owningTableView?.reloadData()
} }
contentView.addSubview(infoConf) myContentView.addSubview(infoConf)
infoConf.imageView?.contentMode = .scaleAspectFit infoConf.imageView?.contentMode = .scaleAspectFit
infoConf.alignUnder(view: subject,withMargin: 15).square(30).alignParentRight(withMargin: 10).alignHorizontalCenterWith(participantsIcon).done() infoConf.alignUnder(view: subject,withMargin: 5).square(25).alignParentRight(withMargin: 10).done()
infoConf.applyTintedIcons(tintedIcons: VoipTheme.conference_info_button) infoConf.applyTintedIcons(tintedIcons: VoipTheme.conference_info_button)
contentView.addSubview(participants) myContentView.addSubview(participants)
participants.alignUnder(view: subject,withMargin: 15).toRightOf(participantsIcon,withLeftMargin:10).toRightOf(participantsIcon,withLeftMargin:10).toLeftOf(infoConf,withRightMargin: 15).done() participants.alignUnder(view: subject,withMargin: 10).toRightOf(participantsIcon,withLeftMargin:10).toRightOf(participantsIcon,withLeftMargin:10).toLeftOf(infoConf,withRightMargin: 15).done()
expandedRows.axis = .vertical expandedRows.axis = .vertical
expandedRows.spacing = 10 expandedRows.spacing = 10
contentView.addSubview(expandedRows) myContentView.addSubview(expandedRows)
expandedRows.alignUnder(view: participants,withMargin: 15).matchParentSideBorders(insetedByDx:10).done() expandedRows.alignUnder(view: participants,withMargin: 15).matchParentSideBorders(insetedByDx:10).done()
expandedRows.addArrangedSubview(descriptionTitle) expandedRows.addArrangedSubview(descriptionTitle)
expandedRows.addArrangedSubview(descriptionValue) expandedRows.addArrangedSubview(descriptionValue)
@ -146,15 +169,15 @@ class ScheduledConferencesCell: UITableViewCell {
joinEditDelete.axis = .horizontal joinEditDelete.axis = .horizontal
joinEditDelete.spacing = 10 joinEditDelete.spacing = 10
joinEditDelete.distribution = .equalSpacing joinEditDelete.distribution = .equalSpacing
contentView.addSubview(joinEditDelete) myContentView.addSubview(joinEditDelete)
joinEditDelete.alignUnder(view: expandedRows,withMargin: 15).alignParentRight(withMargin: 10).done() joinEditDelete.alignUnder(view: expandedRows,withMargin: 10).alignParentRight(withMargin: 10).done()
joinEditDelete.addArrangedSubview(joinConf) joinEditDelete.addArrangedSubview(joinConf)
joinConf.width(150).done() joinConf.width(150).done()
joinConf.onClick { joinConf.onClick {
let view : ConferenceWaitingRoomFragment = self.VIEW(ConferenceWaitingRoomFragment.compositeViewDescription()) let view : ConferenceWaitingRoomView = self.VIEW(ConferenceWaitingRoomView.compositeViewDescription())
PhoneMainView.instance().changeCurrentView(view.compositeViewDescription()) PhoneMainView.instance().changeCurrentView(view.compositeViewDescription())
view.setDetails(subject: (self.conferenceData?.subject.value)!, url: (self.conferenceData?.address.value)!) view.setDetails(subject: (self.conferenceData?.subject.value)!, url: (self.conferenceData?.address.value)!)
} }
@ -166,9 +189,10 @@ class ScheduledConferencesCell: UITableViewCell {
VoipDialog.toast(message: VoipTexts.conference_edit_error) VoipDialog.toast(message: VoipTexts.conference_edit_error)
return return
} }
let infoDate = Date(timeIntervalSince1970: Double(confData.conferenceInfo.dateTime))
ConferenceSchedulingViewModel.shared.reset() ConferenceSchedulingViewModel.shared.reset()
ConferenceSchedulingViewModel.shared.scheduledDateTime.value = infoDate let seconds = confData.conferenceInfo.dateTime % 86400
ConferenceSchedulingViewModel.shared.scheduledDate.value = Date(timeIntervalSince1970:TimeInterval(confData.conferenceInfo.dateTime - seconds))
ConferenceSchedulingViewModel.shared.scheduledTime.value = Date(timeIntervalSince1970:TimeInterval(seconds))
ConferenceSchedulingViewModel.shared.description.value = confData.description.value ConferenceSchedulingViewModel.shared.description.value = confData.description.value
ConferenceSchedulingViewModel.shared.subject.value = confData.subject.value ConferenceSchedulingViewModel.shared.subject.value = confData.subject.value
ConferenceSchedulingViewModel.shared.scheduledDuration.value = ConferenceSchedulingViewModel.durationList.firstIndex(where: {$0.value == confData.conferenceInfo.duration}) ConferenceSchedulingViewModel.shared.scheduledDuration.value = ConferenceSchedulingViewModel.durationList.firstIndex(where: {$0.value == confData.conferenceInfo.duration})
@ -177,7 +201,7 @@ class ScheduledConferencesCell: UITableViewCell {
confData.conferenceInfo.participants.forEach { confData.conferenceInfo.participants.forEach {
ConferenceSchedulingViewModel.shared.selectedAddresses.value?.append($0) ConferenceSchedulingViewModel.shared.selectedAddresses.value?.append($0)
} }
ConferenceSchedulingViewModel.shared.existingConfInfo = confData.conferenceInfo ConferenceSchedulingViewModel.shared.existingConfInfo.value = confData.conferenceInfo
// TOODO TimeZone (as Android 14.6.2022) ConferenceSchedulingViewModel.shared.scheduledTimeZone.value = self.conferenceData?.timezone // TOODO TimeZone (as Android 14.6.2022) ConferenceSchedulingViewModel.shared.scheduledTimeZone.value = self.conferenceData?.timezone
let view : ConferenceSchedulingView = self.VIEW(ConferenceSchedulingView.compositeViewDescription()) let view : ConferenceSchedulingView = self.VIEW(ConferenceSchedulingView.compositeViewDescription())
PhoneMainView.instance().changeCurrentView(view.compositeViewDescription()) PhoneMainView.instance().changeCurrentView(view.compositeViewDescription())
@ -185,16 +209,35 @@ class ScheduledConferencesCell: UITableViewCell {
joinEditDelete.addArrangedSubview(deleteConf) joinEditDelete.addArrangedSubview(deleteConf)
deleteConf.onClick { deleteConf.onClick {
let delete = ButtonAttributes(text:VoipTexts.conference_info_confirm_removal_delete, action: { self.askConfirmationTodeleteEntry()
Core.get().deleteConferenceInformation(conferenceInfo: self.conferenceData!.conferenceInfo)
ScheduledConferencesViewModel.shared.computeConferenceInfoList()
self.owningTableView?.reloadData()
VoipDialog.toast(message: VoipTexts.conference_info_removed)
}, isDestructive:false)
let cancel = ButtonAttributes(text:VoipTexts.cancel, action: {}, isDestructive:true)
VoipDialog(message:VoipTexts.conference_info_confirm_removal, givenButtons: [cancel,delete]).show()
} }
myContentView.addSubview(selectionCheckBox)
selectionCheckBox.alignParentRight(withMargin: delete_checkbox_margin).alignUnder(view:organiser, withMargin: delete_checkbox_margin).done()
ScheduledConferencesViewModel.shared.editionEnabled.readCurrentAndObserve { editing in
self.selectionCheckBox.isHidden = editing != true
}
onLongClick {
ScheduledConferencesViewModel.shared.editionEnabled.value = true
}
UIDeviceBridge.displayModeSwitched.readCurrentAndObserve { _ in
self.clockIcon.image = UIImage(named: "conference_schedule_time_default")?.tinted(with: VoipTheme.voipDrawableColor.get())
self.participantsIcon.image = UIImage(named: "conference_schedule_participants_default")?.tinted(with: VoipTheme.voipDrawableColor.get())
}
}
func askConfirmationTodeleteEntry() {
let delete = ButtonAttributes(text:VoipTexts.conference_info_confirm_removal_delete, action: {
self.deleteEntry()
VoipDialog.toast(message: VoipTexts.conference_info_removed)
}, isDestructive:false)
let cancel = ButtonAttributes(text:VoipTexts.cancel, action: {}, isDestructive:true)
VoipDialog(message:VoipTexts.conference_info_confirm_removal, givenButtons: [cancel,delete]).show()
}
func deleteEntry() {
self.conferenceData?.deleteConference()
ScheduledConferencesViewModel.shared.computeConferenceInfoList()
self.owningTableView?.reloadData()
} }
required init?(coder: NSCoder) { required init?(coder: NSCoder) {

View file

@ -23,9 +23,12 @@ import Foundation
import linphonesw import linphonesw
@objc class ScheduledConferencesView: BackNextNavigationView, UICompositeViewDelegate, UITableViewDataSource, UITableViewDelegate { @objc class ScheduledConferencesView: BackNextNavigationView, UICompositeViewDelegate, UITableViewDataSource, UITableViewDelegate {
let conferenceListView = UITableView() let conferenceListView = UITableView()
let noConference = StyledLabel(VoipTheme.empty_list_font,VoipTexts.conference_no_schedule) let noConference = StyledLabel(VoipTheme.empty_list_font,VoipTexts.conference_no_schedule)
let filters = UIStackView()
let selectAllButton = CallControlButton(buttonTheme:VoipTheme.nav_button("deselect_all"))
let separator = UIView()
static let compositeDescription = UICompositeViewDescription(ScheduledConferencesView.self, statusBar: StatusBarView.self, tabBar: nil, sideMenu: SideMenuView.self, fullscreen: false, isLeftFragment: false,fragmentWith: nil) static let compositeDescription = UICompositeViewDescription(ScheduledConferencesView.self, statusBar: StatusBarView.self, tabBar: nil, sideMenu: SideMenuView.self, fullscreen: false, isLeftFragment: false,fragmentWith: nil)
static func compositeViewDescription() -> UICompositeViewDescription! { return compositeDescription } static func compositeViewDescription() -> UICompositeViewDescription! { return compositeDescription }
@ -35,17 +38,63 @@ import linphonesw
super.viewDidLoad( super.viewDidLoad(
backAction: { backAction: {
PhoneMainView.instance().popView(self.compositeViewDescription()) if (ScheduledConferencesViewModel.shared.editionEnabled.value == true) {
ScheduledConferencesViewModel.shared.editionEnabled.value = false
} else {
PhoneMainView.instance().popView(self.compositeViewDescription())
}
},nextAction: { },nextAction: {
ConferenceSchedulingViewModel.shared.reset() if (ScheduledConferencesViewModel.shared.editionEnabled.value == true) {
PhoneMainView.instance().changeCurrentView(ConferenceSchedulingView.compositeDescription) self.deleteSelection()
} else {
ConferenceSchedulingViewModel.shared.reset()
PhoneMainView.instance().changeCurrentView(ConferenceSchedulingView.compositeDescription)
}
}, },
nextActionEnableCondition: MutableLiveData(), nextActionEnableCondition: MutableLiveData(),
title:VoipTexts.conference_scheduled) title:VoipTexts.conference_scheduled)
super.titleLabel.applyStyle(VoipTheme.navigation_header_font)
// Select all
selectAllButton.setImage(UIImage(named: "deselect_all"), for: .selected)
selectAllButton.setImage(UIImage(named: "select_all_default"), for: .normal)
topBar.addSubview(selectAllButton)
selectAllButton.toLeftOf(nextButton,withRightMargin: CGFloat(side_buttons_margin)).matchParentHeight().done()
// Filter buttons
let showTerminated = getFilterButton(title: VoipTexts.conference_scheduled_terminated_filter)
showTerminated.onClick {
ScheduledConferencesViewModel.shared.showTerminated.value = true
}
filters.addArrangedSubview(showTerminated)
let showScheduled = getFilterButton(title: VoipTexts.conference_scheduled_future_filter)
showScheduled.onClick {
ScheduledConferencesViewModel.shared.showTerminated.value = false
}
filters.addArrangedSubview(showScheduled)
ScheduledConferencesViewModel.shared.showTerminated.readCurrentAndObserve { it in
showTerminated.isSelected = it == true
showScheduled.isSelected = it != true
self.noConference.text = it != true ? VoipTexts.conference_no_schedule : VoipTexts.conference_no_terminated_schedule
ScheduledConferencesViewModel.shared.computeConferenceInfoList()
self.conferenceListView.reloadData()
self.noConference.isHidden = !ScheduledConferencesViewModel.shared.daySplitted.isEmpty
}
self.view.addSubview(filters)
filters.spacing = 10
filters.alignParentLeft(withMargin: 10).alignUnder(view: super.topBar,withMargin: self.form_margin).done()
super.nextButton.applyTintedIcons(tintedIcons: VoipTheme.conference_create_button) self.view.addSubview(separator)
separator.matchParentSideBorders().height(1).alignUnder(view: filters,withMargin: self.form_margin).done()
// Conference list
self.view.addSubview(conferenceListView) self.view.addSubview(conferenceListView)
conferenceListView.alignUnder(view: filters).done()
conferenceListView.isScrollEnabled = true conferenceListView.isScrollEnabled = true
conferenceListView.dataSource = self conferenceListView.dataSource = self
conferenceListView.delegate = self conferenceListView.delegate = self
@ -56,11 +105,53 @@ import linphonesw
conferenceListView.allowsFocus = false conferenceListView.allowsFocus = false
} }
conferenceListView.separatorStyle = .singleLine conferenceListView.separatorStyle = .singleLine
conferenceListView.separatorColor = .white conferenceListView.backgroundColor = .clear
view.addSubview(noConference) view.addSubview(noConference)
noConference.center().done() noConference.center().done()
ScheduledConferencesViewModel.shared.editionEnabled.readCurrentAndObserve { editing in
if (editing == true) {
self.selectAllButton.isSelected = false
self.selectAllButton.isHidden = false
super.nextButton.applyTintedIcons(tintedIcons: VoipTheme.generic_delete_button)
super.backButton.applyTintedIcons(tintedIcons: VoipTheme.generic_cancel)
self.nextButton.isEnabled = ScheduledConferencesViewModel.shared.conferences.value?.filter{$0.selectedForDeletion.value == true}.count ?? 0 > 0
} else {
self.selectAllButton.isHidden = true
ScheduledConferencesViewModel.shared.conferences.value?.forEach {$0.selectedForDeletion.value = false}
super.nextButton.applyTintedIcons(tintedIcons: VoipTheme.conference_create_button)
super.backButton.applyTintedIcons(tintedIcons: VoipTheme.generic_back)
self.nextButton.isEnabled = true
}
}
self.selectAllButton.onClick {
let selectIt = !self.selectAllButton.isSelected
ScheduledConferencesViewModel.shared.conferences.value?.forEach {$0.selectedForDeletion.value = selectIt}
}
UIDeviceBridge.displayModeSwitched.readCurrentAndObserve { _ in
self.view.backgroundColor = VoipTheme.voipBackgroundBWColor.get()
self.separator.backgroundColor = VoipTheme.separatorColor.get()
self.conferenceListView.separatorColor = .clear
self.conferenceListView.reloadData()
}
}
func getFilterButton(title:String) -> UIButton {
let filter_button_height = 35.0
let button = ButtonWithStateBackgrounds(backgroundStateColors: VoipTheme.button_conference_list_filter)
button.setTitle(title, for: .normal)
button.setTitleColor(.black, for: .normal)
button.setTitleColor(VoipTheme.primary_color, for: .selected)
button.height(filter_button_height).done()
button.layer.cornerRadius = filter_button_height / 2
button.clipsToBounds = true
button.applyTitleStyle(VoipTheme.conf_list_filter_button_font)
button.width(0).done()
button.addSidePadding()
return button
} }
@ -69,9 +160,10 @@ import linphonesw
super.viewWillAppear(animated) super.viewWillAppear(animated)
self.conferenceListView.reloadData() self.conferenceListView.reloadData()
self.conferenceListView.removeConstraints().done() self.conferenceListView.removeConstraints().done()
self.conferenceListView.matchParentSideBorders(insetedByDx: 10).alignUnder(view: super.topBar,withMargin: self.form_margin).alignParentBottom().done() self.conferenceListView.matchParentSideBorders(insetedByDx: 10).alignUnder(view: separator).alignParentBottom().done()
noConference.isHidden = !ScheduledConferencesViewModel.shared.daySplitted.isEmpty noConference.isHidden = !ScheduledConferencesViewModel.shared.daySplitted.isEmpty
super.nextButton.isEnabled = Core.get().defaultAccount != nil super.nextButton.isEnabled = Core.get().defaultAccount != nil
ScheduledConferencesViewModel.shared.editionEnabled.value = false
} }
// TableView datasource delegate // TableView datasource delegate
@ -99,12 +191,7 @@ import linphonesw
} }
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
let daysArray = Array(ScheduledConferencesViewModel.shared.daySplitted.keys.sorted().reversed()) return UITableView.automaticDimension
let day = daysArray[indexPath.section]
guard let data = ScheduledConferencesViewModel.shared.daySplitted[day]?[indexPath.row] else {
return UITableView.automaticDimension
}
return data.expanded.value! ? UITableView.automaticDimension : 100
} }
@ -117,8 +204,38 @@ import linphonesw
} }
cell.conferenceData = data cell.conferenceData = data
cell.owningTableView = tableView cell.owningTableView = tableView
data.selectedForDeletion.readCurrentAndObserve { selected in
let selectedCount = ScheduledConferencesViewModel.shared.conferences.value?.filter{$0.selectedForDeletion.value == true}.count ?? 0
let totalCount = ScheduledConferencesViewModel.shared.conferences.value?.count ?? 0
self.nextButton.isEnabled = ScheduledConferencesViewModel.shared.editionEnabled.value == false || selectedCount > 0
self.selectAllButton.isSelected = selectedCount == totalCount
}
return cell return cell
} }
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
if editingStyle == .delete {
let cell = tableView.cellForRow(at: indexPath) as! ScheduledConferencesCell
cell.askConfirmationTodeleteEntry()
}
}
func deleteSelection () {
let selectedCount = ScheduledConferencesViewModel.shared.conferences.value?.filter{$0.selectedForDeletion.value == true}.count ?? 0
let delete = ButtonAttributes(text:VoipTexts.conference_info_confirm_removal_delete, action: {
ScheduledConferencesViewModel.shared.conferences.value?.forEach {
$0.deleteConference()
}
ScheduledConferencesViewModel.shared.computeConferenceInfoList()
self.conferenceListView.reloadData()
VoipDialog.toast(message: selectedCount == 1 ? VoipTexts.conference_info_removed : VoipTexts.conference_infos_removed)
ScheduledConferencesViewModel.shared.editionEnabled.value = false
}, isDestructive:false)
let cancel = ButtonAttributes(text:VoipTexts.cancel, action: {}, isDestructive:true)
VoipDialog(message:selectedCount == 1 ? VoipTexts.conference_info_confirm_removal : VoipTexts.conference_infos_confirm_removal, givenButtons: [cancel,delete]).show()
}
} }

View file

@ -0,0 +1,54 @@
/*
* Copyright (c) 2010-2020 Belledonne Communications SARL.
*
* This file is part of linphone-iphone
*
* 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, see <http://www.gnu.org/licenses/>.
*/
import UIKit
import Photos
import linphonesw
@objc class SnapkitBridge: NSObject {
@objc static func matchParentDimensions(view:UIView) {
view.matchParentDimmensions().done()
}
@objc static func matchParentDimensions(view:UIView,leftInset:CGFloat) {
view.matchParentDimmensions(insetedBy: UIEdgeInsets(top: 0, left: leftInset, bottom: 0, right: 0)).done()
}
@objc static func matchParentDimensions(view:UIView,topInset:CGFloat) {
view.matchParentDimmensions(insetedBy: UIEdgeInsets(top: topInset, left: 0, bottom: 0, right: 0)).done()
}
@objc static func height(view:UIView,heiht:Int) {
view.height(heiht).done()
}
@objc static func square(view:UIView,size:Int) {
view.square(size).done()
}
@objc static func alignParentLeft(view:UIView) {
view.alignParentLeft().done()
}
@objc static func centerY(view:UIView) {
view.centerY().done()
}
}

View file

@ -22,9 +22,9 @@ import SnapKit
import UIKit import UIKit
extension UIButton { extension UIButton {
func addSidePadding(p:CGFloat = 10) { func addSidePadding(p:CGFloat = 10) { // Requires a width to be set prior to this ! SnapKit does not support updateOrCreate.
if let w = titleLabel?.textWidth { if let w = titleLabel?.textWidth {
width(w+2*p).done() updateWidth(w+2*p).done()
} }
} }

View file

@ -48,10 +48,32 @@ extension UIDevice {
} }
static func notchHeight() -> CGFloat { static func notchHeight() -> CGFloat {
guard #available(iOS 11.0, *), let topPadding = UIApplication.shared.keyWindow?.safeAreaInsets.top else { guard #available(iOS 11.0, *), let topPadding = UIApplication.shared.keyWindow?.safeAreaInsets.top, let sidePadding = UIApplication.shared.keyWindow?.safeAreaInsets.left else {
return 0 return 0
} }
return topPadding return [.landscapeRight,.landscapeLeft].contains(UIDevice.current.orientation) ? sidePadding : topPadding
}
static func switchedDisplayMode() -> Bool {
let displayMode = UserDefaults.standard.string(forKey: "displayMode")
if #available(iOS 13.0, *) {
if UITraitCollection.current.userInterfaceStyle == .light {
UserDefaults.standard.set("light", forKey: "displayMode")
} else {
UserDefaults.standard.set("dark", forKey: "displayMode")
}
}
return displayMode != nil && displayMode != UserDefaults.standard.string(forKey: "displayMode")
} }
} }
@objc class UIDeviceBridge : NSObject {
static let displayModeSwitched = MutableLiveData<Bool>()
@objc static func switchedDisplayMode() -> Bool {
return UIDevice.switchedDisplayMode()
}
@objc static func notifyDisplayModeSwitch() {
displayModeSwitched.notifyValue()
}
}

View file

@ -29,4 +29,10 @@ extension UILabel {
let labelSize = myText.boundingRect(with: rect, options: .usesLineFragmentOrigin, attributes: [NSAttributedString.Key.font: myFont], context: nil) let labelSize = myText.boundingRect(with: rect, options: .usesLineFragmentOrigin, attributes: [NSAttributedString.Key.font: myFont], context: nil)
return ceil(labelSize.width) return ceil(labelSize.width)
} }
func addSidePadding(p:CGFloat = 5.0) {
if let w = textWidth {
width(w+2*p).done()
}
}
} }

View file

@ -39,6 +39,14 @@ extension UIView {
return self return self
} }
func squareMax(_ size:Int) -> UIView {
snp.makeConstraints { (make) in
make.height.lessThanOrEqualTo(size).priority(.high)
make.width.equalTo(snp.height).priority(.high)
}
return self
}
func makeHeightMatchWidth() -> UIView { func makeHeightMatchWidth() -> UIView {
snp.makeConstraints { (make) in snp.makeConstraints { (make) in
@ -47,6 +55,13 @@ extension UIView {
return self return self
} }
func makeWidthMatchHeight() -> UIView {
snp.makeConstraints { (make) in
make.width.equalTo(snp.height)
}
return self
}
func size(w:CGFloat,h:CGFloat) -> UIView { func size(w:CGFloat,h:CGFloat) -> UIView {
snp.makeConstraints { (make) in snp.makeConstraints { (make) in
make.width.equalTo(w) make.width.equalTo(w)
@ -55,6 +70,14 @@ extension UIView {
return self return self
} }
func updateSize(w:CGFloat,h:CGFloat) -> UIView {
snp.updateConstraints { (make) in
make.width.equalTo(w)
make.height.equalTo(h)
}
return self
}
func height(_ h:CGFloat) -> UIView { func height(_ h:CGFloat) -> UIView {
snp.makeConstraints { (make) in snp.makeConstraints { (make) in
make.height.equalTo(h) make.height.equalTo(h)
@ -73,6 +96,13 @@ extension UIView {
return self return self
} }
func updateWidth(_ h:CGFloat) -> UIView {
snp.updateConstraints { (make) in
make.width.equalTo(h)
}
return self
}
func width(_ h:Int) -> UIView { func width(_ h:Int) -> UIView {
return width(CGFloat(h)) return width(CGFloat(h))
} }
@ -122,7 +152,17 @@ extension UIView {
return self return self
} }
func matchDimensionsWith(view:UIView, insetedByDx:CGFloat = 0) -> UIView { func matchParentDimmensions(insetedBy:UIEdgeInsets) -> UIView {
snp.makeConstraints { (make) in
make.left.equalToSuperview().offset(insetedBy.left)
make.top.equalToSuperview().offset(insetedBy.top)
make.right.equalToSuperview().offset(-insetedBy.right)
make.bottom.equalToSuperview().offset(-insetedBy.bottom)
}
return self
}
func matchBordersWith(view:UIView, insetedByDx:CGFloat = 0) -> UIView {
snp.makeConstraints { (make) in snp.makeConstraints { (make) in
make.left.top.equalTo(view).offset(insetedByDx) make.left.top.equalTo(view).offset(insetedByDx)
make.right.bottom.equalTo(view).offset(-insetedByDx) make.right.bottom.equalTo(view).offset(-insetedByDx)
@ -130,6 +170,13 @@ extension UIView {
return self return self
} }
func updateTopBorderWith(view:UIView, inset:CGFloat = 0) -> UIView {
snp.updateConstraints { (make) in
make.top.equalTo(view).offset(inset)
}
return self
}
func matchParentEdges() -> UIView { func matchParentEdges() -> UIView {
snp.makeConstraints { (make) in snp.makeConstraints { (make) in
make.edges.equalToSuperview() make.edges.equalToSuperview()
@ -222,10 +269,21 @@ extension UIView {
return self return self
} }
func updateAlignParentBottom(withMargin:CGFloat = 0.0) -> UIView {
snp.updateConstraints { (make) in
make.bottom.equalToSuperview().offset(-withMargin)
}
return self
}
func alignParentBottom(withMargin:Int) -> UIView { func alignParentBottom(withMargin:Int) -> UIView {
return alignParentBottom(withMargin:CGFloat(withMargin)) return alignParentBottom(withMargin:CGFloat(withMargin))
} }
func updateAlignParentBottom(withMargin:Int) -> UIView {
return updateAlignParentBottom(withMargin:CGFloat(withMargin))
}
func alignAbove(view:UIView, withMargin:CGFloat = 0.0) -> UIView { func alignAbove(view:UIView, withMargin:CGFloat = 0.0) -> UIView {
snp.makeConstraints { (make) in snp.makeConstraints { (make) in
make.bottom.equalTo(view.snp.top).offset(-withMargin) make.bottom.equalTo(view.snp.top).offset(-withMargin)
@ -258,6 +316,13 @@ extension UIView {
return self return self
} }
func updateAlignParentLeft(withMargin:CGFloat = 0.0) -> UIView {
snp.updateConstraints { (make) in
make.left.equalToSuperview().offset(withMargin)
}
return self
}
func alignParentLeft(withMargin:Int) -> UIView { func alignParentLeft(withMargin:Int) -> UIView {
return alignParentLeft(withMargin:CGFloat(withMargin)) return alignParentLeft(withMargin:CGFloat(withMargin))
} }
@ -269,10 +334,23 @@ extension UIView {
return self return self
} }
func updateAlignParentRight(withMargin:CGFloat = 0) -> UIView {
snp.updateConstraints { (make) in
make.right.equalToSuperview().offset(-withMargin)
}
return self
}
func alignParentRight(withMargin:CGFloat) -> UIView { func alignParentRight(withMargin:CGFloat) -> UIView {
return alignParentRight(withMargin:Int(withMargin)) return alignParentRight(withMargin:Int(withMargin))
} }
func alignRightWith(_ view:UIView) -> UIView {
snp.makeConstraints { (make) in
make.right.equalTo(view.snp.right)
}
return self
}
func toRightOf(_ view:UIView, withLeftMargin:Int = 0) -> UIView { func toRightOf(_ view:UIView, withLeftMargin:Int = 0) -> UIView {
snp.makeConstraints { (make) in snp.makeConstraints { (make) in
@ -363,12 +441,28 @@ extension UIView {
return self return self
} }
func wrapContent(inset:UIEdgeInsets) -> UIView {
subviews.first?.snp.makeConstraints({ make in
make.left.equalToSuperview().offset(inset.left)
})
subviews.last?.snp.makeConstraints({ make in
make.right.equalToSuperview().offset(-inset.right)
})
subviews.first?.snp.makeConstraints({ make in
make.top.equalToSuperview().offset(inset.top)
})
subviews.last?.snp.makeConstraints({ make in
make.bottom.equalToSuperview().offset(-inset.bottom)
})
return self
}
func done() { func done() {
// to avoid the unused variable warning // to avoid the unused variable warning
} }
// Onclick // Single click
class TapGestureRecognizer: UITapGestureRecognizer { class TapGestureRecognizer: UITapGestureRecognizer {
var action : (()->Void)? = nil var action : (()->Void)? = nil
} }
@ -387,6 +481,22 @@ extension UIView {
sender.action!() sender.action!()
} }
// Long click
class LongPressGestureRecognizer: UILongPressGestureRecognizer {
var action : (()->Void)? = nil
}
func onLongClick(action : @escaping ()->Void ){
let tap = LongPressGestureRecognizer(target: self , action: #selector(self.handleLongClick(_:)))
tap.action = action
tap.cancelsTouchesInView = false
self.addGestureRecognizer(tap)
self.isUserInteractionEnabled = true
}
@objc func handleLongClick(_ sender: LongPressGestureRecognizer) {
sender.action!()
}
func VIEW<T>( _ desc: UICompositeViewDescription) -> T{ func VIEW<T>( _ desc: UICompositeViewDescription) -> T{
return PhoneMainView.instance().mainViewController.getCachedController(desc.name) as! T return PhoneMainView.instance().mainViewController.getCachedController(desc.name) as! T
} }

View file

@ -23,15 +23,16 @@ import linphonesw
extension Address { extension Address {
func initials() -> String? { func initials() -> String? {
var initials = initials(displayName: addressBookEnhancedDisplayName()) var initials = Address.initials(displayName: addressBookEnhancedDisplayName())
if (initials == nil || initials!.isEmpty) { if (initials == nil || initials!.isEmpty) {
initials = String(username.prefix(1)) initials = String(username.prefix(1))
} }
return initials return initials
} }
private func initials(displayName: String?) -> String? { // Basic ImproveMe static func initials(displayName: String?) -> String? { // Basic ImproveMe
return displayName?.components(separatedBy: " ") let separator = displayName?.contains(" ") == true ? " " : "."
return displayName?.components(separatedBy: separator)
.reduce("") { .reduce("") {
($0.isEmpty ? "" : "\($0.first?.uppercased() ?? "")") + ($0.isEmpty ? "" : "\($0.first?.uppercased() ?? "")") +
($1.isEmpty ? "" : "\($1.first?.uppercased() ?? "")") ($1.isEmpty ? "" : "\($1.first?.uppercased() ?? "")")

View file

@ -39,10 +39,7 @@ extension Call {
extension Call : CustomStringConvertible { extension Call : CustomStringConvertible {
public var description: String { public var description: String {
if let callId = callLog?.callId { return "<Call-ID: \(callLog?.callId ?? "pending") pointer:\(Unmanaged.passUnretained(self).toOpaque()) is conference:\(conference != nil) >"
return "<Call-ID: \(callId)>"
}
return "<Raw pointer:\(Unmanaged.passUnretained(self).toOpaque())>"
} }
} }

View file

@ -0,0 +1,139 @@
/*
* Copyright (c) 2010-2020 Belledonne Communications SARL.
*
* This file is part of linphone-iphone
*
* 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, see <http://www.gnu.org/licenses/>.
*/
import UIKit
import linphonesw
@objc class FileUtil: NSObject {
public class func bundleFilePath(_ file: NSString) -> String? {
return Bundle.main.path(forResource: file.deletingPathExtension, ofType: file.pathExtension)
}
public class func bundleFilePathAsUrl(_ file: NSString) -> URL? {
if let bPath = bundleFilePath(file) {
return URL.init(fileURLWithPath: bPath)
}
return nil
}
public class func documentsDirectory() -> URL {
let paths = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
let documentsDirectory = paths[0]
return documentsDirectory
}
public class func libraryDirectory() -> URL {
let paths = FileManager.default.urls(for: .libraryDirectory, in: .userDomainMask)
let documentsDirectory = paths[0]
return documentsDirectory
}
public class func sharedContainerUrl(appGroupName:String) -> URL {
return FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: appGroupName)!
}
@objc public class func ensureDirectoryExists(path:String) {
if !FileManager.default.fileExists(atPath: path) {
do {
try FileManager.default.createDirectory(atPath: path, withIntermediateDirectories: true, attributes: nil)
} catch {
print(error)
}
}
}
public class func ensureFileExists(path:String) {
if !FileManager.default.fileExists(atPath: path) {
FileManager.default.createFile(atPath: path, contents: nil, attributes: nil)
}
}
public class func fileExists(path:String) -> Bool {
return FileManager.default.fileExists(atPath: path)
}
public class func fileExistsAndIsNotEmpty(path:String) -> Bool {
guard FileManager.default.fileExists(atPath: path) else {return false}
do {
let attribute = try FileManager.default.attributesOfItem(atPath: path)
if let size = attribute[FileAttributeKey.size] as? NSNumber {
return size.doubleValue > 0
} else {
return false
}
} catch {
print(error)
return false
}
}
public class func write(string:String, toPath:String) {
do {
try string.write(to: URL(fileURLWithPath:toPath), atomically: true, encoding: String.Encoding.utf8)
} catch {
print(error)
}
}
public class func delete(path:String) {
do {
try FileManager.default.removeItem(atPath: path)
print("FIle \(path) was removed")
} catch {
print("Error deleting file at path \(path) error is \(error)")
}
}
public class func mkdir(path:String) {
do {
try FileManager.default.createDirectory(atPath: path, withIntermediateDirectories: true, attributes: nil)
print("Dir \(path) was created")
} catch {
print("Error creating dir at path \(path) error is \(error)")
}
}
public class func copy(_ fromPath:String, _ toPath: String, overWrite:Bool) {
do {
if (overWrite && fileExists(path: toPath)) {
delete(path: toPath)
}
try FileManager.default.copyItem(at: URL(fileURLWithPath:fromPath), to: URL(fileURLWithPath:toPath))
} catch {
print(error)
}
}
// For debugging
public class func showListOfFilesInSharedDir(appGroupName:String) {
let fileManager = FileManager.default
do {
let fileURLs = try fileManager.contentsOfDirectory(at: FileUtil.sharedContainerUrl(appGroupName: appGroupName), includingPropertiesForKeys: nil)
fileURLs.forEach{print($0)}
} catch {
print("Error while enumerating files \(error.localizedDescription)")
}
}
}

View file

@ -192,7 +192,7 @@ extension ProviderDelegate: CXProviderDelegate {
if (UIApplication.shared.applicationState != .active) { if (UIApplication.shared.applicationState != .active) {
CallManager.instance().backgroundContextCall = call CallManager.instance().backgroundContextCall = call
CallManager.instance().backgroundContextCameraIsEnabled = call!.params?.videoEnabled ?? false CallManager.instance().backgroundContextCameraIsEnabled = call?.params?.videoEnabled == true || call?.callLog?.wasConference() == true
call?.cameraEnabled = false // Disable camera while app is not on foreground call?.cameraEnabled = false // Disable camera while app is not on foreground
} }
CallManager.instance().callkitAudioSessionActivated = false CallManager.instance().callkitAudioSessionActivated = false
@ -205,8 +205,10 @@ extension ProviderDelegate: CXProviderDelegate {
let uuid = action.callUUID let uuid = action.callUUID
let callId = callInfos[uuid]?.callId let callId = callInfos[uuid]?.callId
let call = CallManager.instance().callByCallId(callId: callId) let call = CallManager.instance().callByCallId(callId: callId)
action.fulfill()
if (call == nil) { if (call == nil) {
Log.directLog(BCTBX_LOG_ERROR, text: "CXSetHeldCallAction: no call !")
action.fail()
return return
} }
@ -215,33 +217,46 @@ extension ProviderDelegate: CXProviderDelegate {
try CallManager.instance().lc?.leaveConference() try CallManager.instance().lc?.leaveConference()
Log.directLog(BCTBX_LOG_DEBUG, text: "CallKit: call-id: [\(String(describing: callId))] leaving conference") Log.directLog(BCTBX_LOG_DEBUG, text: "CallKit: call-id: [\(String(describing: callId))] leaving conference")
NotificationCenter.default.post(name: Notification.Name("LinphoneCallUpdate"), object: self) NotificationCenter.default.post(name: Notification.Name("LinphoneCallUpdate"), object: self)
return action.fulfill()
} }else{
let state = action.isOnHold ? "Paused" : "Resumed"
let state = action.isOnHold ? "Paused" : "Resumed" Log.directLog(BCTBX_LOG_DEBUG, text: "CallKit: Call with call-id: [\(String(describing: callId))] and UUID: [\(uuid)] paused status changed to: [\(state)]")
Log.directLog(BCTBX_LOG_DEBUG, text: "CallKit: Call with call-id: [\(String(describing: callId))] and UUID: [\(uuid)] paused status changed to: [\(state)]") if (action.isOnHold) {
if (action.isOnHold) { CallManager.instance().speakerBeforePause = CallManager.instance().isSpeakerEnabled()
if (call!.params?.localConferenceMode ?? false) { try call!.pause()
return CallManager.instance().actionToFulFill = action;
}
CallManager.instance().speakerBeforePause = CallManager.instance().isSpeakerEnabled()
try call!.pause()
} else {
if (CallManager.instance().lc?.conference != nil && CallManager.instance().lc?.callsNb ?? 0 > 1) {
try CallManager.instance().lc?.enterConference()
NotificationCenter.default.post(name: Notification.Name("LinphoneCallUpdate"), object: self)
} else { } else {
try call!.resume() if (CallManager.instance().lc?.conference != nil && CallManager.instance().lc?.callsNb ?? 0 > 1) {
try CallManager.instance().lc?.enterConference()
action.fulfill()
NotificationCenter.default.post(name: Notification.Name("LinphoneCallUpdate"), object: self)
} else {
try call!.resume()
CallManager.instance().actionToFulFill = action;
// HORRIBLE HACK HERE - PLEASE APPLE FIX THIS !!
// When resuming a SIP call after a native call has ended remotely, didActivate: audioSession
// is never called.
// It looks like in this case, it is implicit.
// As a result we have to notify the Core that the AudioSession is active.
// The SpeakerBox demo application written by Apple exhibits this behavior.
// https://developer.apple.com/documentation/callkit/making_and_receiving_voip_calls_with_callkit
// We can clearly see there that startAudio() is called immediately in the CXSetHeldCallAction
// handler, while it is called from didActivate: audioSession otherwise.
// Callkit's design is not consistent, or its documentation imcomplete, wich is somewhat disapointing.
//
Log.directLog(BCTBX_LOG_DEBUG, text: "Assuming AudioSession is active when executing a CXSetHeldCallAction with isOnHold=false.")
CallManager.instance().lc?.activateAudioSession(actived: true)
CallManager.instance().callkitAudioSessionActivated = true
}
} }
} }
} catch { } catch {
Log.directLog(BCTBX_LOG_ERROR, text: "CallKit: Call set held (paused or resumed) \(uuid) failed because \(error)") Log.directLog(BCTBX_LOG_ERROR, text: "CallKit: Call set held (paused or resumed) \(uuid) failed because \(error)")
action.fail()
} }
} }
func provider(_ provider: CXProvider, perform action: CXStartCallAction) { func provider(_ provider: CXProvider, perform action: CXStartCallAction) {
do { do {
let uuid = action.callUUID let uuid = action.callUUID

View file

@ -79,14 +79,9 @@ import linphonesw
scrollView.alignUnder(view: topBar, withMargin: content_margin_top).alignParentBottom().matchParentSideBorders().done() scrollView.alignUnder(view: topBar, withMargin: content_margin_top).alignParentBottom().matchParentSideBorders().done()
scrollView.addSubview(contentView) scrollView.addSubview(contentView)
contentView.matchBordersOf(view: view).alignParentBottom().alignParentTop().done() // don't forget a bottom constraint b/w last element of contentview and contentview contentView.matchBordersOf(view: view).alignParentBottom().alignParentTop().done() // don't forget a bottom constraint b/w last element of contentview and contentview
UIDeviceBridge.displayModeSwitched.readCurrentAndObserve { _ in
} self.topBar.backgroundColor = VoipTheme.voipToolbarBackgroundColor.get()
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
topBar.backgroundColor = VoipTheme.voipToolbarBackgroundColor.get()
} }
} }

View file

@ -57,7 +57,10 @@ class TimestampUtils {
let dateFormatter = DateFormatter() let dateFormatter = DateFormatter()
dateFormatter.dateStyle = .long dateFormatter.dateStyle = .long
dateFormatter.timeStyle = .none dateFormatter.timeStyle = .none
return dateFormatter.string(from: date) let dayFormatter = DateFormatter()
dayFormatter.dateFormat = "EEEE"
let day = dayFormatter.string(from: date)
return day.prefix(1).uppercased() + day.dropFirst()+" "+dateFormatter.string(from: date)
} }
static func timeToString(date:Date) -> String { static func timeToString(date:Date) -> String {

View file

@ -19,7 +19,7 @@
import Foundation import Foundation
class LightDarkColor { @objc class LightDarkColor : NSObject {
var light: UIColor var light: UIColor
var dark : UIColor var dark : UIColor
init(_ l:UIColor,_ d:UIColor){ init(_ l:UIColor,_ d:UIColor){
@ -27,7 +27,7 @@ class LightDarkColor {
dark = d dark = d
} }
func get() -> UIColor { @objc func get() -> UIColor {
if #available(iOS 13.0, *) { if #available(iOS 13.0, *) {
if UITraitCollection.current.userInterfaceStyle == .light { if UITraitCollection.current.userInterfaceStyle == .light {
return light return light

View file

@ -35,9 +35,14 @@ struct TextStyle {
extension UILabel { extension UILabel {
func applyStyle(_ style:TextStyle) {
func applyStyleColors(_ style:TextStyle) {
textColor = style.fgColor.get() textColor = style.fgColor.get()
backgroundColor = style.bgColor.get() backgroundColor = style.bgColor.get()
}
func applyStyle(_ style:TextStyle) {
applyStyleColors(style)
if (style.allCaps) { if (style.allCaps) {
text = self.text?.uppercased() text = self.text?.uppercased()
tag = 1 tag = 1
@ -49,10 +54,10 @@ extension UILabel {
func addIndicatorIcon(iconName:String, padding:CGFloat = 5.0, y:CGFloat = 4.0, trailing: Bool = true) { func addIndicatorIcon(iconName:String, padding:CGFloat = 5.0, y:CGFloat = 4.0, trailing: Bool = true) {
let imageAttachment = NSTextAttachment() let imageAttachment = NSTextAttachment()
imageAttachment.image = UIImage(named:iconName) imageAttachment.image = UIImage(named:iconName)?.tinted(with: VoipTheme.voipDrawableColor.get())
imageAttachment.bounds = CGRect(x: 5.0, y: y , width: font.lineHeight - 2*padding, height: font.lineHeight - 2*padding) imageAttachment.bounds = CGRect(x: 0.0, y: y , width: font.lineHeight - 2*padding, height: font.lineHeight - 2*padding)
let iconString = NSMutableAttributedString(attachment: imageAttachment) let iconString = NSMutableAttributedString(attachment: imageAttachment)
let textXtring = NSMutableAttributedString(string: text != nil ? text! : "") let textXtring = NSMutableAttributedString(string: text != nil ? (!trailing ? " " : "") + text! + (trailing ? " " : "") : "")
if (trailing) { if (trailing) {
textXtring.append(iconString) textXtring.append(iconString)
self.text = nil self.text = nil
@ -78,9 +83,14 @@ extension UIButton {
} }
extension UITextView { extension UITextView {
func applyStyle(_ style:TextStyle) {
func applyStyleColors(_ style:TextStyle) {
textColor = style.fgColor.get() textColor = style.fgColor.get()
backgroundColor = style.bgColor.get() backgroundColor = style.bgColor.get()
}
func applyStyle(_ style:TextStyle) {
applyStyleColors(style)
if (style.allCaps) { if (style.allCaps) {
text = self.text?.uppercased() text = self.text?.uppercased()
tag = 1 tag = 1

Some files were not shown because too many files have changed in this diff Show more