Compare commits

..

96 commits

Author SHA1 Message Date
Peio Rigaux
1156bd78c0 Reduce rights of uploaded files 2026-01-15 12:19:56 +01:00
Ghislain MARY
befa4a2635 Add support of Jabra headsets via the hidapi library. 2026-01-06 17:27:11 +01: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
Gaelle Braud
6d9b5efcc5 hide current call in transfer call list #LINQT-2256 2025-12-15 15:58:22 +01:00
Christophe Deschamps
50ec67298e Update unread count when unread incoming message is retracted 2025-12-12 11:04:00 +01:00
Christophe Deschamps
1bae93aab5 Chat message edition 2025-12-11 15:41:40 +01:00
Christophe Deschamps
d40045d5bb Chat message retraction 2025-12-10 19:20:16 +00: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
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
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
182 changed files with 13799 additions and 4552 deletions

View file

@ -1,5 +1,5 @@
.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
ubuntu2204-ninja-gcc:
@ -71,7 +71,7 @@ ubuntu2204-makefile-gcc-signed:
#################################################
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
needs: []
rules:
@ -100,7 +100,7 @@ ubuntu2204-makefile-gcc-deploy:
ubuntu2204-makefile-gcc-plugins-deploy:
stage: deploy
tags: [ "deploy" ]
tags: [ "deploy-flat" ]
needs:
- ubuntu2204-makefile-gcc
only:

View file

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

View file

@ -26,7 +26,7 @@
.macosx-desktop:
stage: build
tags: [ "macos-min-xcode12.2" ]
tags: [ "macmini-m1-xcode15-flat" ]
rules:
- if: $CI_PIPELINE_SOURCE == "merge_request_event" && $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
macosx-ninja-package:
stage: package
tags: [ "macos-min-xcode12.2" ]
tags: [ "macmini-m1-xcode15-flat" ]
needs: []
rules:
- !reference [.rules-merge-request-manual, rules]
@ -117,7 +117,7 @@ macosx-ninja-package:
macosx-codesigning:
stage: signing
tags: [ "macos-min-xcode12.2" ]
tags: [ "macmini-m1-xcode15-flat" ]
needs:
- macosx-ninja-package
rules:
@ -142,7 +142,7 @@ macosx-codesigning:
macosx-deploy:
stage: deploy
tags: [ "macos-min-xcode12.2" ]
tags: [ "macmini-m1-xcode15-flat" ]
needs:
- macosx-codesigning
only:
@ -160,7 +160,7 @@ macosx-deploy:
macosx-makefile-plugins-deploy:
stage: deploy
tags: [ "macos-min-xcode12.2" ]
tags: [ "macmini-m1-xcode15-flat" ]
needs:
- macosx-makefile
only:

View file

