Compare commits

...

213 commits

Author SHA1 Message Date
Gaelle Braud
d9b879fac7 enable native ringing if ringtone is empty 2026-01-30 11:42:26 +01:00
Gaelle Braud
507bee2946 fix hide spinner in chat messages list when model reset 2026-01-29 09:29:50 +01:00
Gaelle Braud
d4e657adff fix linux packaging (CMAKE_OPTIONS was overriding options set in gitlab CI variables) 2026-01-28 17:42:59 +01:00
Gaelle Braud
7685462960 add debug in crashing connect for debug 2026-01-28 15:21:05 +01:00
Gaelle Braud
fe6e3f4e25 activate bugsplat on linux/macos nightly packages 2026-01-28 12:39:19 +01:00
Gaelle Braud
4916c0ad6e do not clone conference info object on model creation as it blocks the creation
fix typo in cancelCreation calling
2026-01-28 12:14:02 +01:00
Gaelle Braud
9214cb5fed fix typo leading to build error 2026-01-27 17:45:39 +01:00
Gaelle Braud
f06f56bead update SDK to 5.5.0 2026-01-27 17:32:53 +01:00
Gaelle Braud
1f6a979686 fix tool/CMakeLists indentation (formatted in merge) 2026-01-27 17:32:11 +01:00
Gaelle Braud
4fd7199a83 Merge branch 'release/6.1' 2026-01-27 14:38:52 +01:00
Gaelle Braud
a51de0ff7f fix display participant popup when typing @
restore cursor when clicking on mention (cursor was stuck on ibeam)
2026-01-27 11:21:41 +01:00
Sylvain Berfini
53c5c9cce1 Updated translations from Weblate 2026-01-27 08:44:03 +00:00
Gaelle Braud
50efe4d54a clone the linphone conference info pointer when created to get the diff with the old ones (needed to fix participant removing) #SDK-1001
fix position chat message list to first unread index #LINQT-2371

force sending chat message messageRead() callback when chat room is marked as read

try to fix crash with const & in MagicSearchList
2026-01-26 17:30:32 +01:00
Gaelle Braud
4c9d6b4d7c passing const ref when copy not needed 2026-01-26 12:30:40 +01:00
Christophe Deschamps
909bc98622 Refactor ICS export 2026-01-24 08:03:18 +01:00
Gaelle Braud
7aad75075e fix conference event handled condition #LINQT-2146 2026-01-23 17:02:26 +01:00
Christophe Deschamps
ed9fe9c563 Generate ICS manually for calendar export with user friendly information 2026-01-23 15:34:50 +01:00
Gaelle Braud
f52d2212bb override cursor when chat text content hovered (only global cursor works on chat bubble) #LINQT-2364 2026-01-23 11:21:43 +01:00
Gaelle Braud
be5a7fa4e7 force starting timeout when oidc authentication requested so we can cancel the process in case the url isn't working 2026-01-23 11:09:23 +01:00
Gaelle Braud
770ed045e5 Do not automatically select chat when it is added in list because of its Created state, only force selection when user is manually creating it 2026-01-22 17:09:02 +01:00
Gaelle Braud
41b2086b66 try to fix crash when linphone chatroom send a signal and ChatCore is already destroyed 2026-01-22 15:58:12 +01:00
Gaelle Braud
bc8f542896 update SDK to 5.4.80 2026-01-22 15:54:46 +01:00
Gaelle Braud
e416b01ba4 update translations 2026-01-22 14:38:54 +01:00
Gaelle Braud
6d5b9c58f2 Do not add chatroom to the list if created from a conference call 2026-01-22 14:38:47 +01:00
Gaelle Braud
df979d4270 Update settings when configuration succeed in case it contains settings modifications 2026-01-22 14:38:37 +01:00
Gaelle Braud
37d6649fa4 do not replace symbols by their rich text code as qml will handle it itself
replace \n by <br> to handle carriage return
2026-01-22 10:13:11 +01:00
Gaelle Braud
5622016824 fix crash when eventLog from newEvent signal is null 2026-01-21 17:00:23 +01:00
Gaelle Braud
cdd2783f6c fix chat message text format #LINQT-2358 2026-01-21 16:35:51 +01:00
Gaelle Braud
752f9096bf update SDK to 5.4.79 2026-01-21 10:39:27 +01:00
Sylvain Berfini
2f73a297e9 Fixed typos 2026-01-21 09:56:52 +01:00
Sylvain Berfini
401c753cc6 Updated translations from Weblate 2026-01-21 08:48:29 +00:00
Gaelle Braud
b864fed809 fix search in chat history #LINQT-2349 #LINQT-2350 2026-01-19 12:51:46 +01:00
Gaelle Braud
ab35c1d15a fix bold text part searched in chatroom #LINQT-2351 2026-01-18 13:06:08 +01:00
Gaelle Braud
638c53dad6 sso page to manually cancel sso connection 2026-01-17 18:45:39 +01:00
Gaelle Braud
af4e8d2685 update chat participant display name when friend created/updated/removed #LINQT-2347 2026-01-17 18:45:39 +01:00
Peio Rigaux
0a40391074 Reduce rights of uploaded files 2026-01-15 17:41:58 +01:00
Peio Rigaux
1156bd78c0 Reduce rights of uploaded files 2026-01-15 12:19:56 +01:00
Gaelle Braud
c756217b16 try to reduce memory consumption when displaying an animated image #LINQT-2343 2026-01-15 12:17:38 +01:00
Gaelle Braud
7f1708dacb fix call history list sorting #LINQT-2346 2026-01-15 12:17:38 +01:00
Julien Wadel
f039784b26 - Fix crash/opengl errors on loading video.
- Remove PreviewManager to simplify the process as we only have one location to display the preview.
This avoid messing with Qt framebuffers, CallCore instances and SDK instances.
- Update SDK to 5.4.77
2026-01-15 11:09:10 +01:00
Sylvain Berfini
572d859ac8 Updated translation files 2026-01-14 16:44:44 +00:00
Gaelle Braud
d42359aacf Changelog 2026-01-14 15:16:01 +01:00
Gaelle Braud
dc4ebebc9b fix crash when null call history
Hide presence popup button if publish is disabled
2026-01-14 15:16:01 +01:00
Gaelle Braud
4e8ded737c Do not add voice recordings to shared medias (try to fix #LINQT-2308) 2026-01-14 10:02:24 +01:00
Gaelle Braud
e3a1493ceb Store call history list in app 2026-01-13 16:45:09 +01:00
Gaelle Braud
4a0cdc7f1a prevent conference list item from stealing focus when searching conference info #LINQT-2305 2026-01-13 16:03:15 +01:00
Gaelle Braud
16703ec2d5 simulate numeric pad button pressed when pressing numeric key on keyboard in new call form #LINQT-2313 2026-01-13 16:03:15 +01:00
Gaelle Braud
2e7c250d8d Fix register checking ui #LINQT-2332
Add accessibility focus ui on register country combobox and fix register page spacings and interaction #LINQT-2333
2026-01-13 16:03:15 +01:00
Gaelle Braud
7b7d382cc6 add loader to the call status icon so it is changed properly when call state changes #LINQT-2303 2026-01-12 17:57:39 +01:00
gaelle.braud
6890362c41 fix handle app activity for macos #LINQT-2295 2026-01-12 17:06:56 +01:00
Gaelle Braud
b19420afa9 fix update disable meetings feature setting when default account changes #LINQT-2334 2026-01-12 15:28:10 +01:00
Gaelle Braud
1a69b68456 update SDK to 5.4.74 2026-01-12 15:28:10 +01:00
Gaelle Braud
1a127d6dc7 fix typo in conference info description getter 2026-01-12 15:28:10 +01:00
Gaelle Braud
0799fe975a fix leave calls window fullscreen when escape pressed #LINQT-2301 #LINQT-2321 2026-01-12 15:28:10 +01:00
Gaelle Braud
389fae13d8 Activate numeric pad buttons with numpad keyboard in calls window #LINQT-2310 2026-01-12 10:52:08 +01:00
Gaelle Braud
7e9e3e6f55 change date format in chat list #LINQT-2320
Verify local video direction to activate camera #LINQT-2328
2026-01-09 17:38:42 +01:00
Gaelle Braud
e9f04a8aaf write default core values in default rc files instead of forcing them when core starts #LINQT-2319 2026-01-09 16:39:15 +01:00
gaelle
12c12ad2b5 Only create chat if 1-1 call, it should already exist in conference if available
add me in chatroom participants view #LINQT-2312

update admin status on conference joined #LINQT-2311

fix composing status when user starts writing message #LINQT-2317

do not force presence if disabled in account params #LINQT-2318

fix chat uin 1-1 call #LINQT-2322
2026-01-09 16:39:15 +01:00
Julien Wadel
f2f29a0d7c Reuse old git describe for package name. 2026-01-08 17:12:33 +01:00
Gaelle Braud
55be22c56c Remove qtmultimedia dependency in linux docker file
Removed qtmultimedia from dockerfile and pushed new docker image
2026-01-08 11:11:24 +01:00
Sylvain Berfini
591f21bb9f Updated translation files from Weblate 2026-01-08 09:54:30 +00:00
gaelle
b1970ae200 fix playback/capture device in call parameters #LINQT-2302 2026-01-07 16:47:37 +01:00
gaelle
3fb39cbb59 fix transfer message chat list again #LINQT-2246 2026-01-07 16:04:16 +01:00
gaelle
eb1a94fc01 do not display currently selected conf if we are creating or editing a conf #LINQT-2297 2026-01-07 14:57:35 +01:00
Ghislain MARY
befa4a2635 Add support of Jabra headsets via the hidapi library. 2026-01-06 17:27:11 +01:00
Gaelle Braud
e21005045a Remove QtMultimedia dependency 2026-01-06 15:52:26 +01:00
Gaelle Braud
35895ed42d update SDK to 5.4.73 2026-01-06 15:51:17 +01:00
Julien Wadel
d9e5050a40 Add Bugsplat database parameters to build.
Fix crash on call at startup.
2026-01-06 11:56:57 +01:00
Gaelle Braud
c88d9a7f2a Fixes:
call bad signal icon color
Re-send option in message menu if not delivered

Do not show error message creating chatroom if in call as it is not being created by user #LINQT-2287
and do not try to create one if chat is not enabled in conference
2026-01-06 11:26:32 +01:00
Julien Wadel
315cabf9d9 Fix NSIS installer: revert removing useless files because NSIS have a check integrity that is computed from install() commands.
There is no way found to remove these files without changing install on all subprojects.
2026-01-05 17:17:22 +01:00
Gaelle Braud
d57bb585db Fixes:
Only show config error when restarting, not everytime the app starts
send conferenceInfoReceived when conf info state is New

Use App ChatList instead of a new one in each ChatProxy (try to fix crash when catching signals in ChatCore)
2026-01-05 13:51:58 +01:00
Alexandre Jörgensen
b3cc4f8be2 Fix contact settings unavailable 2026-01-02 15:13:21 +01:00
Julien Wadel
b562c529bc Features: CrashReporter with Crashpad and Bugsplat. 2025-12-24 17:48:08 +01:00
Gaelle Braud
1902fe3029 Fixes:
create call chat when call is connected in order to see the unread messages notification in chat button
close right panel when starting new call
2025-12-19 17:33:45 +01:00
Gaelle Braud
b9fe688f01 update SDK to 5.4.69 2025-12-19 09:47:06 +01:00
Gaelle Braud
bd87f3c6c3 clear app lists on restart (fix crash where signals are catch while core has not started yet) 2025-12-19 09:47:06 +01:00
Gaelle Braud
e32ed7f6a1 do not react to http digest signal if account is not in account list 2025-12-18 16:52:38 +01:00
Gaelle Braud
89efda13c8 Fixes:
disable change layout button if paused meeting

hide call button in call history if conference and disable_meetings_feature set

disable camera if switch to audio only layout mode
2025-12-18 15:43:24 +01:00
Gaelle Braud
36fbf44d42 fix new chat selection 2025-12-18 10:13:37 +00:00
Christophe Deschamps
0cf3938dc3 Move Call Forward under the save scope in settings 2025-12-17 18:49:47 +01:00
Christophe Deschamps
c3b160ec3e Move Codecs under the save scope in settings 2025-12-17 18:49:41 +01:00
Christophe Deschamps
dc1ec216e8 Move FPS under the save scope in settings + fix the save popup showing when not saved 2025-12-17 18:49:35 +01:00
Christophe Deschamps
0bfa29dc55 Move AutoStart under the save scope in settings 2025-12-17 18:49:30 +01:00
Christophe Deschamps
0df7065b5e Move IPV6 under the save scope in settings 2025-12-17 18:49:23 +01:00
Christophe Deschamps
78e36bfa88 Move Call Forward under the save scope in settings 2025-12-17 17:46:49 +00:00
Christophe Deschamps
0b6393125f Move Codecs under the save scope in settings 2025-12-17 17:46:49 +00:00
Christophe Deschamps
1e29c57ad3 Move FPS under the save scope in settings + fix the save popup showing when not saved 2025-12-17 17:46:49 +00:00
Christophe Deschamps
c6ac29595a Move AutoStart under the save scope in settings 2025-12-17 17:46:49 +00:00
Christophe Deschamps
f96ffcaf49 Move IPV6 under the save scope in settings 2025-12-17 17:46:49 +00:00
Gaelle Braud
a2c1c8bd13 fix : reset core started value when restarting app 2025-12-17 16:36:54 +01:00
Gaelle Braud
ccd6004e34 if account in remote provisioning, switch to main page even if it is not connected yet (we can add an account without a password and ask for it after) 2025-12-17 16:08:42 +01:00
Gaelle Braud
c0c369e93e call http digest callback if account not connected 2025-12-17 15:50:36 +01:00
Gaelle Braud
44a3a90517 wait for core to be started before initializing lists 2025-12-17 15:17:32 +01:00
Gaelle Braud
21312c99db fix chat room creation inserted twice in list 2025-12-17 14:39:40 +01:00
Gaelle Braud
ec3125e8a5 add emojis 2025-12-17 14:39:40 +01:00
Gaelle Braud
487dadb33f do not store core object in qml files 2025-12-17 09:16:21 +01:00
Gaelle Braud
785005012d fix crashes due to persistent ChatCore stored in qml files 2025-12-16 17:05:46 +01:00
Gaelle Braud
b34e4c24da fix warnings
remove chat SafeConnection destruction

focus sending text area when chat change
2025-12-16 16:30:06 +01:00
Gaelle Braud
e78e1fffbb display ephemeral status in chat header 2025-12-15 17:38:48 +01:00
Gaelle Braud
a6832576d3 fix third party connection with registrar uri 2025-12-15 16:55:56 +01:00
Gaelle Braud
ac2bcc4629 Fixes:
meeting form : initialize meeting title if not empty #LINQT-2125

fix download path

force active focus on sending text area when replying to message
2025-12-15 16:27:48 +01:00
Gaelle Braud
6d9b5efcc5 hide current call in transfer call list #LINQT-2256 2025-12-15 15:58:22 +01:00
Gaelle Braud
75ad42d5e8 hide current call in transfer call list #LINQT-2256 2025-12-15 14:41:17 +01:00
Gaelle Braud
6657c26b87 FIXES:
hide sip address in conversation info if hide_sip_addresses set

write account presence note event if empty (otherwise it cannot be cleaned)

add chatroom to list when Created state signal received

fix navigation to create contact from conversation infos

display copy sip address even if participant is in contact list
2025-12-15 12:31:55 +01:00
Gaelle Braud
1726408839 mwi labels harmonization + tooltip #LINQT-2230 2025-12-15 10:06:36 +01:00
Gaelle Braud
8751670db2 fix disable meeting feature initialization (only check if videoconference factory uri is empty) and auto switch to call page on start if disable meeting property was set and last active tab was meetings 2025-12-12 19:10:24 +01:00
Gaelle Braud
c31def74fe auto select current conference when model reset (conf scheduler send Ready signal then conferenceInfoReceived makes conf info list update)
fix crash
2025-12-12 15:29:41 +01:00
Christophe Deschamps
50ec67298e Update unread count when unread incoming message is retracted 2025-12-12 11:04:00 +01:00
Gaelle Braud
2320cc7444 fix volume indicator slider when in call #LINQT-2254 2025-12-12 10:43:38 +01:00
Christophe Deschamps
1bae93aab5 Chat message edition 2025-12-11 15:41:40 +01:00
Gaelle Braud
be6bf6f2a9 fix account parameters saving + add outbound proxy uri check
add outbound proxy uri check before connecting third party account
2025-12-11 15:16:26 +01:00
Gaelle Braud
9902bb4ae7 do not set the explicit presence when updating it according to the active status of the app (as it is not user initiated) 2025-12-11 14:22:45 +01:00
Gaelle Braud
a75965c14e update presence when app visibility changes #LINQT-2237 2025-12-11 10:47:38 +01:00
Christophe Deschamps
d40045d5bb Chat message retraction 2025-12-10 19:20:16 +00:00
gaelle
158ce01bcd show default avatar image if contact name starts with + #LINQT-2239 2025-12-10 18:33:55 +01:00
gaelle
872f60cefc WIP on release/6.1: 94df04c34 remove connect already in QSortFilterProxyModel (leading to crash in Windows) 2025-12-10 17:43:21 +01:00
gaelle
94df04c347 remove connect already in QSortFilterProxyModel (leading to crash in Windows) 2025-12-10 17:05:44 +01:00
Christophe Deschamps
f8a4f73993 Fix - when accessing and existing settings making no changes do not show Save? popup 2025-12-10 15:05:30 +01:00
Gaelle Braud
73f75ac3d9 reset SDK to 5.4.67 2025-12-10 11:59:30 +01:00
Gaelle Braud
fc42ada7ba fix chat transfer #LINQT-2246 2025-12-10 11:53:26 +01:00
Christophe Deschamps
13ec790648 Fix - when accessing and existing settings making no changes do not show Save? popup 2025-12-10 09:52:32 +01:00
gaelle
528dc1e2bd fix macos ci (use flat runner for SDK 5.5) 2025-12-09 14:28:09 +01:00
gaelle.braud
f173a887cd fix crash on launch : do not create QAccessibleAnnouncementEvent if target is null 2025-12-09 12:49:58 +01:00
gaelle
592eea016d update SDK 5.4 2025-12-09 12:49:58 +01:00
gaelle
f53b992704 put confinfo list in app so it is intanciated only once
chat list in app to load it once
2025-12-09 12:49:58 +01:00
gaelle
a562b0f1c8 add loaders on main pages so they are instanciated only when switching to corresponding views 2025-12-09 12:49:58 +01:00
gaelle
ff3aa59217 Add Readme instruction for Fedora 43 users to get emojis (fix #LINQT-2232) 2025-12-09 12:49:58 +01:00
gaelle
7573f47928 fix meeting list focus #LINQT-1920 2025-12-09 12:49:58 +01:00
Julien Wadel
31726b46cd Fix unfound Microsoft runtimes while installing by removing the set on MSVC_VERSION that should be already set. 2025-12-09 12:48:55 +01:00
Julien Wadel
f5429c4650 Fix unfound Microsoft runtimes while installing by removing the set on MSVC_VERSION that should be already set. 2025-12-09 11:51:13 +01:00
Sylvain Berfini
0d35fa72f7 Updated translations from Weblate 2025-12-08 09:49:36 +00:00
Sylvain Berfini
876fdbe619 Updated version code in CMakeLists to fix build since 6.2.0-alpha tag 2025-12-04 18:26:07 +01:00
Christophe Deschamps
e849891548 Update sdk 2025-12-04 16:16:06 +01:00
Christophe Deschamps
c672762b63 Option to set CCMP server URL in account settings 2025-12-04 15:41:59 +01:00
Christophe Deschamps
e23a49fbd3 Replace core.removeAccount by core.removeAccountWithData() 2025-12-04 15:40:41 +01:00
Christophe Deschamps
a9a1249ecd Add button to export ICS of meeting in meetings and chat views 2025-12-04 15:36:42 +01:00
Christophe Deschamps
ed57ec1ea5 Enable e2e encryption in scheduled conf chat rooms 2025-12-04 15:32:47 +01:00
gaelle
d4c1387c43 Merge branch 'release/6.1' 2025-12-04 14:34:47 +01:00
gaelle
88aea7ba25 update SDK to 5.4.67 2025-12-04 14:14:42 +01:00
Gaelle Braud
605ffdcdd5 refresh register when click on account deactivated status 2025-12-02 17:17:43 +01:00
Gaelle Braud
21e8e2aaba ChatCore: wait for deleted state before emitting deleted signal
fix chats selection and remove useless signal
2025-12-02 17:17:43 +01:00
Sylvain Berfini
60517741a2 CMakeLists changes regarding app name & windows debug symbols 2025-12-02 12:36:16 +00:00
Gaelle Braud
5a90959125 separate enable camera and video and use enableCamera when user activates webcam (fix #LINQT-2216) 2025-12-02 12:38:35 +01:00
Gaelle Braud
c8428d6ade fix automatic presence when app focus changes #LINQT-2172 2025-12-02 11:31:18 +01:00
Gaelle Braud
b2ae9213a2 force enableAudio/Video to false when creating chatroom params to avoid sdk warning 2025-12-01 17:00:43 +01:00
Gaelle Braud
d39a84ca4e update SDK to 5.4.65 2025-12-01 10:35:22 +01:00
Gaelle Braud
dfc88b7657 Only create a chatroom for the current call when the chat button is pressed (fix #LINQT-2228) 2025-12-01 10:35:03 +01:00
Gaelle Braud
f405754e24 FIXES:
auto switch to away/online status when app is inactive/active #LINQT-2172

fix loading ui for chats/meetings lists

use uri only for account uris to remove the < and >
2025-11-28 15:24:29 +01:00
Gaelle Braud
cca8db9055 only switch automatically to main page when account added and app is being restarted (fix #LINQT-2224) 2025-11-27 11:32:42 +01:00
Gaelle Braud
8ee8058065 wait for chat room to receive Created state callback before selecting it #LINQT-2226 2025-11-27 10:08:18 +01:00
Gaelle Braud
96b20f42e2 let the sdk check if registrar uri and outbound proxy uri are valid instead of using regex #LINQT-2227 2025-11-27 09:11:14 +01:00
gaelle
5bbffa79d8 fix crash when account null
fix notification display if max instances reached
2025-11-26 16:53:35 +01:00
Gaelle Braud
e3edeb1bcf update SDK to 5.4.62 2025-11-26 15:50:58 +01:00
Gaelle Braud
7c2e9f6c12 fix binding loop on popup button closed
remove debug

do not force declining call when user in do not disturb status #LINQT-2129 #LINQT-2171

do not handle chat notifications when chat disabled

change transfer direction (transfer paused call to current) #LINQT-2211

fix chat message image size in call #LINQT-2142

update translations

fix crash

remove auto switch audio device to avoid binding loop
2025-11-26 15:50:58 +01:00
Gaelle Braud
ca4bdd3736 remove debug 2025-11-26 10:19:51 +01:00
Gaelle Braud
251f711250 fix third party connection #LINQT-2180 #LINQT-2181 2025-11-26 09:25:04 +01:00
Gaelle Braud
37db390d5c fix chat list view and really fix chat selection after filter reset #LINQT-2199 2025-11-25 09:52:44 +01:00
Gaelle Braud
04d2744bf2 fix force adding chat to list when creating it 2025-11-24 19:40:13 +01:00
Gaelle Braud
a7ba374b8f improve chat messages list view
fix auto scroll when receiving new message #LINQT-2154
2025-11-24 19:11:38 +01:00
Gaelle Braud
29691485bf hide conference joined/left events if not in a chat group #LINQT-2146 2025-11-24 15:17:34 +01:00
Gaelle Braud
735c473b3c add unread notification count on class window chat button #LINQT-2138
reset last active window if destroyed
2025-11-21 16:15:41 +01:00
Gaelle Braud
bba3edd4b6 fix check for update root url #LINQT-2177 2025-11-21 16:15:39 +01:00
Gaelle Braud
514c337192 fix chatroom selection when filter change #LINQT-2199 2025-11-21 14:02:06 +01:00
gaelle.braud
fea9a1b7be fix binding loop in getLastFocusableItemInItem (fix #LINQT-2210) 2025-11-21 14:01:40 +01:00
gaelle
afd3514965 hide chat button if not supported for conference #LINQT-2192 2025-11-21 10:56:48 +01:00
gaelle
3f5797f453 to fix : meeting detail ui
fix meeting detail view #LINQT-2193
2025-11-20 17:55:06 +01:00
gaelle
db5f6dc2af fix thumbnail path initialized with \\ when sent from Windows device 2025-11-20 15:21:04 +01:00
gaelle
d0cf951fe4 fix thumbnail download paths leading to wrong thumbnail path and thumbnail not displayed in message when multiple files sent
remove qt5compat dependency README
2025-11-19 14:39:47 +01:00
gaelle.braud
b802fec33c hide chatroom actions if user left group #LINQT-2189
hide new group chat button if no conference factory uri defined for the account #LINQT-2178
2025-11-18 16:08:17 +01:00
Julien Wadel
3c264fd3ee Set camera state on call creation in order to avoid using not wanted state while updating the call.
Cause: toggling screensharing will activate the camera if the call is a video call.
2025-11-18 11:27:59 +01:00
Gaelle Braud
72e32ec160 fix third party connection #LINQT-2180 #LINQT-2181 2025-11-18 08:33:13 +01:00
Gaelle Braud
bfbafab84b fix verification when trying to connect to an account that is already connected 2025-11-17 17:45:20 +01:00
Gaelle Braud
7a4adbcbb4 remove loader from image so the icons are not reloaded each time a list layout change
remove button color warning
2025-11-17 17:30:03 +01:00
Gaelle Braud
fa3ef0b1a8 try to fix crash when ChatCore destroyed and connection with model is still alive 2025-11-17 15:22:16 +01:00
Gaelle Braud
2fc4439e16 Disable notifications if Offline status #LINQT-2173 2025-11-17 10:56:57 +01:00
Gaelle Braud
0b9a0ead2a fix screen sharing 2025-11-17 10:49:29 +01:00
Gaelle Braud
3870037905 update SDK to 5.4.60 2025-11-15 00:15:41 +01:00
Gaelle Braud
e113058ae9 hide group call button if no videoconference uri set
avoid displaying more messages when the list is not yet initialized

fix meeting form end hour combobox list display #LINQT-2127

check if at least one participant when creating group chat #LINQT-2134

display all the errors at a time if they all occur #LINQT-2133

add signal to add chat to list when state Created #LINQT-2131

fix chat deletion
2025-11-15 00:15:41 +01:00
Gaelle Braud
a4b38e4fd1 Fix crash by replacing unsafe reverse iterator loop in EventLogList (cherry pick 23875ce1) 2025-11-14 10:12:00 +01:00
Gaelle Braud
a0850b9967 fix #LINQT-2130 enable video in conference for the participant to be able to see shared screen without activating camera
do not display notification if message already read

display notif when copying account sip address #LINQT-2126

prevent adding our own address in participants (fix #LINQT-2164)
2025-11-13 17:43:55 +01:00
Andrea Gianarda
bc5022c8f5 Move include of application_info.cmake after setting LINPHONEAPP_APPLICATION_NAME because its value must be known to set the APPLICATION_NAME variable 2025-11-13 13:40:52 +00:00
Gaelle Braud
ac03de6663 update audio device when changed automatically in sdk 2025-11-13 12:51:39 +01:00
Gaelle Braud
58eb93d13b UI fixes :
display real error message when carddav sync fails

hide mark as read button if no unread message #LINQT-2144

fix screencast panel hidden while user is sharing screen #LINQT-2136

disable selection for cancelled meetings #LINQT-2121
2025-11-13 09:23:28 +01:00
Julien Wadel
11487b3aeb - Use of SDK master to prepare for 6.2 (SDK 5.5):
- Remove deprecations (Qt, LDAP, AudioDevice, Compose)
- Fix absolute paths that can be wrong with temporary binaries images like Appimage. This way rootca will target the packaged one.
- Remove some packaged path as they are already set by SDK (from a fix on its side).
- Remove duplicated rootca packaging.
2025-11-10 17:23:10 +01:00
Alexandre Jörgensen
ca73193f6c Fix accessibility screen reader:
* Error on login page not sayed #LINQT-2095
* Incorect translation of close button in french #LINQT-2094
2025-11-07 17:21:14 +00:00
Alexandre Jörgensen
f2e49f21b0 Correct typo weight #LINQT-2078 2025-11-07 16:55:05 +00:00
gaelle
a7c8e8db90 do not minimize app on Windows auto start (fix #BC-97) 2025-11-07 16:41:31 +01:00
Gaelle Braud
afc03daa22 fix admin rights modification #LINQT-2114 2025-11-07 15:51:35 +01:00
Sylvain Berfini
74b2cf299b Updated translations from Weblate 2025-11-07 10:31:05 +00:00
Gaelle Braud
fcdbcdc9c1 Set minimum required version to Qt6.10.0
Display Qt version in troubleshooting page
2025-11-07 11:11:35 +01:00
Gaelle Braud
3dbea1ccb2 display qt version in help page 2025-11-07 10:22:31 +01:00
gaelle.braud
d741e79e2e fix file preview display #LINQT-2098 2025-11-07 08:18:17 +00:00
Gaelle Braud
b3b40d6f99 use native player to open video files #LINQT-2116
hide e2e conf creation toggle #LINQT-2112
2025-11-07 08:18:17 +00:00
Gaelle Braud
d957fae94e fix message content download crash 2025-11-07 08:18:17 +00:00
Alexandre Jörgensen
e224f24e92 Update QT to 6.10.0 and increase security:
* Use QT 6.10.0
* Change invalidateFilter (deprecated) to beginFilterChange/endFilterChange
* Remove warning of presenceStatusItem
* Add changelog with this modification for 6.1.0

* Do not use anymore variables to build docker images. Qt versions are now hardcoded in images to allow multiple opensource Qt versions.
2025-11-06 16:33:34 +01:00
gaelle.braud
cef650fb6a update SDK to 5.4.57 2025-11-06 11:10:45 +01:00
Gaelle Braud
9e10d5e9bd change sip login advanced params order 2025-11-06 09:55:35 +01:00
Gaelle Braud
36c783c9e5 always show scrollbar when needed (fix #LINQT-2102) 2025-11-06 09:45:13 +01:00
Gaelle Braud
0d2e83a60d fix #LINQT-2086 disable video only when starting audio call 2025-11-06 09:45:13 +01:00
Gaelle Braud
c70426f770 fix crash (showing window while it is destroyed) (fix #LINQT-2077, #LINQT-2087, #LINQT-2110) 2025-11-06 09:11:53 +01:00
gaelle
0c87a8d94e change Qt minimum version to Qt6.9.0 #○LINQT-2099 2025-11-05 15:31:53 +01:00
Gaelle Braud
6f4e925766 fix #LINQT-2108 : do not use <= or >= in lessThan comparator (c++ restriction) 2025-11-05 11:05:38 +01:00
Gaelle Braud
99c2a6ddc6 fix #LINQT-1657 do not manipulate internal gains 2025-11-05 09:32:29 +01:00
Gaelle Braud
cbd91b868d update SDK to 5.4.56 2025-11-05 09:31:10 +01:00
Gaelle Braud
80bf126b23 check for update 2025-11-04 17:57:15 +01:00
Peio Rigaux
db1f04350a Fix syntax error in GNU/Linux CI deploy script 2025-10-31 10:03:44 +00:00
Julien Wadel
08f2292881 Remove Core5Compat dependency and use our own opacity implementation.
(cherry picked from commit 229fbe1713)
2025-10-30 17:12:40 +00:00
Julien Wadel
229fbe1713 Remove Core5Compat dependency and use our own opacity implementation. 2025-10-30 17:42:43 +01:00
Gaelle Braud
2d9f568e3d Fixes :
rename fr translation

set enableVideo to false if audio call #LINQT-2086

try to fix crash on macos when switching from call window to main window (#LINQT-2077 and #LINQT-2087)

set error message when not able to download attached file

fix remove chat from list
fix new chat connection after first message sent #LINQT-2090
clean code + invalidate() for Qt6.10 compatibility

fix translations
2025-10-30 17:07:15 +01:00
Simon Morlat
d4ce80f8c6 README.md mentions QT6 additional modules. 2025-10-30 11:13:45 +00:00
Gaelle Braud
eb9fa8aefe update German and French translations (Mantis + #LINQT-2092) 2025-10-30 11:40:17 +01:00
gaelle
cb63b1a112 set enableVideo to false if audio call #LINQT-2086 2025-10-27 18:34:10 +01:00
Gaelle Braud
b3135ea177 Fixes:
fix wrong thread function call

hide recordings button while not implemented

display error message if cannot retrieve remote provisioning
2025-10-27 14:37:31 +01:00
276 changed files with 26583 additions and 7271 deletions

View file

@ -1,5 +1,5 @@
.factorize_ubuntu2204: &docker_image_platform_and_runner_tag .factorize_ubuntu2204: &docker_image_platform_and_runner_tag
tags: [ "docker" ] tags: [ "docker-flat" ]
image: gitlab.linphone.org:4567/bc/public/linphone-desktop/bc-dev-ubuntu-22-04-lts:$UBUNTU_2204_IMAGE_VERSION image: gitlab.linphone.org:4567/bc/public/linphone-desktop/bc-dev-ubuntu-22-04-lts:$UBUNTU_2204_IMAGE_VERSION
ubuntu2204-ninja-gcc: ubuntu2204-ninja-gcc:
@ -71,7 +71,7 @@ ubuntu2204-makefile-gcc-signed:
################################################# #################################################
ubuntu2204-makefile-gcc-package: ubuntu2204-makefile-gcc-package:
tags: [ "docker" ] tags: [ "docker-flat" ]
image: gitlab.linphone.org:4567/bc/public/linphone-desktop/bc-dev-ubuntu-22-04-lts:$UBUNTU_2204_IMAGE_VERSION image: gitlab.linphone.org:4567/bc/public/linphone-desktop/bc-dev-ubuntu-22-04-lts:$UBUNTU_2204_IMAGE_VERSION
needs: [] needs: []
rules: rules:
@ -100,7 +100,7 @@ ubuntu2204-makefile-gcc-deploy:
ubuntu2204-makefile-gcc-plugins-deploy: ubuntu2204-makefile-gcc-plugins-deploy:
stage: deploy stage: deploy
tags: [ "deploy" ] tags: [ "deploy-flat" ]
needs: needs:
- ubuntu2204-makefile-gcc - ubuntu2204-makefile-gcc
only: only:

View file

@ -30,12 +30,11 @@
rm -f file.key rm -f file.key
.deploy_linux: &deploy_linux | .deploy_linux: &deploy_linux |
rsync -rlv --ignore-existing build/OUTPUT/Packages/*.AppImage $DEPLOY_SERVER:$UPLOAD_ROOT_PATH/$LINUX_PLATFORM/$APP_FOLDER rsync -rlv --chmod=Fu=rw,Fg=r,Fo=r --ignore-existing build/OUTPUT/Packages/*.AppImage $DEPLOY_SERVER:$UPLOAD_ROOT_PATH/$LINUX_PLATFORM/$APP_FOLDER
|- if [[ $MAKE_RELEASE_FILE_URL != "" ]]; then
if [[ $MAKE_RELEASE_FILE_URL != "" ]]; then rsync -rlv --chmod=Fu=rw,Fg=r,Fo=r build/OUTPUT/Packages/RELEASE $DEPLOY_SERVER:$UPLOAD_ROOT_PATH/$LINUX_PLATFORM
rsync -rlv build/OUTPUT/Packages/RELEASE $DEPLOY_SERVER:$UPLOAD_ROOT_PATH/$LINUX_PLATFORM rsync -rlv --chmod=Fu=rw,Fg=r,Fo=r build/OUTPUT/Packages/RELEASE $MAIN_DEPLOY_SERVER:$UPLOAD_ROOT_PATH/$LINUX_PLATFORM
rsync -rlv build/OUTPUT/Packages/RELEASE $MAIN_DEPLOY_SERVER:$UPLOAD_ROOT_PATH/$LINUX_PLATFORM fi
fi
.linux-desktop: .linux-desktop:
stage: build stage: build
@ -74,6 +73,6 @@
.linux-deploy: .linux-deploy:
stage: deploy stage: deploy
tags: [ "deploy" ] tags: [ "deploy-flat" ]
script: script:
- *deploy_linux - *deploy_linux

View file

@ -26,7 +26,7 @@
.macosx-desktop: .macosx-desktop:
stage: build stage: build
tags: [ "macos-min-xcode12.2" ] tags: [ "macmini-m1-xcode15-flat" ]
rules: rules:
- if: $CI_PIPELINE_SOURCE == "merge_request_event" && $DOCKER_UPDATE == null && $SKIP_MACOSX == null - if: $CI_PIPELINE_SOURCE == "merge_request_event" && $DOCKER_UPDATE == null && $SKIP_MACOSX == null
- if: $CI_PIPELINE_SOURCE == "schedule" && $DOCKER_UPDATE == null && $SKIP_MACOSX == null - if: $CI_PIPELINE_SOURCE == "schedule" && $DOCKER_UPDATE == null && $SKIP_MACOSX == null
@ -93,7 +93,7 @@ macosx-ninja-novideo:
# WAIT for QT6 for arm64 # WAIT for QT6 for arm64
macosx-ninja-package: macosx-ninja-package:
stage: package stage: package
tags: [ "macos-min-xcode12.2" ] tags: [ "macmini-m1-xcode15-flat" ]
needs: [] needs: []
rules: rules:
- !reference [.rules-merge-request-manual, rules] - !reference [.rules-merge-request-manual, rules]
@ -102,7 +102,7 @@ macosx-ninja-package:
- if: $PACKAGE_MACOSX - if: $PACKAGE_MACOSX
- if: $DEPLOY_MACOSX - if: $DEPLOY_MACOSX
variables: variables:
CMAKE_OPTIONS: -DPython3_ROOT_DIR=/opt/bc/pip-packages/ -DENABLE_APP_PACKAGING=ON -DENABLE_GPL_THIRD_PARTIES=OFF -DENABLE_G729=ON CMAKE_OPTIONS: -DPython3_ROOT_DIR=/opt/bc/pip-packages/ -DENABLE_APP_PACKAGING=ON -DENABLE_GPL_THIRD_PARTIES=OFF -DENABLE_G729=ON -DENABLE_BUGSPLAT_SYMBOLS_UPLOAD=ON -DBUGSPLAT_CLIENT_ID=$BUGSPLAT_CLIENT_ID -DBUGSPLAT_CLIENT_SECRET=$BUGSPLAT_CLIENT_SECRET -DBUGSPLAT_DATABASE=$BUGSPLAT_DATABASE
RELEASE_FILE: -DLINPHONE_SDK_MAKE_RELEASE_FILE_URL=$MAKE_RELEASE_FILE_URL/$MACOSX_PLATFORM/$APP_FOLDER RELEASE_FILE: -DLINPHONE_SDK_MAKE_RELEASE_FILE_URL=$MAKE_RELEASE_FILE_URL/$MACOSX_PLATFORM/$APP_FOLDER
extends: macosx-ninja extends: macosx-ninja
script: script:
@ -117,7 +117,7 @@ macosx-ninja-package:
macosx-codesigning: macosx-codesigning:
stage: signing stage: signing
tags: [ "macos-min-xcode12.2" ] tags: [ "macmini-m1-xcode15-flat" ]
needs: needs:
- macosx-ninja-package - macosx-ninja-package
rules: rules:
@ -142,7 +142,7 @@ macosx-codesigning:
macosx-deploy: macosx-deploy:
stage: deploy stage: deploy
tags: [ "macos-min-xcode12.2" ] tags: [ "macmini-m1-xcode15-flat" ]
needs: needs:
- macosx-codesigning - macosx-codesigning
only: only:
@ -160,7 +160,7 @@ macosx-deploy:
macosx-makefile-plugins-deploy: macosx-makefile-plugins-deploy:
stage: deploy stage: deploy
tags: [ "macos-min-xcode12.2" ] tags: [ "macmini-m1-xcode15-flat" ]
needs: needs:
- macosx-makefile - macosx-makefile
only: only:

View file

@ -92,11 +92,11 @@
.windows-vs2022: .windows-vs2022:
extends: .windows-vs extends: .windows-vs
tags: [ "windows-powershell-vs-17-2022" ] tags: [ "windows-powershell-vs-17-2022-flat" ]
.windows-codesigning: .windows-codesigning:
extends: .prepare extends: .prepare
tags: [ "windows-powershell-vs-17-2022-apps" ] tags: [ "windows-powershell-vs-17-2022-apps-flat" ]
.windows-msbuild-variables: .windows-msbuild-variables:
variables: variables:
@ -180,14 +180,22 @@ win64-ninja-vs2022-scheduled-windows:
needs: [] needs: []
rules: rules:
- !reference [.rules-merge-request-manual, rules] - !reference [.rules-merge-request-manual, rules]
- if: $NIGHTLY_MASTER
- if: $NIGHTLY_RELEASE - if: $NIGHTLY_RELEASE
- if: $PACKAGE_WINDOWS - if: $PACKAGE_WINDOWS
- if: $DEPLOY_WINDOWS
variables: variables:
CMAKE_OPTIONS: -DENABLE_APP_PACKAGING=YES -DENABLE_G729=ON -DENABLE_PQCRYPTO=ON -DENABLE_GPL_THIRD_PARTIES=OFF CMAKE_OPTIONS: -DENABLE_APP_PACKAGING=YES -DENABLE_G729=ON -DENABLE_PQCRYPTO=ON -DENABLE_GPL_THIRD_PARTIES=OFF
RELEASE_FILE: -DLINPHONE_SDK_MAKE_RELEASE_FILE_URL=$MAKE_RELEASE_FILE_URL/$WINDOWS_PLATFORM/$APP_FOLDER RELEASE_FILE: -DLINPHONE_SDK_MAKE_RELEASE_FILE_URL=$MAKE_RELEASE_FILE_URL/$WINDOWS_PLATFORM/$APP_FOLDER
.vs-win64-package-cr:
stage: package
needs: []
rules:
- if: $NIGHTLY_MASTER
- if: $DEPLOY_WINDOWS
variables:
CMAKE_OPTIONS: -DENABLE_APP_PACKAGING=YES -DENABLE_G729=ON -DENABLE_PQCRYPTO=ON -DENABLE_GPL_THIRD_PARTIES=OFF -DENABLE_BUGSPLAT_SYMBOLS_UPLOAD=ON -DBUGSPLAT_CLIENT_ID=$BUGSPLAT_CLIENT_ID -DBUGSPLAT_CLIENT_SECRET=$BUGSPLAT_CLIENT_SECRET -DBUGSPLAT_DATABASE=$BUGSPLAT_DATABASE
RELEASE_FILE: -DLINPHONE_SDK_MAKE_RELEASE_FILE_URL=$MAKE_RELEASE_FILE_URL/$WINDOWS_PLATFORM/$APP_FOLDER
win64-ninja-vs2022-package-windows: win64-ninja-vs2022-package-windows:
variables: variables:
CMAKE_GENERATOR: "Ninja" CMAKE_GENERATOR: "Ninja"
@ -198,6 +206,18 @@ win64-ninja-vs2022-package-windows:
- .windows-ninja-variables - .windows-ninja-variables
- .vs-win64-package - .vs-win64-package
#Packaging for deployment (Upload symbols at the same time of the packaging)
win64-ninja-vs2022-package-cr-windows:
variables:
CMAKE_GENERATOR: "Ninja"
CMAKE_ARCHITECTURE: ""
PARALLEL_OPTIONS: ""
extends:
- .windows-vs2022
- .windows-ninja-variables
- .vs-win64-package-cr
################################################# #################################################
# SIGNING # SIGNING
################################################# #################################################
@ -213,8 +233,28 @@ win64-codesigning:
MINGW_TYPE: mingw64 MINGW_TYPE: mingw64
rules: rules:
- !reference [.rules-merge-request-manual, rules] - !reference [.rules-merge-request-manual, rules]
- if: $NIGHTLY_MASTER
- if: $PACKAGE_WINDOWS - if: $PACKAGE_WINDOWS
script:
- cd build-desktop/OUTPUT/Packages/
- Invoke-Expression "& ${WINDOWS_SIGN_TOOL} sign /fd SHA256 /t ${WINDOWS_SIGN_TIMESTAMP_URL} /sha1 ${WINDOWS_SIGN_HASH} *.exe"
- 'if (-not ($LastExitCode -eq 0)) {throw "Error: Signature failed"}'
artifacts:
paths:
- build-desktop\OUTPUT\Packages\*
when: always
expire_in: 1 week
win64-codesigning-cr:
stage: signing
allow_failure: true
extends:
- .windows-codesigning
needs:
- win64-ninja-vs2022-package-cr-windows
variables:
MINGW_TYPE: mingw64
rules:
- if: $NIGHTLY_MASTER
- if: $DEPLOY_WINDOWS - if: $DEPLOY_WINDOWS
script: script:
- cd build-desktop/OUTPUT/Packages/ - cd build-desktop/OUTPUT/Packages/
@ -232,25 +272,25 @@ win64-codesigning:
.win64-upload: .win64-upload:
stage: deploy stage: deploy
tags: [ "windows-powershell" ] tags: [ "windows-powershell-flat" ]
rules: rules:
- if: $NIGHTLY_MASTER - if: $NIGHTLY_MASTER
- if: $DEPLOY_WINDOWS - if: $DEPLOY_WINDOWS
script: script:
- scp -pr build-desktop/OUTPUT/Packages/*.exe ${DEPLOY_SERVER}:${UPLOAD_ROOT_PATH}/${WINDOWS_PLATFORM}/${APP_FOLDER} - rsync --perms --chmod=Fu=rw,Fg=r,Fo=r build-desktop/OUTPUT/Packages/*.exe ${DEPLOY_SERVER}:${UPLOAD_ROOT_PATH}/${WINDOWS_PLATFORM}/${APP_FOLDER}
- if ($MAKE_RELEASE_FILE_URL) { scp -pr build-desktop/OUTPUT/Packages/RELEASE ${DEPLOY_SERVER}:${UPLOAD_ROOT_PATH}/${WINDOWS_PLATFORM}/ } - if ($MAKE_RELEASE_FILE_URL) { rsync --perms --chmod=Fu=rw,Fg=r,Fo=r build-desktop/OUTPUT/Packages/RELEASE ${DEPLOY_SERVER}:${UPLOAD_ROOT_PATH}/${WINDOWS_PLATFORM}/ }
- if ($MAKE_RELEASE_FILE_URL) { scp -pr build-desktop/OUTPUT/Packages/RELEASE ${MAIN_DEPLOY_SERVER}:${UPLOAD_ROOT_PATH}/${WINDOWS_PLATFORM}/ } - if ($MAKE_RELEASE_FILE_URL) { rsync --perms --chmod=Fu=rw,Fg=r,Fo=r build-desktop/OUTPUT/Packages/RELEASE ${MAIN_DEPLOY_SERVER}:${UPLOAD_ROOT_PATH}/${WINDOWS_PLATFORM}/ }
win64-ninja-vs2022-upload: win64-ninja-vs2022-upload:
extends: extends:
- .win64-upload - .win64-upload
needs: needs:
- win64-codesigning - win64-codesigning-cr
.win64-plugins-upload: .win64-plugins-upload:
stage: deploy stage: deploy
tags: [ "windows" ] tags: [ "windows-flat" ]
rules: rules:
- if: $DEPLOY_PLUGINS - if: $DEPLOY_PLUGINS
script: script:

View file

@ -21,7 +21,7 @@ variables:
#CMAKE_OPTIONS: -DENABLE_LIME_X3DH=YES #CMAKE_OPTIONS: -DENABLE_LIME_X3DH=YES
# Docker image version # Docker image version
UBUNTU_2204_IMAGE_VERSION: 20250930_add_commercial_qt_versions_5.15.14_5.15.19_6.8.1_6.8.3_6.9.1 UBUNTU_2204_IMAGE_VERSION: 20251106_add_commercial_qt_version_5.15.14_5.15.19_6.8.1_6.8.3_6.9.1_6.10.0
workflow: workflow:

9
.gitmodules vendored
View file

@ -1,3 +1,12 @@
[submodule "linphone-sdk"] [submodule "linphone-sdk"]
path = external/linphone-sdk path = external/linphone-sdk
url = https://gitlab.linphone.org/BC/public/linphone-sdk.git url = https://gitlab.linphone.org/BC/public/linphone-sdk.git
[submodule "external/google/gn"]
path = external/google/gn
url = https://gitlab.linphone.org/BC/public/external/gn.git
[submodule "external/google/crashpad"]
path = external/google/crashpad
url = https://gitlab.linphone.org/BC/public/external/crashpad.git
[submodule "external/google/chromium-depot-tools"]
path = external/google/chromium-depot-tools
url = https://gitlab.linphone.org/BC/public/external/chromium-depot-tools.git

View file

@ -10,6 +10,26 @@ Group changes to describe their impact on the project, as follows:
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.
## [6.1.0] - Unreleased
6.1.0 release is the complete version of the new Linphone Desktop with all features including chat
### Added
- Chat: chat with your contacts, including text messaging, voice recording, sharing files or medias
- Presence: get your friend's presence status as long as you both are in your contact list
- Translations: Linphone is now available in English, French, Chinese, Czech, German, Portuguese, Russian and Ukrainian thank's to the Weblate contributors
- Check for update : you will get a notification on start if a new version is available, and you can look for a new version from the help page
- Bugsplat integration: add Bugsplat database parameters to improve crash reporting.
### Fixed
- Fixed "End-to-end encrypted call" label while in conference, the call may be end-to-end encrypted but only to the conference server, not to all participants
- Audio device list : display the correct devices in multimedia settings according to their functions (capture / playback / video)
### Changed
- Minimum supported Qt version is now 6.10.0
- Removed QtMultimedia dependency
## [6.0.0] - 2025-04-17 ## [6.0.0] - 2025-04-17
6.0.0 release is a complete rework of Linphone Desktop, with only the call and contact list features availables 6.0.0 release is a complete rework of Linphone Desktop, with only the call and contact list features availables

View file

@ -60,7 +60,6 @@ project(linphoneqt)
include(GNUInstallDirs) include(GNUInstallDirs)
include(CheckCXXCompilerFlag) include(CheckCXXCompilerFlag)
include(Linphone/application_info.cmake)
set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD 17)
if(LINPHONEAPP_INSTALL_PREFIX) if(LINPHONEAPP_INSTALL_PREFIX)
@ -72,8 +71,11 @@ endif()
set(CMAKE_INSTALL_PREFIX "${APPLICATION_OUTPUT_DIR}") set(CMAKE_INSTALL_PREFIX "${APPLICATION_OUTPUT_DIR}")
set(LINPHONEAPP_APPLICATION_NAME "Linphone6" CACHE STRING "Application name" ) set(LINPHONEAPP_APPLICATION_NAME "Linphone" CACHE STRING "Application name" )
set(LINPHONEAPP_EXECUTABLE_NAME "linphone6" CACHE STRING "Executable name" ) set(LINPHONEAPP_EXECUTABLE_NAME "linphone" CACHE STRING "Executable name" )
# Include application_info.cmake here as the LINPHONEAPP_APPLICATION_NAME variable must be known as it is set to the APPLICATION_NAME variable.
include(Linphone/application_info.cmake)
if( APPLE ) if( APPLE )
set(LINPHONEAPP_MACOS_ARCHS "arm64" CACHE STRING "MacOS architectures to build: comma-separated list of values in [arm64, x86_64]") set(LINPHONEAPP_MACOS_ARCHS "arm64" CACHE STRING "MacOS architectures to build: comma-separated list of values in [arm64, x86_64]")
@ -175,6 +177,7 @@ add_option(OPTION_LIST ENABLE_UNIT_TESTS "Enable unit test of SDK." OFF)
add_option(OPTION_LIST ENABLE_UPDATE_CHECK "Enable update check." ON) add_option(OPTION_LIST ENABLE_UPDATE_CHECK "Enable update check." ON)
add_option(OPTION_LIST ENABLE_VIDEO "Enable Video support." YES) add_option(OPTION_LIST ENABLE_VIDEO "Enable Video support." YES)
add_option(OPTION_LIST ENABLE_WINDOWS_TOOLS_CHECK "Enable tools checks on Windows for auto install." OFF) add_option(OPTION_LIST ENABLE_WINDOWS_TOOLS_CHECK "Enable tools checks on Windows for auto install." OFF)
add_option(OPTION_LIST ENABLE_CRASH_HANDLER "Enable Crash Handler" YES)
add_cache(OPTION_LIST LINPHONE_SDK_MAKE_RELEASE_FILE_URL "Make a RELEASE file that work along check_version and use this URL" "") add_cache(OPTION_LIST LINPHONE_SDK_MAKE_RELEASE_FILE_URL "Make a RELEASE file that work along check_version and use this URL" "")
@ -182,6 +185,13 @@ add_cache(OPTION_LIST LINPHONE_SDK_MAKE_RELEASE_FILE_URL "Make a RELEASE file th
add_option(OPTION_LIST ENABLE_OPENH264 "Enable the use of OpenH264 codec" ${ENABLE_VIDEO}) add_option(OPTION_LIST ENABLE_OPENH264 "Enable the use of OpenH264 codec" ${ENABLE_VIDEO})
add_option(OPTION_LIST ENABLE_SCREENSHARING "Enable screen sharing." ${ENABLE_VIDEO}) add_option(OPTION_LIST ENABLE_SCREENSHARING "Enable screen sharing." ${ENABLE_VIDEO})
add_option(OPTION_LIST ENABLE_BUGSPLAT_SYMBOLS_UPLOAD "Generate and upload symbols to Bugsplat." OFF)
add_cache(OPTION_LIST BUGSPLAT_CLIENT_ID "Client ID for Bugsplat." "")
add_cache(OPTION_LIST BUGSPLAT_CLIENT_SECRET "Client Secret for Bugsplat." "")
add_cache(OPTION_LIST BUGSPLAT_DATABASE "Database name for Bugsplat." "Linphone")
# QtKeychain # QtKeychain
add_option(OPTION_LIST LIBSECRET_SUPPORT "Build with libsecret support" OFF) # Need libsecret-devel add_option(OPTION_LIST LIBSECRET_SUPPORT "Build with libsecret support" OFF) # Need libsecret-devel
if(WIN32) if(WIN32)
@ -206,7 +216,7 @@ set(ENABLE_CSHARP_WRAPPER OFF CACHE BOOL "Build the CSharp wrapper for Liblinpho
set(ENABLE_THEORA OFF) set(ENABLE_THEORA OFF)
set(ENABLE_QT_GL ${ENABLE_VIDEO}) set(ENABLE_QT_GL ${ENABLE_VIDEO})
find_package(Qt6 REQUIRED COMPONENTS Core Quick Widgets Core5Compat) find_package(Qt6 REQUIRED COMPONENTS Core Quick Widgets)
if(NOT Qt6_FOUND) if(NOT Qt6_FOUND)
message(FATAL_ERROR "Minimum supported Qt6!") message(FATAL_ERROR "Minimum supported Qt6!")
@ -224,8 +234,8 @@ if(LINPHONEAPP_BUILD_TYPE STREQUAL "Default")
endif() endif()
if(NOT APPLE OR MONO_ARCH) if(NOT APPLE OR MONO_ARCH)
add_custom_target(linphone-deps)
if(NOT LINPHONE_QT_ONLY) if(NOT LINPHONE_QT_ONLY)
function(add_external) function(add_external)
set(CMAKE_POLICY_DEFAULT_CMP0077 NEW) # Prevent project from overriding the options we just set here set(CMAKE_POLICY_DEFAULT_CMP0077 NEW) # Prevent project from overriding the options we just set here
@ -233,6 +243,9 @@ if(NOT APPLE OR MONO_ARCH)
endfunction() endfunction()
add_external() add_external()
endif() endif()
if(TARGET Crashpad)
set(HAVE_CRASH_HANDLER 1)
endif()
function(add_linphone_app) function(add_linphone_app)
set(CMAKE_POLICY_DEFAULT_CMP0077 NEW) # Prevent project from overriding the options we just set here set(CMAKE_POLICY_DEFAULT_CMP0077 NEW) # Prevent project from overriding the options we just set here
if(UNIX) if(UNIX)

View file

@ -1,6 +1,6 @@
cmake_minimum_required(VERSION 3.16) cmake_minimum_required(VERSION 3.16)
project(Linphone VERSION 6.1.0 LANGUAGES CXX) project(Linphone VERSION 6.2.0 LANGUAGES CXX)
################################################################ ################################################################
# PACKAGES # PACKAGES
@ -22,7 +22,7 @@ set(APP_TARGETS ${LinphoneCxx_TARGET}
${LibLinphone_TARGET})#Liblinphone ${LibLinphone_TARGET})#Liblinphone
set(QT_DEFAULT_MAJOR_VERSION 6) set(QT_DEFAULT_MAJOR_VERSION 6)
set(QT_PACKAGES Quick Qml Widgets Svg Multimedia Test NetworkAuth Concurrent Core5Compat)# Search Core at first for initialize Qt scripts for next find_packages. set(QT_PACKAGES Quick Qml Widgets Svg Test NetworkAuth Concurrent)# Search Core at first for initialize Qt scripts for next find_packages.
if (UNIX AND NOT APPLE) if (UNIX AND NOT APPLE)
list(APPEND QT_PACKAGES DBus) list(APPEND QT_PACKAGES DBus)
endif() endif()
@ -96,18 +96,11 @@ else()
set(MSPLUGINS_DIR "${CMAKE_INSTALL_LIBDIR}/mediastreamer/plugins") set(MSPLUGINS_DIR "${CMAKE_INSTALL_LIBDIR}/mediastreamer/plugins")
endif() endif()
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/config.h.cmake" "${CMAKE_CURRENT_BINARY_DIR}/config.h") if(${Qt6_VERSION} VERSION_LESS "6.10.0")
message( FATAL_ERROR "Linphone requires Qt 6.10.0 or newer. Exiting CMake." )
if(${Qt6_VERSION} VERSION_LESS "6.3.0")
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)
set(CMAKE_AUTOUIC ON)
else()
qt6_standard_project_setup()
endif() endif()
qt6_standard_project_setup()
################################################################ ################################################################
# SOURCES # SOURCES
@ -128,6 +121,7 @@ add_subdirectory(core)
set(LANGUAGES_DIRECTORY "data/languages") set(LANGUAGES_DIRECTORY "data/languages")
set(I18N_FILENAME i18n.qrc) set(I18N_FILENAME i18n.qrc)
set(LANGUAGES en fr de) set(LANGUAGES en fr de)
set(CMAKE_CPP_COMPILER gcc)
# Add languages support. # Add languages support.
add_subdirectory("${LANGUAGES_DIRECTORY}" "data/languages") add_subdirectory("${LANGUAGES_DIRECTORY}" "data/languages")
@ -158,7 +152,7 @@ qt6_add_big_resources(_LINPHONEAPP_FONTS_FILES data/fonts.qrc)
list(APPEND _LINPHONEAPP_FONTS_FILES data/fonts.qrc) list(APPEND _LINPHONEAPP_FONTS_FILES data/fonts.qrc)
set_property(SOURCE data/fonts.qrc PROPERTY SKIP_AUTORCC ON) set_property(SOURCE data/fonts.qrc PROPERTY SKIP_AUTORCC ON)
qt6_add_executable(Linphone qt6_add_executable(${TARGET_NAME}
${_LINPHONEAPP_SOURCES} ${_LINPHONEAPP_SOURCES}
${_LINPHONEAPP_FONTS_FILES} ${_LINPHONEAPP_FONTS_FILES}
${_APPDETAILS_RC_FILE} ${_APPDETAILS_RC_FILE}
@ -177,6 +171,16 @@ qt6_add_qml_module(Linphone
qt6_add_resources(Linphone "resources" PREFIX "/" FILES ${_LINPHONEAPP_RC_FILES}) qt6_add_resources(Linphone "resources" PREFIX "/" FILES ${_LINPHONEAPP_RC_FILES})
set_property(TARGET Linphone PROPERTY POSITION_INDEPENDENT_CODE ON) #Need by Qt set_property(TARGET Linphone PROPERTY POSITION_INDEPENDENT_CODE ON) #Need by Qt
################################################################
# CRASHPAD
################################################################
if (HAVE_CRASH_HANDLER)
add_dependencies(${TARGET_NAME} Crashpad)
target_link_libraries(${TARGET_NAME} PRIVATE Crashpad)
get_target_property(CRASHPAD_EXECUTABLE_NAME Crashpad CRASHPAD_EXECUTABLE_NAME)
endif()
################################################################ ################################################################
# TARGETS LINKS # TARGETS LINKS
################################################################ ################################################################
@ -199,6 +203,7 @@ set_target_properties(${TARGET_NAME} PROPERTIES
if(MSVC) if(MSVC)
set_target_properties(${TARGET_NAME} PROPERTIES PDB_NAME "${EXECUTABLE_NAME}_app") set_target_properties(${TARGET_NAME} PROPERTIES PDB_NAME "${EXECUTABLE_NAME}_app")
endif() endif()
set_target_properties(${TARGET_NAME} PROPERTIES WINDOWS_EXPORT_ALL_SYMBOLS ON)
if(WIN32) if(WIN32)
if(ENABLE_SCREENSHARING) if(ENABLE_SCREENSHARING)
@ -220,3 +225,14 @@ foreach(T ${QT_PACKAGES})
endforeach() endforeach()
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/config.h.cmake" "${CMAKE_CURRENT_BINARY_DIR}/config.h")
if (WIN32)
if(MSVC AND (CMAKE_BUILD_TYPE STREQUAL "Debug" OR CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo"))
install(FILES "$<TARGET_PDB_FILE:${TARGET_NAME}>" DESTINATION ${CMAKE_INSTALL_BINDIR})
foreach(T ${APP_TARGETS})
install(FILES "$<TARGET_PDB_FILE:${T}>" DESTINATION ${CMAKE_INSTALL_BINDIR})
endforeach ()
endif()
endif()

View file

@ -39,3 +39,6 @@
#cmakedefine LINPHONEAPP_SHORT_VERSION "${LINPHONEAPP_SHORT_VERSION}" #cmakedefine LINPHONEAPP_SHORT_VERSION "${LINPHONEAPP_SHORT_VERSION}"
#cmakedefine GIT_BRANCH_NAME "${GIT_BRANCH_NAME}" #cmakedefine GIT_BRANCH_NAME "${GIT_BRANCH_NAME}"
#cmakedefine LINPHONESDK_VERSION "${LINPHONESDK_VERSION}" #cmakedefine LINPHONESDK_VERSION "${LINPHONESDK_VERSION}"
#cmakedefine HAVE_CRASH_HANDLER "${HAVE_CRASH_HANDLER}"
#cmakedefine CRASHPAD_EXECUTABLE_NAME "${CRASHPAD_EXECUTABLE_NAME}"
#cmakedefine BUGSPLAT_DATABASE "${BUGSPLAT_DATABASE}"

View file

@ -101,6 +101,9 @@
#include "tool/accessibility/AccessibilityHelper.hpp" #include "tool/accessibility/AccessibilityHelper.hpp"
#include "tool/accessibility/FocusHelper.hpp" #include "tool/accessibility/FocusHelper.hpp"
#include "tool/accessibility/KeyboardShortcuts.hpp" #include "tool/accessibility/KeyboardShortcuts.hpp"
#ifdef HAVE_CRASH_HANDLER
#include "tool/crash_reporter/CrashReporter.hpp"
#endif
#include "tool/native/DesktopTools.hpp" #include "tool/native/DesktopTools.hpp"
#include "tool/providers/AvatarProvider.hpp" #include "tool/providers/AvatarProvider.hpp"
#include "tool/providers/EmojiProvider.hpp" #include "tool/providers/EmojiProvider.hpp"
@ -267,9 +270,7 @@ void App::setAutoStart(bool enabled) {
void App::setAutoStart(bool enabled) { void App::setAutoStart(bool enabled) {
QSettings settings(AutoStartSettingsFilePath, QSettings::NativeFormat); QSettings settings(AutoStartSettingsFilePath, QSettings::NativeFormat);
QString parameters; if (enabled) settings.setValue(EXECUTABLE_NAME, QDir::toNativeSeparators(applicationFilePath()));
if (!mSettings->getExitOnClose()) parameters = " --minimized";
if (enabled) settings.setValue(EXECUTABLE_NAME, QDir::toNativeSeparators(applicationFilePath()) + parameters);
else settings.remove(EXECUTABLE_NAME); else settings.remove(EXECUTABLE_NAME);
mAutoStart = enabled; mAutoStart = enabled;
@ -287,16 +288,29 @@ App::App(int &argc, char *argv[])
: SingleApplication(argc, argv, true, Mode::User | Mode::ExcludeAppPath | Mode::ExcludeAppVersion) { : SingleApplication(argc, argv, true, Mode::User | Mode::ExcludeAppPath | Mode::ExcludeAppVersion) {
// Do not use APPLICATION_NAME here. // Do not use APPLICATION_NAME here.
// The EXECUTABLE_NAME will be used in qt standard paths. It's our goal. // The EXECUTABLE_NAME will be used in qt standard paths. It's our goal.
QDir::setCurrent(
QCoreApplication::applicationDirPath()); // Set working directory as the executable to allow relative paths.
QThread::currentThread()->setPriority(QThread::HighPriority); QThread::currentThread()->setPriority(QThread::HighPriority);
qDebug() << "app thread is" << QThread::currentThread(); qDebug() << "app thread is" << QThread::currentThread();
lDebug() << "Starting app with Qt version" << qVersion();
QCoreApplication::setApplicationName(EXECUTABLE_NAME); QCoreApplication::setApplicationName(EXECUTABLE_NAME);
QApplication::setOrganizationDomain(EXECUTABLE_NAME); QApplication::setOrganizationDomain(EXECUTABLE_NAME);
QCoreApplication::setApplicationVersion(APPLICATION_SEMVER); QCoreApplication::setApplicationVersion(APPLICATION_SEMVER);
// CarshReporter must be call after app initialization like names.
#ifdef HAVE_CRASH_HANDLER
CrashReporter::start();
#else
lWarning() << "[Main] The application doesn't support the CrashReporter.";
#endif
// If not OpenGL, createRender is never call. // If not OpenGL, createRender is never call.
QQuickWindow::setGraphicsApi(QSGRendererInterface::OpenGL); QQuickWindow::setGraphicsApi(QSGRendererInterface::OpenGL);
setWindowIcon(QIcon(Constants::WindowIconPath)); setWindowIcon(QIcon(Constants::WindowIconPath));
initFonts(); initFonts();
//------------------- //-------------------
mOIDCRefreshTimer.setInterval(1000);
mOIDCRefreshTimer.setSingleShot(false);
mLinphoneThread = new Thread(this); mLinphoneThread = new Thread(this);
init(); init();
@ -305,7 +319,7 @@ App::App(int &argc, char *argv[])
.arg(applicationVersion()) .arg(applicationVersion())
.arg(Utils::getOsProduct()) .arg(Utils::getOsProduct())
.arg(qVersion()); .arg(qVersion());
lInfo() << "at " << QDir().absolutePath();
mCurrentDate = QDate::currentDate(); mCurrentDate = QDate::currentDate();
mAutoStart = autoStartEnabled(); mAutoStart = autoStartEnabled();
mDateUpdateTimer.setInterval(60000); mDateUpdateTimer.setInterval(60000);
@ -336,7 +350,7 @@ void App::setSelf(QSharedPointer<App>(me)) {
auto callCore = CallCore::create(call); auto callCore = CallCore::create(call);
mCoreModelConnection->invokeToCore([this, callCore] { mCoreModelConnection->invokeToCore([this, callCore] {
auto callGui = new CallGui(callCore); auto callGui = new CallGui(callCore);
auto win = getCallsWindow(QVariant::fromValue(callGui)); auto win = getOrCreateCallsWindow(QVariant::fromValue(callGui));
Utils::smartShowWindow(win); Utils::smartShowWindow(win);
auto mainwin = getMainWindow(); auto mainwin = getMainWindow();
QMetaObject::invokeMethod(mainwin, "callCreated"); QMetaObject::invokeMethod(mainwin, "callCreated");
@ -375,9 +389,29 @@ void App::setSelf(QSharedPointer<App>(me)) {
mCoreModelConnection->makeConnectToModel( mCoreModelConnection->makeConnectToModel(
&CoreModel::globalStateChanged, &CoreModel::globalStateChanged,
[this](const std::shared_ptr<linphone::Core> &core, linphone::GlobalState gstate, const std::string &message) { [this](const std::shared_ptr<linphone::Core> &core, linphone::GlobalState gstate, const std::string &message) {
if (gstate == linphone::GlobalState::On) { mCoreModelConnection->invokeToCore([this, gstate] {
mCoreModelConnection->invokeToCore([this] { setCoreStarted(true); }); setCoreStarted(gstate == linphone::GlobalState::On);
} if (gstate == linphone::GlobalState::Configuring) {
if (mMainWindow) {
QMetaObject::invokeMethod(mMainWindow, "openSSOPage", Qt::DirectConnection);
} else {
connect(
this, &App::mainWindowChanged, this,
[this] {
mCoreModelConnection->invokeToModel([this] {
auto gstate = CoreModel::getInstance()->getCore()->getGlobalState();
if (gstate == linphone::GlobalState::Configuring)
mCoreModelConnection->invokeToCore([this] {
if (mMainWindow)
QMetaObject::invokeMethod(mMainWindow, "openSSOPage",
Qt::DirectConnection);
});
});
},
Qt::SingleShotConnection);
}
}
});
}); });
mCoreModelConnection->makeConnectToModel(&CoreModel::authenticationRequested, &App::onAuthenticationRequested); mCoreModelConnection->makeConnectToModel(&CoreModel::authenticationRequested, &App::onAuthenticationRequested);
// Config error message // Config error message
@ -385,7 +419,7 @@ void App::setSelf(QSharedPointer<App>(me)) {
&CoreModel::configuringStatus, [this](const std::shared_ptr<linphone::Core> &core, &CoreModel::configuringStatus, [this](const std::shared_ptr<linphone::Core> &core,
linphone::ConfiguringState status, const std::string &message) { linphone::ConfiguringState status, const std::string &message) {
mustBeInLinphoneThread(log().arg(Q_FUNC_INFO)); mustBeInLinphoneThread(log().arg(Q_FUNC_INFO));
if (status == linphone::ConfiguringState::Failed) { if (mIsRestarting && status == linphone::ConfiguringState::Failed) {
mCoreModelConnection->invokeToCore([this, message]() { mCoreModelConnection->invokeToCore([this, message]() {
mustBeInMainThread(log().arg(Q_FUNC_INFO)); mustBeInMainThread(log().arg(Q_FUNC_INFO));
//: Error //: Error
@ -402,12 +436,20 @@ void App::setSelf(QSharedPointer<App>(me)) {
mustBeInLinphoneThread(log().arg(Q_FUNC_INFO)); mustBeInLinphoneThread(log().arg(Q_FUNC_INFO));
if (CoreModel::getInstance()->mConfigStatus == linphone::ConfiguringState::Successful) { if (CoreModel::getInstance()->mConfigStatus == linphone::ConfiguringState::Successful) {
bool accountConnected = account && account->getState() == linphone::RegistrationState::Ok; bool accountConnected = account && account->getState() == linphone::RegistrationState::Ok;
// update settings if case config contains changes
if (mSettings) mSettings->reloadSettings();
mCoreModelConnection->invokeToCore([this, accountConnected]() { mCoreModelConnection->invokeToCore([this, accountConnected]() {
mustBeInMainThread(log().arg(Q_FUNC_INFO)); mustBeInMainThread(log().arg(Q_FUNC_INFO));
// There is an account added by a remote provisioning, force switching to main page // There is an account added by a remote provisioning, force switching to main page
// because the account may not be connected already // because the account may not be connected already
QMetaObject::invokeMethod(mMainWindow, "openMainPage", Qt::DirectConnection, if (mPossiblyLookForAddedAccount) {
Q_ARG(QVariant, accountConnected)); QMetaObject::invokeMethod(mMainWindow, "openMainPage", Qt::DirectConnection,
Q_ARG(QVariant, accountConnected));
}
mPossiblyLookForAddedAccount = false;
// setLocale(mSettings->getConfigLocale());
// setAutoStart(mSettings->getAutoStart());
// setQuitOnLastWindowClosed(mSettings->getExitOnClose());
}); });
} }
}); });
@ -415,7 +457,28 @@ void App::setSelf(QSharedPointer<App>(me)) {
// Synchronize state for because linphoneCore was ran before any connections. // Synchronize state for because linphoneCore was ran before any connections.
mCoreModelConnection->invokeToModel([this]() { mCoreModelConnection->invokeToModel([this]() {
auto state = CoreModel::getInstance()->getCore()->getGlobalState(); auto state = CoreModel::getInstance()->getCore()->getGlobalState();
mCoreModelConnection->invokeToCore([this, state] { setCoreStarted(state == linphone::GlobalState::On); }); mCoreModelConnection->invokeToCore([this, state] {
setCoreStarted(state == linphone::GlobalState::On);
if (state == linphone::GlobalState::Configuring) {
if (mMainWindow) {
QMetaObject::invokeMethod(mMainWindow, "openSSOPage", Qt::DirectConnection);
} else {
connect(
this, &App::mainWindowChanged, this,
[this] {
mCoreModelConnection->invokeToModel([this] {
auto gstate = CoreModel::getInstance()->getCore()->getGlobalState();
if (gstate == linphone::GlobalState::Configuring)
mCoreModelConnection->invokeToCore([this] {
if (mMainWindow)
QMetaObject::invokeMethod(mMainWindow, "openSSOPage", Qt::DirectConnection);
});
});
},
Qt::SingleShotConnection);
}
}
});
}); });
mCoreModelConnection->makeConnectToModel(&CoreModel::unreadNotificationsChanged, [this] { mCoreModelConnection->makeConnectToModel(&CoreModel::unreadNotificationsChanged, [this] {
@ -430,6 +493,66 @@ void App::setSelf(QSharedPointer<App>(me)) {
}); });
}); });
// Check update
mCoreModelConnection->makeConnectToModel(
&CoreModel::versionUpdateCheckResultReceived,
[this](const std::shared_ptr<linphone::Core> &core, linphone::VersionUpdateCheckResult result,
const std::string &version, const std::string &url, bool checkRequestedByUser) {
mustBeInLinphoneThread(log().arg(Q_FUNC_INFO));
mCoreModelConnection->invokeToCore([this, result, version, url, checkRequestedByUser] {
switch (result) {
case linphone::VersionUpdateCheckResult::Error:
Utils::showInformationPopup(tr("info_popup_error_title"),
//: An error occured while trying to check update. Please
//: try again later or contact support team.
tr("info_popup_error_checking_update"), false);
break;
case linphone::VersionUpdateCheckResult::NewVersionAvailable: {
QString downloadLink =
QStringLiteral("<a href='%1'><font color='DefaultStyle.main2_600'>%2</a>")
.arg(url)
//: Download it !
.arg(tr("info_popup_new_version_download_label"));
Utils::showInformationPopup(
//: New version available !
tr("info_popup_new_version_available_title"),
//: A new version of Linphone (%1) is available. %2
tr("info_popup_new_version_available_message").arg(version).arg(downloadLink));
break;
}
case linphone::VersionUpdateCheckResult::UpToDate:
if (checkRequestedByUser)
//: Up to date
Utils::showInformationPopup(tr("info_popup_version_up_to_date_title"),
//: Your version is up to date
tr("info_popup_version_up_to_date_message"));
}
});
});
mCoreModelConnection->makeConnectToModel(&CoreModel::oidcRemainingTimeBeforeTimeoutChanged,
[this](int remainingTime) {
mCoreModelConnection->invokeToCore([this, remainingTime] {
mRemainingTimeBeforeOidcTimeout = remainingTime;
emit remainingTimeBeforeOidcTimeoutChanged();
});
});
mCoreModelConnection->makeConnectToCore(&App::lForceOidcTimeout, [this] {
qDebug() << "App: force oidc timeout";
mCoreModelConnection->invokeToModel([this] { emit CoreModel::getInstance()->forceOidcTimeout(); });
});
mCoreModelConnection->makeConnectToModel(&CoreModel::timeoutTimerStarted, [this]() {
qDebug() << "App: oidc timer started";
mCoreModelConnection->invokeToCore([this] { mOIDCRefreshTimer.start(); });
});
mCoreModelConnection->makeConnectToModel(&CoreModel::timeoutTimerStopped, [this]() {
qDebug() << "App: oidc timer stopped";
mCoreModelConnection->invokeToCore([this] { mOIDCRefreshTimer.stop(); });
});
connect(&mOIDCRefreshTimer, &QTimer::timeout, this, [this]() {
mCoreModelConnection->invokeToModel([this] { CoreModel::getInstance()->refreshOidcRemainingTime(); });
});
//--------------------------------------------------------------------------------------------- //---------------------------------------------------------------------------------------------
mCliModelConnection = SafeConnection<App, CliModel>::create(me, CliModel::getInstance()); mCliModelConnection = SafeConnection<App, CliModel>::create(me, CliModel::getInstance());
mCliModelConnection->makeConnectToCore(&App::receivedMessage, [this](int, const QByteArray &byteArray) { mCliModelConnection->makeConnectToCore(&App::receivedMessage, [this](int, const QByteArray &byteArray) {
@ -452,7 +575,7 @@ App *App::getInstance() {
} }
Thread *App::getLinphoneThread() { Thread *App::getLinphoneThread() {
return App::getInstance()->mLinphoneThread; return App::getInstance() ? App::getInstance()->mLinphoneThread : nullptr;
} }
Notifier *App::getNotifier() const { Notifier *App::getNotifier() const {
@ -523,7 +646,7 @@ void App::initCore() {
lDebug() << log().arg("Creating SettingsModel"); lDebug() << log().arg("Creating SettingsModel");
SettingsModel::create(); SettingsModel::create();
lDebug() << log().arg("Creating SettingsCore"); lDebug() << log().arg("Creating SettingsCore");
if (!settings) settings = SettingsCore::create(); settings = SettingsCore::create();
lDebug() << log().arg("Checking downloaded codecs updates"); lDebug() << log().arg("Checking downloaded codecs updates");
Utils::checkDownloadedCodecsUpdates(); Utils::checkDownloadedCodecsUpdates();
lDebug() << log().arg("Setting Video Codec Priority Policy"); lDebug() << log().arg("Setting Video Codec Priority Policy");
@ -580,45 +703,55 @@ void App::initCore() {
mNotifier = new Notifier(mEngine); mNotifier = new Notifier(mEngine);
mEngine->setObjectOwnership(settings.get(), QQmlEngine::CppOwnership); mEngine->setObjectOwnership(settings.get(), QQmlEngine::CppOwnership);
mEngine->setObjectOwnership(this, QQmlEngine::CppOwnership); mEngine->setObjectOwnership(this, QQmlEngine::CppOwnership);
if (!mAccountList) setAccountList(AccountList::create());
else {
mAccountList->setInitialized(false);
mAccountList->lUpdate(true);
}
// Update global unread Notifications when an account updates his unread Notifications
connect(mAccountList.get(), &AccountList::unreadNotificationsChanged, this, [this]() {
lDebug() << "unreadNotificationsChanged of AccountList";
mCoreModelConnection->invokeToModel([this] {
int n = mEventCountNotifier->getCurrentEventCount();
mCoreModelConnection->invokeToCore([this, n] { mEventCountNotifier->notifyEventCount(n); });
});
});
if (!mCallList) setCallList(CallList::create());
else mCallList->lUpdate();
if (!mSettings) {
mSettings = settings;
setLocale(settings->getConfigLocale());
setAutoStart(settings->getAutoStart());
setQuitOnLastWindowClosed(settings->getExitOnClose());
mEngine->setObjectOwnership(mSettings.get(), QQmlEngine::CppOwnership);
connect(mSettings.get(), &SettingsCore::exitOnCloseChanged, this, &App::onExitOnCloseChanged, connect(this, &App::coreStartedChanged, this, [this] {
Qt::UniqueConnection); if (mCoreStarted) {
QObject::connect(mSettings.get(), &SettingsCore::autoStartChanged, [this]() { if (!mAccountList) setAccountList(AccountList::create());
mustBeInMainThread(log().arg(Q_FUNC_INFO)); else {
setAutoStart(mSettings->getAutoStart()); mAccountList->setInitialized(false);
}); mAccountList->lUpdate(true);
QObject::connect(mSettings.get(), &SettingsCore::configLocaleChanged, [this]() { }
mustBeInMainThread(log().arg(Q_FUNC_INFO)); // Update global unread Notifications when an account updates his unread Notifications
if (mSettings) setLocale(mSettings->getConfigLocale()); connect(mAccountList.get(), &AccountList::unreadNotificationsChanged, this, [this]() {
}); lDebug() << "unreadNotificationsChanged of AccountList";
connect(mSettings.get(), &SettingsCore::exitOnCloseChanged, this, &App::onExitOnCloseChanged, mCoreModelConnection->invokeToModel([this] {
Qt::UniqueConnection); int n = mEventCountNotifier->getCurrentEventCount();
} else { mCoreModelConnection->invokeToCore(
setLocale(settings->getConfigLocale()); [this, n] { mEventCountNotifier->notifyEventCount(n); });
setAutoStart(settings->getAutoStart()); });
setQuitOnLastWindowClosed(settings->getExitOnClose()); });
} if (!mCallList) setCallList(CallList::create());
else mCallList->lUpdate();
if (!mChatList) setChatList(ChatList::create());
else mChatList->lUpdate();
disconnect(this, &App::coreStartedChanged, this, nullptr);
}
});
// if (!mSettings) {
mSettings = settings;
setLocale(settings->getConfigLocale());
setAutoStart(settings->getAutoStart());
setQuitOnLastWindowClosed(settings->getExitOnClose());
mEngine->setObjectOwnership(mSettings.get(), QQmlEngine::CppOwnership);
connect(mSettings.get(), &SettingsCore::exitOnCloseChanged, this, &App::onExitOnCloseChanged,
Qt::UniqueConnection);
QObject::connect(mSettings.get(), &SettingsCore::autoStartChanged, [this]() {
mustBeInMainThread(log().arg(Q_FUNC_INFO));
setAutoStart(mSettings->getAutoStart());
});
QObject::connect(mSettings.get(), &SettingsCore::configLocaleChanged, [this]() {
mustBeInMainThread(log().arg(Q_FUNC_INFO));
if (mSettings) setLocale(mSettings->getConfigLocale());
});
connect(mSettings.get(), &SettingsCore::exitOnCloseChanged, this, &App::onExitOnCloseChanged,
Qt::UniqueConnection);
// } else {
// setLocale(settings->getConfigLocale());
// setAutoStart(settings->getAutoStart());
// setQuitOnLastWindowClosed(settings->getExitOnClose());
// }
const QUrl url("qrc:/qt/qml/Linphone/view/Page/Window/Main/MainWindow.qml"); const QUrl url("qrc:/qt/qml/Linphone/view/Page/Window/Main/MainWindow.qml");
QObject::connect( QObject::connect(
mEngine, &QQmlApplicationEngine::objectCreated, this, mEngine, &QQmlApplicationEngine::objectCreated, this,
@ -646,21 +779,35 @@ void App::initCore() {
} else lInfo() << log().arg("Stay minimized"); } else lInfo() << log().arg("Stay minimized");
firstOpen = false; firstOpen = false;
lInfo() << log().arg("Checking remote provisioning"); lInfo() << log().arg("Checking remote provisioning");
if (CoreModel::getInstance()->mConfigStatus == linphone::ConfiguringState::Failed && if (mIsRestarting) {
mIsRestarting) { if (CoreModel::getInstance()->mConfigStatus == linphone::ConfiguringState::Failed) {
QMetaObject::invokeMethod(thread(), [this]() { QMetaObject::invokeMethod(thread(), [this]() {
auto message = CoreModel::getInstance()->mConfigMessage; auto message = CoreModel::getInstance()->mConfigMessage;
//: not reachable //: not reachable
if (message.isEmpty()) message = tr("configuration_error_detail"); if (message.isEmpty()) message = tr("configuration_error_detail");
mustBeInMainThread(log().arg(Q_FUNC_INFO)); mustBeInMainThread(log().arg(Q_FUNC_INFO));
//: Error //: Error
Utils::showInformationPopup( Utils::showInformationPopup(
tr("info_popup_error_title"), tr("info_popup_error_title"),
//: Remote provisioning failed : %1 //: Remote provisioning failed : %1
tr("info_popup_configuration_failed_message").arg(message), false); tr("info_popup_configuration_failed_message").arg(message), false);
}); });
} else {
mPossiblyLookForAddedAccount = true;
if (mAccountList && mAccountList->getCount() > 0) {
auto defaultConnected =
mAccountList->getDefaultAccountCore() &&
mAccountList->getDefaultAccountCore()->getRegistrationState() ==
LinphoneEnums::RegistrationState::Ok;
QMetaObject::invokeMethod(mMainWindow, "openMainPage", Qt::DirectConnection,
Q_ARG(QVariant, defaultConnected));
}
}
} }
checkForUpdate();
mIsRestarting = false; mIsRestarting = false;
window->show();
window->requestActivate();
//--------------------------------------------------------------------------------------------- //---------------------------------------------------------------------------------------------
lDebug() << log().arg("Creating KeyboardShortcuts"); lDebug() << log().arg("Creating KeyboardShortcuts");
@ -777,7 +924,6 @@ void App::initCppInterfaces() {
qmlRegisterType<CallHistoryProxy>(Constants::MainQmlUri, 1, 0, "CallHistoryProxy"); qmlRegisterType<CallHistoryProxy>(Constants::MainQmlUri, 1, 0, "CallHistoryProxy");
qmlRegisterType<CallGui>(Constants::MainQmlUri, 1, 0, "CallGui"); qmlRegisterType<CallGui>(Constants::MainQmlUri, 1, 0, "CallGui");
qmlRegisterType<CallProxy>(Constants::MainQmlUri, 1, 0, "CallProxy"); qmlRegisterType<CallProxy>(Constants::MainQmlUri, 1, 0, "CallProxy");
qmlRegisterType<ChatList>(Constants::MainQmlUri, 1, 0, "ChatList");
qmlRegisterType<ChatProxy>(Constants::MainQmlUri, 1, 0, "ChatProxy"); qmlRegisterType<ChatProxy>(Constants::MainQmlUri, 1, 0, "ChatProxy");
qmlRegisterType<ChatGui>(Constants::MainQmlUri, 1, 0, "ChatGui"); qmlRegisterType<ChatGui>(Constants::MainQmlUri, 1, 0, "ChatGui");
qmlRegisterType<EventLogGui>(Constants::MainQmlUri, 1, 0, "EventLogGui"); qmlRegisterType<EventLogGui>(Constants::MainQmlUri, 1, 0, "EventLogGui");
@ -900,8 +1046,14 @@ void App::restart() {
CoreModel::getInstance()->getCore()->stop(); CoreModel::getInstance()->getCore()->stop();
mCoreModelConnection->invokeToCore([this]() { mCoreModelConnection->invokeToCore([this]() {
mIsRestarting = true; mIsRestarting = true;
if (mAccountList) mAccountList->resetData();
if (mCallList) mCallList->resetData();
if (mCallHistoryList) mCallHistoryList->resetData();
if (mChatList) mChatList->resetData();
if (mConferenceInfoList) mConferenceInfoList->resetData();
closeCallsWindow(); closeCallsWindow();
setMainWindow(nullptr); setMainWindow(nullptr);
setCoreStarted(false);
mEngine->clearComponentCache(); mEngine->clearComponentCache();
mEngine->clearSingletons(); mEngine->clearSingletons();
delete mEngine; delete mEngine;
@ -1014,7 +1166,7 @@ bool App::notify(QObject *receiver, QEvent *event) {
} }
if (event->type() == QEvent::MouseButtonPress) { if (event->type() == QEvent::MouseButtonPress) {
auto window = findParentWindow(receiver); auto window = findParentWindow(receiver);
if (getMainWindow() == window) { if (getMainWindow() == window && mAccountList) {
auto defaultAccountCore = mAccountList->getDefaultAccountCore(); auto defaultAccountCore = mAccountList->getDefaultAccountCore();
if (defaultAccountCore && defaultAccountCore->getUnreadCallNotifications() > 0) { if (defaultAccountCore && defaultAccountCore->getUnreadCallNotifications() > 0) {
emit defaultAccountCore->lResetMissedCalls(); emit defaultAccountCore->lResetMissedCalls();
@ -1024,7 +1176,41 @@ bool App::notify(QObject *receiver, QEvent *event) {
return done; return done;
} }
QQuickWindow *App::getCallsWindow(QVariant callGui) { void App::handleAccountActivity(QSharedPointer<AccountCore> accountCore) {
if (!accountCore) return;
auto accountPresence = accountCore->getPresence();
if ((mMainWindow && mMainWindow->isActive() || (mCallsWindow && mCallsWindow->isActive())) &&
accountPresence == LinphoneEnums::Presence::Away) {
accountCore->lSetPresence(LinphoneEnums::Presence::Online, false);
} else if (((!mMainWindow || !mMainWindow->isActive() || !mMainWindow->isVisible()) &&
(!mCallsWindow || !mCallsWindow->isActive() || !mCallsWindow->isVisible())) &&
accountPresence == LinphoneEnums::Presence::Online) {
accountCore->lSetPresence(LinphoneEnums::Presence::Away, false);
}
}
void App::handleAppActivity() {
if (mAccountList) {
for (auto &account : mAccountList->getSharedList<AccountCore>())
handleAccountActivity(account);
} else {
connect(
this, &App::accountsChanged, this,
[this] {
if (mAccountList) {
for (auto &account : mAccountList->getSharedList<AccountCore>())
handleAccountActivity(account);
}
},
Qt::SingleShotConnection);
}
}
QQuickWindow *App::getCallsWindow() {
return mCallsWindow;
}
QQuickWindow *App::getOrCreateCallsWindow(QVariant callGui) {
mustBeInMainThread(getClassName()); mustBeInMainThread(getClassName());
if (!mCallsWindow) { if (!mCallsWindow) {
const QUrl callUrl("qrc:/qt/qml/Linphone/view/Page/Window/Call/CallsWindow.qml"); const QUrl callUrl("qrc:/qt/qml/Linphone/view/Page/Window/Call/CallsWindow.qml");
@ -1059,6 +1245,7 @@ QQuickWindow *App::getCallsWindow(QVariant callGui) {
} }
// window->setParent(mMainWindow); // window->setParent(mMainWindow);
mCallsWindow = window; mCallsWindow = window;
connect(mCallsWindow, &QQuickWindow::activeChanged, this, &App::handleAppActivity);
} }
if (!callGui.isNull() && callGui.isValid()) mCallsWindow->setProperty("call", callGui); if (!callGui.isNull() && callGui.isValid()) mCallsWindow->setProperty("call", callGui);
return mCallsWindow; return mCallsWindow;
@ -1081,8 +1268,11 @@ QQuickWindow *App::getMainWindow() const {
} }
void App::setMainWindow(QQuickWindow *data) { void App::setMainWindow(QQuickWindow *data) {
if (mMainWindow) disconnect(mMainWindow, &QQuickWindow::activeChanged, this, nullptr);
if (mMainWindow != data) { if (mMainWindow != data) {
mMainWindow = data; mMainWindow = data;
connect(mMainWindow, &QQuickWindow::activeChanged, this, &App::handleAppActivity);
handleAppActivity();
emit mainWindowChanged(); emit mainWindowChanged();
} }
} }
@ -1111,6 +1301,41 @@ AccountList *App::getAccounts() const {
return mAccountList.get(); return mAccountList.get();
} }
QSharedPointer<ConferenceInfoList> App::getConferenceInfoList() const {
return mConferenceInfoList;
}
void App::setConferenceInfoList(QSharedPointer<ConferenceInfoList> data) {
if (mConferenceInfoList != data) {
mConferenceInfoList = data;
emit conferenceInfosChanged();
}
}
QSharedPointer<CallHistoryList> App::getCallHistoryList() const {
return mCallHistoryList;
}
void App::setCallHistoryList(QSharedPointer<CallHistoryList> data) {
if (mCallHistoryList != data) {
mCallHistoryList = data;
emit callHistoryChanged();
}
}
QSharedPointer<ChatList> App::getChatList() const {
return mChatList;
}
ChatList *App::getChats() const {
return mChatList.get();
}
void App::setChatList(QSharedPointer<ChatList> data) {
if (mChatList != data) {
mChatList = data;
emit chatsChanged();
}
}
QSharedPointer<CallList> App::getCallList() const { QSharedPointer<CallList> App::getCallList() const {
return mCallList; return mCallList;
} }
@ -1140,12 +1365,24 @@ void App::onAuthenticationRequested(const std::shared_ptr<linphone::Core> &core,
const std::shared_ptr<linphone::AuthInfo> &authInfo, const std::shared_ptr<linphone::AuthInfo> &authInfo,
linphone::AuthMethod method) { linphone::AuthMethod method) {
bool authInfoIsInAccounts = false; bool authInfoIsInAccounts = false;
for (auto &account : core->getAccountList()) { if (authInfo) {
auto accountAuthInfo = account->findAuthInfo(); for (auto &account : core->getAccountList()) {
if (authInfo && accountAuthInfo && authInfo->isEqualButAlgorithms(accountAuthInfo)) { if (!account) continue;
authInfoIsInAccounts = true; auto accountAuthInfo = account->findAuthInfo();
if (account->getState() == linphone::RegistrationState::Ok) return; if (accountAuthInfo) {
break; if (authInfo->isEqualButAlgorithms(accountAuthInfo)) {
authInfoIsInAccounts = true;
break;
}
} else {
auto identityAddress = account->getParams()->getIdentityAddress();
if (!identityAddress) continue;
if (authInfo->getUsername() == identityAddress->getUsername() &&
authInfo->getDomain() == identityAddress->getDomain()) {
authInfoIsInAccounts = true;
break;
}
}
} }
} }
if (!authInfoIsInAccounts) return; if (!authInfoIsInAccounts) return;
@ -1293,12 +1530,12 @@ bool App::event(QEvent *event) {
} else if (event->type() == QEvent::ApplicationActivate) { } else if (event->type() == QEvent::ApplicationActivate) {
for (int i = 0; i < getAccountList()->rowCount(); ++i) { for (int i = 0; i < getAccountList()->rowCount(); ++i) {
auto accountCore = getAccountList()->getAt<AccountCore>(i); auto accountCore = getAccountList()->getAt<AccountCore>(i);
emit accountCore->lSetPresence(LinphoneEnums::Presence::Online, false, false); handleAccountActivity(accountCore);
} }
} else if (event->type() == QEvent::ApplicationDeactivate) { } else if (event->type() == QEvent::ApplicationDeactivate) {
for (int i = 0; i < getAccountList()->rowCount(); ++i) { for (int i = 0; i < getAccountList()->rowCount(); ++i) {
auto accountCore = getAccountList()->getAt<AccountCore>(i); auto accountCore = getAccountList()->getAt<AccountCore>(i);
emit accountCore->lSetPresence(LinphoneEnums::Presence::Away, false, false); handleAccountActivity(accountCore);
} }
} }
@ -1357,6 +1594,12 @@ void App::setSysTrayIcon() {
menu->addSeparator(); menu->addSeparator();
} }
menu->addAction(markAllReadAction); menu->addAction(markAllReadAction);
//: Check for update
if (mSettings->isCheckForUpdateAvailable()) {
QAction *checkForUpdateAction = new QAction(tr("check_for_update"), root);
root->connect(checkForUpdateAction, &QAction::triggered, this, [this] { checkForUpdate(true); });
menu->addAction(checkForUpdateAction);
}
menu->addAction(quitAction); menu->addAction(quitAction);
if (!mSystemTrayIcon) { if (!mSystemTrayIcon) {
systemTrayIcon->setContextMenu(menu); // This is a Qt bug. We cannot call setContextMenu more than once. So systemTrayIcon->setContextMenu(menu); // This is a Qt bug. We cannot call setContextMenu more than once. So
@ -1401,12 +1644,17 @@ void App::setMacOSDockActions() {
void App::setLocale(QString configLocale) { void App::setLocale(QString configLocale) {
if (!configLocale.isEmpty()) mLocale = QLocale(configLocale); if (!configLocale.isEmpty()) mLocale = QLocale(configLocale);
else mLocale = QLocale(QLocale::system().name()); else mLocale = QLocale(QLocale::system().name());
emit localeChanged();
} }
QLocale App::getLocale() { QLocale App::getLocale() {
return mLocale; return mLocale;
} }
QString App::getLocaleAsString() {
return mLocale.name();
}
//----------------------------------------------------------- //-----------------------------------------------------------
// Version infos. // Version infos.
//----------------------------------------------------------- //-----------------------------------------------------------
@ -1436,6 +1684,21 @@ QString App::getSdkVersion() {
#endif #endif
} }
QString App::getQtVersion() const {
return qVersion();
}
void App::checkForUpdate(bool requestedByUser) {
mustBeInMainThread(log().arg(Q_FUNC_INFO));
if (CoreModel::getInstance() && mCoreModelConnection) {
mCoreModelConnection->invokeToModel([this, requestedByUser] {
mustBeInLinphoneThread(log().arg(Q_FUNC_INFO));
CoreModel::getInstance()->checkForUpdate(Utils::appStringToCoreString(applicationVersion()),
requestedByUser);
});
}
}
ChatGui *App::getCurrentChat() const { ChatGui *App::getCurrentChat() const {
return mCurrentChat; return mCurrentChat;
} }

View file

@ -24,8 +24,11 @@
#include <QSharedPointer> #include <QSharedPointer>
#include "core/account/AccountProxy.hpp" #include "core/account/AccountProxy.hpp"
#include "core/call-history/CallHistoryList.hpp"
#include "core/call/CallProxy.hpp" #include "core/call/CallProxy.hpp"
#include "core/chat/ChatGui.hpp" #include "core/chat/ChatGui.hpp"
#include "core/chat/ChatList.hpp"
#include "core/conference/ConferenceInfoList.hpp"
#include "core/setting/SettingsCore.hpp" #include "core/setting/SettingsCore.hpp"
#include "core/singleapplication/singleapplication.h" #include "core/singleapplication/singleapplication.h"
#include "model/cli/CliModel.hpp" #include "model/cli/CliModel.hpp"
@ -45,10 +48,15 @@ class App : public SingleApplication, public AbstractObject {
Q_PROPERTY(bool coreStarted READ getCoreStarted WRITE setCoreStarted NOTIFY coreStartedChanged) Q_PROPERTY(bool coreStarted READ getCoreStarted WRITE setCoreStarted NOTIFY coreStartedChanged)
Q_PROPERTY(AccountList *accounts READ getAccounts NOTIFY accountsChanged) Q_PROPERTY(AccountList *accounts READ getAccounts NOTIFY accountsChanged)
Q_PROPERTY(CallList *calls READ getCalls NOTIFY callsChanged) Q_PROPERTY(CallList *calls READ getCalls NOTIFY callsChanged)
Q_PROPERTY(ChatList *chats READ getChats NOTIFY chatsChanged)
Q_PROPERTY(QString shortApplicationVersion READ getShortApplicationVersion CONSTANT) Q_PROPERTY(QString shortApplicationVersion READ getShortApplicationVersion CONSTANT)
Q_PROPERTY(QString qtVersion READ getQtVersion CONSTANT)
Q_PROPERTY(QString gitBranchName READ getGitBranchName CONSTANT) Q_PROPERTY(QString gitBranchName READ getGitBranchName CONSTANT)
Q_PROPERTY(QString sdkVersion READ getSdkVersion CONSTANT) Q_PROPERTY(QString sdkVersion READ getSdkVersion CONSTANT)
Q_PROPERTY(ChatGui *currentChat READ getCurrentChat WRITE setCurrentChat NOTIFY currentChatChanged) Q_PROPERTY(ChatGui *currentChat READ getCurrentChat WRITE setCurrentChat NOTIFY currentChatChanged)
Q_PROPERTY(QString localeAsString READ getLocaleAsString CONSTANT)
Q_PROPERTY(int remainingTimeBeforeOidcTimeout MEMBER mRemainingTimeBeforeOidcTimeout NOTIFY
remainingTimeBeforeOidcTimeoutChanged)
public: public:
App(int &argc, char *argv[]); App(int &argc, char *argv[]);
@ -131,6 +139,7 @@ public:
} }
void updateSysTrayCount(int n); void updateSysTrayCount(int n);
QLocale getLocale(); QLocale getLocale();
QString getLocaleAsString();
void onLoggerInitialized(); void onLoggerInitialized();
void sendCommand(); void sendCommand();
@ -138,7 +147,10 @@ public:
bool getCoreStarted() const; bool getCoreStarted() const;
void setCoreStarted(bool started); void setCoreStarted(bool started);
QQuickWindow *getCallsWindow(QVariant callGui = QVariant()); QQuickWindow *getCallsWindow();
void handleAccountActivity(QSharedPointer<AccountCore> accountCore);
Q_INVOKABLE void handleAppActivity();
QQuickWindow *getOrCreateCallsWindow(QVariant callGui = QVariant());
void setCallsWindowProperty(const char *id, QVariant property); void setCallsWindowProperty(const char *id, QVariant property);
void closeCallsWindow(); void closeCallsWindow();
@ -151,6 +163,16 @@ public:
void setAccountList(QSharedPointer<AccountList> data); void setAccountList(QSharedPointer<AccountList> data);
Q_INVOKABLE AccountList *getAccounts() const; Q_INVOKABLE AccountList *getAccounts() const;
QSharedPointer<ConferenceInfoList> getConferenceInfoList() const;
void setConferenceInfoList(QSharedPointer<ConferenceInfoList> data);
QSharedPointer<CallHistoryList> getCallHistoryList() const;
void setCallHistoryList(QSharedPointer<CallHistoryList> data);
QSharedPointer<ChatList> getChatList() const;
ChatList *getChats() const;
void setChatList(QSharedPointer<ChatList> data);
QSharedPointer<CallList> getCallList() const; QSharedPointer<CallList> getCallList() const;
void setCallList(QSharedPointer<CallList> data); void setCallList(QSharedPointer<CallList> data);
Q_INVOKABLE CallList *getCalls() const; Q_INVOKABLE CallList *getCalls() const;
@ -164,7 +186,9 @@ public:
QString getShortApplicationVersion(); QString getShortApplicationVersion();
QString getGitBranchName(); QString getGitBranchName();
QString getSdkVersion(); QString getSdkVersion();
QString getQtVersion() const;
Q_INVOKABLE void checkForUpdate(bool requestedByUser = false);
ChatGui *getCurrentChat() const; ChatGui *getCurrentChat() const;
void setCurrentChat(ChatGui *chat); void setCurrentChat(ChatGui *chat);
@ -193,6 +217,12 @@ signals:
void callsChanged(); void callsChanged();
void currentDateChanged(); void currentDateChanged();
void currentChatChanged(); void currentChatChanged();
void conferenceInfosChanged();
void chatsChanged();
void callHistoryChanged();
void localeChanged();
void lForceOidcTimeout();
void remainingTimeBeforeOidcTimeoutChanged();
// void executeCommand(QString command); // void executeCommand(QString command);
private: private:
@ -215,18 +245,24 @@ private:
ChatGui *mCurrentChat = nullptr; ChatGui *mCurrentChat = nullptr;
QSharedPointer<SettingsCore> mSettings; QSharedPointer<SettingsCore> mSettings;
QSharedPointer<AccountList> mAccountList; QSharedPointer<AccountList> mAccountList;
QSharedPointer<ConferenceInfoList> mConferenceInfoList;
QSharedPointer<ChatList> mChatList;
QSharedPointer<CallList> mCallList; QSharedPointer<CallList> mCallList;
QSharedPointer<CallHistoryList> mCallHistoryList;
QSharedPointer<SafeConnection<App, CoreModel>> mCoreModelConnection; QSharedPointer<SafeConnection<App, CoreModel>> mCoreModelConnection;
QSharedPointer<SafeConnection<App, CliModel>> mCliModelConnection; QSharedPointer<SafeConnection<App, CliModel>> mCliModelConnection;
bool mAutoStart = false; bool mAutoStart = false;
bool mCoreStarted = false; bool mCoreStarted = false;
bool mIsRestarting = false; bool mIsRestarting = false;
bool mPossiblyLookForAddedAccount = false;
QLocale mLocale = QLocale::system(); QLocale mLocale = QLocale::system();
DefaultTranslatorCore *mTranslatorCore = nullptr; DefaultTranslatorCore *mTranslatorCore = nullptr;
DefaultTranslatorCore *mDefaultTranslatorCore = nullptr; DefaultTranslatorCore *mDefaultTranslatorCore = nullptr;
QTimer mDateUpdateTimer; QTimer mDateUpdateTimer;
QDate mCurrentDate; QDate mCurrentDate;
float mScreenRatio = 1; float mScreenRatio = 1;
QTimer mOIDCRefreshTimer;
int mRemainingTimeBeforeOidcTimeout = 0;
DECLARE_ABSTRACT_OBJECT DECLARE_ABSTRACT_OBJECT
}; };

View file

@ -18,7 +18,6 @@ list(APPEND _LINPHONEAPP_SOURCES
core/call-history/CallHistoryProxy.cpp core/call-history/CallHistoryProxy.cpp
core/camera/CameraGui.cpp core/camera/CameraGui.cpp
core/camera/CameraDummy.cpp core/camera/CameraDummy.cpp
core/camera/PreviewManager.cpp
core/chat/ChatCore.cpp core/chat/ChatCore.cpp
core/chat/ChatGui.cpp core/chat/ChatGui.cpp
core/chat/ChatList.cpp core/chat/ChatList.cpp

View file

@ -50,6 +50,7 @@ AccountCore::AccountCore(const std::shared_ptr<linphone::Account> &account) : QO
mIsDefaultAccount = CoreModel::getInstance()->getCore()->getDefaultAccount() == account; mIsDefaultAccount = CoreModel::getInstance()->getCore()->getDefaultAccount() == account;
mUnreadNotifications = account->getMissedCallsCount() + account->getUnreadChatMessageCount(); mUnreadNotifications = account->getMissedCallsCount() + account->getUnreadChatMessageCount();
mDisplayName = Utils::coreStringToAppString(identityAddress->getDisplayName()); mDisplayName = Utils::coreStringToAppString(identityAddress->getDisplayName());
mPublishEnabled = params->publishEnabled();
if (mDisplayName.isEmpty()) { if (mDisplayName.isEmpty()) {
mDisplayName = ToolModel::getDisplayName(identityAddress); mDisplayName = ToolModel::getDisplayName(identityAddress);
auto copyAddress = identityAddress->clone(); auto copyAddress = identityAddress->clone();
@ -84,6 +85,7 @@ AccountCore::AccountCore(const std::shared_ptr<linphone::Account> &account) : QO
? Utils::coreStringToAppString(params->getAudioVideoConferenceFactoryAddress()->asString()) ? Utils::coreStringToAppString(params->getAudioVideoConferenceFactoryAddress()->asString())
: ""; : "";
mLimeServerUrl = Utils::coreStringToAppString(params->getLimeServerUrl()); mLimeServerUrl = Utils::coreStringToAppString(params->getLimeServerUrl());
mCcmpServerUrl = Utils::coreStringToAppString(params->getCcmpServerUrl());
// Add listener // Add listener
mAccountModel = Utils::makeQObject_ptr<AccountModel>(account); // OK mAccountModel = Utils::makeQObject_ptr<AccountModel>(account); // OK
@ -148,6 +150,7 @@ AccountCore::AccountCore(const AccountCore &accountCore) {
mConferenceFactoryAddress = accountCore.mConferenceFactoryAddress; mConferenceFactoryAddress = accountCore.mConferenceFactoryAddress;
mAudioVideoConferenceFactoryAddress = accountCore.mAudioVideoConferenceFactoryAddress; mAudioVideoConferenceFactoryAddress = accountCore.mAudioVideoConferenceFactoryAddress;
mLimeServerUrl = accountCore.mLimeServerUrl; mLimeServerUrl = accountCore.mLimeServerUrl;
mCcmpServerUrl = accountCore.mCcmpServerUrl;
} }
void AccountCore::setSelf(QSharedPointer<AccountCore> me) { void AccountCore::setSelf(QSharedPointer<AccountCore> me) {
@ -236,6 +239,10 @@ void AccountCore::setSelf(QSharedPointer<AccountCore> me) {
mAccountModelConnection->makeConnectToModel(&AccountModel::limeServerUrlChanged, [this](QString value) { mAccountModelConnection->makeConnectToModel(&AccountModel::limeServerUrlChanged, [this](QString value) {
mAccountModelConnection->invokeToCore([this, value]() { onLimeServerUrlChanged(value); }); mAccountModelConnection->invokeToCore([this, value]() { onLimeServerUrlChanged(value); });
}); });
mAccountModelConnection->makeConnectToModel(&AccountModel::ccmpServerUrlChanged, [this](QString value) {
mAccountModelConnection->invokeToCore([this, value]() { onCcmpServerUrlChanged(value); });
});
mAccountModelConnection->makeConnectToModel( mAccountModelConnection->makeConnectToModel(
&AccountModel::removed, [this]() { mAccountModelConnection->invokeToCore([this]() { emit removed(); }); }); &AccountModel::removed, [this]() { mAccountModelConnection->invokeToCore([this]() { emit removed(); }); });
@ -314,19 +321,20 @@ void AccountCore::reset(const AccountCore &accountCore) {
setUnreadNotifications(accountCore.mUnreadNotifications); setUnreadNotifications(accountCore.mUnreadNotifications);
setUnreadCallNotifications(accountCore.mUnreadCallNotifications); setUnreadCallNotifications(accountCore.mUnreadCallNotifications);
setUnreadMessageNotifications(accountCore.mUnreadMessageNotifications); setUnreadMessageNotifications(accountCore.mUnreadMessageNotifications);
setMwiServerAddress(accountCore.mMwiServerAddress); onMwiServerAddressChanged(accountCore.mMwiServerAddress);
setVoicemailAddress(accountCore.mVoicemailAddress); onVoicemailAddressChanged(accountCore.mVoicemailAddress);
setTransport(accountCore.mTransport); onTransportChanged(accountCore.mTransport);
setRegistrarUri(accountCore.mRegistrarUri); onRegistrarUriChanged(accountCore.mRegistrarUri);
setOutboundProxyUri(accountCore.mOutboundProxyUri); onOutboundProxyUriChanged(accountCore.mOutboundProxyUri);
setStunServer(accountCore.mStunServer); setStunServer(accountCore.mStunServer);
setIceEnabled(accountCore.mIceEnabled); onIceEnabledChanged(accountCore.mIceEnabled);
setAvpfEnabled(accountCore.mAvpfEnabled); onAvpfEnabledChanged(accountCore.mAvpfEnabled);
setBundleModeEnabled(accountCore.mBundleModeEnabled); onBundleModeEnabledChanged(accountCore.mBundleModeEnabled);
setExpire(accountCore.mExpire); onExpireChanged(accountCore.mExpire);
setConferenceFactoryAddress(accountCore.mConferenceFactoryAddress); onConferenceFactoryAddressChanged(accountCore.mConferenceFactoryAddress);
setAudioVideoConferenceFactoryAddress(accountCore.mAudioVideoConferenceFactoryAddress); onAudioVideoConferenceFactoryAddressChanged(accountCore.mAudioVideoConferenceFactoryAddress);
setLimeServerUrl(accountCore.mLimeServerUrl); onLimeServerUrlChanged(accountCore.mLimeServerUrl);
onCcmpServerUrlChanged(accountCore.mCcmpServerUrl);
} }
const std::shared_ptr<AccountModel> &AccountCore::getModel() const { const std::shared_ptr<AccountModel> &AccountCore::getModel() const {
@ -574,6 +582,10 @@ QString AccountCore::getLimeServerUrl() {
return mLimeServerUrl; return mLimeServerUrl;
} }
QString AccountCore::getCcmpServerUrl() {
return mCcmpServerUrl;
}
void AccountCore::setMwiServerAddress(QString value) { void AccountCore::setMwiServerAddress(QString value) {
if (mMwiServerAddress != value) { if (mMwiServerAddress != value) {
mMwiServerAddress = value; mMwiServerAddress = value;
@ -678,6 +690,14 @@ void AccountCore::setLimeServerUrl(QString value) {
} }
} }
void AccountCore::setCcmpServerUrl(QString value) {
if (mCcmpServerUrl != value) {
mCcmpServerUrl = value;
emit ccmpServerUrlChanged();
setIsSaved(false);
}
}
bool AccountCore::isSaved() const { bool AccountCore::isSaved() const {
return mIsSaved; return mIsSaved;
} }
@ -790,6 +810,13 @@ void AccountCore::onLimeServerUrlChanged(QString value) {
} }
} }
void AccountCore::onCcmpServerUrlChanged(QString value) {
if (value != mCcmpServerUrl) {
mCcmpServerUrl = value;
emit ccmpServerUrlChanged();
}
}
void AccountCore::writeIntoModel(std::shared_ptr<AccountModel> model) const { void AccountCore::writeIntoModel(std::shared_ptr<AccountModel> model) const {
mustBeInLinphoneThread(getClassName() + Q_FUNC_INFO); mustBeInLinphoneThread(getClassName() + Q_FUNC_INFO);
model->setMwiServerAddress(mMwiServerAddress); model->setMwiServerAddress(mMwiServerAddress);
@ -806,6 +833,7 @@ void AccountCore::writeIntoModel(std::shared_ptr<AccountModel> model) const {
model->setConferenceFactoryAddress(mConferenceFactoryAddress); model->setConferenceFactoryAddress(mConferenceFactoryAddress);
model->setAudioVideoConferenceFactoryAddress(mAudioVideoConferenceFactoryAddress); model->setAudioVideoConferenceFactoryAddress(mAudioVideoConferenceFactoryAddress);
model->setLimeServerUrl(mLimeServerUrl); model->setLimeServerUrl(mLimeServerUrl);
model->setCcmpServerUrl(mCcmpServerUrl);
model->setVoicemailAddress(mVoicemailAddress); model->setVoicemailAddress(mVoicemailAddress);
} }
@ -825,6 +853,7 @@ void AccountCore::writeFromModel(const std::shared_ptr<AccountModel> &model) {
onConferenceFactoryAddressChanged(model->getConferenceFactoryAddress()); onConferenceFactoryAddressChanged(model->getConferenceFactoryAddress());
onAudioVideoConferenceFactoryAddressChanged(model->getAudioVideoConferenceFactoryAddress()); onAudioVideoConferenceFactoryAddressChanged(model->getAudioVideoConferenceFactoryAddress());
onLimeServerUrlChanged(model->getLimeServerUrl()); onLimeServerUrlChanged(model->getLimeServerUrl());
onCcmpServerUrlChanged(model->getCcmpServerUrl());
onVoicemailAddressChanged(model->getVoicemailAddress()); onVoicemailAddressChanged(model->getVoicemailAddress());
} }

View file

@ -84,6 +84,8 @@ public:
Q_PROPERTY(LinphoneEnums::Presence explicitPresence MEMBER mExplicitPresence NOTIFY presenceChanged) Q_PROPERTY(LinphoneEnums::Presence explicitPresence MEMBER mExplicitPresence NOTIFY presenceChanged)
Q_PROPERTY(QString presenceNote READ getPresenceNote WRITE setPresenceNote NOTIFY presenceChanged) Q_PROPERTY(QString presenceNote READ getPresenceNote WRITE setPresenceNote NOTIFY presenceChanged)
Q_PROPERTY(int maxPresenceNoteSize MEMBER mMaxPresenceNoteSize CONSTANT) Q_PROPERTY(int maxPresenceNoteSize MEMBER mMaxPresenceNoteSize CONSTANT)
Q_PROPERTY(bool publishEnabled MEMBER mPublishEnabled CONSTANT)
Q_PROPERTY(QString ccmpServerUrl READ getCcmpServerUrl WRITE setCcmpServerUrl NOTIFY ccmpServerUrlChanged)
DECLARE_CORE_GET(int, voicemailCount, VoicemailCount) DECLARE_CORE_GET(int, voicemailCount, VoicemailCount)
static QSharedPointer<AccountCore> create(const std::shared_ptr<linphone::Account> &account); static QSharedPointer<AccountCore> create(const std::shared_ptr<linphone::Account> &account);
@ -143,6 +145,7 @@ public:
QString getAudioVideoConferenceFactoryAddress(); QString getAudioVideoConferenceFactoryAddress();
QString getLimeServerUrl(); QString getLimeServerUrl();
QString getVoicemailAddress(); QString getVoicemailAddress();
QString getCcmpServerUrl();
void setMwiServerAddress(QString value); void setMwiServerAddress(QString value);
void setTransport(QString value); void setTransport(QString value);
@ -157,6 +160,7 @@ public:
void setAudioVideoConferenceFactoryAddress(QString value); void setAudioVideoConferenceFactoryAddress(QString value);
void setLimeServerUrl(QString value); void setLimeServerUrl(QString value);
void setVoicemailAddress(QString value); void setVoicemailAddress(QString value);
void setCcmpServerUrl(QString value);
bool isSaved() const; bool isSaved() const;
void setIsSaved(bool saved); void setIsSaved(bool saved);
@ -175,6 +179,7 @@ public:
void onConferenceFactoryAddressChanged(QString value); void onConferenceFactoryAddressChanged(QString value);
void onAudioVideoConferenceFactoryAddressChanged(QString value); void onAudioVideoConferenceFactoryAddressChanged(QString value);
void onLimeServerUrlChanged(QString value); void onLimeServerUrlChanged(QString value);
void onCcmpServerUrlChanged(QString value);
DECLARE_CORE_GET(bool, showMwi, ShowMwi) DECLARE_CORE_GET(bool, showMwi, ShowMwi)
@ -220,6 +225,7 @@ signals:
void isSavedChanged(); void isSavedChanged();
void voicemailAddressChanged(); void voicemailAddressChanged();
void presenceChanged(); void presenceChanged();
void ccmpServerUrlChanged();
void setValueFailed(const QString &error); void setValueFailed(const QString &error);
@ -268,10 +274,12 @@ private:
QString mAudioVideoConferenceFactoryAddress; QString mAudioVideoConferenceFactoryAddress;
QString mLimeServerUrl; QString mLimeServerUrl;
QString mVoicemailAddress; QString mVoicemailAddress;
QString mCcmpServerUrl;
LinphoneEnums::Presence mPresence = LinphoneEnums::Presence::Undefined; LinphoneEnums::Presence mPresence = LinphoneEnums::Presence::Undefined;
LinphoneEnums::Presence mExplicitPresence; LinphoneEnums::Presence mExplicitPresence;
QString mPresenceNote; QString mPresenceNote;
int mMaxPresenceNoteSize; int mMaxPresenceNoteSize;
bool mPublishEnabled = false;
bool mIsSaved = true; bool mIsSaved = true;

View file

@ -24,6 +24,12 @@
#include "core/App.hpp" #include "core/App.hpp"
AccountProxy::AccountProxy(QObject *parent) : LimitProxy(parent) { AccountProxy::AccountProxy(QObject *parent) : LimitProxy(parent) {
if (!App::getInstance()->getAccountList()) {
mList = AccountList::create();
App::getInstance()->setAccountList(mList);
}
mList = App::getInstance()->getAccountList();
setSourceModel(mList.get());
connect(this, &AccountProxy::initializedChanged, this, &AccountProxy::resetDefaultAccount); connect(this, &AccountProxy::initializedChanged, this, &AccountProxy::resetDefaultAccount);
connect(this, &AccountProxy::initializedChanged, this, &AccountProxy::haveAccountChanged); connect(this, &AccountProxy::initializedChanged, this, &AccountProxy::haveAccountChanged);
} }

View file

@ -59,6 +59,7 @@ signals:
protected: protected:
bool mInitialized = false; bool mInitialized = false;
QSharedPointer<AccountList> mList;
QSharedPointer<AccountCore> mDefaultAccount; // When null, a new UI object is build from List QSharedPointer<AccountCore> mDefaultAccount; // When null, a new UI object is build from List
}; };

View file

@ -73,10 +73,10 @@ void CarddavCore::remove() {
void CarddavCore::setSelf(QSharedPointer<CarddavCore> me) { void CarddavCore::setSelf(QSharedPointer<CarddavCore> me) {
mCarddavModelConnection = SafeConnection<CarddavCore, CarddavModel>::create(me, mCarddavModel); mCarddavModelConnection = SafeConnection<CarddavCore, CarddavModel>::create(me, mCarddavModel);
mCarddavModelConnection->makeConnectToModel(&CarddavModel::saved, [this](bool success) { mCarddavModelConnection->makeConnectToModel(&CarddavModel::saved, [this](bool success, QString message) {
mCarddavModelConnection->invokeToCore([this, success]() { mCarddavModelConnection->invokeToCore([this, success, message]() {
if (success) emit App::getInstance()->getSettings()->cardDAVAddressBookSynchronized(); if (success) emit App::getInstance()->getSettings()->cardDAVAddressBookSynchronized();
emit saved(success); emit saved(success, message);
}); });
}); });
} }

View file

@ -50,7 +50,7 @@ public:
DECLARE_CORE_MEMBER(bool, storeNewFriendsInIt, StoreNewFriendsInIt) DECLARE_CORE_MEMBER(bool, storeNewFriendsInIt, StoreNewFriendsInIt)
signals: signals:
void saved(bool success); void saved(bool success, QString message);
private: private:
std::shared_ptr<CarddavModel> mCarddavModel; std::shared_ptr<CarddavModel> mCarddavModel;

View file

@ -70,7 +70,7 @@ void CallHistoryList::setSelf(QSharedPointer<CallHistoryList> me) {
for (auto it : linphoneCallLogs) { for (auto it : linphoneCallLogs) {
auto model = createCallHistoryCore(it); auto model = createCallHistoryCore(it);
toConnect(model.get()); toConnect(model.get());
callLogs->push_back(model); callLogs->push_front(model);
} }
mModelConnection->invokeToCore([this, callLogs]() { mModelConnection->invokeToCore([this, callLogs]() {
mustBeInMainThread(getClassName()); mustBeInMainThread(getClassName());

View file

@ -27,6 +27,11 @@ DEFINE_ABSTRACT_OBJECT(CallHistoryProxy)
CallHistoryProxy::CallHistoryProxy(QObject *parent) : LimitProxy(parent) { CallHistoryProxy::CallHistoryProxy(QObject *parent) : LimitProxy(parent) {
mHistoryList = CallHistoryList::create(); mHistoryList = CallHistoryList::create();
if (!App::getInstance()->getCallHistoryList()) {
mHistoryList = CallHistoryList::create();
App::getInstance()->setCallHistoryList(mHistoryList);
}
mHistoryList = App::getInstance()->getCallHistoryList();
connect(mHistoryList.get(), &CallHistoryList::listAboutToBeReset, this, &CallHistoryProxy::listAboutToBeReset); connect(mHistoryList.get(), &CallHistoryList::listAboutToBeReset, this, &CallHistoryProxy::listAboutToBeReset);
setSourceModels(new SortFilterList(mHistoryList.get(), Qt::DescendingOrder)); setSourceModels(new SortFilterList(mHistoryList.get(), Qt::DescendingOrder));
connect(App::getInstance(), &App::currentDateChanged, this, [this] { emit mHistoryList->lUpdate(); }); connect(App::getInstance(), &App::currentDateChanged, this, [this] { emit mHistoryList->lUpdate(); });
@ -63,8 +68,5 @@ bool CallHistoryProxy::SortFilterList::filterAcceptsRow(int sourceRow, const QMo
} }
bool CallHistoryProxy::SortFilterList::lessThan(const QModelIndex &sourceLeft, const QModelIndex &sourceRight) const { bool CallHistoryProxy::SortFilterList::lessThan(const QModelIndex &sourceLeft, const QModelIndex &sourceRight) const {
auto l = getItemAtSource<CallHistoryList, CallHistoryCore>(sourceLeft.row()); return true;
auto r = getItemAtSource<CallHistoryList, CallHistoryCore>(sourceRight.row());
return l->mDate < r->mDate;
} }

View file

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2010-2024 Belledonne Communications SARL. * Copyright (c) 2010-2026 Belledonne Communications SARL.
* *
* This file is part of linphone-desktop * This file is part of linphone-desktop
* (see https://www.linphone.org). * (see https://www.linphone.org).
@ -28,6 +28,8 @@
#include "tool/Utils.hpp" #include "tool/Utils.hpp"
#include "tool/thread/SafeConnection.hpp" #include "tool/thread/SafeConnection.hpp"
#include <QQuickWindow>
DEFINE_ABSTRACT_OBJECT(CallCore) DEFINE_ABSTRACT_OBJECT(CallCore)
/***********************************************************************/ /***********************************************************************/
@ -107,9 +109,12 @@ CallCore::CallCore(const std::shared_ptr<linphone::Call> &call) : QObject(nullpt
mCallModel->setSelf(mCallModel); mCallModel->setSelf(mCallModel);
mDuration = call->getDuration(); mDuration = call->getDuration();
mIsStarted = mDuration > 0; mIsStarted = mDuration > 0;
auto videoDirection = call->getParams()->getVideoDirection(); auto callParams = call->getParams();
auto videoDirection = callParams->getVideoDirection();
mLocalVideoEnabled = mLocalVideoEnabled =
videoDirection == linphone::MediaDirection::SendOnly || videoDirection == linphone::MediaDirection::SendRecv; videoDirection == linphone::MediaDirection::SendOnly || videoDirection == linphone::MediaDirection::SendRecv;
mCameraEnabled = mLocalVideoEnabled && callParams->cameraEnabled();
qDebug() << "create call with camera enabled" << mLocalVideoEnabled << callParams->cameraEnabled();
auto remoteParams = call->getRemoteParams(); auto remoteParams = call->getRemoteParams();
videoDirection = remoteParams ? remoteParams->getVideoDirection() : linphone::MediaDirection::Inactive; videoDirection = remoteParams ? remoteParams->getVideoDirection() : linphone::MediaDirection::Inactive;
mRemoteVideoEnabled = mRemoteVideoEnabled =
@ -133,7 +138,7 @@ CallCore::CallCore(const std::shared_ptr<linphone::Call> &call) : QObject(nullpt
mTransferState = LinphoneEnums::fromLinphone(call->getTransferState()); mTransferState = LinphoneEnums::fromLinphone(call->getTransferState());
mLocalToken = Utils::coreStringToAppString(mCallModel->getLocalAtuhenticationToken()); mLocalToken = Utils::coreStringToAppString(mCallModel->getLocalAtuhenticationToken());
mRemoteTokens = mCallModel->getRemoteAtuhenticationTokens(); mRemoteTokens = mCallModel->getRemoteAtuhenticationTokens();
mEncryption = LinphoneEnums::fromLinphone(call->getParams()->getMediaEncryption()); mEncryption = LinphoneEnums::fromLinphone(callParams->getMediaEncryption());
auto tokenVerified = call->getAuthenticationTokenVerified(); auto tokenVerified = call->getAuthenticationTokenVerified();
mIsMismatch = call->getZrtpCacheMismatchFlag(); mIsMismatch = call->getZrtpCacheMismatchFlag();
mIsSecured = (mEncryption == LinphoneEnums::MediaEncryption::Zrtp && tokenVerified) || mIsSecured = (mEncryption == LinphoneEnums::MediaEncryption::Zrtp && tokenVerified) ||
@ -160,7 +165,7 @@ CallCore::CallCore(const std::shared_ptr<linphone::Call> &call) : QObject(nullpt
mPaused = mState == LinphoneEnums::CallState::Pausing || mState == LinphoneEnums::CallState::Paused || mPaused = mState == LinphoneEnums::CallState::Pausing || mState == LinphoneEnums::CallState::Paused ||
mState == LinphoneEnums::CallState::PausedByRemote; mState == LinphoneEnums::CallState::PausedByRemote;
mRecording = call->getParams() && call->getParams()->isRecording(); mRecording = callParams && callParams->isRecording();
mRemoteRecording = call->getRemoteParams() && call->getRemoteParams()->isRecording(); mRemoteRecording = call->getRemoteParams() && call->getRemoteParams()->isRecording();
auto settingsModel = SettingsModel::getInstance(); auto settingsModel = SettingsModel::getInstance();
mMicrophoneVolume = call->getRecordVolume(); mMicrophoneVolume = call->getRecordVolume();
@ -193,8 +198,8 @@ void CallCore::setSelf(QSharedPointer<CallCore> me) {
mCallModelConnection->makeConnectToModel(&CallModel::speakerMutedChanged, [this](bool isMuted) { mCallModelConnection->makeConnectToModel(&CallModel::speakerMutedChanged, [this](bool isMuted) {
mCallModelConnection->invokeToCore([this, isMuted]() { setSpeakerMuted(isMuted); }); mCallModelConnection->invokeToCore([this, isMuted]() { setSpeakerMuted(isMuted); });
}); });
mCallModelConnection->makeConnectToCore(&CallCore::lSetLocalVideoEnabled, [this](bool enabled) { mCallModelConnection->makeConnectToCore(&CallCore::lSetCameraEnabled, [this](bool enabled) {
mCallModelConnection->invokeToModel([this, enabled]() { mCallModel->setLocalVideoEnabled(enabled); }); mCallModelConnection->invokeToModel([this, enabled]() { mCallModel->setCameraEnabled(enabled); });
}); });
mCallModelConnection->makeConnectToCore(&CallCore::lStartRecording, [this]() { mCallModelConnection->makeConnectToCore(&CallCore::lStartRecording, [this]() {
mCallModelConnection->invokeToModel([this]() { mCallModel->startRecording(); }); mCallModelConnection->invokeToModel([this]() { mCallModel->startRecording(); });
@ -204,15 +209,15 @@ void CallCore::setSelf(QSharedPointer<CallCore> me) {
}); });
mCallModelConnection->makeConnectToModel( mCallModelConnection->makeConnectToModel(
&CallModel::recordingChanged, [this](const std::shared_ptr<linphone::Call> &call, bool recording) { &CallModel::recordingChanged, [this](const std::shared_ptr<linphone::Call> &call, bool recording) {
mCallModelConnection->invokeToCore([this, recording]() { auto recordFile = QString::fromStdString(mCallModel->getRecordFile());
mCallModelConnection->invokeToCore([this, recording, recordFile]() {
setRecording(recording); setRecording(recording);
if (recording == false) { if (recording == false) {
//: "Enregistrement terminé" //: "Enregistrement terminé"
Utils::showInformationPopup(tr("call_record_end_message"), Utils::showInformationPopup(tr("call_record_end_message"),
//: "L'appel a été enregistré dans le fichier : %1" //: "L'appel a été enregistré dans le fichier : %1"
tr("call_record_saved_in_file_message") tr("call_record_saved_in_file_message").arg(recordFile), true,
.arg(QString::fromStdString(mCallModel->getRecordFile())), App::getInstance()->getOrCreateCallsWindow());
true, App::getInstance()->getCallsWindow());
} }
}); });
}); });
@ -244,6 +249,9 @@ void CallCore::setSelf(QSharedPointer<CallCore> me) {
mCallModelConnection->makeConnectToModel(&CallModel::localVideoEnabledChanged, [this](bool enabled) { mCallModelConnection->makeConnectToModel(&CallModel::localVideoEnabledChanged, [this](bool enabled) {
mCallModelConnection->invokeToCore([this, enabled]() { setLocalVideoEnabled(enabled); }); mCallModelConnection->invokeToCore([this, enabled]() { setLocalVideoEnabled(enabled); });
}); });
mCallModelConnection->makeConnectToModel(&CallModel::cameraEnabledChanged, [this](bool enabled) {
mCallModelConnection->invokeToCore([this, enabled]() { setCameraEnabled(enabled); });
});
mCallModelConnection->makeConnectToModel(&CallModel::durationChanged, [this](int duration) { mCallModelConnection->makeConnectToModel(&CallModel::durationChanged, [this](int duration) {
mCallModelConnection->invokeToCore([this, duration]() { setDuration(duration); }); mCallModelConnection->invokeToCore([this, duration]() { setDuration(duration); });
}); });
@ -344,6 +352,8 @@ void CallCore::setSelf(QSharedPointer<CallCore> me) {
}); });
mCallModelConnection->makeConnectToModel(&CallModel::conferenceChanged, [this]() { mCallModelConnection->makeConnectToModel(&CallModel::conferenceChanged, [this]() {
auto conference = mCallModel->getMonitor()->getConference(); auto conference = mCallModel->getMonitor()->getConference();
// Force enable video if in conference to handle screen sharing
if (conference && !mCallModel->videoEnabled()) mCallModel->enableVideo(true);
QSharedPointer<ConferenceCore> core = conference ? ConferenceCore::create(conference) : nullptr; QSharedPointer<ConferenceCore> core = conference ? ConferenceCore::create(conference) : nullptr;
mCallModelConnection->invokeToCore([this, core]() { setConference(core); }); mCallModelConnection->invokeToCore([this, core]() { setConference(core); });
}); });
@ -442,6 +452,44 @@ void CallCore::setSelf(QSharedPointer<CallCore> me) {
setVideoStats(videoStats); setVideoStats(videoStats);
} }
}); });
mCallModelConnection->makeConnectToModel(&CallModel::headsetAnswerCallRequested, [this]() {
mCallModelConnection->invokeToCore([this]() {
const auto callList = App::getInstance()->getCallList();
const auto currentPendingCall = callList->getFirstIncommingPendingCall();
if (!currentPendingCall.isNull()) {
const auto gui = new CallGui(currentPendingCall);
Utils::openCallsWindow(gui);
currentPendingCall->lAccept(false);
}
});
});
mCallModelConnection->makeConnectToModel(&CallModel::headsetEndCallRequested, [this]() {
mCallModelConnection->invokeToCore([this]() {
const auto window = Utils::getOrCreateCallsWindow();
window->setProperty("callTerminatedByUser", true);
lTerminate();
});
});
mCallModelConnection->makeConnectToModel(&CallModel::headsetHoldCallRequested, [this]() {
mCallModelConnection->invokeToCore([this]() { lSetPaused(true); });
});
mCallModelConnection->makeConnectToModel(&CallModel::headsetMicrophoneMuteToggled, [this](bool mute) {
mCallModelConnection->invokeToCore([this, mute]() { lSetMicrophoneMuted(mute); });
});
mCallModelConnection->makeConnectToModel(&CallModel::headsetRejectCallRequested, [this]() {
mCallModelConnection->invokeToCore([this]() {
const auto callList = App::getInstance()->getCallList();
const auto currentPendingCall = callList->getFirstIncommingPendingCall();
if (!currentPendingCall.isNull()) {
currentPendingCall->lDecline();
}
});
});
mCallModelConnection->makeConnectToModel(&CallModel::headsetResumeCallRequested, [this]() {
mCallModelConnection->invokeToCore([this]() { lSetPaused(false); });
});
if (mShouldFindRemoteFriend) findRemoteFriend(me); if (mShouldFindRemoteFriend) findRemoteFriend(me);
} }
@ -558,6 +606,18 @@ void CallCore::setLocalVideoEnabled(bool enabled) {
} }
} }
bool CallCore::getCameraEnabled() const {
return mCameraEnabled;
}
void CallCore::setCameraEnabled(bool enabled) {
if (mCameraEnabled != enabled) {
mCameraEnabled = enabled;
lDebug() << "CameraEnabled: " << mCameraEnabled;
emit cameraEnabledChanged();
}
}
bool CallCore::getPaused() const { bool CallCore::getPaused() const {
return mPaused; return mPaused;
} }

View file

@ -117,8 +117,8 @@ public:
Q_PROPERTY(QStringList remoteTokens WRITE setRemoteTokens MEMBER mRemoteTokens NOTIFY remoteTokensChanged) Q_PROPERTY(QStringList remoteTokens WRITE setRemoteTokens MEMBER mRemoteTokens NOTIFY remoteTokensChanged)
Q_PROPERTY( Q_PROPERTY(
bool remoteVideoEnabled READ getRemoteVideoEnabled WRITE setRemoteVideoEnabled NOTIFY remoteVideoEnabledChanged) bool remoteVideoEnabled READ getRemoteVideoEnabled WRITE setRemoteVideoEnabled NOTIFY remoteVideoEnabledChanged)
Q_PROPERTY( Q_PROPERTY(bool localVideoEnabled READ getLocalVideoEnabled NOTIFY localVideoEnabledChanged)
bool localVideoEnabled READ getLocalVideoEnabled WRITE lSetLocalVideoEnabled NOTIFY localVideoEnabledChanged) Q_PROPERTY(bool cameraEnabled READ getCameraEnabled WRITE lSetCameraEnabled NOTIFY cameraEnabledChanged)
Q_PROPERTY(bool recording READ getRecording WRITE setRecording NOTIFY recordingChanged) Q_PROPERTY(bool recording READ getRecording WRITE setRecording NOTIFY recordingChanged)
Q_PROPERTY(bool remoteRecording READ getRemoteRecording WRITE setRemoteRecording NOTIFY remoteRecordingChanged) Q_PROPERTY(bool remoteRecording READ getRemoteRecording WRITE setRemoteRecording NOTIFY remoteRecordingChanged)
Q_PROPERTY(bool recordable READ getRecordable WRITE setRecordable NOTIFY recordableChanged) Q_PROPERTY(bool recordable READ getRecordable WRITE setRecordable NOTIFY recordableChanged)
@ -201,6 +201,9 @@ public:
bool getLocalVideoEnabled() const; bool getLocalVideoEnabled() const;
void setLocalVideoEnabled(bool enabled); void setLocalVideoEnabled(bool enabled);
bool getCameraEnabled() const;
void setCameraEnabled(bool enabled);
bool getRecording() const; bool getRecording() const;
void setRecording(bool recording); void setRecording(bool recording);
@ -255,6 +258,7 @@ signals:
void remoteTokensChanged(); void remoteTokensChanged();
void remoteVideoEnabledChanged(bool remoteVideoEnabled); void remoteVideoEnabledChanged(bool remoteVideoEnabled);
void localVideoEnabledChanged(); void localVideoEnabledChanged();
void cameraEnabledChanged();
void recordingChanged(); void recordingChanged();
void remoteRecordingChanged(); void remoteRecordingChanged();
void recordableChanged(); void recordableChanged();
@ -275,7 +279,7 @@ signals:
void lTerminateAllCalls(); // Hangup all calls void lTerminateAllCalls(); // Hangup all calls
void lSetSpeakerMuted(bool muted); void lSetSpeakerMuted(bool muted);
void lSetMicrophoneMuted(bool isMuted); void lSetMicrophoneMuted(bool isMuted);
void lSetLocalVideoEnabled(bool enabled); void lSetCameraEnabled(bool enabled);
void lSetVideoEnabled(bool enabled); void lSetVideoEnabled(bool enabled);
void lSetPaused(bool paused); void lSetPaused(bool paused);
void lTransferCall(QString address); void lTransferCall(QString address);
@ -331,6 +335,7 @@ private:
bool mSpeakerMuted = false; bool mSpeakerMuted = false;
bool mMicrophoneMuted = false; bool mMicrophoneMuted = false;
bool mLocalVideoEnabled = false; bool mLocalVideoEnabled = false;
bool mCameraEnabled = false;
bool mVideoEnabled = false; bool mVideoEnabled = false;
bool mPaused = false; bool mPaused = false;
bool mRemoteVideoEnabled = false; bool mRemoteVideoEnabled = false;

View file

@ -33,7 +33,7 @@ class CoreModel;
class CallList : public ListProxy, public AbstractObject { class CallList : public ListProxy, public AbstractObject {
Q_OBJECT Q_OBJECT
Q_PROPERTY(CallGui* currentCall READ getCurrentCall WRITE setCurrentCall NOTIFY currentCallChanged) Q_PROPERTY(CallGui *currentCall READ getCurrentCall WRITE setCurrentCall NOTIFY currentCallChanged)
public: public:
static QSharedPointer<CallList> create(); static QSharedPointer<CallList> create();
// Create a CallCore and make connections to List. // Create a CallCore and make connections to List.

View file

@ -25,20 +25,32 @@
DEFINE_ABSTRACT_OBJECT(CallProxy) DEFINE_ABSTRACT_OBJECT(CallProxy)
CallProxy::CallProxy(QObject *parent) : LimitProxy(parent) { CallProxy::CallProxy() : SortFilterProxy() {
mShowCurrentCall = true;
} }
CallProxy::~CallProxy() { CallProxy::~CallProxy() {
} }
CallGui *CallProxy::getCurrentCall() { CallGui *CallProxy::getCurrentCall() {
auto model = getListModel<CallList>(); auto model = qobject_cast<CallList *>(sourceModel());
if (!mCurrentCall && model) mCurrentCall = model->getCurrentCall(); if (!mCurrentCall && model) mCurrentCall = model->getCurrentCall();
return mCurrentCall; return mCurrentCall;
} }
void CallProxy::setShowCurrentCall(bool show) {
if (mShowCurrentCall != show) {
mShowCurrentCall = show;
emit showCurrentCallChanged();
}
}
bool CallProxy::showCurrentCall() const {
return mShowCurrentCall;
}
void CallProxy::setCurrentCall(CallGui *call) { void CallProxy::setCurrentCall(CallGui *call) {
getListModel<CallList>()->setCurrentCall(call); qobject_cast<CallList *>(sourceModel())->setCurrentCall(call);
} }
// Reset the default account to let UI build its new object if needed. // Reset the default account to let UI build its new object if needed.
@ -48,12 +60,12 @@ void CallProxy::resetCurrentCall() {
} }
bool CallProxy::getHaveCall() const { bool CallProxy::getHaveCall() const {
auto model = getListModel<CallList>(); auto model = qobject_cast<CallList *>(sourceModel());
return model ? model->getHaveCall() : false; return model ? model->getHaveCall() : false;
} }
void CallProxy::setSourceModel(QAbstractItemModel *model) { void CallProxy::setSourceModel(QAbstractItemModel *model) {
auto oldCallList = getListModel<CallList>(); auto oldCallList = qobject_cast<CallList *>(sourceModel());
if (oldCallList) { if (oldCallList) {
disconnect(oldCallList); disconnect(oldCallList);
} }
@ -63,24 +75,24 @@ void CallProxy::setSourceModel(QAbstractItemModel *model) {
connect(newCallList, &CallList::haveCallChanged, this, &CallProxy::haveCallChanged, Qt::QueuedConnection); connect(newCallList, &CallList::haveCallChanged, this, &CallProxy::haveCallChanged, Qt::QueuedConnection);
connect(this, &CallProxy::lMergeAll, newCallList, &CallList::lMergeAll); connect(this, &CallProxy::lMergeAll, newCallList, &CallList::lMergeAll);
} }
setSourceModels(new SortFilterList(model)); QSortFilterProxyModel::setSourceModel(model);
} }
bool CallProxy::SortFilterList::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const { bool CallProxy::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const {
bool show = (mFilterText.isEmpty() || mFilterText == "*"); bool show = (mFilterText.isEmpty() || mFilterText == "*");
auto callList = qobject_cast<CallList *>(sourceModel());
auto call = callList->getAt<CallCore>(sourceRow);
if (!mShowCurrentCall && call == callList->getCurrentCallCore()) return false;
if (!show) { if (!show) {
QRegularExpression search(QRegularExpression::escape(mFilterText), QRegularExpression search(QRegularExpression::escape(mFilterText),
QRegularExpression::CaseInsensitiveOption | QRegularExpression::CaseInsensitiveOption |
QRegularExpression::UseUnicodePropertiesOption); QRegularExpression::UseUnicodePropertiesOption);
auto call = qobject_cast<CallList *>(sourceModel())->getAt<CallCore>(sourceRow);
show = call->getRemoteAddress().contains(search); show = call->getRemoteAddress().contains(search);
} }
return show; return show;
} }
bool CallProxy::SortFilterList::lessThan(const QModelIndex &sourceLeft, const QModelIndex &sourceRight) const { bool CallProxy::lessThan(const QModelIndex &sourceLeft, const QModelIndex &sourceRight) const {
auto l = getItemAtSource<CallList, CallCore>(sourceLeft.row()); auto l = getItemAtSource<CallList, CallCore>(sourceLeft.row());
auto r = getItemAtSource<CallList, CallCore>(sourceRight.row()); auto r = getItemAtSource<CallList, CallCore>(sourceRight.row());

View file

@ -28,15 +28,14 @@
// ============================================================================= // =============================================================================
class CallProxy : public LimitProxy, public AbstractObject { class CallProxy : public SortFilterProxy, public AbstractObject {
Q_OBJECT Q_OBJECT
Q_PROPERTY(CallGui *currentCall READ getCurrentCall WRITE setCurrentCall NOTIFY currentCallChanged) Q_PROPERTY(CallGui *currentCall READ getCurrentCall WRITE setCurrentCall NOTIFY currentCallChanged)
Q_PROPERTY(bool haveCall READ getHaveCall NOTIFY haveCallChanged) Q_PROPERTY(bool haveCall READ getHaveCall NOTIFY haveCallChanged)
Q_PROPERTY(bool showCurrentCall READ showCurrentCall WRITE setShowCurrentCall NOTIFY showCurrentCallChanged)
public: public:
DECLARE_SORTFILTER_CLASS() CallProxy();
CallProxy(QObject *parent = Q_NULLPTR);
~CallProxy(); ~CallProxy();
// Get a new object from List or give the stored one. // Get a new object from List or give the stored one.
@ -48,15 +47,23 @@ public:
bool getHaveCall() const; bool getHaveCall() const;
void setShowCurrentCall(bool show);
bool showCurrentCall() const;
void setSourceModel(QAbstractItemModel *sourceModel) override; void setSourceModel(QAbstractItemModel *sourceModel) override;
virtual bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override;
virtual bool lessThan(const QModelIndex &sourceLeft, const QModelIndex &sourceRight) const override;
signals: signals:
void lMergeAll(); void lMergeAll();
void currentCallChanged(); void currentCallChanged();
void haveCallChanged(); void haveCallChanged();
void showCurrentCallChanged();
protected: protected:
CallGui *mCurrentCall = nullptr; // When null, a new UI object is build from List CallGui *mCurrentCall = nullptr; // When null, a new UI object is build from List
bool mShowCurrentCall = false;
DECLARE_ABSTRACT_OBJECT DECLARE_ABSTRACT_OBJECT
}; };

View file

@ -25,7 +25,6 @@
#include "CameraDummy.hpp" #include "CameraDummy.hpp"
#include "CameraGui.hpp" #include "CameraGui.hpp"
#include "PreviewManager.hpp"
#include "core/App.hpp" #include "core/App.hpp"
#include "core/call/CallCore.hpp" #include "core/call/CallCore.hpp"
#include "core/call/CallGui.hpp" #include "core/call/CallGui.hpp"
@ -51,6 +50,10 @@ CameraGui::CameraGui(QQuickItem *parent) : QQuickFramebufferObject(parent) {
CameraGui::~CameraGui() { CameraGui::~CameraGui() {
mustBeInMainThread("~" + getClassName()); mustBeInMainThread("~" + getClassName());
mRefreshTimer.stop(); mRefreshTimer.stop();
if (mIsPreview) {
lDebug() << "[CameraGui] Deactivation";
CoreModel::getInstance()->getCore()->enableVideoPreview(false);
}
setWindowIdLocation(None); setWindowIdLocation(None);
} }
@ -84,9 +87,12 @@ QQuickFramebufferObject::Renderer *CameraGui::createRenderer() const {
// A renderer is mandatory, we cannot wait async. // A renderer is mandatory, we cannot wait async.
switch (getSourceLocation()) { switch (getSourceLocation()) {
case CorePreview: { case CorePreview: {
// if (resetWindowId) PreviewManager::getInstance()->unsubscribe(this); App::postModelBlock([qmlName = mQmlName, callGui = mCallGui, &renderer]() {
renderer = PreviewManager::getInstance()->subscribe(this); lInfo() << "[Camera] (" << qmlName << ") Camera create from Preview";
//(QQuickFramebufferObject::Renderer *)CoreModel::getInstance()->getCore()->createNativePreviewWindowId(); renderer = (QQuickFramebufferObject::Renderer *)CoreModel::getInstance()
->getCore()
->createNativePreviewWindowId(nullptr);
});
} break; } break;
case Call: { case Call: {
@ -135,7 +141,10 @@ void CameraGui::updateSDKRenderer(QQuickFramebufferObject::Renderer *renderer) {
lDebug() << log().arg("Apply Qt Renderer to SDK") << renderer; lDebug() << log().arg("Apply Qt Renderer to SDK") << renderer;
switch (getSourceLocation()) { switch (getSourceLocation()) {
case CorePreview: { case CorePreview: {
App::postModelBlock([qmlName = mQmlName, renderer]() {
lInfo() << "[Camera] (" << qmlName << ") Camera to CorePreview";
CoreModel::getInstance()->getCore()->setNativePreviewWindowId(renderer);
});
} break; } break;
case Call: { case Call: {
App::postModelAsync([qmlName = mQmlName, callGui = mCallGui, renderer]() { App::postModelAsync([qmlName = mQmlName, callGui = mCallGui, renderer]() {
@ -197,6 +206,11 @@ bool CameraGui::getIsPreview() const {
void CameraGui::setIsPreview(bool status) { void CameraGui::setIsPreview(bool status) {
if (mIsPreview != status) { if (mIsPreview != status) {
mIsPreview = status; mIsPreview = status;
// We block it to serialize the action and allow only one CameraGui to change the state.
App::postModelBlock([status]() {
lDebug() << "[CameraGui] " << (status ? "Activation" : "Deactivation");
CoreModel::getInstance()->getCore()->enableVideoPreview(status);
});
updateWindowIdLocation(); updateWindowIdLocation();
update(); update();
@ -240,18 +254,9 @@ CameraGui::WindowIdLocation CameraGui::getSourceLocation() const {
void CameraGui::setWindowIdLocation(const WindowIdLocation &location) { void CameraGui::setWindowIdLocation(const WindowIdLocation &location) {
if (mWindowIdLocation != location) { if (mWindowIdLocation != location) {
lDebug() << log().arg("Update Window Id location from %2 to %3").arg(mWindowIdLocation).arg(location); lDebug() << log().arg("Update Window Id location from %2 to %3").arg(mWindowIdLocation).arg(location);
if (mWindowIdLocation == CorePreview) PreviewManager::getInstance()->unsubscribe(this);
// else if (mWindowIdLocation != None) resetWindowId(); // Location change: Reset old window ID.
resetWindowId(); resetWindowId();
mWindowIdLocation = location; mWindowIdLocation = location;
if (mWindowIdLocation == CorePreview) PreviewManager::getInstance()->subscribe(this); updateSDKRenderer();
else updateSDKRenderer();
// QTimer::singleShot(100, this, &CameraGui::requestNewRenderer);
// if (mWindowIdLocation == WindowIdLocation::CorePreview) {
// mLastVideoDefinition =
// CoreManager::getInstance()->getSettingsModel()->getCurrentPreviewVideoDefinition(); emit
// videoDefinitionChanged(); mLastVideoDefinitionChecker.start();
// } else mLastVideoDefinitionChecker.stop();
} }
} }
void CameraGui::updateWindowIdLocation() { void CameraGui::updateWindowIdLocation() {
@ -262,7 +267,7 @@ void CameraGui::updateWindowIdLocation() {
setWindowIdLocation(WindowIdLocation::Device); setWindowIdLocation(WindowIdLocation::Device);
else setWindowIdLocation(WindowIdLocation::CorePreview); else setWindowIdLocation(WindowIdLocation::CorePreview);
} }
// TODO to remove
void CameraGui::callStateChanged(LinphoneEnums::CallState state) { void CameraGui::callStateChanged(LinphoneEnums::CallState state) {
if (getSourceLocation() == CorePreview && state == LinphoneEnums::CallState::Connected) { if (getSourceLocation() == CorePreview && state == LinphoneEnums::CallState::Connected) {
if (!getIsReady()) { if (!getIsReady()) {

View file

@ -1,136 +0,0 @@
/*
* Copyright (c) 2010-2024 Belledonne Communications SARL.
*
* This file is part of linphone-desktop
* (see https://www.linphone.org).
*
* 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/>.
*/
#include <QOpenGLFramebufferObject>
#include <QQuickWindow>
#include <QThread>
#include <QTimer>
#include "../App.hpp"
#include "PreviewManager.hpp"
#include "tool/Utils.hpp"
DEFINE_ABSTRACT_OBJECT(PreviewManager)
// =============================================================================
PreviewManager *PreviewManager::gInstance = nullptr;
PreviewManager::PreviewManager(QObject *parent) : QObject(parent) {
}
PreviewManager::~PreviewManager() {
}
PreviewManager *PreviewManager::getInstance() {
if (gInstance) return gInstance;
else {
gInstance = new PreviewManager();
return gInstance;
}
}
// Create a Renderer from SDK preview
QQuickFramebufferObject::Renderer *PreviewManager::subscribe(const CameraGui *candidate) {
QQuickFramebufferObject::Renderer *renderer = nullptr;
mCounterMutex.lock();
if (mCandidates.size() == 0) {
activate();
}
auto itCandidate =
std::find_if(mCandidates.begin(), mCandidates.end(),
[candidate](const QPair<const CameraGui *, QQuickFramebufferObject::Renderer *> &item) {
return item.first == candidate;
});
if (itCandidate == mCandidates.end()) {
connect(candidate, &QObject::destroyed, this, qOverload<QObject *>(&PreviewManager::unsubscribe));
mCandidates.append({candidate, nullptr});
itCandidate = mCandidates.end() - 1;
lDebug() << log().arg("Subscribing New") << itCandidate->first->getQmlName();
} else {
lDebug() << log().arg("Resubscribing") << itCandidate->first->getQmlName();
}
mCounterMutex.unlock();
App::postModelBlock([&renderer, isFirst = (itCandidate == mCandidates.begin()),
name = itCandidate->first->getQmlName()]() {
renderer =
(QQuickFramebufferObject::Renderer *)CoreModel::getInstance()->getCore()->createNativePreviewWindowId(
nullptr);
if (!renderer) { // TODO debug
renderer =
(QQuickFramebufferObject::Renderer *)CoreModel::getInstance()->getCore()->createNativePreviewWindowId(
nullptr);
}
if (isFirst) {
lDebug() << "[PreviewManager] " << name << " Set Native Preview Id with " << renderer;
CoreModel::getInstance()->getCore()->setNativePreviewWindowId(renderer);
}
});
mCounterMutex.lock();
itCandidate->second = renderer;
mCounterMutex.unlock();
return renderer;
}
void PreviewManager::unsubscribe(const CameraGui *candidate) { // If nullptr, Use of sender()
mCounterMutex.lock();
auto itCandidate = std::find_if(mCandidates.begin(), mCandidates.end(),
[candidate = (candidate ? candidate : sender())](
const QPair<const CameraGui *, QQuickFramebufferObject::Renderer *> &item) {
return item.first == candidate;
});
if (itCandidate != mCandidates.end()) {
lDebug() << log().arg("Unsubscribing") << itCandidate->first->getQmlName();
disconnect(candidate, nullptr, this, nullptr);
if (mCandidates.size() == 1) {
mCandidates.erase(itCandidate);
deactivate();
} else if (mCandidates.begin() == itCandidate) {
mCandidates.erase(itCandidate);
lDebug() << log().arg("Update") << mCandidates.first().first->getQmlName();
auto renderer = mCandidates.first().second;
if (renderer)
App::postModelBlock([renderer = mCandidates.first().second]() {
CoreModel::getInstance()->getCore()->setNativePreviewWindowId(renderer);
});
} else {
mCandidates.erase(itCandidate);
}
}
mCounterMutex.unlock();
}
void PreviewManager::unsubscribe(QObject *sender) {
unsubscribe(dynamic_cast<CameraGui *>(sender));
}
void PreviewManager::activate() {
App::postModelAsync([]() {
lDebug() << "[PreviewManager] Activation";
CoreModel::getInstance()->getCore()->enableVideoPreview(true);
});
}
void PreviewManager::deactivate() {
App::postModelAsync([]() {
lDebug() << "[PreviewManager] Deactivation";
CoreModel::getInstance()->getCore()->enableVideoPreview(false);
});
}

View file

@ -1,62 +0,0 @@
/*
* Copyright (c) 2024 Belledonne Communications SARL.
*
* This file is part of linphone-desktop
* (see https://www.linphone.org).
*
* 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/>.
*/
#ifndef PREVIEW_MANAGER_H_
#define PREVIEW_MANAGER_H_
#include "CameraGui.hpp"
#include "tool/AbstractObject.hpp"
#include <QMutex>
#include <QObject>
#include <QPair>
#include <QQuickFramebufferObject>
// Manage the SDK preview as a singleton.
// The goal is to process the limitation that only one preview can be displayed.
// On asynchronized application, the destruction of a previous Preview can be done AFTER the creation on a new Preview
// Sticker.
// =============================================================================
class PreviewManager : public QObject, public AbstractObject {
Q_OBJECT
public:
PreviewManager(QObject *parent = nullptr);
virtual ~PreviewManager();
static PreviewManager *getInstance();
QQuickFramebufferObject::Renderer *subscribe(const CameraGui *candidate);
void unsubscribe(const CameraGui *candidate);
void activate();
void deactivate();
public slots:
void unsubscribe(QObject *sender);
private:
QMutex mCounterMutex;
QList<QPair<const CameraGui *, QQuickFramebufferObject::Renderer *>> mCandidates;
static PreviewManager *gInstance;
QQuickFramebufferObject::Renderer *mPreviewRenderer = nullptr;
DECLARE_ABSTRACT_OBJECT
};
#endif

View file

@ -43,7 +43,7 @@ QSharedPointer<ChatCore> ChatCore::create(const std::shared_ptr<linphone::ChatRo
} }
ChatCore::ChatCore(const std::shared_ptr<linphone::ChatRoom> &chatRoom) : QObject(nullptr) { ChatCore::ChatCore(const std::shared_ptr<linphone::ChatRoom> &chatRoom) : QObject(nullptr) {
// lDebug() << "[ChatCore] new" << this; lDebug() << "[ChatCore] new" << this;
mustBeInLinphoneThread(getClassName()); mustBeInLinphoneThread(getClassName());
App::getInstance()->mEngine->setObjectOwnership(this, QQmlEngine::CppOwnership); App::getInstance()->mEngine->setObjectOwnership(this, QQmlEngine::CppOwnership);
mLastUpdatedTime = QDateTime::fromSecsSinceEpoch(chatRoom->getLastUpdateTime()); mLastUpdatedTime = QDateTime::fromSecsSinceEpoch(chatRoom->getLastUpdateTime());
@ -121,7 +121,7 @@ ChatCore::~ChatCore() {
emit mChatModel->removeListener(); emit mChatModel->removeListener();
} }
void ChatCore::setSelf(QSharedPointer<ChatCore> me) { void ChatCore::setSelf(const QSharedPointer<ChatCore> &me) {
mChatModelConnection = SafeConnection<ChatCore, ChatModel>::create(me, mChatModel); mChatModelConnection = SafeConnection<ChatCore, ChatModel>::create(me, mChatModel);
mChatModelConnection->makeConnectToCore(&ChatCore::lDeleteHistory, [this]() { mChatModelConnection->makeConnectToCore(&ChatCore::lDeleteHistory, [this]() {
mChatModelConnection->invokeToModel([this]() { mChatModel->deleteHistory(); }); mChatModelConnection->invokeToModel([this]() { mChatModel->deleteHistory(); });
@ -162,14 +162,13 @@ void ChatCore::setSelf(QSharedPointer<ChatCore> me) {
mChatModelConnection->makeConnectToCore(&ChatCore::lDelete, [this]() { mChatModelConnection->makeConnectToCore(&ChatCore::lDelete, [this]() {
mChatModelConnection->invokeToModel([this]() { mChatModel->deleteChatRoom(); }); mChatModelConnection->invokeToModel([this]() { mChatModel->deleteChatRoom(); });
}); });
mChatModelConnection->makeConnectToModel(
&ChatModel::deleted, [this]() { mChatModelConnection->invokeToCore([this]() { emit deleted(); }); });
mChatModelConnection->makeConnectToModel( mChatModelConnection->makeConnectToModel(
&ChatModel::stateChanged, &ChatModel::stateChanged,
[this](const std::shared_ptr<linphone::ChatRoom> &chatRoom, linphone::ChatRoom::State newState) { [this](const std::shared_ptr<linphone::ChatRoom> &chatRoom, linphone::ChatRoom::State newState) {
auto state = LinphoneEnums::fromLinphone(newState); auto state = LinphoneEnums::fromLinphone(newState);
bool isReadOnly = chatRoom->isReadOnly(); bool isReadOnly = chatRoom->isReadOnly();
if (newState == linphone::ChatRoom::State::Deleted) emit deleted();
mChatModelConnection->invokeToCore([this, state, isReadOnly]() { mChatModelConnection->invokeToCore([this, state, isReadOnly]() {
setChatRoomState(state); setChatRoomState(state);
setIsReadOnly(isReadOnly); setIsReadOnly(isReadOnly);
@ -199,7 +198,11 @@ void ChatCore::setSelf(QSharedPointer<ChatCore> me) {
emit conferenceJoined(); emit conferenceJoined();
}); });
} }
mChatModelConnection->invokeToCore([this, participants]() { setParticipants(participants); }); auto meAdmin = chatRoom->getMe() && chatRoom->getMe()->isAdmin();
mChatModelConnection->invokeToCore([this, participants, meAdmin]() {
setParticipants(participants);
setMeAdmin(meAdmin);
});
}); });
// Events (excluding messages) // Events (excluding messages)
@ -207,8 +210,9 @@ void ChatCore::setSelf(QSharedPointer<ChatCore> me) {
&ChatModel::newEvent, [this](const std::shared_ptr<linphone::ChatRoom> &chatRoom, &ChatModel::newEvent, [this](const std::shared_ptr<linphone::ChatRoom> &chatRoom,
const std::shared_ptr<const linphone::EventLog> &eventLog) { const std::shared_ptr<const linphone::EventLog> &eventLog) {
if (mChatModel->getMonitor() != chatRoom) return; if (mChatModel->getMonitor() != chatRoom) return;
qDebug() << "EVENT LOG RECEIVED IN CHATROOM" << mChatModel->getTitle(); if (!eventLog) return;
auto event = EventLogCore::create(eventLog); lDebug() << log().arg("EVENT LOG RECEIVED IN CHATROOM") << this << mChatModel->getTitle();
auto event = EventLogCore::create(eventLog, chatRoom);
if (event->isHandled()) { if (event->isHandled()) {
mChatModelConnection->invokeToCore([this, event]() { emit eventsInserted({event}); }); mChatModelConnection->invokeToCore([this, event]() { emit eventsInserted({event}); });
} }
@ -219,11 +223,25 @@ void ChatCore::setSelf(QSharedPointer<ChatCore> me) {
mChatModelConnection->makeConnectToModel( mChatModelConnection->makeConnectToModel(
&ChatModel::chatMessagesReceived, [this](const std::shared_ptr<linphone::ChatRoom> &chatRoom, &ChatModel::chatMessagesReceived, [this](const std::shared_ptr<linphone::ChatRoom> &chatRoom,
const std::list<std::shared_ptr<linphone::EventLog>> &eventsLog) { const std::list<std::shared_ptr<linphone::EventLog>> &eventsLog) {
if (!mChatModel) {
lWarning() << log().arg("Chat model is null !");
return;
} else if (!mChatModelConnection) {
lWarning() << log().arg("Connection between Core and Model is null !");
return;
}
if (mChatModel->getMonitor() != chatRoom) return; if (mChatModel->getMonitor() != chatRoom) return;
qDebug() << "CHAT MESSAGE RECEIVED IN CHATROOM" << mChatModel->getTitle(); lDebug() << log().arg("CHAT MESSAGE RECEIVED IN CHATROOM") << this << mChatModel->getTitle();
lInfo() << log().arg("Chat message received in chatroom") << this << mChatModel->getTitle();
lInfo() << log().arg("Safe Connection =") << mChatModelConnection.get();
lInfo() << log().arg("this =") << this;
QList<QSharedPointer<EventLogCore>> list; QList<QSharedPointer<EventLogCore>> list;
for (auto &e : eventsLog) { for (auto &e : eventsLog) {
auto event = EventLogCore::create(e); if (!e) {
lWarning() << log().arg("Event log is null, continue");
continue;
}
auto event = EventLogCore::create(e, chatRoom);
list.push_back(event); list.push_back(event);
} }
mChatModelConnection->invokeToCore([this, list]() { mChatModelConnection->invokeToCore([this, list]() {
@ -234,13 +252,14 @@ void ChatCore::setSelf(QSharedPointer<ChatCore> me) {
}); });
mChatModelConnection->makeConnectToCore(&ChatCore::lMarkAsRead, [this]() { mChatModelConnection->makeConnectToCore(&ChatCore::lMarkAsRead, [this]() {
auto mainWindow = Utils::getMainWindow(); auto lastActiveWindow = Utils::getLastActiveWindow();
if (mainWindow->isActive()) mChatModelConnection->invokeToModel([this]() { mChatModel->markAsRead(); }); if (lastActiveWindow && lastActiveWindow->isActive())
mChatModelConnection->invokeToModel([this]() { mChatModel->markAsRead(); });
else { else {
connect(mainWindow, &QQuickWindow::activeChanged, this, [this, mainWindow] { connect(lastActiveWindow, &QQuickWindow::activeChanged, this, [this, lastActiveWindow] {
if (mainWindow->isActive()) { if (lastActiveWindow->isActive()) {
disconnect(mainWindow, &QQuickWindow::activeChanged, this, nullptr); disconnect(lastActiveWindow, &QQuickWindow::activeChanged, this, nullptr);
mChatModelConnection->invokeToModel([this, mainWindow] { mChatModel->markAsRead(); }); mChatModelConnection->invokeToModel([this, lastActiveWindow] { mChatModel->markAsRead(); });
} }
}); });
} }
@ -285,7 +304,7 @@ void ChatCore::setSelf(QSharedPointer<ChatCore> me) {
mChatModelConnection->makeConnectToModel( mChatModelConnection->makeConnectToModel(
&ChatModel::chatMessageSending, [this](const std::shared_ptr<linphone::ChatRoom> &chatRoom, &ChatModel::chatMessageSending, [this](const std::shared_ptr<linphone::ChatRoom> &chatRoom,
const std::shared_ptr<const linphone::EventLog> &eventLog) { const std::shared_ptr<const linphone::EventLog> &eventLog) {
auto event = EventLogCore::create(eventLog); auto event = EventLogCore::create(eventLog, chatRoom);
mChatModelConnection->invokeToCore([this, event]() { emit eventsInserted({event}); }); mChatModelConnection->invokeToCore([this, event]() { emit eventsInserted({event}); });
}); });
mChatModelConnection->makeConnectToCore( mChatModelConnection->makeConnectToCore(
@ -362,12 +381,16 @@ void ChatCore::setSelf(QSharedPointer<ChatCore> me) {
auto participants = buildParticipants(chatRoom); auto participants = buildParticipants(chatRoom);
mChatModelConnection->invokeToCore([this, participants]() { setParticipants(participants); }); mChatModelConnection->invokeToCore([this, participants]() { setParticipants(participants); });
}); });
mChatModelConnection->makeConnectToModel( mChatModelConnection->makeConnectToModel(&ChatModel::participantAdminStatusChanged,
&ChatModel::participantAdminStatusChanged, [this](const std::shared_ptr<linphone::ChatRoom> &chatRoom, [this](const std::shared_ptr<linphone::ChatRoom> &chatRoom,
const std::shared_ptr<const linphone::EventLog> &eventLog) { const std::shared_ptr<const linphone::EventLog> &eventLog) {
auto participants = buildParticipants(chatRoom); auto participants = buildParticipants(chatRoom);
mChatModelConnection->invokeToCore([this, participants]() { setParticipants(participants); }); bool meAdmin = chatRoom->getMe()->isAdmin();
}); mChatModelConnection->invokeToCore([this, participants, meAdmin]() {
setParticipants(participants);
setMeAdmin(meAdmin);
});
});
mChatModelConnection->makeConnectToCore(&ChatCore::lRemoveParticipantAtIndex, [this](int index) { mChatModelConnection->makeConnectToCore(&ChatCore::lRemoveParticipantAtIndex, [this](int index) {
mChatModelConnection->invokeToModel([this, index]() { mChatModel->removeParticipantAtIndex(index); }); mChatModelConnection->invokeToModel([this, index]() { mChatModel->removeParticipantAtIndex(index); });
}); });
@ -491,7 +514,7 @@ ChatMessageGui *ChatCore::getLastMessage() const {
void ChatCore::setLastMessage(QSharedPointer<ChatMessageCore> lastMessage) { void ChatCore::setLastMessage(QSharedPointer<ChatMessageCore> lastMessage) {
if (mLastMessage != lastMessage) { if (mLastMessage != lastMessage) {
disconnect(mLastMessage.get()); if (mLastMessage) disconnect(mLastMessage.get(), &ChatMessageCore::messageStateChanged, this, nullptr);
mLastMessage = lastMessage; mLastMessage = lastMessage;
connect(mLastMessage.get(), &ChatMessageCore::messageStateChanged, this, &ChatCore::lastMessageChanged); connect(mLastMessage.get(), &ChatMessageCore::messageStateChanged, this, &ChatCore::lastMessageChanged);
emit lastMessageChanged(); emit lastMessageChanged();
@ -544,10 +567,6 @@ std::shared_ptr<ChatModel> ChatCore::getModel() const {
return mChatModel; return mChatModel;
} }
QSharedPointer<SafeConnection<ChatCore, ChatModel>> ChatCore::getChatModelConnection() const {
return mChatModelConnection;
}
bool ChatCore::isMuted() const { bool ChatCore::isMuted() const {
return mIsMuted; return mIsMuted;
} }
@ -621,6 +640,8 @@ ChatCore::buildParticipants(const std::shared_ptr<linphone::ChatRoom> &chatRoom)
auto participantCore = ParticipantCore::create(participant); auto participantCore = ParticipantCore::create(participant);
result.append(participantCore); result.append(participantCore);
} }
auto meCore = ParticipantCore::create(chatRoom->getMe());
if (meCore) result.append(meCore);
return result; return result;
} }

View file

@ -64,7 +64,6 @@ public:
Q_PROPERTY( Q_PROPERTY(
int ephemeralLifetime READ getEphemeralLifetime WRITE lSetEphemeralLifetime NOTIFY ephemeralLifetimeChanged) int ephemeralLifetime READ getEphemeralLifetime WRITE lSetEphemeralLifetime NOTIFY ephemeralLifetimeChanged)
Q_PROPERTY(bool muted READ isMuted WRITE lSetMuted NOTIFY mutedChanged) Q_PROPERTY(bool muted READ isMuted WRITE lSetMuted NOTIFY mutedChanged)
Q_PROPERTY(bool conferenceJoined MEMBER mConferenceJoined NOTIFY conferenceJoined)
Q_PROPERTY(bool meAdmin READ getMeAdmin WRITE setMeAdmin NOTIFY meAdminChanged) Q_PROPERTY(bool meAdmin READ getMeAdmin WRITE setMeAdmin NOTIFY meAdminChanged)
Q_PROPERTY(QVariantList participants READ getParticipantsGui NOTIFY participantsChanged) Q_PROPERTY(QVariantList participants READ getParticipantsGui NOTIFY participantsChanged)
Q_PROPERTY(QStringList participantsAddresses READ getParticipantsAddresses WRITE lSetParticipantsAddresses NOTIFY Q_PROPERTY(QStringList participantsAddresses READ getParticipantsAddresses WRITE lSetParticipantsAddresses NOTIFY
@ -75,7 +74,7 @@ public:
static QSharedPointer<ChatCore> create(const std::shared_ptr<linphone::ChatRoom> &chatRoom); static QSharedPointer<ChatCore> create(const std::shared_ptr<linphone::ChatRoom> &chatRoom);
ChatCore(const std::shared_ptr<linphone::ChatRoom> &chatRoom); ChatCore(const std::shared_ptr<linphone::ChatRoom> &chatRoom);
~ChatCore(); ~ChatCore();
void setSelf(QSharedPointer<ChatCore> me); void setSelf(const QSharedPointer<ChatCore> &me);
QDateTime getLastUpdatedTime() const; QDateTime getLastUpdatedTime() const;
void setLastUpdatedTime(QDateTime time); void setLastUpdatedTime(QDateTime time);
@ -142,7 +141,6 @@ public:
void setComposingAddress(QString composingAddress); void setComposingAddress(QString composingAddress);
std::shared_ptr<ChatModel> getModel() const; std::shared_ptr<ChatModel> getModel() const;
QSharedPointer<SafeConnection<ChatCore, ChatModel>> getChatModelConnection() const;
void setParticipants(QList<QSharedPointer<ParticipantCore>> participants); void setParticipants(QList<QSharedPointer<ParticipantCore>> participants);
QList<QSharedPointer<ParticipantCore>> buildParticipants(const std::shared_ptr<linphone::ChatRoom> &chatRoom) const; QList<QSharedPointer<ParticipantCore>> buildParticipants(const std::shared_ptr<linphone::ChatRoom> &chatRoom) const;

View file

@ -50,17 +50,19 @@ ChatList::ChatList(QObject *parent) : ListProxy(parent) {
ChatList::~ChatList() { ChatList::~ChatList() {
mustBeInMainThread("~" + getClassName()); mustBeInMainThread("~" + getClassName());
mModelConnection = nullptr; mModelConnection->disconnect();
} }
void ChatList::connectItem(QSharedPointer<ChatCore> chat) { void ChatList::connectItem(QSharedPointer<ChatCore> chat) {
connect(chat.get(), &ChatCore::deleted, this, [this, chat] { connect(
disconnect(chat.get()); chat.get(), &ChatCore::deleted, this,
remove(chat); [this, chat] {
// We cannot use countChanged here because it is called before mList disconnect(chat.get(), &ChatCore::unreadMessagesCountChanged, this, nullptr);
// really has removed the item, then emit specific signal disconnect(chat.get(), &ChatCore::lastUpdatedTimeChanged, this, nullptr);
emit chatRemoved(chat ? new ChatGui(chat) : nullptr); disconnect(chat.get(), &ChatCore::lastMessageChanged, this, nullptr);
}); remove(chat);
},
Qt::SingleShotConnection);
auto dataChange = [this, chat] { auto dataChange = [this, chat] {
int i = -1; int i = -1;
get(chat.get(), &i); get(chat.get(), &i);
@ -71,7 +73,9 @@ void ChatList::connectItem(QSharedPointer<ChatCore> chat) {
}; };
connect(chat.get(), &ChatCore::unreadMessagesCountChanged, this, [this, dataChange] { connect(chat.get(), &ChatCore::unreadMessagesCountChanged, this, [this, dataChange] {
dataChange(); dataChange();
auto defaultAccount = App::getInstance()->getAccountList()->getDefaultAccountCore(); auto defaultAccount = App::getInstance()->getAccountList()
? App::getInstance()->getAccountList()->getDefaultAccountCore()
: nullptr;
if (defaultAccount) emit defaultAccount->lRefreshNotifications(); if (defaultAccount) emit defaultAccount->lRefreshNotifications();
}); });
connect(chat.get(), &ChatCore::lastUpdatedTimeChanged, this, dataChange); connect(chat.get(), &ChatCore::lastUpdatedTimeChanged, this, dataChange);
@ -91,16 +95,17 @@ void ChatList::setSelf(QSharedPointer<ChatList> me) {
return; return;
} }
setIsUpdating(true); setIsUpdating(true);
beginResetModel();
mModelConnection->invokeToModel([this]() { mModelConnection->invokeToModel([this]() {
mustBeInLinphoneThread(getClassName()); mustBeInLinphoneThread(getClassName());
beginResetModel();
mList.clear();
// Avoid copy to lambdas // Avoid copy to lambdas
QList<QSharedPointer<ChatCore>> *chats = new QList<QSharedPointer<ChatCore>>(); QList<QSharedPointer<ChatCore>> *chats = new QList<QSharedPointer<ChatCore>>();
auto currentAccount = CoreModel::getInstance()->getCore()->getDefaultAccount(); auto currentAccount = CoreModel::getInstance()->getCore()->getDefaultAccount();
if (!currentAccount) { if (!currentAccount) {
setIsUpdating(false); mModelConnection->invokeToCore([this, chats]() {
endResetModel(); setIsUpdating(false);
endResetModel();
});
return; return;
} }
auto linphoneChatRooms = currentAccount->filterChatRooms(Utils::appStringToCoreString(mFilter)); auto linphoneChatRooms = currentAccount->filterChatRooms(Utils::appStringToCoreString(mFilter));
@ -118,12 +123,14 @@ void ChatList::setSelf(QSharedPointer<ChatList> me) {
disconnect(chat.get(), &ChatCore::lastMessageChanged, this, nullptr); disconnect(chat.get(), &ChatCore::lastMessageChanged, this, nullptr);
} }
} }
mList.clear();
for (auto &chat : *chats) { for (auto &chat : *chats) {
connectItem(chat); connectItem(chat);
mList.append(chat);
} }
add(*chats);
endResetModel(); endResetModel();
setIsUpdating(false); setIsUpdating(false);
chats->clear();
delete chats; delete chats;
}); });
}); });
@ -139,22 +146,11 @@ void ChatList::setSelf(QSharedPointer<ChatList> me) {
mustBeInLinphoneThread(log().arg(Q_FUNC_INFO)); mustBeInLinphoneThread(log().arg(Q_FUNC_INFO));
if (!message) return; if (!message) return;
if (room->getAccount() != core->getDefaultAccount()) { if (room->getAccount() != core->getDefaultAccount()) {
qWarning() << log().arg("Chat room does not refer to current account, return"); qInfo() << log().arg("Chat room does not refer to current account, return");
return; return;
} }
auto chatCore = ChatCore::create(room); auto chatCore = ChatCore::create(room);
mModelConnection->invokeToCore([this, chatCore] { mModelConnection->invokeToCore([this, chatCore] { addChatInList(chatCore, false); });
auto chatList = getSharedList<ChatCore>();
auto it = std::find_if(chatList.begin(), chatList.end(), [chatCore](const QSharedPointer<ChatCore> item) {
return item && chatCore && item->getModel() && chatCore->getModel() &&
item->getModel()->getMonitor() == chatCore->getModel()->getMonitor();
});
if (it == chatList.end()) {
connectItem(chatCore);
add(chatCore);
emit chatAdded();
}
});
}; };
mModelConnection->makeConnectToModel(&CoreModel::messageReceived, mModelConnection->makeConnectToModel(&CoreModel::messageReceived,
[this, addChatToList](const std::shared_ptr<linphone::Core> &core, [this, addChatToList](const std::shared_ptr<linphone::Core> &core,
@ -177,6 +173,33 @@ void ChatList::setSelf(QSharedPointer<ChatList> me) {
const std::shared_ptr<const linphone::ChatMessageReaction> &reaction) { const std::shared_ptr<const linphone::ChatMessageReaction> &reaction) {
addChatToList(core, room, message); addChatToList(core, room, message);
}); });
mModelConnection->makeConnectToModel(
&CoreModel::chatRoomStateChanged,
[this, addChatToList](const std::shared_ptr<linphone::Core> &core,
const std::shared_ptr<linphone::ChatRoom> &chatRoom, linphone::ChatRoom::State state) {
if (state == linphone::ChatRoom::State::Created) {
bool sendAddSignal = false;
if (chatRoom == CoreModel::getInstance()->mChatRoomBeingCreated) {
sendAddSignal = true;
}
CoreModel::getInstance()->mChatRoomBeingCreated = nullptr;
if (chatRoom->getConferenceInfo()) {
qWarning() << log().arg("Chatroom created during a conference, return");
return;
}
auto chatAccount = chatRoom->getAccount();
auto defaultAccount = core->getDefaultAccount();
if (!chatAccount || !defaultAccount) return;
if (!chatAccount->getParams()->getIdentityAddress()->weakEqual(
defaultAccount->getParams()->getIdentityAddress())) {
qWarning() << log().arg("Chatroom does not refer to current account, return");
return;
}
auto chatCore = ChatCore::create(chatRoom);
mModelConnection->invokeToCore(
[this, chatCore, sendAddSignal] { addChatInList(chatCore, sendAddSignal); });
}
});
connect(this, &ChatList::filterChanged, [this](QString filter) { connect(this, &ChatList::filterChanged, [this](QString filter) {
mFilter = filter; mFilter = filter;
@ -195,17 +218,23 @@ int ChatList::findChatIndex(ChatGui *chatGui) {
return it == chatList.end() ? -1 : std::distance(chatList.begin(), it); return it == chatList.end() ? -1 : std::distance(chatList.begin(), it);
} }
void ChatList::addChatInList(QSharedPointer<ChatCore> chatCore) { bool ChatList::addChatInList(QSharedPointer<ChatCore> chatCore, bool emitAddSignal) {
mustBeInMainThread(log().arg(Q_FUNC_INFO));
if (chatCore->getIdentifier().isEmpty()) {
qWarning() << "ChatRoom with invalid identifier cannot be added to the list, return";
return false;
}
auto chatList = getSharedList<ChatCore>(); auto chatList = getSharedList<ChatCore>();
auto it = std::find_if(chatList.begin(), chatList.end(), [chatCore](const QSharedPointer<ChatCore> item) { auto it = std::find_if(chatList.begin(), chatList.end(), [chatCore](const QSharedPointer<ChatCore> item) {
return item && chatCore && item->getModel() && chatCore->getModel() && return item && chatCore && item->getIdentifier() == chatCore->getIdentifier();
item->getModel()->getMonitor() == chatCore->getModel()->getMonitor();
}); });
if (it == chatList.end()) { if (it == chatList.end()) {
connectItem(chatCore); connectItem(chatCore);
add(chatCore); add(chatCore);
emit chatAdded(); if (emitAddSignal) emit chatAdded(chatCore);
return true;
} }
return false;
} }
QVariant ChatList::data(const QModelIndex &index, int role) const { QVariant ChatList::data(const QModelIndex &index, int role) const {

View file

@ -42,14 +42,13 @@ public:
void connectItem(QSharedPointer<ChatCore> chat); void connectItem(QSharedPointer<ChatCore> chat);
int findChatIndex(ChatGui *chat); int findChatIndex(ChatGui *chat);
void addChatInList(QSharedPointer<ChatCore> chatCore); bool addChatInList(QSharedPointer<ChatCore> chatCore, bool emitAddSignal);
virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
signals: signals:
void lUpdate(); void lUpdate();
void filterChanged(QString filter); void filterChanged(QString filter);
void chatRemoved(ChatGui *chat); void chatAdded(QSharedPointer<ChatCore> chatCore);
void chatAdded();
void chatUpdated(); void chatUpdated();
private: private:

View file

@ -25,63 +25,62 @@
DEFINE_ABSTRACT_OBJECT(ChatProxy) DEFINE_ABSTRACT_OBJECT(ChatProxy)
ChatProxy::ChatProxy(QObject *parent) : LimitProxy(parent) { ChatProxy::ChatProxy(QObject *parent) {
mList = ChatList::create(); mList = ChatList::create();
setSourceModel(mList.get()); setSourceModel(mList.get());
setDynamicSortFilter(true);
} }
ChatProxy::~ChatProxy() { ChatProxy::~ChatProxy() {
} }
void ChatProxy::setSourceModel(QAbstractItemModel *model) { void ChatProxy::setSourceModel(QAbstractItemModel *model) {
auto oldChatList = getListModel<ChatList>(); auto oldChatList = dynamic_cast<ChatList *>(sourceModel());
if (oldChatList) { if (oldChatList) {
disconnect(oldChatList); disconnect(this, &ChatProxy::filterTextChanged, oldChatList, nullptr);
disconnect(oldChatList, &ChatList::chatAdded, this, nullptr);
disconnect(oldChatList, &ChatList::dataChanged, this, nullptr);
} }
auto newChatList = dynamic_cast<ChatList *>(model); auto newChatList = dynamic_cast<ChatList *>(model);
if (newChatList) { if (newChatList) {
connect(this, &ChatProxy::filterTextChanged, newChatList, connect(this, &ChatProxy::filterTextChanged, newChatList,
[this, newChatList] { emit newChatList->filterChanged(getFilterText()); }); [this, newChatList] { emit newChatList->filterChanged(getFilterText()); });
connect(newChatList, &ChatList::chatRemoved, this, &ChatProxy::chatRemoved); connect(newChatList, &ChatList::chatAdded, this, [this](QSharedPointer<ChatCore> chatCore) {
connect(newChatList, &ChatList::chatAdded, this, [this] { invalidate(); }); if (chatCore) {
invalidate();
emit chatAdded(new ChatGui(chatCore));
}
});
connect(newChatList, &ChatList::dataChanged, this, [this] { invalidate(); }); connect(newChatList, &ChatList::dataChanged, this, [this] { invalidate(); });
} }
auto firstList = new SortFilterList(model, Qt::AscendingOrder); QSortFilterProxyModel::setSourceModel(newChatList);
firstList->setDynamicSortFilter(true);
setSourceModels(firstList);
sort(0); sort(0);
emit modelChanged();
} }
int ChatProxy::findChatIndex(ChatGui *chatGui) { int ChatProxy::findChatIndex(ChatGui *chatGui) {
auto chatList = getListModel<ChatList>(); auto chatList = dynamic_cast<ChatList *>(sourceModel());
if (chatList) { if (chatList) {
auto listIndex = chatList->findChatIndex(chatGui); auto listIndex = chatList->findChatIndex(chatGui);
if (listIndex != -1) { if (listIndex != -1) {
listIndex = listIndex = mapFromSource(chatList->index(listIndex, 0)).row();
dynamic_cast<SortFilterList *>(sourceModel())->mapFromSource(chatList->index(listIndex, 0)).row();
if (mMaxDisplayItems <= listIndex) setMaxDisplayItems(listIndex + mDisplayItemsStep);
return listIndex; return listIndex;
} }
} }
return -1; return -1;
} }
void ChatProxy::addChatInList(ChatGui *chatGui) { bool ChatProxy::addChatInList(ChatGui *chatGui) {
auto chatList = getListModel<ChatList>(); auto chatList = dynamic_cast<ChatList *>(sourceModel());
if (chatList && chatGui) { if (chatList && chatGui) {
chatList->addChatInList(chatGui->mCore); return chatList->addChatInList(chatGui->mCore, true);
} }
return false;
} }
bool ChatProxy::SortFilterList::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const { bool ChatProxy::lessThan(const QModelIndex &sourceLeft, const QModelIndex &sourceRight) const {
return true;
}
bool ChatProxy::SortFilterList::lessThan(const QModelIndex &sourceLeft, const QModelIndex &sourceRight) const {
if (!mFilterText.isEmpty()) return false; if (!mFilterText.isEmpty()) return false;
auto l = getItemAtSource<ChatList, ChatCore>(sourceLeft.row()); auto l = getItemAtSource<ChatList, ChatCore>(sourceLeft.row());
auto r = getItemAtSource<ChatList, ChatCore>(sourceRight.row()); auto r = getItemAtSource<ChatList, ChatCore>(sourceRight.row());
if (l && r) return l->getLastUpdatedTime() >= r->getLastUpdatedTime(); if (l && r) return l->getLastUpdatedTime() > r->getLastUpdatedTime();
return false; return false;
} }

View file

@ -21,29 +21,31 @@
#ifndef CHAT_PROXY_H_ #ifndef CHAT_PROXY_H_
#define CHAT_PROXY_H_ #define CHAT_PROXY_H_
#include "../proxy/LimitProxy.hpp" #include "../proxy/SortFilterProxy.hpp"
#include "core/chat/ChatGui.hpp" #include "core/chat/ChatGui.hpp"
#include "core/chat/ChatList.hpp" #include "core/chat/ChatList.hpp"
#include "tool/AbstractObject.hpp" #include "tool/AbstractObject.hpp"
// ============================================================================= // =============================================================================
class ChatProxy : public LimitProxy, public AbstractObject { class ChatProxy : public SortFilterProxy, public AbstractObject {
Q_OBJECT Q_OBJECT
Q_PROPERTY(QAbstractItemModel *model WRITE setSourceModel NOTIFY modelChanged)
public: public:
DECLARE_SORTFILTER_CLASS()
ChatProxy(QObject *parent = Q_NULLPTR); ChatProxy(QObject *parent = Q_NULLPTR);
~ChatProxy(); ~ChatProxy();
void setSourceModel(QAbstractItemModel *sourceModel) override; void setSourceModel(QAbstractItemModel *sourceModel) override;
bool lessThan(const QModelIndex &sourceLeft, const QModelIndex &sourceRight) const override;
Q_INVOKABLE int findChatIndex(ChatGui *chatGui); Q_INVOKABLE int findChatIndex(ChatGui *chatGui);
Q_INVOKABLE void addChatInList(ChatGui *chatGui); Q_INVOKABLE bool addChatInList(ChatGui *chatGui);
signals: signals:
void chatRemoved(ChatGui *chat); void chatAdded(ChatGui *chat);
void modelChanged();
protected: protected:
QSharedPointer<ChatList> mList; QSharedPointer<ChatList> mList;

View file

@ -77,10 +77,12 @@ void ChatMessageFileList::setSelf(QSharedPointer<ChatMessageFileList> me) {
docs = chatModel->getSharedDocuments(); docs = chatModel->getSharedDocuments();
} }
for (auto it : medias) { for (auto it : medias) {
if (it->isVoiceRecording()) continue;
auto model = ChatMessageContentCore::create(it, nullptr); auto model = ChatMessageContentCore::create(it, nullptr);
contents->push_back(model); contents->push_back(model);
} }
for (auto it : docs) { for (auto it : docs) {
if (it->isVoiceRecording()) continue;
auto model = ChatMessageContentCore::create(it, nullptr); auto model = ChatMessageContentCore::create(it, nullptr);
contents->push_back(model); contents->push_back(model);
} }

View file

@ -112,88 +112,97 @@ ChatMessageCore::ChatMessageCore(const std::shared_ptr<linphone::ChatMessage> &c
// lDebug() << "[ChatMessageCore] new" << this; // lDebug() << "[ChatMessageCore] new" << this;
mustBeInLinphoneThread(getClassName()); mustBeInLinphoneThread(getClassName());
App::getInstance()->mEngine->setObjectOwnership(this, QQmlEngine::CppOwnership); App::getInstance()->mEngine->setObjectOwnership(this, QQmlEngine::CppOwnership);
mChatMessageModel = Utils::makeQObject_ptr<ChatMessageModel>(chatmessage); if (chatmessage) {
mChatMessageModel->setSelf(mChatMessageModel); mChatMessageModel = Utils::makeQObject_ptr<ChatMessageModel>(chatmessage);
mText = ToolModel::getMessageFromContent(chatmessage->getContents()); mChatMessageModel->setSelf(mChatMessageModel);
mUtf8Text = mChatMessageModel->getUtf8Text(); mText = ToolModel::getMessageFromMessage(chatmessage);
mHasTextContent = mChatMessageModel->getHasTextContent(); mUtf8Text = mChatMessageModel->getUtf8Text();
mTimestamp = QDateTime::fromSecsSinceEpoch(chatmessage->getTime()); mHasTextContent = mChatMessageModel->getHasTextContent();
mIsOutgoing = chatmessage->isOutgoing(); mTimestamp = QDateTime::fromSecsSinceEpoch(chatmessage->getTime());
mIsRemoteMessage = !chatmessage->isOutgoing(); mIsOutgoing = chatmessage->isOutgoing();
mPeerAddress = Utils::coreStringToAppString(chatmessage->getPeerAddress()->asStringUriOnly()); mIsRetractable =
mPeerName = ToolModel::getDisplayName(chatmessage->getPeerAddress()); chatmessage->isOutgoing() && chatmessage->isRetractable() && !chatmessage->getChatRoom()->isReadOnly();
auto fromAddress = chatmessage->getFromAddress(); mIsRetracted = chatmessage->isRetracted();
// fromAddress->clean(); mIsEditable =
mFromAddress = Utils::coreStringToAppString(fromAddress->asStringUriOnly()); chatmessage->isOutgoing() && chatmessage->isEditable() && !chatmessage->getChatRoom()->isReadOnly();
mFromName = ToolModel::getDisplayName(chatmessage->getFromAddress()); mIsEdited = chatmessage->isEdited();
mToName = ToolModel::getDisplayName(chatmessage->getToAddress()); mIsRemoteMessage = !chatmessage->isOutgoing();
mPeerAddress = Utils::coreStringToAppString(chatmessage->getPeerAddress()->asStringUriOnly());
mPeerName = ToolModel::getDisplayName(chatmessage->getPeerAddress());
auto fromAddress = chatmessage->getFromAddress();
// fromAddress->clean();
mFromAddress = Utils::coreStringToAppString(fromAddress->asStringUriOnly());
mFromName = ToolModel::getDisplayName(chatmessage->getFromAddress());
mToName = ToolModel::getDisplayName(chatmessage->getToAddress());
auto chatroom = chatmessage->getChatRoom();
mIsFromChatGroup = chatroom->hasCapability((int)linphone::ChatRoom::Capabilities::Conference) &&
!chatroom->hasCapability((int)linphone::ChatRoom::Capabilities::OneToOne);
mIsRead = chatmessage->isRead();
mMessageState = LinphoneEnums::fromLinphone(chatmessage->getState());
mIsEphemeral = chatmessage->isEphemeral();
auto chatroom = chatmessage->getChatRoom(); if (mIsEphemeral) {
mIsFromChatGroup = chatroom->hasCapability((int)linphone::ChatRoom::Capabilities::Conference) && auto now = QDateTime::currentDateTime();
!chatroom->hasCapability((int)linphone::ChatRoom::Capabilities::OneToOne); mEphemeralDuration = chatmessage->getEphemeralExpireTime() == 0
mIsRead = chatmessage->isRead(); ? chatmessage->getEphemeralLifetime()
mMessageState = LinphoneEnums::fromLinphone(chatmessage->getState()); : now.secsTo(QDateTime::fromSecsSinceEpoch(chatmessage->getEphemeralExpireTime()));
mIsEphemeral = chatmessage->isEphemeral();
if (mIsEphemeral) {
auto now = QDateTime::currentDateTime();
mEphemeralDuration = chatmessage->getEphemeralExpireTime() == 0
? chatmessage->getEphemeralLifetime()
: now.secsTo(QDateTime::fromSecsSinceEpoch(chatmessage->getEphemeralExpireTime()));
}
mMessageId = Utils::coreStringToAppString(chatmessage->getMessageId());
for (auto content : chatmessage->getContents()) {
auto contentCore = ChatMessageContentCore::create(content, mChatMessageModel);
mChatMessageContentList.push_back(contentCore);
if ((content->isFile() || content->isFileTransfer()) && !content->isVoiceRecording()) mHasFileContent = true;
if (content->isIcalendar()) mIsCalendarInvite = true;
if (content->isVoiceRecording()) {
mIsVoiceRecording = true;
mVoiceRecordingContent = contentCore;
} }
} mMessageId = Utils::coreStringToAppString(chatmessage->getMessageId());
//: "Reactions": all reactions for one message label for (auto content : chatmessage->getContents()) {
mTotalReactionsLabel = tr("all_reactions_label"); auto contentCore = ChatMessageContentCore::create(content, mChatMessageModel);
auto reac = chatmessage->getOwnReaction(); mChatMessageContentList.push_back(contentCore);
mOwnReaction = reac ? Utils::coreStringToAppString(reac->getBody()) : QString(); if ((content->isFile() || content->isFileTransfer()) && !content->isVoiceRecording())
for (auto &reaction : chatmessage->getReactions()) { mHasFileContent = true;
if (reaction) { if (content->isIcalendar()) mIsCalendarInvite = true;
auto fromAddr = reaction->getFromAddress()->clone(); if (content->isVoiceRecording()) {
fromAddr->clean(); mIsVoiceRecording = true;
auto reac = mVoiceRecordingContent = contentCore;
Reaction::createMessageReactionVariant(Utils::coreStringToAppString(reaction->getBody()),
Utils::coreStringToAppString(fromAddr->asStringUriOnly()));
mReactions.append(reac);
auto it = std::find_if(mReactionsSingletonMap.begin(), mReactionsSingletonMap.end(),
[body = reac.mBody](QVariant data) {
auto dataBody = data.toMap()["body"].toString();
return body == dataBody;
});
if (it == mReactionsSingletonMap.end())
mReactionsSingletonMap.push_back(createReactionSingletonVariant(reac.mBody, 1));
else {
auto map = it->toMap();
auto count = map["count"].toInt();
++count;
map.remove("count");
map.insert("count", count);
mReactionsSingletonMap.erase(it);
mReactionsSingletonMap.push_back(map);
} }
} }
} //: "Reactions": all reactions for one message label
connect(this, &ChatMessageCore::messageReactionChanged, this, &ChatMessageCore::resetReactionsSingleton); mTotalReactionsLabel = tr("all_reactions_label");
auto reac = chatmessage->getOwnReaction();
mOwnReaction = reac ? Utils::coreStringToAppString(reac->getBody()) : QString();
for (const auto &reaction : chatmessage->getReactions()) {
if (reaction) {
auto fromAddr = reaction->getFromAddress()->clone();
fromAddr->clean();
auto reac =
Reaction::createMessageReactionVariant(Utils::coreStringToAppString(reaction->getBody()),
Utils::coreStringToAppString(fromAddr->asStringUriOnly()));
mReactions.append(reac);
mIsForward = chatmessage->isForward(); auto it = std::find_if(mReactionsSingletonMap.begin(), mReactionsSingletonMap.end(),
mIsReply = chatmessage->isReply(); [body = reac.mBody](QVariant data) {
if (mIsReply) { auto dataBody = data.toMap()["body"].toString();
auto replymessage = chatmessage->getReplyMessage(); return body == dataBody;
if (replymessage) { });
mReplyText = ToolModel::getMessageFromContent(replymessage->getContents()); if (it == mReactionsSingletonMap.end())
if (mIsFromChatGroup) mRepliedToName = ToolModel::getDisplayName(replymessage->getFromAddress()); mReactionsSingletonMap.push_back(createReactionSingletonVariant(reac.mBody, 1));
else {
auto map = it->toMap();
auto count = map["count"].toInt();
++count;
map.remove("count");
map.insert("count", count);
mReactionsSingletonMap.erase(it);
mReactionsSingletonMap.push_back(map);
}
}
} }
connect(this, &ChatMessageCore::messageReactionChanged, this, &ChatMessageCore::resetReactionsSingleton);
mIsForward = chatmessage->isForward();
mIsReply = chatmessage->isReply();
if (mIsReply) {
auto replymessage = chatmessage->getReplyMessage();
if (replymessage) {
mReplyText = ToolModel::getMessageFromMessage(replymessage);
if (mIsFromChatGroup) mRepliedToName = ToolModel::getDisplayName(replymessage->getFromAddress());
}
}
mImdnStatusList = computeDeliveryStatus(chatmessage);
} }
mImdnStatusList = computeDeliveryStatus(chatmessage);
} }
ChatMessageCore::~ChatMessageCore() { ChatMessageCore::~ChatMessageCore() {
@ -204,6 +213,9 @@ void ChatMessageCore::setSelf(QSharedPointer<ChatMessageCore> me) {
mChatMessageModelConnection->makeConnectToCore(&ChatMessageCore::lDelete, [this] { mChatMessageModelConnection->makeConnectToCore(&ChatMessageCore::lDelete, [this] {
mChatMessageModelConnection->invokeToModel([this] { mChatMessageModel->deleteMessageFromChatRoom(true); }); mChatMessageModelConnection->invokeToModel([this] { mChatMessageModel->deleteMessageFromChatRoom(true); });
}); });
mChatMessageModelConnection->makeConnectToCore(&ChatMessageCore::lRetract, [this] {
mChatMessageModelConnection->invokeToModel([this] { mChatMessageModel->retractMessageFromChatRoom(); });
});
mChatMessageModelConnection->makeConnectToModel(&ChatMessageModel::messageDeleted, [this](bool deletedByUser) { mChatMessageModelConnection->makeConnectToModel(&ChatMessageModel::messageDeleted, [this](bool deletedByUser) {
mChatMessageModelConnection->invokeToCore([this, deletedByUser] { mChatMessageModelConnection->invokeToCore([this, deletedByUser] {
//: Deleted //: Deleted
@ -227,9 +239,11 @@ void ChatMessageCore::setSelf(QSharedPointer<ChatMessageCore> me) {
}); });
} }
}); });
mChatMessageModelConnection->makeConnectToModel(&ChatMessageModel::messageRead, [this]() { mChatMessageModelConnection->makeConnectToModel(
mChatMessageModelConnection->invokeToCore([this] { setIsRead(true); }); &ChatMessageModel::messageRead, [this](const std::shared_ptr<linphone::ChatMessage> &chatMessage) {
}); bool isRead = chatMessage->isRead();
mChatMessageModelConnection->invokeToCore([this, isRead] { setIsRead(isRead); });
});
mChatMessageModelConnection->makeConnectToCore(&ChatMessageCore::lSendReaction, [this](const QString &reaction) { mChatMessageModelConnection->makeConnectToCore(&ChatMessageCore::lSendReaction, [this](const QString &reaction) {
mChatMessageModelConnection->invokeToModel([this, reaction] { mChatMessageModel->sendReaction(reaction); }); mChatMessageModelConnection->invokeToModel([this, reaction] { mChatMessageModel->sendReaction(reaction); });
}); });
@ -347,6 +361,22 @@ void ChatMessageCore::setSelf(QSharedPointer<ChatMessageCore> me) {
int duration = now.secsTo(QDateTime::fromSecsSinceEpoch(expireTime)); int duration = now.secsTo(QDateTime::fromSecsSinceEpoch(expireTime));
mChatMessageModelConnection->invokeToCore([this, duration] { setEphemeralDuration(duration); }); mChatMessageModelConnection->invokeToCore([this, duration] { setEphemeralDuration(duration); });
}); });
mChatMessageModelConnection->makeConnectToModel(&ChatMessageModel::retracted,
[this](const std::shared_ptr<linphone::ChatMessage> &message) {
QString text = ToolModel::getMessageFromMessage(message);
mChatMessageModelConnection->invokeToCore([this, text] {
setText(text);
setRetracted();
});
});
mChatMessageModelConnection->makeConnectToModel(&ChatMessageModel::contentEdited,
[this](const std::shared_ptr<linphone::ChatMessage> &message) {
mChatMessageModelConnection->invokeToCore([this] {
mIsEdited = true;
emit edited();
});
});
} }
QList<ImdnStatus> ChatMessageCore::computeDeliveryStatus(const std::shared_ptr<linphone::ChatMessage> &message) { QList<ImdnStatus> ChatMessageCore::computeDeliveryStatus(const std::shared_ptr<linphone::ChatMessage> &message) {
@ -469,6 +499,22 @@ void ChatMessageCore::setIsRead(bool read) {
} }
} }
void ChatMessageCore::setRetracted() {
if (!mIsRetracted) {
mIsRetracted = true;
emit isRetractedChanged();
emit messageStateChanged();
}
}
bool ChatMessageCore::isRetracted() const {
return mIsRetracted;
}
bool ChatMessageCore::isEdited() const {
return mIsEdited;
}
QString ChatMessageCore::getOwnReaction() const { QString ChatMessageCore::getOwnReaction() const {
return mOwnReaction; return mOwnReaction;
} }

View file

@ -111,6 +111,11 @@ class ChatMessageCore : public QObject, public AbstractObject {
Q_PROPERTY(bool hasFileContent MEMBER mHasFileContent CONSTANT) Q_PROPERTY(bool hasFileContent MEMBER mHasFileContent CONSTANT)
Q_PROPERTY(bool isVoiceRecording MEMBER mIsVoiceRecording CONSTANT) Q_PROPERTY(bool isVoiceRecording MEMBER mIsVoiceRecording CONSTANT)
Q_PROPERTY(bool isCalendarInvite MEMBER mIsCalendarInvite CONSTANT) Q_PROPERTY(bool isCalendarInvite MEMBER mIsCalendarInvite CONSTANT)
Q_PROPERTY(bool isOutgoing MEMBER mIsOutgoing CONSTANT)
Q_PROPERTY(bool isRetractable MEMBER mIsRetractable CONSTANT)
Q_PROPERTY(bool isRetracted READ isRetracted NOTIFY isRetractedChanged)
Q_PROPERTY(bool isEditable MEMBER mIsEditable CONSTANT)
Q_PROPERTY(bool isEdited READ isEdited NOTIFY edited)
public: public:
static QSharedPointer<ChatMessageCore> create(const std::shared_ptr<linphone::ChatMessage> &chatmessage); static QSharedPointer<ChatMessageCore> create(const std::shared_ptr<linphone::ChatMessage> &chatmessage);
@ -143,6 +148,10 @@ public:
bool isRead() const; bool isRead() const;
void setIsRead(bool read); void setIsRead(bool read);
bool isRetracted() const;
void setRetracted();
bool isEdited() const;
QString getOwnReaction() const; QString getOwnReaction() const;
void setOwnReaction(const QString &reaction); void setOwnReaction(const QString &reaction);
QString getTotalReactionsLabel() const; QString getTotalReactionsLabel() const;
@ -176,9 +185,12 @@ signals:
void messageReactionChanged(); void messageReactionChanged();
void singletonReactionMapChanged(); void singletonReactionMapChanged();
void ephemeralDurationChanged(int duration); void ephemeralDurationChanged(int duration);
void isRetractedChanged();
void edited();
void lDelete(); void lDelete();
void deleted(); void deleted();
void lRetract();
void lMarkAsRead(); void lMarkAsRead();
void readChanged(); void readChanged();
void lSendReaction(const QString &reaction); void lSendReaction(const QString &reaction);
@ -214,6 +226,10 @@ private:
bool mIsVoiceRecording = false; bool mIsVoiceRecording = false;
bool mIsEphemeral = false; bool mIsEphemeral = false;
int mEphemeralDuration = 0; int mEphemeralDuration = 0;
bool mIsRetractable = false;
bool mIsRetracted = false;
bool mIsEditable = false;
bool mIsEdited = false;
bool mIsOutgoing = false; bool mIsOutgoing = false;
QString mTotalReactionsLabel; QString mTotalReactionsLabel;

View file

@ -26,14 +26,16 @@
DEFINE_ABSTRACT_OBJECT(EventLogCore) DEFINE_ABSTRACT_OBJECT(EventLogCore)
QSharedPointer<EventLogCore> EventLogCore::create(const std::shared_ptr<const linphone::EventLog> &eventLog) { QSharedPointer<EventLogCore> EventLogCore::create(const std::shared_ptr<const linphone::EventLog> &eventLog,
auto sharedPointer = QSharedPointer<EventLogCore>(new EventLogCore(eventLog), &QObject::deleteLater); const std::shared_ptr<linphone::ChatRoom> &chatRoom) {
auto sharedPointer = QSharedPointer<EventLogCore>(new EventLogCore(eventLog, chatRoom), &QObject::deleteLater);
sharedPointer->setSelf(sharedPointer); sharedPointer->setSelf(sharedPointer);
sharedPointer->moveToThread(App::getInstance()->thread()); sharedPointer->moveToThread(App::getInstance()->thread());
return sharedPointer; return sharedPointer;
} }
EventLogCore::EventLogCore(const std::shared_ptr<const linphone::EventLog> &eventLog) { EventLogCore::EventLogCore(const std::shared_ptr<const linphone::EventLog> &eventLog,
const std::shared_ptr<linphone::ChatRoom> &chatRoom) {
mustBeInLinphoneThread(getClassName()); mustBeInLinphoneThread(getClassName());
App::getInstance()->mEngine->setObjectOwnership(this, QQmlEngine::CppOwnership); App::getInstance()->mEngine->setObjectOwnership(this, QQmlEngine::CppOwnership);
mEventLogType = LinphoneEnums::fromLinphone(eventLog->getType()); mEventLogType = LinphoneEnums::fromLinphone(eventLog->getType());
@ -52,7 +54,7 @@ EventLogCore::EventLogCore(const std::shared_ptr<const linphone::EventLog> &even
QString type = QString::fromLatin1( QString type = QString::fromLatin1(
QMetaEnum::fromType<LinphoneEnums::EventLogType>().valueToKey(static_cast<int>(mEventLogType))); QMetaEnum::fromType<LinphoneEnums::EventLogType>().valueToKey(static_cast<int>(mEventLogType)));
mEventId = type + QString::number(static_cast<qint64>(eventLog->getCreationTime())); mEventId = type + QString::number(static_cast<qint64>(eventLog->getCreationTime()));
computeEvent(eventLog); computeEvent(eventLog, chatRoom);
} }
} }
@ -94,7 +96,8 @@ std::shared_ptr<EventLogModel> EventLogCore::getModel() const {
// Events (other than ChatMessage and CallLog which are handled in their respective Core) // Events (other than ChatMessage and CallLog which are handled in their respective Core)
void EventLogCore::computeEvent(const std::shared_ptr<const linphone::EventLog> &eventLog) { void EventLogCore::computeEvent(const std::shared_ptr<const linphone::EventLog> &eventLog,
const std::shared_ptr<linphone::ChatRoom> &chatRoom) {
mustBeInLinphoneThread(getClassName()); mustBeInLinphoneThread(getClassName());
mHandled = true; mHandled = true;
mImportant = false; mImportant = false;
@ -104,9 +107,15 @@ void EventLogCore::computeEvent(const std::shared_ptr<const linphone::EventLog>
switch (eventLog->getType()) { switch (eventLog->getType()) {
case linphone::EventLog::Type::ConferenceCreated: case linphone::EventLog::Type::ConferenceCreated:
if (chatRoom->hasCapability((int)linphone::ChatRoom::Capabilities::OneToOne) ||
!chatRoom->hasCapability((int)linphone::ChatRoom::Capabilities::Conference))
mHandled = false;
mEventDetails = tr("conference_created_event"); mEventDetails = tr("conference_created_event");
break; break;
case linphone::EventLog::Type::ConferenceTerminated: case linphone::EventLog::Type::ConferenceTerminated:
if (chatRoom->hasCapability((int)linphone::ChatRoom::Capabilities::OneToOne) ||
!chatRoom->hasCapability((int)linphone::ChatRoom::Capabilities::Conference))
mHandled = false;
mEventDetails = tr("conference_created_terminated"); mEventDetails = tr("conference_created_terminated");
mImportant = true; mImportant = true;
break; break;

View file

@ -51,8 +51,10 @@ class EventLogCore : public QObject, public AbstractObject {
Q_PROPERTY(QDateTime timestamp READ getTimestamp CONSTANT) Q_PROPERTY(QDateTime timestamp READ getTimestamp CONSTANT)
public: public:
static QSharedPointer<EventLogCore> create(const std::shared_ptr<const linphone::EventLog> &eventLog); static QSharedPointer<EventLogCore> create(const std::shared_ptr<const linphone::EventLog> &eventLog,
EventLogCore(const std::shared_ptr<const linphone::EventLog> &eventLog); const std::shared_ptr<linphone::ChatRoom> &chatRoom);
EventLogCore(const std::shared_ptr<const linphone::EventLog> &eventLog,
const std::shared_ptr<linphone::ChatRoom> &chatRoom);
~EventLogCore(); ~EventLogCore();
void setSelf(QSharedPointer<EventLogCore> me); void setSelf(QSharedPointer<EventLogCore> me);
QString getEventLogId(); QString getEventLogId();
@ -87,7 +89,8 @@ private:
ChatMessageCore *getChatMessageCorePointer(); ChatMessageCore *getChatMessageCorePointer();
CallHistoryCore *getCallHistoryCorePointer(); CallHistoryCore *getCallHistoryCorePointer();
std::shared_ptr<EventLogModel> mEventLogModel; std::shared_ptr<EventLogModel> mEventLogModel;
void computeEvent(const std::shared_ptr<const linphone::EventLog> &eventLog); void computeEvent(const std::shared_ptr<const linphone::EventLog> &eventLog,
const std::shared_ptr<linphone::ChatRoom> &chatRoom);
}; };
#endif // EventLogCore_H_ #endif // EventLogCore_H_

View file

@ -64,7 +64,8 @@ void EventLogList::disconnectItem(const QSharedPointer<EventLogCore> &item) {
if (message) { if (message) {
disconnect(message.get(), &ChatMessageCore::isReadChanged, this, nullptr); disconnect(message.get(), &ChatMessageCore::isReadChanged, this, nullptr);
disconnect(message.get(), &ChatMessageCore::deleted, this, nullptr); disconnect(message.get(), &ChatMessageCore::deleted, this, nullptr);
disconnect(message.get(), &ChatMessageCore::ephemeralDurationChanged, this, nullptr); disconnect(message.get(), &ChatMessageCore::edited, this, nullptr);
disconnect(message.get(), &ChatMessageCore::isRetractedChanged, this, nullptr);
} }
} }
@ -78,49 +79,55 @@ void EventLogList::connectItem(const QSharedPointer<EventLogCore> &item) {
if (mChatCore) emit mChatCore->lUpdateLastMessage(); if (mChatCore) emit mChatCore->lUpdateLastMessage();
remove(item); remove(item);
}); });
connect(message.get(), &ChatMessageCore::isRetractedChanged, this, [this, item] {
if (mChatCore) emit mChatCore->lUpdateUnreadCount();
});
connect(message.get(), &ChatMessageCore::edited, this, [this, item] {
auto eventLogModel = item->getModel();
mCoreModelConnection->invokeToModel([this, eventLogModel, item]() {
auto chatRoom = mChatCore->getModel()->getMonitor();
auto newEventLog = EventLogCore::create(eventLogModel->getEventLog(), chatRoom);
bool wasLastMessage =
mChatCore->getModel()->getLastChatMessage() == eventLogModel->getEventLog()->getChatMessage();
mCoreModelConnection->invokeToCore([this, newEventLog, wasLastMessage, item] {
connectItem(newEventLog);
replace(item, newEventLog);
if (wasLastMessage) mChatCore->setLastMessage(newEventLog->getChatMessageCore());
});
});
});
} }
} }
void EventLogList::setChatCore(QSharedPointer<ChatCore> core) { void EventLogList::setChatCore(QSharedPointer<ChatCore> core) {
auto updateChatCore = [this](QSharedPointer<ChatCore> core) { if (mChatCore != core) {
if (mChatCore != core) { if (mChatCore) {
if (mChatCore) { disconnect(mChatCore.get(), &ChatCore::eventsInserted, this, nullptr);
disconnect(mChatCore.get(), &ChatCore::eventsInserted, this, nullptr); disconnect(mChatCore.get(), &ChatCore::eventListCleared, this, nullptr);
disconnect(mChatCore.get(), &ChatCore::eventListCleared, this, nullptr);
}
mChatCore = core;
if (mChatCore) {
connect(mChatCore.get(), &ChatCore::eventListCleared, this, [this] { resetData(); });
connect(mChatCore.get(), &ChatCore::eventsInserted, this,
[this](QList<QSharedPointer<EventLogCore>> list) {
auto eventsList = getSharedList<EventLogCore>();
for (auto &event : list) {
auto it = std::find_if(
eventsList.begin(), eventsList.end(),
[event](const QSharedPointer<EventLogCore> item) { return item == event; });
if (it == eventsList.end()) {
connectItem(event);
add(event);
int index;
get(event.get(), &index);
emit eventInserted(index, new EventLogGui(event));
}
}
});
}
lUpdate();
emit chatGuiChanged();
} }
}; mChatCore = core;
if (mIsUpdating) { if (mChatCore) {
connect(this, &EventLogList::isUpdatingChanged, this, [this, core, updateChatCore] { connect(mChatCore.get(), &ChatCore::eventListCleared, this, [this] { resetData(); });
if (!mIsUpdating) { connect(mChatCore.get(), &ChatCore::eventsInserted, this, [this](QList<QSharedPointer<EventLogCore>> list) {
updateChatCore(core); auto eventsList = getSharedList<EventLogCore>();
disconnect(this, &EventLogList::isUpdatingChanged, this, nullptr); for (const auto &event : list) {
} auto it = std::find_if(eventsList.begin(), eventsList.end(),
}); [event](const QSharedPointer<EventLogCore> item) { return item == event; });
} else { if (it == eventsList.end()) {
updateChatCore(core); connectItem(event);
prepend(event);
int index;
get(event.get(), &index);
if (event->getChatMessageCore() && !event->getChatMessageCore()->isRemoteMessage()) {
emit eventInsertedByUser(index);
}
}
}
});
}
lUpdate();
// setIsUpdating(false);
emit chatGuiChanged();
} }
} }
@ -136,6 +143,13 @@ void EventLogList::setDisplayItemsStep(int displayItemsStep) {
} }
} }
void EventLogList::markIndexAsRead(int index) {
if (index < mList.count()) {
auto eventLog = mList[index].objectCast<EventLogCore>();
if (eventLog && eventLog->getChatMessageCore()) eventLog->getChatMessageCore()->lMarkAsRead();
}
}
void EventLogList::displayMore() { void EventLogList::displayMore() {
auto loadMoreItems = [this] { auto loadMoreItems = [this] {
if (!mChatCore) return; if (!mChatCore) return;
@ -152,14 +166,17 @@ void EventLogList::displayMore() {
auto linphoneLogs = chatModel->getHistoryRange(totalItemsCount, newCount); auto linphoneLogs = chatModel->getHistoryRange(totalItemsCount, newCount);
QList<QSharedPointer<EventLogCore>> *events = new QList<QSharedPointer<EventLogCore>>(); QList<QSharedPointer<EventLogCore>> *events = new QList<QSharedPointer<EventLogCore>>();
for (auto it : linphoneLogs) { for (auto it : linphoneLogs) {
auto model = EventLogCore::create(it); auto model = EventLogCore::create(it, chatModel->getMonitor());
events->push_back(model); if (it->getChatMessage() || model->isHandled()) events->push_front(model);
} }
mCoreModelConnection->invokeToCore([this, events] { mCoreModelConnection->invokeToCore([this, events] {
int currentCount = mList.count(); int currentCount = mList.count();
for (auto it = events->end() - 1; it >= events->begin(); --it) { if (!events->isEmpty()) {
connectItem(*it); for (int i = events->size() - 1; i >= 0; --i) {
prepend(*it); const auto &ev = events->at(i);
connectItem(ev);
}
add(*events);
} }
}); });
}); });
@ -177,8 +194,10 @@ void EventLogList::displayMore() {
void EventLogList::loadMessagesUpTo(std::shared_ptr<linphone::EventLog> event) { void EventLogList::loadMessagesUpTo(std::shared_ptr<linphone::EventLog> event) {
mustBeInLinphoneThread(log().arg(Q_FUNC_INFO)); mustBeInLinphoneThread(log().arg(Q_FUNC_INFO));
auto oldestEventLoaded = getAt<EventLogCore>(0); auto oldestEventLoaded = mList.count() > 0 ? getAt<EventLogCore>(mList.count() - 1) : nullptr;
auto linOldest = std::const_pointer_cast<linphone::EventLog>(oldestEventLoaded->getModel()->getEventLog()); auto linOldest = oldestEventLoaded
? std::const_pointer_cast<linphone::EventLog>(oldestEventLoaded->getModel()->getEventLog())
: nullptr;
auto chatModel = mChatCore->getModel(); auto chatModel = mChatCore->getModel();
assert(chatModel); assert(chatModel);
if (!chatModel) return; if (!chatModel) return;
@ -187,47 +206,60 @@ void EventLogList::loadMessagesUpTo(std::shared_ptr<linphone::EventLog> event) {
auto beforeEvents = chatModel->getHistoryRangeNear(mItemsToLoadBeforeSearchResult, 0, event, filters); auto beforeEvents = chatModel->getHistoryRangeNear(mItemsToLoadBeforeSearchResult, 0, event, filters);
auto linphoneLogs = chatModel->getHistoryRangeBetween(event, linOldest, filters); auto linphoneLogs = chatModel->getHistoryRangeBetween(event, linOldest, filters);
QList<QSharedPointer<EventLogCore>> *events = new QList<QSharedPointer<EventLogCore>>(); QList<QSharedPointer<EventLogCore>> *events = new QList<QSharedPointer<EventLogCore>>();
for (auto it : beforeEvents) { const auto &linChatRoom = chatModel->getMonitor();
auto model = EventLogCore::create(it); for (const auto &it : beforeEvents) {
events->push_back(model); auto model = EventLogCore::create(it, linChatRoom);
if (it->getChatMessage() || model->isHandled()) events->push_front(model);
} }
for (auto it : linphoneLogs) { for (const auto &it : linphoneLogs) {
auto model = EventLogCore::create(it); auto model = EventLogCore::create(it, linChatRoom);
events->push_back(model); if (it->getChatMessage() || model->isHandled()) events->push_front(model);
} }
mCoreModelConnection->invokeToCore([this, events, event] { mCoreModelConnection->invokeToCore([this, events, event] {
for (auto &e : *events) { for (const auto &e : *events) {
connectItem(e); connectItem(e);
add(e);
} }
add(*events);
emit messagesLoadedUpTo(event); emit messagesLoadedUpTo(event);
}); });
} }
int EventLogList::findFirstUnreadIndex() { int EventLogList::findFirstUnreadIndex() {
auto eventList = getSharedList<EventLogCore>(); auto eventList = getSharedList<EventLogCore>();
auto it = std::find_if(eventList.begin(), eventList.end(), [](const QSharedPointer<EventLogCore> item) { auto it = std::find_if(eventList.rbegin(), eventList.rend(), [](const QSharedPointer<EventLogCore> item) {
return item->getChatMessageCore() && !item->getChatMessageCore()->isRead(); auto chatmessage = item->getChatMessageCore();
return chatmessage && !chatmessage->isRead();
}); });
return it == eventList.end() ? -1 : std::distance(eventList.begin(), it); return it == eventList.rend() ? -1 : std::distance(it, eventList.rend()) - 1;
} }
void EventLogList::findChatMessageWithFilter(QString filter, void EventLogList::findChatMessageWithFilter(QString filter, int startIndex, bool forward, bool isFirstResearch) {
QSharedPointer<EventLogCore> startEvent,
bool forward,
bool isFirstResearch) {
if (mChatCore) { if (mChatCore) {
if (isFirstResearch) mLastFoundResult.reset(); if (isFirstResearch) mLastFoundResult.reset();
auto chatModel = mChatCore->getModel(); auto chatModel = mChatCore->getModel();
auto startEvent =
startIndex >= 0 && startIndex < mList.count() ? mList[startIndex].objectCast<EventLogCore>() : nullptr;
lInfo() << log().arg("searching event starting from index") << startIndex << "| event :"
<< (startEvent && startEvent->getChatMessageCore() ? startEvent->getChatMessageCore()->getText()
: "null")
<< "| filter :" << filter << "forward =" << forward;
auto startEventModel = startEvent ? startEvent->getModel() : nullptr; auto startEventModel = startEvent ? startEvent->getModel() : nullptr;
mCoreModelConnection->invokeToModel([this, chatModel, startEventModel, filter, forward, isFirstResearch] { mCoreModelConnection->invokeToModel([this, chatModel, startEventModel, filter, forward, isFirstResearch] {
auto linStartEvent = startEventModel ? startEventModel->getEventLog() : nullptr; auto linStartEvent = startEventModel ? startEventModel->getEventLog() : nullptr;
auto eventLog = chatModel->searchMessageByText(filter, linStartEvent, forward); auto eventLog = chatModel->searchMessageByText(filter, linStartEvent, forward);
if (!eventLog) if (!eventLog && isFirstResearch) {
// event not found, search backward
lInfo() << log().arg("not found, search backward");
eventLog = chatModel->searchMessageByText(filter, linStartEvent, !forward);
}
if (!eventLog && isFirstResearch) {
// event not found, search in the entire history // event not found, search in the entire history
auto eventLog = chatModel->searchMessageByText(filter, nullptr, forward); lInfo() << log().arg("not found, search in entire history");
eventLog = chatModel->searchMessageByText(filter, nullptr, forward);
}
int index = -1; int index = -1;
if (eventLog) { if (eventLog) {
lInfo() << log().arg("event with filter found") << eventLog.get();
auto eventList = getSharedList<EventLogCore>(); auto eventList = getSharedList<EventLogCore>();
auto it = std::find_if(eventList.begin(), eventList.end(), auto it = std::find_if(eventList.begin(), eventList.end(),
[eventLog](const QSharedPointer<EventLogCore> item) { [eventLog](const QSharedPointer<EventLogCore> item) {
@ -255,6 +287,7 @@ void EventLogList::findChatMessageWithFilter(QString filter,
loadMessagesUpTo(eventLog); loadMessagesUpTo(eventLog);
} }
} else { } else {
lInfo() << log().arg("event not found at all in history");
mCoreModelConnection->invokeToCore([this, index] { emit messageWithFilterFound(index); }); mCoreModelConnection->invokeToCore([this, index] { emit messageWithFilterFound(index); });
} }
}); });
@ -277,16 +310,21 @@ void EventLogList::setSelf(QSharedPointer<EventLogList> me) {
} }
setIsUpdating(true); setIsUpdating(true);
beginResetModel(); beginResetModel();
for (auto &event : getSharedList<EventLogCore>()) {
disconnectItem(event);
}
mList.clear(); mList.clear();
if (!mChatCore) { if (!mChatCore) {
endResetModel(); endResetModel();
setIsUpdating(false); setIsUpdating(false);
emit modelUpdated();
return; return;
} }
auto chatModel = mChatCore->getModel(); auto chatModel = mChatCore->getModel();
if (!chatModel) { if (!chatModel) {
endResetModel(); endResetModel();
setIsUpdating(false); setIsUpdating(false);
emit modelUpdated();
return; return;
} }
mCoreModelConnection->invokeToModel([this, chatModel]() { mCoreModelConnection->invokeToModel([this, chatModel]() {
@ -294,23 +332,17 @@ void EventLogList::setSelf(QSharedPointer<EventLogList> me) {
auto linphoneLogs = chatModel->getHistoryRange(0, mDisplayItemsStep); auto linphoneLogs = chatModel->getHistoryRange(0, mDisplayItemsStep);
QList<QSharedPointer<EventLogCore>> *events = new QList<QSharedPointer<EventLogCore>>(); QList<QSharedPointer<EventLogCore>> *events = new QList<QSharedPointer<EventLogCore>>();
for (auto it : linphoneLogs) { for (auto it : linphoneLogs) {
auto model = EventLogCore::create(it); auto model = EventLogCore::create(it, chatModel->getMonitor());
events->push_back(model); if (it->getChatMessage() || model->isHandled()) events->push_front(model);
} }
mCoreModelConnection->invokeToCore([this, events] { mCoreModelConnection->invokeToCore([this, events] {
for (auto &event : getSharedList<EventLogCore>()) {
auto message = event->getChatMessageCore();
if (message) {
disconnect(message.get(), &ChatMessageCore::ephemeralDurationChanged, this, nullptr);
disconnect(message.get(), &ChatMessageCore::deleted, this, nullptr);
}
}
for (auto &event : *events) { for (auto &event : *events) {
connectItem(event); connectItem(event);
mList.append(event); mList.append(event);
} }
endResetModel(); endResetModel();
setIsUpdating(false); setIsUpdating(false);
emit modelUpdated();
}); });
}); });
}); });

View file

@ -54,13 +54,12 @@ public:
int findFirstUnreadIndex(); int findFirstUnreadIndex();
void markIndexAsRead(int index);
void displayMore(); void displayMore();
void setDisplayItemsStep(int displayItemsStep); void setDisplayItemsStep(int displayItemsStep);
void findChatMessageWithFilter(QString filter, void findChatMessageWithFilter(QString filter, int startIndex, bool forward = true, bool isFirstResearch = true);
QSharedPointer<EventLogCore> startEvent,
bool forward = true,
bool isFirstResearch = true);
void setSelf(QSharedPointer<EventLogList> me); void setSelf(QSharedPointer<EventLogList> me);
virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
@ -68,8 +67,9 @@ public:
signals: signals:
void lUpdate(); void lUpdate();
void modelUpdated();
void filterChanged(QString filter); void filterChanged(QString filter);
void eventInserted(int index, EventLogGui *message); void eventInsertedByUser(int index);
void messageWithFilterFound(int index); void messageWithFilterFound(int index);
void listAboutToBeReset(); void listAboutToBeReset();
void chatGuiChanged(); void chatGuiChanged();
@ -79,7 +79,6 @@ signals:
private: private:
QString mFilter; QString mFilter;
QSharedPointer<ChatCore> mChatCore; QSharedPointer<ChatCore> mChatCore;
QSharedPointer<SafeConnection<ChatCore, ChatModel>> mChatModelConnection;
QSharedPointer<SafeConnection<EventLogList, CoreModel>> mCoreModelConnection; QSharedPointer<SafeConnection<EventLogList, CoreModel>> mCoreModelConnection;
int mDisplayItemsStep = 0; int mDisplayItemsStep = 0;
int mItemsToLoadBeforeSearchResult = 3; int mItemsToLoadBeforeSearchResult = 3;

View file

@ -26,7 +26,7 @@
DEFINE_ABSTRACT_OBJECT(EventLogProxy) DEFINE_ABSTRACT_OBJECT(EventLogProxy)
EventLogProxy::EventLogProxy(QObject *parent) : LimitProxy(parent) { EventLogProxy::EventLogProxy(QObject *parent) : QSortFilterProxyModel(parent) {
mList = EventLogList::create(); mList = EventLogList::create();
setSourceModel(mList.get()); setSourceModel(mList.get());
} }
@ -35,54 +35,42 @@ EventLogProxy::~EventLogProxy() {
} }
void EventLogProxy::setSourceModel(QAbstractItemModel *model) { void EventLogProxy::setSourceModel(QAbstractItemModel *model) {
auto oldEventLogList = getListModel<EventLogList>(); auto oldEventLogList = dynamic_cast<EventLogList *>(sourceModel());
if (oldEventLogList) { if (oldEventLogList) {
disconnect(oldEventLogList); disconnect(oldEventLogList, &EventLogList::displayItemsStepChanged, this, nullptr);
disconnect(oldEventLogList, &EventLogList::messageWithFilterFound, this, nullptr);
disconnect(oldEventLogList, &EventLogList::eventInsertedByUser, this, nullptr);
} }
auto newEventLogList = dynamic_cast<EventLogList *>(model); auto newEventLogList = dynamic_cast<EventLogList *>(model);
if (newEventLogList) { if (newEventLogList) {
connect(newEventLogList, &EventLogList::listAboutToBeReset, this, &EventLogProxy::listAboutToBeReset);
connect(newEventLogList, &EventLogList::chatGuiChanged, this, &EventLogProxy::chatGuiChanged);
connect(this, &EventLogProxy::displayItemsStepChanged, newEventLogList, connect(this, &EventLogProxy::displayItemsStepChanged, newEventLogList,
[this, newEventLogList] { newEventLogList->setDisplayItemsStep(mDisplayItemsStep); }); [this, newEventLogList] { newEventLogList->setDisplayItemsStep(mDisplayItemsStep); });
connect(newEventLogList, &EventLogList::eventInserted, this,
[this, newEventLogList](int index, EventLogGui *event) {
invalidate();
int proxyIndex = -1;
if (index != -1) {
proxyIndex = dynamic_cast<SortFilterList *>(sourceModel())
->mapFromSource(newEventLogList->index(index, 0))
.row();
}
loadUntil(proxyIndex);
emit eventInserted(proxyIndex, event);
});
connect(newEventLogList, &EventLogList::messageWithFilterFound, this, [this, newEventLogList](int i) { connect(newEventLogList, &EventLogList::messageWithFilterFound, this, [this, newEventLogList](int i) {
connect(this, &EventLogProxy::layoutChanged, newEventLogList, [this, i, newEventLogList] { auto model = dynamic_cast<EventLogList *>(sourceModel());
disconnect(this, &EventLogProxy::layoutChanged, newEventLogList, nullptr); int proxyIndex = mapFromSource(newEventLogList->index(i, 0)).row();
auto model = getListModel<EventLogList>(); if (i != -1) {
int proxyIndex = loadUntil(proxyIndex);
dynamic_cast<SortFilterList *>(sourceModel())->mapFromSource(newEventLogList->index(i, 0)).row(); }
if (i != -1) { emit indexWithFilterFound(proxyIndex);
loadUntil(proxyIndex);
}
emit indexWithFilterFound(proxyIndex);
});
invalidate();
}); });
connect(newEventLogList, &EventLogList::eventInsertedByUser, this, [this, newEventLogList](int i) {
int proxyIndex = mapFromSource(newEventLogList->index(i, 0)).row();
emit eventInsertedByUser(proxyIndex);
});
connect(newEventLogList, &EventLogList::modelUpdated, this, &EventLogProxy::modelUpdated);
} }
setSourceModels(new SortFilterList(model, Qt::DescendingOrder)); QSortFilterProxyModel::setSourceModel(model);
sort(0, Qt::DescendingOrder);
} }
ChatGui *EventLogProxy::getChatGui() { ChatGui *EventLogProxy::getChatGui() {
auto model = getListModel<EventLogList>(); auto model = dynamic_cast<EventLogList *>(sourceModel());
if (!mChatGui && model) mChatGui = model->getChat(); if (!mChatGui && model) mChatGui = model->getChat();
return mChatGui; return mChatGui;
} }
void EventLogProxy::setChatGui(ChatGui *chat) { void EventLogProxy::setChatGui(ChatGui *chat) {
getListModel<EventLogList>()->setChatGui(chat); auto model = dynamic_cast<EventLogList *>(sourceModel());
if (model) model->setChatGui(chat);
} }
EventLogGui *EventLogProxy::getEventAtIndex(int i) { EventLogGui *EventLogProxy::getEventAtIndex(int i) {
@ -90,30 +78,87 @@ EventLogGui *EventLogProxy::getEventAtIndex(int i) {
return eventCore == nullptr ? nullptr : new EventLogGui(eventCore); return eventCore == nullptr ? nullptr : new EventLogGui(eventCore);
} }
int EventLogProxy::getCount() const {
return rowCount();
}
int EventLogProxy::getInitialDisplayItems() const {
return mInitialDisplayItems;
}
void EventLogProxy::setInitialDisplayItems(int initialItems) {
if (mInitialDisplayItems != initialItems) {
mInitialDisplayItems = initialItems;
if (getMaxDisplayItems() <= mInitialDisplayItems) setMaxDisplayItems(initialItems);
if (getDisplayItemsStep() <= 0) setDisplayItemsStep(initialItems);
emit initialDisplayItemsChanged();
}
}
int EventLogProxy::getDisplayCount(int listCount, int maxCount) {
return maxCount >= 0 ? qMin(listCount, maxCount) : listCount;
}
int EventLogProxy::getDisplayCount(int listCount) const {
return getDisplayCount(listCount, mMaxDisplayItems);
}
QSharedPointer<EventLogCore> EventLogProxy::getEventCoreAtIndex(int i) { QSharedPointer<EventLogCore> EventLogProxy::getEventCoreAtIndex(int i) {
return getItemAt<SortFilterList, EventLogList, EventLogCore>(i); auto model = dynamic_cast<EventLogList *>(sourceModel());
if (model) {
return model->getAt<EventLogCore>(mapToSource(index(i, 0)).row());
}
return nullptr;
} }
void EventLogProxy::displayMore() { void EventLogProxy::displayMore() {
auto model = getListModel<EventLogList>(); auto model = dynamic_cast<EventLogList *>(sourceModel());
if (model) { if (model) {
model->displayMore(); model->displayMore();
} }
} }
int EventLogProxy::getMaxDisplayItems() const {
return mMaxDisplayItems;
}
void EventLogProxy::setMaxDisplayItems(int maxItems) {
if (mMaxDisplayItems != maxItems) {
auto model = sourceModel();
int modelCount = model ? model->rowCount() : 0;
int oldCount = getDisplayCount(modelCount);
mMaxDisplayItems = maxItems;
if (getInitialDisplayItems() > mMaxDisplayItems) setInitialDisplayItems(maxItems);
if (getDisplayItemsStep() <= 0) setDisplayItemsStep(maxItems);
emit maxDisplayItemsChanged();
if (model && getDisplayCount(modelCount) != oldCount) {
invalidate();
}
}
}
int EventLogProxy::getDisplayItemsStep() const {
return mDisplayItemsStep;
}
void EventLogProxy::setDisplayItemsStep(int step) {
if (step > 0 && mDisplayItemsStep != step) {
mDisplayItemsStep = step;
emit displayItemsStepChanged();
}
}
void EventLogProxy::loadUntil(int index) { void EventLogProxy::loadUntil(int index) {
auto confInfoList = getListModel<EventLogList>(); if (mMaxDisplayItems <= index) setMaxDisplayItems(index + mDisplayItemsStep);
if (mMaxDisplayItems < index) setMaxDisplayItems(index + mDisplayItemsStep);
} }
int EventLogProxy::findFirstUnreadIndex() { int EventLogProxy::findFirstUnreadIndex() {
auto eventLogList = getListModel<EventLogList>(); auto eventLogList = dynamic_cast<EventLogList *>(sourceModel());
if (eventLogList) { if (eventLogList) {
auto listIndex = eventLogList->findFirstUnreadIndex(); auto listIndex = eventLogList->findFirstUnreadIndex();
if (listIndex != -1) { if (listIndex != -1) {
listIndex = listIndex = mapFromSource(eventLogList->index(listIndex, 0)).row();
dynamic_cast<SortFilterList *>(sourceModel())->mapFromSource(eventLogList->index(listIndex, 0)).row(); loadUntil(listIndex);
if (mMaxDisplayItems <= listIndex) setMaxDisplayItems(listIndex + mDisplayItemsStep);
return listIndex; return listIndex;
} else { } else {
return 0; return 0;
@ -122,32 +167,43 @@ int EventLogProxy::findFirstUnreadIndex() {
return 0; return 0;
} }
QString EventLogProxy::getFilterText() const {
return mFilterText;
}
void EventLogProxy::setFilterText(const QString &filter) {
if (mFilterText != filter) {
#if QT_VERSION >= QT_VERSION_CHECK(6, 10, 0)
beginFilterChange();
mFilterText = filter;
endFilterChange();
#else
mFilterText = filter;
invalidateFilter();
#endif
emit filterTextChanged();
}
}
QSharedPointer<EventLogCore> EventLogProxy::getAt(int atIndex) const {
auto model = dynamic_cast<EventLogList *>(sourceModel());
if (model) {
return model->getAt<EventLogCore>(mapToSource(index(atIndex, 0)).row());
}
return nullptr;
}
void EventLogProxy::markIndexAsRead(int proxyIndex) { void EventLogProxy::markIndexAsRead(int proxyIndex) {
auto event = getItemAt<SortFilterList, EventLogList, EventLogCore>(proxyIndex); auto event = getAt(proxyIndex);
if (event && event->getChatMessageCore()) event->getChatMessageCore()->lMarkAsRead(); if (event && event->getChatMessageCore()) event->getChatMessageCore()->lMarkAsRead();
} }
void EventLogProxy::findIndexCorrespondingToFilter(int startIndex, bool forward, bool isFirstResearch) { void EventLogProxy::findIndexCorrespondingToFilter(int startIndex, bool forward, bool isFirstResearch) {
auto filter = getFilterText(); auto filter = getFilterText();
if (filter.isEmpty()) return; if (filter.isEmpty()) return;
auto eventLogList = getListModel<EventLogList>(); auto eventLogList = dynamic_cast<EventLogList *>(sourceModel());
if (eventLogList) { if (eventLogList) {
auto startEvent = mLastSearchStart; auto listIndex = mapToSource(index(startIndex, 0)).row();
if (!startEvent) { eventLogList->findChatMessageWithFilter(filter, listIndex, forward, isFirstResearch);
startEvent = getItemAt<SortFilterList, EventLogList, EventLogCore>(startIndex);
}
eventLogList->findChatMessageWithFilter(filter, startEvent, forward, isFirstResearch);
} }
} }
bool EventLogProxy::SortFilterList::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const {
auto l = getItemAtSource<EventLogList, EventLogCore>(sourceRow);
return l != nullptr;
}
bool EventLogProxy::SortFilterList::lessThan(const QModelIndex &sourceLeft, const QModelIndex &sourceRight) const {
auto l = getItemAtSource<EventLogList, EventLogCore>(sourceLeft.row());
auto r = getItemAtSource<EventLogList, EventLogCore>(sourceRight.row());
if (l && r) return l->getTimestamp() <= r->getTimestamp();
return true;
}

View file

@ -22,19 +22,26 @@
#define EVENT_LIST_PROXY_H_ #define EVENT_LIST_PROXY_H_
#include "EventLogList.hpp" #include "EventLogList.hpp"
#include "core/proxy/LimitProxy.hpp" // #include "core/proxy/LimitProxy.hpp"
#include "tool/AbstractObject.hpp" #include "tool/AbstractObject.hpp"
#include <QSortFilterProxyModel>
// ============================================================================= // =============================================================================
class ChatGui; class ChatGui;
class EventLogProxy : public LimitProxy, public AbstractObject { class EventLogProxy : public QSortFilterProxyModel, public AbstractObject {
Q_OBJECT Q_OBJECT
Q_PROPERTY(int count READ getCount NOTIFY countChanged)
Q_PROPERTY(ChatGui *chatGui READ getChatGui WRITE setChatGui NOTIFY chatGuiChanged) Q_PROPERTY(ChatGui *chatGui READ getChatGui WRITE setChatGui NOTIFY chatGuiChanged)
Q_PROPERTY(int initialDisplayItems READ getInitialDisplayItems WRITE setInitialDisplayItems NOTIFY
initialDisplayItemsChanged)
Q_PROPERTY(int maxDisplayItems READ getMaxDisplayItems WRITE setMaxDisplayItems NOTIFY maxDisplayItemsChanged)
Q_PROPERTY(int displayItemsStep READ getDisplayItemsStep WRITE setDisplayItemsStep NOTIFY displayItemsStepChanged)
Q_PROPERTY(QString filterText READ getFilterText WRITE setFilterText NOTIFY filterTextChanged)
public: public:
DECLARE_SORTFILTER_CLASS() // DECLARE_SORTFILTER_CLASS()
EventLogProxy(QObject *parent = Q_NULLPTR); EventLogProxy(QObject *parent = Q_NULLPTR);
~EventLogProxy(); ~EventLogProxy();
@ -43,8 +50,27 @@ public:
void setChatGui(ChatGui *chat); void setChatGui(ChatGui *chat);
void setSourceModel(QAbstractItemModel *sourceModel) override; void setSourceModel(QAbstractItemModel *sourceModel) override;
virtual int getCount() const;
static int getDisplayCount(int listCount, int maxCount);
int getDisplayCount(int listCount) const;
int getInitialDisplayItems() const;
void setInitialDisplayItems(int initialItems);
Q_INVOKABLE void displayMore() override; int getMaxDisplayItems() const;
void setMaxDisplayItems(int maxItems);
int getDisplayItemsStep() const;
void setDisplayItemsStep(int step);
QString getFilterText() const;
void setFilterText(const QString &filter);
// bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override;
// bool lessThan(const QModelIndex &sourceLeft, const QModelIndex &sourceRight) const override;
QSharedPointer<EventLogCore> getAt(int atIndex) const;
Q_INVOKABLE void displayMore();
Q_INVOKABLE void loadUntil(int index); Q_INVOKABLE void loadUntil(int index);
Q_INVOKABLE EventLogGui *getEventAtIndex(int i); Q_INVOKABLE EventLogGui *getEventAtIndex(int i);
QSharedPointer<EventLogCore> getEventCoreAtIndex(int i); QSharedPointer<EventLogCore> getEventCoreAtIndex(int i);
@ -53,15 +79,24 @@ public:
Q_INVOKABLE void findIndexCorrespondingToFilter(int startIndex, bool forward = true, bool isFirstResearch = true); Q_INVOKABLE void findIndexCorrespondingToFilter(int startIndex, bool forward = true, bool isFirstResearch = true);
signals: signals:
void eventInserted(int index, EventLogGui *message); void eventInsertedByUser(int index);
void indexWithFilterFound(int index); void indexWithFilterFound(int index);
void listAboutToBeReset();
void chatGuiChanged(); void chatGuiChanged();
void countChanged();
void initialDisplayItemsChanged();
void maxDisplayItemsChanged();
void displayItemsStepChanged();
void filterTextChanged();
void modelUpdated();
protected: protected:
QSharedPointer<EventLogList> mList; QSharedPointer<EventLogList> mList;
QSharedPointer<EventLogCore> mLastSearchStart; QSharedPointer<EventLogCore> mLastSearchStart;
ChatGui *mChatGui = nullptr; ChatGui *mChatGui = nullptr;
int mInitialDisplayItems = -1;
int mMaxDisplayItems = -1;
int mDisplayItemsStep = 5;
QString mFilterText;
DECLARE_ABSTRACT_OBJECT DECLARE_ABSTRACT_OBJECT
}; };

View file

@ -46,7 +46,7 @@ ChatMessageContentCore::ChatMessageContentCore(const std::shared_ptr<linphone::C
mName = QFileInfo(fileName).baseName(); mName = QFileInfo(fileName).baseName();
} }
} }
mFilePath = Utils::coreStringToAppString(content->getFilePath()); mFilePath = QDir::fromNativeSeparators(Utils::coreStringToAppString(content->getFilePath()));
mIsFile = content->isFile(); mIsFile = content->isFile();
mIsFileEncrypted = content->isFileEncrypted(); mIsFileEncrypted = content->isFileEncrypted();
mIsFileTransfer = content->isFileTransfer(); mIsFileTransfer = content->isFileTransfer();
@ -64,11 +64,17 @@ ChatMessageContentCore::ChatMessageContentCore(const std::shared_ptr<linphone::C
mFileOffset = 0; mFileOffset = 0;
mUtf8Text = Utils::coreStringToAppString(content->getUtf8Text()); mUtf8Text = Utils::coreStringToAppString(content->getUtf8Text());
auto chatRoom = chatMessageModel ? chatMessageModel->getMonitor()->getChatRoom() : nullptr; auto chatRoom = chatMessageModel ? chatMessageModel->getMonitor()->getChatRoom() : nullptr;
mRichFormatText = ToolModel::encodeTextToQmlRichFormat(mUtf8Text, {}, chatRoom); mRichFormatText = ToolModel::encodeTextToQmlRichFormat(mUtf8Text, mSearchedTextPart, {}, chatRoom);
connect(this, &ChatMessageContentCore::searchedTextPartChanged, this, [this] {
auto chatroom = mChatMessageContentModel->getChatMessageModel()
? mChatMessageContentModel->getChatMessageModel()->getMonitor()->getChatRoom()
: nullptr;
setRichFormatText(ToolModel::encodeTextToQmlRichFormat(mUtf8Text, mSearchedTextPart, {}, chatroom));
});
mWasDownloaded = !mFilePath.isEmpty() && QFileInfo(mFilePath).isFile(); mWasDownloaded = !mFilePath.isEmpty() && QFileInfo(mFilePath).isFile();
mThumbnail = mFilePath.isEmpty() mThumbnail = mFilePath.isEmpty()
? QString() ? QUrl()
: QStringLiteral("image://%1/%2").arg(ThumbnailProvider::ProviderId).arg(mFilePath); : QUrl(QString("image://%1/%2").arg(ThumbnailProvider::ProviderId).arg(mFilePath));
mChatMessageContentModel = Utils::makeQObject_ptr<ChatMessageContentModel>(content, chatMessageModel); mChatMessageContentModel = Utils::makeQObject_ptr<ChatMessageContentModel>(content, chatMessageModel);
} }
} }
@ -92,11 +98,22 @@ void ChatMessageContentCore::setSelf(QSharedPointer<ChatMessageContentCore> me)
}); });
mChatMessageContentModelConnection->makeConnectToModel( mChatMessageContentModelConnection->makeConnectToModel(
&ChatMessageContentModel::thumbnailChanged, [this, updateThumbnailType](QString thumbnail) { &ChatMessageContentModel::thumbnailChanged, [this, updateThumbnailType](QString thumbnail) {
mChatMessageContentModelConnection->invokeToCore([this, thumbnail] { setThumbnail(thumbnail); }); mChatMessageContentModelConnection->invokeToCore([this, thumbnail] { setThumbnail(QUrl(thumbnail)); });
}); });
mChatMessageContentModelConnection->makeConnectToCore(&ChatMessageContentCore::lDownloadFile, [this]() { mChatMessageContentModelConnection->makeConnectToCore(&ChatMessageContentCore::lDownloadFile, [this]() {
mChatMessageContentModelConnection->invokeToModel([this] { mChatMessageContentModel->downloadFile(mName); }); mChatMessageContentModelConnection->invokeToModel([this] {
QString *error = new QString();
bool downloaded = mChatMessageContentModel->downloadFile(mName, error);
if (!downloaded) {
mChatMessageContentModelConnection->invokeToCore([this, error] {
//: Error downloading file %1
if (error->isEmpty()) *error = tr("download_file_default_error").arg(mName);
Utils::showInformationPopup(tr("info_popup_error_titile"), *error, false);
delete error;
});
} else delete error;
});
}); });
mChatMessageContentModelConnection->makeConnectToModel( mChatMessageContentModelConnection->makeConnectToModel(
&ChatMessageContentModel::wasDownloadedChanged, &ChatMessageContentModel::wasDownloadedChanged,
@ -235,20 +252,20 @@ ConferenceInfoGui *ChatMessageContentCore::getConferenceInfoGui() const {
return mConferenceInfo ? new ConferenceInfoGui(mConferenceInfo) : nullptr; return mConferenceInfo ? new ConferenceInfoGui(mConferenceInfo) : nullptr;
} }
bool ChatMessageContentCore::wasDownloaded() const { QUrl ChatMessageContentCore::getThumbnail() const {
return mWasDownloaded;
}
QString ChatMessageContentCore::getThumbnail() const {
return mThumbnail; return mThumbnail;
} }
void ChatMessageContentCore::setThumbnail(const QString &data) { void ChatMessageContentCore::setThumbnail(const QUrl &data) {
if (mThumbnail != data) { if (mThumbnail != data) {
mThumbnail = data; mThumbnail = data;
emit thumbnailChanged(); emit thumbnailChanged();
} }
} }
bool ChatMessageContentCore::wasDownloaded() const {
return mWasDownloaded;
}
void ChatMessageContentCore::setWasDownloaded(bool wasDownloaded) { void ChatMessageContentCore::setWasDownloaded(bool wasDownloaded) {
if (mWasDownloaded != wasDownloaded) { if (mWasDownloaded != wasDownloaded) {
mWasDownloaded = wasDownloaded; mWasDownloaded = wasDownloaded;
@ -256,6 +273,24 @@ void ChatMessageContentCore::setWasDownloaded(bool wasDownloaded) {
} }
} }
void ChatMessageContentCore::setRichFormatText(const QString &richFormatText) {
if (mRichFormatText != richFormatText) {
mRichFormatText = richFormatText;
emit richFormatTextChanged();
}
}
void ChatMessageContentCore::setSearchedTextPart(const QString &searchedTextPart) {
if (mSearchedTextPart != searchedTextPart) {
mSearchedTextPart = searchedTextPart;
emit searchedTextPartChanged();
}
}
QString ChatMessageContentCore::getSearchedTextPart() const {
return mSearchedTextPart;
}
const std::shared_ptr<ChatMessageContentModel> &ChatMessageContentCore::getContentModel() const { const std::shared_ptr<ChatMessageContentModel> &ChatMessageContentCore::getContentModel() const {
return mChatMessageContentModel; return mChatMessageContentModel;
} }

View file

@ -36,11 +36,12 @@ class ChatMessageContentCore : public QObject, public AbstractObject {
Q_PROPERTY(QString name READ getName CONSTANT) Q_PROPERTY(QString name READ getName CONSTANT)
Q_PROPERTY(quint64 fileOffset READ getFileOffset WRITE setFileOffset NOTIFY fileOffsetChanged) Q_PROPERTY(quint64 fileOffset READ getFileOffset WRITE setFileOffset NOTIFY fileOffsetChanged)
Q_PROPERTY(QString thumbnail READ getThumbnail WRITE setThumbnail NOTIFY thumbnailChanged) Q_PROPERTY(QUrl thumbnail READ getThumbnail WRITE setThumbnail NOTIFY thumbnailChanged)
Q_PROPERTY(bool wasDownloaded READ wasDownloaded WRITE setWasDownloaded NOTIFY wasDownloadedChanged) Q_PROPERTY(bool wasDownloaded READ wasDownloaded WRITE setWasDownloaded NOTIFY wasDownloadedChanged)
Q_PROPERTY(QString filePath READ getFilePath WRITE setFilePath NOTIFY filePathChanged) Q_PROPERTY(QString filePath READ getFilePath WRITE setFilePath NOTIFY filePathChanged)
Q_PROPERTY(QString utf8Text READ getUtf8Text CONSTANT) Q_PROPERTY(QString utf8Text READ getUtf8Text CONSTANT)
Q_PROPERTY(QString richFormatText MEMBER mRichFormatText CONSTANT) Q_PROPERTY(QString richFormatText WRITE setRichFormatText MEMBER mRichFormatText NOTIFY richFormatTextChanged)
Q_PROPERTY(QString searchTextPart READ getSearchedTextPart WRITE setSearchedTextPart NOTIFY searchedTextPartChanged)
Q_PROPERTY(bool isFile READ isFile WRITE setIsFile NOTIFY isFileChanged) Q_PROPERTY(bool isFile READ isFile WRITE setIsFile NOTIFY isFileChanged)
Q_PROPERTY(bool isFileEncrypted READ isFileEncrypted WRITE setIsFileEncrypted NOTIFY isFileEncryptedChanged) Q_PROPERTY(bool isFileEncrypted READ isFileEncrypted WRITE setIsFileEncrypted NOTIFY isFileEncryptedChanged)
Q_PROPERTY(bool isFileTransfer READ isFileTransfer WRITE setIsFileTransfer NOTIFY isFileTransferChanged) Q_PROPERTY(bool isFileTransfer READ isFileTransfer WRITE setIsFileTransfer NOTIFY isFileTransferChanged)
@ -84,12 +85,16 @@ public:
int getFileDuration() const; int getFileDuration() const;
ConferenceInfoGui *getConferenceInfoGui() const; ConferenceInfoGui *getConferenceInfoGui() const;
void setThumbnail(const QString &data); void setThumbnail(const QUrl &data);
QString getThumbnail() const; QUrl getThumbnail() const;
bool wasDownloaded() const; bool wasDownloaded() const;
void setWasDownloaded(bool downloaded); void setWasDownloaded(bool downloaded);
void setRichFormatText(const QString &richFormatText);
Q_INVOKABLE void setSearchedTextPart(const QString &searchedTextPart);
QString getSearchedTextPart() const;
const std::shared_ptr<ChatMessageContentModel> &getContentModel() const; const std::shared_ptr<ChatMessageContentModel> &getContentModel() const;
signals: signals:
@ -102,6 +107,8 @@ signals:
void isFileEncryptedChanged(); void isFileEncryptedChanged();
void wasDownloadedChanged(bool downloaded); void wasDownloadedChanged(bool downloaded);
void isVideoChanged(); void isVideoChanged();
void searchedTextPartChanged();
void richFormatTextChanged();
void lCreateThumbnail(const bool &force = false); void lCreateThumbnail(const bool &force = false);
void lRemoveDownloadedFile(); void lRemoveDownloadedFile();
@ -121,9 +128,10 @@ private:
bool mIsText; bool mIsText;
bool mIsVoiceRecording; bool mIsVoiceRecording;
int mFileDuration; int mFileDuration;
QString mThumbnail; QUrl mThumbnail;
QString mUtf8Text; QString mUtf8Text;
QString mRichFormatText; QString mRichFormatText;
QString mSearchedTextPart;
QString mFilePath; QString mFilePath;
QString mName; QString mName;
quint64 mFileSize; quint64 mFileSize;

View file

@ -197,7 +197,8 @@ void ChatMessageContentList::setSelf(QSharedPointer<ChatMessageContentList> me)
mModelConnection->makeConnectToCore(&ChatMessageContentList::lUpdate, [this]() { mModelConnection->makeConnectToCore(&ChatMessageContentList::lUpdate, [this]() {
for (auto &content : getSharedList<ChatMessageContentCore>()) { for (auto &content : getSharedList<ChatMessageContentCore>()) {
if (content) disconnect(content.get()); if (content) disconnect(content.get(), &ChatMessageContentCore::wasDownloadedChanged, this, nullptr);
if (content) disconnect(content.get(), &ChatMessageContentCore::thumbnailChanged, this, nullptr);
} }
if (!mChatMessageCore) return; if (!mChatMessageCore) return;
auto contents = mChatMessageCore->getChatMessageContentList(); auto contents = mChatMessageCore->getChatMessageContentList();

View file

@ -47,6 +47,7 @@ ConferenceCore::ConferenceCore(const std::shared_ptr<linphone::Conference> &conf
mIsLocalScreenSharing = mConferenceModel->isLocalScreenSharing(); mIsLocalScreenSharing = mConferenceModel->isLocalScreenSharing();
mIsScreenSharingEnabled = mConferenceModel->isScreenSharingEnabled(); mIsScreenSharingEnabled = mConferenceModel->isScreenSharingEnabled();
mIsRecording = conference->isRecording(); mIsRecording = conference->isRecording();
if (conference->getCurrentParams()) mIsChatEnabled = conference->getCurrentParams()->chatEnabled();
auto me = conference->getMe(); auto me = conference->getMe();
auto confAddress = conference->getConferenceAddress(); auto confAddress = conference->getConferenceAddress();
if (confAddress) { if (confAddress) {
@ -205,6 +206,10 @@ void ConferenceCore::setIsScreenSharingEnabled(bool state) {
} }
} }
bool ConferenceCore::isChatEnabled() const {
return mIsChatEnabled;
}
std::shared_ptr<ConferenceModel> ConferenceCore::getModel() const { std::shared_ptr<ConferenceModel> ConferenceCore::getModel() const {
return mConferenceModel; return mConferenceModel;
} }

View file

@ -37,6 +37,7 @@ class ConferenceCore : public QObject, public AbstractObject {
Q_OBJECT Q_OBJECT
public: public:
Q_PROPERTY(QDateTime startDate READ getStartDate CONSTANT) Q_PROPERTY(QDateTime startDate READ getStartDate CONSTANT)
Q_PROPERTY(bool isChatEnabled READ isChatEnabled CONSTANT)
// Q_PROPERTY(ParticipantDeviceList *participantDevices READ getParticipantDeviceList CONSTANT) // Q_PROPERTY(ParticipantDeviceList *participantDevices READ getParticipantDeviceList CONSTANT)
// Q_PROPERTY(ParticipantModel* localParticipant READ getLocalParticipant NOTIFY localParticipantChanged) // Q_PROPERTY(ParticipantModel* localParticipant READ getLocalParticipant NOTIFY localParticipantChanged)
Q_PROPERTY(bool isReady MEMBER mIsReady WRITE setIsReady NOTIFY isReadyChanged) Q_PROPERTY(bool isReady MEMBER mIsReady WRITE setIsReady NOTIFY isReadyChanged)
@ -81,6 +82,8 @@ public:
void setIsLocalScreenSharing(bool state); void setIsLocalScreenSharing(bool state);
void setIsScreenSharingEnabled(bool state); void setIsScreenSharingEnabled(bool state);
bool isChatEnabled() const;
std::shared_ptr<ConferenceModel> getModel() const; std::shared_ptr<ConferenceModel> getModel() const;
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
@ -108,6 +111,7 @@ private:
bool mIsRecording = false; bool mIsRecording = false;
bool mIsLocalScreenSharing = false; bool mIsLocalScreenSharing = false;
bool mIsScreenSharingEnabled = false; bool mIsScreenSharingEnabled = false;
bool mIsChatEnabled = false;
QString mSubject; QString mSubject;
QString mConfUri; QString mConfUri;
QDateTime mStartDate = QDateTime::currentDateTime(); QDateTime mStartDate = QDateTime::currentDateTime();

View file

@ -21,12 +21,16 @@
#include "ConferenceInfoCore.hpp" #include "ConferenceInfoCore.hpp"
#include "core/App.hpp" #include "core/App.hpp"
#include "core/path/Paths.hpp"
#include "core/proxy/ListProxy.hpp" #include "core/proxy/ListProxy.hpp"
#include "model/object/VariantObject.hpp" #include "model/object/VariantObject.hpp"
#include "model/tool/ToolModel.hpp" #include "model/tool/ToolModel.hpp"
#include "tool/Utils.hpp" #include "tool/Utils.hpp"
#include "tool/thread/SafeConnection.hpp" #include "tool/thread/SafeConnection.hpp"
#include <QDesktopServices>
#include <QRegularExpression>
DEFINE_ABSTRACT_OBJECT(ConferenceInfoCore) DEFINE_ABSTRACT_OBJECT(ConferenceInfoCore)
QSharedPointer<ConferenceInfoCore> QSharedPointer<ConferenceInfoCore>
@ -69,6 +73,7 @@ ConferenceInfoCore::ConferenceInfoCore(std::shared_ptr<linphone::ConferenceInfo>
mUri = address && address->isValid() && !address->getDomain().empty() mUri = address && address->isValid() && !address->getDomain().empty()
? Utils::coreStringToAppString(address->asStringUriOnly()) ? Utils::coreStringToAppString(address->asStringUriOnly())
: ""; : "";
mIcalendarString = Utils::coreStringToAppString(conferenceInfo->getIcalendarString());
mDateTime = QDateTime::fromMSecsSinceEpoch(conferenceInfo->getDateTime() * 1000); mDateTime = QDateTime::fromMSecsSinceEpoch(conferenceInfo->getDateTime() * 1000);
mDuration = conferenceInfo->getDuration(); mDuration = conferenceInfo->getDuration();
mEndDateTime = mDateTime.addSecs(mDuration * 60); mEndDateTime = mDateTime.addSecs(mDuration * 60);
@ -123,6 +128,7 @@ ConferenceInfoCore::ConferenceInfoCore(const ConferenceInfoCore &conferenceInfoC
mIsEnded = conferenceInfoCore.mIsEnded; mIsEnded = conferenceInfoCore.mIsEnded;
mInviteEnabled = conferenceInfoCore.mInviteEnabled; mInviteEnabled = conferenceInfoCore.mInviteEnabled;
mConferenceInfoState = conferenceInfoCore.mConferenceInfoState; mConferenceInfoState = conferenceInfoCore.mConferenceInfoState;
mIcalendarString = conferenceInfoCore.mIcalendarString;
} }
ConferenceInfoCore::~ConferenceInfoCore() { ConferenceInfoCore::~ConferenceInfoCore() {
@ -192,15 +198,14 @@ void ConferenceInfoCore::setSelf(QSharedPointer<ConferenceInfoCore> me) {
mConfInfoModelConnection->makeConnectToModel( mConfInfoModelConnection->makeConnectToModel(
&ConferenceInfoModel::schedulerStateChanged, [this](linphone::ConferenceScheduler::State state) { &ConferenceInfoModel::schedulerStateChanged, [this](linphone::ConferenceScheduler::State state) {
auto confInfoState = mConferenceInfoModel->getState(); auto confInfoState = mConferenceInfoModel->getState();
QString uri;
if (state == linphone::ConferenceScheduler::State::Ready) { if (state == linphone::ConferenceScheduler::State::Ready) {
uri = mConferenceInfoModel->getConferenceScheduler()->getUri(); if (confInfoState == linphone::ConferenceInfo::State::New) {
emit CoreModel::getInstance()->conferenceInfoReceived( emit CoreModel::getInstance()->conferenceInfoReceived(
CoreModel::getInstance()->getCore(), mConferenceInfoModel->getConferenceInfo()); CoreModel::getInstance()->getCore(), mConferenceInfoModel->getConferenceInfo());
}
} }
mConfInfoModelConnection->invokeToCore([this, state = LinphoneEnums::fromLinphone(state), mConfInfoModelConnection->invokeToCore([this, state = LinphoneEnums::fromLinphone(state),
infoState = LinphoneEnums::fromLinphone(confInfoState), infoState = LinphoneEnums::fromLinphone(confInfoState)] {
uri] {
setConferenceSchedulerState(state); setConferenceSchedulerState(state);
setConferenceInfoState(infoState); setConferenceInfoState(infoState);
}); });
@ -600,6 +605,9 @@ void ConferenceInfoCore::save() {
} else lCritical() << "No default account"; } else lCritical() << "No default account";
// Add text capability for chat in conf // Add text capability for chat in conf
linphoneConf->setCapability(linphone::StreamType::Text, true); linphoneConf->setCapability(linphone::StreamType::Text, true);
if (SettingsModel::getInstance()->getCreateEndToEndEncryptedMeetingsAndGroupCalls())
linphoneConf->setSecurityLevel(linphone::Conference::SecurityLevel::EndToEnd);
else linphoneConf->setSecurityLevel(linphone::Conference::SecurityLevel::PointToPoint);
auto confInfoModel = Utils::makeQObject_ptr<ConferenceInfoModel>(linphoneConf); auto confInfoModel = Utils::makeQObject_ptr<ConferenceInfoModel>(linphoneConf);
auto confSchedulerModel = confInfoModel->getConferenceScheduler(); auto confSchedulerModel = confInfoModel->getConferenceScheduler();
if (!confSchedulerModel) { if (!confSchedulerModel) {
@ -649,3 +657,117 @@ bool ConferenceInfoCore::isAllDayConf() const {
return mDateTime.time().hour() == 0 && mDateTime.time().minute() == 0 && mEndDateTime.time().hour() == 23 && return mDateTime.time().hour() == 0 && mDateTime.time().minute() == 0 && mEndDateTime.time().hour() == 23 &&
mEndDateTime.time().minute() == 59; mEndDateTime.time().minute() == 59;
} }
void ConferenceInfoCore::exportConferenceToICS() {
// Collect participant addresses
QStringList participantAddresses;
for (const auto &participant : mParticipants) {
auto map = participant.toMap();
QString address = map["address"].toString();
if (!address.isEmpty()) {
participantAddresses.append(address);
}
}
// Copy data needed for ICS generation
QString uri = mUri;
QString organizerAddress = mOrganizerAddress;
QString organizerName = mOrganizerName;
QString subject = mSubject;
QString description = mDescription;
QDateTime dateTime = mDateTime;
QDateTime endDateTime = mEndDateTime;
// Generate ICS on model thread (for display name lookup) then open file
App::postModelAsync(
[participantAddresses, uri, organizerAddress, organizerName, subject, description, dateTime, endDateTime]() {
// Helper lambda to escape special characters in ICS text fields
auto escapeIcsText = [](const QString &text) {
QString escaped = text;
escaped.replace("\\", "\\\\");
escaped.replace(";", "\\;");
escaped.replace(",", "\\,");
escaped.replace("\n", "\\n");
return escaped;
};
// Helper lambda to format datetime in ICS format (UTC)
auto formatIcsDateTime = [](const QDateTime &dt) { return dt.toUTC().toString("yyyyMMdd'T'HHmmss'Z'"); };
// Generate a unique UID based on URI or datetime + organizer
QString uid;
if (!uri.isEmpty()) {
uid = uri;
uid.replace("sip:", "").replace("@", "-at-");
} else {
uid = dateTime.toUTC().toString("yyyyMMddHHmmss") + "-" + organizerAddress;
uid.replace("sip:", "").replace("@", "-at-");
}
// Build the ICS content
QString icsContent;
QTextStream out(&icsContent);
out << "BEGIN:VCALENDAR\r\n";
out << "VERSION:2.0\r\n";
out << "PRODID:-//Titanium Comms//EN\r\n";
out << "METHOD:REQUEST\r\n";
out << "BEGIN:VEVENT\r\n";
// UID and timestamps
out << "UID:" << uid << "\r\n";
out << "DTSTAMP:" << formatIcsDateTime(QDateTime::currentDateTimeUtc()) << "\r\n";
out << "DTSTART:" << formatIcsDateTime(dateTime) << "\r\n";
out << "DTEND:" << formatIcsDateTime(endDateTime) << "\r\n";
// Organizer
if (!organizerAddress.isEmpty()) {
out << "ORGANIZER";
if (!organizerName.isEmpty()) {
out << ";CN=" << escapeIcsText(organizerName);
}
out << ":" << organizerAddress << "\r\n";
}
// Attendees/Participants
for (const QString &address : participantAddresses) {
QString displayName = ToolModel::getDisplayName(address);
out << "ATTENDEE;ROLE=REQ-PARTICIPANT;PARTSTAT=NEEDS-ACTION;RSVP=TRUE";
if (!displayName.isEmpty()) {
out << ";CN=" << escapeIcsText(displayName);
}
out << ":" << address << "\r\n";
}
// Subject/Summary
if (!subject.isEmpty()) {
out << "SUMMARY:" << escapeIcsText(subject) << "\r\n";
}
// Description
if (!description.isEmpty()) {
out << "DESCRIPTION:" << escapeIcsText(description) << "\r\n";
}
// Location (conference URI)
if (!uri.isEmpty()) {
out << "LOCATION:" << uri << "\r\n";
out << "URL:" << uri << "\r\n";
}
out << "STATUS:CONFIRMED\r\n";
out << "SEQUENCE:0\r\n";
out << "END:VEVENT\r\n";
out << "END:VCALENDAR\r\n";
// Write the file and open it
QString filePath(Paths::getAppLocalDirPath() + "conference.ics");
QFile file(filePath);
if (file.open(QIODevice::WriteOnly | QIODevice::Text)) {
QTextStream fileOut(&file);
fileOut << icsContent;
file.close();
}
QDesktopServices::openUrl(QUrl::fromLocalFile(filePath));
});
}

View file

@ -1,4 +1,4 @@
/* /*
* Copyright (c) 2022 Belledonne Communications SARL. * Copyright (c) 2022 Belledonne Communications SARL.
* *
* This file is part of linphone-desktop * This file is part of linphone-desktop
@ -135,6 +135,8 @@ public:
Q_INVOKABLE bool isAllDayConf() const; Q_INVOKABLE bool isAllDayConf() const;
Q_INVOKABLE void exportConferenceToICS();
signals: signals:
void dateTimeChanged(); void dateTimeChanged();
void endDateTimeChanged(); void endDateTimeChanged();
@ -177,6 +179,7 @@ private:
QString mSubject; QString mSubject;
QString mDescription; QString mDescription;
QString mUri; QString mUri;
QString mIcalendarString;
QVariantList mParticipants; QVariantList mParticipants;
QSharedPointer<TimeZoneModel> mTimeZoneModel; QSharedPointer<TimeZoneModel> mTimeZoneModel;
LinphoneEnums::ConferenceSchedulerState mConferenceSchedulerState; LinphoneEnums::ConferenceSchedulerState mConferenceSchedulerState;

View file

@ -235,13 +235,17 @@ int ConferenceInfoList::getCurrentDateIndex() {
return it == confInfoList.end() ? -1 : std::distance(confInfoList.begin(), it); return it == confInfoList.end() ? -1 : std::distance(confInfoList.begin(), it);
} }
QSharedPointer<ConferenceInfoCore> ConferenceInfoList::getCurrentDateConfInfo() { QSharedPointer<ConferenceInfoCore> ConferenceInfoList::getCurrentDateConfInfo(bool enableCancelledConference) {
auto today = QDate::currentDate(); auto today = QDate::currentDate();
auto confInfoList = getSharedList<ConferenceInfoCore>(); auto confInfoList = getSharedList<ConferenceInfoCore>();
QList<QSharedPointer<ConferenceInfoCore>>::iterator it; QList<QSharedPointer<ConferenceInfoCore>>::iterator it;
if (mHaveCurrentDate) { if (mHaveCurrentDate) {
it = std::find_if(confInfoList.begin(), confInfoList.end(), it = std::find_if(confInfoList.begin(), confInfoList.end(),
[today](const QSharedPointer<ConferenceInfoCore> &item) { [today, enableCancelledConference](const QSharedPointer<ConferenceInfoCore> &item) {
if (!item) return false;
if (!enableCancelledConference &&
item->getConferenceInfoState() == LinphoneEnums::ConferenceInfoState::Cancelled)
return false;
return item && item->getDateTimeUtc().date() == today; return item && item->getDateTimeUtc().date() == today;
}); });
} else it = std::find(confInfoList.begin(), confInfoList.end(), nullptr); } else it = std::find(confInfoList.begin(), confInfoList.end(), nullptr);

View file

@ -40,6 +40,9 @@ public:
void setSelf(QSharedPointer<ConferenceInfoList> me); void setSelf(QSharedPointer<ConferenceInfoList> me);
void resetData(QList<QSharedPointer<ConferenceInfoCore>> data); void resetData(QList<QSharedPointer<ConferenceInfoCore>> data);
void resetData() override {
ListProxy::resetData();
}
void addConference(const std::shared_ptr<linphone::ConferenceInfo> &confInfo); void addConference(const std::shared_ptr<linphone::ConferenceInfo> &confInfo);
@ -48,7 +51,7 @@ public:
void updateHaveCurrentDate(); void updateHaveCurrentDate();
int getCurrentDateIndex(); int getCurrentDateIndex();
QSharedPointer<ConferenceInfoCore> getCurrentDateConfInfo(); QSharedPointer<ConferenceInfoCore> getCurrentDateConfInfo(bool enableCancelledConference = false);
QSharedPointer<ConferenceInfoCore> build(const std::shared_ptr<linphone::ConferenceInfo> &conferenceInfo); QSharedPointer<ConferenceInfoCore> build(const std::shared_ptr<linphone::ConferenceInfo> &conferenceInfo);
void connectItem(QSharedPointer<ConferenceInfoCore> confInfoCore); void connectItem(QSharedPointer<ConferenceInfoCore> confInfoCore);

View file

@ -27,7 +27,11 @@
DEFINE_ABSTRACT_OBJECT(ConferenceInfoProxy) DEFINE_ABSTRACT_OBJECT(ConferenceInfoProxy)
ConferenceInfoProxy::ConferenceInfoProxy(QObject *parent) : LimitProxy(parent) { ConferenceInfoProxy::ConferenceInfoProxy(QObject *parent) : LimitProxy(parent) {
mList = ConferenceInfoList::create(); if (!App::getInstance()->getConferenceInfoList()) {
mList = ConferenceInfoList::create();
App::getInstance()->setConferenceInfoList(mList);
}
mList = App::getInstance()->getConferenceInfoList();
setSourceModels(new SortFilterList(mList.get(), Qt::AscendingOrder)); setSourceModels(new SortFilterList(mList.get(), Qt::AscendingOrder));
connect( connect(
mList.get(), &ConferenceInfoList::haveCurrentDateChanged, this, mList.get(), &ConferenceInfoList::haveCurrentDateChanged, this,
@ -105,7 +109,7 @@ void ConferenceInfoProxy::clear() {
mList->clearData(); mList->clearData();
} }
ConferenceInfoGui *ConferenceInfoProxy::getCurrentDateConfInfo() { ConferenceInfoGui *ConferenceInfoProxy::getCurrentDateConfInfo(bool enableCancelledConference) {
if (mList) { if (mList) {
auto confInfo = mList->getCurrentDateConfInfo(); auto confInfo = mList->getCurrentDateConfInfo();
return confInfo ? new ConferenceInfoGui(confInfo) : nullptr; return confInfo ? new ConferenceInfoGui(confInfo) : nullptr;
@ -150,7 +154,7 @@ bool ConferenceInfoProxy::SortFilterList::lessThan(const QModelIndex &sourceLeft
auto nowDate = QDate::currentDate(); auto nowDate = QDate::currentDate();
if (!l || !r) { // sort on date if (!l || !r) { // sort on date
auto rdate = r ? r->getDateTimeUtc().date() : QDate::currentDate(); auto rdate = r ? r->getDateTimeUtc().date() : QDate::currentDate();
return !l ? nowDate <= r->getDateTimeUtc().date() : l->getDateTimeUtc().date() < nowDate; return !l ? nowDate < r->getDateTimeUtc().date() : l->getDateTimeUtc().date() < nowDate;
} else { } else {
return l->getDateTimeUtc() < r->getDateTimeUtc(); return l->getDateTimeUtc() < r->getDateTimeUtc();
} }

View file

@ -47,7 +47,7 @@ public:
bool getAccountConnected() const; bool getAccountConnected() const;
Q_INVOKABLE void clear(); Q_INVOKABLE void clear();
Q_INVOKABLE ConferenceInfoGui *getCurrentDateConfInfo(); Q_INVOKABLE ConferenceInfoGui *getCurrentDateConfInfo(bool enableCancelledConference = false);
Q_INVOKABLE int loadUntil(ConferenceInfoGui *confInfo); Q_INVOKABLE int loadUntil(ConferenceInfoGui *confInfo);
int loadUntil(QSharedPointer<ConferenceInfoCore> data); int loadUntil(QSharedPointer<ConferenceInfoCore> data);
signals: signals:

View file

@ -130,7 +130,7 @@ Notifier::~Notifier() {
bool Notifier::createNotification(Notifier::NotificationType type, QVariantMap data) { bool Notifier::createNotification(Notifier::NotificationType type, QVariantMap data) {
mMutex->lock(); mMutex->lock();
// Q_ASSERT(mInstancesNumber <= MaxNotificationsNumber); // Q_ASSERT(mInstancesNumber <= MaxNotificationsNumber);
if (mInstancesNumber == MaxNotificationsNumber) { // Check existing instances. if (mInstancesNumber >= MaxNotificationsNumber) { // Check existing instances.
qWarning() << QStringLiteral("Unable to create another notification."); qWarning() << QStringLiteral("Unable to create another notification.");
mMutex->unlock(); mMutex->unlock();
return false; return false;
@ -288,9 +288,6 @@ void Notifier::notifyReceivedCall(const shared_ptr<linphone::Call> &call) {
auto voicemailAddress = linphone::Factory::get()->createAddress( auto voicemailAddress = linphone::Factory::get()->createAddress(
Utils::appStringToCoreString(accountModel->getVoicemailAddress())); Utils::appStringToCoreString(accountModel->getVoicemailAddress()));
if (voicemailAddress) call->transferTo(voicemailAddress); if (voicemailAddress) call->transferTo(voicemailAddress);
} else {
lInfo() << log().arg("Declining call.");
call->decline(linphone::Reason::Busy);
} }
return; return;
} }
@ -335,7 +332,8 @@ void Notifier::notifyReceivedMessages(const std::shared_ptr<linphone::ChatRoom>
if (receiverAccount) { if (receiverAccount) {
auto senderAccount = ToolModel::findAccount(message->getFromAddress()); auto senderAccount = ToolModel::findAccount(message->getFromAddress());
auto currentAccount = CoreModel::getInstance()->getCore()->getDefaultAccount(); auto currentAccount = CoreModel::getInstance()->getCore()->getDefaultAccount();
if (senderAccount && senderAccount->getContactAddress()->weakEqual(currentAccount->getContactAddress())) { if (senderAccount && senderAccount->getContactAddress() && currentAccount->getContactAddress() &&
senderAccount->getContactAddress()->weakEqual(currentAccount->getContactAddress())) {
qDebug() << "sender is current account, return"; qDebug() << "sender is current account, return";
return; return;
} }
@ -360,7 +358,7 @@ void Notifier::notifyReceivedMessages(const std::shared_ptr<linphone::ChatRoom>
if (content->isText()) txt += content->getUtf8Text().c_str(); if (content->isText()) txt += content->getUtf8Text().c_str();
} }
} else if (fileContent->isVoiceRecording()) } else if (fileContent->isVoiceRecording())
//: 'Voice message received!' : message to warn the user in a notofication for voice messages. //: 'Voice message received!' : message to warn the user in a notification for voice messages.
txt = tr("new_voice_message"); txt = tr("new_voice_message");
else txt = tr("new_file_message"); else txt = tr("new_file_message");
if (txt.isEmpty() && message->hasConferenceInvitationContent()) if (txt.isEmpty() && message->hasConferenceInvitationContent())
@ -369,6 +367,7 @@ void Notifier::notifyReceivedMessages(const std::shared_ptr<linphone::ChatRoom>
}; };
if (messages.size() == 1) { // Display only sender on mono message. if (messages.size() == 1) { // Display only sender on mono message.
if (message->isRead()) return;
getMessage(message); getMessage(message);
if (txt.isEmpty()) { // Do not notify message without content if (txt.isEmpty()) { // Do not notify message without content
qDebug() << "empty notif, return"; qDebug() << "empty notif, return";

View file

@ -20,10 +20,8 @@
#include <QQmlApplicationEngine> #include <QQmlApplicationEngine>
#include "core/App.hpp"
#include "ParticipantCore.hpp" #include "ParticipantCore.hpp"
// #include "ParticipantDeviceList.hpp" #include "core/App.hpp"
#include "model/participant/ParticipantModel.hpp" #include "model/participant/ParticipantModel.hpp"
#include "model/tool/ToolModel.hpp" #include "model/tool/ToolModel.hpp"
#include "tool/Utils.hpp" #include "tool/Utils.hpp"
@ -77,6 +75,48 @@ void ParticipantCore::setSelf(QSharedPointer<ParticipantCore> me) {
QTimer::singleShot(secs * 1000, this, &ParticipantCore::onEndOfInvitation); QTimer::singleShot(secs * 1000, this, &ParticipantCore::onEndOfInvitation);
}); });
connect(this, &ParticipantCore::sipAddressChanged, this, &ParticipantCore::updateIsMe); connect(this, &ParticipantCore::sipAddressChanged, this, &ParticipantCore::updateIsMe);
auto update = [this, remoteAddress = mSipAddress](const std::shared_ptr<linphone::Friend> &updatedFriend) {
bool isThisFriend = false;
std::shared_ptr<linphone::Address> friendAddress;
auto participantAddress = mParticipantModel->getAddress();
for (auto address : updatedFriend->getAddresses()) {
if (address->weakEqual(participantAddress)) {
isThisFriend = true;
friendAddress = address;
break;
}
}
if (isThisFriend) {
auto displayName = friendAddress->getDisplayName();
mCoreModelConnection->invokeToCore([this, displayName] {
auto me = mCoreModelConnection->mCore.mQData; // Locked from previous call.
setDisplayName(Utils::coreStringToAppString(displayName));
});
}
};
auto onRemoved = [this](const std::shared_ptr<linphone::Friend> &updatedFriend) {
bool isThisFriend = false;
std::shared_ptr<linphone::Address> friendAddress;
auto participantAddress = mParticipantModel->getAddress();
for (auto address : updatedFriend->getAddresses()) {
if (address->weakEqual(participantAddress)) {
isThisFriend = true;
friendAddress = address;
break;
}
}
if (isThisFriend) {
auto displayName = ToolModel::getDisplayName(participantAddress);
mCoreModelConnection->invokeToCore([this, displayName] {
auto me = mCoreModelConnection->mCore.mQData; // Locked from previous call.
setDisplayName(displayName);
});
}
};
mCoreModelConnection = SafeConnection<ParticipantCore, CoreModel>::create(me, CoreModel::getInstance());
mCoreModelConnection->makeConnectToModel(&CoreModel::friendCreated, update);
mCoreModelConnection->makeConnectToModel(&CoreModel::friendUpdated, update);
mCoreModelConnection->makeConnectToModel(&CoreModel::friendRemoved, onRemoved);
} }
LinphoneEnums::SecurityLevel ParticipantCore::getSecurityLevel() const { LinphoneEnums::SecurityLevel ParticipantCore::getSecurityLevel() const {

View file

@ -107,6 +107,7 @@ signals:
private: private:
std::shared_ptr<ParticipantModel> mParticipantModel; std::shared_ptr<ParticipantModel> mParticipantModel;
QSharedPointer<SafeConnection<ParticipantCore, ParticipantModel>> mParticipantConnection; QSharedPointer<SafeConnection<ParticipantCore, ParticipantModel>> mParticipantConnection;
QSharedPointer<SafeConnection<ParticipantCore, CoreModel>> mCoreModelConnection;
QList<QVariant> mParticipantDevices; QList<QVariant> mParticipantDevices;

View file

@ -63,8 +63,8 @@ void ParticipantDeviceProxy::setCurrentCall(CallGui *call) {
mCurrentCall = call; mCurrentCall = call;
if (mCurrentCall) callCore = mCurrentCall->getCore(); if (mCurrentCall) callCore = mCurrentCall->getCore();
if (callCore) { if (callCore) {
connect(callCore, &CallCore::conferenceChanged, mParticipants.get(), [this]() { connect(callCore, &CallCore::conferenceChanged, mParticipants.get(), [this, callCore]() {
auto conference = mCurrentCall->getCore()->getConferenceCore(); auto conference = callCore->getConferenceCore();
lDebug() << log().arg("Set conference") << this << " => " << conference; lDebug() << log().arg("Set conference") << this << " => " << conference;
mParticipants->setConferenceModel(conference ? conference->getModel() : nullptr); mParticipants->setConferenceModel(conference ? conference->getModel() : nullptr);
}); });

View file

@ -48,7 +48,7 @@ ParticipantInfoList::~ParticipantInfoList() {
void ParticipantInfoList::setChatCore(const QSharedPointer<ChatCore> &chatCore) { void ParticipantInfoList::setChatCore(const QSharedPointer<ChatCore> &chatCore) {
mustBeInMainThread(log().arg(Q_FUNC_INFO)); mustBeInMainThread(log().arg(Q_FUNC_INFO));
if (mChatCore) disconnect(mChatCore.get()); if (mChatCore) disconnect(mChatCore.get(), &ChatCore::participantsChanged, this, nullptr);
mChatCore = chatCore; mChatCore = chatCore;
lDebug() << "[ParticipantInfoList] : set Chat " << mChatCore.get(); lDebug() << "[ParticipantInfoList] : set Chat " << mChatCore.get();
clearData(); clearData();

View file

@ -44,7 +44,7 @@ ChatGui *ParticipantInfoProxy::getChat() const {
} }
void ParticipantInfoProxy::setChat(ChatGui *chat) { void ParticipantInfoProxy::setChat(ChatGui *chat) {
lDebug() << "[ParticipantInfoProxy] set current chat " << chat; lDebug() << "[ParticipantInfoProxy] set current chat " << chat << (chat ? chat->mCore->getTitle() : "NULL");
if (mChat != chat) { if (mChat != chat) {
mChat = chat; mChat = chat;
mParticipants->setChatCore(chat ? chat->mCore : nullptr); mParticipants->setChatCore(chat ? chat->mCore : nullptr);

View file

@ -109,7 +109,7 @@ void ParticipantProxy::setShowMe(const bool &show) {
if (list->mShowMe != show) { if (list->mShowMe != show) {
list->mShowMe = show; list->mShowMe = show;
emit showMeChanged(); emit showMeChanged();
invalidateFilter(); invalidate();
} }
} }

View file

@ -115,6 +115,7 @@ static inline QDir getAppPackageDir() {
} }
static inline QString getAppPackageDataDirPath() { static inline QString getAppPackageDataDirPath() {
QDir executableDir(QCoreApplication::applicationDirPath());
QDir dir = getAppPackageDir(); QDir dir = getAppPackageDir();
#ifdef __APPLE__ #ifdef __APPLE__
if (!dir.cd("Resources")) { if (!dir.cd("Resources")) {
@ -126,17 +127,26 @@ static inline QString getAppPackageDataDirPath() {
dir.mkdir("share"); dir.mkdir("share");
dir.cd("share"); dir.cd("share");
} }
return dir.absolutePath(); lInfo() << executableDir.absolutePath() << " VS " << dir.absolutePath()
<< " == " << executableDir.relativeFilePath(dir.absolutePath());
return executableDir.relativeFilePath(dir.absolutePath());
} }
static inline QString getAppPackageMsPluginsDirPath() { static inline QString getAppPackageMsPluginsDirPath() {
QDir executableDir(QCoreApplication::applicationDirPath());
QDir dir = getAppPackageDir(); QDir dir = getAppPackageDir();
dir.cd(MSPLUGINS_DIR); dir.cd(MSPLUGINS_DIR);
return dir.absolutePath(); return executableDir.relativeFilePath(dir.absolutePath());
} }
static inline QString getAppPackagePluginsDirPath() { static inline QString getAppPackagePluginsDirPath() {
return getAppPackageDir().absolutePath() + Constants::PathPlugins; QDir executableDir(QCoreApplication::applicationDirPath());
QDir packageDir = getAppPackageDir();
return executableDir.relativeFilePath(packageDir.absolutePath() + Constants::PathPlugins);
}
static inline QString getAppBinDirPath() {
return getAppPackageDir().absolutePath() + Constants::PathBin;
} }
static inline QString getAppAssistantConfigDirPath() { static inline QString getAppAssistantConfigDirPath() {
@ -159,29 +169,6 @@ static inline QString getAppFriendsFilePath() {
return QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation) + Constants::PathFriendsList; return QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation) + Constants::PathFriendsList;
} }
static inline QString getAppRootCaFilePath() {
QString rootca = getAppPackageDataDirPath() + Constants::PathRootCa;
if (Paths::filePathExists(rootca)) { // Packaged
return rootca;
} else {
lInfo() << "Root ca path does not exist. Create it";
QFileInfo rootcaInfo(rootca);
if (!rootcaInfo.absoluteDir().exists()) {
QDir dataDir(getAppPackageDataDirPath());
if (!dataDir.mkpath(Constants::PathRootCa)) {
lCritical() << "ERROR : COULD NOT CREATE DIRECTORY WITH PATH" << Constants::PathRootCa;
return "";
}
}
QFile rootCaFile(rootca);
if (rootCaFile.open(QIODevice::ReadWrite)) return rootca;
else {
lCritical() << "ERROR : COULD NOT CREATE ROOTCA WITH PATH" << rootca;
}
}
return "";
}
static inline QString getAppMessageHistoryFilePath() { static inline QString getAppMessageHistoryFilePath() {
return QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation) + Constants::PathMessageHistoryList; return QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation) + Constants::PathMessageHistoryList;
} }
@ -198,7 +185,9 @@ bool Paths::filePathExists(const QString &path, const bool isWritable) {
QFile file(path); QFile file(path);
return file.exists(); return file.exists();
} }
bool Paths::isSameRelativeFile(const QString &filePath, const QString &relativeFilePath) {
return filePath != relativeFilePath && QFileInfo(relativeFilePath) == QFileInfo(filePath);
}
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
QString Paths::getAppLocalDirPath() { QString Paths::getAppLocalDirPath() {
@ -283,15 +272,30 @@ QString Paths::getLogsDirPath() {
Constants::PathLogs); Constants::PathLogs);
} }
QString Paths::getAppRootCaFilePath() {
// Hardcoded because it comes from linphone and is not customizable.
return getReadableFilePath(getAppPackageDataDirPath() + "/linphone/rootca.pem");
}
QString Paths::getCrashpadDirPath() {
#ifdef HAVE_CRASH_HANDLER
return getWritableDirPath(QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation) +
Constants::PathCrashpad);
#else
return "";
#endif
}
QString Paths::getMetricsDirPath() {
return getWritableDirPath(QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation) +
Constants::PathMetrics);
}
QString Paths::getMessageHistoryFilePath() { QString Paths::getMessageHistoryFilePath() {
return getReadableFilePath( return getReadableFilePath(
getAppMessageHistoryFilePath()); // No need to ensure that the file exists as this DB is deprecated getAppMessageHistoryFilePath()); // No need to ensure that the file exists as this DB is deprecated
} }
QString Paths::getPackageDataDirPath() {
return getReadableDirPath(getAppPackageDataDirPath() + Constants::PathData);
}
QString Paths::getPackageMsPluginsDirPath() { QString Paths::getPackageMsPluginsDirPath() {
return getReadableDirPath(getAppPackageMsPluginsDirPath()); return getReadableDirPath(getAppPackageMsPluginsDirPath());
} }
@ -300,10 +304,6 @@ QString Paths::getPackagePluginsAppDirPath() {
return getReadableDirPath(getAppPackagePluginsDirPath() + Constants::PathPluginsApp); return getReadableDirPath(getAppPackagePluginsDirPath() + Constants::PathPluginsApp);
} }
QString Paths::getPackageSoundsResourcesDirPath() {
return getReadableDirPath(getAppPackageDataDirPath() + Constants::PathSounds);
}
QString Paths::getPackageTopDirPath() { QString Paths::getPackageTopDirPath() {
return getReadableDirPath(getAppPackageDataDirPath()); return getReadableDirPath(getAppPackageDataDirPath());
} }
@ -319,10 +319,6 @@ QStringList Paths::getPluginsAppFolders() {
return pluginPaths; return pluginPaths;
} }
QString Paths::getRootCaFilePath() {
return getReadableFilePath(getAppRootCaFilePath());
}
QString Paths::getToolsDirPath() { QString Paths::getToolsDirPath() {
return getWritableDirPath(QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation) + return getWritableDirPath(QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation) +
Constants::PathTools); Constants::PathTools);
@ -337,6 +333,14 @@ QString Paths::getZrtpSecretsFilePath() {
Constants::PathZrtpSecrets); Constants::PathZrtpSecrets);
} }
QString Paths::getCrashpadHandlerFilePath() {
#ifdef HAVE_CRASH_HANDLER
return getAppBinDirPath() + Constants::PathCrashpadHandler;
#else
return "";
#endif
}
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
void Paths::migrate() { void Paths::migrate() {

View file

@ -27,8 +27,13 @@
namespace Paths { namespace Paths {
bool filePathExists(const QString &path, const bool isWritable = false); bool filePathExists(const QString &path, const bool isWritable = false);
// bool convertToRelativePath(const QString &path, QString *relativePath);
// Return true if paths are different and point to the same file
bool isSameRelativeFile(const QString &filePath, const QString &relativeFilePath);
QString getAppLocalDirPath(); QString getAppLocalDirPath();
QString getAppRootCaFilePath();
QString getAssistantConfigDirPath(); QString getAssistantConfigDirPath();
QString getAvatarsDirPath(); QString getAvatarsDirPath();
QString getVCardsPath(); QString getVCardsPath();
@ -43,19 +48,19 @@ QString getFactoryConfigFilePath();
QString getFriendsListFilePath(); QString getFriendsListFilePath();
QString getLimeDatabasePath(); QString getLimeDatabasePath();
QString getLogsDirPath(); QString getLogsDirPath();
QString getCrashpadDirPath();
QString getMetricsDirPath();
QString getMessageHistoryFilePath(); QString getMessageHistoryFilePath();
QString getPackageDataDirPath();
QString getPackageMsPluginsDirPath(); QString getPackageMsPluginsDirPath();
QString getPackagePluginsAppDirPath(); QString getPackagePluginsAppDirPath();
QString getPackageSoundsResourcesDirPath();
QString getPackageTopDirPath(); QString getPackageTopDirPath();
QString getPluginsAppDirPath(); QString getPluginsAppDirPath();
QStringList getPluginsAppFolders(); QStringList getPluginsAppFolders();
QString getRootCaFilePath();
QString getToolsDirPath(); QString getToolsDirPath();
QString getUserCertificatesDirPath(); QString getUserCertificatesDirPath();
QString getZrtpDataFilePath(); QString getZrtpDataFilePath();
QString getZrtpSecretsFilePath(); QString getZrtpSecretsFilePath();
QString getCrashpadHandlerFilePath();
void migrate(); void migrate();
} // namespace Paths } // namespace Paths

View file

@ -51,8 +51,25 @@ PayloadTypeCore::~PayloadTypeCore() {
void PayloadTypeCore::setSelf(QSharedPointer<PayloadTypeCore> me) { void PayloadTypeCore::setSelf(QSharedPointer<PayloadTypeCore> me) {
mPayloadTypeModelConnection = SafeConnection<PayloadTypeCore, PayloadTypeModel>::create(me, mPayloadTypeModel); mPayloadTypeModelConnection = SafeConnection<PayloadTypeCore, PayloadTypeModel>::create(me, mPayloadTypeModel);
DEFINE_CORE_GETSET_CONNECT(mPayloadTypeModelConnection, PayloadTypeCore, PayloadTypeModel, mPayloadTypeModel, bool, mPayloadTypeModelConnection->makeConnectToCore(&PayloadTypeCore::setEnabled, [this](bool enabled) {
enabled, Enabled) if (enabled != mEnabled) {
mChanged = true;
emit changed();
}
mEnabled = enabled;
});
mPayloadTypeModelConnection->makeConnectToModel(&PayloadTypeModel::enabledChanged, [this](bool enabled) {
mPayloadTypeModelConnection->invokeToCore([this, enabled]() {
if (mEnabled != enabled) {
mEnabled = enabled;
emit enabledChanged();
}
});
});
}
void PayloadTypeCore::save() {
if (mChanged) mPayloadTypeModelConnection->invokeToModel([this]() { mPayloadTypeModel->setEnabled(mEnabled); });
} }
PayloadTypeCore::Family PayloadTypeCore::getFamily() { PayloadTypeCore::Family PayloadTypeCore::getFamily() {

View file

@ -51,9 +51,15 @@ public:
bool isDownloadable(); bool isDownloadable();
QString getMimeType(); QString getMimeType();
Q_INVOKABLE void save();
signals:
void changed();
protected: protected:
Family mFamily; Family mFamily;
bool mDownloadable = false; bool mDownloadable = false;
bool mChanged = false;
DECLARE_CORE_GETSET_MEMBER(bool, enabled, Enabled) DECLARE_CORE_GETSET_MEMBER(bool, enabled, Enabled)
DECLARE_CORE_MEMBER(QString, mimeType, MimeType) DECLARE_CORE_MEMBER(QString, mimeType, MimeType)
DECLARE_CORE_MEMBER(QString, encoderDescription, EncoderDescription) DECLARE_CORE_MEMBER(QString, encoderDescription, EncoderDescription)

View file

@ -31,14 +31,7 @@ class PayloadTypeProxy : public LimitProxy, public AbstractObject {
Q_OBJECT Q_OBJECT
public: public:
enum PayloadTypeProxyFiltering { enum PayloadTypeProxyFiltering { All = 0, Audio = 2, Video = 4, Text = 8, Downloadable = 16, NotDownloadable = 32 };
All = 0,
Audio = 2,
Video = 4,
Text = 8,
Downloadable = 16,
NotDownloadable = 32
};
Q_ENUMS(PayloadTypeProxyFiltering) Q_ENUMS(PayloadTypeProxyFiltering)
DECLARE_SORTFILTER_CLASS() DECLARE_SORTFILTER_CLASS()

View file

@ -37,13 +37,21 @@ LimitProxy::~LimitProxy() {
bool LimitProxy::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const { bool LimitProxy::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const {
return mMaxDisplayItems == -1 || sourceRow < mMaxDisplayItems; return mMaxDisplayItems == -1 || sourceRow < mMaxDisplayItems;
} }
void LimitProxy::invalidateFilter() {
// TODO for a better filter management by encapsulating filter change between begin/end
#if QT_VERSION < QT_VERSION_CHECK(6, 10, 0)
QSortFilterProxyModel::invalidateFilter();
#else
QSortFilterProxyModel::beginFilterChange();
QSortFilterProxyModel::endFilterChange();
#endif
}
void LimitProxy::setSourceModels(SortFilterProxy *firstList) { void LimitProxy::setSourceModels(SortFilterProxy *firstList) {
auto secondList = firstList->sourceModel(); auto secondList = firstList->sourceModel();
if (secondList) { if (secondList) {
connect(secondList, &QAbstractItemModel::rowsInserted, this, &LimitProxy::onAdded); connect(secondList, &QAbstractItemModel::rowsInserted, this, &LimitProxy::onAdded);
connect(secondList, &QAbstractItemModel::rowsRemoved, this, &LimitProxy::onRemoved); connect(secondList, &QAbstractItemModel::rowsRemoved, this, &LimitProxy::onRemoved);
connect(secondList, &QAbstractItemModel::modelReset, this, &LimitProxy::invalidateRowsFilter); connect(secondList, &QAbstractItemModel::modelReset, this, &LimitProxy::invalidate);
} }
connect(firstList, &SortFilterProxy::filterTextChanged, this, &LimitProxy::filterTextChanged); connect(firstList, &SortFilterProxy::filterTextChanged, this, &LimitProxy::filterTextChanged);
connect(firstList, &SortFilterProxy::filterTypeChanged, this, &LimitProxy::filterTypeChanged); connect(firstList, &SortFilterProxy::filterTypeChanged, this, &LimitProxy::filterTypeChanged);
@ -60,8 +68,8 @@ void LimitProxy::setSourceModels(SortFilterProxy *firstList) {
/* /*
void LimitProxy::setSourceModels(SortFilterProxy *firstList, QAbstractItemModel *secondList) { void LimitProxy::setSourceModels(SortFilterProxy *firstList, QAbstractItemModel *secondList) {
connect(secondList, &QAbstractItemModel::rowsInserted, this, &LimitProxy::invalidateFilter); connect(secondList, &QAbstractItemModel::rowsInserted, this, &LimitProxy::invalidate);
connect(secondList, &QAbstractItemModel::rowsRemoved, this, &LimitProxy::invalidateFilter); connect(secondList, &QAbstractItemModel::rowsRemoved, this, &LimitProxy::invalidate);
connect(firstList, &SortFilterProxy::filterTextChanged, this, &LimitProxy::filterTextChanged); connect(firstList, &SortFilterProxy::filterTextChanged, this, &LimitProxy::filterTextChanged);
setSourceModel(firstList); setSourceModel(firstList);
}*/ }*/
@ -119,7 +127,7 @@ void LimitProxy::setMaxDisplayItems(int maxItems) {
emit maxDisplayItemsChanged(); emit maxDisplayItemsChanged();
if (model && getDisplayCount(modelCount) != oldCount) { if (model && getDisplayCount(modelCount) != oldCount) {
invalidateFilter(); invalidate();
} }
} }
} }
@ -178,6 +186,6 @@ void LimitProxy::onAdded() {
void LimitProxy::onRemoved() { void LimitProxy::onRemoved() {
int count = sourceModel()->rowCount(); int count = sourceModel()->rowCount();
if (mMaxDisplayItems > 0 && mMaxDisplayItems <= count) { if (mMaxDisplayItems > 0 && mMaxDisplayItems <= count) {
invalidateFilter(); invalidate();
} }
} }

View file

@ -41,7 +41,7 @@ public:
LimitProxy(QObject *parent = nullptr); LimitProxy(QObject *parent = nullptr);
virtual ~LimitProxy(); virtual ~LimitProxy();
virtual bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override; virtual bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override;
virtual void invalidateFilter();
// Helper for setting the limit with sorted/filtered list // Helper for setting the limit with sorted/filtered list
void setSourceModels(SortFilterProxy *firstList); void setSourceModels(SortFilterProxy *firstList);

View file

@ -26,6 +26,9 @@ SortFilterProxy::SortFilterProxy(QAbstractItemModel *list) : QSortFilterProxyMod
setSourceModel(list); setSourceModel(list);
} }
SortFilterProxy::SortFilterProxy() {
}
SortFilterProxy::SortFilterProxy(QAbstractItemModel *list, Qt::SortOrder order) : SortFilterProxy(list) { SortFilterProxy::SortFilterProxy(QAbstractItemModel *list, Qt::SortOrder order) : SortFilterProxy(list) {
sort(0, order); sort(0, order);
} }
@ -54,7 +57,7 @@ int SortFilterProxy::getCount() const {
QVariant SortFilterProxy::getAt(const int &atIndex) const { QVariant SortFilterProxy::getAt(const int &atIndex) const {
auto modelIndex = index(atIndex, 0); auto modelIndex = index(atIndex, 0);
return sourceModel()->data(mapToSource(modelIndex), 0); return sourceModel() ? sourceModel()->data(mapToSource(modelIndex), 0) : QVariant();
} }
int SortFilterProxy::getFilterType() const { int SortFilterProxy::getFilterType() const {
@ -63,9 +66,15 @@ int SortFilterProxy::getFilterType() const {
void SortFilterProxy::setFilterType(int filterType) { void SortFilterProxy::setFilterType(int filterType) {
if (getFilterType() != filterType) { if (getFilterType() != filterType) {
#if QT_VERSION >= QT_VERSION_CHECK(6, 10, 0)
beginFilterChange();
mFilterType = filterType; mFilterType = filterType;
endFilterChange();
#else
mFilterType = filterType;
invalidateFilter();
#endif
emit filterTypeChanged(filterType); emit filterTypeChanged(filterType);
invalidate();
} }
} }
@ -75,8 +84,14 @@ QString SortFilterProxy::getFilterText() const {
void SortFilterProxy::setFilterText(const QString &filter) { void SortFilterProxy::setFilterText(const QString &filter) {
if (mFilterText != filter) { if (mFilterText != filter) {
#if QT_VERSION >= QT_VERSION_CHECK(6, 10, 0)
beginFilterChange();
mFilterText = filter;
endFilterChange();
#else
mFilterText = filter; mFilterText = filter;
invalidateFilter(); invalidateFilter();
#endif
emit filterTextChanged(); emit filterTextChanged();
} }
} }
@ -90,5 +105,10 @@ void SortFilterProxy::remove(int index, int count) {
} }
void SortFilterProxy::invalidateFilter() { void SortFilterProxy::invalidateFilter() {
QSortFilterProxyModel::invalidateFilter(); #if QT_VERSION >= QT_VERSION_CHECK(6, 10, 0)
QSortFilterProxyModel::beginFilterChange();
QSortFilterProxyModel::endFilterChange();
#else
invalidateFilter();
#endif
} }

View file

@ -41,6 +41,7 @@ public:
Q_PROPERTY(int filterType READ getFilterType WRITE setFilterType NOTIFY filterTypeChanged) Q_PROPERTY(int filterType READ getFilterType WRITE setFilterType NOTIFY filterTypeChanged)
Q_PROPERTY(QString filterText READ getFilterText WRITE setFilterText NOTIFY filterTextChanged) Q_PROPERTY(QString filterText READ getFilterText WRITE setFilterText NOTIFY filterTextChanged)
SortFilterProxy();
SortFilterProxy(QAbstractItemModel *parent); SortFilterProxy(QAbstractItemModel *parent);
SortFilterProxy(QAbstractItemModel *parent, Qt::SortOrder order); SortFilterProxy(QAbstractItemModel *parent, Qt::SortOrder order);
virtual ~SortFilterProxy(); virtual ~SortFilterProxy();

View file

@ -49,7 +49,7 @@ MagicSearchList::~MagicSearchList() {
mustBeInMainThread("~" + getClassName()); mustBeInMainThread("~" + getClassName());
} }
void MagicSearchList::setSelf(QSharedPointer<MagicSearchList> me) { void MagicSearchList::setSelf(const QSharedPointer<MagicSearchList> &me) {
mCoreModelConnection = SafeConnection<MagicSearchList, CoreModel>::create(me, CoreModel::getInstance()); mCoreModelConnection = SafeConnection<MagicSearchList, CoreModel>::create(me, CoreModel::getInstance());
mCoreModelConnection->makeConnectToModel( mCoreModelConnection->makeConnectToModel(
&CoreModel::friendCreated, [this](const std::shared_ptr<linphone::Friend> &f) { &CoreModel::friendCreated, [this](const std::shared_ptr<linphone::Friend> &f) {
@ -92,20 +92,33 @@ void MagicSearchList::setSelf(QSharedPointer<MagicSearchList> me) {
mModelConnection->makeConnectToModel( mModelConnection->makeConnectToModel(
&MagicSearchModel::searchResultsReceived, &MagicSearchModel::searchResultsReceived,
[this](const std::list<std::shared_ptr<linphone::SearchResult>> &results) { [this](const std::list<std::shared_ptr<linphone::SearchResult>> &results) {
lInfo() << log().arg("Search result received : Safe Connection =") << mModelConnection.get();
lInfo() << log().arg("this =") << this;
auto *contacts = new QList<QSharedPointer<FriendCore>>(); auto *contacts = new QList<QSharedPointer<FriendCore>>();
auto ldapContacts = ToolModel::getLdapFriendList(); auto ldapContacts = ToolModel::getLdapFriendList();
auto core = CoreModel::getInstance()->getCore();
auto userAddress = core->getDefaultAccount() && core->getDefaultAccount()->getParams()
? core->getDefaultAccount()->getParams()->getIdentityAddress()
: nullptr;
for (auto it : results) { for (auto it : results) {
QSharedPointer<FriendCore> contact; QSharedPointer<FriendCore> contact;
auto linphoneFriend = it->getFriend(); auto linphoneFriend = it->getFriend();
bool isStored = false; bool isStored = false;
if (linphoneFriend) { if (linphoneFriend) {
if (!mShowMe && userAddress && userAddress->weakEqual(linphoneFriend->getAddress())) {
lWarning() << log().arg("do not show my own address in this contact list");
continue;
}
isStored = isStored =
(ldapContacts->findFriendByAddress(linphoneFriend->getAddress()) != linphoneFriend); (ldapContacts->findFriendByAddress(linphoneFriend->getAddress()) != linphoneFriend);
contact = FriendCore::create(linphoneFriend, isStored, it->getSourceFlags()); contact = FriendCore::create(linphoneFriend, isStored, it->getSourceFlags());
contacts->append(contact); contacts->append(contact);
} else if (auto address = it->getAddress()) { } else if (auto address = it->getAddress()) {
auto linphoneFriend = CoreModel::getInstance()->getCore()->createFriend(); if (!mShowMe && userAddress && userAddress->weakEqual(address)) {
lWarning() << log().arg("do not show my own address in this contact list");
continue;
}
auto linphoneFriend = core->createFriend();
linphoneFriend->setAddress(address); linphoneFriend->setAddress(address);
contact = FriendCore::create(linphoneFriend, isStored, it->getSourceFlags()); contact = FriendCore::create(linphoneFriend, isStored, it->getSourceFlags());
auto displayName = Utils::coreStringToAppString(address->getDisplayName()); auto displayName = Utils::coreStringToAppString(address->getDisplayName());
@ -125,7 +138,7 @@ void MagicSearchList::setSelf(QSharedPointer<MagicSearchList> me) {
contacts->append(contact); contacts->append(contact);
} else if (!it->getPhoneNumber().empty()) { } else if (!it->getPhoneNumber().empty()) {
auto phoneNumber = it->getPhoneNumber(); auto phoneNumber = it->getPhoneNumber();
linphoneFriend = CoreModel::getInstance()->getCore()->createFriend(); linphoneFriend = core->createFriend();
linphoneFriend->addPhoneNumber(phoneNumber); linphoneFriend->addPhoneNumber(phoneNumber);
contact = FriendCore::create(linphoneFriend, isStored, it->getSourceFlags()); contact = FriendCore::create(linphoneFriend, isStored, it->getSourceFlags());
contact->setGivenName(Utils::coreStringToAppString(it->getPhoneNumber())); contact->setGivenName(Utils::coreStringToAppString(it->getPhoneNumber()));
@ -202,6 +215,17 @@ void MagicSearchList::setMaxResults(int maxResults) {
} }
} }
bool MagicSearchList::getShowMe() const {
return mShowMe;
}
void MagicSearchList::setShowMe(bool showMe) {
if (mShowMe != showMe) {
mShowMe = showMe;
emit showMeChanged(mShowMe);
}
}
LinphoneEnums::MagicSearchAggregation MagicSearchList::getAggregationFlag() const { LinphoneEnums::MagicSearchAggregation MagicSearchList::getAggregationFlag() const {
return mAggregationFlag; return mAggregationFlag;
} }

View file

@ -40,8 +40,8 @@ public:
MagicSearchList(QObject *parent = Q_NULLPTR); MagicSearchList(QObject *parent = Q_NULLPTR);
~MagicSearchList(); ~MagicSearchList();
void setSelf(QSharedPointer<MagicSearchList> me); void setSelf(const QSharedPointer<MagicSearchList> &me);
void connectContact(FriendCore* data); void connectContact(FriendCore *data);
void setSearch(const QString &search); void setSearch(const QString &search);
void setResults(const QList<QSharedPointer<FriendCore>> &contacts); void setResults(const QList<QSharedPointer<FriendCore>> &contacts);
void add(QSharedPointer<FriendCore> contact); void add(QSharedPointer<FriendCore> contact);
@ -55,6 +55,9 @@ public:
int getMaxResults() const; int getMaxResults() const;
void setMaxResults(int maxResults); void setMaxResults(int maxResults);
bool getShowMe() const;
void setShowMe(bool showMe);
virtual QHash<int, QByteArray> roleNames() const override; virtual QHash<int, QByteArray> roleNames() const override;
virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
@ -68,6 +71,7 @@ signals:
void sourceFlagsChanged(int sourceFlags); void sourceFlagsChanged(int sourceFlags);
void aggregationFlagChanged(LinphoneEnums::MagicSearchAggregation flag); void aggregationFlagChanged(LinphoneEnums::MagicSearchAggregation flag);
void maxResultsChanged(int maxResults); void maxResultsChanged(int maxResults);
void showMeChanged(bool showMe);
void friendCreated(int index, FriendGui *data); void friendCreated(int index, FriendGui *data);
void friendStarredChanged(); void friendStarredChanged();
@ -81,6 +85,7 @@ private:
LinphoneEnums::MagicSearchAggregation mAggregationFlag; LinphoneEnums::MagicSearchAggregation mAggregationFlag;
QString mSearchFilter; QString mSearchFilter;
int mMaxResults = -1; int mMaxResults = -1;
bool mShowMe = false;
std::shared_ptr<MagicSearchModel> mMagicSearch; std::shared_ptr<MagicSearchModel> mMagicSearch;
QSharedPointer<SafeConnection<MagicSearchList, MagicSearchModel>> mModelConnection; QSharedPointer<SafeConnection<MagicSearchList, MagicSearchModel>> mModelConnection;

View file

@ -148,6 +148,14 @@ void MagicSearchProxy::setMaxResults(int flags) {
mList->setMaxResults(flags); mList->setMaxResults(flags);
} }
bool MagicSearchProxy::getShowMe() const {
return mList->getShowMe();
}
void MagicSearchProxy::setShowMe(bool showMe) {
mList->setShowMe(showMe);
}
MagicSearchProxy *MagicSearchProxy::getParentProxy() const { MagicSearchProxy *MagicSearchProxy::getParentProxy() const {
return mParentProxy; return mParentProxy;
} }

View file

@ -34,6 +34,7 @@ class MagicSearchProxy : public LimitProxy {
Q_PROPERTY(int sourceFlags READ getSourceFlags WRITE setSourceFlags NOTIFY sourceFlagsChanged) Q_PROPERTY(int sourceFlags READ getSourceFlags WRITE setSourceFlags NOTIFY sourceFlagsChanged)
Q_PROPERTY(int maxResults READ getMaxResults WRITE setMaxResults NOTIFY maxResultsChanged) Q_PROPERTY(int maxResults READ getMaxResults WRITE setMaxResults NOTIFY maxResultsChanged)
Q_PROPERTY(bool showMe READ getShowMe WRITE setShowMe NOTIFY showMeChanged)
Q_PROPERTY(LinphoneEnums::MagicSearchAggregation aggregationFlag READ getAggregationFlag WRITE setAggregationFlag Q_PROPERTY(LinphoneEnums::MagicSearchAggregation aggregationFlag READ getAggregationFlag WRITE setAggregationFlag
NOTIFY aggregationFlagChanged) NOTIFY aggregationFlagChanged)
@ -60,6 +61,9 @@ public:
int getMaxResults() const; int getMaxResults() const;
void setMaxResults(int maxResults); void setMaxResults(int maxResults);
bool getShowMe() const;
void setShowMe(bool showMe);
MagicSearchProxy *getParentProxy() const; MagicSearchProxy *getParentProxy() const;
void setList(QSharedPointer<MagicSearchList> list); void setList(QSharedPointer<MagicSearchList> list);
Q_INVOKABLE void setParentProxy(MagicSearchProxy *proxy); Q_INVOKABLE void setParentProxy(MagicSearchProxy *proxy);
@ -77,6 +81,7 @@ signals:
void sourceFlagsChanged(int sourceFlags); void sourceFlagsChanged(int sourceFlags);
void aggregationFlagChanged(LinphoneEnums::MagicSearchAggregation aggregationFlag); void aggregationFlagChanged(LinphoneEnums::MagicSearchAggregation aggregationFlag);
void maxResultsChanged(int maxResults); void maxResultsChanged(int maxResults);
void showMeChanged(bool showMe);
void forceUpdate(); void forceUpdate();
void localFriendCreated(int index); void localFriendCreated(int index);
void parentProxyChanged(); void parentProxyChanged();

View file

@ -56,11 +56,20 @@ SettingsCore::SettingsCore(QObject *parent) : QObject(parent) {
if (ringtone.exists()) { if (ringtone.exists()) {
mRingtoneFileName = ringtone.fileName(); mRingtoneFileName = ringtone.fileName();
mRingtoneFolder = ringtone.absolutePath(); mRingtoneFolder = ringtone.absolutePath();
CoreModel::getInstance()->getCore()->enableNativeRinging(false);
} else { } else {
mRingtoneFileName = mRingtonePath.right(mRingtonePath.lastIndexOf(QDir::separator())); mRingtoneFileName = mRingtonePath.right(mRingtonePath.lastIndexOf(QDir::separator()));
mRingtoneFolder = mRingtonePath.left(mRingtonePath.lastIndexOf(QDir::separator())); mRingtoneFolder = mRingtonePath.left(mRingtonePath.lastIndexOf(QDir::separator()));
CoreModel::getInstance()->getCore()->enableNativeRinging(true);
} }
// Network
mIpv6Enabled = settingsModel->getIpv6Enabled();
// Advanced
mAutoStart = settingsModel->getAutoStart();
mHideFps = settingsModel->getHideFps();
// Audio // Audio
mCaptureDevices = settingsModel->getCaptureDevices(); mCaptureDevices = settingsModel->getCaptureDevices();
mPlaybackDevices = settingsModel->getPlaybackDevices(); mPlaybackDevices = settingsModel->getPlaybackDevices();
@ -90,6 +99,7 @@ SettingsCore::SettingsCore(QObject *parent) : QObject(parent) {
// Logs // Logs
mLogsEnabled = settingsModel->getLogsEnabled(); mLogsEnabled = settingsModel->getLogsEnabled();
mFullLogsEnabled = settingsModel->getFullLogsEnabled(); mFullLogsEnabled = settingsModel->getFullLogsEnabled();
mCrashReporterEnabled = settingsModel->getCrashReporterEnabled();
mLogsFolder = settingsModel->getLogsFolder(); mLogsFolder = settingsModel->getLogsFolder();
mLogsEmail = settingsModel->getLogsEmail(); mLogsEmail = settingsModel->getLogsEmail();
@ -107,16 +117,17 @@ SettingsCore::SettingsCore(QObject *parent) : QObject(parent) {
mEmojiFont = settingsModel->getEmojiFont(); mEmojiFont = settingsModel->getEmojiFont();
mTextMessageFont = settingsModel->getTextMessageFont(); mTextMessageFont = settingsModel->getTextMessageFont();
// Check for update
mIsCheckForUpdateAvailable = settingsModel->isCheckForUpdateAvailable();
// Ui // Ui
INIT_CORE_MEMBER(DisableChatFeature, settingsModel) INIT_CORE_MEMBER(DisableChatFeature, settingsModel)
INIT_CORE_MEMBER(DisableMeetingsFeature, settingsModel) INIT_CORE_MEMBER(DisableMeetingsFeature, settingsModel)
INIT_CORE_MEMBER(DisableBroadcastFeature, settingsModel) INIT_CORE_MEMBER(DisableBroadcastFeature, settingsModel)
INIT_CORE_MEMBER(HideSettings, settingsModel) INIT_CORE_MEMBER(HideSettings, settingsModel)
INIT_CORE_MEMBER(HideAccountSettings, settingsModel) INIT_CORE_MEMBER(HideAccountSettings, settingsModel)
INIT_CORE_MEMBER(HideFps, settingsModel)
INIT_CORE_MEMBER(DisableCallRecordings, settingsModel) INIT_CORE_MEMBER(DisableCallRecordings, settingsModel)
INIT_CORE_MEMBER(AssistantHideCreateAccount, settingsModel) INIT_CORE_MEMBER(AssistantHideCreateAccount, settingsModel)
INIT_CORE_MEMBER(AssistantHideCreateAccount, settingsModel)
INIT_CORE_MEMBER(AssistantDisableQrCode, settingsModel) INIT_CORE_MEMBER(AssistantDisableQrCode, settingsModel)
INIT_CORE_MEMBER(AssistantHideThirdPartyAccount, settingsModel) INIT_CORE_MEMBER(AssistantHideThirdPartyAccount, settingsModel)
@ -129,7 +140,6 @@ SettingsCore::SettingsCore(QObject *parent) : QObject(parent) {
INIT_CORE_MEMBER(AutoStart, settingsModel) INIT_CORE_MEMBER(AutoStart, settingsModel)
INIT_CORE_MEMBER(ExitOnClose, settingsModel) INIT_CORE_MEMBER(ExitOnClose, settingsModel)
INIT_CORE_MEMBER(SyncLdapContacts, settingsModel) INIT_CORE_MEMBER(SyncLdapContacts, settingsModel)
INIT_CORE_MEMBER(Ipv6Enabled, settingsModel)
INIT_CORE_MEMBER(ConfigLocale, settingsModel) INIT_CORE_MEMBER(ConfigLocale, settingsModel)
INIT_CORE_MEMBER(DownloadFolder, settingsModel) INIT_CORE_MEMBER(DownloadFolder, settingsModel)
@ -138,7 +148,6 @@ SettingsCore::SettingsCore(QObject *parent) : QObject(parent) {
INIT_CORE_MEMBER(CallToneIndicationsEnabled, settingsModel) INIT_CORE_MEMBER(CallToneIndicationsEnabled, settingsModel)
INIT_CORE_MEMBER(CommandLine, settingsModel) INIT_CORE_MEMBER(CommandLine, settingsModel)
INIT_CORE_MEMBER(DisableCommandLine, settingsModel) INIT_CORE_MEMBER(DisableCommandLine, settingsModel)
INIT_CORE_MEMBER(DisableCallForward, settingsModel)
INIT_CORE_MEMBER(CallForwardToAddress, settingsModel) INIT_CORE_MEMBER(CallForwardToAddress, settingsModel)
INIT_CORE_MEMBER(ThemeMainColor, settingsModel) INIT_CORE_MEMBER(ThemeMainColor, settingsModel)
@ -182,6 +191,7 @@ SettingsCore::SettingsCore(const SettingsCore &settingsCore) {
// Logs // Logs
mLogsEnabled = settingsCore.mLogsEnabled; mLogsEnabled = settingsCore.mLogsEnabled;
mFullLogsEnabled = settingsCore.mFullLogsEnabled; mFullLogsEnabled = settingsCore.mFullLogsEnabled;
mCrashReporterEnabled = settingsCore.mCrashReporterEnabled;
mLogsFolder = settingsCore.mLogsFolder; mLogsFolder = settingsCore.mLogsFolder;
mLogsEmail = settingsCore.mLogsEmail; mLogsEmail = settingsCore.mLogsEmail;
@ -207,10 +217,10 @@ SettingsCore::SettingsCore(const SettingsCore &settingsCore) {
mAssistantGoDirectlyToThirdPartySipAccountLogin = settingsCore.mAssistantGoDirectlyToThirdPartySipAccountLogin; mAssistantGoDirectlyToThirdPartySipAccountLogin = settingsCore.mAssistantGoDirectlyToThirdPartySipAccountLogin;
mAssistantThirdPartySipAccountDomain = settingsCore.mAssistantThirdPartySipAccountDomain; mAssistantThirdPartySipAccountDomain = settingsCore.mAssistantThirdPartySipAccountDomain;
mAssistantThirdPartySipAccountTransport = settingsCore.mAssistantThirdPartySipAccountTransport; mAssistantThirdPartySipAccountTransport = settingsCore.mAssistantThirdPartySipAccountTransport;
mAutoStart = settingsCore.mAutoStart;
mExitOnClose = settingsCore.mExitOnClose; mExitOnClose = settingsCore.mExitOnClose;
mSyncLdapContacts = settingsCore.mSyncLdapContacts; mSyncLdapContacts = settingsCore.mSyncLdapContacts;
mIpv6Enabled = settingsCore.mIpv6Enabled; mIpv6Enabled = settingsCore.mIpv6Enabled;
mAutoStart = settingsCore.mAutoStart;
mConfigLocale = settingsCore.mConfigLocale; mConfigLocale = settingsCore.mConfigLocale;
mDownloadFolder = settingsCore.mDownloadFolder; mDownloadFolder = settingsCore.mDownloadFolder;
mShortcutCount = settingsCore.mShortcutCount; mShortcutCount = settingsCore.mShortcutCount;
@ -218,7 +228,7 @@ SettingsCore::SettingsCore(const SettingsCore &settingsCore) {
mCallToneIndicationsEnabled = settingsCore.mCallToneIndicationsEnabled; mCallToneIndicationsEnabled = settingsCore.mCallToneIndicationsEnabled;
mCommandLine = settingsCore.mCommandLine; mCommandLine = settingsCore.mCommandLine;
mDisableCommandLine = settingsCore.mDisableCommandLine; mDisableCommandLine = settingsCore.mDisableCommandLine;
mDisableCallForward = settingsCore.mDisableCallForward; mCallForwardToAddress = settingsCore.mCallForwardToAddress;
mDefaultDomain = settingsCore.mDefaultDomain; mDefaultDomain = settingsCore.mDefaultDomain;
mShowAccountDevices = settingsCore.mShowAccountDevices; mShowAccountDevices = settingsCore.mShowAccountDevices;
@ -230,10 +240,121 @@ SettingsCore::SettingsCore(const SettingsCore &settingsCore) {
SettingsCore::~SettingsCore() { SettingsCore::~SettingsCore() {
} }
void SettingsCore::reloadSettings() {
mustBeInLinphoneThread(getClassName());
auto settingsModel = SettingsModel::getInstance();
assert(settingsModel);
// Security
setVfsEnabled(settingsModel->getVfsEnabled());
// Call
setVideoEnabled(settingsModel->getVideoEnabled());
setEchoCancellationEnabled(settingsModel->getEchoCancellationEnabled());
setAutoDownloadReceivedFiles(settingsModel->getAutoDownloadReceivedFiles());
setAutomaticallyRecordCallsEnabled(settingsModel->getAutomaticallyRecordCallsEnabled());
setRingtone(settingsModel->getRingtone());
// Network
setIpv6Enabled(settingsModel->getIpv6Enabled());
// Advanced
setAutoStart(settingsModel->getAutoStart());
setHideFps(settingsModel->getHideFps());
// Audio
setCaptureDevices(settingsModel->getCaptureDevices());
setPlaybackDevices(settingsModel->getPlaybackDevices());
setRingerDevices(settingsModel->getRingerDevices());
setCaptureDevice(settingsModel->getCaptureDevice());
setPlaybackDevice(settingsModel->getPlaybackDevice());
setRingerDevice(settingsModel->getRingerDevice());
setConferenceLayout(
LinphoneEnums::toVariant(LinphoneEnums::fromLinphone(settingsModel->getDefaultConferenceLayout())));
setMediaEncryption(
LinphoneEnums::toVariant(LinphoneEnums::fromLinphone(settingsModel->getDefaultMediaEncryption())));
setMediaEncryptionMandatory(settingsModel->getMediaEncryptionMandatory());
setCreateEndToEndEncryptedMeetingsAndGroupCalls(settingsModel->getCreateEndToEndEncryptedMeetingsAndGroupCalls());
setCaptureGain(settingsModel->getCaptureGain());
setPlaybackGain(settingsModel->getPlaybackGain());
// Video
setVideoDevice(settingsModel->getVideoDevice());
setVideoDevices(settingsModel->getVideoDevices());
// Logs
setLogsEnabled(settingsModel->getLogsEnabled());
setFullLogsEnabled(settingsModel->getFullLogsEnabled());
setCrashReporterEnabled(settingsModel->getCrashReporterEnabled());
setLogsFolder(settingsModel->getLogsFolder());
mLogsEmail = settingsModel->getLogsEmail();
// DND
setDndEnabled(settingsModel->dndEnabled());
mDefaultDomain = settingsModel->getDefaultDomain();
auto currentAccount = CoreModel::getInstance()->getCore()->getDefaultAccount();
if (currentAccount) {
auto accountDomain = Utils::coreStringToAppString(currentAccount->getParams()->getDomain());
setShowAccountDevices(accountDomain == mDefaultDomain);
}
// Chat
mEmojiFont = settingsModel->getEmojiFont();
mTextMessageFont = settingsModel->getTextMessageFont();
// Check for update
mIsCheckForUpdateAvailable = settingsModel->isCheckForUpdateAvailable();
setDisableChatFeature(settingsModel->getDisableChatFeature());
setDisableMeetingsFeature(settingsModel->getDisableMeetingsFeature());
setDisableBroadcastFeature(settingsModel->getDisableBroadcastFeature());
setHideSettings(settingsModel->getHideSettings());
setHideAccountSettings(settingsModel->getHideAccountSettings());
setDisableCallRecordings(settingsModel->getDisableCallRecordings());
setAssistantHideCreateAccount(settingsModel->getAssistantHideCreateAccount());
setAssistantDisableQrCode(settingsModel->getAssistantDisableQrCode());
setAssistantHideThirdPartyAccount(settingsModel->getAssistantHideThirdPartyAccount());
setHideSipAddresses(settingsModel->getHideSipAddresses());
setDarkModeAllowed(settingsModel->getDarkModeAllowed());
setMaxAccount(settingsModel->getMaxAccount());
setAssistantGoDirectlyToThirdPartySipAccountLogin(
settingsModel->getAssistantGoDirectlyToThirdPartySipAccountLogin());
setAssistantGoDirectlyToThirdPartySipAccountLogin(
settingsModel->getAssistantGoDirectlyToThirdPartySipAccountLogin());
setAssistantThirdPartySipAccountDomain(settingsModel->getAssistantThirdPartySipAccountDomain());
setAssistantThirdPartySipAccountTransport(settingsModel->getAssistantThirdPartySipAccountTransport());
setAutoStart(settingsModel->getAutoStart());
setExitOnClose(settingsModel->getExitOnClose());
setSyncLdapContacts(settingsModel->getSyncLdapContacts());
setConfigLocale(settingsModel->getConfigLocale());
setDownloadFolder(settingsModel->getDownloadFolder());
setCallToneIndicationsEnabled(settingsModel->getCallToneIndicationsEnabled());
setCommandLine(settingsModel->getCommandLine());
setDisableCommandLine(settingsModel->getDisableCommandLine());
setCallForwardToAddress(settingsModel->getCallForwardToAddress());
setThemeMainColor(settingsModel->getThemeMainColor());
setThemeAboutPictureUrl(settingsModel->getThemeAboutPictureUrl());
}
void SettingsCore::setSelf(QSharedPointer<SettingsCore> me) { void SettingsCore::setSelf(QSharedPointer<SettingsCore> me) {
mustBeInLinphoneThread(getClassName()); mustBeInLinphoneThread(getClassName());
mSettingsModelConnection = SafeConnection<SettingsCore, SettingsModel>::create(me, SettingsModel::getInstance()); mSettingsModelConnection = SafeConnection<SettingsCore, SettingsModel>::create(me, SettingsModel::getInstance());
mSettingsModelConnection->makeConnectToModel(&SettingsModel::captureGraphRunningChanged, [this](bool running) {
mSettingsModelConnection->invokeToCore([this, running] {
mCaptureGraphRunning = running;
emit captureGraphRunningChanged(running);
});
});
// VFS // VFS
mSettingsModelConnection->makeConnectToModel(&SettingsModel::vfsEnabledChanged, [this](const bool enabled) { mSettingsModelConnection->makeConnectToModel(&SettingsModel::vfsEnabledChanged, [this](const bool enabled) {
mSettingsModelConnection->invokeToCore([this, enabled]() { setVfsEnabled(enabled); }); mSettingsModelConnection->invokeToCore([this, enabled]() { setVfsEnabled(enabled); });
@ -250,6 +371,31 @@ void SettingsCore::setSelf(QSharedPointer<SettingsCore> me) {
mSettingsModelConnection->invokeToCore([this, enabled]() { setEchoCancellationEnabled(enabled); }); mSettingsModelConnection->invokeToCore([this, enabled]() { setEchoCancellationEnabled(enabled); });
}); });
// IPV6
mSettingsModelConnection->makeConnectToModel(&SettingsModel::ipv6EnabledChanged, [this](const bool enabled) {
mSettingsModelConnection->invokeToCore([this, enabled]() { setIpv6Enabled(enabled); });
});
// Call Forward
mSettingsModelConnection->makeConnectToModel(
&SettingsModel::callForwardToAddressChanged, [this](const QString address) {
mSettingsModelConnection->invokeToCore([this, address]() { setCallForwardToAddress(address); });
});
// Hide FPS
mSettingsModelConnection->makeConnectToModel(&SettingsModel::hideFpsChanged, [this](const bool hide) {
mSettingsModelConnection->invokeToCore([this, hide]() { setHideFps(hide); });
});
// AutoStart
mSettingsModelConnection->makeConnectToModel(&SettingsModel::autoStartChanged, [this](const bool enabled) {
mSettingsModelConnection->invokeToCore([this, enabled]() {
bool emitSignal = mAutoStart != enabled;
setAutoStart(enabled);
if (emitSignal) emit autoStartChanged();
});
});
// Auto download incoming files // Auto download incoming files
mSettingsModelConnection->makeConnectToModel( mSettingsModelConnection->makeConnectToModel(
&SettingsModel::autoDownloadReceivedFilesChanged, [this](const bool enabled) { &SettingsModel::autoDownloadReceivedFilesChanged, [this](const bool enabled) {
@ -294,7 +440,7 @@ void SettingsCore::setSelf(QSharedPointer<SettingsCore> me) {
}); });
}); });
mSettingsModelConnection->makeConnectToModel(&SettingsModel::playbackGainChanged, [this](const float value) { mSettingsModelConnection->makeConnectToModel(&SettingsModel::playbackGainChanged, [this](const float value) {
mSettingsModelConnection->invokeToCore([this, value]() { setPlaybackGain(value); }); mSettingsModelConnection->invokeToCore([this, value]() { setPlaybackGainFromModel(value); });
}); });
mSettingsModelConnection->makeConnectToCore(&SettingsCore::lSetCaptureGain, [this](const float value) { mSettingsModelConnection->makeConnectToCore(&SettingsCore::lSetCaptureGain, [this](const float value) {
@ -304,7 +450,7 @@ void SettingsCore::setSelf(QSharedPointer<SettingsCore> me) {
}); });
}); });
mSettingsModelConnection->makeConnectToModel(&SettingsModel::captureGainChanged, [this](const float value) { mSettingsModelConnection->makeConnectToModel(&SettingsModel::captureGainChanged, [this](const float value) {
mSettingsModelConnection->invokeToCore([this, value]() { setCaptureGain(value); }); mSettingsModelConnection->invokeToCore([this, value]() { setCaptureGainFromModel(value); });
}); });
mSettingsModelConnection->makeConnectToModel(&SettingsModel::micVolumeChanged, [this](const float value) { mSettingsModelConnection->makeConnectToModel(&SettingsModel::micVolumeChanged, [this](const float value) {
@ -377,7 +523,10 @@ void SettingsCore::setSelf(QSharedPointer<SettingsCore> me) {
// Logs // Logs
mSettingsModelConnection->makeConnectToModel(&SettingsModel::logsEnabledChanged, [this](const bool status) { mSettingsModelConnection->makeConnectToModel(&SettingsModel::logsEnabledChanged, [this](const bool status) {
mSettingsModelConnection->invokeToCore([this, status]() { setLogsEnabled(status); }); mSettingsModelConnection->invokeToCore([this, status]() {
setCrashReporterEnabled(status);
setLogsEnabled(status);
});
}); });
mSettingsModelConnection->makeConnectToModel(&SettingsModel::fullLogsEnabledChanged, [this](const bool status) { mSettingsModelConnection->makeConnectToModel(&SettingsModel::fullLogsEnabledChanged, [this](const bool status) {
@ -440,8 +589,6 @@ void SettingsCore::setSelf(QSharedPointer<SettingsCore> me) {
ExitOnClose) ExitOnClose)
DEFINE_CORE_GETSET_CONNECT(mSettingsModelConnection, SettingsCore, SettingsModel, settingsModel, bool, DEFINE_CORE_GETSET_CONNECT(mSettingsModelConnection, SettingsCore, SettingsModel, settingsModel, bool,
syncLdapContacts, SyncLdapContacts) syncLdapContacts, SyncLdapContacts)
DEFINE_CORE_GETSET_CONNECT(mSettingsModelConnection, SettingsCore, SettingsModel, settingsModel, bool, ipv6Enabled,
Ipv6Enabled)
DEFINE_CORE_GETSET_CONNECT(mSettingsModelConnection, SettingsCore, SettingsModel, settingsModel, QString, DEFINE_CORE_GETSET_CONNECT(mSettingsModelConnection, SettingsCore, SettingsModel, settingsModel, QString,
configLocale, ConfigLocale) configLocale, ConfigLocale)
DEFINE_CORE_GETSET_CONNECT(mSettingsModelConnection, SettingsCore, SettingsModel, settingsModel, QString, DEFINE_CORE_GETSET_CONNECT(mSettingsModelConnection, SettingsCore, SettingsModel, settingsModel, QString,
@ -456,10 +603,6 @@ void SettingsCore::setSelf(QSharedPointer<SettingsCore> me) {
commandLine, CommandLine) commandLine, CommandLine)
DEFINE_CORE_GETSET_CONNECT(mSettingsModelConnection, SettingsCore, SettingsModel, settingsModel, bool, DEFINE_CORE_GETSET_CONNECT(mSettingsModelConnection, SettingsCore, SettingsModel, settingsModel, bool,
disableCommandLine, DisableCommandLine) disableCommandLine, DisableCommandLine)
DEFINE_CORE_GETSET_CONNECT(mSettingsModelConnection, SettingsCore, SettingsModel, settingsModel, bool,
disableCallForward, DisableCallForward)
DEFINE_CORE_GETSET_CONNECT(mSettingsModelConnection, SettingsCore, SettingsModel, settingsModel, QString,
callForwardToAddress, CallForwardToAddress)
DEFINE_CORE_GETSET_CONNECT(mSettingsModelConnection, SettingsCore, SettingsModel, settingsModel, QString, DEFINE_CORE_GETSET_CONNECT(mSettingsModelConnection, SettingsCore, SettingsModel, settingsModel, QString,
themeAboutPictureUrl, ThemeAboutPictureUrl) themeAboutPictureUrl, ThemeAboutPictureUrl)
DEFINE_CORE_GETSET_CONNECT(mSettingsModelConnection, SettingsCore, SettingsModel, settingsModel, QString, DEFINE_CORE_GETSET_CONNECT(mSettingsModelConnection, SettingsCore, SettingsModel, settingsModel, QString,
@ -525,6 +668,7 @@ void SettingsCore::reset(const SettingsCore &settingsCore) {
// Logs // Logs
setLogsEnabled(settingsCore.mLogsEnabled); setLogsEnabled(settingsCore.mLogsEnabled);
setFullLogsEnabled(settingsCore.mFullLogsEnabled); setFullLogsEnabled(settingsCore.mFullLogsEnabled);
setCrashReporterEnabled(settingsCore.mCrashReporterEnabled);
setLogsFolder(settingsCore.mLogsFolder); setLogsFolder(settingsCore.mLogsFolder);
// DND // DND
@ -549,13 +693,14 @@ void SettingsCore::reset(const SettingsCore &settingsCore) {
setAssistantGoDirectlyToThirdPartySipAccountLogin(settingsCore.mAssistantGoDirectlyToThirdPartySipAccountLogin); setAssistantGoDirectlyToThirdPartySipAccountLogin(settingsCore.mAssistantGoDirectlyToThirdPartySipAccountLogin);
setAssistantThirdPartySipAccountDomain(settingsCore.mAssistantThirdPartySipAccountDomain); setAssistantThirdPartySipAccountDomain(settingsCore.mAssistantThirdPartySipAccountDomain);
setAssistantThirdPartySipAccountTransport(settingsCore.mAssistantThirdPartySipAccountTransport); setAssistantThirdPartySipAccountTransport(settingsCore.mAssistantThirdPartySipAccountTransport);
setAutoStart(settingsCore.mAutoStart);
setExitOnClose(settingsCore.mExitOnClose); setExitOnClose(settingsCore.mExitOnClose);
setSyncLdapContacts(settingsCore.mSyncLdapContacts); setSyncLdapContacts(settingsCore.mSyncLdapContacts);
setCardDAVMinCharForResearch(settingsCore.mCardDAVMinCharForResearch); setCardDAVMinCharForResearch(settingsCore.mCardDAVMinCharForResearch);
setIpv6Enabled(settingsCore.mIpv6Enabled); setIpv6Enabled(settingsCore.mIpv6Enabled);
setAutoStart(settingsCore.mAutoStart);
setConfigLocale(settingsCore.mConfigLocale); setConfigLocale(settingsCore.mConfigLocale);
setDownloadFolder(settingsCore.mDownloadFolder); setDownloadFolder(settingsCore.mDownloadFolder);
setCallForwardToAddress(settingsCore.mCallForwardToAddress);
} }
QString SettingsCore::getConfigPath(const QCommandLineParser &parser) { QString SettingsCore::getConfigPath(const QCommandLineParser &parser) {
@ -614,6 +759,36 @@ void SettingsCore::setEchoCancellationEnabled(bool enabled) {
} }
} }
void SettingsCore::setIpv6Enabled(bool enabled) {
if (mIpv6Enabled != enabled) {
mIpv6Enabled = enabled;
emit ipv6EnabledChanged();
setIsSaved(false);
}
}
void SettingsCore::setAutoStart(bool enabled) {
if (mAutoStart != enabled) {
mAutoStart = enabled;
setIsSaved(false);
}
}
void SettingsCore::setCallForwardToAddress(QString address) {
if (mCallForwardToAddress != address) {
mCallForwardToAddress = address;
setIsSaved(false);
}
}
void SettingsCore::setHideFps(bool hide) {
if (mHideFps != hide) {
mHideFps = hide;
emit hideFpsChanged();
setIsSaved(false);
}
}
void SettingsCore::setAutoDownloadReceivedFiles(bool enabled) { void SettingsCore::setAutoDownloadReceivedFiles(bool enabled) {
if (mAutoDownloadReceivedFiles != enabled) { if (mAutoDownloadReceivedFiles != enabled) {
mAutoDownloadReceivedFiles = enabled; mAutoDownloadReceivedFiles = enabled;
@ -706,7 +881,7 @@ bool SettingsCore::isSaved() const {
void SettingsCore::setIsSaved(bool saved) { void SettingsCore::setIsSaved(bool saved) {
if (mIsSaved != saved) { if (mIsSaved != saved) {
mIsSaved = saved; mIsSaved = saved;
emit isSavedChanged(); emit isSavedChanged(saved);
} }
} }
@ -757,6 +932,13 @@ void SettingsCore::setCaptureGain(float gain) {
} }
} }
void SettingsCore::setCaptureGainFromModel(float gain) {
if (mCaptureGain != gain) {
mCaptureGain = gain;
emit captureGainChanged(gain);
}
}
QVariantMap SettingsCore::getConferenceLayout() const { QVariantMap SettingsCore::getConferenceLayout() const {
return mConferenceLayout; return mConferenceLayout;
} }
@ -801,6 +983,13 @@ void SettingsCore::setPlaybackGain(float gain) {
} }
} }
void SettingsCore::setPlaybackGainFromModel(float gain) {
if (mPlaybackGain != gain) {
mPlaybackGain = gain;
emit playbackGainChanged(gain);
}
}
QVariantMap SettingsCore::getCaptureDevice() const { QVariantMap SettingsCore::getCaptureDevice() const {
return mCaptureDevice; return mCaptureDevice;
} }
@ -929,6 +1118,18 @@ void SettingsCore::setFullLogsEnabled(bool enabled) {
} }
} }
bool SettingsCore::getCrashReporterEnabled() const {
return mCrashReporterEnabled;
}
void SettingsCore::setCrashReporterEnabled(bool enabled) {
if (mCrashReporterEnabled != enabled) {
mCrashReporterEnabled = enabled;
emit crashReporterEnabledChanged();
setIsSaved(false);
}
}
void SettingsCore::setRingtone(QString path) { void SettingsCore::setRingtone(QString path) {
if (mRingtonePath != path) { if (mRingtonePath != path) {
mRingtonePath = path; mRingtonePath = path;
@ -998,10 +1199,6 @@ void SettingsCore::setShowAccountDevices(bool show) {
} }
} }
bool SettingsCore::getAutoStart() const {
return mAutoStart;
}
bool SettingsCore::getExitOnClose() const { bool SettingsCore::getExitOnClose() const {
return mExitOnClose; return mExitOnClose;
} }
@ -1028,7 +1225,9 @@ QString SettingsCore::getConfigLocale() const {
QString SettingsCore::getDownloadFolder() const { QString SettingsCore::getDownloadFolder() const {
auto path = mDownloadFolder; auto path = mDownloadFolder;
if (mDownloadFolder.isEmpty()) path = Paths::getDownloadDirPath(); if (mDownloadFolder.isEmpty()) path = Paths::getDownloadDirPath();
return QDir::cleanPath(path) + QDir::separator(); QString cleanPath = QDir::cleanPath(path);
if (!cleanPath.endsWith(QDir::separator())) cleanPath.append(QDir::separator());
return cleanPath;
} }
void SettingsCore::writeIntoModel(std::shared_ptr<SettingsModel> model) const { void SettingsCore::writeIntoModel(std::shared_ptr<SettingsModel> model) const {
@ -1068,6 +1267,7 @@ void SettingsCore::writeIntoModel(std::shared_ptr<SettingsModel> model) const {
// Logs // Logs
model->setLogsEnabled(mLogsEnabled); model->setLogsEnabled(mLogsEnabled);
model->setFullLogsEnabled(mFullLogsEnabled); model->setFullLogsEnabled(mFullLogsEnabled);
model->setCrashReporterEnabled(mLogsEnabled);
// UI // UI
model->setDisableChatFeature(mDisableChatFeature); model->setDisableChatFeature(mDisableChatFeature);
@ -1088,12 +1288,13 @@ void SettingsCore::writeIntoModel(std::shared_ptr<SettingsModel> model) const {
model->setAssistantGoDirectlyToThirdPartySipAccountLogin(mAssistantGoDirectlyToThirdPartySipAccountLogin); model->setAssistantGoDirectlyToThirdPartySipAccountLogin(mAssistantGoDirectlyToThirdPartySipAccountLogin);
model->setAssistantThirdPartySipAccountDomain(mAssistantThirdPartySipAccountDomain); model->setAssistantThirdPartySipAccountDomain(mAssistantThirdPartySipAccountDomain);
model->setAssistantThirdPartySipAccountTransport(mAssistantThirdPartySipAccountTransport); model->setAssistantThirdPartySipAccountTransport(mAssistantThirdPartySipAccountTransport);
model->setAutoStart(mAutoStart);
model->setExitOnClose(mExitOnClose); model->setExitOnClose(mExitOnClose);
model->setSyncLdapContacts(mSyncLdapContacts); model->setSyncLdapContacts(mSyncLdapContacts);
model->setIpv6Enabled(mIpv6Enabled); model->setIpv6Enabled(mIpv6Enabled);
model->setAutoStart(mAutoStart);
model->setConfigLocale(mConfigLocale); model->setConfigLocale(mConfigLocale);
model->setDownloadFolder(mDownloadFolder); model->setDownloadFolder(mDownloadFolder);
model->setCallForwardToAddress(mCallForwardToAddress);
} }
void SettingsCore::writeFromModel(const std::shared_ptr<SettingsModel> &model) { void SettingsCore::writeFromModel(const std::shared_ptr<SettingsModel> &model) {
@ -1112,6 +1313,9 @@ void SettingsCore::writeFromModel(const std::shared_ptr<SettingsModel> &model) {
mRingtoneFileName = mRingtoneFileName =
ringtone.exists() ? ringtone.fileName() : mRingtonePath.right(mRingtonePath.lastIndexOf(QDir::separator())); ringtone.exists() ? ringtone.fileName() : mRingtonePath.right(mRingtonePath.lastIndexOf(QDir::separator()));
// Advanced
mAutoStart = model->getAutoStart();
// Chat // Chat
mAutoDownloadReceivedFiles = model->getAutoDownloadReceivedFiles(); mAutoDownloadReceivedFiles = model->getAutoDownloadReceivedFiles();
@ -1140,9 +1344,13 @@ void SettingsCore::writeFromModel(const std::shared_ptr<SettingsModel> &model) {
// Logs // Logs
mLogsEnabled = model->getLogsEnabled(); mLogsEnabled = model->getLogsEnabled();
mFullLogsEnabled = model->getFullLogsEnabled(); mFullLogsEnabled = model->getFullLogsEnabled();
mCrashReporterEnabled = model->getCrashReporterEnabled();
mLogsFolder = model->getLogsFolder(); mLogsFolder = model->getLogsFolder();
mLogsEmail = model->getLogsEmail(); mLogsEmail = model->getLogsEmail();
// Check update
mIsCheckForUpdateAvailable = model->isCheckForUpdateAvailable();
// UI // UI
mDisableChatFeature = model->getDisableChatFeature(); mDisableChatFeature = model->getDisableChatFeature();
mDisableMeetingsFeature = model->getDisableMeetingsFeature(); mDisableMeetingsFeature = model->getDisableMeetingsFeature();
@ -1167,13 +1375,20 @@ void SettingsCore::writeFromModel(const std::shared_ptr<SettingsModel> &model) {
mSyncLdapContacts = model->getSyncLdapContacts(); mSyncLdapContacts = model->getSyncLdapContacts();
mCardDAVMinCharForResearch = model->getCardDAVMinCharResearch(); mCardDAVMinCharForResearch = model->getCardDAVMinCharResearch();
mIpv6Enabled = model->getIpv6Enabled(); mIpv6Enabled = model->getIpv6Enabled();
mAutoStart = model->getAutoStart();
mConfigLocale = model->getConfigLocale(); mConfigLocale = model->getConfigLocale();
mDownloadFolder = model->getDownloadFolder(); mDownloadFolder = model->getDownloadFolder();
mCallForwardToAddress = model->getCallForwardToAddress();
}
bool SettingsCore::isCheckForUpdateAvailable() const {
return mIsCheckForUpdateAvailable;
} }
void SettingsCore::save() { void SettingsCore::save() {
mustBeInMainThread(getClassName() + Q_FUNC_INFO); mustBeInMainThread(getClassName() + Q_FUNC_INFO);
SettingsCore *thisCopy = new SettingsCore(*this); SettingsCore *thisCopy = new SettingsCore(*this);
emit autoStartChanged();
if (SettingsModel::getInstance()) { if (SettingsModel::getInstance()) {
mSettingsModelConnection->invokeToModel([this, thisCopy] { mSettingsModelConnection->invokeToModel([this, thisCopy] {
mustBeInLinphoneThread(getClassName() + Q_FUNC_INFO); mustBeInLinphoneThread(getClassName() + Q_FUNC_INFO);

View file

@ -62,6 +62,17 @@ public:
Q_PROPERTY(QVariantMap playbackDevice READ getPlaybackDevice WRITE setPlaybackDevice NOTIFY playbackDeviceChanged) Q_PROPERTY(QVariantMap playbackDevice READ getPlaybackDevice WRITE setPlaybackDevice NOTIFY playbackDeviceChanged)
Q_PROPERTY(QVariantMap ringerDevice READ getRingerDevice WRITE setRingerDevice NOTIFY ringerDeviceChanged) Q_PROPERTY(QVariantMap ringerDevice READ getRingerDevice WRITE setRingerDevice NOTIFY ringerDeviceChanged)
// Call Forward
Q_PROPERTY(QString callForwardToAddress READ getCallForwardToAddress WRITE setCallForwardToAddress NOTIFY
callForwardToAddressChanged)
// Network
Q_PROPERTY(bool ipv6Enabled READ getIpv6Enabled WRITE setIpv6Enabled NOTIFY ipv6EnabledChanged)
Q_PROPERTY(bool hideFps READ getHideFps WRITE setHideFps NOTIFY hideFpsChanged)
// Advanced
Q_PROPERTY(bool autoStart READ getAutoStart WRITE setAutoStart NOTIFY autoStartChanged)
Q_PROPERTY( Q_PROPERTY(
QVariantMap conferenceLayout READ getConferenceLayout WRITE setConferenceLayout NOTIFY conferenceLayoutChanged) QVariantMap conferenceLayout READ getConferenceLayout WRITE setConferenceLayout NOTIFY conferenceLayoutChanged)
Q_PROPERTY( Q_PROPERTY(
@ -80,6 +91,8 @@ public:
Q_PROPERTY(bool logsEnabled READ getLogsEnabled WRITE setLogsEnabled NOTIFY logsEnabledChanged) Q_PROPERTY(bool logsEnabled READ getLogsEnabled WRITE setLogsEnabled NOTIFY logsEnabledChanged)
Q_PROPERTY(bool fullLogsEnabled READ getFullLogsEnabled WRITE setFullLogsEnabled NOTIFY fullLogsEnabledChanged) Q_PROPERTY(bool fullLogsEnabled READ getFullLogsEnabled WRITE setFullLogsEnabled NOTIFY fullLogsEnabledChanged)
Q_PROPERTY(bool crashReporterEnabled READ getCrashReporterEnabled WRITE setCrashReporterEnabled NOTIFY
crashReporterEnabledChanged)
Q_PROPERTY(QString logsEmail READ getLogsEmail) Q_PROPERTY(QString logsEmail READ getLogsEmail)
Q_PROPERTY(QString logsFolder READ getLogsFolder) Q_PROPERTY(QString logsFolder READ getLogsFolder)
Q_PROPERTY(QString ringtoneName READ getRingtoneFileName NOTIFY ringtoneChanged) Q_PROPERTY(QString ringtoneName READ getRingtoneFileName NOTIFY ringtoneChanged)
@ -96,6 +109,8 @@ public:
SettingsCore(const SettingsCore &settingsCore); SettingsCore(const SettingsCore &settingsCore);
virtual ~SettingsCore(); virtual ~SettingsCore();
void reloadSettings();
void setSelf(QSharedPointer<SettingsCore> me); void setSelf(QSharedPointer<SettingsCore> me);
void reset(const SettingsCore &settingsCore); void reset(const SettingsCore &settingsCore);
@ -144,9 +159,11 @@ public:
float getPlaybackGain() const; float getPlaybackGain() const;
void setPlaybackGain(float gain); void setPlaybackGain(float gain);
void setPlaybackGainFromModel(float gain);
float getCaptureGain() const; float getCaptureGain() const;
void setCaptureGain(float gain); void setCaptureGain(float gain);
void setCaptureGainFromModel(float gain);
QVariantList getCaptureDevices() const; QVariantList getCaptureDevices() const;
void setCaptureDevices(QVariantList devices); void setCaptureDevices(QVariantList devices);
@ -193,11 +210,38 @@ public:
Q_INVOKABLE void closeCallSettings(); Q_INVOKABLE void closeCallSettings();
Q_INVOKABLE void updateMicVolume() const; Q_INVOKABLE void updateMicVolume() const;
// Call Forward. --------------------------------------------------------------------
QString getCallForwardToAddress() {
return mCallForwardToAddress;
}
void setCallForwardToAddress(QString address);
// Network. --------------------------------------------------------------------
bool getIpv6Enabled() {
return mIpv6Enabled;
}
void setIpv6Enabled(bool enabled);
// Advanced. --------------------------------------------------------------------
bool getAutoStart() {
return mAutoStart;
}
void setAutoStart(bool enabled);
bool getHideFps() {
return mHideFps;
}
void setHideFps(bool hide);
bool getLogsEnabled() const; bool getLogsEnabled() const;
void setLogsEnabled(bool enabled); void setLogsEnabled(bool enabled);
bool getFullLogsEnabled() const; bool getFullLogsEnabled() const;
void setFullLogsEnabled(bool enabled); void setFullLogsEnabled(bool enabled);
bool getCrashReporterEnabled() const;
void setCrashReporterEnabled(bool enabled);
void setRingtone(QString path); void setRingtone(QString path);
QString getRingtoneFileName() const; QString getRingtoneFileName() const;
@ -218,6 +262,7 @@ public:
bool getCardDAVMinCharForResearch() const; bool getCardDAVMinCharForResearch() const;
void setCardDAVMinCharForResearch(int min); void setCardDAVMinCharForResearch(int min);
bool isCheckForUpdateAvailable() const;
Q_INVOKABLE void save(); Q_INVOKABLE void save();
Q_INVOKABLE void undo(); Q_INVOKABLE void undo();
@ -226,7 +271,6 @@ public:
DECLARE_CORE_GETSET_MEMBER(bool, disableBroadcastFeature, DisableBroadcastFeature) DECLARE_CORE_GETSET_MEMBER(bool, disableBroadcastFeature, DisableBroadcastFeature)
DECLARE_CORE_GETSET_MEMBER(bool, hideSettings, HideSettings) DECLARE_CORE_GETSET_MEMBER(bool, hideSettings, HideSettings)
DECLARE_CORE_GETSET_MEMBER(bool, hideAccountSettings, HideAccountSettings) DECLARE_CORE_GETSET_MEMBER(bool, hideAccountSettings, HideAccountSettings)
DECLARE_CORE_GETSET_MEMBER(bool, hideFps, HideFps)
DECLARE_CORE_GETSET_MEMBER(bool, disableCallRecordings, DisableCallRecordings) DECLARE_CORE_GETSET_MEMBER(bool, disableCallRecordings, DisableCallRecordings)
DECLARE_CORE_GETSET_MEMBER(bool, assistantHideCreateAccount, AssistantHideCreateAccount) DECLARE_CORE_GETSET_MEMBER(bool, assistantHideCreateAccount, AssistantHideCreateAccount)
DECLARE_CORE_GETSET_MEMBER(bool, assistantDisableQrCode, AssistantDisableQrCode) DECLARE_CORE_GETSET_MEMBER(bool, assistantDisableQrCode, AssistantDisableQrCode)
@ -239,10 +283,8 @@ public:
AssistantGoDirectlyToThirdPartySipAccountLogin) AssistantGoDirectlyToThirdPartySipAccountLogin)
DECLARE_CORE_GETSET_MEMBER(QString, assistantThirdPartySipAccountDomain, AssistantThirdPartySipAccountDomain) DECLARE_CORE_GETSET_MEMBER(QString, assistantThirdPartySipAccountDomain, AssistantThirdPartySipAccountDomain)
DECLARE_CORE_GETSET_MEMBER(QString, assistantThirdPartySipAccountTransport, AssistantThirdPartySipAccountTransport) DECLARE_CORE_GETSET_MEMBER(QString, assistantThirdPartySipAccountTransport, AssistantThirdPartySipAccountTransport)
DECLARE_CORE_GETSET(bool, autoStart, AutoStart)
DECLARE_CORE_GETSET(bool, exitOnClose, ExitOnClose) DECLARE_CORE_GETSET(bool, exitOnClose, ExitOnClose)
DECLARE_CORE_GETSET(bool, syncLdapContacts, SyncLdapContacts) DECLARE_CORE_GETSET(bool, syncLdapContacts, SyncLdapContacts)
DECLARE_CORE_GETSET_MEMBER(bool, ipv6Enabled, Ipv6Enabled)
DECLARE_CORE_GETSET(QString, configLocale, ConfigLocale) DECLARE_CORE_GETSET(QString, configLocale, ConfigLocale)
DECLARE_CORE_GETSET(QString, downloadFolder, DownloadFolder) DECLARE_CORE_GETSET(QString, downloadFolder, DownloadFolder)
// Read-only // Read-only
@ -251,8 +293,6 @@ public:
DECLARE_CORE_GETSET_MEMBER(bool, callToneIndicationsEnabled, CallToneIndicationsEnabled) DECLARE_CORE_GETSET_MEMBER(bool, callToneIndicationsEnabled, CallToneIndicationsEnabled)
DECLARE_CORE_GETSET_MEMBER(bool, disableCommandLine, DisableCommandLine) DECLARE_CORE_GETSET_MEMBER(bool, disableCommandLine, DisableCommandLine)
DECLARE_CORE_GETSET_MEMBER(QString, commandLine, CommandLine) DECLARE_CORE_GETSET_MEMBER(QString, commandLine, CommandLine)
DECLARE_CORE_GETSET_MEMBER(bool, disableCallForward, DisableCallForward)
DECLARE_CORE_GETSET_MEMBER(QString, callForwardToAddress, CallForwardToAddress)
DECLARE_CORE_GET_CONSTANT(QFont, emojiFont, EmojiFont) DECLARE_CORE_GET_CONSTANT(QFont, emojiFont, EmojiFont)
DECLARE_CORE_GET_CONSTANT(QFont, textMessageFont, TextMessageFont) DECLARE_CORE_GET_CONSTANT(QFont, textMessageFont, TextMessageFont)
// Theme // Theme
@ -282,6 +322,17 @@ signals:
void captureDevicesChanged(const QVariantList &devices); void captureDevicesChanged(const QVariantList &devices);
void playbackDevicesChanged(const QVariantList &devices); void playbackDevicesChanged(const QVariantList &devices);
void ringerDevicesChanged(const QVariantList &devices); void ringerDevicesChanged(const QVariantList &devices);
// Network
void ipv6EnabledChanged();
// Call Forward
void callForwardToAddressChanged();
// Advanced
void autoStartChanged();
void hideFpsChanged();
void conferenceLayoutsChanged(const QVariantList &layouts); void conferenceLayoutsChanged(const QVariantList &layouts);
void mediaEncryptionsChanged(const QVariantList &encryptions); void mediaEncryptionsChanged(const QVariantList &encryptions);
@ -302,7 +353,7 @@ signals:
void createEndToEndEncryptedMeetingsAndGroupCallsChanged(bool endtoend); void createEndToEndEncryptedMeetingsAndGroupCallsChanged(bool endtoend);
void isSavedChanged(); void isSavedChanged(bool saved);
void lSetPlaybackDevice(QVariantMap device); void lSetPlaybackDevice(QVariantMap device);
void playbackDeviceChanged(const QVariantMap &device); void playbackDeviceChanged(const QVariantMap &device);
@ -318,6 +369,7 @@ signals:
void logsEnabledChanged(); void logsEnabledChanged();
void fullLogsEnabledChanged(); void fullLogsEnabledChanged();
void crashReporterEnabledChanged();
void logsUploadTerminated(bool status, QString url); void logsUploadTerminated(bool status, QString url);
void logsFolderChanged(const QString &folder); void logsFolderChanged(const QString &folder);
@ -380,9 +432,20 @@ private:
float mPlaybackGain; float mPlaybackGain;
int mEchoCancellationCalibration; int mEchoCancellationCalibration;
// Network
bool mIpv6Enabled;
// Call Forward
QString mCallForwardToAddress;
// Advanced
bool mAutoStart;
bool mHideFps;
// Debug logs // Debug logs
bool mLogsEnabled; bool mLogsEnabled;
bool mFullLogsEnabled; bool mFullLogsEnabled;
bool mCrashReporterEnabled;
QString mLogsFolder; QString mLogsFolder;
QString mLogsEmail; QString mLogsEmail;
@ -401,6 +464,9 @@ private:
// CardDAV // CardDAV
int mCardDAVMinCharForResearch = 0; int mCardDAVMinCharForResearch = 0;
// Check update
bool mIsCheckForUpdateAvailable = false;
DECLARE_ABSTRACT_OBJECT DECLARE_ABSTRACT_OBJECT
}; };
#endif #endif

View file

@ -296,7 +296,7 @@ bool SingleApplicationPrivate::writeConfirmedFrame(int msecs, const QByteArray &
socket->write(msg); socket->write(msg);
socket->flush(); socket->flush();
bool result = socket->waitForReadyRead( msecs < 0 ? -1 : msecs); // await ack byte bool result = socket->waitForReadyRead(msecs < 0 ? -1 : msecs); // await ack byte
if (result) { if (result) {
socket->read(1); socket->read(1);
return true; return true;

View file

@ -54,8 +54,8 @@ private:
// class MAC_APPLICATION_MENU : public QObject{ // class MAC_APPLICATION_MENU : public QObject{
// QString forcedTranslation(){ // QString forcedTranslation(){
// return tr("About %1") + tr("Preferences…") + tr("Services") + tr("Hide %1") + tr("Hide Others") + tr("Show All") + // return tr("About %1") + tr("Preferences…") + tr("Services") + tr("Hide %1") + tr("Hide Others") + tr("Show All")
//tr("Quit %1"); //+ tr("Quit %1");
// } // }
// }; // };

View file

@ -2,6 +2,7 @@ list(APPEND _LINPHONEAPP_RC_FILES data/assistant/use-app-sip-account.rc
data/assistant/create-app-sip-account.rc data/assistant/create-app-sip-account.rc
data/assistant/use-other-sip-account.rc data/assistant/use-other-sip-account.rc
data/shaders/roundEffect.frag.qsb data/shaders/roundEffect.frag.qsb
data/shaders/opacityMask.frag.qsb
data/emoji/emoji.json data/emoji/emoji.json
) )

View file

@ -2,6 +2,7 @@
<config xmlns="http://www.linphone.org/xsds/lpconfig.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.linphone.org/xsds/lpconfig.xsd lpconfig.xsd"> <config xmlns="http://www.linphone.org/xsds/lpconfig.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.linphone.org/xsds/lpconfig.xsd lpconfig.xsd">
<section name="app"> <section name="app">
<entry name="auto_download_incoming_files_max_size" overwrite="true">10000000</entry> <entry name="auto_download_incoming_files_max_size" overwrite="true">10000000</entry>
<entry name="record_aware" overwrite="true">1</entry>
</section> </section>
<section name="proxy_default_values"> <section name="proxy_default_values">
<entry name="avpf" overwrite="true">1</entry> <entry name="avpf" overwrite="true">1</entry>
@ -35,11 +36,20 @@
</section> </section>
<section name="sip"> <section name="sip">
<entry name="rls_uri" overwrite="true">sips:rls@sip.linphone.org</entry> <entry name="rls_uri" overwrite="true">sips:rls@sip.linphone.org</entry>
<entry name="sip_tcp_port" overwrite="true">-2</entry>
<entry name="sip_udp_port" overwrite="true">-2</entry>
<entry name="sip_tls_port" overwrite="true">-2</entry>
</section> </section>
<section name="video"> <section name="video">
<entry name="automatically_accept" overwrite="true">1</entry> <entry name="automatically_accept" overwrite="true">1</entry>
<entry name="automatically_initiate" overwrite="true">0</entry> <entry name="automatically_initiate" overwrite="true">0</entry>
<entry name="automatically_accept_direction" overwrite="true">2</entry> <entry name="automatically_accept_direction" overwrite="true">2</entry>
<entry name="displaytype" overwrite="true">MSQOGL</entry>
<entry name="show_local" overwrite="true">0</entry>
</section>
<section name="sound">
<entry name="mic_gain_db" overwrite="true">0</entry>
<entry name="playback_gain_db" overwrite="true">0</entry>
</section> </section>
<section name="assistant"> <section name="assistant">
<entry name="domain" overwrite="true">sip.linphone.org</entry> <entry name="domain" overwrite="true">sip.linphone.org</entry>

View file

@ -2,6 +2,7 @@
<config xmlns="http://www.linphone.org/xsds/lpconfig.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.linphone.org/xsds/lpconfig.xsd lpconfig.xsd"> <config xmlns="http://www.linphone.org/xsds/lpconfig.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.linphone.org/xsds/lpconfig.xsd lpconfig.xsd">
<section name="app"> <section name="app">
<entry name="auto_download_incoming_files_max_size" overwrite="true">10000000</entry> <entry name="auto_download_incoming_files_max_size" overwrite="true">10000000</entry>
<entry name="record_aware" overwrite="true">1</entry>
</section> </section>
<section name="proxy_default_values"> <section name="proxy_default_values">
<entry name="avpf" overwrite="true">0</entry> <entry name="avpf" overwrite="true">0</entry>
@ -30,11 +31,20 @@
<section name="sip"> <section name="sip">
<entry name="use_rls_presence" overwrite="true"></entry> <entry name="use_rls_presence" overwrite="true"></entry>
<entry name="rls_uri" overwrite="true"></entry> <entry name="rls_uri" overwrite="true"></entry>
<entry name="sip_tcp_port" overwrite="true">-2</entry>
<entry name="sip_udp_port" overwrite="true">-2</entry>
<entry name="sip_tls_port" overwrite="true">-2</entry>
</section> </section>
<section name="video"> <section name="video">
<entry name="automatically_accept" overwrite="true">1</entry> <entry name="automatically_accept" overwrite="true">1</entry>
<entry name="automatically_initiate" overwrite="true">0</entry> <entry name="automatically_initiate" overwrite="true">0</entry>
<entry name="automatically_accept_direction" overwrite="true">2</entry> <entry name="automatically_accept_direction" overwrite="true">2</entry>
<entry name="displaytype" overwrite="true">MSQOGL</entry>
<entry name="show_local" overwrite="true">0</entry>
</section>
<section name="sound">
<entry name="mic_gain_db" overwrite="true">0</entry>
<entry name="playback_gain_db" overwrite="true">0</entry>
</section> </section>
<section name="assistant"> <section name="assistant">
<entry name="domain" overwrite="true"></entry> <entry name="domain" overwrite="true"></entry>

View file

@ -128,6 +128,20 @@
"smile" "smile"
] ]
}, },
{
"code": "1fae0",
"char": "🫠",
"name": "melting face",
"keywords": [
"face",
"melting",
"smile",
"embarrassed",
"overwhelmed",
"shame",
"dread"
]
},
{ {
"code": "1f609", "code": "1f609",
"char": "😉", "char": "😉",
@ -360,6 +374,30 @@
"surprise" "surprise"
] ]
}, },
{
"code": "1fae2",
"char": "🫢",
"name": "face with open eyes and hand over mouth",
"keywords": [
"face with open eyes hand over mouth",
"whoops",
"shock",
"gasp",
"sudden realization",
"surprise"
]
},
{
"code": "1fae3",
"char": "🫣",
"name": "face with peeking eye",
"keywords": [
"face with peeking eye",
"hide",
"hidden",
"eye"
]
},
{ {
"code": "1f92b", "code": "1f92b",
"char": "🤫", "char": "🤫",
@ -378,6 +416,17 @@
"thinking face" "thinking face"
] ]
}, },
{
"code": "1fae1",
"char": "🫡",
"name": "saluting face",
"keywords": [
"face",
"saluting face",
"respect",
"honor"
]
},
{ {
"code": "1f910", "code": "1f910",
"char": "🤐", "char": "🤐",
@ -437,6 +486,32 @@
"silent" "silent"
] ]
}, },
{
"code": "1fae5",
"char": "🫥",
"name": "dotted line face",
"keywords": [
"dotted line face",
"shy",
"embarrased",
"quiet",
"ignore"
]
},
{
"code": "1f636-200d-1f32b-feof",
"char": "😶‍🌫️",
"name": "face in clouds",
"keywords": [
"face in clouds",
"confused",
"embarrased",
"lost",
"daydreaming",
"overwhelmed",
"hazy"
]
},
{ {
"code": "1f60f", "code": "1f60f",
"char": "😏", "char": "😏",
@ -476,6 +551,20 @@
"grimace face" "grimace face"
] ]
}, },
{
"code": "1f62e-200d-1f4a8",
"char": "😮‍💨",
"name": "face exhaling",
"keywords": [
"face",
"face exhaling",
"relief",
"exhausted",
"frustrated",
"exasperation",
"calm"
]
},
{ {
"code": "1f925", "code": "1f925",
"char": "🤥", "char": "🤥",
@ -487,6 +576,39 @@
"lying face" "lying face"
] ]
}, },
{
"code": "1fae8",
"char": "🫨",
"name": "shaking face",
"keywords": [
"face",
"shaking",
"overwhelmed",
"excited",
"shocked",
"anxious"
]
},
{
"code": "1f642-200d-2194-fe0f",
"char": "🙂‍↔️",
"name": "head shaking horizontally",
"keywords": [
"head shaking horizontally",
"no"
]
},
{
"code": "1f642-200d-2195-fe0f",
"char": "🙂‍↕️",
"name": "head shaking vertically",
"keywords": [
"head shaking vertically",
"yes",
"support",
"confirm"
]
},
{ {
"code": "1f60c", "code": "1f60c",
"char": "😌", "char": "😌",
@ -536,6 +658,18 @@
"zzz" "zzz"
] ]
}, },
{
"code": "1fae9",
"char": "🫩",
"name": "face with bags under eyes",
"keywords": [
"face",
"sleeping",
"bags under eyes",
"tired",
"stressed"
]
},
{ {
"code": "1f637", "code": "1f637",
"char": "😷", "char": "😷",

View file

@ -0,0 +1,75 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 26.3.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_7" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 128 128" style="enable-background:new 0 0 128 128;" xml:space="preserve">
<g>
<radialGradient id="face_00000019647643363045288750000001315328106473507229_" cx="64" cy="3167.6001" r="56.9597" gradientTransform="matrix(1 0 0 1 0 -3106)" gradientUnits="userSpaceOnUse">
<stop offset="0.5" style="stop-color:#FDE030"/>
<stop offset="0.9188" style="stop-color:#F7C02B"/>
<stop offset="1" style="stop-color:#F4A223"/>
</radialGradient>
<path id="face_19_" style="fill:url(#face_00000019647643363045288750000001315328106473507229_);" d="M64,117.5
c-27.9,0-58-17.5-58-55.9S36.1,5.7,64,5.7c15.5,0,29.8,5.1,40.4,14.4C115.9,30.3,122,44.7,122,61.6s-6.1,31.2-17.6,41.4
C93.8,112.3,79.4,117.5,64,117.5z"/>
<path style="fill:#EB8F00;" d="M111.9,28.3c5.3,8.6,8.1,18.8,8.1,30.2c0,16.9-6.1,31.2-17.6,41.4c-10.6,9.3-25,14.5-40.4,14.5
c-18.1,0-37-7.3-48.2-22.9c10.8,17.7,31,25.9,50.2,25.9c15.4,0,29.8-5.2,40.4-14.5C115.9,92.8,122,78.5,122,61.6
C122,48.8,118.5,37.5,111.9,28.3z"/>
</g>
<g>
<g>
<g id="eyebrows_28_">
<path style="fill:#422B0D;" d="M44.7,30.8c0,0,1.9-1.1,3.5,0.3c0.9,0.7,1.4,2.1,0.4,3.1c-4,5-14.5,8-19.6,6.5
c-1-0.3-1.5-1.4-1.2-2.3c0.3-0.9,1.1-1.4,2-1.3C36.5,36.4,40.8,34.3,44.7,30.8z"/>
</g>
</g>
<g>
<g id="eyebrows_20_">
<path style="fill:#422B0D;" d="M69.9,30.3c0,0-1.9-1.1-3.5,0.3c-0.9,0.7-1.4,2.1-0.4,3.1c4,5,14.5,8,19.6,6.5
c1-0.3,1.5-1.4,1.2-2.3c-0.3-0.9-1.1-1.4-2-1.3C78.2,35.9,73.9,33.7,69.9,30.3z"/>
</g>
</g>
</g>
<g>
<g id="face-with-open-mouth_3_">
<path id="mouth_10_" style="fill:#422B0D;" d="M35.3,91.3c-0.4,3.5,3.4,3.9,3.4,3.9s28,1.2,36.8,1.2c5.2,0,4.3-5.2-0.8-9.1
c-3.1-2.3-8.1-4.8-19.8-4.8C46,82.5,35.8,87.4,35.3,91.3z"/>
</g>
<path style="fill:#F48FB1;" d="M46,95.5c0,0,4.6-3.5,11-3.6c7.8-0.1,14.6,4.5,14.6,4.5s-7.1,0.1-13.9-0.1S46,95.5,46,95.5z"/>
</g>
<g>
<g>
<g>
<path style="fill:#422B0D;" d="M46.1,57.6H25.3c-1.4,0-2.5-1.1-2.5-2.5s1.1-2.5,2.5-2.5h20.8c1.4,0,2.5,1.1,2.5,2.5
S47.4,57.6,46.1,57.6z"/>
</g>
<path style="fill:#422B0D;" d="M28.9,55.6c0,0.5-0.1,0.9-0.1,1.4c0,5.9,3.4,9.4,7,9.4s7-3.5,7-9.4c0-0.5,0-1-0.1-1.4H28.9z"/>
</g>
<g>
<g>
<path style="fill:#422B0D;" d="M89.3,57.6H68.5c-1.4,0-2.5-1.1-2.5-2.5s1.1-2.5,2.5-2.5h20.8c1.4,0,2.5,1.1,2.5,2.5
S90.7,57.6,89.3,57.6z"/>
</g>
<path style="fill:#422B0D;" d="M72.5,55.1c-0.1,0.6-0.1,1.3-0.1,2c0,5.9,3.4,9.4,7,9.4c3.7,0,7-3.5,7-9.4c0-0.7-0.1-1.4-0.1-2
H72.5z"/>
</g>
</g>
<path style="fill:#EB8F00;" d="M67.2,102.9c-2,0.4-3.6,0.5-5.3,0.6c-1.6,0.1-3.2,0.1-4.8,0s-3.1-0.2-4.7-0.4
c-1.6-0.2-3.2-0.4-5.2-0.8l-0.1-0.3c1.6-0.7,3.3-1.1,5-1.3c1.7-0.2,3.4-0.3,5.1-0.2c1.7,0.1,3.4,0.3,5.1,0.6
c1.7,0.3,3.3,0.8,4.9,1.4V102.9z"/>
<g>
<g>
<path style="fill:#FFFFFF;" d="M37.7,92.8c-2,0.3-4.1,0.5-6.1,0.7c-2.7,0.3-5.5,0.4-7.8-0.8c-1.1-0.6-2.1-1.4-3.2-2
c-2.7-1.4-6.1-1-8.9,0.4C9.3,92.4,8,94.5,7.3,97c-0.7,2.6,0.8,5.5,2.9,7c3.8,2.7,1.6,3.1,2.7,6.8c1.1,4,4.9,6.2,8.9,5
c1.4-0.4,2.8-1.4,4.2-1.7c1.6-0.3,2.9,0.7,4.4,0.6c3.3,0,5.1-2.6,5.5-5.9c0.2-1.6,0-3.2,0.3-4.8c0.9-4.9,5.3-8.2,8.9-11.6
c1.7-1.6-1.7-0.8-2.3-0.7C41.2,92.2,39.4,92.5,37.7,92.8z"/>
</g>
<path style="fill:#92CBEB;" d="M6.8,97c0.7-2.6,2.4-4.9,4.8-6.2c2.7-1.4,6.5-1.9,9.3-0.2c0.2,0.1,0.3,0.3,0.4,0.5
c-1.2-0.1-2.4-0.2-3.6-0.1c-1.7,0.2-3.5,0.7-4.7,1.9c-0.5,0.5-0.9,1-1.3,1.6c-0.9,1.5-1.5,3.3-1,5.1c0.3,1.3,1.5,2.8,3,3.1
c1.6,0.2,3-1.2,4.7-1c0.7,0.1,1.5,0.9,1.1,1.5c-0.2,0.4-0.7,0.4-1.2,0.5c-1.4,0.3-2.6,1.2-3.1,2.5c-0.3,0.6-0.4,1.3-0.4,2
c0,2.6,1.8,5.1,4.2,5.9c1.6,0.5,3.4,0.3,4.8-0.4c0.7-0.4,1.1-1.1,1.8-1.4c0.7-0.3,1.7-0.4,2.4-0.3c0.8,0,1.6,0.2,2.4,0.1
c1.9-0.2,3.3-1.5,4.1-3.1c0.9-1.9,0.6-3.7,0.8-5.8c0.2-1.8,1.1-3.4,2.2-4.8c1.1-1.3,2.3-2.5,3.7-3.6c0.7-0.6,1.5-1,2.2-1.4
c0.3-0.2,2-1.4,2.2-1.3c0.3,0.1,0.3,0.4-0.3,0.9c-3.6,3.4-8.1,6.7-8.9,11.6c-0.3,1.6-0.1,3.2-0.3,4.8c-0.3,3.3-2.7,6.8-6,6.8
c-1.6,0-3.2-0.9-4.8-0.6c-1.4,0.3-2.7,1.5-4.1,1.9c-4,1.2-8.6-1.9-9.7-5.9c-1-3.7,1.2-4.4-2.6-7.1C6.8,102.9,6,99.6,6.8,97z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 4.1 KiB

View file

@ -0,0 +1,73 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 24.1.2, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_3" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 128 128" style="enable-background:new 0 0 128 128;" xml:space="preserve">
<g>
<radialGradient id="face_1_" cx="64" cy="-2088.8999" r="56.9597" gradientTransform="matrix(1 0 0 -1 0 -2026)" gradientUnits="userSpaceOnUse">
<stop offset="0.5" style="stop-color:#FDE030"/>
<stop offset="0.9188" style="stop-color:#F7C02B"/>
<stop offset="1" style="stop-color:#F4A223"/>
</radialGradient>
<path id="face_18_" style="fill:url(#face_1_);" d="M64,118.8c-27.9,0-58-17.5-58-55.9S36.1,7,64,7c15.5,0,29.8,5.1,40.4,14.4
C115.9,31.6,122,46,122,62.9s-6.1,31.2-17.6,41.4C93.8,113.6,79.4,118.8,64,118.8z"/>
<path style="fill:#EB8F00;" d="M111.89,29.67c5.33,8.6,8.11,18.84,8.11,30.23c0,16.9-6.1,31.2-17.6,41.4
c-10.6,9.3-25,14.5-40.4,14.5c-18.06,0-37.04-7.35-48.18-22.94C24.58,110.52,44.81,118.8,64,118.8c15.4,0,29.8-5.2,40.4-14.5
C115.9,94.1,122,79.8,122,62.9C122,50.16,118.53,38.84,111.89,29.67z"/>
<radialGradient id="face_2_" cx="64" cy="-2088.8999" r="56.9597" gradientTransform="matrix(1 0 0 -1 0 -2026)" gradientUnits="userSpaceOnUse">
<stop offset="0.5" style="stop-color:#FDE030"/>
<stop offset="0.9188" style="stop-color:#F7C02B"/>
<stop offset="1" style="stop-color:#F4A223"/>
</radialGradient>
<path id="face_17_" style="fill:url(#face_2_);" d="M64,118.8c-27.9,0-58-17.5-58-55.9S36.1,7,64,7c15.5,0,29.8,5.1,40.4,14.4
C115.9,31.6,122,46,122,62.9s-6.1,31.2-17.6,41.4C93.8,113.6,79.4,118.8,64,118.8z"/>
<path style="fill:#EB8F00;" d="M111.89,29.67c5.33,8.6,8.11,18.84,8.11,30.23c0,16.9-6.1,31.2-17.6,41.4
c-10.6,9.3-25,14.5-40.4,14.5c-18.06,0-37.04-7.35-48.18-22.94C24.58,110.52,44.81,118.8,64,118.8c15.4,0,29.8-5.2,40.4-14.5
C115.9,94.1,122,79.8,122,62.9C122,50.16,118.53,38.84,111.89,29.67z"/>
</g>
<g id="prop_18_">
<g>
<g>
<path style="fill:#5F7AFF;" d="M38.53,48C38.58,48,38.55,48,38.53,48L38.53,48z"/>
</g>
</g>
<g>
<g>
<path style="fill:#5F7AFF;" d="M95.86,48C95.9,48,95.88,48,95.86,48L95.86,48z"/>
</g>
</g>
</g>
<g id="prop_17_">
<g>
<g>
<path style="fill:#5F7AFF;" d="M36.53,48C36.58,48,36.55,48,36.53,48L36.53,48z"/>
</g>
</g>
<g>
<g>
<path style="fill:#5F7AFF;" d="M93.86,48C93.9,48,93.88,48,93.86,48L93.86,48z"/>
</g>
</g>
</g>
<g>
<path style="fill:#422B0D;" d="M82.1,34.68c-8.45,2.31-13.06,9.49-11.62,18.67c0.88,5.66,6.55,16.63,21.59,13.31
c8.97-1.98,13.51-8.51,13.34-17.06c-0.01-0.53-0.35-0.84-0.45-0.86c0,0-0.21,0.2-0.55,1.47c-1.72,6.39-6.21,12.36-14.24,13.25
c-10.05,1.12-15.17-5.86-15.95-10.86c-1.03-6.65,2.35-12.47,8.89-14.26c3.87-1.06,7.63,0.04,10.32,3.02
c1.26,1.4,2.62,3.99,2.16,6.55c-1.27,6.99-7.76,6.89-9.99,5.23c-1.7-1.28-2.3-3.94,1.25-4.81c1.29-0.31,2.4-0.61,2.75-2.32
c0.23-1.09-0.67-2.2-1.64-2.69c-2.14-1.09-4.85-0.08-6.46,1.32c-1.73,1.5-2.45,3.85-1.99,6.44c0.36,2.02,2,4.66,4.83,6.07
c3.49,1.74,12.69,1.53,14.85-7.49c0.99-4.15-0.82-8.44-2.76-10.65C90.96,32.82,83.48,34.3,82.1,34.68z"/>
<path style="fill:#422B0D;" d="M45.9,66.85c8.45-2.31,13.06-9.49,11.62-18.67c-0.88-5.66-6.55-16.63-21.59-13.31
c-8.97,1.98-13.51,8.51-13.34,17.06c0.01,0.53,0.35,0.84,0.45,0.86c0,0,0.21-0.2,0.55-1.47c1.72-6.39,6.21-12.36,14.24-13.25
c10.05-1.12,15.17,5.86,15.95,10.86c1.03,6.65-2.35,12.47-8.89,14.26c-3.87,1.06-7.63-0.04-10.32-3.02
c-1.26-1.4-2.62-3.99-2.16-6.55c1.27-6.99,7.76-6.89,9.99-5.23c1.7,1.28,2.3,3.94-1.25,4.81c-1.29,0.31-2.4,0.61-2.75,2.32
c-0.23,1.09,0.67,2.2,1.64,2.69c2.14,1.09,4.85,0.08,6.46-1.32c1.73-1.5,2.45-3.85,1.99-6.44c-0.36-2.02-2-4.66-4.83-6.07
c-3.49-1.74-12.69-1.53-14.85,7.49c-0.99,4.15,0.82,8.44,2.76,10.65C37.04,68.7,44.52,67.23,45.9,66.85z"/>
</g>
<path style="fill:#422B0D;" d="M99.49,87.26c-1.49-1.72-6.05-4.81-14.33-0.76c-1.84,0.9-2.81,1.92-6.14,1.68
c-2.19-0.15-3.83-1.21-5.71-2.42c-2.39-1.54-5.1-3.26-9.3-3.43c-4.2,0.17-6.91,1.89-9.3,3.43c-1.89,1.22-3.52,2.27-5.71,2.42
c-3.33,0.24-4.39-0.62-6.23-1.52c-8.28-4.05-12.75-1.12-14.24,0.6c-1.33,1.54-0.43,3.18,2.02,1.43c1.51-1.08,4.22-2.45,10.35,1.01
c1.97,1.11,3.83,2.79,8.38,2.47c3.22-0.23,5.45-1.66,7.6-3.05c2.1-1.36,4.11-2.63,7.13-2.79c3.03,0.16,5.03,1.43,7.13,2.79
c2.15,1.39,4.38,2.83,7.6,3.05c4.55,0.32,6.32-1.52,8.29-2.63c6.13-3.46,8.93-1.93,10.44-0.85C99.91,90.44,100.82,88.8,99.49,87.26z
"/>
</svg>

After

Width:  |  Height:  |  Size: 4.4 KiB

View file

@ -0,0 +1,54 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 26.3.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_7" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 128 128" style="enable-background:new 0 0 128 128;" xml:space="preserve">
<radialGradient id="SVGID_1_" cx="65.1292" cy="68.0936" r="53.2369" gradientTransform="matrix(1 0 0 -1 0 130)" gradientUnits="userSpaceOnUse">
<stop offset="0.5" style="stop-color:#FDE030"/>
<stop offset="0.9188" style="stop-color:#F7C02B"/>
<stop offset="1" style="stop-color:#F4A223"/>
</radialGradient>
<path style="fill:url(#SVGID_1_);" d="M97.9,32.9c-0.4-0.9-0.7-1.9-1.5-2.5c-1-0.9-2.4-1.2-3.7-1.1c-4.2,0.5-10.5-0.1-14.7-3
c-4.1-2.9-7-6.8-8.8-10.6l-0.1-0.1c-5.3,5.7-14.4,7.2-21.4,4c-0.8,7-8.7,12.2-15.4,10c-1.5,6.6-5.8,13-12.1,15.6
c-3.3,1.4-6.1,2-8.4,2.2c-0.3,0.1-0.6,0.1-0.9,0.2c-0.9,4.1-1.4,8.5-1.4,13.2c0,1.5,0.2,4,0.3,5.8c4.3,0.9,9.7,3.6,14,11
c3.7,6.4,6.6,4.3,12.9,2.9c7.9-1.8,16.4-1.2,23.6,3.1c1.3,0.7,2.4,1.6,3.5,2.6c2.1-2.7,4.6-5.2,11.2-7.4c9.3-3.1,16.3,1.5,16.3,1.5
c3-6.3,7-12.3,12.7-16.5c3.6-2.7,9.6-4.2,14.3-4.6h0.1c-0.2-5.3-1-10.7-2.4-15.4C110.2,45,102.6,43.7,97.9,32.9z"/>
<path style="fill:#EB8F00;" d="M116.5,58.2c0,0.3,0,1.1,0,1.4c0.6-0.1,1.2-0.2,1.9-0.2c-0.2-5.3-1-10.7-2.4-15.4
c-0.4,0.1-0.7,0.1-1.1,0.1C116,48.5,116.5,53.2,116.5,58.2z"/>
<ellipse style="fill:#422B0D;" cx="42.6" cy="59.5" rx="7" ry="11.5"/>
<path style="fill:#422B0D;" d="M83.8,70.8c3.7,0,7-4.3,7-11.5s-3.4-11.5-7-11.5c-3.7,0-7,4.3-7,11.5C76.7,66.5,80.1,70.8,83.8,70.8z
"/>
<path style="fill:#FFFFFF;" d="M60.4,83.8c-7.2-4.3-15.7-4.9-23.6-3.1c-6.2,1.4-9.2,3.5-12.9-2.9C17.9,67.6,10,66.3,5.5,66.5
c-1.1,0-1.6,1.5-0.6,2.1c4,2.6,11,8.5,13.4,18.8c0,0-6.2,7.1-6.6,13.2c-0.1,0.9,1.1,1.4,1.7,0.8c5.3-5.1,21.2-14.5,32.2-10
C59.5,97,62,108.9,62.4,114.5c0.1,1.4,1.5,2.3,2.8,1.9c2.9-0.9,7.9-1.9,13.4-0.8c8.8,1.7,15.3,8.5,15.3,8.5
c0-10.6-7.8-16.8-17.3-17.9c-1-0.1-4.3,0.5-5.2-5.3C70.4,93.8,66.5,87.5,60.4,83.8z"/>
<path style="fill:#FFFFFF;" d="M119.9,59.3c-4.9,0-11.9,1.7-16,4.7c-5.6,4.2-9.7,10.2-12.7,16.5c0,0-7-4.6-16.3-1.5
c-6.5,2.2-9.1,4.7-11.2,7.4c2.6,2.4,4.6,5.3,6,8.6c4.7-5.1,12.1-6.4,20.2-6c11.4,0.7,20.1,8.2,20.1,8.2s-1.9-8.7-11.8-14
c4.6-10.1,11.7-18.5,23.2-19.5c1.5-0.1,3.8-0.2,3.8-1.5C125.3,61.2,123.1,59.3,119.9,59.3z"/>
<path style="fill:#FFFFFF;" d="M108,11.6c0.4-0.5,1.2-0.3,1.2,0.4c0.7,8.3-6.2,14.4-6.5,15c-0.7,1.4,1.4,4.8,2.2,6
c1.4,2.1,3.2,3.7,5.2,5.2c3.6,2.7,8.7,3,11.6,3c0.5,0,0.7,0.7,0.2,0.9c-4.9,2.3-17.4,6.2-24-9.1c-0.4-0.9-0.7-1.9-1.5-2.5
c-1-0.9-2.4-1.2-3.7-1.1c-4.2,0.5-10.5-0.1-14.7-3c-9.7-7-12.7-19.8-10.5-23c1-1.5,2.5,10.8,15.1,14.6
C97.5,22.5,103.8,16.9,108,11.6z"/>
<g>
<path style="fill:#FFFFFF;" d="M67.2,9.5c-4.1,4.3-12.3,7.7-18,5.8c1.7-8-4.1-11.1-4.1-11.1S48.9,15.6,38,22s-20.1-3.5-20.1-3.5
c-1.4,7.9,6.4,11.1,6.4,11.1S24.8,40,2.8,44.4c0,0,4.2,6.4,17.5,0.8c6.3-2.6,10.5-9,12.1-15.6c6.7,2.1,14.6-3.1,15.4-10
c7,3.2,16.1,1.6,21.4-4C69.2,15.6,67.9,13.3,67.2,9.5z"/>
</g>
<g style="opacity:0.7;">
<path style="fill:#92CBEB;" d="M67.2,9.5c-4.1,4.3-12.3,7.7-18,5.8c1.7-8-4.1-11.1-4.1-11.1S48.9,15.6,38,22s-20.1-3.5-20.1-3.5
c-1.4,7.9,6.4,11.1,6.4,11.1S24.8,40,2.8,44.4c0,0,4.2,6.4,17.5,0.8c6.3-2.6,10.5-9,12.1-15.6c6.7,2.1,14.6-3.1,15.4-10
c7,3.2,16.1,1.6,21.4-4C69.2,15.6,67.9,13.3,67.2,9.5z"/>
</g>
<path style="opacity:0.8;fill:#D5EBF5;enable-background:new ;" d="M60.4,83.8c-7.2-4.3-15.7-4.9-23.6-3.1
c-6.2,1.4-9.2,3.5-12.9-2.9C17.9,67.6,10,66.3,5.5,66.5c-1.1,0-1.6,1.5-0.6,2.1c4,2.6,11,8.5,13.4,18.8c0,0-6.2,7.1-6.6,13.2
c-0.1,0.9,1.1,1.4,1.7,0.8c5.3-5.1,21.2-14.5,32.2-10C59.5,97,62,108.9,62.4,114.5c0.1,1.4,1.5,2.3,2.8,1.9
c2.9-0.9,7.9-1.9,13.4-0.8c8.8,1.7,15.3,8.5,15.3,8.5c0-10.6-7.8-16.8-17.3-17.9c-1-0.1-4.3,0.5-5.2-5.3
C70.4,93.8,66.5,87.5,60.4,83.8z"/>
<path style="opacity:0.8;fill:#92CBEB;enable-background:new ;" d="M119.9,59.3c-4.9,0-11.9,1.7-16,4.7
c-5.6,4.2-9.7,10.2-12.7,16.5c0,0-7-4.6-16.3-1.5c-6.5,2.2-9.1,4.7-11.2,7.4c2.6,2.4,4.6,5.3,6,8.6c4.7-5.1,12.1-6.4,20.2-6
c11.4,0.7,20.1,8.2,20.1,8.2s-1.9-8.7-11.8-14c4.6-10.1,11.7-18.5,23.2-19.5c1.5-0.1,3.8-0.2,3.8-1.5
C125.3,61.2,123.1,59.3,119.9,59.3z"/>
<path style="opacity:0.8;fill:#D5EBF5;enable-background:new ;" d="M108,11.6c0.4-0.5,1.2-0.3,1.2,0.4c0.7,8.3-6.2,14.4-6.5,15
c-0.7,1.4,1.4,4.8,2.2,6c1.4,2.1,3.2,3.7,5.2,5.2c3.6,2.7,8.7,3,11.6,3c0.5,0,0.7,0.7,0.2,0.9c-4.9,2.3-17.4,6.2-24-9.1
c-0.4-0.9-0.7-1.9-1.5-2.5c-1-0.9-2.4-1.2-3.7-1.1c-4.2,0.5-10.5-0.1-14.7-3c-9.7-7-12.7-19.8-10.5-23c1-1.5,2.5,10.8,15.1,14.6
C97.5,22.5,103.8,16.9,108,11.6z"/>
</svg>

After

Width:  |  Height:  |  Size: 4.5 KiB

View file

@ -0,0 +1,46 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 26.3.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_8" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 128 128" style="enable-background:new 0 0 128 128;" xml:space="preserve">
<g>
<g>
<g>
<g id="gold_00000165203022899322124450000003472139253212578981_">
<radialGradient id="face_00000132807857306419350450000001097916018144416688_" cx="-360.1916" cy="-976" r="50.808" gradientTransform="matrix(1 0 0 -1 417.6916 -912)" gradientUnits="userSpaceOnUse">
<stop offset="0.5" style="stop-color:#FDE030"/>
<stop offset="0.92" style="stop-color:#F7C02B"/>
<stop offset="1" style="stop-color:#F4A223"/>
</radialGradient>
<path id="face_00000007413901337749749020000011305445033205062047_" style="fill:url(#face_00000132807857306419350450000001097916018144416688_);" d="
M57.5,113.9C32.6,113.9,5.8,98.3,5.8,64s26.9-49.9,51.7-49.9c13.8,0,26.6,4.5,36,12.8c10.3,9.1,15.7,21.9,15.7,37
s-5.4,27.8-15.7,36.9C84.1,109.2,71.2,113.9,57.5,113.9z"/>
<path style="fill:#EB8F00;" d="M100.2,34.4c4.8,7.7,7.2,16.8,7.2,27c0,15.1-5.4,27.8-15.7,36.9c-9.5,8.3-22.3,12.9-36,12.9
c-16.1,0-33-6.6-43-20.5c9.6,15.8,27.7,23.1,44.8,23.1c13.7,0,26.6-4.6,36-12.9c10.3-9.1,15.7-21.9,15.7-36.9
C109.2,52.6,106.1,42.5,100.2,34.4z"/>
</g>
<g id="facial_expressions_00000025421089436170878270000002927336676223602107_">
<g id="relieved-face_00000016054590052930058270000014482799045747817651_">
<g id="eyes_00000110462259112332263830000011555022149649664945_">
<g id="peepers_00000153697237836777825860000002547105109993645750_">
<path style="fill:#422B0D;" d="M60.4,54.3c-2.6,2.9-5.1,4-7.7,3.9c-2.1-0.1-4-0.8-5.6-3.2c-1.7-2.6-5.1-2.1-4.8,1.3
c0.5,5,5.6,8.4,10.4,8.6c5.9,0.2,10.5-3,12.5-7.8C66.5,54.1,62.4,52,60.4,54.3z"/>
<path style="fill:#422B0D;" d="M26.3,54.3c-2.5,3.1-4.6,3.9-7.2,3.9c-2,0-3.7-1.7-4.5-3.4c-1.1-2.1-3.9-1.6-3.9,0.9
c0,4.6,4,8.6,8.7,8.6c5.8,0,9.6-2.9,11.2-7.2C31.6,54,28.3,52,26.3,54.3z"/>
</g>
</g>
<path style="fill:#422B0D;" d="M51.8,78.3c-0.5-0.7-2.1-0.9-3.8-0.6c-9.5,1.7-18.1,1-22.2-0.1c-1.4-0.4-2.7-0.6-3.3,0.2
c-0.6,0.6-0.5,1.9,0.4,3c2.2,2.5,6.5,4.3,10,5c6.7,1.4,14.8-1.5,18.5-4.4C52.4,80.4,52.4,79.1,51.8,78.3z"/>
</g>
</g>
</g>
</g>
<path style="fill:#B0BEC5;" d="M107.2,48.8c0.4,1.5,0.7,3,1.1,4.5c6.6,1.6,11.7,4.3,11.5,8.5c-0.3,6.2-23.7,9.1-40.5,8.9
c-1.3,0-2.4,0.9-2.4,2s1.1,2,2.4,2c14.5,0,45.4-0.8,46-12.6C125.6,54.3,115.6,50.5,107.2,48.8z"/>
<g>
<path style="fill:#B0BEC5;" d="M79.7,87.8c-2.1,0-2.9-0.9-2.9-2s1-1.9,2.4-2c7-0.3,22.6-0.8,35.8-5.1c2.1-0.7,3.3-0.5,3.7,0.4
c0.5,1.2,0,2.4-2,3.2C102.8,88.2,91.3,87.8,79.7,87.8z"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.8 KiB

View file

@ -0,0 +1,65 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 26.3.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_7" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 128 128" style="enable-background:new 0 0 128 128;" xml:space="preserve">
<g>
<g>
<g id="gold_00000017474218815335838940000003074807137798816404_">
<radialGradient id="face_00000067949348182267862280000012244060020478301350_" cx="64" cy="-985.9" r="51.0588" gradientTransform="matrix(1 0 0 -1 0 -912)" gradientUnits="userSpaceOnUse">
<stop offset="0.5" style="stop-color:#FDE030"/>
<stop offset="0.92" style="stop-color:#F7C02B"/>
<stop offset="1" style="stop-color:#F4A223"/>
</radialGradient>
<path id="face_00000134933215444162423110000011147743369961211012_" style="fill:url(#face_00000067949348182267862280000012244060020478301350_);" d="
M64,124c-25,0-52-15.7-52-50.1s27-50.1,52-50.1c13.9,0,26.7,4.6,36.2,12.9c10.3,9.1,15.8,22.1,15.8,37.2s-5.5,28-15.8,37.1
C90.7,119.3,77.8,124,64,124z"/>
<path style="fill:#EB8F00;" d="M106.9,44.1c4.8,7.7,7.3,16.9,7.3,27.1c0,15.2-5.5,28-15.8,37.1c-9.5,8.3-22.4,13-36.2,13
c-16.2,0-33.2-6.6-43.2-20.6c9.6,15.8,27.8,23.3,45,23.3c13.8,0,26.7-4.7,36.2-13c10.3-9.1,15.8-22,15.8-37.1
C116,62.4,112.9,52.3,106.9,44.1z"/>
</g>
<g id="facial_expressions_00000069385602432109716340000017232438276675465613_">
<g id="relieved-face_00000006674354916450258610000003542533979739874985_">
<g id="eyes_00000142877458650344134320000012098703580094482597_">
<g id="peepers_00000071527329038589895370000005779041439730716814_">
<path style="fill:#422B0D;" d="M51.1,81.9l-0.2,0.2c-0.1,0.2-0.3,0.3-0.5,0.4L49.8,83c-0.3,0.2-0.6,0.4-0.9,0.6
c-0.3,0.2-0.7,0.4-1.1,0.6c-0.4,0.2-0.8,0.4-1.2,0.5s-0.9,0.2-1.3,0.3c-0.5,0.1-0.9,0.1-1.4,0.1h-0.4c-0.2,0-0.3,0-0.4,0
L42.5,85c-0.5-0.1-0.9-0.2-1.3-0.3c-0.4-0.2-0.8-0.3-1.2-0.6c-0.4-0.2-0.7-0.4-1.1-0.6c-0.5-0.4-1-0.7-1.5-1.2l-0.6-0.5
l-0.2-0.2c-1.1-1-2.9-1.1-4.1-0.2c-0.9,0.7-1.2,1.9-0.7,2.9l0.1,0.3c0.1,0.3,0.3,0.5,0.4,0.8c0.9,1.5,2.2,2.9,3.7,3.9
c0.7,0.5,1.5,0.9,2.3,1.3c0.9,0.4,1.8,0.7,2.7,0.9c0.5,0.1,1,0.2,1.6,0.2c0.3,0,0.6,0.1,0.7,0.1H44c1,0,2-0.1,3-0.3
c0.9-0.2,1.8-0.5,2.7-0.9c0.8-0.3,1.5-0.8,2.3-1.3c1.1-0.8,2.1-1.7,2.9-2.8l0.4-0.6l0.4-0.6c0.1-0.2,0.3-0.5,0.3-0.7l0.1-0.2
c0.5-1.3-0.1-2.8-1.4-3.3c-0.1,0-0.2-0.1-0.3-0.1C53.1,80.7,51.9,81,51.1,81.9z"/>
<path style="fill:#422B0D;" d="M91.4,81.9l-0.2,0.2c-0.1,0.2-0.3,0.3-0.5,0.4L90.2,83c-0.3,0.2-0.6,0.4-0.9,0.6
s-0.7,0.4-1.1,0.6s-0.8,0.4-1.2,0.5c-0.4,0.1-0.9,0.2-1.3,0.3c-0.5,0.1-0.9,0.1-1.4,0.1h-0.4c-0.2,0-0.3,0-0.4,0L82.9,85
c-0.5-0.1-0.9-0.2-1.3-0.3c-0.4-0.2-0.8-0.3-1.2-0.6c-0.4-0.2-0.7-0.4-1.1-0.6c-0.5-0.4-1-0.7-1.5-1.2l-0.6-0.5L77,81.6
c-1.1-1-2.9-1.1-4.1-0.2c-0.9,0.7-1.2,1.9-0.7,2.9l0.1,0.3c0.1,0.3,0.3,0.5,0.4,0.8c0.9,1.5,2.2,2.9,3.7,3.9
c0.7,0.5,1.5,0.9,2.3,1.3c0.9,0.4,1.8,0.7,2.7,0.9c0.5,0.1,1,0.2,1.6,0.2c0.3,0,0.6,0.1,0.7,0.1h0.7c1,0,2-0.1,3-0.3
c0.9-0.2,1.9-0.5,2.7-0.9c0.8-0.3,1.5-0.8,2.3-1.3c1.1-0.8,2.1-1.7,2.9-2.8l0.4-0.6l0.4-0.6c0.1-0.2,0.3-0.5,0.3-0.7l0.1-0.2
c0.5-1.3-0.1-2.8-1.4-3.3c-0.1,0-0.2-0.1-0.3-0.1C93.6,80.7,92.3,81,91.4,81.9z"/>
</g>
</g>
<path style="fill:#422B0D;" d="M80.4,104.1c-0.4-0.7-1.2-0.7-2.5-0.3c-3.8,1.2-9.2,1.6-13.8,1.6s-10-0.4-13.9-1.6
c-1.3-0.4-2.1-0.4-2.5,0.3c-0.4,0.7-0.4,1.6,0.4,2.7c1.6,2.3,8.8,5.7,16,5.7s14.3-3.5,16-5.7C80.8,105.7,80.8,104.8,80.4,104.1z
"/>
</g>
</g>
</g>
<g>
<g id="lines_00000098203385351539163110000000097349340490915742_">
<line id="XMLID_00000018916849331934550530000017805734775020808871_" style="fill:none;stroke:#B0BEC5;stroke-width:5;stroke-linecap:round;stroke-miterlimit:10;" x1="30.6" y1="26.4" x2="22.1" y2="15.8"/>
<line id="XMLID_00000101781988475875228320000002106814448799351993_" style="fill:none;stroke:#B0BEC5;stroke-width:5;stroke-linecap:round;stroke-miterlimit:10;" x1="52.3" y1="18.5" x2="48.5" y2="3.2"/>
</g>
<g>
<g id="lines_00000050638600932056272870000006303227320535829131_">
<line id="XMLID_00000005957764186300516780000014482876088468405913_" style="fill:none;stroke:#B0BEC5;stroke-width:5;stroke-linecap:round;stroke-miterlimit:10;" x1="97.4" y1="26.4" x2="105.9" y2="15.8"/>
<line id="XMLID_00000146464136326571224610000000004984912525994428_" style="fill:none;stroke:#B0BEC5;stroke-width:5;stroke-linecap:round;stroke-miterlimit:10;" x1="75.7" y1="18.5" x2="79.5" y2="3.2"/>
</g>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 4.5 KiB

View file

@ -1,56 +1,79 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 25.2.3, SVG Export Plug-In . SVG Version: 6.00 Build 0) --> <!-- Generator: Adobe Illustrator 25.2.3, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Raised-Hand" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 128 128" style="enable-background:new 0 0 128 128;" xml:space="preserve"> <svg version="1.1" id="Raised-Hand" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px"
y="0px" viewBox="0 0 128 128" style="enable-background:new 0 0 128 128;" xml:space="preserve">
<radialGradient id="face_1_" cx="63.6" cy="7861.3501" r="56.9597" gradientTransform="matrix(1 0 0 1 0 -7798.4497)" gradientUnits="userSpaceOnUse"> <radialGradient id="face_1_" cx="63.6" cy="7861.3501" r="56.9597" gradientTransform="matrix(1 0 0 1 0 -7798.4497)" gradientUnits="userSpaceOnUse">
<stop offset="0.5" style="stop-color:#FDE030"/> <stop offset="0.5" style="stop-color:#FDE030"/>
<stop offset="0.92" style="stop-color:#F7C02B"/> <stop offset="0.92" style="stop-color:#F7C02B"/>
<stop offset="1" style="stop-color:#F4A223"/> <stop offset="1" style="stop-color:#F4A223"/>
</radialGradient> </radialGradient>
<path id="face" style="fill:url(#face_1_);" d="M63.6,118.8c-27.9,0-58-17.5-58-55.9S35.7,7,63.6,7c15.5,0,29.8,5.1,40.4,14.4 c11.5,10.2,17.6,24.6,17.6,41.5s-6.1,31.2-17.6,41.4C93.4,113.6,79,118.8,63.6,118.8z"/> <path id="face" style="fill:url(#face_1_);" d="M63.6,118.8c-27.9,0-58-17.5-58-55.9S35.7,7,63.6,7c15.5,0,29.8,5.1,40.4,14.4
c11.5,10.2,17.6,24.6,17.6,41.5s-6.1,31.2-17.6,41.4C93.4,113.6,79,118.8,63.6,118.8z"/>
<g id="eyes"> <g id="eyes">
<path style="fill:#FFFFFF;" d="M43,47.7c9.58,0.03,17.33,7.82,17.3,17.4s-7.82,17.33-17.4,17.3c-9.58-0.03-17.33-7.82-17.3-17.4 C25.66,55.43,33.43,47.71,43,47.7"/> <path style="fill:#FFFFFF;" d="M43,47.7c9.58,0.03,17.33,7.82,17.3,17.4s-7.82,17.33-17.4,17.3c-9.58-0.03-17.33-7.82-17.3-17.4
C25.66,55.43,33.43,47.71,43,47.7"/>
<circle style="fill:#422B0D;" cx="42.7" cy="62.8" r="15.4"/> <circle style="fill:#422B0D;" cx="42.7" cy="62.8" r="15.4"/>
<ellipse transform="matrix(0.7659 -0.6429 0.6429 0.7659 -32.8541 47.0076)" style="fill:#FFFFFF;" cx="48.13" cy="68.62" rx="2.6" ry="2.4"/> <ellipse transform="matrix(0.7659 -0.6429 0.6429 0.7659 -32.8541 47.0076)" style="fill:#FFFFFF;" cx="48.13" cy="68.62" rx="2.6" ry="2.4"/>
<ellipse transform="matrix(0.8022 -0.5971 0.5971 0.8022 -26.9821 34.5782)" style="fill:#FFFFFF;" cx="38.69" cy="58.01" rx="9" ry="7.3"/> <ellipse transform="matrix(0.8022 -0.5971 0.5971 0.8022 -26.9821 34.5782)" style="fill:#FFFFFF;" cx="38.69" cy="58.01" rx="9" ry="7.3"/>
<path style="fill:#FFFFFF;" d="M86,47.7c9.58,0.03,17.33,7.82,17.3,17.4c-0.03,9.58-7.82,17.33-17.4,17.3 c-9.58-0.03-17.33-7.82-17.3-17.4C68.66,55.43,76.43,47.71,86,47.7"/> <path style="fill:#FFFFFF;" d="M86,47.7c9.58,0.03,17.33,7.82,17.3,17.4c-0.03,9.58-7.82,17.33-17.4,17.3
c-9.58-0.03-17.33-7.82-17.3-17.4C68.66,55.43,76.43,47.71,86,47.7"/>
<circle style="fill:#422B0D;" cx="85.7" cy="62.8" r="15.4"/> <circle style="fill:#422B0D;" cx="85.7" cy="62.8" r="15.4"/>
<ellipse transform="matrix(0.7659 -0.6429 0.6429 0.7659 -22.7305 74.6885)" style="fill:#FFFFFF;" cx="91.21" cy="68.56" rx="2.6" ry="2.4"/> <ellipse transform="matrix(0.7659 -0.6429 0.6429 0.7659 -22.7305 74.6885)" style="fill:#FFFFFF;" cx="91.21" cy="68.56" rx="2.6" ry="2.4"/>
<ellipse transform="matrix(0.8022 -0.5971 0.5971 0.8022 -18.4797 60.2586)" style="fill:#FFFFFF;" cx="81.7" cy="58.02" rx="9" ry="7.3"/> <ellipse transform="matrix(0.8022 -0.5971 0.5971 0.8022 -18.4797 60.2586)" style="fill:#FFFFFF;" cx="81.7" cy="58.02" rx="9" ry="7.3"/>
</g> </g>
<path style="fill:none;" d="M43,47.7c-9.58,0.03-17.33,7.82-17.3,17.4c0.03,9.58,7.82,17.33,17.4,17.3 c9.58-0.03,17.33-7.82,17.3-17.4C60.34,55.43,52.57,47.71,43,47.7"/> <path style="fill:none;" d="M43,47.7c-9.58,0.03-17.33,7.82-17.3,17.4c0.03,9.58,7.82,17.33,17.4,17.3
c9.58-0.03,17.33-7.82,17.3-17.4C60.34,55.43,52.57,47.71,43,47.7"/>
<circle style="fill:none;" cx="43.3" cy="62.8" r="15.4"/> <circle style="fill:none;" cx="43.3" cy="62.8" r="15.4"/>
<ellipse transform="matrix(0.6429 -0.7659 0.7659 0.6429 -39.0221 53.4261)" style="fill:none;" cx="37.79" cy="68.56" rx="2.4" ry="2.6"/> <ellipse transform="matrix(0.6429 -0.7659 0.7659 0.6429 -39.0221 53.4261)" style="fill:none;" cx="37.79" cy="68.56" rx="2.4" ry="2.6"/>
<ellipse transform="matrix(0.5971 -0.8022 0.8022 0.5971 -27.4803 61.3103)" style="fill:none;" cx="47.29" cy="58.01" rx="7.3" ry="9"/> <ellipse transform="matrix(0.5971 -0.8022 0.8022 0.5971 -27.4803 61.3103)" style="fill:none;" cx="47.29" cy="58.01" rx="7.3" ry="9"/>
<g> <g>
<defs> <defs>
<path id="SVGID_1_" d="M86,47.7c-9.58,0.03-17.33,7.82-17.3,17.4c0.03,9.58,7.82,17.33,17.4,17.3c9.58-0.03,17.33-7.82,17.3-17.4 C103.34,55.43,95.57,47.71,86,47.7"/> <path id="SVGID_1_" d="M86,47.7c-9.58,0.03-17.33,7.82-17.3,17.4c0.03,9.58,7.82,17.33,17.4,17.3c9.58-0.03,17.33-7.82,17.3-17.4
C103.34,55.43,95.57,47.71,86,47.7"/>
</defs> </defs>
<clipPath id="SVGID_2_"> <clipPath id="SVGID_2_">
<use xlink:href="#SVGID_1_" style="overflow:visible;"/> <use xlink:href="#SVGID_1_" style="overflow:visible;"/>
</clipPath> </clipPath>
<g style="clip-path:url(#SVGID_2_);"> <g style="clip-path:url(#SVGID_2_);">
<path style="fill:#29B6F6;" d="M102.35,68.47c-2.16-0.36-4.35,0.48-5.71,2.21c-0.97,1.27-2.49,1.99-4.09,1.95h-0.27 c-1.1,0-2.19,0.3-3.13,0.87c-1.48,0.9-3.35,0.9-4.83,0c-0.94-0.57-2.03-0.87-3.13-0.87c-0.24-0.02-0.47-0.02-0.71,0 c-1.71,0.25-3.42-0.43-4.48-1.79c-1.14-1.55-2.95-2.46-4.88-2.45c-3.31,0.08-5.96,2.76-6,6.07c0.03,3.3,2.7,5.97,6,6 c0.28,0,0.57-0.02,0.85-0.06c1.65-0.27,3.31,0.4,4.31,1.74c1.85,2.65,5.46,3.36,8.17,1.61c1.41-0.89,3.2-0.89,4.61,0 c2.63,1.68,6.1,1.07,8-1.4c0.91-1.25,2.38-1.96,3.93-1.9h0.38c3.34-0.05,6.01-2.8,5.96-6.14c-0.04-2.89-2.12-5.34-4.96-5.86 L102.35,68.47z"/> <path style="fill:#29B6F6;" d="M102.35,68.47c-2.16-0.36-4.35,0.48-5.71,2.21c-0.97,1.27-2.49,1.99-4.09,1.95h-0.27
c-1.1,0-2.19,0.3-3.13,0.87c-1.48,0.9-3.35,0.9-4.83,0c-0.94-0.57-2.03-0.87-3.13-0.87c-0.24-0.02-0.47-0.02-0.71,0
c-1.71,0.25-3.42-0.43-4.48-1.79c-1.14-1.55-2.95-2.46-4.88-2.45c-3.31,0.08-5.96,2.76-6,6.07c0.03,3.3,2.7,5.97,6,6
c0.28,0,0.57-0.02,0.85-0.06c1.65-0.27,3.31,0.4,4.31,1.74c1.85,2.65,5.46,3.36,8.17,1.61c1.41-0.89,3.2-0.89,4.61,0
c2.63,1.68,6.1,1.07,8-1.4c0.91-1.25,2.38-1.96,3.93-1.9h0.38c3.34-0.05,6.01-2.8,5.96-6.14c-0.04-2.89-2.12-5.34-4.96-5.86
L102.35,68.47z"/>
</g> </g>
</g> </g>
<g> <g>
<defs> <defs>
<path id="SVGID_3_" d="M43,47.7c-9.58,0.03-17.33,7.82-17.3,17.4c0.03,9.58,7.82,17.33,17.4,17.3c9.58-0.03,17.33-7.82,17.3-17.4 C60.34,55.43,52.57,47.71,43,47.7"/> <path id="SVGID_3_" d="M43,47.7c-9.58,0.03-17.33,7.82-17.3,17.4c0.03,9.58,7.82,17.33,17.4,17.3c9.58-0.03,17.33-7.82,17.3-17.4
C60.34,55.43,52.57,47.71,43,47.7"/>
</defs> </defs>
<clipPath id="SVGID_4_"> <clipPath id="SVGID_4_">
<use xlink:href="#SVGID_3_" style="overflow:visible;"/> <use xlink:href="#SVGID_3_" style="overflow:visible;"/>
</clipPath> </clipPath>
<g style="clip-path:url(#SVGID_4_);"> <g style="clip-path:url(#SVGID_4_);">
<path style="fill:#29B6F6;" d="M59.9,68.47c-2.16-0.36-4.35,0.49-5.7,2.21c-0.98,1.27-2.5,1.99-4.1,1.95h-0.26 c-1.1-0.01-2.19,0.29-3.13,0.87c-1.49,0.9-3.35,0.9-4.84,0c-0.94-0.58-2.03-0.88-3.13-0.87c-0.23-0.01-0.47-0.01-0.7,0 c-1.73,0.26-3.47-0.44-4.53-1.83c-1.14-1.55-2.95-2.46-4.87-2.45c-3.31,0.08-5.96,2.76-6,6.07c0,3.31,2.69,6,6,6 c0.02,0,0.03,0,0.05,0c0.28,0,0.56-0.02,0.84-0.06c1.65-0.27,3.32,0.4,4.32,1.74c1.82,2.66,5.42,3.4,8.15,1.68 c1.4-0.89,3.2-0.89,4.6,0c2.63,1.68,6.1,1.07,8-1.4c0.9-1.25,2.38-1.96,3.92-1.9h0.39c3.31,0.28,6.22-2.19,6.5-5.5 S63.22,68.76,59.9,68.47L59.9,68.47z"/> <path style="fill:#29B6F6;" d="M59.9,68.47c-2.16-0.36-4.35,0.49-5.7,2.21c-0.98,1.27-2.5,1.99-4.1,1.95h-0.26
c-1.1-0.01-2.19,0.29-3.13,0.87c-1.49,0.9-3.35,0.9-4.84,0c-0.94-0.58-2.03-0.88-3.13-0.87c-0.23-0.01-0.47-0.01-0.7,0
c-1.73,0.26-3.47-0.44-4.53-1.83c-1.14-1.55-2.95-2.46-4.87-2.45c-3.31,0.08-5.96,2.76-6,6.07c0,3.31,2.69,6,6,6
c0.02,0,0.03,0,0.05,0c0.28,0,0.56-0.02,0.84-0.06c1.65-0.27,3.32,0.4,4.32,1.74c1.82,2.66,5.42,3.4,8.15,1.68
c1.4-0.89,3.2-0.89,4.6,0c2.63,1.68,6.1,1.07,8-1.4c0.9-1.25,2.38-1.96,3.92-1.9h0.39c3.31,0.28,6.22-2.19,6.5-5.5
S63.22,68.76,59.9,68.47L59.9,68.47z"/>
</g> </g>
</g> </g>
<g id="eyebrows"> <g id="eyebrows">
<path style="fill:#422B0D;" d="M27.4,39.8c-2.2,0.4-2.3,3.6,0.1,3.7c5.3,0.07,10.42-1.9,14.3-5.5c1.48-1.28,2.73-2.8,3.7-4.5 c0.58-0.83,0.38-1.97-0.45-2.55c-0.83-0.58-1.97-0.38-2.55,0.45l-0.1,0.1C38.48,35.88,33.19,38.81,27.4,39.8z"/> <path style="fill:#422B0D;" d="M27.4,39.8c-2.2,0.4-2.3,3.6,0.1,3.7c5.3,0.07,10.42-1.9,14.3-5.5c1.48-1.28,2.73-2.8,3.7-4.5
<path style="fill:#422B0D;" d="M84.5,31.4c-0.58-0.83-1.72-1.03-2.55-0.45c-0.83,0.58-1.03,1.72-0.45,2.55 c0.97,1.7,2.22,3.22,3.7,4.5c3.9,3.57,9.01,5.54,14.3,5.5c2.5-0.1,2.3-3.3,0.1-3.7C93.74,38.84,88.41,35.87,84.5,31.4L84.5,31.4"/> c0.58-0.83,0.38-1.97-0.45-2.55c-0.83-0.58-1.97-0.38-2.55,0.45l-0.1,0.1C38.48,35.88,33.19,38.81,27.4,39.8z"/>
<path style="fill:#422B0D;" d="M84.5,31.4c-0.58-0.83-1.72-1.03-2.55-0.45c-0.83,0.58-1.03,1.72-0.45,2.55
c0.97,1.7,2.22,3.22,3.7,4.5c3.9,3.57,9.01,5.54,14.3,5.5c2.5-0.1,2.3-3.3,0.1-3.7C93.74,38.84,88.41,35.87,84.5,31.4L84.5,31.4"/>
</g> </g>
<path style="fill:#EB8F00;" d="M111.49,29.67c5.33,8.6,8.11,18.84,8.11,30.23c0,16.9-6.1,31.2-17.6,41.4 c-10.6,9.3-25,14.5-40.4,14.5c-18.06,0-37-7.35-48.18-22.94c10.76,17.66,31,25.94,50.18,25.94c15.4,0,29.8-5.2,40.4-14.5 c11.5-10.2,17.6-24.5,17.6-41.4C121.6,50.16,118.13,38.84,111.49,29.67z"/> <path style="fill:#EB8F00;" d="M111.49,29.67c5.33,8.6,8.11,18.84,8.11,30.23c0,16.9-6.1,31.2-17.6,41.4
<path id="mouth" style="fill:#422B0D;" d="M64,103.2c10.8,0,17.8-7.9,19.7-11.6c0.7-1.4,0.7-2.6,0.1-3.1c-0.64-0.4-1.46-0.4-2.1,0 c-0.32,0.13-0.62,0.3-0.9,0.5c-4.9,3.52-10.77,5.44-16.8,5.5c-6.01-0.08-11.87-1.96-16.8-5.4c-0.28-0.2-0.58-0.37-0.9-0.5 c-0.64-0.4-1.46-0.4-2.1,0c-0.6,0.6-0.6,1.7,0.1,3.1C46.2,95.3,53.2,103.2,64,103.2z"/> c-10.6,9.3-25,14.5-40.4,14.5c-18.06,0-37-7.35-48.18-22.94c10.76,17.66,31,25.94,50.18,25.94c15.4,0,29.8-5.2,40.4-14.5
c11.5-10.2,17.6-24.5,17.6-41.4C121.6,50.16,118.13,38.84,111.49,29.67z"/>
<path id="mouth" style="fill:#422B0D;" d="M64,103.2c10.8,0,17.8-7.9,19.7-11.6c0.7-1.4,0.7-2.6,0.1-3.1c-0.64-0.4-1.46-0.4-2.1,0
c-0.32,0.13-0.62,0.3-0.9,0.5c-4.9,3.52-10.77,5.44-16.8,5.5c-6.01-0.08-11.87-1.96-16.8-5.4c-0.28-0.2-0.58-0.37-0.9-0.5
c-0.64-0.4-1.46-0.4-2.1,0c-0.6,0.6-0.6,1.7,0.1,3.1C46.2,95.3,53.2,103.2,64,103.2z"/>
</svg> </svg>

Before

Width:  |  Height:  |  Size: 5.3 KiB

After

Width:  |  Height:  |  Size: 5.3 KiB

View file

@ -0,0 +1,119 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 25.2.3, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_9" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 128 128" style="enable-background:new 0 0 128 128;" xml:space="preserve">
<g>
<g>
<radialGradient id="SVGID_1_" cx="64" cy="55.0293" r="51.6864" gradientUnits="userSpaceOnUse">
<stop offset="0.5" style="stop-color:#FDE030"/>
<stop offset="0.9188" style="stop-color:#F7C02B"/>
<stop offset="1" style="stop-color:#F4A223"/>
</radialGradient>
<path style="fill:url(#SVGID_1_);" d="M79.23,6.71C40.06-6.28,2.19,29.66,15.96,68.34c3.09,9.95-0.34,11.7,1.36,16.28
c2.03,5.47,8.24,2.96,8.24,2.96s0.02,12.11,2.03,15.78c0.72,1.31,2.33,2.13,3.79,1.85c5.08-0.96,3.97-10.34,4.26-12.68
c0.54-4.38,3.21-2.43,4.07-1.39c0.86,1.04,1.03,2.46,1.21,3.8c0.83,8.29,4.31,13.61,12.84,10.35c0.56-0.21,2.41-0.63,2.16,0.45
c-0.22,3.03-0.4,8.86-0.27,11.9c0.13,2.99,1.12,7.16,4.9,6.23c9.13-2.53,4.48-13.83,4.9-21.32c0-6.2,7.02-5.13,7.02-0.65
c0,4.48-1.58,10.63,2.14,13.29c6.51,4.64,7.93-2.28,7.57-5.58c0,0-0.36-7.81,0.71-9.15c1.07-1.35,1.5,3.03,6.14,1.99
c6.23-1.4,5.97-8.45,6.52-11.76c0.55-3.31,2.41-6.24,3.57-2.91c1.16,3.33-0.7,8.21,0,12.05c0.7,3.84,2.19,4.96,4.88,4.72
c4.19-0.37,4.86-4.92,4.92-8.56c0.06-3.64,0.08-10.57,0.76-14.05c1.35-6.89,1.94-9.82,2.9-12.77
C120.98,43.41,106.05,15.45,79.23,6.71z"/>
<radialGradient id="SVGID_2_" cx="64.8001" cy="56.6768" r="54.0921" gradientTransform="matrix(0.9842 0.1772 -0.17 0.9438 10.6584 -8.297)" gradientUnits="userSpaceOnUse">
<stop offset="0.5" style="stop-color:#FDE030"/>
<stop offset="0.9188" style="stop-color:#F7C02B"/>
<stop offset="1" style="stop-color:#F4A223"/>
</radialGradient>
<path style="fill:url(#SVGID_2_);" d="M79.23,6.71C40.06-6.28,2.19,29.66,15.96,68.34c3.09,9.95-0.34,11.7,1.36,16.28
c2.03,5.47,8.24,2.96,8.24,2.96s0.02,12.11,2.03,15.78c0.72,1.31,2.33,2.13,3.79,1.85c5.08-0.96,3.97-10.34,4.26-12.68
c0.54-4.38,3.21-2.43,4.07-1.39c0.86,1.04,1.03,2.46,1.21,3.8c0.83,8.29,4.31,13.61,12.84,10.35c0.56-0.21,2.41-0.63,2.16,0.45
c-0.22,3.03-0.4,8.86-0.27,11.9c0.13,2.99,1.12,7.16,4.9,6.23c9.13-2.53,4.48-13.83,4.9-21.32c0-6.2,7.02-5.13,7.02-0.65
c0,4.48-1.58,10.63,2.14,13.29c6.51,4.64,7.93-2.28,7.57-5.58c0,0-0.36-7.81,0.71-9.15c1.07-1.35,1.5,3.03,6.14,1.99
c6.23-1.4,5.97-8.45,6.52-11.76c0.55-3.31,2.41-6.24,3.57-2.91c1.16,3.33-0.7,8.21,0,12.05c0.7,3.84,2.19,4.96,4.88,4.72
c4.19-0.37,4.86-4.92,4.92-8.56c0.06-3.64,0.08-10.57,0.76-14.05c1.35-6.89,1.94-9.82,2.9-12.77
C120.98,43.41,106.05,15.45,79.23,6.71z"/>
<radialGradient id="SVGID_3_" cx="87.6611" cy="-537.0126" r="6.7462" gradientTransform="matrix(0.9999 6.846566e-03 -0.0189 0.8615 -10.1591 557.2983)" gradientUnits="userSpaceOnUse">
<stop offset="0" style="stop-color:#F7C02B"/>
<stop offset="0.7524" style="stop-color:#F7C02B;stop-opacity:0"/>
</radialGradient>
<path style="fill:url(#SVGID_3_);" d="M81.76,95.74c-0.63-5.66,2.62-8.24,5.86-8.24s6.63,0,5.86,8.24
c-0.29,3.13-2.62,5.69-5.86,5.69S82.1,98.86,81.76,95.74z"/>
<radialGradient id="SVGID_4_" cx="53.3259" cy="-179.5832" r="5.5946" gradientTransform="matrix(0.9006 9.366775e-03 -0.017 1.1786 -29.3565 291.0061)" gradientUnits="userSpaceOnUse">
<stop offset="0" style="stop-color:#F7C02B"/>
<stop offset="0.7524" style="stop-color:#F7C02B;stop-opacity:0"/>
</radialGradient>
<path style="fill:url(#SVGID_4_);" d="M17.1,79.83c0.11-0.99,0.48-2.26,0.49-3.52c0.01-1.35-0.34-2.67-0.34-2.67
c0.96-1.51,2.33-2.46,3.85-2.46c2.92,0,5.28,3.48,5.28,7.78c0,4.3-2.36,7.78-5.28,7.78c-1.39,0-2.65-0.79-3.59-2.08
C17.51,84.65,16.82,82.48,17.1,79.83z"/>
<radialGradient id="SVGID_5_" cx="30.4605" cy="90.5767" r="4.7613" gradientTransform="matrix(0.9789 0.2042 -0.5216 2.5004 47.8825 -142.1224)" gradientUnits="userSpaceOnUse">
<stop offset="0" style="stop-color:#F7C02B;stop-opacity:0.9706"/>
<stop offset="0.928" style="stop-color:#F7C02B;stop-opacity:0"/>
</radialGradient>
<path style="fill:url(#SVGID_5_);" d="M25.57,87.58c-0.01-1.93-0.61-8.62,2.63-8.62s4.91,2.55,5.5,7.01
c0.57,4.35,0.14,10.24-0.02,12.51c-0.34,4.72-3.03,6.65-6.33,4.32C27.34,102.8,25.62,98.67,25.57,87.58z"/>
<radialGradient id="SVGID_6_" cx="60.4735" cy="107.8734" r="5.2571" gradientTransform="matrix(0.9999 0.0144 -0.0367 2.554 3.9615 -168.4997)" gradientUnits="userSpaceOnUse">
<stop offset="0.3068" style="stop-color:#F7C02B;stop-opacity:0.9706"/>
<stop offset="1" style="stop-color:#F7C02B;stop-opacity:0"/>
</radialGradient>
<path style="fill:url(#SVGID_6_);" d="M56.11,104.34c-0.01-1.93-0.17-9.88,3.07-9.88s5.04,1.09,5.63,5.55
c0.57,4.35,1.68,13.22,0.65,16.98c-1.61,5.91-7.19,4.98-8.53,2.83C55.04,116.81,56.11,104.34,56.11,104.34z"/>
<radialGradient id="SVGID_7_" cx="76.7644" cy="103.2429" r="6.6172" gradientTransform="matrix(1 0 0 2.0255 0 -105.8721)" gradientUnits="userSpaceOnUse">
<stop offset="0.0737" style="stop-color:#F7C02B"/>
<stop offset="0.7875" style="stop-color:#F7C02B;stop-opacity:0"/>
</radialGradient>
<path style="fill:url(#SVGID_7_);" d="M73.22,96.06c0.41-3.89,2.11-5.87,5.35-5.87s9.82,4.45,4.32,10.25
c-1.22,1.28-0.69,4.78-1.27,10.05c-0.4,3.57-3.21,3.2-4.49,3.2C71.27,113.69,71.83,109.46,73.22,96.06z"/>
<radialGradient id="SVGID_8_" cx="102.7368" cy="88.1204" r="5.4789" gradientTransform="matrix(0.9769 0.2136 -0.5379 2.46 49.7747 -150.6061)" gradientUnits="userSpaceOnUse">
<stop offset="0" style="stop-color:#F7C02B"/>
<stop offset="0.7524" style="stop-color:#F7C02B;stop-opacity:0"/>
</radialGradient>
<path style="fill:url(#SVGID_8_);" d="M99.13,87.48c-0.99-3.79,2.51-16.16,5.74-16.16s0.06,7.04,1.87,26.23
c0.34,3.58-2.75,4.61-4.03,4.61C96.85,102.16,100.41,92.39,99.13,87.48z"/>
<path style="fill:#EB8F00;" d="M109.45,32.23c-0.05,1.92,6.38,17.74,0.28,35.99c-1.05,3.13-1.66,6.29-2.99,13.12
c-1.58,5.7,0.86,19.82-3.76,23.17c4.69,0.7,6.08-4.86,5.95-8.54c0.06-3.64,0.08-10.57,0.76-14.05c1.35-6.89,1.94-9.82,2.9-12.77
C116.7,56.53,115.2,43.39,109.45,32.23z"/>
<path style="fill:#EB8F00;" d="M97.58,84.59c-0.41-1.88-3.73-0.96-4.58,1.46c-1.18,3.35-0.57,6.74-1.33,9.99
c-0.42,1.9-2.62,5.36-5.98,3.15c-1.58-1.04-3.25-2.98-5.14-0.6c-3.02,4.94,0.4,12.32-1.9,16.39c-0.62,1.1-1.8,1.44-1.8,1.44
c3.53,1.13,5.01-1.34,5.33-4.68c0.35-3.67-0.58-8.43,0.71-11.29c0.14-0.18,0.28-0.26,0.41-0.26c0.74,0,1.5,2.41,4.37,2.41
c0.41,0,0.86-0.05,1.36-0.16c5.31-1,6.2-7.01,6.48-11.48C95.78,86.74,98.15,87.19,97.58,84.59z"/>
<path style="fill:#EB8F00;" d="M68.31,96.51c-2.26-1.06-4.24,1.14-4.72,3.14c-1.93,5.5,0.83,10.18-0.78,15.76
c-0.79,2.04-1.5,3.21-3.1,3.59c-2.9,0.69-3.82,2.86-2.14,4.27c0.82,0.77,1.92,0.85,3,0.59c12.29-3.03-0.41-23.07,7.77-25.6
c0.09,0,0.6-0.19,2.2,0.36C70.53,98.62,70.53,97.55,68.31,96.51z"/>
<path style="fill:#EB8F00;" d="M41.48,88.58c-1.13-1.69-2.81-2.33-4.84-1.75c-3.64,1.5-3.99,3.66-3.45,10.99
c0.17,2.38-0.52,6.91-0.52,6.91s3.31-1.5,2.98-12.2c-0.09-3.07,2.61-3.09,4.07-1.39c0.86,1.04,1.03,2.46,1.21,3.8
c0.66,6.6,3,11.32,8.24,11.32c1.67,0.03,4.18-0.92,5.34-1.13c1.16-0.21,1.7-1.33,1.44-2.07c-1.06-3-4.95,0.65-6.78,0.2
c-1.09,0-4.84,0.12-5.7-8.5C43.36,92.76,42.59,90.22,41.48,88.58z"/>
<path style="fill:#EB8F00;" d="M27.08,79.48c-0.51-0.61-0.95,2-2.13,3.86c-1.56,2.46-3.94,3.17-6.92,2.47
c0.36,1.35,2.24,2.93,5.58,2.25C26.59,87.45,27.08,85.79,27.08,79.48z"/>
</g>
<g>
<g>
<g>
<path style="fill:#422B0D;" d="M48.62,24.6L48.62,24.6c-3.68-0.74-7.65,1.69-8.69,6.85c-1.04,5.16,1.68,8.93,5.36,9.67l0,0
c3.69,0.74,7.65-1.69,8.68-6.84C55.01,29.13,52.34,25.35,48.62,24.6z"/>
<g id="peepers_123_">
<path style="fill:#896024;" d="M47.62,27.98L47.62,27.98c-1.13-0.85-2.73-0.63-3.59,0.5c-0.67,0.88-0.69,2.09-0.06,3l0,0
c1.13,0.85,2.73,0.63,3.59-0.5C48.22,30.1,48.24,28.89,47.62,27.98z"/>
</g>
</g>
<g>
<path style="fill:#422B0D;" d="M77.51,33.94L77.51,33.94c-3.75,0-7.16,3.17-7.16,8.43s3.41,8.42,7.16,8.42l0,0
c3.76,0,7.16-3.17,7.16-8.42S81.31,33.94,77.51,33.94z"/>
<g id="peepers_122_">
<path style="fill:#896024;" d="M77.2,37.46L77.2,37.46c-1.27-0.61-2.8-0.07-3.42,1.2c-0.48,1-0.26,2.19,0.53,2.95l0,0
c1.27,0.61,2.8,0.07,3.42-1.2C78.21,39.42,78,38.23,77.2,37.46z"/>
</g>
</g>
</g>
<path id="mouth_80_" style="fill:#422B0D;stroke:#422B0D;stroke-width:1.0587;stroke-miterlimit:10;" d="M63.18,73.11
c-9.88-1.62-16.9-7.86-20.34-13.86c-0.35-0.6-0.33-1.34,0.05-1.92c0.38-0.59,1.05-0.91,1.69-0.81l0,0
c0.29,0.05,0.57,0.18,0.81,0.38c4,3.44,10.91,8.02,18.89,9.33l0.77,0.06c8.74,0.82,13.48-1.81,18-3.68
c0.29-0.12,1.87-0.64,2.43,0.93c0.23,0.65-0.29,1.59-0.76,2.1c-3.65,4-10.08,8.51-21.01,7.52L63.18,73.11z"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 8.7 KiB

View file

@ -0,0 +1,71 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 25.2.3, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_8" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 128 128" style="enable-background:new 0 0 128 128;" xml:space="preserve">
<radialGradient id="face_1_" cx="63.6" cy="-2382.8486" r="56.9597" gradientTransform="matrix(1 0 0 -1 0 -2316.9487)" gradientUnits="userSpaceOnUse">
<stop offset="0.5" style="stop-color:#FDE030"/>
<stop offset="0.9188" style="stop-color:#F7C02B"/>
<stop offset="1" style="stop-color:#F4A223"/>
</radialGradient>
<path id="face_8_" style="fill:url(#face_1_);" d="M63.6,121.8c-27.9,0-58-17.5-58-55.9S35.7,10,63.6,10c15.5,0,29.8,5.1,40.4,14.4
c11.5,10.2,17.6,24.6,17.6,41.5s-6.1,31.2-17.6,41.4C93.4,116.6,79,121.8,63.6,121.8z"/>
<path style="fill:#EB8F00;" d="M111.49,32.67c5.33,8.6,8.11,18.84,8.11,30.23c0,16.9-6.1,31.2-17.6,41.4
c-10.6,9.3-25,14.5-40.4,14.5c-18.06,0-37.04-7.35-48.18-22.94c10.76,17.66,30.99,25.94,50.18,25.94c15.4,0,29.8-5.2,40.4-14.5
c11.5-10.2,17.6-24.5,17.6-41.4C121.6,53.16,118.13,41.84,111.49,32.67z"/>
<path style="fill:#422B0D;" d="M84.74,87.06c-2.98-0.41-9.6-0.55-21.53-0.55c-0.02,0-0.04,0-0.05,0s-0.04,0-0.05,0
c-11.94,0-18.55,0.14-21.53,0.55c-1.81,0.25-2.5,2.5-0.19,4.02c4.26,2.8,10.65,4.71,21.78,4.79c11.13-0.07,17.52-1.99,21.78-4.79
C87.24,89.56,86.55,87.31,84.74,87.06z"/>
<path style="fill:#F09300;" d="M73.51,13.93c-0.05-0.07-0.1-0.13-0.15-0.19c-1.11-1.39-5.02-3.41-6.83-2.39
c-0.8,0.45-1.07,2.02-1.44,2.8c-0.65,1.37-1.49,2.62-2.32,3.89c-2.63,4.01-43.48,34.05-43.48,34.05L7.67,50.17
C6.56,53,6.41,56.33,6.41,56.33s3.17,2.31,16.34,1.55c8.51-0.49,24.79-7.48,27.86-12.6c0.75-1.25,6.99-9.94,6.93-10.7
c1.41-0.74,8.49-5.06,12.15-8.12c2.79-2.33,4.57-4.18,4.9-7.95C74.73,16.92,74.45,15.23,73.51,13.93z"/>
<path style="fill:#422B0D;" d="M44.04,47.94L44.04,47.94c-4.19,0-8,3.54-8,9.42s3.81,9.42,8,9.42l0,0c4.19,0,8-3.54,8-9.42
S48.24,47.94,44.04,47.94z"/>
<g>
<g id="peepers_39_">
<path style="fill:#896024;" d="M43.65,51.87L43.65,51.87c-1.03-0.72-2.58-0.49-3.58,0.95c-1,1.45-0.67,2.97,0.36,3.69l0,0
c1.03,0.72,2.58,0.49,3.58-0.95S44.69,52.59,43.65,51.87z"/>
</g>
</g>
<path style="fill:#422B0D;" d="M82.4,47.94L82.4,47.94c-4.19,0-8,3.54-8,9.42s3.81,9.42,8,9.42l0,0c4.19,0,8-3.54,8-9.42
S86.59,47.94,82.4,47.94z"/>
<g>
<g id="peepers_6_">
<path style="fill:#896024;" d="M82.01,51.87L82.01,51.87c-1.03-0.72-2.58-0.49-3.58,0.95c-1,1.45-0.67,2.97,0.36,3.69l0,0
c1.03,0.72,2.58,0.49,3.58-0.95C83.38,54.11,83.04,52.59,82.01,51.87z"/>
</g>
</g>
<g>
<linearGradient id="SVGID_1_" gradientUnits="userSpaceOnUse" x1="51.815" y1="59.9018" x2="35.7392" y2="20.1638">
<stop offset="0" style="stop-color:#FFC400"/>
<stop offset="1" style="stop-color:#FFF176"/>
</linearGradient>
<path style="fill:url(#SVGID_1_);" d="M7.67,50.17c-8.08-8.1-5.56-26.65,9.86-30.96c7.22-2.02,10.41-4.34,14.36-5.8
c5.45-2.02,12.4-6.37,14.86-6.51c3.25-0.18,4.57,2.46,4.57,2.46s3.2-1.79,5.46-3.16s3.77-1.83,5.13-1.05
c1.63,0.92,2.57,3.22,2.57,3.22s1.03-0.89,2.12-1.96c1.51-1.48,4.38-1.01,5.61,1.54c1.12,2.32-1.02,7.33-1.02,7.33
s3.24,0.02,1.74,4.77s-3.95,5.85-5.76,7.47s-6.99,4.71-9.27,6.12c0,0,1.87,2.18,1.09,4.97c-0.35,1.24-1.47,2.07-2.43,2.89
c-1.34,1.15-8.16,5.82-9.6,6.84c-3.4,2.41-14,7.65-20.25,8.02C26.69,56.35,15.92,58.45,7.67,50.17z"/>
<g>
<path style="fill:#F09300;" d="M15.84,41.29c-1.31,0.65-3.05-0.11-1.79-1.59c2.45-2.46,5.28-4.58,7.28-7.46
c1.08-1.38,1.96-3.02,2.33-3.7c0.37-0.68,0.96-1.42,1.27-0.55c0.21,0.6-0.16,1.87-0.51,2.79C22.49,34.83,19.79,39,15.84,41.29z"/>
</g>
<path style="fill:#F09300;" d="M73.52,20.45c1.68-4.69-1.71-5.85-1.71-5.85s2.37-4.41,0.57-7.47c-1.34-2.29-3.11-2.67-5.18-1.61
c-1.42,0.73-2.82,2.13-2.82,2.13s-0.36-2.2-1.99-3.12c-1.95-0.94-3.42-0.92-6.33,1.32c-1.72,1.32-4.75,3.52-4.75,3.52
c-1.94-3.12-4.47-2.47-4.57-2.46c-2.47,0.13-9.41,4.49-14.86,6.51c-3.95,1.46-7.9,2.11-14.9,4.79C3.38,23.42,0.66,33.51,3.57,42.84
c-0.7-14,7.41-19.12,14.48-21.83c4-1.53,6.96-2.38,9.57-3.12c4.66-1.18,8.98-3.08,13.24-5.3c2.18-1.09,3.65-1.97,4.43-2.35
c1.04-0.5,2.22-0.4,3.06,0.12c0.81,0.5,1.85,1.74,0.41,3.65c-1.18,1.56-4.43,2.77-7.36,4.13c-1.65,0.83-11.35,4.98-9.7,7.25
c0.29,0.43,0.82,0.64,1.32,0.51c0.43-0.14,1.27-0.2,1.58-0.57c2.58-2.55,5.95-3.74,9.37-5.41c3.8-1.77,6.95-3.24,8.7-7.75
l0.59-0.38c1.24-1.03,3.74-3.12,5.49-4.46c1.35-1.28,4.04-0.34,4.34,1.36c0.3,1.7-0.82,3.27-3.18,5.84
c-4.25,4.34-14.53,8.24-18.93,13.5c-0.73,0.87,0.17,2.27,1.27,1.96c0.5-0.14,1.21-0.33,2.44-1.79s4.43-3.52,8.31-5.83
c5.13-3.11,9.99-7.18,12.05-12.3c0.61-1.51,2.42-2.46,4.09-2.48c1.81,0.01,3.06,2.27,1.61,5.23c-0.65,1.34-1.28,2.53-4.24,5.69
c-2.62,2.79-6.17,4.87-9.9,7.01c-3.34,1.91-6.64,4.03-8.22,5.92c-0.78,0.87-0.29,2.32,0.86,2.04c0.48-0.1,0.73,0.25,1.87-1.09
c1.14-1.34,3.58-3.22,6.58-4.93c5.31-2.71,9.7-6.58,12.96-10.98c0,0,2.01,0.23,1.38,2.83c-1.77,7.38-10.63,10.06-16.3,14.11
c-3.36,1.72-11.59,8.41-13.23,7.69c-3.65-1.61-13.17-2.17-18.55,2.91c-0.6,0.57-1.58,1.58-1.26,2c0.32,0.42,1.02-0.17,1.49-0.47
c4.24-2.68,10.2-3.98,17.45-1.7c1.52,0.48,12.22-6.57,15.38-8.53c-0.05-0.03,5.47-3.37,8.46-5.45
C68.49,27.78,71.82,25.19,73.52,20.45z"/>
<path style="fill:#F09300;" d="M58.55,33.99C58.55,33.99,58.55,33.99,58.55,33.99c0.46-0.29-2.13,1.25-2.13,1.25
c0.15,0.16,1.51,1.51,1.25,2.43c-0.11,0.4,0.03,1.35-2.06,2.87c-1.12,0.81-10.54,7.43-10.54,7.43c-23.74,15.85-39.04,0.55-37.4,2.2
c0.01,0.01,0.02,0.02,0.03,0.03c8.24,8.24,19.42,7.05,19.42,7.05c10.2-1.51,16.8-5.93,20.2-8.34c1.44-1.02,8.69-6.09,10.03-7.24
c0.96-0.82,2.01-1.74,2.36-2.99C60.47,35.96,58.65,34.09,58.55,33.99z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 5.6 KiB

View file

@ -0,0 +1,66 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 25.2.3, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Raised-Hand" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px"
y="0px" viewBox="0 0 128 128" style="enable-background:new 0 0 128 128;" xml:space="preserve">
<radialGradient id="face_1_" cx="63.6" cy="-7019.4609" r="56.9597" gradientTransform="matrix(1 0 0 -1 0 -6956.5605)" gradientUnits="userSpaceOnUse">
<stop offset="0.5" style="stop-color:#FDE030"/>
<stop offset="0.9188" style="stop-color:#F7C02B"/>
<stop offset="1" style="stop-color:#F4A223"/>
</radialGradient>
<path id="face_44_" style="fill:url(#face_1_);" d="M63.6,118.8c-27.9,0-58-17.5-58-55.9S35.7,7,63.6,7c15.5,0,29.8,5.1,40.4,14.4
c11.5,10.2,17.6,24.6,17.6,41.5s-6.1,31.2-17.6,41.4C93.4,113.6,79,118.8,63.6,118.8z"/>
<linearGradient id="face_2_" gradientUnits="userSpaceOnUse" x1="63.6" y1="-7075.3608" x2="63.6" y2="-6963.5605" gradientTransform="matrix(1 0 0 -1 0 -6956.5605)">
<stop offset="0.1579" style="stop-color:#F4A223"/>
<stop offset="0.333" style="stop-color:#F7C02B"/>
<stop offset="0.8071" style="stop-color:#FDE030;stop-opacity:0"/>
</linearGradient>
<path id="face_65_" style="fill:url(#face_2_);" d="M63.6,118.8c-27.9,0-58-17.5-58-55.9S35.7,7,63.6,7c15.5,0,29.8,5.1,40.4,14.4
c11.5,10.2,17.6,24.6,17.6,41.5s-6.1,31.2-17.6,41.4C93.4,113.6,79,118.8,63.6,118.8z"/>
<path style="fill:#EB8F00;" d="M111.49,29.67c5.33,8.6,8.11,18.84,8.11,30.23c0,16.9-6.1,31.2-17.6,41.4
c-10.6,9.3-25,14.5-40.4,14.5c-18.06,0-37.04-7.35-48.18-22.94c10.76,17.66,30.99,25.94,50.18,25.94c15.4,0,29.8-5.2,40.4-14.5
c11.5-10.2,17.6-24.5,17.6-41.4C121.6,50.16,118.13,38.84,111.49,29.67z"/>
<g>
<path style="fill:#422B0D;" d="M44.04,40.94L44.04,40.94c-4.19,0-8,3.54-8,9.42s3.81,9.42,8,9.42l0,0c4.19,0,8-3.54,8-9.42
S48.24,40.94,44.04,40.94z"/>
<g>
<g id="peepers_146_">
<path style="fill:#896024;" d="M43.65,44.87L43.65,44.87c-1.03-0.72-2.58-0.49-3.58,0.95c-1,1.45-0.67,2.97,0.36,3.69l0,0
c1.03,0.72,2.58,0.49,3.58-0.95S44.69,45.59,43.65,44.87z"/>
</g>
</g>
<path style="fill:#422B0D;" d="M82.4,40.94L82.4,40.94c-4.19,0-8,3.54-8,9.42s3.81,9.42,8,9.42l0,0c4.19,0,8-3.54,8-9.42
S86.59,40.94,82.4,40.94z"/>
<g>
<g id="peepers_145_">
<path style="fill:#896024;" d="M82.01,44.87L82.01,44.87c-1.03-0.72-2.58-0.49-3.58,0.95c-1,1.45-0.67,2.97,0.36,3.69l0,0
c1.03,0.72,2.58,0.49,3.58-0.95C83.38,47.11,83.04,45.59,82.01,44.87z"/>
</g>
</g>
</g>
<g id="palm">
<radialGradient id="prop-2_1_" cx="64.5261" cy="-4876.936" r="65.3306" gradientTransform="matrix(1 0 0 -1 0 -4800.5605)" gradientUnits="userSpaceOnUse">
<stop offset="0.27" style="stop-color:#FFF176"/>
<stop offset="1" style="stop-color:#FFC400"/>
</radialGradient>
<path id="prop-2" style="fill:url(#prop-2_1_);" d="M96.32,81.86c-2.22-3.18-8,0.07-8,0.07s3-2.61,5.5-5c1.6-1.53,3-4.79,1.69-7
s-5.12-2.07-6.99-0.92l-7.33,4.5c0,0,2.75-1.2,3.58-5.92c0.52-1.91-0.6-3.88-2.51-4.4c-0.36-0.1-0.73-0.14-1.1-0.12
c-1.73,0.15-3.38,0.79-4.77,1.84c-5.3,3.76-19.45,13.35-22.62,13.1c-1.23-0.1,0.35-3.89,2.63-6.07s4.85-7.2,3.4-10.65
c-0.85-2-4.26-3.44-5.49-1.51c-0.93,1.42-4.51,6.02-8.07,9.48c-3.56,3.47-9.42,11.22-9.93,11.98c-3.73,5.52-10.25,18.06-2.97,29.6
c6.44,10.22,21.14,10.7,28.67,8c14.39-5.1,27.64-14.9,27.89-15.1c2.62-2.08,5-5.13,3.92-7.91c-1.33-3.44-5.74-1.4-5.74-1.4
c2.55-1.55,4.84-3.5,6.77-5.78C96.68,86.29,97.81,84.01,96.32,81.86z"/>
<path style="fill:#EB8F00;" d="M96.03,89.56c1.18-1.52,3.94-5.07,1.52-8.56c-0.87-1.23-2.08-2-3.49-2.24
c2.37-1.97,4.91-6.57,2.2-10.35c-1.01-1.41-2.94-2.51-6.01-1.8c-2.21,0.51-4.19,2.14-4.19,2.14c0.07-0.27,1.81-5.18-4.2-6.4
c-2.42-0.49-5.17,1.11-6.75,2.3c-9.13,6.48-20.13,12.4-19.73,11.55c1.42-2.82,3.82-4.78,5.12-7.35c1.43-2.84,1.23-5.01,0.84-6.87
c-0.64-3-3.35-4.94-6.09-4.09c-2.41,0.75-3.17,3.83-7.49,8.5c-7,7.56-17.46,13.63-17.46,29.57c0,0,1.71-9.25,6.01-14.74
c4.05-5.16,9.97-10.13,13.65-14.19c3.5-3.85,3.51-5.16,4.67-6.14c2.02-1.7,5.12-0.51,4.81,3.19c-0.36,4.45-2.23,5.95-4.26,8.49
c-1.25,1.57-2.58,3.42-3.14,4.74c-0.36,0.84,0.33,1.74,1.24,1.62c6.13-0.81,20.37-10.55,24-12.84c1.16-0.88,2.54-1.41,3.93-1.54
c2.74-0.39,2.6,3.45,2.6,3.45c-0.77,3.15-1.26,3.15-4.4,5.79c-2.23,1.87-10.12,7.61-15.24,10.19c0,0,0.09,1.17,2.46,0.5
s11.71-6.54,16.74-10.57c0,0,6.52-5.57,9.3-4.79c2.82,0.8,2.57,2.76,1.68,4.62c-0.88,1.86-3.19,4.1-6.85,6.89
c-6.58,5-16.77,11.82-16.77,11.82s0.21,1,2.19,0.43c4.03-1.17,16.61-10.49,16.61-10.49c0.98-0.76,4.26-1.81,5.54,0.34
c0.69,1.01,0.53,2.66-1.89,5.52c-2.88,3.43-11.67,9.41-19.31,13.75c0,0,0.58,0.98,2.69,0.26s10.83-6.03,12.21-6.49
c1.38-0.46,2.76-0.9,3.68,0.73c1.09,2.25-0.37,4.03-3.26,6.33c-0.12,0.09-13.88,10.85-28.19,15.69c-7.56,2.56-16.01,1.24-25.7-5.09
c5.88,7.1,18.72,10.27,27.43,7.09c14.65-5.36,28.21-15.25,28.32-15.34c2.1-1.67,6.47-5.13,4.06-10.08
c-0.63-1.11-1.47-1.8-2.39-2.19C93.91,91.89,95.01,90.77,96.03,89.56z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 4.9 KiB

View file

@ -0,0 +1,108 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 25.2.3, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_2" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 128 128" style="enable-background:new 0 0 128 128;" xml:space="preserve">
<g id="smiling-eyes-smiling-face_10_">
<path id="mouth_9_" style="display:none;fill:#35220B;" d="M-213.36,98.75c12.29,0,23.26-7.11,30.51-18.26
c1.1-1.7-1.23-4.29-3.23-2.29c-7.12,7.12-16.57,11.03-27.28,11.03h-0.21c-10.7,0-20.16-3.9-27.28-11.03c-2-2-4.33,0.59-3.23,2.29
c7.24,11.15,18.22,18.26,30.51,18.26H-213.36z"/>
</g>
<radialGradient id="face_1_" cx="63" cy="-2091.1001" r="56.9597" gradientTransform="matrix(1 0 0 -1 0 -2028)" gradientUnits="userSpaceOnUse">
<stop offset="0.5" style="stop-color:#FDE030"/>
<stop offset="0.92" style="stop-color:#F7C02B"/>
<stop offset="1" style="stop-color:#F4A223"/>
</radialGradient>
<path id="face_10_" style="fill:url(#face_1_);" d="M63,119c-27.9,0-58-17.5-58-55.9S35.1,7.2,63,7.2c15.5,0,29.8,5.1,40.4,14.4
C114.9,31.8,121,46.2,121,63.1s-6.1,31.2-17.6,41.4C92.8,113.8,78.4,119,63,119z"/>
<linearGradient id="face-2_1_" gradientUnits="userSpaceOnUse" x1="63" y1="1457" x2="63" y2="1345.2" gradientTransform="matrix(1 0 0 1 0 -1338)">
<stop offset="0" style="stop-color:#F4A223"/>
<stop offset="0.3812" style="stop-color:#F7C02B"/>
<stop offset="0.7478" style="stop-color:#FDE030;stop-opacity:0"/>
</linearGradient>
<path id="face-2_2_" style="fill:url(#face-2_1_);" d="M63,119c-27.9,0-58-17.5-58-55.9S35.1,7.2,63,7.2c15.5,0,29.8,5.1,40.4,14.4
C114.9,31.8,121,46.2,121,63.1s-6.1,31.2-17.6,41.4C92.8,113.8,78.4,119,63,119z"/>
<path style="fill:#EB8F00;" d="M110.89,29.87c5.33,8.6,8.11,18.84,8.11,30.23c0,16.9-6.1,31.2-17.6,41.4C90.8,110.8,76.4,116,61,116
c-18.06,0-37.04-7.35-48.18-22.94C23.58,110.72,43.81,119,63,119c15.4,0,29.8-5.2,40.4-14.5C114.9,94.3,121,80,121,63.1
C121,50.36,117.53,39.04,110.89,29.87z"/>
<radialGradient id="SVGID_1_" cx="35.5447" cy="72.5759" r="19.4444" gradientTransform="matrix(0.9791 0 0 0.9301 -3.7557 10.8462)" gradientUnits="userSpaceOnUse">
<stop offset="0" style="stop-color:#ED7770"/>
<stop offset="0.9" style="stop-color:#ED7770;stop-opacity:0"/>
</radialGradient>
<circle style="opacity:0.8;fill:url(#SVGID_1_);" cx="31.05" cy="78.35" r="17.5"/>
<radialGradient id="SVGID_2_" cx="101.6369" cy="72.5759" r="19.4444" gradientTransform="matrix(0.9791 0 0 0.9301 -3.7557 10.8462)" gradientUnits="userSpaceOnUse">
<stop offset="0" style="stop-color:#ED7770"/>
<stop offset="0.9" style="stop-color:#ED7770;stop-opacity:0"/>
</radialGradient>
<circle style="opacity:0.8;fill:url(#SVGID_2_);" cx="95.75" cy="78.35" r="17.5"/>
<g>
<g>
<circle style="fill:#FFFFFF;" cx="82.04" cy="58.16" r="18.53"/>
<circle style="fill:#422B0D;" cx="81.88" cy="58.16" r="8.04"/>
</g>
<g id="peepers_1_">
<path style="fill:#896024;" d="M81.1,52.69L81.1,52.69c-1.07-0.56-2.59-0.02-3.49,1.7c-0.9,1.72-0.46,3.27,0.61,3.83l0,0
c1.07,0.56,2.59,0.02,3.49-1.7C82.61,54.8,82.17,53.25,81.1,52.69z"/>
</g>
</g>
<path style="fill:#422B0D;" d="M70,92.41c-0.56,0-1.12,0.12-1.66,0.36c-5.96,2.72-11.34,0.17-11.56,0.06
c-1.98-0.96-4.37-0.16-5.35,1.81c-0.98,1.97-0.2,4.35,1.76,5.34c0.36,0.18,8.92,4.42,18.47,0.07c2.01-0.92,2.9-3.29,1.98-5.3
C72.97,93.28,71.52,92.41,70,92.41z"/>
<g>
<g>
<g>
<linearGradient id="SVGID_3_" gradientUnits="userSpaceOnUse" x1="48.1545" y1="75.9095" x2="0.5267" y2="123.9094" gradientTransform="matrix(-0.5681 -0.823 -0.823 0.5681 181.4017 36.6179)">
<stop offset="0" style="stop-color:#FFF176"/>
<stop offset="1" style="stop-color:#FFC400"/>
</linearGradient>
<path style="fill:url(#SVGID_3_);" d="M76.68,97.9c-1.46-1.9-4.02-6.07-8.8-12.22c-3.34-4.29-15.77-12.94-19.08-17.47
c-1.71-2.34-3.15-4.69-2.23-6.42c3.06-5.77,6.93-0.85,6.93-0.85s-8.84-4.91-3.11-10.09c2.51-2.26,4.94-1.99,10.12,1.87
c0,0,22.27,15.35,24.14,14.07c1.86-1.29-4.14-25.54-4.44-33.36c-0.16-4.22-0.34-7.65,3.56-8.7c5.68-1.54,6.35,6.44,6.24,5.29
s1.53-4.16,5.61-4.96c3.16-0.63,4.72,3.41,5.74,9.46c1.02,6.05,6.61,28.07,9.29,29.92c0,0,0.94-11.93,6.74-16.18
c2.63-1.93,7.22-2.7,7.79,0.56c0.33,1.92,0.33,5.24-1.19,10.15c-1.31,4.21-1.22,14.66-0.76,22.35
c0.39,6.49,1.99,16.95-12.5,24.92C94.83,114.97,83.09,106.19,76.68,97.9z"/>
</g>
</g>
<path style="fill:#EB8F00;" d="M79.53,33.75c-0.35-3.34-0.51-6.02,1.84-8.25c2.59-2.47,5.47-2.42,6.92-0.95
c0.61,0.62,1.23,1.59,1.84,3.05c0.94-2.41,2.69-3.48,5.16-3.84c2.87-0.42,5.29,1.68,6.33,6.21c0.62,2.69,7.14,28.2,8.41,29.62
c0,0,0.02-9.3,8.64-13.4c2.36-1.12,5.12-1.02,7.22,1.22s-0.48,6.31-2.16,16.97c-0.91,5.78-0.65,12.64-0.65,12.64
s-2.74-3.68-1.4-16.56c0.5-4.83,3.87-9.86,1.44-11.66c-1.57-1.16-3.16-1.39-6.13,1.35c-6.11,5.64-4.6,14.79-4.94,15.93
c-0.34,1.14-1.49,1.8-3.53-3.61c-2.03-5.4-7.29-25.43-8.91-31.6c-1.31-4.98-3.14-5.39-5.42-4.18c-2.84,1.5-2,6.24-1.59,7.79
c0.34,1.28,3.6,18.82,5.39,24.87c0.38,1.28,1.37,3.26,1.24,4.17c-0.15,1-3.58,2.94-4.2-2.23c-0.68-5.67-4.96-26.41-4.96-26.41
s-0.67-4.28-1.9-6.97c-0.56-1.23-2.28-2.65-5.12-0.41c-2.43,1.92-1.09,7.71-1.09,7.71l4.62,28.09c0,0,0.73,3.29-0.65,4.56
c-1.6,1.48-4.39-0.65-4.39-0.65s-19.29-12.73-22.3-14.37c-3-1.64-4.85-3.55-7.69-1.72c-0.82,0.53-2.46,2.84,0.29,5.21
c2.75,2.37,11.91,9.7,22.66,16.79c0.62,0.42,5.21,1.67,3.3,4.76c-1.9,3.09-5.06-1.58-5.62-2.01C65.03,70.3,56.9,63.91,52.14,61.32
c-3.58-1.95-5.73,2.12-2.35,5.87c2.21,2.46,5.63,5.66,9.6,9.01c3.88,3.27,7.09,6.55,8.77,8.75c4.11,5.36,5.9,8.38,8.69,12.3
c11.9,14.34,24.49,13.81,38.02,6.33c-1.76,2.01-25.49,19.29-42.38-7.45c-1.53-2.28-3.63-5.4-6.68-9.39
c-1.49-1.95-4.78-4.95-8.33-8.29S43.56,66.27,45.27,61.46c1.17-3.28,4.1-3.44,4.1-3.44s-4.38-5.13,1.29-9.15
c3.08-2.18,7.76,1.14,7.76,1.14l22.1,14.21c0,0,0.95,0.71,2.14,1.31c1.59,0.79,1.48-1.54,1.39-2.1
C83.17,57.94,79.81,36.46,79.53,33.75z"/>
</g>
<g>
<radialGradient id="prop-2_1_" cx="58.7255" cy="87.6971" r="71.7862" gradientTransform="matrix(0.7789 -0.6272 -0.6272 -0.7789 35.3961 162.4946)" gradientUnits="userSpaceOnUse">
<stop offset="0.27" style="stop-color:#FFF176"/>
<stop offset="1" style="stop-color:#FFC400"/>
</radialGradient>
<path id="prop-2" style="fill:url(#prop-2_1_);" d="M56.81,39.61c-4.11-1.2-6.83,5.6-6.83,5.6s0.77-4.32,1.27-8.1
c0.32-2.42-0.74-6.19-3.39-7.19c-2.66-0.99-5.83,1.76-6.64,4.05l-3.19,8.94c0,0,1.53-2.93-1.02-7.57c-0.87-2-3.2-2.91-5.2-2.04
c-0.38,0.16-0.72,0.38-1.03,0.66c-1.38,1.33-2.36,3.02-2.83,4.88c-1.95,6.9-7.48,24.93-10.37,26.91c-1.13,0.77-1.35,0.68-2.96-5.88
c-0.81-4.06-3.38-7.54-7.03-9.5c-2.11-1.13-5.86,1.11-5.59,3.62c0.18,1.86,2.02,5.32,3.05,9.45c1.64,6.82,1.6,17.8,1.68,18.8
c0.61,7.33,1.76,20.98,16,25.86c12.61,4.33,25.57-5.43,30.18-12.97C61.75,80.8,66.36,63.2,66.43,62.86
c0.81-3.6,0.75-7.87-2.11-9.51c-3.52-2.04-5.9,2.77-5.9,2.77c1.12-3.1,1.74-6.36,1.82-9.65C60.18,43.17,59.58,40.43,56.81,39.61z"
/>
<path style="fill:#EB8F00;" d="M61.9,46.43c-0.04-2.12-0.13-7.08-4.62-8.41c-1.6-0.45-3.17-0.28-4.55,0.5
c0.67-3.33-0.32-9.04-5.27-10.41c-1.84-0.51-4.27-0.12-6.41,2.61c-1.55,1.97-2.12,4.74-2.12,4.74c-0.13-0.28-2.03-5.71-8.04-2.59
c-2.42,1.26-3.68,4.53-4.2,6.65C23.32,51.4,17.4,64.9,17.15,63.89c0.08-6.68-4.04-13.16-8.91-15.04c-2.11-1.13-7.53,1.13-7.07,4.65
c0.38,2.89,3.32,8.13,4.08,13.58c0.59,4.21,0.7,20.26,3.2,26.23c0,0-0.38-4.36-0.6-10.77c-0.24-6.83-0.36-15.83-1.2-19.5
c-0.73-3.28-2.45-6.53-3.01-9.23c-0.16-1.18,1.17-3.51,4.41-2.07c3.24,1.44,5.03,4.91,5.97,9.62c1.29,5.25,0.61,6.05,2.1,6.23
c4.99,0.61,11.71-23.34,13.43-28.35c0.39-1.56,1.21-2.98,2.31-4.04c2.09-2.23,4.62,1.16,4.62,1.16c1.52,3.24,1.1,3.58,0.23,8.02
c-0.62,3.16-3.43,13.55-6.05,19.3c0,0,0.88,0.94,2.46-1.28c1.58-2.22,5.53-13.73,7.07-20.67c0,0,1.75-9.3,4.68-10.55
c2.98-1.27,4.11,0.59,4.64,2.8s0.1,5.73-1.12,10.66c-2.19,8.85-6.23,21.76-6.23,21.76s0.87,0.72,2.18-1.14
c2.65-3.79,7.01-20.51,7.01-20.51c0.31-1.33,2.41-4.5,5-3.55c1.29,0.39,2.3,1.93,2.2,6.05c-0.1,4.94-3.51,16.16-7.08,25.18
c0,0,1.18,0.44,2.49-1.64s5.13-12.67,6-14.03c0.87-1.36,1.75-2.68,3.66-1.92c2.5,1.18,2.47,3.72,1.57,7.7
c-0.04,0.17-4.43,18.93-13.37,32.99c-4.72,7.43-12.9,12.15-25.6,13.41c9.97,2.03,23.2-4.13,28.47-12.89
c8.88-14.74,13.69-32.62,13.72-32.78c0.65-2.88,2-8.89-3.48-11.47c-1.31-0.52-2.51-0.53-3.57-0.23
C61.68,49.9,61.86,48.18,61.9,46.43z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 8.2 KiB

View file

@ -0,0 +1,34 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 25.2.3, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Raised-Hand" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px"
y="0px" viewBox="0 0 128 128" style="enable-background:new 0 0 128 128;" xml:space="preserve">
<radialGradient id="face_1_" cx="63.6" cy="-7019.0771" r="56.9597" gradientTransform="matrix(1 0 0 -1 0 -6956.1772)" gradientUnits="userSpaceOnUse">
<stop offset="0.5" style="stop-color:#FDE030"/>
<stop offset="0.9188" style="stop-color:#F7C02B"/>
<stop offset="1" style="stop-color:#F4A223"/>
</radialGradient>
<path id="face_73_" style="fill:url(#face_1_);" d="M63.6,118.8c-27.9,0-58-17.5-58-55.9S35.7,7,63.6,7c15.5,0,29.8,5.1,40.4,14.4
c11.5,10.2,17.6,24.6,17.6,41.5s-6.1,31.2-17.6,41.4C93.4,113.6,79,118.8,63.6,118.8z"/>
<path style="fill:#EB8F00;" d="M111.49,29.67c5.33,8.6,8.11,18.84,8.11,30.23c0,16.9-6.1,31.2-17.6,41.4
c-10.6,9.3-25,14.5-40.4,14.5c-18.06,0-37.04-7.35-48.18-22.94c10.76,17.66,30.99,25.94,50.18,25.94c15.4,0,29.8-5.2,40.4-14.5
c11.5-10.2,17.6-24.5,17.6-41.4C121.6,50.16,118.13,38.84,111.49,29.67z"/>
<path style="fill:#422B0D;" d="M44.67,45.94L44.67,45.94c-4.19,0-8,3.54-8,9.42s3.81,9.42,8,9.42l0,0c4.19,0,8-3.54,8-9.42
S48.86,45.94,44.67,45.94z"/>
<g>
<g id="peepers_57_">
<path style="fill:#896024;" d="M44.28,49.87L44.28,49.87c-1.03-0.72-2.58-0.49-3.58,0.95c-1,1.45-0.67,2.97,0.36,3.69l0,0
c1.03,0.72,2.58,0.49,3.58-0.95C45.64,52.11,45.31,50.59,44.28,49.87z"/>
</g>
</g>
<path style="fill:#422B0D;" d="M83.02,45.94L83.02,45.94c-4.19,0-8,3.54-8,9.42s3.81,9.42,8,9.42l0,0c4.19,0,8-3.54,8-9.42
S87.21,45.94,83.02,45.94z"/>
<g>
<g id="peepers_56_">
<path style="fill:#896024;" d="M82.63,49.87L82.63,49.87c-1.03-0.72-2.58-0.49-3.58,0.95c-1,1.45-0.67,2.97,0.36,3.69l0,0
c1.03,0.72,2.58,0.49,3.58-0.95C84,52.11,83.67,50.59,82.63,49.87z"/>
</g>
</g>
<path style="fill:#422B0D;" d="M89.3,79.3c-2.83,1.7-9.46,4.7-21.51,9.86c-0.02,0.01-0.04,0.02-0.05,0.02
c-0.02,0.01-0.04,0.01-0.06,0.02c-12.06,5.16-18.8,7.88-21.99,8.75c-1.94,0.53-3.6-1.45-1.93-3.98c3.09-4.67,38.48-19.8,43.99-18.81
C90.75,75.7,91.03,78.27,89.3,79.3z"/>
</svg>

After

Width:  |  Height:  |  Size: 2.2 KiB

View file

@ -0,0 +1,46 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 26.3.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Raised-Hand" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px"
y="0px" viewBox="0 0 128 128" style="enable-background:new 0 0 128 128;" xml:space="preserve">
<g>
<g id="neutral-face_00000080885983375266818540000014933881844342678202_">
<path id="mouth_00000064329789738385436900000009578621846817519233_" style="fill:#823E00;" d="M79,88H49c-2.21,0-4-1.79-4-4
s1.79-4,4-4h30c2.21,0,4,1.79,4,4S81.21,88,79,88z"/>
</g>
<path style="fill:#AF6300;" d="M44.67,45.94L44.67,45.94c-4.19,0-8,3.54-8,9.42s3.81,9.42,8,9.42l0,0c4.19,0,8-3.54,8-9.42
S48.86,45.94,44.67,45.94z"/>
<path style="fill:#AF6300;" d="M83.02,45.94L83.02,45.94c-4.19,0-8,3.54-8,9.42s3.81,9.42,8,9.42l0,0c4.19,0,8-3.54,8-9.42
S87.21,45.94,83.02,45.94z"/>
</g>
<linearGradient id="SVGID_1_" gradientUnits="userSpaceOnUse" x1="63.7232" y1="118.8388" x2="63.7232" y2="6.9618">
<stop offset="0.015" style="stop-color:#EB8F00"/>
<stop offset="1" style="stop-color:#FFD54F"/>
</linearGradient>
<path style="fill:url(#SVGID_1_);" d="M63.77,118.84H63.6c-3.99,0-8.01-0.36-11.93-1.07l0.54-2.95c3.75,0.68,7.58,1.03,11.4,1.03
h0.17V118.84z M75.71,117.74l-0.55-2.95c3.78-0.7,7.48-1.76,10.98-3.14l1.1,2.79C83.56,115.89,79.68,117,75.71,117.74z
M40.12,114.54c-3.81-1.47-7.41-3.29-10.72-5.43l1.63-2.52c3.13,2.03,6.55,3.76,10.17,5.15L40.12,114.54z M97.91,108.92l-1.65-2.51
c2-1.32,3.92-2.77,5.69-4.33c1.05-0.93,2.08-1.92,3.04-2.94l2.18,2.07c-1.02,1.08-2.11,2.13-3.23,3.12
C102.06,105.99,100.03,107.53,97.91,108.92z M20.09,101.47c-2.8-2.91-5.24-6.15-7.25-9.63l2.6-1.5c1.89,3.27,4.18,6.32,6.81,9.05
L20.09,101.47z M114.37,91.56l-2.6-1.49c1.86-3.24,3.37-6.75,4.47-10.44l2.88,0.86C117.95,84.39,116.35,88.12,114.37,91.56z
M8.1,80.77c-1.11-3.72-1.86-7.69-2.22-11.79l2.99-0.26c0.35,3.9,1.05,7.67,2.11,11.2L8.1,80.77z M121.34,68.68l-2.99-0.26
c0.16-1.8,0.24-3.66,0.24-5.52c0-2-0.09-3.99-0.27-5.92l2.99-0.28c0.19,2.02,0.28,4.11,0.28,6.2
C121.58,64.85,121.5,66.79,121.34,68.68z M8.85,57.27l-2.99-0.26c0.35-4.1,1.08-8.07,2.18-11.8l2.88,0.85
C9.88,49.6,9.19,53.37,8.85,57.27z M116.13,45.79c-1.12-3.68-2.65-7.18-4.53-10.41l2.59-1.51c2,3.43,3.61,7.15,4.8,11.04
L116.13,45.79z M15.35,35.62l-2.6-1.49c1.99-3.49,4.42-6.74,7.21-9.66l2.17,2.07C19.51,29.29,17.23,32.35,15.35,35.62z
M104.78,26.32c-0.89-0.93-1.85-1.85-2.83-2.72c-1.85-1.62-3.85-3.13-5.95-4.49l1.63-2.52c2.22,1.44,4.34,3.04,6.3,4.76
c1.05,0.93,2.06,1.9,3.01,2.89L104.78,26.32z M30.87,19.31l-1.64-2.51c3.29-2.15,6.89-3.99,10.7-5.47l1.09,2.8
C37.41,15.53,33.99,17.27,30.87,19.31z M85.84,13.96c-3.5-1.34-7.21-2.36-11.01-3.03l0.52-2.96c3.99,0.7,7.88,1.78,11.57,3.19
L85.84,13.96z M52.02,11.02l-0.54-2.95c3.92-0.72,7.93-1.1,11.93-1.11l0.01,3C59.6,9.97,55.77,10.33,52.02,11.02z"/>
<g>
<g id="peepers_00000000943899703814137150000008559515959999254185_">
<path style="opacity:0.4;fill:#FFFFFF;" d="M44.28,49.87L44.28,49.87c-1.03-0.72-2.58-0.49-3.58,0.95c-1,1.45-0.67,2.97,0.36,3.69
l0,0c1.03,0.72,2.58,0.49,3.58-0.95C45.64,52.11,45.31,50.59,44.28,49.87z"/>
</g>
</g>
<g>
<g id="peepers_00000032608173352806590750000000877398772088101567_">
<path style="opacity:0.4;fill:#FFFFFF;" d="M82.63,49.87L82.63,49.87c-1.03-0.72-2.58-0.49-3.58,0.95c-1,1.45-0.67,2.97,0.36,3.69
l0,0c1.03,0.72,2.58,0.49,3.58-0.95C84,52.11,83.67,50.59,82.63,49.87z"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.4 KiB

View file

@ -0,0 +1,23 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 25.2.3, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_5" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 128 128" style="enable-background:new 0 0 128 128;" xml:space="preserve">
<g>
<path style="fill:#EF4D8F;" d="M81.86,35.45c-9.09-1.02-14.68,2.81-16.34,3.73c-0.56,0.31-0.96,0.49-1.31,0.58
c-0.34-0.09-0.75-0.27-1.31-0.58c-1.66-0.92-7.28-4.97-16.34-3.73C28.75,37.89,12.02,56.1,4.89,67.2
c-1.66,2.59-0.92,6.04,1.66,7.72c3.19,2.07,9.82,3.33,15.71,5.22c17.92,5.75,48.15,24.1,68.89,19.54
c23.71-5.21,27.54-22.7,30.72-24.76c0,0,3.33-5.12,1.66-7.72C116.41,56.1,101.27,37.62,81.86,35.45z"/>
<path style="fill:#C51162;" d="M73.46,74.29c-3.12-0.67-15.69-1.37-31.05,0.09c-6.59,0.63-14.4,0.38-16.02-1.73
c-1.62-2.11,0.14-3.49,6.74-4.85C53.44,63.62,72.92,74.18,73.46,74.29z"/>
<path style="fill:#3E2723;" d="M40.93,65.04c-11.02,4.88-26.38,5.21-26.04,3.8c0.28-1.16,7.38-1.39,15.22-4.86
c8.77-3.89,19.74-8.75,34.01-8.75c14.61,0,26.25,5.87,35.07,9.81c4.2,1.88,8.44,3.11,11.36,3.83c1.25,0.31,1.44,2,0.3,2.59
l-14.09,7.22c0,0-7.41,3.26-13.88,0S47.85,61.98,40.93,65.04z"/>
<path style="fill:#BDBDBD;" d="M101.27,65.93c0,0-0.01-0.01-0.01-0.01c-0.69-0.28-1.39-0.56-2.08-0.87
c-8.82-3.94-20.46-9.81-35.07-9.81c-14.27,0-25.24,4.86-34.01,8.75c-0.46,0.2-0.9,0.4-1.35,0.6l0,0c0,0,2.29,8.21,35.99,8.21
S101.27,65.93,101.27,65.93z"/>
<path style="fill:#FFFFFF;" d="M101.27,65.93c0,0-0.01-0.01-0.01-0.01c-0.69-0.28-22.54-7.11-37.15-7.11
c-14.27,0-34.91,5.57-35.36,5.77l0,0c0,0,2.29,8.21,35.99,8.21S101.27,65.93,101.27,65.93z"/>
<path style="fill:#FF80AB;" d="M79.85,83.27c2.51,0.88,6.65,2.03,10.29,2.18c1.35,0.06,4.51-0.16,4.37,1.88
c-0.22,3.09-10.81,7.1-19.25,4.48c-8.57-2.66-14.4-9.04-11.26-10.36C67.11,80.14,74.22,81.29,79.85,83.27z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.9 KiB

View file

@ -0,0 +1,84 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 26.3.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_2" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 128 128" style="enable-background:new 0 0 128 128;" xml:space="preserve">
<radialGradient id="face_00000183927110871212819990000014011780520149750707_" cx="193.5273" cy="-2704.4158" r="49.6592" gradientTransform="matrix(0.8455 -0.5339 -0.5339 -0.8455 -1541.9877 -2121.0471)" gradientUnits="userSpaceOnUse">
<stop offset="0.5" style="stop-color:#FDE030"/>
<stop offset="0.9188" style="stop-color:#F7C02B"/>
<stop offset="1" style="stop-color:#F4A223"/>
</radialGradient>
<path id="face_00000063628597563178067820000001178035299985375403_" style="fill:url(#face_00000183927110871212819990000014011780520149750707_);" d="
M91.56,103.55c-20.57,12.99-50.9,14.1-68.78-14.21s-3.83-55.22,16.74-68.21C50.94,13.92,63.86,11.02,76,12.94
c13.23,2.17,24.42,9.94,32.29,22.4s10.03,25.84,6.3,38.71C111.1,85.85,102.91,96.38,91.56,103.55z"/>
<g>
<defs>
<path id="face_00000177468677908721967140000015650757606444898459_" d="M91.56,103.55c-20.57,12.99-50.9,14.1-68.78-14.21
s-3.83-55.22,16.74-68.21C50.94,13.92,63.86,11.02,76,12.94c13.23,2.17,24.42,9.94,32.29,22.4s10.03,25.84,6.3,38.71
C111.1,85.85,102.91,96.38,91.56,103.55z"/>
</defs>
<clipPath id="face_00000134210471610893173210000013670477117985087906_">
<use xlink:href="#face_00000177468677908721967140000015650757606444898459_" style="overflow:visible;"/>
</clipPath>
</g>
<path id="face_00000151525897067003441860000000483302440425602690_" style="fill:#EEBA4B;" d="M60.08,118.82
c-0.06,0.51-0.51,0.89-1.02,0.85c-5.9-0.47-11.81-1.9-17.26-4.46c-5.78-2.61-10.96-6.45-15.48-10.86
c-4.5-4.47-8.21-9.61-11.32-15.07c-3.03-5.51-5.38-11.49-6.61-17.73C7.2,65.32,6.93,58.85,8.1,52.61c0.44-2.73,1.16-5.4,2.04-8
c0.21-0.62,0.89-0.96,1.51-0.76h0c0.62,0.2,0.97,0.86,0.79,1.49c-0.71,2.51-1.32,5.05-1.62,7.65c-0.85,5.94-0.51,12.02,0.77,17.87
c1.27,5.86,3.5,11.5,6.45,16.72c5.77,10.48,14.25,19.71,24.99,25.16c5.06,2.6,10.59,4.25,16.22,5.03
C59.78,117.84,60.15,118.31,60.08,118.82L60.08,118.82z"/>
<path id="face_00000086667315457375879230000001308986425907973016_" style="fill:#EEBA4B;" d="M3.9,69.68
c0.52,0.05,0.93,0.43,1.02,0.94c0.39,2.1,0.9,4.17,1.52,6.21c2.24,7.46,5.88,14.44,10.33,20.81c4.19,5.98,9.22,11.42,15.11,15.75
c0.63,0.46,0.78,1.32,0.34,1.96l0,0c-0.43,0.63-1.29,0.82-1.94,0.4c-3.05-1.96-5.84-4.32-8.48-6.81c-2.92-2.86-5.53-6.01-7.91-9.3
c-2.25-3.37-4.35-6.85-6.07-10.54c-1.67-3.7-3.13-7.52-4.03-11.5c-0.54-2.19-0.93-4.43-1.17-6.67C2.57,70.22,3.18,69.61,3.9,69.68
L3.9,69.68z"/>
<path id="face_00000003096618102886432540000018273636649996048533_" style="fill:#EEBA4B;" d="M121.73,63.28
c-0.74,0.01-1.35-0.57-1.41-1.31c-0.42-5.32-1.55-10.56-3.48-15.53c-1.98-5.42-4.86-10.54-8.19-15.29
c-3.28-4.79-7.18-9.12-11.66-12.71c-4.1-3.36-8.71-6.09-13.66-8.05c-0.69-0.27-1.06-1.03-0.83-1.74v0c0.24-0.74,1.05-1.16,1.79-0.9
c11.1,3.88,20.59,11.76,27.24,21.39c3.45,5.02,6.43,10.41,8.44,16.18c1.88,5.3,3,10.88,3.19,16.5
C123.16,62.62,122.52,63.28,121.73,63.28L121.73,63.28z"/>
<path id="face_00000070798384020294411340000014144441823226825863_" style="fill:#EEBA4B;" d="M97.68,7.3
c0.15-0.66,0.9-0.99,1.48-0.64c2.94,1.82,5.72,3.9,8.29,6.22c5.86,5.23,10.69,11.53,14.61,18.32c1.63,2.93,3.08,5.97,4.31,9.1
c0.34,0.87-0.12,1.85-0.99,2.17l0,0c-0.84,0.31-1.77-0.11-2.11-0.94c-1.23-2.95-2.64-5.82-4.25-8.6c-1.83-3.28-3.88-6.43-6.12-9.44
c-2.19-3.05-4.68-5.85-7.28-8.56c-2.34-2.39-4.87-4.59-7.57-6.63C97.75,8.06,97.6,7.68,97.68,7.3L97.68,7.3z"/>
<g>
<path style="fill:#EB8F00;" d="M85.37,15.55c7.93,3.86,14.75,10.12,20.05,18.51c7.87,12.46,10.03,25.84,6.3,38.71
c-3.49,11.79-11.68,22.33-23.03,29.49c-13.32,8.41-30.72,11.82-46.19,5.52c16.15,8.01,34.92,4.7,49.06-4.24
c11.35-7.17,19.55-17.7,23.03-29.49c3.73-12.87,1.57-26.25-6.3-38.71C102.36,25.95,94.53,19.22,85.37,15.55z"/>
<g>
<g>
<g id="big-eyes-grinning-face_00000033336518990135324380000003319762986285226685_">
<g id="eyes_00000155132990368273455040000016720763808801284782_">
<path style="fill:#422B0D;" d="M65.92,34.68L65.92,34.68c2.55-1.61,7.25,0.71,11.2,6.98s4.03,11.51,1.48,13.12l0,0
c-2.55,1.61-7.25-0.71-11.2-6.98S63.37,36.28,65.92,34.68z"/>
<path style="fill:#422B0D;" d="M40.88,50.48L40.88,50.48c-2.72,1.72-2.8,7.05,1.16,13.32s8.81,8.49,11.53,6.77l0,0
c2.72-1.72,2.8-7.05-1.16-13.32S43.6,48.77,40.88,50.48z"/>
</g>
</g>
<g id="peepers_00000017516456057931630050000008656183960350421163_">
<path style="fill:#896024;" d="M42.28,53.04L42.28,53.04c-0.99-0.04-1.94,0.76-2,2.15c-0.06,1.39,0.81,2.27,1.8,2.32l0,0
c0.99,0.04,1.94-0.76,2-2.15S43.28,53.08,42.28,53.04z"/>
</g>
<g id="peepers_00000077303468335883764480000001602613450775713458_">
<path style="fill:#896024;" d="M67.84,36.9L67.84,36.9c-0.99-0.04-1.94,0.76-2,2.15c-0.06,1.39,0.81,2.27,1.8,2.32l0,0
c0.99,0.04,1.94-0.76,2-2.15C69.7,37.82,68.83,36.94,67.84,36.9z"/>
</g>
</g>
<path style="fill:#BF9052;" d="M37.69,77.48c-2.73-2.37-4.95-5.1-6.61-8.06c-1.49-2.67-2.52-5.52-3.03-8.47
c-0.06-0.37-0.51-1.02-1.25-0.99s-0.97,0.72-0.99,1.02c-0.16,3.31,0.74,6.51,2.56,9.65c2.16,3.74,4.67,6.27,7.78,8.43
c0.34,0.24,1.12,0.59,1.7-0.1S37.96,77.71,37.69,77.48z"/>
<g>
<path style="fill:#896024;" d="M39.57,65.36c-2.45-3.87-3.35-7.39-3.04-9.92c-1.49-0.5-2.84-0.42-3.88,0.23
c-2.72,1.72-2.8,7.05,1.16,13.32s8.81,8.49,11.53,6.77c1.04-0.65,1.69-1.84,1.88-3.4C44.81,71.56,42.02,69.24,39.57,65.36z"/>
</g>
<path style="fill:#BF9052;" d="M90.75,43.97c-0.97-3.48-2.48-6.66-4.44-9.43c-1.77-2.49-3.9-4.65-6.35-6.37
c-0.31-0.22-0.7-0.89-0.36-1.55c0.34-0.66,1.07-0.57,1.35-0.45c3.06,1.27,5.56,3.46,7.62,6.46c2.44,3.56,3.65,6.91,4.26,10.65
c0.07,0.41,0.05,1.27-0.82,1.49S90.85,44.31,90.75,43.97z"/>
<g>
<path style="fill:#896024;" d="M79.01,40.46c-2.45-3.87-5.24-6.2-7.65-7.01c0.19-1.56,0.84-2.75,1.88-3.4
c2.72-1.72,7.57,0.51,11.53,6.77s3.87,11.6,1.16,13.32c-1.04,0.65-2.39,0.73-3.88,0.23C82.35,47.85,81.45,44.34,79.01,40.46z"/>
</g>
</g>
<path style="fill:#422B0D;" d="M68.55,86.57c-1.21-2-1.97-4.95-1.48-8.14c0.98-6.44,6.85-10.15,13.09-8.31
c5.91,1.74,10.09,8.12,9.34,14.25c-0.88,7.15-7.75,11.05-14.45,8.18C72.49,91.44,70.48,89.68,68.55,86.57z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 6.2 KiB

View file

@ -0,0 +1,57 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 26.3.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="BAGS_UNDER_EYES" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px"
y="0px" viewBox="0 0 128 128" style="enable-background:new 0 0 128 128;" xml:space="preserve">
<radialGradient id="face_00000088816004770144277250000009556130401377754250_" cx="63.22" cy="-86.9" r="56.9597" gradientTransform="matrix(1 0 0 -1 0 -24)" gradientUnits="userSpaceOnUse">
<stop offset="0.5" style="stop-color:#FDE030"/>
<stop offset="0.92" style="stop-color:#F7C02B"/>
<stop offset="1" style="stop-color:#F4A223"/>
</radialGradient>
<path id="face" style="fill:url(#face_00000088816004770144277250000009556130401377754250_);" d="M63.22,118.8
c-27.9,0-58-17.5-58-55.9S35.32,7,63.22,7c15.5,0,29.8,5.1,40.4,14.4c11.5,10.2,17.6,24.6,17.6,41.5s-6.1,31.2-17.6,41.4
C93,113.6,78.62,118.8,63.22,118.8z"/>
<path style="fill:#EB8F00;" d="M111.28,29.89c5.33,8.6,8.32,18.62,8.32,30.01c0,16.9-6.1,31.2-17.6,41.4
c-10.6,9.3-25,14.5-40.4,14.5c-18.06,0-37-7.35-48.18-22.94c10.76,17.66,31,25.94,50.18,25.94c15.4,0,29.8-5.2,40.4-14.5
c11.5-10.2,17.6-24.5,17.6-41.4C121.6,50.16,117.92,39.06,111.28,29.89z"/>
<path style="fill:#422B0D;" d="M82.4,100.1c-4.4,0.3-32.3,1.3-35.1,0.8c-3.8-0.7-3.5-5.9,0.3-6.4c1.8-0.2,12.5-1.2,17.6-1.2
s8.3,0.5,10.3,0.8c1.8,0.3,6.2,1.5,7.2,1.7C84.8,96.4,84.8,99.9,82.4,100.1z M100.4,40.2C99,39.3,89,35.7,77,38.4
c-3.2,0.7-2.1,5.1,0.8,4.5c10.4-2.2,18.8,0.3,21.2,0.7S101.8,41.1,100.4,40.2z"/>
<path style="fill:#EB8F00;" d="M98.5,64.2c4.5-1.9,6.6,2.2,5.7,5.8c-2.3,8.9-22.6,16.7-32.2,0.7c-2.8-4.6,2.3-7.4,5.2-5.3
C84.9,71,93.9,66.1,98.5,64.2z M29.8,65.2c-2.2-2.2-6.6,0-5.9,3.6c2.8,13.9,27.4,15.9,32.1,1.4c1.2-3.9-2.1-6.4-5.7-4.4
C41.5,70.7,34.7,69.9,29.8,65.2z"/>
<path style="fill:#FFFFFF;" d="M72.9,54.8c6.1-0.2,20.7,0.3,26.9,0.8c2.2,0.2,2.4,2.1,2.2,3.4c-2.7,15.8-29.5,18.7-31.4-1.2
C70.4,55.1,71.8,54.8,72.9,54.8z M27.6,56.8c-1.7,0.3-2.4,2-2,3.7c3.7,15.6,30.3,15.4,31.5-3c0-1.7-0.6-3.1-2.7-3.1
C49.8,54.4,34.1,55.8,27.6,56.8z"/>
<g>
<g>
<defs>
<path id="SVGID_1_" d="M72.9,54.8c6.1-0.2,20.7,0.3,26.9,0.8c2.2,0.2,2.4,2.1,2.2,3.4c-2.7,15.8-29.5,18.7-31.4-1.2
C70.4,55.1,71.8,54.8,72.9,54.8z M27.6,56.8c-1.7,0.3-2.4,2-2,3.7c3.7,15.6,30.3,15.4,31.5-3c0-1.7-0.6-3.1-2.7-3.1
C49.8,54.4,34.1,55.8,27.6,56.8z"/>
</defs>
<clipPath id="SVGID_00000085220233596697445750000011632254972251972495_">
<use xlink:href="#SVGID_1_" style="overflow:visible;"/>
</clipPath>
<g style="clip-path:url(#SVGID_00000085220233596697445750000011632254972251972495_);">
<path style="fill:#422B0D;" d="M92.3,54.4c2.7,4.5-0.4,10.4-6.2,10.4s-8.9-5.4-5.3-10.8C83,50.6,89.8,50.2,92.3,54.4z M35.7,54.8
c-2.9,4.9,0.9,10.1,5.6,10.1s8.2-6.2,5.3-10.4C44.3,51.2,37.8,51.2,35.7,54.8z"/>
</g>
</g>
<g>
<defs>
<path id="SVGID_00000002378470936803287900000015685046093406688959_" d="M72.9,54.8c6.1-0.2,20.7,0.3,26.9,0.8
c2.2,0.2,2.4,2.1,2.2,3.4c-2.7,15.8-29.5,18.7-31.4-1.2C70.4,55.1,71.8,54.8,72.9,54.8z M27.6,56.8c-1.7,0.3-2.4,2-2,3.7
c3.7,15.6,30.3,15.4,31.5-3c0-1.7-0.6-3.1-2.7-3.1C49.8,54.4,34.1,55.8,27.6,56.8z"/>
</defs>
<clipPath id="SVGID_00000009573153617938949080000012364698636391462039_">
<use xlink:href="#SVGID_00000002378470936803287900000015685046093406688959_" style="overflow:visible;"/>
</clipPath>
<g style="clip-path:url(#SVGID_00000009573153617938949080000012364698636391462039_);">
<path style="fill:#896024;" d="M41.5,56.1c0,1.3-1.1,2.4-2.5,2.4s-2.5-1.1-2.5-2.4s1.1-2.4,2.5-2.4S41.5,54.8,41.5,56.1z
M83.8,53.4c-1.4,0-2.5,1.1-2.5,2.5s1.1,2.5,2.5,2.5s2.5-1.1,2.5-2.5S85.2,53.4,83.8,53.4z"/>
</g>
</g>
</g>
<path style="fill:#422B0D;" d="M53.18,36.85c-1.48-0.76-11.79-3.34-23.46,0.55c-3.11,1.02-1.58,5.28,1.25,4.4
c10.13-3.23,18.74-1.58,21.16-1.43S54.67,37.6,53.18,36.85z"/>
</svg>

After

Width:  |  Height:  |  Size: 3.8 KiB

View file

@ -0,0 +1,68 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 26.3.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_2_00000012443026519394611760000004431583338484677266_"
xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 128 128"
style="enable-background:new 0 0 128 128;" xml:space="preserve">
<path style="fill:#FFCA28;" d="M54.8,14.4c0,0,3.2-3.8,5.1-6c4.7-5.5,13.8-2.8,11.7,5C70,18.9,65.3,28.1,64.2,30
c0.4,1.7,0.9,3.6,1.1,4.4C69.8,33,78,36.3,76.1,46c5.8-6.7,17.4-0.9,13.6,10.5c2-2.1,7.3-0.8,9.3,1.8c1.8,2.2,2.7,3.7,2.8,6.9
c0.1,3.1-0.1,5.1-2.3,7.2c-0.1,4.7-1.2,9.6-1.9,10.4c2.8,7.6-0.6,17.5-5.5,22.1c-1.5,3.6-3.4,8.4-4.2,11c-0.7,2.6-6.4,5.1-9,5.5
c-5.6,0.9-19.9,0.4-27-4c-3-1.8-5.5-3.2-5.3-6c0.1-2.1,0.9-8.4,1-9.7c0.1-1.3,0.1-4.2-1.9-6.4C41.8,91,33,84.7,27.1,74
c-1.9-3.5-1.5-10.6,0.4-16.2s8.3-16.7,17-28.2c-2.8-5.1-5.7-11.1-6.8-15.3c-1.8-6.9,4.3-9.9,8.7-7.9C50.3,8.1,54.8,14.4,54.8,14.4z"
/>
<g>
<g>
<defs>
<path id="SVGID_1_" d="M54.8,14.4c0,0,3.2-3.8,5.1-6c4.7-5.5,13.8-2.8,11.7,5C70,18.9,65.3,28.1,64.2,30c0.4,1.7,0.9,3.6,1.1,4.4
C69.8,33,78,36.3,76.1,46c5.8-6.7,17.4-0.9,13.6,10.5c2-2.1,7.3-0.8,9.3,1.8c1.8,2.2,2.7,3.7,2.8,6.9c0.1,3.1-0.1,5.1-2.3,7.2
c-0.1,4.7-1.2,9.6-1.9,10.4c2.8,7.6-0.6,17.5-5.5,22.1c-1.5,3.6-3.4,8.4-4.2,11c-0.7,2.6-6.4,5.1-9,5.5c-5.6,0.9-19.9,0.4-27-4
c-3-1.8-5.5-3.2-5.3-6c0.1-2.1,0.9-8.4,1-9.7c0.1-1.3,0.1-4.2-1.9-6.4C41.8,91,33,84.7,27.1,74c-1.9-3.5-1.5-10.6,0.4-16.2
s8.3-16.7,17-28.2c-2.8-5.1-5.7-11.1-6.8-15.3c-1.8-6.9,4.3-9.9,8.7-7.9C50.3,8.1,54.8,14.4,54.8,14.4z"/>
</defs>
<clipPath id="SVGID_00000150089440312502309920000002382018553492475035_">
<use xlink:href="#SVGID_1_" style="overflow:visible;"/>
</clipPath>
<g style="clip-path:url(#SVGID_00000150089440312502309920000002382018553492475035_);">
<path style="fill:#FAA700;" d="M48.7,116.1c0,0,1.1-12.1,1.2-14.6c4,4.1,14.8,15.6,33,20.6c10.1,2.8,18.1-28,18.1-28l-0.5-13.7
c0,0-3.6-3.1-5.1,2s-4.2,11.1-8.1,13.3c-2.9,1.6-5.8,0.8-7.2,0.3c-1.4-0.6-3.7-2-6.6-1.7c-3.8,0.4-8.1-0.5-10.1-2.3
s-4.9-3.7-9.5-3.9c-4.6-0.2-7.7-1.1-9.7-3.9c-1.9-2.6-4.3-4.3-7.2-4.8c-3.5-0.5-6.3-1.9-7.7-5.4c-1.4-3.5-1.1-9.5,0.8-14.3
c3.2-8.4,12.5-23.3,14-26.1c1.6-2.9-4.8-2.3-4.8-2.3L14.6,64l17.9,53.7l10.7,1.5L48.7,116.1z"/>
<path style="fill:#FAA700;" d="M37.7,18.6c0.7,0.8,3.8,0.6,2.7-3.6c-1.8-7,4.7-8,7.2-5.4c1.2,1.3,2.2,2.5,2.7,3.1
c0.2,0.3,0.3,0.6,0.1,1c-0.4,1-1.9,3.4-7.1,9.3c-2,2.2,2.5,6.2,5.7,2.4C52.6,21.3,59.6,12,61.3,10c3.9-4.9,9.1-2.1,8.2,3.4
s1.7,5.3,1.7,5.3L76.6,8L63.2,1.7L35.5,3.8L37.7,18.6z"/>
<path style="fill:#FAA700;" d="M69.3,32.1c-7.6-0.7-9.1,5.5-6.8,5.2c4.1-0.5,12.4,0.6,12.3,9.3c0,1.5,1.1,1.4,1.7,1
c3.8-2.2,13.6-1.2,11.9,9.6c-0.2,1.5,1.1,1.5,1.5,1.1c1.8-1.6,6.8-1.4,8.2,2.4c0.5,1.2,0.4,3.2,0.5,4.6c0.1,1.4,0.2,7,0.2,7.6
s1.4,1.5,2.3,0c1.2-1.9,4.6-6,4.5-11C105.5,57,98.2,34.9,69.3,32.1z"/>
</g>
</g>
</g>
<g>
<g>
<defs>
<path id="SVGID_00000093882312774502917520000008863851808784800921_" d="M54.8,14.4c0,0,3.2-3.8,5.1-6c4.7-5.5,13.8-2.8,11.7,5
C70,18.9,65.3,28.1,64.2,30c0.4,1.7,0.9,3.6,1.1,4.4C69.8,33,78,36.3,76.1,46c5.8-6.7,17.4-0.9,13.6,10.5c2-2.1,7.3-0.8,9.3,1.8
c1.8,2.2,2.7,3.7,2.8,6.9c0.1,3.1-0.1,5.1-2.3,7.2c-0.1,4.7-1.2,9.6-1.9,10.4c2.8,7.6-0.6,17.5-5.5,22.1c-1.5,3.6-3.4,8.4-4.2,11
c-0.7,2.6-6.4,5.1-9,5.5c-5.6,0.9-19.9,0.4-27-4c-3-1.8-5.5-3.2-5.3-6c0.1-2.1,0.9-8.4,1-9.7c0.1-1.3,0.1-4.2-1.9-6.4
C41.8,91,33,84.7,27.1,74c-1.9-3.5-1.5-10.6,0.4-16.2s8.3-16.7,17-28.2c-2.8-5.1-5.7-11.1-6.8-15.3c-1.8-6.9,4.3-9.9,8.7-7.9
C50.3,8.1,54.8,14.4,54.8,14.4z"/>
</defs>
<clipPath id="SVGID_00000124159253594305469440000001827886973601835951_">
<use xlink:href="#SVGID_00000093882312774502917520000008863851808784800921_" style="overflow:visible;"/>
</clipPath>
<g style="clip-path:url(#SVGID_00000124159253594305469440000001827886973601835951_);">
<path style="fill:#B55E19;" d="M25.2,73c1.6-0.6,3-0.4,4.9,2.7c2.4,3.9,9.8,11.4,16.5,17.3c1.7,1.5,3.7,3.9,3.3,8.4
c-0.2,2.6,0.1,7.2-3.1,7.7c-3.2,0.6-6.2-7.3-6.2-7.3S24,75.7,24,74.9S25.2,73,25.2,73z"/>
<path style="fill:#B55E19;" d="M89.7,113c0,0,0.8-7.3,1.1-10.2c1.5-1.6,6.6-6.7,5.2-16.3c-0.3-2.2-0.8-4,0.1-6.2
c1.3-3.3,2.3-14,2.3-14.7c0-1,1.5-1,1.5,0c0,0.8-0.3,6.8-0.3,6.8l1.9,2.3l1.9,23.6l-8.3,15.1L89.7,113z"/>
<path style="fill:#B55E19;" d="M39.4,6.7c-0.2,1.7-0.5,5.2,0.6,7.6c1.7,4,3.7,7.7,4.3,8.5c0.5,0.6,1.2,0.6,1.6,0.3
c1-0.7,4.5-2.9,5.1-3.3c0.8-0.6,2.5-0.3,1.5,1.5C51.4,23,44,34,42.7,36c-1.4,2-3.7,2.6-4.6,2S33.3,9.3,33.3,9.3L39.4,6.7z"/>
<path style="fill:#B55E19;" d="M69.1,5.8c0.7,1.7,1.4,5,0.2,7.7c-2.2,4.9-15.1,29.1-21,39.7c-0.7,1.3-1.8,2-2.2,2.9
c-1.6,3,2.8,5.6,4.6,2.7c1.1-1.8,0-2.6,1-4.3c1.4-2.5,7.9-15,8.9-16.5c1-1.6,2.8-3,4.8-3.6c1.8-2.2,4.6-5.6,4.6-5.6L78.1,12
l-4.4-8L69.1,5.8z"/>
<path style="fill:#B55E19;" d="M64.5,63.7c-0.5,0.9-1.9,1.5-2.4,2.4c-1.4,2.6,2.8,4.6,4.2,2.4c0.7-1.2,0.3-2.3,0.8-3.3
s9-16.9,9.2-17.9c0.3-1-0.7-1.6-1.3-0.9C74.3,47.3,65,62.8,64.5,63.7z"/>
<path style="fill:#B55E19;" d="M80.5,73.1c-0.4,0.7-1.3,0.9-1.8,1.8c-1.3,2.5,3.1,4.8,4.3,2.2c0.4-0.9,0-1.7,0.3-2.5
c0.4-0.8,4.1-10.6,6.6-16.8c0.4-1.1-0.7-1.7-1.3-0.6C86.7,61,80.5,73.1,80.5,73.1z"/>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 5.2 KiB

View file

@ -0,0 +1,65 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 26.3.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_2_00000072270158875804069490000012580625440359467709_"
xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 128 128"
style="enable-background:new 0 0 128 128;" xml:space="preserve">
<path style="fill:#FFCA28;" d="M80,97.3c-1.7,7.6-6.2,12.3-13.5,12.6c0.4,3.2-3.3,10.4-16.7,10.4c-3.2,0-11.3-0.1-19.1-6.9
C26,109.3,14.5,100.3,10,90c-2.1-4.9-3.7-9.4-2.3-13c2.5-6.6,8.4-5,7.1-13.9c-0.5-3.5,0.6-10.6,7.8-13.1c2.7-0.9,6.1-4.2,5.4-9
s2.2-11.8,11.8-14.2c10.8-2.8,6.5-12,15.8-16.6c9.8-5,17.9-1.7,22.2,5.4c1.6,2.6,3.1,5,3.7,11.2c7.6,9.1,13.5,22.3,22.7,26.7
c2.7,1.3,8.9,2.4,12,10.5c1.6,4.1,3.2,8.6,3.8,10.2c2,5.5-0.4,13.3-5.5,16.3c-6.5,3.8-13.5,9.2-16.3,12.1
C97.3,103.4,86.9,111.3,80,97.3z"/>
<g>
<g>
<defs>
<path id="SVGID_1_" d="M80,97.3c-1.7,7.6-6.2,12.3-13.5,12.6c0.4,3.2-3.3,10.4-16.7,10.4c-3.2,0-11.3-0.1-19.1-6.9
C26,109.3,14.5,100.3,10,90c-2.1-4.9-3.7-9.4-2.3-13c2.5-6.6,8.4-5,7.1-13.9c-0.5-3.5,0.6-10.6,7.8-13.1c2.7-0.9,6.1-4.2,5.4-9
s2.2-11.8,11.8-14.2c10.8-2.8,6.5-12,15.8-16.6c9.8-5,17.9-1.7,22.2,5.4c1.6,2.6,3.1,5,3.7,11.2c7.6,9.1,13.5,22.3,22.7,26.7
c2.7,1.3,8.9,2.4,12,10.5c1.6,4.1,3.2,8.6,3.8,10.2c2,5.5-0.4,13.3-5.5,16.3c-6.5,3.8-13.5,9.2-16.3,12.1
C97.3,103.4,86.9,111.3,80,97.3z"/>
</defs>
<clipPath id="SVGID_00000060735560097370283170000000082404634594780842_">
<use xlink:href="#SVGID_1_" style="overflow:visible;"/>
</clipPath>
<g style="clip-path:url(#SVGID_00000060735560097370283170000000082404634594780842_);">
<path style="fill:#FAA700;" d="M79.5,25.9c-0.6-3.2-3.9-15.1-14.4-15.7c-2.9-0.2-10.9,0.6-14.4,9.6c-1.1,2.8-1.6,7.8-10.3,9.8
c-4.2,1-10.3,3.2-9.5,12c0.4,4-0.7,9.4-7.4,11.4c-2.1,0.6-7.2,3.8-5.9,10.5c0.9,4.9-0.2,8.3-4.3,11.1c-1.2,0.9-4.3,4.5-2.2,10.6
c2.8,8.2,16.9,27.3,31.5,28.9c4.4,0.5,9.4-5.6,5.3-10.8c-1.2-1.5,0.7-3.5,2.9-1.9s7.6,3.2,13.4-3.3c2.5-2.8,4.1-5.8,0.4-11.6
c-1.9-3-3.1-6.3-0.9-4.9c4.7,3,9.3,1.5,13.8-3.9c4-4.6,5.9-5.9,3-14.3c-1.1-3.1-10.4-14.2-10.4-14.2l10.6-23.6l1.7-0.2l30.7,30.1
c0,0-0.2,3.2-4.6,1.4s-11.1-4.4-14.2-9s-7.9-10.6-8.5-11.6c-0.9-1.4-2.9-1-2,1.1c7.5,19,2.8,23.2,4.4,26.7
c0.7,1.6,2.9-0.2,4.6-4.1c0.9-2.1,4.7-1.8,2,8.9c-0.6,2.4-3.2,5.5-3.7,6.2c-1.3,1.8-1,4.6,1.2,2.8c2.8-2.2,7.1-4.9,10.3-5.6
c3.2-0.7,4.6,0.9,5.5,2.1s4.8,5.4,5.6,6.2c0.8,0.9,2.8,2.7,0.8,5.6c-1.4,1.9-17.1,19.8-17.1,19.8l-40.3,16.8h-22L5.5,91.1L3,72.4
l22.8-45L58.2,4.5l19.2,1.7l5,16.6L79.5,25.9z"/>
<path style="fill:#FAA700;" d="M71.5,19.5c-7.5,0-12.2,3.3-15.8,8.6c-0.7,1-2.7,0.5-2-1.3c3.1-8.2,10-11.9,18.3-9.9
C73.5,17.3,73.6,19.5,71.5,19.5z"/>
</g>
</g>
</g>
<g>
<g>
<defs>
<path id="SVGID_00000019653605689884674110000008513767694685607826_" d="M80,97.3c-1.7,7.6-6.2,12.3-13.5,12.6
c0.4,3.2-3.3,10.4-16.7,10.4c-3.2,0-11.3-0.1-19.1-6.9C26,109.3,14.5,100.3,10,90c-2.1-4.9-3.7-9.4-2.3-13
c2.5-6.6,8.4-5,7.1-13.9c-0.5-3.5,0.6-10.6,7.8-13.1c2.7-0.9,6.1-4.2,5.4-9s2.2-11.8,11.8-14.2c10.8-2.8,6.5-12,15.8-16.6
c9.8-5,17.9-1.7,22.2,5.4c1.6,2.6,3.1,5,3.7,11.2c7.6,9.1,13.5,22.3,22.7,26.7c2.7,1.3,8.9,2.4,12,10.5c1.6,4.1,3.2,8.6,3.8,10.2
c2,5.5-0.4,13.3-5.5,16.3c-6.5,3.8-13.5,9.2-16.3,12.1C97.3,103.4,86.9,111.3,80,97.3z"/>
</defs>
<clipPath id="SVGID_00000180356263846920664810000006122165984388409773_">
<use xlink:href="#SVGID_00000019653605689884674110000008513767694685607826_" style="overflow:visible;"/>
</clipPath>
<g style="clip-path:url(#SVGID_00000180356263846920664810000006122165984388409773_);">
<path style="fill:#B55E19;" d="M96.1,104.3c0.8-1,3.8-4.3,5.9-7.5s5.9-5.5,8.2-6.8c3.3-2,9-7.4,7.1-14.8c-0.6-2.3-3.3-9.8-3.7-11
c-0.7-2-2.6-5.5-7.4-7.8c-2.3-1.1-6.4-4.1-7.1-4.8s-1.3-2-0.4-3.1c0.9-1.2,2.9-0.4,2.9-0.4l17,9.1l6.6,24.4l-5.1,10
c0,0-22.4,13.7-23.2,14.1S95.3,105.3,96.1,104.3z"/>
<path style="fill:#B55E19;" d="M24.8,108.3c17.2,13.1,32,9,34,7.7c2.4-1.5,4.2-2.8,4.6-6c-11-2.6-13.5-2.6-28.3-20.5
c-3-3.6-4.5-2.2-6.1-4.2c-0.9-1.1,0.6-2.9,1.1-3.5c1.1-1.3,2.8-2,3.9-0.8c1.7,1.8-0.2,1.8,3.4,5.6c14.5,15.8,15,18.8,27.9,20.6
c3,0.4,10.8-3.2,11.7-12.3c-8.6-2.6-14.9-9.8-28.4-24.2c-1.7-1.8-3.2-0.8-4.1-2.2c-1.2-1.8,0.9-3.4,2-4.2
c1.1-0.7,2.6-1.6,3.7-0.4c1.1,1.3,0.4,1.5,2,3.4c12.2,14.9,17.1,21.4,22.6,22.3c1.7,0.3,3.7,1,7.8-4.3c3.2-4.2,3.9-6.9,3.1-10.8
c-0.7-3.4-0.9-7.1-17.1-22.8c-5,0.2-13.6-7.6-16.2-18.5c-0.3-1.4,1-1.5,1.5-0.6c5.9,10.8,15.3,7.4,18,5.9
c3-1.6,10.3-6.9,6.4-20.4c-0.2-0.8,0-1.6,0.9-1.8c2.7-0.6,6.5,6.1,2.4,10.3c0.5,2.9,1.9,17.7-7.1,23.8
c8.6,10.5,12.4,15.5,12.8,17c1-6.4,5.7-8,6.4-8.2c0.9-0.2,1.7,0.3,1,1.1s-4.5,3.8-4.8,10.3c-0.1,1.7,1.2,1.4,1.7,0.9
c2-1.9,6.3-4.3,10.2-3.9c1.3,0.2,1.8,1.4,0.2,2c-2.4,0.8-6.2,1.4-9.7,5.8c-1.5,1.8-7.1,10.3-7.6,11.1c-2.5,3.9-2.6,10.8,0,15.1
c1.2,2.1,0,3.1,0,3.1l-22.5,17.8l-26.5-0.2l-14.8-16.7L24.8,108.3z"/>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 4.9 KiB

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