@ -92,11 +92,11 @@
.windows-vs2022:
extends: .windows-vs
tags: [ "windows-powershell-vs-17-2022" ]
tags: [ "windows-powershell-vs-17-2022-flat" ]
.windows-codesigning:
extends: .prepare
tags: [ "windows-powershell-vs-17-2022-apps" ]
tags: [ "windows-powershell-vs-17-2022-apps-flat" ]
.windows-msbuild-variables:
variables:
@ -232,15 +232,15 @@ win64-codesigning:
.win64-upload:
stage: deploy
tags: [ "windows-powershell" ]
tags: [ "windows-powershell-flat" ]
rules:
- if: $NIGHTLY_MASTER
- if: $DEPLOY_WINDOWS
script:
- scp -pr 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) { scp -pr build-desktop/OUTPUT/Packages/RELEASE ${MAIN_DEPLOY_SERVER}:${UPLOAD_ROOT_PATH}/${WINDOWS_PLATFORM}/ }
- 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) { 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) { 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:
extends:
@ -250,7 +250,7 @@ win64-ninja-vs2022-upload:
.win64-plugins-upload:
stage: deploy
tags: [ "windows" ]
tags: [ "windows-flat" ]
rules:
- if: $DEPLOY_PLUGINS
script:

View file

@ -21,7 +21,7 @@ variables:
#CMAKE_OPTIONS: -DENABLE_LIME_X3DH=YES
# 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:

View file

@ -31,3 +31,9 @@ Group changes to describe their impact on the project, as follows:
- Default screen (between contacts, call history, conversations & meetings list) will change depending on where you were when the app was paused or killed, and you will return to that last visited screen on the next startup.
- Minimum supported Qt version is now 6.5.3
- Some settings have changed name and/or section in linphonerc file.
## [6.1.0] - XXXX-XX-XX
### Changed
- Minimum supported Qt version is now 6.10.0

View file

@ -60,7 +60,6 @@ project(linphoneqt)
include(GNUInstallDirs)
include(CheckCXXCompilerFlag)
include(Linphone/application_info.cmake)
set(CMAKE_CXX_STANDARD 17)
if(LINPHONEAPP_INSTALL_PREFIX)
@ -72,8 +71,11 @@ endif()
set(CMAKE_INSTALL_PREFIX "${APPLICATION_OUTPUT_DIR}")
set(LINPHONEAPP_APPLICATION_NAME "Linphone6" CACHE STRING "Application name" )
set(LINPHONEAPP_EXECUTABLE_NAME "linphone6" CACHE STRING "Executable name" )
set(LINPHONEAPP_APPLICATION_NAME "Linphone" CACHE STRING "Application 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 )
set(LINPHONEAPP_MACOS_ARCHS "arm64" CACHE STRING "MacOS architectures to build: comma-separated list of values in [arm64, x86_64]")
@ -206,7 +208,7 @@ set(ENABLE_CSHARP_WRAPPER OFF CACHE BOOL "Build the CSharp wrapper for Liblinpho
set(ENABLE_THEORA OFF)
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)
message(FATAL_ERROR "Minimum supported Qt6!")

View file

@ -1,6 +1,6 @@
cmake_minimum_required(VERSION 3.16)
project(Linphone VERSION 6.1.0 LANGUAGES CXX)
project(Linphone VERSION 6.2.0 LANGUAGES CXX)
################################################################
# PACKAGES
@ -22,7 +22,7 @@ set(APP_TARGETS ${LinphoneCxx_TARGET}
${LibLinphone_TARGET})#Liblinphone
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 Multimedia Test NetworkAuth Concurrent)# Search Core at first for initialize Qt scripts for next find_packages.
if (UNIX AND NOT APPLE)
list(APPEND QT_PACKAGES DBus)
endif()
@ -98,15 +98,11 @@ endif()
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/config.h.cmake" "${CMAKE_CURRENT_BINARY_DIR}/config.h")
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()
if(${Qt6_VERSION} VERSION_LESS "6.10.0")
message( FATAL_ERROR "Linphone requires Qt 6.10.0 or newer. Exiting CMake." )
endif()
qt6_standard_project_setup()
################################################################
@ -174,6 +170,12 @@ qt6_add_qml_module(Linphone
RESOURCES data/fonts.qrc
)
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})
endif()
endif()
qt6_add_resources(Linphone "resources" PREFIX "/" FILES ${_LINPHONEAPP_RC_FILES})
set_property(TARGET Linphone PROPERTY POSITION_INDEPENDENT_CODE ON) #Need by Qt
@ -199,6 +201,7 @@ set_target_properties(${TARGET_NAME} PROPERTIES
if(MSVC)
set_target_properties(${TARGET_NAME} PROPERTIES PDB_NAME "${EXECUTABLE_NAME}_app")
endif()
set_target_properties(${TARGET_NAME} PROPERTIES WINDOWS_EXPORT_ALL_SYMBOLS ON)
if(WIN32)
if(ENABLE_SCREENSHARING)

View file

@ -267,9 +267,7 @@ void App::setAutoStart(bool enabled) {
void App::setAutoStart(bool enabled) {
QSettings settings(AutoStartSettingsFilePath, QSettings::NativeFormat);
QString parameters;
if (!mSettings->getExitOnClose()) parameters = " --minimized";
if (enabled) settings.setValue(EXECUTABLE_NAME, QDir::toNativeSeparators(applicationFilePath()) + parameters);
if (enabled) settings.setValue(EXECUTABLE_NAME, QDir::toNativeSeparators(applicationFilePath()));
else settings.remove(EXECUTABLE_NAME);
mAutoStart = enabled;
@ -287,8 +285,11 @@ App::App(int &argc, char *argv[])
: SingleApplication(argc, argv, true, Mode::User | Mode::ExcludeAppPath | Mode::ExcludeAppVersion) {
// Do not use APPLICATION_NAME here.
// 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);
qDebug() << "app thread is" << QThread::currentThread();
lDebug() << "Starting app with Qt version" << qVersion();
QCoreApplication::setApplicationName(EXECUTABLE_NAME);
QApplication::setOrganizationDomain(EXECUTABLE_NAME);
QCoreApplication::setApplicationVersion(APPLICATION_SEMVER);
@ -305,7 +306,7 @@ App::App(int &argc, char *argv[])
.arg(applicationVersion())
.arg(Utils::getOsProduct())
.arg(qVersion());
lInfo() << "at " << QDir().absolutePath();
mCurrentDate = QDate::currentDate();
mAutoStart = autoStartEnabled();
mDateUpdateTimer.setInterval(60000);
@ -336,7 +337,7 @@ void App::setSelf(QSharedPointer<App>(me)) {
auto callCore = CallCore::create(call);
mCoreModelConnection->invokeToCore([this, callCore] {
auto callGui = new CallGui(callCore);
auto win = getCallsWindow(QVariant::fromValue(callGui));
auto win = getOrCreateCallsWindow(QVariant::fromValue(callGui));
Utils::smartShowWindow(win);
auto mainwin = getMainWindow();
QMetaObject::invokeMethod(mainwin, "callCreated");
@ -406,8 +407,12 @@ void App::setSelf(QSharedPointer<App>(me)) {
mustBeInMainThread(log().arg(Q_FUNC_INFO));
// There is an account added by a remote provisioning, force switching to main page
// because the account may not be connected already
QMetaObject::invokeMethod(mMainWindow, "openMainPage", Qt::DirectConnection,
Q_ARG(QVariant, accountConnected));
// if (accountConnected)
if (mPossiblyLookForAddedAccount) {
QMetaObject::invokeMethod(mMainWindow, "openMainPage", Qt::DirectConnection,
Q_ARG(QVariant, accountConnected));
}
mPossiblyLookForAddedAccount = false;
});
}
});
@ -430,6 +435,43 @@ 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"));
}
});
});
//---------------------------------------------------------------------------------------------
mCliModelConnection = SafeConnection<App, CliModel>::create(me, CliModel::getInstance());
mCliModelConnection->makeConnectToCore(&App::receivedMessage, [this](int, const QByteArray &byteArray) {
@ -646,20 +688,24 @@ void App::initCore() {
} else lInfo() << log().arg("Stay minimized");
firstOpen = false;
lInfo() << log().arg("Checking remote provisioning");
if (CoreModel::getInstance()->mConfigStatus == linphone::ConfiguringState::Failed &&
mIsRestarting) {
QMetaObject::invokeMethod(thread(), [this]() {
auto message = CoreModel::getInstance()->mConfigMessage;
//: not reachable
if (message.isEmpty()) message = tr("configuration_error_detail");
mustBeInMainThread(log().arg(Q_FUNC_INFO));
//: Error
Utils::showInformationPopup(
tr("info_popup_error_title"),
//: Remote provisioning failed : %1
tr("info_popup_configuration_failed_message").arg(message), false);
});
if (mIsRestarting) {
if (CoreModel::getInstance()->mConfigStatus == linphone::ConfiguringState::Failed) {
QMetaObject::invokeMethod(thread(), [this]() {
auto message = CoreModel::getInstance()->mConfigMessage;
//: not reachable
if (message.isEmpty()) message = tr("configuration_error_detail");
mustBeInMainThread(log().arg(Q_FUNC_INFO));
//: Error
Utils::showInformationPopup(
tr("info_popup_error_title"),
//: Remote provisioning failed : %1
tr("info_popup_configuration_failed_message").arg(message), false);
});
} else {
mPossiblyLookForAddedAccount = true;
}
}
checkForUpdate();
mIsRestarting = false;
//---------------------------------------------------------------------------------------------
@ -1024,7 +1070,38 @@ bool App::notify(QObject *receiver, QEvent *event) {
return done;
}
QQuickWindow *App::getCallsWindow(QVariant callGui) {
void App::handleAppActivity() {
auto handle = [this](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);
if (((!mMainWindow || !mMainWindow->isActive()) && (!mCallsWindow || !mCallsWindow->isActive())) &&
accountPresence == LinphoneEnums::Presence::Online)
accountCore->lSetPresence(LinphoneEnums::Presence::Away);
};
if (mAccountList) {
for (auto &account : mAccountList->getSharedList<AccountCore>())
handle(account);
} else {
connect(
this, &App::accountsChanged, this,
[this, &handle] {
if (mAccountList) {
for (auto &account : mAccountList->getSharedList<AccountCore>())
handle(account);
}
},
Qt::SingleShotConnection);
}
}
QQuickWindow *App::getCallsWindow() {
return mCallsWindow;
}
QQuickWindow *App::getOrCreateCallsWindow(QVariant callGui) {
mustBeInMainThread(getClassName());
if (!mCallsWindow) {
const QUrl callUrl("qrc:/qt/qml/Linphone/view/Page/Window/Call/CallsWindow.qml");
@ -1059,6 +1136,7 @@ QQuickWindow *App::getCallsWindow(QVariant callGui) {
}
// window->setParent(mMainWindow);
mCallsWindow = window;
connect(mCallsWindow, &QQuickWindow::activeChanged, this, &App::handleAppActivity);
}
if (!callGui.isNull() && callGui.isValid()) mCallsWindow->setProperty("call", callGui);
return mCallsWindow;
@ -1081,8 +1159,11 @@ QQuickWindow *App::getMainWindow() const {
}
void App::setMainWindow(QQuickWindow *data) {
if (mMainWindow) disconnect(mMainWindow, &QQuickWindow::activeChanged, this, nullptr);
if (mMainWindow != data) {
mMainWindow = data;
connect(mMainWindow, &QQuickWindow::activeChanged, this, &App::handleAppActivity);
handleAppActivity();
emit mainWindowChanged();
}
}
@ -1357,6 +1438,12 @@ void App::setSysTrayIcon() {
menu->addSeparator();
}
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);
if (!mSystemTrayIcon) {
systemTrayIcon->setContextMenu(menu); // This is a Qt bug. We cannot call setContextMenu more than once. So
@ -1436,6 +1523,21 @@ QString App::getSdkVersion() {
#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 {
return mCurrentChat;
}
@ -1464,4 +1566,4 @@ QAction *App::createMarkAsReadAction(QQuickWindow *window) {
mCoreModelConnection->invokeToModel([this]() { CoreModel::getInstance()->unreadNotificationsChanged(); });
});
return markAllReadAction;
}
}

View file

@ -46,6 +46,7 @@ class App : public SingleApplication, public AbstractObject {
Q_PROPERTY(AccountList *accounts READ getAccounts NOTIFY accountsChanged)
Q_PROPERTY(CallList *calls READ getCalls NOTIFY callsChanged)
Q_PROPERTY(QString shortApplicationVersion READ getShortApplicationVersion CONSTANT)
Q_PROPERTY(QString qtVersion READ getQtVersion CONSTANT)
Q_PROPERTY(QString gitBranchName READ getGitBranchName CONSTANT)
Q_PROPERTY(QString sdkVersion READ getSdkVersion CONSTANT)
Q_PROPERTY(ChatGui *currentChat READ getCurrentChat WRITE setCurrentChat NOTIFY currentChatChanged)
@ -138,7 +139,9 @@ public:
bool getCoreStarted() const;
void setCoreStarted(bool started);
QQuickWindow *getCallsWindow(QVariant callGui = QVariant());
QQuickWindow *getCallsWindow();
Q_INVOKABLE void handleAppActivity();
QQuickWindow *getOrCreateCallsWindow(QVariant callGui = QVariant());
void setCallsWindowProperty(const char *id, QVariant property);
void closeCallsWindow();
@ -164,7 +167,9 @@ public:
QString getShortApplicationVersion();
QString getGitBranchName();
QString getSdkVersion();
QString getQtVersion() const;
Q_INVOKABLE void checkForUpdate(bool requestedByUser = false);
ChatGui *getCurrentChat() const;
void setCurrentChat(ChatGui *chat);
@ -221,6 +226,7 @@ private:
bool mAutoStart = false;
bool mCoreStarted = false;
bool mIsRestarting = false;
bool mPossiblyLookForAddedAccount = false;
QLocale mLocale = QLocale::system();
DefaultTranslatorCore *mTranslatorCore = nullptr;
DefaultTranslatorCore *mDefaultTranslatorCore = nullptr;

View file

@ -84,6 +84,7 @@ AccountCore::AccountCore(const std::shared_ptr<linphone::Account> &account) : QO
? Utils::coreStringToAppString(params->getAudioVideoConferenceFactoryAddress()->asString())
: "";
mLimeServerUrl = Utils::coreStringToAppString(params->getLimeServerUrl());
mCcmpServerUrl = Utils::coreStringToAppString(params->getCcmpServerUrl());
// Add listener
mAccountModel = Utils::makeQObject_ptr<AccountModel>(account); // OK
@ -148,6 +149,7 @@ AccountCore::AccountCore(const AccountCore &accountCore) {
mConferenceFactoryAddress = accountCore.mConferenceFactoryAddress;
mAudioVideoConferenceFactoryAddress = accountCore.mAudioVideoConferenceFactoryAddress;
mLimeServerUrl = accountCore.mLimeServerUrl;
mCcmpServerUrl = accountCore.mCcmpServerUrl;
}
void AccountCore::setSelf(QSharedPointer<AccountCore> me) {
@ -236,6 +238,10 @@ void AccountCore::setSelf(QSharedPointer<AccountCore> me) {
mAccountModelConnection->makeConnectToModel(&AccountModel::limeServerUrlChanged, [this](QString value) {
mAccountModelConnection->invokeToCore([this, value]() { onLimeServerUrlChanged(value); });
});
mAccountModelConnection->makeConnectToModel(&AccountModel::ccmpServerUrlChanged, [this](QString value) {
mAccountModelConnection->invokeToCore([this, value]() { onCcmpServerUrlChanged(value); });
});
mAccountModelConnection->makeConnectToModel(
&AccountModel::removed, [this]() { mAccountModelConnection->invokeToCore([this]() { emit removed(); }); });
@ -327,6 +333,7 @@ void AccountCore::reset(const AccountCore &accountCore) {
setConferenceFactoryAddress(accountCore.mConferenceFactoryAddress);
setAudioVideoConferenceFactoryAddress(accountCore.mAudioVideoConferenceFactoryAddress);
setLimeServerUrl(accountCore.mLimeServerUrl);
setCcmpServerUrl(accountCore.mCcmpServerUrl);
}
const std::shared_ptr<AccountModel> &AccountCore::getModel() const {
@ -574,6 +581,10 @@ QString AccountCore::getLimeServerUrl() {
return mLimeServerUrl;
}
QString AccountCore::getCcmpServerUrl() {
return mCcmpServerUrl;
}
void AccountCore::setMwiServerAddress(QString value) {
if (mMwiServerAddress != value) {
mMwiServerAddress = value;
@ -678,6 +689,14 @@ void AccountCore::setLimeServerUrl(QString value) {
}
}
void AccountCore::setCcmpServerUrl(QString value) {
if (mCcmpServerUrl != value) {
mCcmpServerUrl = value;
emit ccmpServerUrlChanged();
setIsSaved(false);
}
}
bool AccountCore::isSaved() const {
return mIsSaved;
}
@ -790,6 +809,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 {
mustBeInLinphoneThread(getClassName() + Q_FUNC_INFO);
model->setMwiServerAddress(mMwiServerAddress);
@ -806,6 +832,7 @@ void AccountCore::writeIntoModel(std::shared_ptr<AccountModel> model) const {
model->setConferenceFactoryAddress(mConferenceFactoryAddress);
model->setAudioVideoConferenceFactoryAddress(mAudioVideoConferenceFactoryAddress);
model->setLimeServerUrl(mLimeServerUrl);
model->setCcmpServerUrl(mCcmpServerUrl);
model->setVoicemailAddress(mVoicemailAddress);
}
@ -825,6 +852,7 @@ void AccountCore::writeFromModel(const std::shared_ptr<AccountModel> &model) {
onConferenceFactoryAddressChanged(model->getConferenceFactoryAddress());
onAudioVideoConferenceFactoryAddressChanged(model->getAudioVideoConferenceFactoryAddress());
onLimeServerUrlChanged(model->getLimeServerUrl());
onCcmpServerUrlChanged(model->getCcmpServerUrl());
onVoicemailAddressChanged(model->getVoicemailAddress());
}

View file

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

View file

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

View file

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

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
* (see https://www.linphone.org).
@ -28,6 +28,8 @@
#include "tool/Utils.hpp"
#include "tool/thread/SafeConnection.hpp"
#include <QQuickWindow>
DEFINE_ABSTRACT_OBJECT(CallCore)
/***********************************************************************/
@ -107,9 +109,11 @@ CallCore::CallCore(const std::shared_ptr<linphone::Call> &call) : QObject(nullpt
mCallModel->setSelf(mCallModel);
mDuration = call->getDuration();
mIsStarted = mDuration > 0;
auto videoDirection = call->getParams()->getVideoDirection();
auto callParams = call->getParams();
auto videoDirection = callParams->getVideoDirection();
mLocalVideoEnabled =
videoDirection == linphone::MediaDirection::SendOnly || videoDirection == linphone::MediaDirection::SendRecv;
mCameraEnabled = callParams->cameraEnabled();
auto remoteParams = call->getRemoteParams();
videoDirection = remoteParams ? remoteParams->getVideoDirection() : linphone::MediaDirection::Inactive;
mRemoteVideoEnabled =
@ -133,7 +137,7 @@ CallCore::CallCore(const std::shared_ptr<linphone::Call> &call) : QObject(nullpt
mTransferState = LinphoneEnums::fromLinphone(call->getTransferState());
mLocalToken = Utils::coreStringToAppString(mCallModel->getLocalAtuhenticationToken());
mRemoteTokens = mCallModel->getRemoteAtuhenticationTokens();
mEncryption = LinphoneEnums::fromLinphone(call->getParams()->getMediaEncryption());
mEncryption = LinphoneEnums::fromLinphone(callParams->getMediaEncryption());
auto tokenVerified = call->getAuthenticationTokenVerified();
mIsMismatch = call->getZrtpCacheMismatchFlag();
mIsSecured = (mEncryption == LinphoneEnums::MediaEncryption::Zrtp && tokenVerified) ||
@ -160,7 +164,7 @@ CallCore::CallCore(const std::shared_ptr<linphone::Call> &call) : QObject(nullpt
mPaused = mState == LinphoneEnums::CallState::Pausing || mState == LinphoneEnums::CallState::Paused ||
mState == LinphoneEnums::CallState::PausedByRemote;
mRecording = call->getParams() && call->getParams()->isRecording();
mRecording = callParams && callParams->isRecording();
mRemoteRecording = call->getRemoteParams() && call->getRemoteParams()->isRecording();
auto settingsModel = SettingsModel::getInstance();
mMicrophoneVolume = call->getRecordVolume();
@ -193,8 +197,8 @@ void CallCore::setSelf(QSharedPointer<CallCore> me) {
mCallModelConnection->makeConnectToModel(&CallModel::speakerMutedChanged, [this](bool isMuted) {
mCallModelConnection->invokeToCore([this, isMuted]() { setSpeakerMuted(isMuted); });
});
mCallModelConnection->makeConnectToCore(&CallCore::lSetLocalVideoEnabled, [this](bool enabled) {
mCallModelConnection->invokeToModel([this, enabled]() { mCallModel->setLocalVideoEnabled(enabled); });
mCallModelConnection->makeConnectToCore(&CallCore::lSetCameraEnabled, [this](bool enabled) {
mCallModelConnection->invokeToModel([this, enabled]() { mCallModel->setCameraEnabled(enabled); });
});
mCallModelConnection->makeConnectToCore(&CallCore::lStartRecording, [this]() {
mCallModelConnection->invokeToModel([this]() { mCallModel->startRecording(); });
@ -204,15 +208,15 @@ void CallCore::setSelf(QSharedPointer<CallCore> me) {
});
mCallModelConnection->makeConnectToModel(
&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);
if (recording == false) {
//: "Enregistrement terminé"
Utils::showInformationPopup(tr("call_record_end_message"),
//: "L'appel a été enregistré dans le fichier : %1"
tr("call_record_saved_in_file_message")
.arg(QString::fromStdString(mCallModel->getRecordFile())),
true, App::getInstance()->getCallsWindow());
tr("call_record_saved_in_file_message").arg(recordFile), true,
App::getInstance()->getOrCreateCallsWindow());
}
});
});
@ -244,6 +248,9 @@ void CallCore::setSelf(QSharedPointer<CallCore> me) {
mCallModelConnection->makeConnectToModel(&CallModel::localVideoEnabledChanged, [this](bool 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->invokeToCore([this, duration]() { setDuration(duration); });
});
@ -344,6 +351,8 @@ void CallCore::setSelf(QSharedPointer<CallCore> me) {
});
mCallModelConnection->makeConnectToModel(&CallModel::conferenceChanged, [this]() {
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;
mCallModelConnection->invokeToCore([this, core]() { setConference(core); });
});
@ -442,6 +451,44 @@ void CallCore::setSelf(QSharedPointer<CallCore> me) {
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);
}
@ -558,6 +605,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 {
return mPaused;
}

View file

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

View file

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

View file

@ -25,20 +25,32 @@
DEFINE_ABSTRACT_OBJECT(CallProxy)
CallProxy::CallProxy(QObject *parent) : LimitProxy(parent) {
CallProxy::CallProxy() : SortFilterProxy() {
mShowCurrentCall = true;
}
CallProxy::~CallProxy() {
}
CallGui *CallProxy::getCurrentCall() {
auto model = getListModel<CallList>();
auto model = qobject_cast<CallList *>(sourceModel());
if (!mCurrentCall && model) mCurrentCall = model->getCurrentCall();
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) {
getListModel<CallList>()->setCurrentCall(call);
qobject_cast<CallList *>(sourceModel())->setCurrentCall(call);
}
// Reset the default account to let UI build its new object if needed.
@ -48,12 +60,12 @@ void CallProxy::resetCurrentCall() {
}
bool CallProxy::getHaveCall() const {
auto model = getListModel<CallList>();
auto model = qobject_cast<CallList *>(sourceModel());
return model ? model->getHaveCall() : false;
}
void CallProxy::setSourceModel(QAbstractItemModel *model) {
auto oldCallList = getListModel<CallList>();
auto oldCallList = qobject_cast<CallList *>(sourceModel());
if (oldCallList) {
disconnect(oldCallList);
}
@ -63,24 +75,24 @@ void CallProxy::setSourceModel(QAbstractItemModel *model) {
connect(newCallList, &CallList::haveCallChanged, this, &CallProxy::haveCallChanged, Qt::QueuedConnection);
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 == "*");
auto callList = qobject_cast<CallList *>(sourceModel());
auto call = callList->getAt<CallCore>(sourceRow);
if (!mShowCurrentCall && call == callList->getCurrentCallCore()) return false;
if (!show) {
QRegularExpression search(QRegularExpression::escape(mFilterText),
QRegularExpression::CaseInsensitiveOption |
QRegularExpression::UseUnicodePropertiesOption);
auto call = qobject_cast<CallList *>(sourceModel())->getAt<CallCore>(sourceRow);
show = call->getRemoteAddress().contains(search);
}
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 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_PROPERTY(CallGui *currentCall READ getCurrentCall WRITE setCurrentCall NOTIFY currentCallChanged)
Q_PROPERTY(bool haveCall READ getHaveCall NOTIFY haveCallChanged)
Q_PROPERTY(bool showCurrentCall READ showCurrentCall WRITE setShowCurrentCall NOTIFY showCurrentCallChanged)
public:
DECLARE_SORTFILTER_CLASS()
CallProxy(QObject *parent = Q_NULLPTR);
CallProxy();
~CallProxy();
// Get a new object from List or give the stored one.
@ -48,15 +47,23 @@ public:
bool getHaveCall() const;
void setShowCurrentCall(bool show);
bool showCurrentCall() const;
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:
void lMergeAll();
void currentCallChanged();
void haveCallChanged();
void showCurrentCallChanged();
protected:
CallGui *mCurrentCall = nullptr; // When null, a new UI object is build from List
bool mShowCurrentCall = false;
DECLARE_ABSTRACT_OBJECT
};

View file

@ -118,6 +118,7 @@ ChatCore::ChatCore(const std::shared_ptr<linphone::ChatRoom> &chatRoom) : QObjec
ChatCore::~ChatCore() {
lDebug() << "[ChatCore] delete" << this;
mustBeInMainThread("~" + getClassName());
if (mChatModelConnection) mChatModelConnection->disconnect();
emit mChatModel->removeListener();
}
@ -162,14 +163,13 @@ void ChatCore::setSelf(QSharedPointer<ChatCore> me) {
mChatModelConnection->makeConnectToCore(&ChatCore::lDelete, [this]() {
mChatModelConnection->invokeToModel([this]() { mChatModel->deleteChatRoom(); });
});
mChatModelConnection->makeConnectToModel(
&ChatModel::deleted, [this]() { mChatModelConnection->invokeToCore([this]() { emit deleted(); }); });
mChatModelConnection->makeConnectToModel(
&ChatModel::stateChanged,
[this](const std::shared_ptr<linphone::ChatRoom> &chatRoom, linphone::ChatRoom::State newState) {
auto state = LinphoneEnums::fromLinphone(newState);
bool isReadOnly = chatRoom->isReadOnly();
if (newState == linphone::ChatRoom::State::Deleted) emit deleted();
mChatModelConnection->invokeToCore([this, state, isReadOnly]() {
setChatRoomState(state);
setIsReadOnly(isReadOnly);
@ -207,8 +207,8 @@ void ChatCore::setSelf(QSharedPointer<ChatCore> me) {
&ChatModel::newEvent, [this](const std::shared_ptr<linphone::ChatRoom> &chatRoom,
const std::shared_ptr<const linphone::EventLog> &eventLog) {
if (mChatModel->getMonitor() != chatRoom) return;
qDebug() << "EVENT LOG RECEIVED IN CHATROOM" << mChatModel->getTitle();
auto event = EventLogCore::create(eventLog);
lDebug() << "EVENT LOG RECEIVED IN CHATROOM" << mChatModel->getTitle();
auto event = EventLogCore::create(eventLog, chatRoom);
if (event->isHandled()) {
mChatModelConnection->invokeToCore([this, event]() { emit eventsInserted({event}); });
}
@ -220,10 +220,10 @@ void ChatCore::setSelf(QSharedPointer<ChatCore> me) {
&ChatModel::chatMessagesReceived, [this](const std::shared_ptr<linphone::ChatRoom> &chatRoom,
const std::list<std::shared_ptr<linphone::EventLog>> &eventsLog) {
if (mChatModel->getMonitor() != chatRoom) return;
qDebug() << "CHAT MESSAGE RECEIVED IN CHATROOM" << mChatModel->getTitle();
lDebug() << "CHAT MESSAGE RECEIVED IN CHATROOM" << mChatModel->getTitle();
QList<QSharedPointer<EventLogCore>> list;
for (auto &e : eventsLog) {
auto event = EventLogCore::create(e);
auto event = EventLogCore::create(e, chatRoom);
list.push_back(event);
}
mChatModelConnection->invokeToCore([this, list]() {
@ -234,13 +234,14 @@ void ChatCore::setSelf(QSharedPointer<ChatCore> me) {
});
mChatModelConnection->makeConnectToCore(&ChatCore::lMarkAsRead, [this]() {
auto mainWindow = Utils::getMainWindow();
if (mainWindow->isActive()) mChatModelConnection->invokeToModel([this]() { mChatModel->markAsRead(); });
auto lastActiveWindow = Utils::getLastActiveWindow();
if (lastActiveWindow && lastActiveWindow->isActive())
mChatModelConnection->invokeToModel([this]() { mChatModel->markAsRead(); });
else {
connect(mainWindow, &QQuickWindow::activeChanged, this, [this, mainWindow] {
if (mainWindow->isActive()) {
disconnect(mainWindow, &QQuickWindow::activeChanged, this, nullptr);
mChatModelConnection->invokeToModel([this, mainWindow] { mChatModel->markAsRead(); });
connect(lastActiveWindow, &QQuickWindow::activeChanged, this, [this, lastActiveWindow] {
if (lastActiveWindow->isActive()) {
disconnect(lastActiveWindow, &QQuickWindow::activeChanged, this, nullptr);
mChatModelConnection->invokeToModel([this, lastActiveWindow] { mChatModel->markAsRead(); });
}
});
}
@ -285,7 +286,7 @@ void ChatCore::setSelf(QSharedPointer<ChatCore> me) {
mChatModelConnection->makeConnectToModel(
&ChatModel::chatMessageSending, [this](const std::shared_ptr<linphone::ChatRoom> &chatRoom,
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->makeConnectToCore(
@ -362,12 +363,16 @@ void ChatCore::setSelf(QSharedPointer<ChatCore> me) {
auto participants = buildParticipants(chatRoom);
mChatModelConnection->invokeToCore([this, participants]() { setParticipants(participants); });
});
mChatModelConnection->makeConnectToModel(
&ChatModel::participantAdminStatusChanged, [this](const std::shared_ptr<linphone::ChatRoom> &chatRoom,
const std::shared_ptr<const linphone::EventLog> &eventLog) {
auto participants = buildParticipants(chatRoom);
mChatModelConnection->invokeToCore([this, participants]() { setParticipants(participants); });
});
mChatModelConnection->makeConnectToModel(&ChatModel::participantAdminStatusChanged,
[this](const std::shared_ptr<linphone::ChatRoom> &chatRoom,
const std::shared_ptr<const linphone::EventLog> &eventLog) {
auto participants = buildParticipants(chatRoom);
bool meAdmin = chatRoom->getMe()->isAdmin();
mChatModelConnection->invokeToCore([this, participants, meAdmin]() {
setParticipants(participants);
setMeAdmin(meAdmin);
});
});
mChatModelConnection->makeConnectToCore(&ChatCore::lRemoveParticipantAtIndex, [this](int index) {
mChatModelConnection->invokeToModel([this, index]() { mChatModel->removeParticipantAtIndex(index); });
});
@ -389,6 +394,7 @@ void ChatCore::setSelf(QSharedPointer<ChatCore> me) {
[this](std::shared_ptr<linphone::Friend> f) { updateInfo(f); });
mCoreModelConnection->makeConnectToModel(&CoreModel::friendRemoved,
[this](std::shared_ptr<linphone::Friend> f) { updateInfo(f, true); });
}
QDateTime ChatCore::getLastUpdatedTime() const {
@ -491,7 +497,7 @@ ChatMessageGui *ChatCore::getLastMessage() const {
void ChatCore::setLastMessage(QSharedPointer<ChatMessageCore> lastMessage) {
if (mLastMessage != lastMessage) {
disconnect(mLastMessage.get());
if (mLastMessage) disconnect(mLastMessage.get(), &ChatMessageCore::messageStateChanged, this, nullptr);
mLastMessage = lastMessage;
connect(mLastMessage.get(), &ChatMessageCore::messageStateChanged, this, &ChatCore::lastMessageChanged);
emit lastMessageChanged();
@ -544,10 +550,6 @@ std::shared_ptr<ChatModel> ChatCore::getModel() const {
return mChatModel;
}
QSharedPointer<SafeConnection<ChatCore, ChatModel>> ChatCore::getChatModelConnection() const {
return mChatModelConnection;
}
bool ChatCore::isMuted() const {
return mIsMuted;
}
@ -681,4 +683,4 @@ void ChatCore::updateInfo(const std::shared_ptr<linphone::Friend> &updatedFriend
}
}
}
}
}

View file

@ -64,7 +64,6 @@ public:
Q_PROPERTY(
int ephemeralLifetime READ getEphemeralLifetime WRITE lSetEphemeralLifetime NOTIFY ephemeralLifetimeChanged)
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(QVariantList participants READ getParticipantsGui NOTIFY participantsChanged)
Q_PROPERTY(QStringList participantsAddresses READ getParticipantsAddresses WRITE lSetParticipantsAddresses NOTIFY
@ -142,7 +141,6 @@ public:
void setComposingAddress(QString composingAddress);
std::shared_ptr<ChatModel> getModel() const;
QSharedPointer<SafeConnection<ChatCore, ChatModel>> getChatModelConnection() const;
void setParticipants(QList<QSharedPointer<ParticipantCore>> participants);
QList<QSharedPointer<ParticipantCore>> buildParticipants(const std::shared_ptr<linphone::ChatRoom> &chatRoom) const;

View file

@ -54,13 +54,15 @@ ChatList::~ChatList() {
}
void ChatList::connectItem(QSharedPointer<ChatCore> chat) {
connect(chat.get(), &ChatCore::deleted, this, [this, chat] {
disconnect(chat.get());
remove(chat);
// We cannot use countChanged here because it is called before mList
// really has removed the item, then emit specific signal
emit chatRemoved(chat ? new ChatGui(chat) : nullptr);
});
connect(
chat.get(), &ChatCore::deleted, this,
[this, chat] {
disconnect(chat.get(), &ChatCore::unreadMessagesCountChanged, this, nullptr);
disconnect(chat.get(), &ChatCore::lastUpdatedTimeChanged, this, nullptr);
disconnect(chat.get(), &ChatCore::lastMessageChanged, this, nullptr);
remove(chat);
},
Qt::SingleShotConnection);
auto dataChange = [this, chat] {
int i = -1;
get(chat.get(), &i);
@ -94,7 +96,6 @@ void ChatList::setSelf(QSharedPointer<ChatList> me) {
mModelConnection->invokeToModel([this]() {
mustBeInLinphoneThread(getClassName());
beginResetModel();
mList.clear();
// Avoid copy to lambdas
QList<QSharedPointer<ChatCore>> *chats = new QList<QSharedPointer<ChatCore>>();
auto currentAccount = CoreModel::getInstance()->getCore()->getDefaultAccount();
@ -118,6 +119,7 @@ void ChatList::setSelf(QSharedPointer<ChatList> me) {
disconnect(chat.get(), &ChatCore::lastMessageChanged, this, nullptr);
}
}
mList.clear();
for (auto &chat : *chats) {
connectItem(chat);
}
@ -143,18 +145,7 @@ void ChatList::setSelf(QSharedPointer<ChatList> me) {
return;
}
auto chatCore = ChatCore::create(room);
mModelConnection->invokeToCore([this, chatCore] {
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->invokeToCore([this, chatCore] { addChatInList(chatCore); });
};
mModelConnection->makeConnectToModel(&CoreModel::messageReceived,
[this, addChatToList](const std::shared_ptr<linphone::Core> &core,
@ -195,17 +186,19 @@ int ChatList::findChatIndex(ChatGui *chatGui) {
return it == chatList.end() ? -1 : std::distance(chatList.begin(), it);
}
void ChatList::addChatInList(QSharedPointer<ChatCore> chatCore) {
bool ChatList::addChatInList(QSharedPointer<ChatCore> chatCore) {
mustBeInMainThread(log().arg(Q_FUNC_INFO));
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();
return item && chatCore && item->getIdentifier() == chatCore->getIdentifier();
});
if (it == chatList.end()) {
connectItem(chatCore);
add(chatCore);
emit chatAdded();
return true;
}
return false;
}
QVariant ChatList::data(const QModelIndex &index, int role) const {

View file

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

View file

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

View file

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

View file

@ -112,88 +112,97 @@ ChatMessageCore::ChatMessageCore(const std::shared_ptr<linphone::ChatMessage> &c
// lDebug() << "[ChatMessageCore] new" << this;
mustBeInLinphoneThread(getClassName());
App::getInstance()->mEngine->setObjectOwnership(this, QQmlEngine::CppOwnership);
mChatMessageModel = Utils::makeQObject_ptr<ChatMessageModel>(chatmessage);
mChatMessageModel->setSelf(mChatMessageModel);
mText = ToolModel::getMessageFromContent(chatmessage->getContents());
mUtf8Text = mChatMessageModel->getUtf8Text();
mHasTextContent = mChatMessageModel->getHasTextContent();
mTimestamp = QDateTime::fromSecsSinceEpoch(chatmessage->getTime());
mIsOutgoing = chatmessage->isOutgoing();
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());
if (chatmessage) {
mChatMessageModel = Utils::makeQObject_ptr<ChatMessageModel>(chatmessage);
mChatMessageModel->setSelf(mChatMessageModel);
mText = ToolModel::getMessageFromMessage(chatmessage);
mUtf8Text = mChatMessageModel->getUtf8Text();
mHasTextContent = mChatMessageModel->getHasTextContent();
mTimestamp = QDateTime::fromSecsSinceEpoch(chatmessage->getTime());
mIsOutgoing = chatmessage->isOutgoing();
mIsRetractable =
chatmessage->isOutgoing() && chatmessage->isRetractable() && !chatmessage->getChatRoom()->isReadOnly();
mIsRetracted = chatmessage->isRetracted();
mIsEditable =
chatmessage->isOutgoing() && chatmessage->isEditable() && !chatmessage->getChatRoom()->isReadOnly();
mIsEdited = chatmessage->isEdited();
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();
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();
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;
if (mIsEphemeral) {
auto now = QDateTime::currentDateTime();
mEphemeralDuration = chatmessage->getEphemeralExpireTime() == 0
? chatmessage->getEphemeralLifetime()
: now.secsTo(QDateTime::fromSecsSinceEpoch(chatmessage->getEphemeralExpireTime()));
}
}
//: "Reactions": all reactions for one message label
mTotalReactionsLabel = tr("all_reactions_label");
auto reac = chatmessage->getOwnReaction();
mOwnReaction = reac ? Utils::coreStringToAppString(reac->getBody()) : QString();
for (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);
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);
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;
}
}
}
connect(this, &ChatMessageCore::messageReactionChanged, this, &ChatMessageCore::resetReactionsSingleton);
//: "Reactions": all reactions for one message label
mTotalReactionsLabel = tr("all_reactions_label");
auto reac = chatmessage->getOwnReaction();
mOwnReaction = reac ? Utils::coreStringToAppString(reac->getBody()) : QString();
for (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();
mIsReply = chatmessage->isReply();
if (mIsReply) {
auto replymessage = chatmessage->getReplyMessage();
if (replymessage) {
mReplyText = ToolModel::getMessageFromContent(replymessage->getContents());
if (mIsFromChatGroup) mRepliedToName = ToolModel::getDisplayName(replymessage->getFromAddress());
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);
}
}
}
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() {
@ -204,6 +213,9 @@ void ChatMessageCore::setSelf(QSharedPointer<ChatMessageCore> me) {
mChatMessageModelConnection->makeConnectToCore(&ChatMessageCore::lDelete, [this] {
mChatMessageModelConnection->invokeToModel([this] { mChatMessageModel->deleteMessageFromChatRoom(true); });
});
mChatMessageModelConnection->makeConnectToCore(&ChatMessageCore::lRetract, [this] {
mChatMessageModelConnection->invokeToModel([this] { mChatMessageModel->retractMessageFromChatRoom(); });
});
mChatMessageModelConnection->makeConnectToModel(&ChatMessageModel::messageDeleted, [this](bool deletedByUser) {
mChatMessageModelConnection->invokeToCore([this, deletedByUser] {
//: Deleted
@ -347,6 +359,22 @@ void ChatMessageCore::setSelf(QSharedPointer<ChatMessageCore> me) {
int duration = now.secsTo(QDateTime::fromSecsSinceEpoch(expireTime));
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) {
@ -469,6 +497,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 {
return mOwnReaction;
}
@ -647,4 +691,4 @@ std::shared_ptr<ChatMessageModel> ChatMessageCore::getModel() const {
ChatMessageContentGui *ChatMessageCore::getVoiceRecordingContent() const {
return new ChatMessageContentGui(mVoiceRecordingContent);
}
}

View file

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

View file

@ -26,14 +26,16 @@
DEFINE_ABSTRACT_OBJECT(EventLogCore)
QSharedPointer<EventLogCore> EventLogCore::create(const std::shared_ptr<const linphone::EventLog> &eventLog) {
auto sharedPointer = QSharedPointer<EventLogCore>(new EventLogCore(eventLog), &QObject::deleteLater);
QSharedPointer<EventLogCore> EventLogCore::create(const std::shared_ptr<const linphone::EventLog> &eventLog,
const std::shared_ptr<linphone::ChatRoom> &chatRoom) {
auto sharedPointer = QSharedPointer<EventLogCore>(new EventLogCore(eventLog, chatRoom), &QObject::deleteLater);
sharedPointer->setSelf(sharedPointer);
sharedPointer->moveToThread(App::getInstance()->thread());
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());
App::getInstance()->mEngine->setObjectOwnership(this, QQmlEngine::CppOwnership);
mEventLogType = LinphoneEnums::fromLinphone(eventLog->getType());
@ -52,7 +54,7 @@ EventLogCore::EventLogCore(const std::shared_ptr<const linphone::EventLog> &even
QString type = QString::fromLatin1(
QMetaEnum::fromType<LinphoneEnums::EventLogType>().valueToKey(static_cast<int>(mEventLogType)));
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)
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());
mHandled = true;
mImportant = false;
@ -104,9 +107,15 @@ void EventLogCore::computeEvent(const std::shared_ptr<const linphone::EventLog>
switch (eventLog->getType()) {
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");
break;
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");
mImportant = true;
break;

View file

@ -51,8 +51,10 @@ class EventLogCore : public QObject, public AbstractObject {
Q_PROPERTY(QDateTime timestamp READ getTimestamp CONSTANT)
public:
static QSharedPointer<EventLogCore> create(const std::shared_ptr<const linphone::EventLog> &eventLog);
EventLogCore(const std::shared_ptr<const linphone::EventLog> &eventLog);
static QSharedPointer<EventLogCore> create(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();
void setSelf(QSharedPointer<EventLogCore> me);
QString getEventLogId();
@ -87,7 +89,8 @@ private:
ChatMessageCore *getChatMessageCorePointer();
CallHistoryCore *getCallHistoryCorePointer();
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_

View file

@ -64,7 +64,8 @@ void EventLogList::disconnectItem(const QSharedPointer<EventLogCore> &item) {
if (message) {
disconnect(message.get(), &ChatMessageCore::isReadChanged, 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();
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) {
auto updateChatCore = [this](QSharedPointer<ChatCore> core) {
if (mChatCore != core) {
if (mChatCore) {
disconnect(mChatCore.get(), &ChatCore::eventsInserted, 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();
if (mChatCore != core) {
if (mChatCore) {
disconnect(mChatCore.get(), &ChatCore::eventsInserted, this, nullptr);
disconnect(mChatCore.get(), &ChatCore::eventListCleared, this, nullptr);
}
};
if (mIsUpdating) {
connect(this, &EventLogList::isUpdatingChanged, this, [this, core, updateChatCore] {
if (!mIsUpdating) {
updateChatCore(core);
disconnect(this, &EventLogList::isUpdatingChanged, this, nullptr);
}
});
} else {
updateChatCore(core);
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);
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() {
auto loadMoreItems = [this] {
if (!mChatCore) return;
@ -152,14 +166,17 @@ void EventLogList::displayMore() {
auto linphoneLogs = chatModel->getHistoryRange(totalItemsCount, newCount);
QList<QSharedPointer<EventLogCore>> *events = new QList<QSharedPointer<EventLogCore>>();
for (auto it : linphoneLogs) {
auto model = EventLogCore::create(it);
events->push_back(model);
auto model = EventLogCore::create(it, chatModel->getMonitor());
if (it->getChatMessage() || model->isHandled()) events->push_front(model);
}
mCoreModelConnection->invokeToCore([this, events] {
int currentCount = mList.count();
for (auto it = events->end() - 1; it >= events->begin(); --it) {
connectItem(*it);
prepend(*it);
if (!events->isEmpty()) {
for (int i = events->size() - 1; i >= 0; --i) {
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) {
mustBeInLinphoneThread(log().arg(Q_FUNC_INFO));
auto oldestEventLoaded = getAt<EventLogCore>(0);
auto linOldest = std::const_pointer_cast<linphone::EventLog>(oldestEventLoaded->getModel()->getEventLog());
auto oldestEventLoaded = mList.count() > 0 ? getAt<EventLogCore>(mList.count() - 1) : nullptr;
auto linOldest = oldestEventLoaded
? std::const_pointer_cast<linphone::EventLog>(oldestEventLoaded->getModel()->getEventLog())
: nullptr;
auto chatModel = mChatCore->getModel();
assert(chatModel);
if (!chatModel) return;
@ -187,47 +206,55 @@ void EventLogList::loadMessagesUpTo(std::shared_ptr<linphone::EventLog> event) {
auto beforeEvents = chatModel->getHistoryRangeNear(mItemsToLoadBeforeSearchResult, 0, event, filters);
auto linphoneLogs = chatModel->getHistoryRangeBetween(event, linOldest, filters);
QList<QSharedPointer<EventLogCore>> *events = new QList<QSharedPointer<EventLogCore>>();
for (auto it : beforeEvents) {
auto model = EventLogCore::create(it);
events->push_back(model);
const auto &linChatRoom = chatModel->getMonitor();
for (const auto &it : beforeEvents) {
auto model = EventLogCore::create(it, linChatRoom);
if (it->getChatMessage() || model->isHandled()) events->push_front(model);
}
for (auto it : linphoneLogs) {
auto model = EventLogCore::create(it);
events->push_back(model);
for (const auto &it : linphoneLogs) {
auto model = EventLogCore::create(it, linChatRoom);
if (it->getChatMessage() || model->isHandled()) events->push_front(model);
}
mCoreModelConnection->invokeToCore([this, events, event] {
for (auto &e : *events) {
for (const auto &e : *events) {
connectItem(e);
add(e);
}
add(*events);
emit messagesLoadedUpTo(event);
});
}
int EventLogList::findFirstUnreadIndex() {
auto eventList = getSharedList<EventLogCore>();
auto it = std::find_if(eventList.begin(), eventList.end(), [](const QSharedPointer<EventLogCore> item) {
return item->getChatMessageCore() && !item->getChatMessageCore()->isRead();
auto it = std::find_if(eventList.rbegin(), eventList.rend(), [](const QSharedPointer<EventLogCore> item) {
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,
QSharedPointer<EventLogCore> startEvent,
bool forward,
bool isFirstResearch) {
void EventLogList::findChatMessageWithFilter(QString filter, int startIndex, bool forward, bool isFirstResearch) {
if (mChatCore) {
if (isFirstResearch) mLastFoundResult.reset();
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;
auto startEventModel = startEvent ? startEvent->getModel() : nullptr;
mCoreModelConnection->invokeToModel([this, chatModel, startEventModel, filter, forward, isFirstResearch] {
auto linStartEvent = startEventModel ? startEventModel->getEventLog() : nullptr;
auto eventLog = chatModel->searchMessageByText(filter, linStartEvent, forward);
if (!eventLog)
if (!eventLog) {
// event not found, search in the entire history
lInfo() << log().arg("not found, search in entire history");
auto eventLog = chatModel->searchMessageByText(filter, nullptr, forward);
}
int index = -1;
if (eventLog) {
lInfo() << log().arg("event with filter found") << eventLog.get();
auto eventList = getSharedList<EventLogCore>();
auto it = std::find_if(eventList.begin(), eventList.end(),
[eventLog](const QSharedPointer<EventLogCore> item) {
@ -255,6 +282,7 @@ void EventLogList::findChatMessageWithFilter(QString filter,
loadMessagesUpTo(eventLog);
}
} else {
lInfo() << log().arg("event not found at all in history");
mCoreModelConnection->invokeToCore([this, index] { emit messageWithFilterFound(index); });
}
});
@ -277,6 +305,9 @@ void EventLogList::setSelf(QSharedPointer<EventLogList> me) {
}
setIsUpdating(true);
beginResetModel();
for (auto &event : getSharedList<EventLogCore>()) {
disconnectItem(event);
}
mList.clear();
if (!mChatCore) {
endResetModel();
@ -294,17 +325,10 @@ void EventLogList::setSelf(QSharedPointer<EventLogList> me) {
auto linphoneLogs = chatModel->getHistoryRange(0, mDisplayItemsStep);
QList<QSharedPointer<EventLogCore>> *events = new QList<QSharedPointer<EventLogCore>>();
for (auto it : linphoneLogs) {
auto model = EventLogCore::create(it);
events->push_back(model);
auto model = EventLogCore::create(it, chatModel->getMonitor());
if (it->getChatMessage() || model->isHandled()) events->push_front(model);
}
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) {
connectItem(event);
mList.append(event);

View file

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

View file

@ -26,7 +26,7 @@
DEFINE_ABSTRACT_OBJECT(EventLogProxy)
EventLogProxy::EventLogProxy(QObject *parent) : LimitProxy(parent) {
EventLogProxy::EventLogProxy(QObject *parent) : QSortFilterProxyModel(parent) {
mList = EventLogList::create();
setSourceModel(mList.get());
}
@ -35,54 +35,41 @@ EventLogProxy::~EventLogProxy() {
}
void EventLogProxy::setSourceModel(QAbstractItemModel *model) {
auto oldEventLogList = getListModel<EventLogList>();
auto oldEventLogList = dynamic_cast<EventLogList *>(sourceModel());
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);
if (newEventLogList) {
connect(newEventLogList, &EventLogList::listAboutToBeReset, this, &EventLogProxy::listAboutToBeReset);
connect(newEventLogList, &EventLogList::chatGuiChanged, this, &EventLogProxy::chatGuiChanged);
connect(this, &EventLogProxy::displayItemsStepChanged, newEventLogList,
[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(this, &EventLogProxy::layoutChanged, newEventLogList, [this, i, newEventLogList] {
disconnect(this, &EventLogProxy::layoutChanged, newEventLogList, nullptr);
auto model = getListModel<EventLogList>();
int proxyIndex =
dynamic_cast<SortFilterList *>(sourceModel())->mapFromSource(newEventLogList->index(i, 0)).row();
if (i != -1) {
loadUntil(proxyIndex);
}
emit indexWithFilterFound(proxyIndex);
});
invalidate();
auto model = dynamic_cast<EventLogList *>(sourceModel());
int proxyIndex = mapFromSource(newEventLogList->index(i, 0)).row();
if (i != -1) {
loadUntil(proxyIndex);
}
emit indexWithFilterFound(proxyIndex);
});
connect(newEventLogList, &EventLogList::eventInsertedByUser, this, [this, newEventLogList](int i) {
int proxyIndex = mapFromSource(newEventLogList->index(i, 0)).row();
emit eventInsertedByUser(proxyIndex);
});
}
setSourceModels(new SortFilterList(model, Qt::DescendingOrder));
sort(0, Qt::DescendingOrder);
QSortFilterProxyModel::setSourceModel(model);
}
ChatGui *EventLogProxy::getChatGui() {
auto model = getListModel<EventLogList>();
auto model = dynamic_cast<EventLogList *>(sourceModel());
if (!mChatGui && model) mChatGui = model->getChat();
return mChatGui;
}
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) {
@ -90,29 +77,86 @@ EventLogGui *EventLogProxy::getEventAtIndex(int i) {
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) {
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() {
auto model = getListModel<EventLogList>();
auto model = dynamic_cast<EventLogList *>(sourceModel());
if (model) {
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) {
auto confInfoList = getListModel<EventLogList>();
if (mMaxDisplayItems < index) setMaxDisplayItems(index + mDisplayItemsStep);
}
int EventLogProxy::findFirstUnreadIndex() {
auto eventLogList = getListModel<EventLogList>();
auto eventLogList = dynamic_cast<EventLogList *>(sourceModel());
if (eventLogList) {
auto listIndex = eventLogList->findFirstUnreadIndex();
if (listIndex != -1) {
listIndex =
dynamic_cast<SortFilterList *>(sourceModel())->mapFromSource(eventLogList->index(listIndex, 0)).row();
listIndex = mapFromSource(eventLogList->index(listIndex, 0)).row();
if (mMaxDisplayItems <= listIndex) setMaxDisplayItems(listIndex + mDisplayItemsStep);
return listIndex;
} else {
@ -122,32 +166,43 @@ int EventLogProxy::findFirstUnreadIndex() {
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) {
auto event = getItemAt<SortFilterList, EventLogList, EventLogCore>(proxyIndex);
auto event = getAt(proxyIndex);
if (event && event->getChatMessageCore()) event->getChatMessageCore()->lMarkAsRead();
}
void EventLogProxy::findIndexCorrespondingToFilter(int startIndex, bool forward, bool isFirstResearch) {
auto filter = getFilterText();
if (filter.isEmpty()) return;
auto eventLogList = getListModel<EventLogList>();
auto eventLogList = dynamic_cast<EventLogList *>(sourceModel());
if (eventLogList) {
auto startEvent = mLastSearchStart;
if (!startEvent) {
startEvent = getItemAt<SortFilterList, EventLogList, EventLogCore>(startIndex);
}
eventLogList->findChatMessageWithFilter(filter, startEvent, forward, isFirstResearch);
auto listIndex = mapToSource(index(startIndex, 0)).row();
eventLogList->findChatMessageWithFilter(filter, listIndex, 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_
#include "EventLogList.hpp"
#include "core/proxy/LimitProxy.hpp"
// #include "core/proxy/LimitProxy.hpp"
#include "tool/AbstractObject.hpp"
#include <QSortFilterProxyModel>
// =============================================================================
class ChatGui;
class EventLogProxy : public LimitProxy, public AbstractObject {
class EventLogProxy : public QSortFilterProxyModel, public AbstractObject {
Q_OBJECT
Q_PROPERTY(int count READ getCount NOTIFY countChanged)
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:
DECLARE_SORTFILTER_CLASS()
// DECLARE_SORTFILTER_CLASS()
EventLogProxy(QObject *parent = Q_NULLPTR);
~EventLogProxy();
@ -43,8 +50,27 @@ public:
void setChatGui(ChatGui *chat);
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 EventLogGui *getEventAtIndex(int i);
QSharedPointer<EventLogCore> getEventCoreAtIndex(int i);
@ -53,15 +79,23 @@ public:
Q_INVOKABLE void findIndexCorrespondingToFilter(int startIndex, bool forward = true, bool isFirstResearch = true);
signals:
void eventInserted(int index, EventLogGui *message);
void eventInsertedByUser(int index);
void indexWithFilterFound(int index);
void listAboutToBeReset();
void chatGuiChanged();
void countChanged();
void initialDisplayItemsChanged();
void maxDisplayItemsChanged();
void displayItemsStepChanged();
void filterTextChanged();
protected:
QSharedPointer<EventLogList> mList;
QSharedPointer<EventLogCore> mLastSearchStart;
ChatGui *mChatGui = nullptr;
int mInitialDisplayItems = -1;
int mMaxDisplayItems = -1;
int mDisplayItemsStep = 5;
QString mFilterText;
DECLARE_ABSTRACT_OBJECT
};

View file

@ -46,7 +46,7 @@ ChatMessageContentCore::ChatMessageContentCore(const std::shared_ptr<linphone::C
mName = QFileInfo(fileName).baseName();
}
}
mFilePath = Utils::coreStringToAppString(content->getFilePath());
mFilePath = QDir::fromNativeSeparators(Utils::coreStringToAppString(content->getFilePath()));
mIsFile = content->isFile();
mIsFileEncrypted = content->isFileEncrypted();
mIsFileTransfer = content->isFileTransfer();
@ -67,8 +67,8 @@ ChatMessageContentCore::ChatMessageContentCore(const std::shared_ptr<linphone::C
mRichFormatText = ToolModel::encodeTextToQmlRichFormat(mUtf8Text, {}, chatRoom);
mWasDownloaded = !mFilePath.isEmpty() && QFileInfo(mFilePath).isFile();
mThumbnail = mFilePath.isEmpty()
? QString()
: QStringLiteral("image://%1/%2").arg(ThumbnailProvider::ProviderId).arg(mFilePath);
? QUrl()
: QUrl(QString("image://%1/%2").arg(ThumbnailProvider::ProviderId).arg(mFilePath));
mChatMessageContentModel = Utils::makeQObject_ptr<ChatMessageContentModel>(content, chatMessageModel);
}
}
@ -92,11 +92,22 @@ void ChatMessageContentCore::setSelf(QSharedPointer<ChatMessageContentCore> me)
});
mChatMessageContentModelConnection->makeConnectToModel(
&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->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(
&ChatMessageContentModel::wasDownloadedChanged,
@ -239,11 +250,11 @@ bool ChatMessageContentCore::wasDownloaded() const {
return mWasDownloaded;
}
QString ChatMessageContentCore::getThumbnail() const {
QUrl ChatMessageContentCore::getThumbnail() const {
return mThumbnail;
}
void ChatMessageContentCore::setThumbnail(const QString &data) {
void ChatMessageContentCore::setThumbnail(const QUrl &data) {
if (mThumbnail != data) {
mThumbnail = data;
emit thumbnailChanged();

View file

@ -36,7 +36,7 @@ class ChatMessageContentCore : public QObject, public AbstractObject {
Q_PROPERTY(QString name READ getName CONSTANT)
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(QString filePath READ getFilePath WRITE setFilePath NOTIFY filePathChanged)
Q_PROPERTY(QString utf8Text READ getUtf8Text CONSTANT)
@ -84,8 +84,8 @@ public:
int getFileDuration() const;
ConferenceInfoGui *getConferenceInfoGui() const;
void setThumbnail(const QString &data);
QString getThumbnail() const;
void setThumbnail(const QUrl &data);
QUrl getThumbnail() const;
bool wasDownloaded() const;
void setWasDownloaded(bool downloaded);
@ -121,7 +121,7 @@ private:
bool mIsText;
bool mIsVoiceRecording;
int mFileDuration;
QString mThumbnail;
QUrl mThumbnail;
QString mUtf8Text;
QString mRichFormatText;
QString mFilePath;

View file

@ -197,7 +197,8 @@ void ChatMessageContentList::setSelf(QSharedPointer<ChatMessageContentList> me)
mModelConnection->makeConnectToCore(&ChatMessageContentList::lUpdate, [this]() {
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;
auto contents = mChatMessageCore->getChatMessageContentList();

View file

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

View file

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

View file

@ -21,12 +21,15 @@
#include "ConferenceInfoCore.hpp"
#include "core/App.hpp"
#include "core/path/Paths.hpp"
#include "core/proxy/ListProxy.hpp"
#include "model/object/VariantObject.hpp"
#include "model/tool/ToolModel.hpp"
#include "tool/Utils.hpp"
#include "tool/thread/SafeConnection.hpp"
#include <QDesktopServices>
DEFINE_ABSTRACT_OBJECT(ConferenceInfoCore)
QSharedPointer<ConferenceInfoCore>
@ -69,6 +72,7 @@ ConferenceInfoCore::ConferenceInfoCore(std::shared_ptr<linphone::ConferenceInfo>
mUri = address && address->isValid() && !address->getDomain().empty()
? Utils::coreStringToAppString(address->asStringUriOnly())
: "";
mIcalendarString = Utils::coreStringToAppString(conferenceInfo->getIcalendarString());
mDateTime = QDateTime::fromMSecsSinceEpoch(conferenceInfo->getDateTime() * 1000);
mDuration = conferenceInfo->getDuration();
mEndDateTime = mDateTime.addSecs(mDuration * 60);
@ -123,6 +127,7 @@ ConferenceInfoCore::ConferenceInfoCore(const ConferenceInfoCore &conferenceInfoC
mIsEnded = conferenceInfoCore.mIsEnded;
mInviteEnabled = conferenceInfoCore.mInviteEnabled;
mConferenceInfoState = conferenceInfoCore.mConferenceInfoState;
mIcalendarString = conferenceInfoCore.mIcalendarString;
}
ConferenceInfoCore::~ConferenceInfoCore() {
@ -195,8 +200,9 @@ void ConferenceInfoCore::setSelf(QSharedPointer<ConferenceInfoCore> me) {
QString uri;
if (state == linphone::ConferenceScheduler::State::Ready) {
uri = mConferenceInfoModel->getConferenceScheduler()->getUri();
emit CoreModel::getInstance()->conferenceInfoReceived(
CoreModel::getInstance()->getCore(), mConferenceInfoModel->getConferenceInfo());
emit CoreModel::getInstance() -> conferenceInfoReceived(
CoreModel::getInstance()->getCore(),
mConferenceInfoModel->getConferenceInfo());
}
mConfInfoModelConnection->invokeToCore([this, state = LinphoneEnums::fromLinphone(state),
infoState = LinphoneEnums::fromLinphone(confInfoState),
@ -600,6 +606,9 @@ void ConferenceInfoCore::save() {
} else lCritical() << "No default account";
// Add text capability for chat in conf
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 confSchedulerModel = confInfoModel->getConferenceScheduler();
if (!confSchedulerModel) {
@ -649,3 +658,14 @@ bool ConferenceInfoCore::isAllDayConf() const {
return mDateTime.time().hour() == 0 && mDateTime.time().minute() == 0 && mEndDateTime.time().hour() == 23 &&
mEndDateTime.time().minute() == 59;
}
void ConferenceInfoCore::exportConferenceToICS() const {
QString filePath(Paths::getAppLocalDirPath() + "conference.ics");
QFile file(filePath);
if (file.open(QIODevice::WriteOnly | QIODevice::Text)) {
QTextStream out(&file);
out << mIcalendarString;
file.close();
}
QDesktopServices::openUrl(QUrl::fromLocalFile(filePath));
}

View file

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

View file

@ -235,13 +235,17 @@ int ConferenceInfoList::getCurrentDateIndex() {
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 confInfoList = getSharedList<ConferenceInfoCore>();
QList<QSharedPointer<ConferenceInfoCore>>::iterator it;
if (mHaveCurrentDate) {
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;
});
} else it = std::find(confInfoList.begin(), confInfoList.end(), nullptr);

View file

@ -48,7 +48,7 @@ public:
void updateHaveCurrentDate();
int getCurrentDateIndex();
QSharedPointer<ConferenceInfoCore> getCurrentDateConfInfo();
QSharedPointer<ConferenceInfoCore> getCurrentDateConfInfo(bool enableCancelledConference = false);
QSharedPointer<ConferenceInfoCore> build(const std::shared_ptr<linphone::ConferenceInfo> &conferenceInfo);
void connectItem(QSharedPointer<ConferenceInfoCore> confInfoCore);

View file

@ -105,7 +105,7 @@ void ConferenceInfoProxy::clear() {
mList->clearData();
}
ConferenceInfoGui *ConferenceInfoProxy::getCurrentDateConfInfo() {
ConferenceInfoGui *ConferenceInfoProxy::getCurrentDateConfInfo(bool enableCancelledConference) {
if (mList) {
auto confInfo = mList->getCurrentDateConfInfo();
return confInfo ? new ConferenceInfoGui(confInfo) : nullptr;
@ -150,7 +150,7 @@ bool ConferenceInfoProxy::SortFilterList::lessThan(const QModelIndex &sourceLeft
auto nowDate = QDate::currentDate();
if (!l || !r) { // sort on date
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 {
return l->getDateTimeUtc() < r->getDateTimeUtc();
}

View file

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

View file

@ -130,7 +130,7 @@ Notifier::~Notifier() {
bool Notifier::createNotification(Notifier::NotificationType type, QVariantMap data) {
mMutex->lock();
// Q_ASSERT(mInstancesNumber <= MaxNotificationsNumber);
if (mInstancesNumber == MaxNotificationsNumber) { // Check existing instances.
if (mInstancesNumber >= MaxNotificationsNumber) { // Check existing instances.
qWarning() << QStringLiteral("Unable to create another notification.");
mMutex->unlock();
return false;
@ -288,9 +288,6 @@ void Notifier::notifyReceivedCall(const shared_ptr<linphone::Call> &call) {
auto voicemailAddress = linphone::Factory::get()->createAddress(
Utils::appStringToCoreString(accountModel->getVoicemailAddress()));
if (voicemailAddress) call->transferTo(voicemailAddress);
} else {
lInfo() << log().arg("Declining call.");
call->decline(linphone::Reason::Busy);
}
return;
}
@ -335,7 +332,8 @@ void Notifier::notifyReceivedMessages(const std::shared_ptr<linphone::ChatRoom>
if (receiverAccount) {
auto senderAccount = ToolModel::findAccount(message->getFromAddress());
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";
return;
}
@ -369,6 +367,7 @@ void Notifier::notifyReceivedMessages(const std::shared_ptr<linphone::ChatRoom>
};
if (messages.size() == 1) { // Display only sender on mono message.
if (message->isRead()) return;
getMessage(message);
if (txt.isEmpty()) { // Do not notify message without content
qDebug() << "empty notif, return";

View file

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

View file

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

View file

@ -115,6 +115,7 @@ static inline QDir getAppPackageDir() {
}
static inline QString getAppPackageDataDirPath() {
QDir executableDir(QCoreApplication::applicationDirPath());
QDir dir = getAppPackageDir();
#ifdef __APPLE__
if (!dir.cd("Resources")) {
@ -126,17 +127,21 @@ static inline QString getAppPackageDataDirPath() {
dir.mkdir("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() {
QDir executableDir(QCoreApplication::applicationDirPath());
QDir dir = getAppPackageDir();
dir.cd(MSPLUGINS_DIR);
return dir.absolutePath();
return executableDir.relativeFilePath(dir.absolutePath());
}
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 getAppAssistantConfigDirPath() {
@ -159,29 +164,6 @@ static inline QString getAppFriendsFilePath() {
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() {
return QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation) + Constants::PathMessageHistoryList;
}
@ -198,7 +180,9 @@ bool Paths::filePathExists(const QString &path, const bool isWritable) {
QFile file(path);
return file.exists();
}
bool Paths::isSameRelativeFile(const QString &filePath, const QString &relativeFilePath) {
return filePath != relativeFilePath && QFileInfo(relativeFilePath) == QFileInfo(filePath);
}
// -----------------------------------------------------------------------------
QString Paths::getAppLocalDirPath() {
@ -283,15 +267,16 @@ QString Paths::getLogsDirPath() {
Constants::PathLogs);
}
QString Paths::getAppRootCaFilePath() {
// Hardcoded because it comes from linphone and is not customizable.
return getReadableFilePath(getAppPackageDataDirPath() + "/linphone/rootca.pem");
}
QString Paths::getMessageHistoryFilePath() {
return getReadableFilePath(
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() {
return getReadableDirPath(getAppPackageMsPluginsDirPath());
}
@ -300,10 +285,6 @@ QString Paths::getPackagePluginsAppDirPath() {
return getReadableDirPath(getAppPackagePluginsDirPath() + Constants::PathPluginsApp);
}
QString Paths::getPackageSoundsResourcesDirPath() {
return getReadableDirPath(getAppPackageDataDirPath() + Constants::PathSounds);
}
QString Paths::getPackageTopDirPath() {
return getReadableDirPath(getAppPackageDataDirPath());
}
@ -319,10 +300,6 @@ QStringList Paths::getPluginsAppFolders() {
return pluginPaths;
}
QString Paths::getRootCaFilePath() {
return getReadableFilePath(getAppRootCaFilePath());
}
QString Paths::getToolsDirPath() {
return getWritableDirPath(QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation) +
Constants::PathTools);

View file

@ -27,8 +27,13 @@
namespace Paths {
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 getAppRootCaFilePath();
QString getAssistantConfigDirPath();
QString getAvatarsDirPath();
QString getVCardsPath();
@ -44,14 +49,11 @@ QString getFriendsListFilePath();
QString getLimeDatabasePath();
QString getLogsDirPath();
QString getMessageHistoryFilePath();
QString getPackageDataDirPath();
QString getPackageMsPluginsDirPath();
QString getPackagePluginsAppDirPath();
QString getPackageSoundsResourcesDirPath();
QString getPackageTopDirPath();
QString getPluginsAppDirPath();
QStringList getPluginsAppFolders();
QString getRootCaFilePath();
QString getToolsDirPath();
QString getUserCertificatesDirPath();
QString getZrtpDataFilePath();

View file

@ -51,8 +51,25 @@ PayloadTypeCore::~PayloadTypeCore() {
void PayloadTypeCore::setSelf(QSharedPointer<PayloadTypeCore> me) {
mPayloadTypeModelConnection = SafeConnection<PayloadTypeCore, PayloadTypeModel>::create(me, mPayloadTypeModel);
DEFINE_CORE_GETSET_CONNECT(mPayloadTypeModelConnection, PayloadTypeCore, PayloadTypeModel, mPayloadTypeModel, bool,
enabled, Enabled)
mPayloadTypeModelConnection->makeConnectToCore(&PayloadTypeCore::setEnabled, [this](bool 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() {

View file

@ -43,7 +43,7 @@ public:
const std::shared_ptr<linphone::PayloadType> &payloadType);
PayloadTypeCore(Family family, const std::shared_ptr<linphone::PayloadType> &payloadType);
PayloadTypeCore(){};
PayloadTypeCore() {};
~PayloadTypeCore();
void setSelf(QSharedPointer<PayloadTypeCore> me);
@ -51,9 +51,15 @@ public:
bool isDownloadable();
QString getMimeType();
Q_INVOKABLE void save();
signals:
void changed();
protected:
Family mFamily;
bool mDownloadable = false;
bool mChanged = false;
DECLARE_CORE_GETSET_MEMBER(bool, enabled, Enabled)
DECLARE_CORE_MEMBER(QString, mimeType, MimeType)
DECLARE_CORE_MEMBER(QString, encoderDescription, EncoderDescription)

View file

@ -37,13 +37,21 @@ LimitProxy::~LimitProxy() {
bool LimitProxy::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const {
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) {
auto secondList = firstList->sourceModel();
if (secondList) {
connect(secondList, &QAbstractItemModel::rowsInserted, this, &LimitProxy::onAdded);
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::filterTypeChanged, this, &LimitProxy::filterTypeChanged);
@ -60,8 +68,8 @@ void LimitProxy::setSourceModels(SortFilterProxy *firstList) {
/*
void LimitProxy::setSourceModels(SortFilterProxy *firstList, QAbstractItemModel *secondList) {
connect(secondList, &QAbstractItemModel::rowsInserted, this, &LimitProxy::invalidateFilter);
connect(secondList, &QAbstractItemModel::rowsRemoved, this, &LimitProxy::invalidateFilter);
connect(secondList, &QAbstractItemModel::rowsInserted, this, &LimitProxy::invalidate);
connect(secondList, &QAbstractItemModel::rowsRemoved, this, &LimitProxy::invalidate);
connect(firstList, &SortFilterProxy::filterTextChanged, this, &LimitProxy::filterTextChanged);
setSourceModel(firstList);
}*/
@ -119,7 +127,7 @@ void LimitProxy::setMaxDisplayItems(int maxItems) {
emit maxDisplayItemsChanged();
if (model && getDisplayCount(modelCount) != oldCount) {
invalidateFilter();
invalidate();
}
}
}
@ -178,6 +186,6 @@ void LimitProxy::onAdded() {
void LimitProxy::onRemoved() {
int count = sourceModel()->rowCount();
if (mMaxDisplayItems > 0 && mMaxDisplayItems <= count) {
invalidateFilter();
invalidate();
}
}

View file

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

View file

@ -26,6 +26,9 @@ SortFilterProxy::SortFilterProxy(QAbstractItemModel *list) : QSortFilterProxyMod
setSourceModel(list);
}
SortFilterProxy::SortFilterProxy() {
}
SortFilterProxy::SortFilterProxy(QAbstractItemModel *list, Qt::SortOrder order) : SortFilterProxy(list) {
sort(0, order);
}
@ -63,9 +66,15 @@ int SortFilterProxy::getFilterType() const {
void SortFilterProxy::setFilterType(int filterType) {
if (getFilterType() != filterType) {
#if QT_VERSION >= QT_VERSION_CHECK(6, 10, 0)
beginFilterChange();
mFilterType = filterType;
endFilterChange();
#else
mFilterType = filterType;
invalidateFilter();
#endif
emit filterTypeChanged(filterType);
invalidate();
}
}
@ -75,8 +84,14 @@ QString SortFilterProxy::getFilterText() const {
void SortFilterProxy::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();
}
}
@ -90,5 +105,10 @@ void SortFilterProxy::remove(int index, int count) {
}
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(QString filterText READ getFilterText WRITE setFilterText NOTIFY filterTextChanged)
SortFilterProxy();
SortFilterProxy(QAbstractItemModel *parent);
SortFilterProxy(QAbstractItemModel *parent, Qt::SortOrder order);
virtual ~SortFilterProxy();

View file

@ -94,18 +94,29 @@ void MagicSearchList::setSelf(QSharedPointer<MagicSearchList> me) {
[this](const std::list<std::shared_ptr<linphone::SearchResult>> &results) {
auto *contacts = new QList<QSharedPointer<FriendCore>>();
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) {
QSharedPointer<FriendCore> contact;
auto linphoneFriend = it->getFriend();
bool isStored = false;
if (linphoneFriend) {
if (!mShowMe && userAddress && userAddress->weakEqual(linphoneFriend->getAddress())) {
lWarning() << log().arg("do not show my own address in this contact list");
continue;
}
isStored =
(ldapContacts->findFriendByAddress(linphoneFriend->getAddress()) != linphoneFriend);
contact = FriendCore::create(linphoneFriend, isStored, it->getSourceFlags());
contacts->append(contact);
} 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);
contact = FriendCore::create(linphoneFriend, isStored, it->getSourceFlags());
auto displayName = Utils::coreStringToAppString(address->getDisplayName());
@ -125,7 +136,7 @@ void MagicSearchList::setSelf(QSharedPointer<MagicSearchList> me) {
contacts->append(contact);
} else if (!it->getPhoneNumber().empty()) {
auto phoneNumber = it->getPhoneNumber();
linphoneFriend = CoreModel::getInstance()->getCore()->createFriend();
linphoneFriend = core->createFriend();
linphoneFriend->addPhoneNumber(phoneNumber);
contact = FriendCore::create(linphoneFriend, isStored, it->getSourceFlags());
contact->setGivenName(Utils::coreStringToAppString(it->getPhoneNumber()));
@ -202,6 +213,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 {
return mAggregationFlag;
}

View file

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

View file

@ -148,6 +148,14 @@ void MagicSearchProxy::setMaxResults(int flags) {
mList->setMaxResults(flags);
}
bool MagicSearchProxy::getShowMe() const {
return mList->getShowMe();
}
void MagicSearchProxy::setShowMe(bool showMe) {
mList->setShowMe(showMe);
}
MagicSearchProxy *MagicSearchProxy::getParentProxy() const {
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 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
NOTIFY aggregationFlagChanged)
@ -60,6 +61,9 @@ public:
int getMaxResults() const;
void setMaxResults(int maxResults);
bool getShowMe() const;
void setShowMe(bool showMe);
MagicSearchProxy *getParentProxy() const;
void setList(QSharedPointer<MagicSearchList> list);
Q_INVOKABLE void setParentProxy(MagicSearchProxy *proxy);
@ -77,6 +81,7 @@ signals:
void sourceFlagsChanged(int sourceFlags);
void aggregationFlagChanged(LinphoneEnums::MagicSearchAggregation aggregationFlag);
void maxResultsChanged(int maxResults);
void showMeChanged(bool showMe);
void forceUpdate();
void localFriendCreated(int index);
void parentProxyChanged();

View file

@ -61,6 +61,13 @@ SettingsCore::SettingsCore(QObject *parent) : QObject(parent) {
mRingtoneFolder = mRingtonePath.left(mRingtonePath.lastIndexOf(QDir::separator()));
}
// Network
mIpv6Enabled = settingsModel->getIpv6Enabled();
// Advanced
mAutoStart = settingsModel->getAutoStart();
mHideFps = settingsModel->getHideFps();
// Audio
mCaptureDevices = settingsModel->getCaptureDevices();
mPlaybackDevices = settingsModel->getPlaybackDevices();
@ -107,13 +114,15 @@ SettingsCore::SettingsCore(QObject *parent) : QObject(parent) {
mEmojiFont = settingsModel->getEmojiFont();
mTextMessageFont = settingsModel->getTextMessageFont();
// Check for update
mIsCheckForUpdateAvailable = settingsModel->isCheckForUpdateAvailable();
// Ui
INIT_CORE_MEMBER(DisableChatFeature, settingsModel)
INIT_CORE_MEMBER(DisableMeetingsFeature, settingsModel)
INIT_CORE_MEMBER(DisableBroadcastFeature, settingsModel)
INIT_CORE_MEMBER(HideSettings, settingsModel)
INIT_CORE_MEMBER(HideAccountSettings, settingsModel)
INIT_CORE_MEMBER(HideFps, settingsModel)
INIT_CORE_MEMBER(DisableCallRecordings, settingsModel)
INIT_CORE_MEMBER(AssistantHideCreateAccount, settingsModel)
INIT_CORE_MEMBER(AssistantHideCreateAccount, settingsModel)
@ -129,7 +138,6 @@ SettingsCore::SettingsCore(QObject *parent) : QObject(parent) {
INIT_CORE_MEMBER(AutoStart, settingsModel)
INIT_CORE_MEMBER(ExitOnClose, settingsModel)
INIT_CORE_MEMBER(SyncLdapContacts, settingsModel)
INIT_CORE_MEMBER(Ipv6Enabled, settingsModel)
INIT_CORE_MEMBER(ConfigLocale, settingsModel)
INIT_CORE_MEMBER(DownloadFolder, settingsModel)
@ -138,7 +146,6 @@ SettingsCore::SettingsCore(QObject *parent) : QObject(parent) {
INIT_CORE_MEMBER(CallToneIndicationsEnabled, settingsModel)
INIT_CORE_MEMBER(CommandLine, settingsModel)
INIT_CORE_MEMBER(DisableCommandLine, settingsModel)
INIT_CORE_MEMBER(DisableCallForward, settingsModel)
INIT_CORE_MEMBER(CallForwardToAddress, settingsModel)
INIT_CORE_MEMBER(ThemeMainColor, settingsModel)
@ -207,10 +214,10 @@ SettingsCore::SettingsCore(const SettingsCore &settingsCore) {
mAssistantGoDirectlyToThirdPartySipAccountLogin = settingsCore.mAssistantGoDirectlyToThirdPartySipAccountLogin;
mAssistantThirdPartySipAccountDomain = settingsCore.mAssistantThirdPartySipAccountDomain;
mAssistantThirdPartySipAccountTransport = settingsCore.mAssistantThirdPartySipAccountTransport;
mAutoStart = settingsCore.mAutoStart;
mExitOnClose = settingsCore.mExitOnClose;
mSyncLdapContacts = settingsCore.mSyncLdapContacts;
mIpv6Enabled = settingsCore.mIpv6Enabled;
mAutoStart = settingsCore.mAutoStart;
mConfigLocale = settingsCore.mConfigLocale;
mDownloadFolder = settingsCore.mDownloadFolder;
mShortcutCount = settingsCore.mShortcutCount;
@ -218,7 +225,7 @@ SettingsCore::SettingsCore(const SettingsCore &settingsCore) {
mCallToneIndicationsEnabled = settingsCore.mCallToneIndicationsEnabled;
mCommandLine = settingsCore.mCommandLine;
mDisableCommandLine = settingsCore.mDisableCommandLine;
mDisableCallForward = settingsCore.mDisableCallForward;
mCallForwardToAddress = settingsCore.mCallForwardToAddress;
mDefaultDomain = settingsCore.mDefaultDomain;
mShowAccountDevices = settingsCore.mShowAccountDevices;
@ -234,6 +241,13 @@ void SettingsCore::setSelf(QSharedPointer<SettingsCore> me) {
mustBeInLinphoneThread(getClassName());
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
mSettingsModelConnection->makeConnectToModel(&SettingsModel::vfsEnabledChanged, [this](const bool enabled) {
mSettingsModelConnection->invokeToCore([this, enabled]() { setVfsEnabled(enabled); });
@ -250,6 +264,31 @@ void SettingsCore::setSelf(QSharedPointer<SettingsCore> me) {
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
mSettingsModelConnection->makeConnectToModel(
&SettingsModel::autoDownloadReceivedFilesChanged, [this](const bool enabled) {
@ -294,7 +333,7 @@ void SettingsCore::setSelf(QSharedPointer<SettingsCore> me) {
});
});
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) {
@ -304,7 +343,7 @@ void SettingsCore::setSelf(QSharedPointer<SettingsCore> me) {
});
});
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) {
@ -440,8 +479,6 @@ void SettingsCore::setSelf(QSharedPointer<SettingsCore> me) {
ExitOnClose)
DEFINE_CORE_GETSET_CONNECT(mSettingsModelConnection, SettingsCore, SettingsModel, settingsModel, bool,
syncLdapContacts, SyncLdapContacts)
DEFINE_CORE_GETSET_CONNECT(mSettingsModelConnection, SettingsCore, SettingsModel, settingsModel, bool, ipv6Enabled,
Ipv6Enabled)
DEFINE_CORE_GETSET_CONNECT(mSettingsModelConnection, SettingsCore, SettingsModel, settingsModel, QString,
configLocale, ConfigLocale)
DEFINE_CORE_GETSET_CONNECT(mSettingsModelConnection, SettingsCore, SettingsModel, settingsModel, QString,
@ -456,10 +493,6 @@ void SettingsCore::setSelf(QSharedPointer<SettingsCore> me) {
commandLine, CommandLine)
DEFINE_CORE_GETSET_CONNECT(mSettingsModelConnection, SettingsCore, SettingsModel, settingsModel, bool,
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,
themeAboutPictureUrl, ThemeAboutPictureUrl)
DEFINE_CORE_GETSET_CONNECT(mSettingsModelConnection, SettingsCore, SettingsModel, settingsModel, QString,
@ -549,13 +582,14 @@ void SettingsCore::reset(const SettingsCore &settingsCore) {
setAssistantGoDirectlyToThirdPartySipAccountLogin(settingsCore.mAssistantGoDirectlyToThirdPartySipAccountLogin);
setAssistantThirdPartySipAccountDomain(settingsCore.mAssistantThirdPartySipAccountDomain);
setAssistantThirdPartySipAccountTransport(settingsCore.mAssistantThirdPartySipAccountTransport);
setAutoStart(settingsCore.mAutoStart);
setExitOnClose(settingsCore.mExitOnClose);
setSyncLdapContacts(settingsCore.mSyncLdapContacts);
setCardDAVMinCharForResearch(settingsCore.mCardDAVMinCharForResearch);
setIpv6Enabled(settingsCore.mIpv6Enabled);
setAutoStart(settingsCore.mAutoStart);
setConfigLocale(settingsCore.mConfigLocale);
setDownloadFolder(settingsCore.mDownloadFolder);
setCallForwardToAddress(settingsCore.mCallForwardToAddress);
}
QString SettingsCore::getConfigPath(const QCommandLineParser &parser) {
@ -614,6 +648,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) {
if (mAutoDownloadReceivedFiles != enabled) {
mAutoDownloadReceivedFiles = enabled;
@ -706,7 +770,7 @@ bool SettingsCore::isSaved() const {
void SettingsCore::setIsSaved(bool saved) {
if (mIsSaved != saved) {
mIsSaved = saved;
emit isSavedChanged();
emit isSavedChanged(saved);
}
}
@ -757,6 +821,13 @@ void SettingsCore::setCaptureGain(float gain) {
}
}
void SettingsCore::setCaptureGainFromModel(float gain) {
if (mCaptureGain != gain) {
mCaptureGain = gain;
emit captureGainChanged(gain);
}
}
QVariantMap SettingsCore::getConferenceLayout() const {
return mConferenceLayout;
}
@ -801,6 +872,13 @@ void SettingsCore::setPlaybackGain(float gain) {
}
}
void SettingsCore::setPlaybackGainFromModel(float gain) {
if (mPlaybackGain != gain) {
mPlaybackGain = gain;
emit playbackGainChanged(gain);
}
}
QVariantMap SettingsCore::getCaptureDevice() const {
return mCaptureDevice;
}
@ -998,10 +1076,6 @@ void SettingsCore::setShowAccountDevices(bool show) {
}
}
bool SettingsCore::getAutoStart() const {
return mAutoStart;
}
bool SettingsCore::getExitOnClose() const {
return mExitOnClose;
}
@ -1028,7 +1102,8 @@ QString SettingsCore::getConfigLocale() const {
QString SettingsCore::getDownloadFolder() const {
auto path = mDownloadFolder;
if (mDownloadFolder.isEmpty()) path = Paths::getDownloadDirPath();
return QDir::cleanPath(path) + QDir::separator();
QString cleanPath = QDir::cleanPath(path);
return cleanPath;
}
void SettingsCore::writeIntoModel(std::shared_ptr<SettingsModel> model) const {
@ -1088,12 +1163,13 @@ void SettingsCore::writeIntoModel(std::shared_ptr<SettingsModel> model) const {
model->setAssistantGoDirectlyToThirdPartySipAccountLogin(mAssistantGoDirectlyToThirdPartySipAccountLogin);
model->setAssistantThirdPartySipAccountDomain(mAssistantThirdPartySipAccountDomain);
model->setAssistantThirdPartySipAccountTransport(mAssistantThirdPartySipAccountTransport);
model->setAutoStart(mAutoStart);
model->setExitOnClose(mExitOnClose);
model->setSyncLdapContacts(mSyncLdapContacts);
model->setIpv6Enabled(mIpv6Enabled);
model->setAutoStart(mAutoStart);
model->setConfigLocale(mConfigLocale);
model->setDownloadFolder(mDownloadFolder);
model->setCallForwardToAddress(mCallForwardToAddress);
}
void SettingsCore::writeFromModel(const std::shared_ptr<SettingsModel> &model) {
@ -1112,6 +1188,9 @@ void SettingsCore::writeFromModel(const std::shared_ptr<SettingsModel> &model) {
mRingtoneFileName =
ringtone.exists() ? ringtone.fileName() : mRingtonePath.right(mRingtonePath.lastIndexOf(QDir::separator()));
// Advanced
mAutoStart = model->getAutoStart();
// Chat
mAutoDownloadReceivedFiles = model->getAutoDownloadReceivedFiles();
@ -1143,6 +1222,9 @@ void SettingsCore::writeFromModel(const std::shared_ptr<SettingsModel> &model) {
mLogsFolder = model->getLogsFolder();
mLogsEmail = model->getLogsEmail();
// Check update
mIsCheckForUpdateAvailable = model->isCheckForUpdateAvailable();
// UI
mDisableChatFeature = model->getDisableChatFeature();
mDisableMeetingsFeature = model->getDisableMeetingsFeature();
@ -1167,13 +1249,20 @@ void SettingsCore::writeFromModel(const std::shared_ptr<SettingsModel> &model) {
mSyncLdapContacts = model->getSyncLdapContacts();
mCardDAVMinCharForResearch = model->getCardDAVMinCharResearch();
mIpv6Enabled = model->getIpv6Enabled();
mAutoStart = model->getAutoStart();
mConfigLocale = model->getConfigLocale();
mDownloadFolder = model->getDownloadFolder();
mCallForwardToAddress = model->getCallForwardToAddress();
}
bool SettingsCore::isCheckForUpdateAvailable() const {
return mIsCheckForUpdateAvailable;
}
void SettingsCore::save() {
mustBeInMainThread(getClassName() + Q_FUNC_INFO);
SettingsCore *thisCopy = new SettingsCore(*this);
emit autoStartChanged();
if (SettingsModel::getInstance()) {
mSettingsModelConnection->invokeToModel([this, thisCopy] {
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 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(
QVariantMap conferenceLayout READ getConferenceLayout WRITE setConferenceLayout NOTIFY conferenceLayoutChanged)
Q_PROPERTY(
@ -144,9 +155,11 @@ public:
float getPlaybackGain() const;
void setPlaybackGain(float gain);
void setPlaybackGainFromModel(float gain);
float getCaptureGain() const;
void setCaptureGain(float gain);
void setCaptureGainFromModel(float gain);
QVariantList getCaptureDevices() const;
void setCaptureDevices(QVariantList devices);
@ -193,6 +206,32 @@ public:
Q_INVOKABLE void closeCallSettings();
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;
void setLogsEnabled(bool enabled);
@ -218,6 +257,7 @@ public:
bool getCardDAVMinCharForResearch() const;
void setCardDAVMinCharForResearch(int min);
bool isCheckForUpdateAvailable() const;
Q_INVOKABLE void save();
Q_INVOKABLE void undo();
@ -226,7 +266,6 @@ public:
DECLARE_CORE_GETSET_MEMBER(bool, disableBroadcastFeature, DisableBroadcastFeature)
DECLARE_CORE_GETSET_MEMBER(bool, hideSettings, HideSettings)
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, assistantHideCreateAccount, AssistantHideCreateAccount)
DECLARE_CORE_GETSET_MEMBER(bool, assistantDisableQrCode, AssistantDisableQrCode)
@ -239,10 +278,8 @@ public:
AssistantGoDirectlyToThirdPartySipAccountLogin)
DECLARE_CORE_GETSET_MEMBER(QString, assistantThirdPartySipAccountDomain, AssistantThirdPartySipAccountDomain)
DECLARE_CORE_GETSET_MEMBER(QString, assistantThirdPartySipAccountTransport, AssistantThirdPartySipAccountTransport)
DECLARE_CORE_GETSET(bool, autoStart, AutoStart)
DECLARE_CORE_GETSET(bool, exitOnClose, ExitOnClose)
DECLARE_CORE_GETSET(bool, syncLdapContacts, SyncLdapContacts)
DECLARE_CORE_GETSET_MEMBER(bool, ipv6Enabled, Ipv6Enabled)
DECLARE_CORE_GETSET(QString, configLocale, ConfigLocale)
DECLARE_CORE_GETSET(QString, downloadFolder, DownloadFolder)
// Read-only
@ -251,8 +288,6 @@ public:
DECLARE_CORE_GETSET_MEMBER(bool, callToneIndicationsEnabled, CallToneIndicationsEnabled)
DECLARE_CORE_GETSET_MEMBER(bool, disableCommandLine, DisableCommandLine)
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, textMessageFont, TextMessageFont)
// Theme
@ -282,6 +317,17 @@ signals:
void captureDevicesChanged(const QVariantList &devices);
void playbackDevicesChanged(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 mediaEncryptionsChanged(const QVariantList &encryptions);
@ -302,7 +348,7 @@ signals:
void createEndToEndEncryptedMeetingsAndGroupCallsChanged(bool endtoend);
void isSavedChanged();
void isSavedChanged(bool saved);
void lSetPlaybackDevice(QVariantMap device);
void playbackDeviceChanged(const QVariantMap &device);
@ -380,6 +426,16 @@ private:
float mPlaybackGain;
int mEchoCancellationCalibration;
// Network
bool mIpv6Enabled;
// Call Forward
QString mCallForwardToAddress;
// Advanced
bool mAutoStart;
bool mHideFps;
// Debug logs
bool mLogsEnabled;
bool mFullLogsEnabled;
@ -401,6 +457,9 @@ private:
// CardDAV
int mCardDAVMinCharForResearch = 0;
// Check update
bool mIsCheckForUpdateAvailable = false;
DECLARE_ABSTRACT_OBJECT
};
#endif

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/use-other-sip-account.rc
data/shaders/roundEffect.frag.qsb
data/shaders/opacityMask.frag.qsb
data/emoji/emoji.json
)

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" fill="#000000" viewBox="0 0 256 256"><path d="M208,32H184V24a8,8,0,0,0-16,0v8H88V24a8,8,0,0,0-16,0v8H48A16,16,0,0,0,32,48V208a16,16,0,0,0,16,16H208a16,16,0,0,0,16-16V48A16,16,0,0,0,208,32ZM72,48v8a8,8,0,0,0,16,0V48h80v8a8,8,0,0,0,16,0V48h24V80H48V48ZM208,208H48V96H208V208Zm-48-56a8,8,0,0,1-8,8H136v16a8,8,0,0,1-16,0V160H104a8,8,0,0,1,0-16h16V128a8,8,0,0,1,16,0v16h16A8,8,0,0,1,160,152Z"></path></svg>

After

Width:  |  Height:  |  Size: 464 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

View file

@ -80,7 +80,7 @@
<location filename="../../core/account/AccountCore.cpp" line="500"/>
<source>manage_account_status_cleared_summary</source>
<extracomment>&quot;Compte désactivé, vous ne recevrez ni appel ni message.&quot;</extracomment>
<translation type="unfinished"></translation>
<translation>Compte desactivat, no rebràs pas trucades ni missatges.</translation>
</message>
</context>
<context>
@ -2187,6 +2187,11 @@
<extracomment>Could not open file : unknown path %1</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../core/chat/message/content/ChatMessageContentCore.cpp" line="108"/>
<source>info_popup_error_titile</source>
<translation>Error</translation>
</message>
</context>
<context>
<name>ChatMessageContentList</name>
@ -2241,18 +2246,6 @@ Error</extracomment>
</context>
<context>
<name>ChatMessageContentModel</name>
<message>
<location filename="../../model/chat/message/content/ChatMessageContentModel.cpp" line="105"/>
<source>popup_error_title</source>
<extracomment>Error</extracomment>
<translation>Error</translation>
</message>
<message>
<location filename="../../model/chat/message/content/ChatMessageContentModel.cpp" line="108"/>
<source>popup_download_error_message</source>
<extracomment>This file was already downloaded and is no more on the server. Your peer have to resend it if you want to get it</extracomment>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>ChatMessageCore</name>
@ -2606,7 +2599,7 @@ Error</extracomment>
<message>
<location filename="../../view/Page/Form/Contact/ContactEdition.qml" line="62"/>
<source>close_accessible_name</source>
<extracomment>Close %n</extracomment>
<extracomment>Close %1</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
@ -7429,4 +7422,12 @@ Failed to create 1-1 conversation with %1 !</extracomment>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>CoreModel</name>
<message>
<location filename="../../model/core/CoreModel.cpp" line="220"/>
<source>info_popup_error_title</source>
<translation>Error</translation>
</message>
</context>
</TS>

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -2241,18 +2241,6 @@ Error</extracomment>
</context>
<context>
<name>ChatMessageContentModel</name>
<message>
<location filename="../../model/chat/message/content/ChatMessageContentModel.cpp" line="105"/>
<source>popup_error_title</source>
<extracomment>Error</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../model/chat/message/content/ChatMessageContentModel.cpp" line="108"/>
<source>popup_download_error_message</source>
<extracomment>This file was already downloaded and is no more on the server. Your peer have to resend it if you want to get it</extracomment>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>ChatMessageCore</name>
@ -2606,7 +2594,7 @@ Error</extracomment>
<message>
<location filename="../../view/Page/Form/Contact/ContactEdition.qml" line="62"/>
<source>close_accessible_name</source>
<extracomment>Close %n</extracomment>
<extracomment>Close %1</extracomment>
<translation type="unfinished"></translation>
</message>
<message>

View file

@ -19,7 +19,7 @@
<location filename="../../view/Page/Layout/Settings/AbstractSettingsLayout.qml" line="90"/>
<source>save_settings_accessible_name</source>
<extracomment>Save %1 settings</extracomment>
<translation type="unfinished"></translation>
<translation>Gorde %1 ezarpenak</translation>
</message>
</context>
<context>
@ -152,37 +152,37 @@
<location filename="../../model/account/AccountModel.cpp" line="253"/>
<source>set_mwi_server_address_failed_error_message</source>
<extracomment>&quot;Unable to set voicemail server address, failed creating address from %1&quot; : %1 is address</extracomment>
<translation type="unfinished"></translation>
<translation>Ezin da ahots-postontziaren zerbitzariaren helbidea ezarri, huts egin du %1 (e)tik helbidea sortzean</translation>
</message>
<message>
<location filename="../../model/account/AccountModel.cpp" line="292"/>
<source>set_server_address_failed_error_message</source>
<extracomment>&quot;Unable to set server address, failed creating address from %1&quot;</extracomment>
<translation type="unfinished"></translation>
<translation>Ezin da zerbitzariaren helbidea ezarri, huts egin du %1(e)tik helbidea sortzean</translation>
</message>
<message>
<location filename="../../model/account/AccountModel.cpp" line="309"/>
<source>set_outbound_proxy_uri_failed_error_message</source>
<extracomment>Unable to set outbound proxy uri, failed creating address from %1</extracomment>
<translation type="unfinished"></translation>
<translation>Ezin da irteerako proxy uri-a ezarri, huts egin du %1 (e)tik helbidea sortzean</translation>
</message>
<message>
<location filename="../../model/account/AccountModel.cpp" line="418"/>
<source>set_conference_factory_address_failed_error_message</source>
<extracomment>&quot;Unable to set the conversation server address, failed creating address from %1&quot;</extracomment>
<translation type="unfinished"></translation>
<translation>Ezin da elkarrizketa-zerbitzariaren helbidea ezarri, huts egin du %1 (e)tik helbidea sortzean</translation>
</message>
<message>
<location filename="../../model/account/AccountModel.cpp" line="440"/>
<source>set_audio_conference_factory_address_failed_error_message</source>
<extracomment>&quot;Unable to set the meeting server address, failed creating address from %1&quot;</extracomment>
<translation type="unfinished"></translation>
<translation>Ezin da bilera-zerbitzariaren helbidea ezarri, huts egin du %1 (e)tik helbidea sortzean</translation>
</message>
<message>
<location filename="../../model/account/AccountModel.cpp" line="487"/>
<source>set_voicemail_address_failed_error_message</source>
<extracomment>Unable to set voicemail address, failed creating address from %1</extracomment>
<translation type="unfinished"></translation>
<translation>Ezin da ahots-postontziaren helbidea ezarri, huts egin du %1 (e)tik helbidea sortzean</translation>
</message>
</context>
<context>
@ -302,7 +302,7 @@
<location filename="../../view/Page/Layout/Settings/AccountSettingsGeneralLayout.qml" line="345"/>
<source>device_last_updated_time_no_info</source>
<extracomment>&quot;No information&quot;</extracomment>
<translation type="unfinished"></translation>
<translation>Ez dago informaziorik</translation>
</message>
</context>
<context>
@ -415,7 +415,7 @@
<message>
<location filename="../../view/Page/Layout/Settings/AccountSettingsParametersLayout.qml" line="127"/>
<source>account_settings_registrar_uri_title</source>
<translation type="unfinished"></translation>
<translation>Erregistratu URIa</translation>
</message>
<message>
<location filename="../../view/Page/Layout/Settings/AccountSettingsParametersLayout.qml" line="140"/>
@ -426,7 +426,7 @@
<location filename="../../view/Page/Layout/Settings/AccountSettingsParametersLayout.qml" line="145"/>
<source>login_proxy_server_url_tooltip</source>
<extracomment>&quot;If this field is filled, the outbound proxy will be enabled automatically. Leave it empty to disable it.&quot;</extracomment>
<translation type="unfinished"></translation>
<translation>Eremu hau betetzen bada, irteerako proxya automatikoki aktibatuko da. Utzi hutsik desaktibatzeko.</translation>
</message>
<message>
<location filename="../../view/Page/Layout/Settings/AccountSettingsParametersLayout.qml" line="157"/>
@ -490,16 +490,16 @@
<source>add_participant_selected_count</source>
<comment>0</comment>
<extracomment>&quot;%n participant(s) sélectionné(s)&quot;</extracomment>
<translation type="unfinished">
<numerusform></numerusform>
<numerusform></numerusform>
<translation>
<numerusform>Partaide %1 aukeratuta</numerusform>
<numerusform>%1 partaide aukeratuta</numerusform>
</translation>
</message>
<message>
<location filename="../../view/Page/Form/Meeting/AddParticipantsForm.qml" line="95"/>
<source>remove_participant_accessible_name</source>
<extracomment>Remove participant %1</extracomment>
<translation type="unfinished"></translation>
<translation>Ezabatu %1 partaidea</translation>
</message>
<message>
<location filename="../../view/Page/Form/Meeting/AddParticipantsForm.qml" line="151"/>
@ -578,7 +578,7 @@
<location filename="../../view/Page/Layout/Settings/AdvancedSettingsLayout.qml" line="103"/>
<source>download_apply_remote_provisioning_accessible_name</source>
<extracomment>&quot;Download and apply remote provisioning&quot;</extracomment>
<translation type="unfinished"></translation>
<translation>Deskargatu eta aplikatu urruneko hornikuntza</translation>
</message>
<message>
<location filename="../../view/Page/Layout/Settings/AdvancedSettingsLayout.qml" line="116"/>
@ -724,7 +724,7 @@
<message>
<location filename="../../core/App.cpp" line="1448"/>
<source>mark_all_read_action</source>
<translation type="unfinished"></translation>
<translation>Markatu denak irakurrita bezala</translation>
</message>
</context>
<context>
@ -849,7 +849,7 @@
<location filename="../../view/Page/Layout/Settings/CallForwardSettingsLayout.qml" line="73"/>
<source>settings_call_forward_activate_title</source>
<extracomment>&quot;Forward calls&quot;</extracomment>
<translation type="unfinished"></translation>
<translation>Deiak desbideratzea</translation>
</message>
<message>
<location filename="../../view/Page/Layout/Settings/CallForwardSettingsLayout.qml" line="75"/>
@ -2187,6 +2187,11 @@
<extracomment>Could not open file : unknown path %1</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../core/chat/message/content/ChatMessageContentCore.cpp" line="108"/>
<source>info_popup_error_titile</source>
<translation>Errorea</translation>
</message>
</context>
<context>
<name>ChatMessageContentList</name>
@ -2241,18 +2246,6 @@ Error</extracomment>
</context>
<context>
<name>ChatMessageContentModel</name>
<message>
<location filename="../../model/chat/message/content/ChatMessageContentModel.cpp" line="105"/>
<source>popup_error_title</source>
<extracomment>Error</extracomment>
<translation>Errorea</translation>
</message>
<message>
<location filename="../../model/chat/message/content/ChatMessageContentModel.cpp" line="108"/>
<source>popup_download_error_message</source>
<extracomment>This file was already downloaded and is no more on the server. Your peer have to resend it if you want to get it</extracomment>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>ChatMessageCore</name>
@ -2606,7 +2599,7 @@ Error</extracomment>
<message>
<location filename="../../view/Page/Form/Contact/ContactEdition.qml" line="62"/>
<source>close_accessible_name</source>
<extracomment>Close %n</extracomment>
<extracomment>Close %1</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
@ -7433,4 +7426,12 @@ Failed to create 1-1 conversation with %1 !</extracomment>
<translation>Ok</translation>
</message>
</context>
<context>
<name>CoreModel</name>
<message>
<location filename="../../model/core/CoreModel.cpp" line="220"/>
<source>info_popup_error_title</source>
<translation>Errorea</translation>
</message>
</context>
</TS>

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -2241,18 +2241,6 @@ Error</extracomment>
</context>
<context>
<name>ChatMessageContentModel</name>
<message>
<location filename="../../model/chat/message/content/ChatMessageContentModel.cpp" line="105"/>
<source>popup_error_title</source>
<extracomment>Error</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../model/chat/message/content/ChatMessageContentModel.cpp" line="108"/>
<source>popup_download_error_message</source>
<extracomment>This file was already downloaded and is no more on the server. Your peer have to resend it if you want to get it</extracomment>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>ChatMessageCore</name>
@ -2606,7 +2594,7 @@ Error</extracomment>
<message>
<location filename="../../view/Page/Form/Contact/ContactEdition.qml" line="62"/>
<source>close_accessible_name</source>
<extracomment>Close %n</extracomment>
<extracomment>Close %1</extracomment>
<translation type="unfinished"></translation>
</message>
<message>

View file

@ -2241,18 +2241,6 @@ Error</extracomment>
</context>
<context>
<name>ChatMessageContentModel</name>
<message>
<location filename="../../model/chat/message/content/ChatMessageContentModel.cpp" line="105"/>
<source>popup_error_title</source>
<extracomment>Error</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../model/chat/message/content/ChatMessageContentModel.cpp" line="108"/>
<source>popup_download_error_message</source>
<extracomment>This file was already downloaded and is no more on the server. Your peer have to resend it if you want to get it</extracomment>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>ChatMessageCore</name>
@ -2606,7 +2594,7 @@ Error</extracomment>
<message>
<location filename="../../view/Page/Form/Contact/ContactEdition.qml" line="62"/>
<source>close_accessible_name</source>
<extracomment>Close %n</extracomment>
<extracomment>Close %1</extracomment>
<translation type="unfinished"></translation>
</message>
<message>

View file

@ -2241,18 +2241,6 @@ Error</extracomment>
</context>
<context>
<name>ChatMessageContentModel</name>
<message>
<location filename="../../model/chat/message/content/ChatMessageContentModel.cpp" line="105"/>
<source>popup_error_title</source>
<extracomment>Error</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../model/chat/message/content/ChatMessageContentModel.cpp" line="108"/>
<source>popup_download_error_message</source>
<extracomment>This file was already downloaded and is no more on the server. Your peer have to resend it if you want to get it</extracomment>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>ChatMessageCore</name>
@ -2606,7 +2594,7 @@ Error</extracomment>
<message>
<location filename="../../view/Page/Form/Contact/ContactEdition.qml" line="62"/>
<source>close_accessible_name</source>
<extracomment>Close %n</extracomment>
<extracomment>Close %1</extracomment>
<translation type="unfinished"></translation>
</message>
<message>

View file

@ -1121,7 +1121,7 @@
<location filename="../../model/call/CallModel.cpp" line="376"/>
<source>call_error_io_error_toast</source>
<extracomment>&quot;Unavailable service or network error&quot;</extracomment>
<translation type="unfinished"></translation>
<translation>Serviço indisponível ou erro de rede</translation>
</message>
<message>
<location filename="../../model/call/CallModel.cpp" line="380"/>
@ -1668,7 +1668,7 @@
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="1114"/>
<source>conference_participants_list_title</source>
<extracomment>&quot;Participants (%1)&quot;</extracomment>
<translation type="unfinished"></translation>
<translation>Participantes (%1)</translation>
</message>
<message numerus="yes">
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="1135"/>
@ -2187,6 +2187,11 @@
<extracomment>Could not open file : unknown path %1</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../core/chat/message/content/ChatMessageContentCore.cpp" line="108"/>
<source>info_popup_error_titile</source>
<translation>Erro</translation>
</message>
</context>
<context>
<name>ChatMessageContentList</name>
@ -2241,18 +2246,6 @@ Error</extracomment>
</context>
<context>
<name>ChatMessageContentModel</name>
<message>
<location filename="../../model/chat/message/content/ChatMessageContentModel.cpp" line="105"/>
<source>popup_error_title</source>
<extracomment>Error</extracomment>
<translation>Erro</translation>
</message>
<message>
<location filename="../../model/chat/message/content/ChatMessageContentModel.cpp" line="108"/>
<source>popup_download_error_message</source>
<extracomment>This file was already downloaded and is no more on the server. Your peer have to resend it if you want to get it</extracomment>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>ChatMessageCore</name>
@ -2606,7 +2599,7 @@ Error</extracomment>
<message>
<location filename="../../view/Page/Form/Contact/ContactEdition.qml" line="62"/>
<source>close_accessible_name</source>
<extracomment>Close %n</extracomment>
<extracomment>Close %1</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
@ -3229,7 +3222,7 @@ Error</extracomment>
<message>
<location filename="../../view/Page/Layout/Chat/ConversationInfos.qml" line="270"/>
<source>group_infos_participants</source>
<translation type="unfinished"></translation>
<translation>Participantes (%1)</translation>
</message>
<message>
<location filename="../../view/Page/Layout/Chat/ConversationInfos.qml" line="287"/>
@ -7429,4 +7422,12 @@ Failed to create 1-1 conversation with %1 !</extracomment>
<translation>Ok</translation>
</message>
</context>
<context>
<name>CoreModel</name>
<message>
<location filename="../../model/core/CoreModel.cpp" line="220"/>
<source>info_popup_error_title</source>
<translation>Erro</translation>
</message>
</context>
</TS>

File diff suppressed because it is too large Load diff

View file

@ -2189,6 +2189,11 @@
<extracomment>Could not open file : unknown path %1</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../core/chat/message/content/ChatMessageContentCore.cpp" line="108"/>
<source>info_popup_error_titile</source>
<translation>Ошибка</translation>
</message>
</context>
<context>
<name>ChatMessageContentList</name>
@ -2244,18 +2249,6 @@ Error</extracomment>
</context>
<context>
<name>ChatMessageContentModel</name>
<message>
<location filename="../../model/chat/message/content/ChatMessageContentModel.cpp" line="105"/>
<source>popup_error_title</source>
<extracomment>Error</extracomment>
<translation>Ошибка</translation>
</message>
<message>
<location filename="../../model/chat/message/content/ChatMessageContentModel.cpp" line="108"/>
<source>popup_download_error_message</source>
<extracomment>This file was already downloaded and is no more on the server. Your peer have to resend it if you want to get it</extracomment>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>ChatMessageCore</name>
@ -2610,7 +2603,7 @@ Error</extracomment>
<message>
<location filename="../../view/Page/Form/Contact/ContactEdition.qml" line="62"/>
<source>close_accessible_name</source>
<extracomment>Close %n</extracomment>
<extracomment>Close %1</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
@ -7455,4 +7448,12 @@ Failed to create 1-1 conversation with %1 !</extracomment>
<translation>Ок</translation>
</message>
</context>
<context>
<name>CoreModel</name>
<message>
<location filename="../../model/core/CoreModel.cpp" line="220"/>
<source>info_popup_error_title</source>
<translation>Ошибка</translation>
</message>
</context>
</TS>

View file

@ -2244,18 +2244,6 @@ Error</extracomment>
</context>
<context>
<name>ChatMessageContentModel</name>
<message>
<location filename="../../model/chat/message/content/ChatMessageContentModel.cpp" line="105"/>
<source>popup_error_title</source>
<extracomment>Error</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../model/chat/message/content/ChatMessageContentModel.cpp" line="108"/>
<source>popup_download_error_message</source>
<extracomment>This file was already downloaded and is no more on the server. Your peer have to resend it if you want to get it</extracomment>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>ChatMessageCore</name>
@ -2610,7 +2598,7 @@ Error</extracomment>
<message>
<location filename="../../view/Page/Form/Contact/ContactEdition.qml" line="62"/>
<source>close_accessible_name</source>
<extracomment>Close %n</extracomment>
<extracomment>Close %1</extracomment>
<translation type="unfinished"></translation>
</message>
<message>

View file

@ -2189,6 +2189,11 @@
<extracomment>Could not open file : unknown path %1</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../core/chat/message/content/ChatMessageContentCore.cpp" line="108"/>
<source>info_popup_error_titile</source>
<translation>Помилка</translation>
</message>
</context>
<context>
<name>ChatMessageContentList</name>
@ -2244,18 +2249,6 @@ Error</extracomment>
</context>
<context>
<name>ChatMessageContentModel</name>
<message>
<location filename="../../model/chat/message/content/ChatMessageContentModel.cpp" line="105"/>
<source>popup_error_title</source>
<extracomment>Error</extracomment>
<translation>Помилка</translation>
</message>
<message>
<location filename="../../model/chat/message/content/ChatMessageContentModel.cpp" line="108"/>
<source>popup_download_error_message</source>
<extracomment>This file was already downloaded and is no more on the server. Your peer have to resend it if you want to get it</extracomment>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>ChatMessageCore</name>
@ -2610,7 +2603,7 @@ Error</extracomment>
<message>
<location filename="../../view/Page/Form/Contact/ContactEdition.qml" line="62"/>
<source>close_accessible_name</source>
<extracomment>Close %n</extracomment>
<extracomment>Close %1</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
@ -7455,4 +7448,12 @@ Failed to create 1-1 conversation with %1 !</extracomment>
<translation>Ок</translation>
</message>
</context>
<context>
<name>CoreModel</name>
<message>
<location filename="../../model/core/CoreModel.cpp" line="220"/>
<source>info_popup_error_title</source>
<translation>Помилка</translation>
</message>
</context>
</TS>

View file

@ -7,7 +7,7 @@
<location filename="../../view/Page/Layout/Settings/AbstractSettingsLayout.qml" line="67"/>
<source>return_accessible_name</source>
<extracomment>Return</extracomment>
<translation type="unfinished"></translation>
<translation></translation>
</message>
<message>
<location filename="../../view/Page/Layout/Settings/AbstractSettingsLayout.qml" line="86"/>
@ -19,7 +19,7 @@
<location filename="../../view/Page/Layout/Settings/AbstractSettingsLayout.qml" line="90"/>
<source>save_settings_accessible_name</source>
<extracomment>Save %1 settings</extracomment>
<translation type="unfinished"></translation>
<translation>%1</translation>
</message>
</context>
<context>
@ -152,37 +152,37 @@
<location filename="../../model/account/AccountModel.cpp" line="253"/>
<source>set_mwi_server_address_failed_error_message</source>
<extracomment>&quot;Unable to set voicemail server address, failed creating address from %1&quot; : %1 is address</extracomment>
<translation type="unfinished"></translation>
<translation>%1</translation>
</message>
<message>
<location filename="../../model/account/AccountModel.cpp" line="292"/>
<source>set_server_address_failed_error_message</source>
<extracomment>&quot;Unable to set server address, failed creating address from %1&quot;</extracomment>
<translation type="unfinished"></translation>
<translation>%1</translation>
</message>
<message>
<location filename="../../model/account/AccountModel.cpp" line="309"/>
<source>set_outbound_proxy_uri_failed_error_message</source>
<extracomment>Unable to set outbound proxy uri, failed creating address from %1</extracomment>
<translation type="unfinished"></translation>
<translation> URI %1 </translation>
</message>
<message>
<location filename="../../model/account/AccountModel.cpp" line="418"/>
<source>set_conference_factory_address_failed_error_message</source>
<extracomment>&quot;Unable to set the conversation server address, failed creating address from %1&quot;</extracomment>
<translation type="unfinished"></translation>
<translation>%1</translation>
</message>
<message>
<location filename="../../model/account/AccountModel.cpp" line="440"/>
<source>set_audio_conference_factory_address_failed_error_message</source>
<extracomment>&quot;Unable to set the meeting server address, failed creating address from %1&quot;</extracomment>
<translation type="unfinished"></translation>
<translation>%1</translation>
</message>
<message>
<location filename="../../model/account/AccountModel.cpp" line="487"/>
<source>set_voicemail_address_failed_error_message</source>
<extracomment>Unable to set voicemail address, failed creating address from %1</extracomment>
<translation type="unfinished"></translation>
<translation>%1</translation>
</message>
</context>
<context>
@ -302,7 +302,7 @@
<location filename="../../view/Page/Layout/Settings/AccountSettingsGeneralLayout.qml" line="345"/>
<source>device_last_updated_time_no_info</source>
<extracomment>&quot;No information&quot;</extracomment>
<translation type="unfinished"></translation>
<translation></translation>
</message>
</context>
<context>
@ -645,13 +645,13 @@
<location filename="../../core/App.cpp" line="660"/>
<source>info_popup_configuration_failed_message</source>
<extracomment>Remote provisioning failed : %1</extracomment>
<translation type="unfinished"></translation>
<translation>%1</translation>
</message>
<message>
<location filename="../../core/App.cpp" line="654"/>
<source>configuration_error_detail</source>
<extracomment>not reachable</extracomment>
<translation type="unfinished"></translation>
<translation></translation>
</message>
<message>
<location filename="../../core/App.cpp" line="926"/>
@ -725,6 +725,11 @@
<source>mark_all_read_action</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../core/App.cpp" line="452"/>
<source>info_popup_new_version_download_label</source>
<translation> </translation>
</message>
</context>
<context>
<name>AuthenticationDialog</name>
@ -2185,6 +2190,11 @@
<extracomment>Could not open file : unknown path %1</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../core/chat/message/content/ChatMessageContentCore.cpp" line="108"/>
<source>info_popup_error_titile</source>
<translation></translation>
</message>
</context>
<context>
<name>ChatMessageContentList</name>
@ -2238,18 +2248,6 @@ Error</extracomment>
</context>
<context>
<name>ChatMessageContentModel</name>
<message>
<location filename="../../model/chat/message/content/ChatMessageContentModel.cpp" line="105"/>
<source>popup_error_title</source>
<extracomment>Error</extracomment>
<translation></translation>
</message>
<message>
<location filename="../../model/chat/message/content/ChatMessageContentModel.cpp" line="108"/>
<source>popup_download_error_message</source>
<extracomment>This file was already downloaded and is no more on the server. Your peer have to resend it if you want to get it</extracomment>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>ChatMessageCore</name>
@ -2602,7 +2600,7 @@ Error</extracomment>
<message>
<location filename="../../view/Page/Form/Contact/ContactEdition.qml" line="62"/>
<source>close_accessible_name</source>
<extracomment>Close %n</extracomment>
<extracomment>Close %1</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
@ -7411,4 +7409,12 @@ Failed to create 1-1 conversation with %1 !</extracomment>
<translation>OK</translation>
</message>
</context>
<context>
<name>CoreModel</name>
<message>
<location filename="../../model/core/CoreModel.cpp" line="220"/>
<source>info_popup_error_title</source>
<translation></translation>
</message>
</context>
</TS>

View file

@ -19,6 +19,7 @@
*/
#include "AccountManager.hpp"
#include "tool/accessibility/AccessibilityHelper.hpp"
#include <QDebug>
#include <QDesktopServices>
@ -66,7 +67,7 @@ bool AccountManager::login(QString username,
mustBeInLinphoneThread(log().arg(Q_FUNC_INFO));
auto core = CoreModel::getInstance()->getCore();
auto factory = linphone::Factory::get();
QString assistantFile = (!QString::compare(domain, "sip.linphone.org") || domain.isEmpty())
QString assistantFile = (!QString::compare(domain, "sip.linphone.org", Qt::CaseInsensitive) || domain.isEmpty())
? "use-app-sip-account.rc"
: "use-other-sip-account.rc";
auto account = createAccount(assistantFile);
@ -81,8 +82,13 @@ bool AccountManager::login(QString username,
auto otherAccounts = core->getAccountList();
for (auto otherAccount : otherAccounts) {
auto otherParams = otherAccount->getParams();
if (otherParams->getIdentityAddress()->getUsername() == Utils::appStringToCoreString(username) &&
otherParams->getDomain() == Utils::appStringToCoreString(domain)) {
if (domain.isEmpty()) {
lDebug() << "domain is empty, setting \"sip.linphone.org\" by default";
domain = "sip.linphone.org";
}
if (!QString::compare(Utils::coreStringToAppString(otherParams->getIdentityAddress()->getUsername()), username,
Qt::CaseInsensitive) &&
!QString::compare(Utils::coreStringToAppString(otherParams->getDomain()), domain, Qt::CaseInsensitive)) {
//: "The account is already connected"
*errorMessage = tr("assistant_account_login_already_connected_error");
return false;
@ -96,7 +102,7 @@ bool AccountManager::login(QString username,
}
if (!outboundProxyAddress.isEmpty()) {
auto linOutboundProxyAddress = ToolModel::interpretUrl(outboundProxyAddress);
params->setRoutesAddresses({linOutboundProxyAddress});
if (linOutboundProxyAddress) params->setRoutesAddresses({linOutboundProxyAddress});
}
if (!domain.isEmpty()) {
identity->setDomain(Utils::appStringToCoreString(domain));
@ -153,6 +159,7 @@ bool AccountManager::login(QString username,
errorMessage = tr("assistant_account_login_forbidden_error");
//: "Error during connection, please verify your parameters"
else errorMessage = tr("assistant_account_login_error");
AccessibilityHelper::announceMessage(errorMessage);
mAccountModel->removeAccount();
} else if (state == linphone::RegistrationState::Ok) {
core->setDefaultAccount(account);

View file

@ -125,7 +125,7 @@ void AccountModel::setPictureUri(QString uri) {
// Hack because Account doesn't provide callbacks on updated data
// emit pictureUriChanged(uri);
auto core = CoreModel::getInstance()->getCore();
emit CoreModel::getInstance()->defaultAccountChanged(core, core->getDefaultAccount());
emit CoreModel::getInstance() -> defaultAccountChanged(core, core->getDefaultAccount());
}
void AccountModel::onDefaultAccountChanged() {
@ -148,7 +148,7 @@ void AccountModel::removeAccount() {
? Utils::coreStringToAppString(params->getIdentityAddress()->asString())
: "Null");
mToRemove = true;
if (mMonitor) core->removeAccount(mMonitor);
if (mMonitor) core->removeAccountWithData(mMonitor);
}
std::shared_ptr<linphone::Account> AccountModel::getAccount() const {
@ -182,7 +182,7 @@ void AccountModel::setDisplayName(QString displayName) {
// Hack because Account doesn't provide callbacks on updated data
// emit displayNameChanged(displayName);
auto core = CoreModel::getInstance()->getCore();
emit CoreModel::getInstance()->defaultAccountChanged(core, core->getDefaultAccount());
emit CoreModel::getInstance() -> defaultAccountChanged(core, core->getDefaultAccount());
}
void AccountModel::setDialPlan(int index) {
@ -274,7 +274,7 @@ void AccountModel::setTransport(linphone::TransportType value, bool save) {
QString AccountModel::getRegistrarUri() const {
if (mMonitor->getParams()->getServerAddress())
return Utils::coreStringToAppString(mMonitor->getParams()->getServerAddress()->asString());
return Utils::coreStringToAppString(mMonitor->getParams()->getServerAddress()->asStringUriOnly());
else return "";
}
@ -292,13 +292,12 @@ void AccountModel::setRegistrarUri(QString value) {
emit setValueFailed(tr("set_server_address_failed_error_message").arg(value));
qWarning() << "Unable to set ServerAddress, failed creating address from" << value;
}
emit registrarUriChanged(Utils::coreStringToAppString(address->asString()));
}
QString AccountModel::getOutboundProxyUri() const {
auto routeAddresses = mMonitor->getParams()->getRoutesAddresses();
auto outbound =
routeAddresses.empty() ? QString() : Utils::coreStringToAppString(routeAddresses.front()->asString());
routeAddresses.empty() ? QString() : Utils::coreStringToAppString(routeAddresses.front()->asStringUriOnly());
return outbound;
}
@ -308,11 +307,11 @@ void AccountModel::setOutboundProxyUri(QString value) {
//: Unable to set outbound proxy uri, failed creating address from %1
emit setValueFailed(tr("set_outbound_proxy_uri_failed_error_message").arg(value));
return;
} else {
auto params = mMonitor->getParams()->clone();
params->setRoutesAddresses({linOutboundProxyAddress});
emit outboundProxyUriChanged(value);
}
auto params = mMonitor->getParams()->clone();
params->setRoutesAddresses({linOutboundProxyAddress});
emit outboundProxyUriChanged(value);
}
bool AccountModel::getOutboundProxyEnabled() const {
@ -400,7 +399,7 @@ void AccountModel::setExpire(int value) {
QString AccountModel::getConferenceFactoryAddress() const {
auto confAddress = mMonitor->getParams()->getConferenceFactoryAddress();
return confAddress ? Utils::coreStringToAppString(confAddress->asString()) : QString();
return confAddress ? Utils::coreStringToAppString(confAddress->asStringUriOnly()) : QString();
}
void AccountModel::setConferenceFactoryAddress(QString value) {
@ -422,7 +421,7 @@ void AccountModel::setConferenceFactoryAddress(QString value) {
QString AccountModel::getAudioVideoConferenceFactoryAddress() const {
auto confAddress = mMonitor->getParams()->getAudioVideoConferenceFactoryAddress();
return confAddress ? Utils::coreStringToAppString(confAddress->asString()) : QString();
return confAddress ? Utils::coreStringToAppString(confAddress->asStringUriOnly()) : QString();
}
void AccountModel::setAudioVideoConferenceFactoryAddress(QString value) {
@ -491,7 +490,7 @@ void AccountModel::setVoicemailAddress(QString value) {
QString AccountModel::getVoicemailAddress() const {
auto addr = mMonitor->getParams()->getVoicemailAddress();
return addr ? Utils::coreStringToAppString(addr->asString()) : "";
return addr ? Utils::coreStringToAppString(addr->asStringUriOnly()) : "";
}
// UserData (see hpp for explanations)
@ -585,7 +584,7 @@ void AccountModel::setPresence(LinphoneEnums::Presence presence,
}
setNotificationsAllowed(
presence != LinphoneEnums::Presence::DoNotDisturb &&
presence != LinphoneEnums::Presence::Offline && presence != LinphoneEnums::Presence::DoNotDisturb &&
(presence != LinphoneEnums::Presence::Away ||
core->getConfig()->getBool(accountSection, "allow_notifications_in_presence_away", true)) &&
(presence != LinphoneEnums::Presence::Busy ||
@ -606,4 +605,16 @@ bool AccountModel::forwardToVoiceMailInDndPresence() {
std::list<std::shared_ptr<linphone::ChatRoom>> AccountModel::getChatRooms() {
return mMonitor->getChatRooms();
}
}
QString AccountModel::getCcmpServerUrl() const {
return Utils::coreStringToAppString(mMonitor->getParams()->getCcmpServerUrl());
}
void AccountModel::setCcmpServerUrl(QString value) {
mustBeInLinphoneThread(log().arg(Q_FUNC_INFO));
auto params = mMonitor->getParams()->clone();
params->setCcmpServerUrl(Utils::appStringToCoreString(value));
mMonitor->setParams(params);
emit ccmpServerUrlChanged(value);
}

View file

@ -91,6 +91,8 @@ public:
std::string configAccountSection();
bool forwardToVoiceMailInDndPresence();
std::list<std::shared_ptr<linphone::ChatRoom>> getChatRooms();
QString getCcmpServerUrl() const;
void setCcmpServerUrl(QString value);
signals:
void registrationStateChanged(const std::shared_ptr<linphone::Account> &account,
@ -126,6 +128,7 @@ signals:
void showMwiChanged(bool show);
void voicemailAddressChanged(QString value);
void presenceChanged(LinphoneEnums::Presence presence, bool userInitiated);
void ccmpServerUrlChanged(QString value);
void setValueFailed(const QString &errorMessage);

View file

@ -97,15 +97,16 @@ void CarddavModel::remove() {
void CarddavModel::onSyncStatusChanged(const std::shared_ptr<linphone::FriendList> &friendList,
linphone::FriendList::SyncStatus status,
const std::string &message) {
lInfo() << log().arg("carddav sync status changed :") << message;
if (status == linphone::FriendList::SyncStatus::Successful) {
lInfo() << log().arg("Successfully synchronized:") << mCarddavFriendList->getUri();
setMonitor(nullptr);
SettingsModel::setCardDAVListForNewFriends(mStoreNewFriendsInIt ? friendList->getDisplayName() : "");
emit saved(true);
emit saved(true, Utils::coreStringToAppString(message));
}
if (status == linphone::FriendList::SyncStatus::Failure) {
lWarning() << log().arg("Synchronization failure:") << mCarddavFriendList->getUri();
setMonitor(nullptr);
emit saved(false);
emit saved(false, Utils::coreStringToAppString(message));
}
}

View file

@ -36,12 +36,17 @@ public:
CarddavModel(const std::shared_ptr<linphone::FriendList> &carddavFriendList, QObject *parent = nullptr);
~CarddavModel();
void save(std::string displayName, std::string uri, std::string username, std::string password, std::string realm, bool storeNewFriendsInIt);
void save(std::string displayName,
std::string uri,
std::string username,
std::string password,
std::string realm,
bool storeNewFriendsInIt);
void remove();
bool storeNewFriendsInIt();
signals:
void saved(bool success);
void saved(bool success, QString message);
void removed();
private:

View file

@ -28,17 +28,16 @@ LdapModel::LdapModel(const std::shared_ptr<linphone::RemoteContactDirectory> &ld
mustBeInLinphoneThread(getClassName());
if (ldap) {
mLdap = ldap;
mLdapParamsClone = mLdap->getLdapParams();
mLdapParamsClone = mLdap->getLdapRemoteContactDirectory();
} else {
mLdapParamsClone = CoreModel::getInstance()->getCore()->createLdapParams();
mLdapParamsClone->setDelay(2000);
mLdapParamsClone->enableTls(true);
mLdapParamsClone->setEnabled(true);
mLdap = CoreModel::getInstance()->getCore()->createLdapRemoteContactDirectory(mLdapParamsClone);
mLdap->setDelay(2000);
mLdap->setTimeout(5);
mLdap->setLimit(50);
mLdap->setMinCharacters(0); // Needs to be 0 if Contacts list should be synchronized with LDAP AB
mLdap->enable(true);
}
}
@ -65,7 +64,7 @@ void LdapModel::save() {
mLdap->setMinCharacters(oldMinChars);
core->addRemoteContactDirectory(mLdap);
lDebug() << log().arg("LDAP Server saved");
mLdapParamsClone = mLdap->getLdapParams();
mLdapParamsClone = mLdap->getLdapRemoteContactDirectory();
// Clean cache to take account new searches
auto ldapFriendList = core->getFriendListByName("ldap_friends");
if (ldapFriendList) core->removeFriendList(ldapFriendList);
@ -92,7 +91,7 @@ void LdapModel::setDebug(const bool &data) {
}
}
DEFINE_GETSET(LdapModel, bool, enabled, Enabled, mLdapParamsClone)
DEFINE_GETSET_ENABLE(LdapModel, enabled, Enabled, mLdap)
DEFINE_GETSET_MODEL_STRING(LdapModel, serverUrl, ServerUrl, mLdap)
DEFINE_GETSET_MODEL_STRING(LdapModel, bindDn, BindDn, mLdapParamsClone)
DEFINE_GETSET_MODEL_STRING(LdapModel, password, Password, mLdapParamsClone)
@ -107,7 +106,7 @@ DEFINE_GETSET_MODEL_STRING(LdapModel, baseObject, BaseObject, mLdapParamsClone)
DEFINE_GETSET_MODEL_STRING(LdapModel, filter, Filter, mLdapParamsClone)
DEFINE_GETSET(LdapModel, int, limit, Limit, mLdap)
DEFINE_GETSET(LdapModel, int, timeout, Timeout, mLdap)
DEFINE_GETSET(LdapModel, int, delay, Delay, mLdapParamsClone)
DEFINE_GETSET(LdapModel, int, delay, Delay, mLdap)
DEFINE_GETSET(LdapModel, int, minCharacters, MinCharacters, mLdap)
DEFINE_GETSET_MODEL_STRING(LdapModel, nameAttribute, NameAttribute, mLdapParamsClone)
DEFINE_GETSET_MODEL_STRING(LdapModel, sipAttribute, SipAttribute, mLdapParamsClone)

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
* (see https://www.linphone.org).
@ -69,6 +69,7 @@ void CallModel::accept(bool withVideo) {
activateLocalVideo(params, withVideo);
mMonitor->acceptWithParams(params);
emit localVideoEnabledChanged(withVideo);
emit cameraEnabledChanged(params->cameraEnabled());
}
void CallModel::decline() {
@ -104,7 +105,9 @@ void CallModel::transferTo(const std::shared_ptr<linphone::Address> &address) {
void CallModel::transferToAnother(const std::shared_ptr<linphone::Call> &call) {
mustBeInLinphoneThread(log().arg(Q_FUNC_INFO));
if (mMonitor->transferToAnother(call) == -1)
if (!call) return;
// Transfer paused call to current call
if (call->transferToAnother(mMonitor) == -1)
lWarning() << log()
.arg(QStringLiteral("Unable to transfer: `%1`."))
.arg(Utils::coreStringToAppString(call->getRemoteAddress()->asStringUriOnly()));
@ -128,13 +131,25 @@ void CallModel::setSpeakerMuted(bool isMuted) {
emit speakerMutedChanged(isMuted);
}
void CallModel::enableVideo(bool enable) {
mustBeInLinphoneThread(log().arg(Q_FUNC_INFO));
auto params = CoreModel::getInstance()->getCore()->createCallParams(mMonitor);
params->enableVideo(enable);
mMonitor->update(params);
}
bool CallModel::videoEnabled() const {
return mMonitor->getParams()->videoEnabled();
}
void CallModel::activateLocalVideo(std::shared_ptr<linphone::CallParams> &params, bool enable) {
lInfo() << sLog()
.arg("Updating call with video enabled and media direction set to %1")
.arg((int)params->getVideoDirection());
params->enableVideo(SettingsModel::getInstance()->getVideoEnabled());
auto videoDirection = enable ? linphone::MediaDirection::SendRecv : linphone::MediaDirection::RecvOnly;
params->setVideoDirection(videoDirection);
auto videoDirection = params->getVideoDirection();
params->setVideoDirection(enable || params->screenSharingEnabled() ? linphone::MediaDirection::SendRecv
: linphone::MediaDirection::RecvOnly);
}
void CallModel::setLocalVideoEnabled(bool enabled) {
@ -144,6 +159,15 @@ void CallModel::setLocalVideoEnabled(bool enabled) {
mMonitor->update(params);
}
void CallModel::setCameraEnabled(bool enabled) {
mustBeInLinphoneThread(log().arg(Q_FUNC_INFO));
auto params = CoreModel::getInstance()->getCore()->createCallParams(mMonitor);
activateLocalVideo(params, enabled);
lInfo() << log().arg("Enable camera :") << enabled;
params->enableCamera(enabled);
mMonitor->update(params);
}
void CallModel::startRecording() {
mustBeInLinphoneThread(log().arg(Q_FUNC_INFO));
mMonitor->startRecording();
@ -431,8 +455,11 @@ void CallModel::onStateChanged(const std::shared_ptr<linphone::Call> &call,
setConference(call->getConference());
mDurationTimer.start();
// After UpdatedByRemote, video direction could be changed.
auto videoDirection = call->getParams()->getVideoDirection();
auto params = call->getParams();
auto videoDirection = params->getVideoDirection();
auto remoteVideoDirection = call->getRemoteParams()->getVideoDirection();
lInfo() << log().arg("Camera enabled changed") << params->cameraEnabled();
emit cameraEnabledChanged(params->cameraEnabled());
emit localVideoEnabledChanged(videoDirection == linphone::MediaDirection::SendOnly ||
videoDirection == linphone::MediaDirection::SendRecv);
emit remoteVideoEnabledChanged(remoteVideoDirection == linphone::MediaDirection::SendOnly ||
@ -491,6 +518,7 @@ void CallModel::onVideoDisplayErrorOccurred(const std::shared_ptr<linphone::Call
void CallModel::onAudioDeviceChanged(const std::shared_ptr<linphone::Call> &call,
const std::shared_ptr<linphone::AudioDevice> &audioDevice) {
lInfo() << log().arg("audio device changed");
emit audioDeviceChanged(call, audioDevice);
}
@ -501,3 +529,27 @@ void CallModel::onRemoteRecording(const std::shared_ptr<linphone::Call> &call, b
void CallModel::onAuthenticationTokenVerified(const std::shared_ptr<linphone::Call> &call, bool verified) {
emit authenticationTokenVerified(call, verified);
}
void CallModel::onHeadsetAnswerCallRequested(const std::shared_ptr<linphone::Call> &call) {
emit headsetAnswerCallRequested();
}
void CallModel::onHeadsetEndCallRequested(const std::shared_ptr<linphone::Call> &call) {
emit headsetEndCallRequested();
}
void CallModel::onHeadsetHoldCallRequested(const std::shared_ptr<linphone::Call> &call) {
emit headsetHoldCallRequested();
}
void CallModel::onHeadsetMicrophoneMuteToggled(const std::shared_ptr<linphone::Call> &call, bool mute) {
emit headsetMicrophoneMuteToggled(mute);
}
void CallModel::onHeadsetRejectCallRequested(const std::shared_ptr<linphone::Call> &call) {
emit headsetRejectCallRequested();
}
void CallModel::onHeadsetResumeCallRequested(const std::shared_ptr<linphone::Call> &call) {
emit headsetResumeCallRequested();
}

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
* (see https://www.linphone.org).
@ -45,6 +45,7 @@ public:
void setMicrophoneMuted(bool isMuted);
void setSpeakerMuted(bool isMuted);
void setLocalVideoEnabled(bool enabled);
void setCameraEnabled(bool enabled);
void startRecording();
void stopRecording();
void setRecordFile(const std::string &path);
@ -82,7 +83,8 @@ public:
LinphoneEnums::VideoSourceScreenSharingType getVideoSourceType() const;
int getScreenSharingIndex() const;
void setVideoSourceDescriptorModel(std::shared_ptr<VideoSourceDescriptorModel> model = nullptr);
void enableVideo(bool enable);
bool videoEnabled() const;
static void activateLocalVideo(std::shared_ptr<linphone::CallParams> &params, bool enable);
void sendDtmf(const QString &dtmf);
@ -97,6 +99,7 @@ signals:
void microphoneVolumeChanged(float);
void pausedChanged(bool paused);
void remoteVideoEnabledChanged(bool remoteVideoEnabled);
void cameraEnabledChanged(bool enalbed);
void localVideoEnabledChanged(bool enabled);
void recordingChanged(const std::shared_ptr<linphone::Call> &call, bool recording);
void speakerVolumeGainChanged(float volume);
@ -153,6 +156,12 @@ private:
const std::shared_ptr<linphone::AudioDevice> &audioDevice) override;
virtual void onRemoteRecording(const std::shared_ptr<linphone::Call> &call, bool recording) override;
virtual void onAuthenticationTokenVerified(const std::shared_ptr<linphone::Call> &call, bool verified) override;
virtual void onHeadsetAnswerCallRequested(const std::shared_ptr<linphone::Call> &call) override;
virtual void onHeadsetEndCallRequested(const std::shared_ptr<linphone::Call> &call) override;
virtual void onHeadsetHoldCallRequested(const std::shared_ptr<linphone::Call> &call) override;
virtual void onHeadsetMicrophoneMuteToggled(const std::shared_ptr<linphone::Call> &call, bool mute) override;
virtual void onHeadsetRejectCallRequested(const std::shared_ptr<linphone::Call> &call) override;
virtual void onHeadsetResumeCallRequested(const std::shared_ptr<linphone::Call> &call) override;
signals:
void dtmfReceived(const std::shared_ptr<linphone::Call> &call, int dtmf);
@ -182,6 +191,12 @@ signals:
const std::shared_ptr<linphone::AudioDevice> &audioDevice);
void remoteRecording(const std::shared_ptr<linphone::Call> &call, bool recording);
void authenticationTokenVerified(const std::shared_ptr<linphone::Call> &call, bool verified);
void headsetAnswerCallRequested();
void headsetEndCallRequested();
void headsetHoldCallRequested();
void headsetMicrophoneMuteToggled(bool mute);
void headsetRejectCallRequested();
void headsetResumeCallRequested();
};
#endif

View file

@ -44,7 +44,7 @@ ChatModel::ChatModel(const std::shared_ptr<linphone::ChatRoom> &chatroom, QObjec
ChatModel::~ChatModel() {
mustBeInLinphoneThread("~" + getClassName());
disconnect(CoreModel::getInstance().get(), &CoreModel::messageReadInChatRoom, this, nullptr);
disconnect(CoreModel::getInstance().get(), &CoreModel::chatRoomRead, this, nullptr);
}
QDateTime ChatModel::getLastUpdateTime() {
@ -173,7 +173,6 @@ void ChatModel::leave() {
void ChatModel::deleteChatRoom() {
CoreModel::getInstance()->getCore()->deleteChatRoom(mMonitor);
emit deleted();
}
std::shared_ptr<linphone::ChatMessage>
@ -186,6 +185,11 @@ ChatModel::createReplyMessage(const std::shared_ptr<linphone::ChatMessage> &mess
return mMonitor->createReplyMessage(message);
}
std::shared_ptr<linphone::ChatMessage>
ChatModel::createReplacesMessage(const std::shared_ptr<linphone::ChatMessage> &message) {
return mMonitor->createReplacesMessage(message);
}
std::shared_ptr<linphone::ChatMessage>
ChatModel::createForwardMessage(const std::shared_ptr<linphone::ChatMessage> &message) {
return mMonitor->createForwardMessage(message);
@ -212,7 +216,7 @@ ChatModel::searchMessageByText(QString text, std::shared_ptr<const linphone::Eve
}
void ChatModel::compose() {
mMonitor->compose();
mMonitor->composeTextMessage();
}
linphone::ChatRoom::State ChatModel::getState() const {

View file

@ -68,6 +68,7 @@ public:
std::shared_ptr<linphone::ChatMessage> createReplyMessage(const std::shared_ptr<linphone::ChatMessage> &message);
std::shared_ptr<linphone::ChatMessage> createForwardMessage(const std::shared_ptr<linphone::ChatMessage> &message);
std::shared_ptr<linphone::ChatMessage> createReplacesMessage(const std::shared_ptr<linphone::ChatMessage> &message);
std::shared_ptr<linphone::ChatMessage> createTextMessageFromText(QString text);
std::shared_ptr<linphone::ChatMessage> createMessage(QString text,

View file

@ -51,7 +51,7 @@ ChatMessageModel::~ChatMessageModel() {
}
QString ChatMessageModel::getText() const {
return ToolModel::getMessageFromContent(mMonitor->getContents());
return ToolModel::getMessageFromMessage(mMonitor);
}
QString ChatMessageModel::getUtf8Text() const {
@ -92,7 +92,7 @@ bool ChatMessageModel::isRead() const {
void ChatMessageModel::markAsRead() {
mMonitor->markAsRead();
emit messageRead();
emit CoreModel::getInstance()->messageReadInChatRoom(mMonitor->getChatRoom());
emit CoreModel::getInstance() -> messageReadInChatRoom(mMonitor->getChatRoom());
}
void ChatMessageModel::deleteMessageFromChatRoom(bool deletedByUser) {
@ -103,6 +103,13 @@ void ChatMessageModel::deleteMessageFromChatRoom(bool deletedByUser) {
}
}
void ChatMessageModel::retractMessageFromChatRoom() {
auto chatRoom = mMonitor->getChatRoom();
if (chatRoom) {
chatRoom->retractMessage(mMonitor);
}
}
void ChatMessageModel::sendReaction(const QString &reaction) {
auto linReaction = mMonitor->createReaction(Utils::appStringToCoreString(reaction));
linReaction->send();
@ -188,3 +195,11 @@ void ChatMessageModel::onEphemeralMessageTimerStarted(const std::shared_ptr<linp
void ChatMessageModel::onEphemeralMessageDeleted(const std::shared_ptr<linphone::ChatMessage> &message) {
emit ephemeralMessageDeleted(message);
}
void ChatMessageModel::onRetracted(const std::shared_ptr<linphone::ChatMessage> &message) {
emit retracted(message);
}
void ChatMessageModel::onContentEdited(const std::shared_ptr<linphone::ChatMessage> &message) {
emit contentEdited(message);
}

View file

@ -52,6 +52,7 @@ public:
void markAsRead();
void deleteMessageFromChatRoom(bool deletedByUser);
void retractMessageFromChatRoom();
void sendReaction(const QString &reaction);
@ -95,6 +96,8 @@ signals:
void ephemeralMessageTimerStarted(const std::shared_ptr<linphone::ChatMessage> &message);
void ephemeralMessageDeleted(const std::shared_ptr<linphone::ChatMessage> &message);
void ephemeralMessageTimeUpdated(const std::shared_ptr<linphone::ChatMessage> &message, int expireTime);
void retracted(const std::shared_ptr<linphone::ChatMessage> &message);
void contentEdited(const std::shared_ptr<linphone::ChatMessage> &message);
private:
linphone::ChatMessage::State mMessageState;
@ -130,6 +133,8 @@ private:
const std::shared_ptr<const linphone::ParticipantImdnState> &state) override;
void onEphemeralMessageTimerStarted(const std::shared_ptr<linphone::ChatMessage> &message) override;
void onEphemeralMessageDeleted(const std::shared_ptr<linphone::ChatMessage> &message) override;
void onRetracted(const std::shared_ptr<linphone::ChatMessage> &message) override;
void onContentEdited(const std::shared_ptr<linphone::ChatMessage> &message) override;
};
#endif
#endif

View file

@ -74,8 +74,12 @@ void ChatMessageContentModel::removeDownloadedFile(QString filePath) {
}
}
void ChatMessageContentModel::downloadFile(const QString &name) {
if (!mChatMessageModel) return;
bool ChatMessageContentModel::downloadFile(const QString &name, QString *error) {
if (!mChatMessageModel) {
//: Internal error : message object does not exist anymore !
if (error) *error = tr("download_error_object_doesnt_exist");
return false;
}
switch (mChatMessageModel->getState()) {
case linphone::ChatMessage::State::Delivered:
case linphone::ChatMessage::State::DeliveredToUser:
@ -83,10 +87,13 @@ void ChatMessageContentModel::downloadFile(const QString &name) {
case linphone::ChatMessage::State::FileTransferDone:
break;
case linphone::ChatMessage::State::FileTransferInProgress:
return;
return true;
default:
lWarning() << QStringLiteral("Wrong message state when requesting downloading, state=.")
<< LinphoneEnums::fromLinphone(mChatMessageModel->getState());
auto state = LinphoneEnums::fromLinphone(mChatMessageModel->getState());
lWarning() << QStringLiteral("Wrong message state when requesting downloading, state=") << state;
//: Error while trying to download content : %1
if (error) *error = tr("download_file_server_error").arg(LinphoneEnums::toString(state));
return false;
}
bool soFarSoGood;
const QString safeFilePath = Utils::getSafeFilePath(
@ -94,21 +101,32 @@ void ChatMessageContentModel::downloadFile(const QString &name) {
if (!soFarSoGood) {
lWarning() << QStringLiteral("Unable to create safe file path for: %1.").arg(name);
return;
//: Unable to create safe file path for: %1
if (error) *error = tr("download_file_error_no_safe_file_path").arg(name);
return false;
}
mContent->setFilePath(Utils::appStringToCoreString(safeFilePath));
if (!mContent->isFileTransfer()) {
lWarning() << QStringLiteral("file transfer is not available");
Utils::showInformationPopup(
//: Error
tr("popup_error_title"),
//: This file was already downloaded and is no more on the server. Your peer have to resend it if you want
//: to get it
tr("popup_download_error_message"), false);
//: This file was already downloaded and is no more on the server. Your peer have to resend it if you want
//: to get it
if (error) *error = tr("download_file_error_file_transfer_unavailable");
return false;
} else if (mContent->getName().empty()) {
lWarning() << QStringLiteral("content name is null, can't download it !");
//: Content name is null, can't download it !
if (error) *error = tr("download_file_error_null_name");
return false;
} else {
if (!mChatMessageModel->getMonitor()->downloadContent(mContent))
lDebug() << log().arg("download file : %1").arg(name);
auto downloaded = mChatMessageModel->getMonitor()->downloadContent(mContent);
if (!downloaded) {
lWarning() << QStringLiteral("Unable to download file of entry %1.").arg(name);
//: Unable to download file of entry %1
if (error) *error = tr("download_file_error_unable_to_download").arg(name);
}
return downloaded;
}
}

View file

@ -40,17 +40,21 @@ public:
std::shared_ptr<ChatMessageModel> chatMessageModel);
~ChatMessageContentModel();
QString getThumbnail() const;
void setThumbnail(const QString &data);
void setWasDownloaded(bool wasDownloaded);
void createThumbnail();
void removeDownloadedFile(QString filePath);
void downloadFile(const QString &name);
/**
* Returns true if download succeed, false otherwise
*/
bool downloadFile(const QString &name, QString *error = nullptr);
void cancelDownloadFile();
void openFile(const QString &name, bool wasDownloaded, bool showDirectory = false);
/**
* Returns true if file saved successfully, false otherwise
*/
bool saveAs(const QString &path);
const std::shared_ptr<linphone::Content> &getContent() const;
@ -70,4 +74,4 @@ private:
QSharedPointer<ConferenceInfoModel> mConferenceInfoModel;
};
#endif
#endif

View file

@ -236,8 +236,8 @@ void ConferenceModel::onParticipantDeviceMediaCapabilityChanged(
void ConferenceModel::onParticipantDeviceMediaAvailabilityChanged(
const std::shared_ptr<linphone::Conference> &conference,
const std::shared_ptr<const linphone::ParticipantDevice> &participantDevice) {
lInfo() << "onParticipantDeviceMediaAvailabilityChanged: "
<< (int)participantDevice->getStreamAvailability(linphone::StreamType::Video)
lInfo() << "onParticipantDeviceMediaAvailabilityChanged: video stream available ="
<< participantDevice->getStreamAvailability(linphone::StreamType::Video)
<< ". Device: " << participantDevice->getAddress()->asString().c_str();
emit participantDeviceMediaAvailabilityChanged(participantDevice);
}

View file

@ -114,6 +114,10 @@ void CoreModel::start() {
if (mCore->getLogCollectionUploadServerUrl().empty())
mCore->setLogCollectionUploadServerUrl(Constants::DefaultUploadLogsServer);
/// These 2 API should not be used as they manage internal gains insterad of those of the soundcard.
// Use playback/capture gain from capture graph and call only
mCore->setMicGainDb(0.0);
mCore->setPlaybackGainDb(0.0);
mIterateTimer = new QTimer(this);
mIterateTimer->setInterval(20);
connect(mIterateTimer, &QTimer::timeout, [this]() { mCore->iterate(); });
@ -171,8 +175,6 @@ void CoreModel::setPathBeforeCreation() {
std::shared_ptr<linphone::Factory> factory = linphone::Factory::get();
SET_FACTORY_PATH(Msplugins, Paths::getPackageMsPluginsDirPath());
SET_FACTORY_PATH(TopResources, Paths::getPackageTopDirPath());
SET_FACTORY_PATH(SoundResources, Paths::getPackageSoundsResourcesDirPath());
SET_FACTORY_PATH(DataResources, Paths::getPackageDataDirPath());
SET_FACTORY_PATH(Data, Paths::getAppLocalDirPath());
SET_FACTORY_PATH(Download, Paths::getDownloadDirPath());
SET_FACTORY_PATH(Config, Paths::getConfigDirPath(true));
@ -199,8 +201,13 @@ void CoreModel::setPathAfterStart() {
mCore->setUserCertificatesPath(Utils::appStringToCoreString(Paths::getUserCertificatesDirPath()));
lInfo() << "[CoreModel] Using UserCertificate path : " << QString::fromStdString(mCore->getUserCertificatesPath());
// Use application path if Linphone default is not available
if (mCore->getRootCa().empty() || !Paths::filePathExists(Utils::coreStringToAppString(mCore->getRootCa())))
mCore->setRootCa(Utils::appStringToCoreString(Paths::getRootCaFilePath()));
QString rootCaPath = Utils::coreStringToAppString(mCore->getRootCa());
QString relativeRootCa = Paths::getAppRootCaFilePath();
lDebug() << "[CoreModel] Getting rootCa paths: " << rootCaPath << " VS " << relativeRootCa;
if (!Paths::filePathExists(rootCaPath) || Paths::isSameRelativeFile(rootCaPath, relativeRootCa)) {
lInfo() << "[CoreModel] Reset rootCa path to: " << relativeRootCa;
mCore->setRootCa(Utils::appStringToCoreString(relativeRootCa));
}
lInfo() << "[CoreModel] Using RootCa path : " << QString::fromStdString(mCore->getRootCa());
}
@ -216,7 +223,10 @@ QString CoreModel::getFetchConfig(QString filePath, bool *error) {
if (!filePath.isEmpty()) filePath = "file://" + filePath;
}
if (filePath.isEmpty()) {
qWarning() << "Remote provisionning cannot be retrieved. Command have been cleaned";
qWarning() << "Remote provisioning cannot be retrieved. Command have been cleaned";
Utils::showInformationPopup(tr("info_popup_error_title"),
//: "Remote provisioning cannot be retrieved"
tr("fetching_config_failed_error_message"), false);
*error = true;
}
}
@ -365,6 +375,23 @@ void CoreModel::searchInMagicSearch(QString filter,
mMagicSearch->search(filter, sourceFlags, aggregation, maxResults);
}
void CoreModel::checkForUpdate(const std::string &applicationVersion, bool requestedByUser) {
mCheckVersionRequestedByUser = requestedByUser;
auto settingsModel = SettingsModel::getInstance();
if (settingsModel->isCheckForUpdateEnabled()) {
if (settingsModel->getVersionCheckUrl().isEmpty())
settingsModel->setVersionCheckUrl(Constants::VersionCheckReleaseUrl);
lInfo() << log().arg("Checking for update for version") << applicationVersion;
getCore()->checkForUpdate(applicationVersion);
} else {
lWarning() << log().arg("Check for update settings is not set");
}
}
bool CoreModel::isCheckVersionRequestedByUser() const {
return mCheckVersionRequestedByUser;
}
//---------------------------------------------------------------------------------------------------------------------------
void CoreModel::onAccountAdded(const std::shared_ptr<linphone::Core> &core,
@ -522,6 +549,7 @@ void CoreModel::onLogCollectionUploadProgressIndication(const std::shared_ptr<li
void CoreModel::onMessageReceived(const std::shared_ptr<linphone::Core> &core,
const std::shared_ptr<linphone::ChatRoom> &room,
const std::shared_ptr<linphone::ChatMessage> &message) {
if (SettingsModel::getInstance()->getDisableChatFeature()) return;
if (message->isOutgoing()) return;
emit unreadNotificationsChanged();
std::list<std::shared_ptr<linphone::ChatMessage>> messages;
@ -534,6 +562,7 @@ void CoreModel::onMessageReceived(const std::shared_ptr<linphone::Core> &core,
void CoreModel::onMessagesReceived(const std::shared_ptr<linphone::Core> &core,
const std::shared_ptr<linphone::ChatRoom> &room,
const std::list<std::shared_ptr<linphone::ChatMessage>> &messages) {
if (SettingsModel::getInstance()->getDisableChatFeature()) return;
std::list<std::shared_ptr<linphone::ChatMessage>> finalMessages;
for (auto &message : messages) {
if (message->isOutgoing()) continue;
@ -551,6 +580,7 @@ void CoreModel::onNewMessageReaction(const std::shared_ptr<linphone::Core> &core
const std::shared_ptr<linphone::ChatRoom> &chatRoom,
const std::shared_ptr<linphone::ChatMessage> &message,
const std::shared_ptr<const linphone::ChatMessageReaction> &reaction) {
if (SettingsModel::getInstance()->getDisableChatFeature()) return;
emit newMessageReaction(core, chatRoom, message, reaction);
}
void CoreModel::onNotifyPresenceReceivedForUriOrTel(
@ -571,6 +601,7 @@ void CoreModel::onReactionRemoved(const std::shared_ptr<linphone::Core> &core,
const std::shared_ptr<linphone::ChatRoom> &chatRoom,
const std::shared_ptr<linphone::ChatMessage> &message,
const std::shared_ptr<const linphone::Address> &address) {
if (SettingsModel::getInstance()->getDisableChatFeature()) return;
emit reactionRemoved(core, chatRoom, message, address);
}
void CoreModel::onTransferStateChanged(const std::shared_ptr<linphone::Core> &core,
@ -582,7 +613,7 @@ void CoreModel::onVersionUpdateCheckResultReceived(const std::shared_ptr<linphon
linphone::VersionUpdateCheckResult result,
const std::string &version,
const std::string &url) {
emit versionUpdateCheckResultReceived(core, result, version, url);
emit versionUpdateCheckResultReceived(core, result, version, url, mCheckVersionRequestedByUser);
}
void CoreModel::onFriendListRemoved(const std::shared_ptr<linphone::Core> &core,
@ -608,3 +639,12 @@ void CoreModel::onFriendListRemoved(const std::shared_ptr<linphone::Core> &core,
}
*/
}
void CoreModel::onAudioDevicesListUpdated(const std::shared_ptr<linphone::Core> &core) {
emit audioDevicesListUpdated(core);
}
void CoreModel::onAudioDeviceChanged(const std::shared_ptr<linphone::Core> &core,
const std::shared_ptr<linphone::AudioDevice> &device) {
emit audioDeviceChanged(core, device);
}

View file

@ -71,6 +71,9 @@ public:
LinphoneEnums::MagicSearchAggregation aggregation,
int maxResults);
void checkForUpdate(const std::string &applicationVersion, bool requestedByUser = false);
bool isCheckVersionRequestedByUser() const;
bool mEnd = false;
linphone::ConfiguringState mConfigStatus;
QString mConfigMessage;
@ -97,6 +100,7 @@ private:
QMap<QString, OIDCModel *> mOpenIdConnections;
std::shared_ptr<MagicSearchModel> mMagicSearch;
bool mStarted = false;
bool mCheckVersionRequestedByUser = false;
void setPathBeforeCreation();
void setPathsAfterCreation();
@ -202,6 +206,9 @@ private:
const std::string &url) override;
virtual void onFriendListRemoved(const std::shared_ptr<linphone::Core> &core,
const std::shared_ptr<linphone::FriendList> &friendList) override;
virtual void onAudioDevicesListUpdated(const std::shared_ptr<linphone::Core> &core) override;
virtual void onAudioDeviceChanged(const std::shared_ptr<linphone::Core> &core,
const std::shared_ptr<linphone::AudioDevice> &device) override;
signals:
void accountAdded(const std::shared_ptr<linphone::Core> &core, const std::shared_ptr<linphone::Account> &account);
@ -281,9 +288,13 @@ signals:
void versionUpdateCheckResultReceived(const std::shared_ptr<linphone::Core> &core,
linphone::VersionUpdateCheckResult result,
const std::string &version,
const std::string &url);
const std::string &url,
bool checkRequestedByUser);
void friendListRemoved(const std::shared_ptr<linphone::Core> &core,
const std::shared_ptr<linphone::FriendList> &friendList);
void audioDevicesListUpdated(const std::shared_ptr<linphone::Core> &core);
void audioDeviceChanged(const std::shared_ptr<linphone::Core> &core,
const std::shared_ptr<linphone::AudioDevice> &device);
};
#endif

View file

@ -155,5 +155,5 @@ void MagicSearchModel::updateFriendListWithFriend(const std::shared_ptr<linphone
}
qInfo() << log().arg("Adding Friend:") << linphoneFriend.get();
if (friendList) friendList->addFriend(linphoneFriend);
emit CoreModel::getInstance()->friendCreated(linphoneFriend);
emit CoreModel::getInstance() -> friendCreated(linphoneFriend);
}

View file

@ -81,6 +81,20 @@ SettingsModel::SettingsModel() {
QObject::connect(CoreModel::getInstance().get(), &CoreModel::lastCallEnded, this, [this]() {
if (mCaptureGraphListenerCount > 0) createCaptureGraph(); // Repair the capture graph
});
QObject::connect(CoreModel::getInstance().get(), &CoreModel::audioDevicesListUpdated, this,
[this](const std::shared_ptr<linphone::Core> &core) {
lInfo() << log().arg("audio device list updated");
updateCallSettings();
});
QObject::connect(
CoreModel::getInstance().get(), &CoreModel::audioDeviceChanged, this,
[this](const std::shared_ptr<linphone::Core> &core, const std::shared_ptr<linphone::AudioDevice> &device) {
lInfo() << log().arg("audio device changed");
if (device) lInfo() << "device :" << device->getDeviceName();
// emit playbackDeviceChanged(getPlaybackDevice());
// emit captureDeviceChanged(getCaptureDevice());
// emit ringerDeviceChanged(getRingerDevice());
});
}
SettingsModel::~SettingsModel() {
@ -173,8 +187,10 @@ void SettingsModel::stopCaptureGraph() {
// Force a call on the 'detect' method of all audio filters, updating new or removed devices
void SettingsModel::accessCallSettings() {
// Audio
mustBeInLinphoneThread(log().arg(Q_FUNC_INFO));
startCaptureGraph();
// Audio
CoreModel::getInstance()->getCore()->reloadSoundDevices();
emit captureDevicesChanged(getCaptureDevices());
emit playbackDevicesChanged(getPlaybackDevices());
@ -185,12 +201,28 @@ void SettingsModel::accessCallSettings() {
emit playbackGainChanged(getPlaybackGain());
emit captureGainChanged(getCaptureGain());
startCaptureGraph();
// Video
CoreModel::getInstance()->getCore()->reloadVideoDevices();
emit videoDevicesChanged(getVideoDevices());
}
void SettingsModel::updateCallSettings() {
mustBeInLinphoneThread(log().arg(Q_FUNC_INFO));
// Audio
emit captureDevicesChanged(getCaptureDevices());
emit playbackDevicesChanged(getPlaybackDevices());
emit playbackDeviceChanged(getPlaybackDevice());
emit ringerDevicesChanged(getRingerDevices());
emit ringerDeviceChanged(getRingerDevice());
emit captureDeviceChanged(getCaptureDevice());
emit playbackGainChanged(getPlaybackGain());
emit captureGainChanged(getCaptureGain());
// Video
emit videoDevicesChanged(getVideoDevices());
}
void SettingsModel::closeCallSettings() {
mustBeInLinphoneThread(log().arg(Q_FUNC_INFO));
stopCaptureGraph();
@ -205,13 +237,12 @@ bool SettingsModel::getCaptureGraphRunning() {
float SettingsModel::getMicVolume() {
mustBeInLinphoneThread(log().arg(Q_FUNC_INFO));
float v = 0.0;
if (mSimpleCaptureGraph && mSimpleCaptureGraph->isRunning()) {
v = mSimpleCaptureGraph->getCaptureVolume();
} else {
auto call = CoreModel::getInstance()->getCore()->getCurrentCall();
if (call) {
v = MediastreamerUtils::computeVu(call->getRecordVolume());
v = call->getRecordVolume();
}
}
@ -221,33 +252,49 @@ float SettingsModel::getMicVolume() {
float SettingsModel::getPlaybackGain() const {
mustBeInLinphoneThread(log().arg(Q_FUNC_INFO));
float dbGain = CoreModel::getInstance()->getCore()->getPlaybackGainDb();
return MediastreamerUtils::dbToLinear(dbGain);
if (mSimpleCaptureGraph && mSimpleCaptureGraph->isRunning()) {
return mSimpleCaptureGraph->getPlaybackGain();
} else {
auto call = CoreModel::getInstance()->getCore()->getCurrentCall();
if (call) return call->getSpeakerVolumeGain();
else return 0.0;
}
}
void SettingsModel::setPlaybackGain(float gain) {
mustBeInLinphoneThread(log().arg(Q_FUNC_INFO));
float oldGain = getPlaybackGain();
CoreModel::getInstance()->getCore()->setPlaybackGainDb(MediastreamerUtils::linearToDb(gain));
if (mSimpleCaptureGraph && mSimpleCaptureGraph->isRunning()) {
mSimpleCaptureGraph->setPlaybackGain(gain);
}
auto currentCall = CoreModel::getInstance()->getCore()->getCurrentCall();
if (currentCall) {
currentCall->setSpeakerVolumeGain(gain);
}
if ((int)(oldGain * 1000) != (int)(gain * 1000)) emit playbackGainChanged(gain);
}
float SettingsModel::getCaptureGain() const {
mustBeInLinphoneThread(getClassName());
float dbGain = CoreModel::getInstance()->getCore()->getMicGainDb();
return MediastreamerUtils::dbToLinear(dbGain);
if (mSimpleCaptureGraph && mSimpleCaptureGraph->isRunning()) {
return mSimpleCaptureGraph->getCaptureGain();
} else {
auto call = CoreModel::getInstance()->getCore()->getCurrentCall();
if (call) return call->getMicrophoneVolumeGain();
else return 0.0;
}
}
void SettingsModel::setCaptureGain(float gain) {
mustBeInLinphoneThread(log().arg(Q_FUNC_INFO));
float oldGain = getCaptureGain();
CoreModel::getInstance()->getCore()->setMicGainDb(MediastreamerUtils::linearToDb(gain));
if (mSimpleCaptureGraph && mSimpleCaptureGraph->isRunning()) {
mSimpleCaptureGraph->setCaptureGain(gain);
}
auto currentCall = CoreModel::getInstance()->getCore()->getCurrentCall();
if (currentCall) {
currentCall->setMicrophoneVolumeGain(gain);
}
if ((int)(oldGain * 1000) != (int)(gain * 1000)) emit captureGainChanged(gain);
}
@ -395,14 +442,20 @@ void SettingsModel::setPlaybackDevice(const QVariantMap &device) {
QVariantMap SettingsModel::getRingerDevice() const {
mustBeInLinphoneThread(log().arg(Q_FUNC_INFO));
auto id = Utils::coreStringToAppString(CoreModel::getInstance()->getCore()->getRingerDevice());
auto audioDevice = ToolModel::findAudioDevice(id, linphone::AudioDevice::Capabilities::CapabilityPlay);
return ToolModel::createVariant(audioDevice);
for (const auto &device : CoreModel::getInstance()->getCore()->getExtendedAudioDevices()) {
if (device->getUseForRinging()) return ToolModel::createVariant(device);
}
return ToolModel::createVariant(nullptr);
}
void SettingsModel::setRingerDevice(QVariantMap device) {
mustBeInLinphoneThread(log().arg(Q_FUNC_INFO));
CoreModel::getInstance()->getCore()->setRingerDevice(Utils::appStringToCoreString(device["id"].toString()));
for (const auto &ldevice : CoreModel::getInstance()->getCore()->getExtendedAudioDevices()) {
auto id = Utils::appStringToCoreString(device["id"].toString());
if (ldevice->getId() == id) {
ldevice->setUseForRinging(true);
} else ldevice->setUseForRinging(false);
}
emit ringerDeviceChanged(device);
}
@ -784,26 +837,13 @@ QString SettingsModel::getDefaultDomain() const {
mConfig->getString(SettingsModel::AppSection, "default_domain", "sip.linphone.org"));
}
void SettingsModel::enableCallForward(QString destination) {
// TODO implement business logic to activate call forward to destination on PBX via external API (contains voicemail
// or a destination).
mConfig->setString(UiSection, "call_forward_to_address", Utils::appStringToCoreString(destination));
emit callForwardToAddressChanged(getCallForwardToAddress());
}
void SettingsModel::disableCallForward() {
// TODO implement business logic to de-activate call forward on PBX via external API
mConfig->setString(UiSection, "call_forward_to_address", "");
}
QString SettingsModel::getCallForwardToAddress() const {
mustBeInLinphoneThread(log().arg(Q_FUNC_INFO));
return Utils::coreStringToAppString(mConfig->getString(UiSection, "call_forward_to_address", ""));
}
void SettingsModel::setCallForwardToAddress(const QString &data) {
if (data == "") disableCallForward();
else enableCallForward(data);
mConfig->setString(UiSection, "call_forward_to_address", Utils::appStringToCoreString(data)); // TODO implement BL
emit(callForwardToAddressChanged(data));
}
@ -855,6 +895,40 @@ bool SettingsModel::getDisableMeetingsFeature() const {
return !!mConfig->getInt(UiSection, "disable_meetings_feature", 0);
}
bool SettingsModel::isCheckForUpdateAvailable() const {
#ifdef ENABLE_UPDATE_CHECK
return true;
#else
return false;
#endif
}
bool SettingsModel::isCheckForUpdateEnabled() const {
return !!mConfig->getInt(UiSection, "check_for_update_enabled", isCheckForUpdateAvailable());
}
void SettingsModel::setCheckForUpdateEnabled(bool enable) {
mConfig->setInt(UiSection, "check_for_update_enabled", enable);
emit checkForUpdateEnabledChanged();
}
QString SettingsModel::getVersionCheckUrl() {
auto url = mConfig->getString("misc", "version_check_url_root", "");
if (url == "") {
url = Constants::VersionCheckReleaseUrl;
if (url != "") mConfig->setString("misc", "version_check_url_root", url);
}
return Utils::coreStringToAppString(url);
}
void SettingsModel::setVersionCheckUrl(const QString &url) {
if (url != getVersionCheckUrl()) {
// Do not trim the url before because we want to update GUI from potential auto fix.
mConfig->setString("misc", "version_check_url_root", Utils::appStringToCoreString(url.trimmed()));
emit versionCheckUrlChanged();
}
}
void SettingsModel::setChatNotificationSoundPath(const QString &path) {
QString cleanedPath = QDir::cleanPath(path);
mConfig->setString(UiSection, "chat_sound_notification_file", Utils::appStringToCoreString(cleanedPath));
@ -1052,13 +1126,6 @@ DEFINE_GETSET_CONFIG(SettingsModel,
DisableCommandLine,
"disable_command_line",
false)
DEFINE_GETSET_CONFIG(SettingsModel,
bool,
Bool,
disableCallForward,
DisableCallForward,
"disable_call_forward",
true)
DEFINE_GETSET_CONFIG_STRING(SettingsModel,
themeMainColor,
ThemeMainColor,

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