Compare commits

...

No commits in common. "5.0.3" and "master" have entirely different histories.

5163 changed files with 239631 additions and 170921 deletions

31
.clang-format Normal file
View file

@ -0,0 +1,31 @@
# Copyright (c) 2010-2023 Belledonne Communications SARL.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
---
Language: Cpp
BasedOnStyle: LLVM
AccessModifierOffset: -4
AllowShortFunctionsOnASingleLine: None
AllowShortIfStatementsOnASingleLine: AllIfsAndElse
AlwaysBreakTemplateDeclarations: Yes
BinPackParameters: false
ColumnLimit: 120
PointerAlignment: Right
IndentCaseLabels: true
IndentWidth: 4
Standard: c++14
TabWidth: 4
UseTab: ForIndentation
...

View file

@ -1,136 +0,0 @@
---
name: Bug report
description: File a bug/issue
title: "[Bug]: "
labels: ["bug"]
body:
- type: markdown
attributes:
value: '# Reminder'
- type: markdown
attributes:
value: |
The responses are provided by the **community** and, on a **best effort** basis, by some Belledonne Communications SARL engineers working on Linphone and its related projects.
The community means any people all around the world simply willing to participate to the discussions.
Belledonne Communications SARL **disclaims any WARRANTY** that the content posted on github issues or mailing lists is technically correct.
Responses from Belledonne Communications SARL engineers shall be considered as individual contributions and shall not be seen as Belledonne Communications's official point of view or commitment.
The Github issue tracker must be seen as a place for **collaboration**. Issues submitted should be of general interest, in the goal of improving the software. Consider that a **well documented** issue (with precise reproduction procedure, logs, stack trace if relevant, possibly a corrective patch) has a higher chance to receive interest and feedback from community members and Belledonne Communications' engineers.
__Issues poorly documented, with no facts, or asking for debugging assistance for a custom app using Linphone's libraries, or for a modified version of Linphone are unlikely to receive any kind of response.__
People using Linphone or its related projects within the scope of their company job are invited to contact [Belledonne Communications](https://linphone.org/contact#content-bottom3) in order to obtain commercial support.
- type: markdown
attributes:
value: |
# Well ordered issues are treated issues
**If the issue is about the SDK (build, issue, etc...) open the ticket in the [Linphone-SDK](https://github.com/BelledonneCommunications/linphone-sdk) repository.**
- type: markdown
attributes:
value: |
# Useful links
[Linphone.org](https://linphone.org)
[Linphone commercial contact](https://linphone.org/contact#content-bottom3)
Linphone Vulnerability/Security contact: vulnerabilities@linphone.org
[Contributor agreement (to sign and to return to sales@belledonne-communications.com for a pull request)](https://linphone.org/sites/default/files/bc-contributor-agreement_0.pdf)
- type: textarea
attributes:
label: |
Context
description: |
- For which purpose do you use the project ?
- With which software/hardware it is integrated ?
- Did you use sip.linphone.org or a different SIP service (in this case specify which one and which version) ?
placeholder: |
I use the linphone-sdk in the Linphone-desktop Linux version with sip.linphone.org for my company. I want to do a simple call between an Android phone and a Linux client.
validations:
required: true
- type: textarea
attributes:
label: General information
description: |
Complete it multiple time if there are multiple devices involved.
Please note that the issue has more chances to be read if you report a bug seen in the latest version of the app.
Ex :
- Device: [e.g. ASUS Zenbook Pro UX501]
- OS: [e.g. Manjaro 21.1.6 Pahvo KDE, Windows 10 - 2004, MacOs 10.5]
- Version of the App [e.g. 4.3.2]
- Version of the SDK [e.g 5.0.49]
value: |
- Device:
- OS:
- Version of the App:
- Version of the SDK:
validations:
required: true
- type: textarea
attributes:
label: Expected behaviour
description: "A clear and concise description of what you expected to happen."
value: |
I wanted to do a simple call with the Linux client calling the Android phone. However, the desktop app crashed.
validations:
required: true
- type: textarea
attributes:
label: To Reproduce
description: "Steps to reproduce the behavior:"
placeholder: |
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
validations:
required: true
- type: textarea
attributes:
label: 'Additional context'
placeholder: Add any other context about the problem here.
- type: markdown
attributes:
value: |
# Logs
## SDK logs
Launch the application with --verbose parameter to get full logs and send it with your request.
Enable debug logs in advanced section of the settings, restart the app, reproduce the issue and then go to the advanced section of settings page, click on "Send logs" and copy/paste the link here (or send them in attachments).
- type: input
attributes:
label: 'SDK logs URL'
- type: markdown
attributes:
value: |
## SDK crash logs
In case of a crash of the app related to the SDK, please also provide the backtrace of the crash in attachments using adb logcat (Android) or the device console (iOS).
For desktop versions, you can get the backtrace from a core dump.
- type: markdown
attributes:
value: |
# Screenshots
Please add screenshots in attachments to help us to understand your problem.
- type: markdown
attributes:
value: |
# Pcap file
If this is a network issue, join a pcap file of your attempt in attachments (done with Wireshark or TCPDump, for example)
- type: markdown
attributes:
value: |
# Contributing
You can follow this [part](https://gitlab.linphone.org/BC/public/linphone-desktop#contributing) to contribute.

21
.gitignore vendored
View file

@ -4,7 +4,6 @@
.*
\#*\#
.#.*
# Project configuration --------------------------------------------------------
*.pro.user
@ -42,3 +41,23 @@ rpm-*/
vgcore.*
linphone.spec
# Exceptions -------------------------------------------------------------------
!.clang-format
!.gitignore
!.gitlab-ci*
!.gitmodules
# VSCode
linphone60*.log
linphone61*.log
linphone62*.log
# QT Creator
*.cflags
*.config
*.creator
*.cxxflags
*.files
*.includes

View file

@ -1,124 +0,0 @@
.install-qt-webview: &install-qt-webview
before_script:
- sudo pacman -Sy qt5-webview --noprogressbar --noconfirm
job-archlinux-latest-ninja-clang:
tags: [ "docker-archlinux-latest" ]
image: gitlab.linphone.org:4567/bc/public/linphone-sdk/bc-dev-archlinux:$ARCHLINUX_IMAGE_VERSION
except:
refs:
- schedules
variables:
CMAKE_GENERATOR: Ninja
CMAKE_OPTIONS: -DENABLE_PQCRYPTO=ON
CC: clang
CXX: clang++
extends: .job-linux-desktop
<<: *install-qt-webview
#################################################
# Nightly
#################################################
job-archlinux-latest-makefile-gcc:
tags: [ "docker-archlinux-latest" ]
image: gitlab.linphone.org:4567/bc/public/linphone-sdk/bc-dev-archlinux:$ARCHLINUX_IMAGE_VERSION
only:
variables:
- $NIGHTLY_MASTER
variables:
CMAKE_GENERATOR: Unix Makefiles
CMAKE_OPTIONS: -DENABLE_PQCRYPTO=ON
CC: gcc
CXX: g++
extends: .job-linux-desktop
<<: *install-qt-webview
job-archlinux-latest-makefile-clang:
tags: [ "docker-archlinux-latest" ]
image: gitlab.linphone.org:4567/bc/public/linphone-sdk/bc-dev-archlinux:$ARCHLINUX_IMAGE_VERSION
only:
variables:
- $NIGHTLY_MASTER
variables:
CMAKE_GENERATOR: Unix Makefiles
CMAKE_OPTIONS: -DENABLE_PQCRYPTO=ON
CC: clang
CXX: clang++
extends: .job-linux-desktop
<<: *install-qt-webview
job-archlinux-latest-ninja-gcc:
tags: [ "docker-archlinux-latest" ]
image: gitlab.linphone.org:4567/bc/public/linphone-sdk/bc-dev-archlinux:$ARCHLINUX_IMAGE_VERSION
only:
variables:
- $NIGHTLY_MASTER
variables:
CMAKE_GENERATOR: Ninja
CMAKE_OPTIONS: -DENABLE_PQCRYPTO=ON
CC: gcc
CXX: g++
extends: .job-linux-desktop
<<: *install-qt-webview
job-archlinux-latest-ninja-gcc-novideo:
tags: [ "docker-archlinux-latest" ]
only:
variables:
- $NIGHTLY_MASTER
variables:
CMAKE_OPTIONS: -DENABLE_VIDEO=NO
extends: job-archlinux-latest-ninja-gcc
<<: *install-qt-webview
job-archlinux-latest-ninja-clang-novideo:
tags: [ "docker-archlinux-latest" ]
image: gitlab.linphone.org:4567/bc/public/linphone-sdk/bc-dev-archlinux:$ARCHLINUX_IMAGE_VERSION
only:
variables:
- $NIGHTLY_MASTER
variables:
CMAKE_OPTIONS: -DENABLE_VIDEO=NO -DENABLE_PQCRYPTO=OFF
CMAKE_GENERATOR: Ninja
CC: clang
CXX: clang++
extends: .job-linux-desktop
<<: *install-qt-webview
#################################################
# Package - Nightly
#################################################
#job-archlinux-latest-makefile-clang-package:
# stage: package
# tags: [ "docker-archlinux-latest" ]
# only:
# variables:
# - $NIGHTLY_MASTER
# - $DEPLOY_LINUX
# variables:
# CMAKE_OPTIONS: -DENABLE_APP_PACKAGING=YES -DENABLE_G729=ON
# extends: job-archlinux-latest-makefile-clang
# artifacts:
# paths:
# - build/OUTPUT/Packages/Linphone*.AppImage
# expire_in: 1 week
#
#################################################
# Deploy - Nightly
#################################################
#job-archlinux-latest-makefile-clang-deploy:
# stage: deploy
# tags: [ "docker-archlinux-latest" ]
# dependencies:
# - job-archlinux-latest-makefile-clang-package
# only:
# variables:
# - $NIGHTLY_MASTER
# - $DEPLOY_LINUX
# script:
# - scp build/OUTPUT/Packages/Linphone*.AppImage $DEPLOY_SERVER:$APPIMAGE_UPLOAD_DIRECTORY/

View file

@ -1,148 +0,0 @@
#job-centos7-makefile-gcc:
# tags: [ "docker" ]
# image: gitlab.linphone.org:4567/bc/public/linphone-sdk/bc-dev-centos:7
# only:
# variables:
# - $NIGHTLY_MASTER
# variables:
# CMAKE_GENERATOR: Unix Makefiles
# CC: gcc
# CXX: g++
# extends: .job-linux-desktop
#job-centos7-makefile-clang:
# tags: [ "docker-centos7" ]
# image: gitlab.linphone.org:4567/bc/public/linphone-desktop/bc-dev-centos7-fuse-qt-wget:$CENTOS_7_QT_IMAGE_VERSION
# only:
# variables:
# - $NIGHTLY_MASTER
# variables:
# CMAKE_GENERATOR: Unix Makefiles
# CC: clang
# CXX: clang++
# extends: .job-linux-desktop
#################################################
# Ninja
#################################################
job-centos7-ninja-gcc:
tags: [ "docker" ]
image: gitlab.linphone.org:4567/bc/public/linphone-desktop/bc-dev-centos7-fuse-qt-wget:$CENTOS_7_QT_IMAGE_VERSION
except:
refs:
- schedules
variables:
CMAKE_GENERATOR: Ninja
CMAKE_OPTIONS: -DENABLE_PQCRYPTO=ON
CC: gcc
CXX: g++
extends: .job-linux-desktop
before_script:
- source /opt/rh/devtoolset-8/enable
#job-centos7-ninja-clang:
# tags: [ "docker-centos7" ]
# only:
# variables:
# - $NIGHTLY_MASTER
# image: gitlab.linphone.org:4567/bc/public/linphone-sdk/bc-dev-centos:7
# variables:
# CMAKE_GENERATOR: Ninja
# CC: clang
# CXX: clang++
# CMAKE_OPTIONS: -DENABLE_LIME=ON
# extends: .job-linux-desktop
#################################################
# Package - Nightly
#################################################
job-centos7-ninja-gcc-package:
stage: package
tags: [ "docker-test-centos7-liblinphone-nuc" ]
image: gitlab.linphone.org:4567/bc/public/linphone-desktop/bc-dev-centos7-fuse-qt-wget:$CENTOS_7_QT_IMAGE_VERSION
dependencies: []
only:
variables:
- $DEPLOY_LINUX_CENTOS7
variables:
CMAKE_OPTIONS: -DENABLE_APP_PACKAGING=YES -DLINPHONE_BUILDER_SIGNING_IDENTITY=$GPG_SIGNING_KEYID -DENABLE_G729=ON -DENABLE_PQCRYPTO=ON
CMAKE_GENERATOR: Ninja
CC: gcc
CXX: g++
APPIMAGETOOL_SIGN_PASSPHRASE: $GPG_SIGNING_PASS
extends: .job-linux-desktop
before_script:
- source /opt/rh/devtoolset-8/enable
script:
- echo "$GPG_SIGNING_PUB" > file.key && sed -i 's/\r /\n/g' file.key && chmod 600 file.key
- gpg --import file.key
- echo "$GPG_SIGNING_KEY" > file.key && sed -i 's/\r /\n/g' file.key && chmod 600 file.key
- gpg --import file.key
- cmake --version
- export CC=$CC
- export CXX=$CXX
- mkdir -p build/OUTPUT
- echo $CI_BUILD_TYPE
- echo $CMAKE_GENERATOR
- echo $DEFAULT_LINUX_CMAKE_OPTIONS
- echo $CMAKE_SANITIZER_OPTIONS
- cd build
- cmake .. -G "$CMAKE_GENERATOR" -DCMAKE_VERBOSE_MAKEFILE=ON -DLINPHONESDK_PLATFORM=Desktop -DCMAKE_BUILD_TYPE=$CI_BUILD_TYPE $DEFAULT_LINUX_CMAKE_OPTIONS $CMAKE_OPTIONS $SCHEDULE_CMAKE_OPTIONS $CMAKE_SANITIZER_OPTIONS
- cmake --build . --target install --config $CI_BUILD_TYPE $LBC_NODEBUG_OPTIONS
artifacts:
paths:
- build/OUTPUT/Packages/*.AppImage
when: always
expire_in: 1 week
#################################################
# Deploy - Nightly
#################################################
job-centos7-ninja-gcc-deploy:
stage: deploy
tags: [ "deploy" ]
dependencies:
- job-centos7-ninja-gcc-package
only:
variables:
- $DEPLOY_LINUX_CENTOS7
script:
- rsync -rlv --ignore-existing build/OUTPUT/Packages/*.AppImage $DEPLOY_SERVER:$APPIMAGE_UPLOAD_DIRECTORY/
#################################################
# Debug
#################################################
job-centos7-debug-nuc:
stage: deploy
tags: [ "docker-test-centos7-liblinphone-nuc" ]
#image: gitlab.linphone.org:4567/bc/public/linphone-sdk/bc-dev-centos:7
image: gitlab.linphone.org:4567/bc/public/linphone-desktop/bc-dev-centos7-fuse-qt-wget:$CENTOS_7_QT_IMAGE_VERSION
dependencies: []
only:
variables:
- $DEBUG_CENTOS7_NUC
script:
- sleep 10m
job-centos7-debug:
stage: deploy
tags: [ "docker" ]
#image: gitlab.linphone.org:4567/bc/public/linphone-sdk/bc-dev-centos:7
image: gitlab.linphone.org:4567/bc/public/linphone-desktop/bc-dev-centos7-fuse-qt-wget:$CENTOS_7_QT_IMAGE_VERSION
dependencies: []
only:
variables:
- $DEBUG_CENTOS7
script:
- sleep 10m

View file

@ -1,133 +0,0 @@
job-debian10-ninja-gcc:
tags: [ "docker-debian10" ]
image: gitlab.linphone.org:4567/bc/public/linphone-sdk/bc-dev-debian10:$DEBIAN_10_IMAGE_VERSION
except:
refs:
- schedules
variables:
CMAKE_GENERATOR: Ninja
CMAKE_OPTIONS: -DENABLE_PQCRYPTO=ON
CC: gcc
CXX: g++
extends: .job-linux-desktop
#################################################
# Nightly
#################################################
job-debian10-makefile-gcc:
tags: [ "docker-debian10" ]
image: gitlab.linphone.org:4567/bc/public/linphone-sdk/bc-dev-debian10:$DEBIAN_10_IMAGE_VERSION
only:
variables:
- $NIGHTLY_MASTER
variables:
CMAKE_GENERATOR: Unix Makefiles
CMAKE_OPTIONS: -DENABLE_PQCRYPTO=ON
CC: gcc
CXX: g++
ADDITIONAL_BUILD_OPTIONS: -j$MAKEFILE_JOBS
extends: .job-linux-desktop
job-debian10-ninja-gcc-novideo:
tags: [ "docker-debian10" ]
image: gitlab.linphone.org:4567/bc/public/linphone-sdk/bc-dev-debian10:$DEBIAN_10_IMAGE_VERSION
only:
variables:
- $NIGHTLY_MASTER
variables:
CMAKE_OPTIONS: -DENABLE_VIDEO=NO -DENABLE_PQCRYPTO=OFF
CMAKE_GENERATOR: Ninja
CC: gcc
CXX: g++
extends: .job-linux-desktop
job-debian10-ninja-gcc-smallsdk:
tags: [ "docker-debian10" ]
image: gitlab.linphone.org:4567/bc/public/linphone-sdk/bc-dev-debian10:$DEBIAN_10_IMAGE_VERSION
only:
variables:
- $NIGHTLY_MASTER
variables:
CMAKE_OPTIONS: -DENABLE_VIDEO=NO -DENABLE_ADVANCED_IM=NO -DENABLE_DB_STORAGE=NO -DENABLE_PQCRYPTO=OFF
CMAKE_GENERATOR: Ninja
CC: gcc
CXX: g++
extends: .job-linux-desktop
job-debian10-ninja-clang:
tags: [ "docker-debian10" ]
only:
variables:
- $NIGHTLY_MASTER
image: gitlab.linphone.org:4567/bc/public/linphone-sdk/bc-dev-debian10:$DEBIAN_10_IMAGE_VERSION
variables:
CMAKE_GENERATOR: Ninja
CMAKE_OPTIONS: -DENABLE_PQCRYPTO=ON
CC: clang
CXX: clang++
extends: .job-linux-desktop
job-debian10-ninja-clang-novideo:
only:
variables:
- $NIGHTLY_MASTER
variables:
CMAKE_OPTIONS: -DENABLE_VIDEO=NO
extends: job-debian10-ninja-clang
job-debian10-makefile-clang:
tags: [ "docker-debian10" ]
image: gitlab.linphone.org:4567/bc/public/linphone-sdk/bc-dev-debian10:$DEBIAN_10_IMAGE_VERSION
only:
variables:
- $NIGHTLY_MASTER
- $DEPLOY_LINUX
variables:
CMAKE_GENERATOR: Unix Makefiles
CMAKE_OPTIONS: -DENABLE_PQCRYPTO=ON
CC: clang
CXX: clang++
ADDITIONAL_BUILD_OPTIONS: -j$MAKEFILE_JOBS
extends: .job-linux-desktop
#################################################
# Package - Nightly
#################################################
job-debian10-makefile-clang-package:
stage: package
tags: [ "docker-debian10" ]
dependencies: []
only:
variables:
- $NIGHTLY_MASTER
- $DEPLOY_LINUX
variables:
CMAKE_OPTIONS: -DENABLE_APP_PACKAGING=YES -DENABLE_G729=ON -DENABLE_PQCRYPTO=ON
extends: job-debian10-makefile-clang
artifacts:
paths:
- build/OUTPUT/Packages/Linphone*.AppImage
expire_in: 1 week
#################################################
# Deploy - Nightly
#################################################
job-debian10-makefile-clang-deploy:
stage: deploy
tags: [ "docker-debian10" ]
dependencies:
- job-debian10-makefile-clang-package
only:
variables:
- $NIGHTLY_MASTER
- $DEPLOY_LINUX
script:
- scp build/OUTPUT/Packages/Linphone*.AppImage $DEPLOY_SERVER:$APPIMAGE_UPLOAD_DIRECTORY/

View file

@ -1,69 +0,0 @@
#################################################
# Makefile
#################################################
job-debian8-makefile-gcc:
tags: [ "docker-debian8" ]
image: gitlab.linphone.org:4567/bc/public/linphone-sdk/bc-dev-debian:8
except:
refs:
- schedules
variables:
CMAKE_GENERATOR: Unix Makefiles
CMAKE_OPTIONS: -DENABLE_PQCRYPTO=ON
CC: gcc
CXX: g++
ADDITIONAL_BUILD_OPTIONS: -j$MAKEFILE_JOBS
LBC_NODEBUG_OPTIONS: -- -j$MAKEFILE_JOBS
extends: .job-linux-desktop
job-debian8-makefile-clang:
tags: [ "docker-debian8" ]
image: gitlab.linphone.org:4567/bc/public/linphone-sdk/bc-dev-debian:8
only:
variables:
- $NIGHTLY_MASTER
variables:
CMAKE_GENERATOR: Unix Makefiles
CMAKE_OPTIONS: -DENABLE_PQCRYPTO=ON
CC: clang
CXX: clang++
ADDITIONAL_BUILD_OPTIONS: -j$MAKEFILE_JOBS
LBC_NODEBUG_OPTIONS: -- -j$MAKEFILE_JOBS
extends: .job-linux-desktop
#################################################
# Package - Nightly
#################################################
#job-debian8-makefile-clang-package:
# stage: package
# tags: [ "docker-debian8" ]
# only:
# variables:
# - $NIGHTLY_MASTER
# - $DEPLOY_LINUX
# variables:
# CMAKE_OPTIONS: -DENABLE_APP_PACKAGING=YES -DENABLE_G729=ON
# extends: job-debian8-makefile-clang
# artifacts:
# paths:
# - build/OUTPUT/Packages/Linphone*.AppImage
# expire_in: 1 week
#################################################
# Deploy - Nightly
#################################################
#job-debian8-makefile-clang-deploy:
# stage: deploy
# tags: [ "docker-debian8" ]
# dependencies:
# - job-debian8-makefile-clang-package
# only:
# variables:
# - $NIGHTLY_MASTER
# - $DEPLOY_LINUX
# script:
# - scp build/OUTPUT/Packages/Linphone*.AppImage $DEPLOY_SERVER:$APPIMAGE_UPLOAD_DIRECTORY/

View file

@ -1,196 +0,0 @@
job-debian9-ninja-gcc:
tags: [ "docker-debian9" ]
image: gitlab.linphone.org:4567/bc/public/linphone-desktop/bc-dev-debian9-qt-fuse-wget-gpg2:$DEBIAN_9_QT_IMAGE_VERSION
except:
refs:
- schedules
variables:
CMAKE_GENERATOR: Ninja
CMAKE_OPTIONS: -DENABLE_PQCRYPTO=ON
CC: gcc
CXX: g++
extends: .job-linux-desktop
job-debian9-ninja-gcc-smallsdk:
except:
refs:
- schedules
variables:
CMAKE_OPTIONS: -DENABLE_VIDEO=NO -DENABLE_ADVANCED_IM=NO -DENABLE_DB_STORAGE=NO -DENABLE_PQCRYPTO=OFF
extends: job-debian9-ninja-gcc
#################################################
# Nightly
#################################################
job-debian9-makefile-gcc:
tags: [ "docker-debian9" ]
image: gitlab.linphone.org:4567/bc/public/linphone-desktop/bc-dev-debian9-qt-fuse-wget-gpg2:$DEBIAN_9_QT_IMAGE_VERSION
only:
variables:
- $NIGHTLY_MASTER
variables:
CMAKE_GENERATOR: Unix Makefiles
CMAKE_OPTIONS: -DENABLE_PQCRYPTO=ON
CC: gcc
CXX: g++
ADDITIONAL_BUILD_OPTIONS: -j$MAKEFILE_JOBS
extends: .job-linux-desktop
job-debian9-ninja-gcc-novideo:
only:
variables:
- $NIGHTLY_MASTER
variables:
CMAKE_OPTIONS: -DENABLE_VIDEO=NO -DENABLE_PQCRYPTO=OFF
extends: job-debian9-ninja-gcc
job-debian9-ninja-clang:
tags: [ "docker-debian9" ]
only:
variables:
- $NIGHTLY_MASTER
image: gitlab.linphone.org:4567/bc/public/linphone-desktop/bc-dev-debian9-qt-fuse-wget-gpg2:$DEBIAN_9_QT_IMAGE_VERSION
variables:
CMAKE_OPTIONS: -DENABLE_DOC=ON -DENABLE_G729=ON -DENABLE_PQCRYPTO=ON
CMAKE_GENERATOR: Ninja
CC: clang
CXX: clang++
extends: .job-linux-desktop
job-debian9-ninja-clang-novideo:
only:
variables:
- $NIGHTLY_MASTER
variables:
CMAKE_OPTIONS: -DENABLE_VIDEO=NO -DENABLE_PQCRYPTO=OFF
extends: job-debian9-ninja-clang
job-debian9-makefile-clang:
tags: [ "docker-debian9" ]
image: gitlab.linphone.org:4567/bc/public/linphone-desktop/bc-dev-debian9-qt-fuse-wget-gpg2:$DEBIAN_9_QT_IMAGE_VERSION
only:
variables:
- $NIGHTLY_MASTER
- $DEPLOY_PLUGINS
variables:
CMAKE_OPTIONS: -DLINPHONE_BUILDER_SIGNING_IDENTITY=$GPG_SIGNING_KEYID -DENABLE_G729=ON -DENABLE_PQCRYPTO=ON
CMAKE_GENERATOR: Unix Makefiles
CC: clang
CXX: clang++
ADDITIONAL_BUILD_OPTIONS: -j$MAKEFILE_JOBS
APPIMAGETOOL_SIGN_PASSPHRASE: $GPG_SIGNING_PASS
script:
- echo "$GPG_SIGNING_PUB" > file.key && sed -i 's/\r /\n/g' file.key && chmod 600 file.key
- gpg --import file.key
- rm -f file.key
- echo "$GPG_SIGNING_KEY" > file.key && sed -i 's/\r /\n/g' file.key && chmod 600 file.key
- base64 -w 0 file.key | base64 -d | gpg --import --no-tty --batch --yes
- rm -f file.key
- cmake --version
- export CC=$CC
- export CXX=$CXX
- mkdir -p build/OUTPUT
- echo $CI_BUILD_TYPE
- echo $CMAKE_GENERATOR
- echo $DEFAULT_LINUX_CMAKE_OPTIONS
- echo $CMAKE_SANITIZER_OPTIONS
- cd build
- cmake .. -G "$CMAKE_GENERATOR" -DCMAKE_VERBOSE_MAKEFILE=ON -DLINPHONESDK_PLATFORM=Desktop -DCMAKE_BUILD_TYPE=$CI_BUILD_TYPE $DEFAULT_LINUX_CMAKE_OPTIONS $CMAKE_OPTIONS $SCHEDULE_CMAKE_OPTIONS $CMAKE_SANITIZER_OPTIONS
- cmake --build . --target install --config $CI_BUILD_TYPE $LBC_NODEBUG_OPTIONS
extends: .job-linux-desktop
#################################################
# Package - Nightly
#################################################
job-debian9-makefile-clang-package:
stage: package
tags: [ "docker-test-liblinphone" ]
image: gitlab.linphone.org:4567/bc/public/linphone-desktop/bc-dev-debian9-qt-fuse-wget-gpg2:$DEBIAN_9_QT_IMAGE_VERSION
dependencies: []
only:
variables:
- $NIGHTLY_MASTER
- $PACKAGE_LINUX
- $DEPLOY_LINUX
variables:
CMAKE_OPTIONS: -DENABLE_APP_PACKAGING=YES -DLINPHONE_BUILDER_SIGNING_IDENTITY=$GPG_SIGNING_KEYID -DENABLE_G729=ON -DLINPHONE_SDK_MAKE_RELEASE_FILE_URL=$MAKE_RELEASE_FILE_URL/$LINUX_PLATFORM/$APP_FOLDER -DENABLE_PQCRYPTO=ON
CMAKE_GENERATOR: Unix Makefiles
CC: clang
CXX: clang++
APPIMAGETOOL_SIGN_PASSPHRASE: $GPG_SIGNING_PASS
extends: .job-linux-desktop
script:
- echo "$GPG_SIGNING_PUB" > file.key && sed -i 's/\r /\n/g' file.key && chmod 600 file.key
- gpg --import file.key
- rm -f file.key
- echo "$GPG_SIGNING_KEY" > file.key && sed -i 's/\r /\n/g' file.key && chmod 600 file.key
- base64 -w 0 file.key | base64 -d | gpg --import --no-tty --batch --yes
- rm -f file.key
- cmake --version
- export CC=$CC
- export CXX=$CXX
- mkdir -p build/OUTPUT
- echo $CI_BUILD_TYPE
- echo $CMAKE_GENERATOR
- echo $DEFAULT_LINUX_CMAKE_OPTIONS
- echo $CMAKE_SANITIZER_OPTIONS
- cd build
- cmake .. -G "$CMAKE_GENERATOR" -DCMAKE_VERBOSE_MAKEFILE=ON -DLINPHONESDK_PLATFORM=Desktop -DCMAKE_BUILD_TYPE=$CI_BUILD_TYPE $DEFAULT_LINUX_CMAKE_OPTIONS $CMAKE_OPTIONS $SCHEDULE_CMAKE_OPTIONS $CMAKE_SANITIZER_OPTIONS
- cmake --build . --target install --config $CI_BUILD_TYPE $LBC_NODEBUG_OPTIONS
artifacts:
paths:
- build/OUTPUT/*
expire_in: 1 week
#################################################
# Deploy - Nightly
#################################################
job-debian9-makefile-clang-deploy:
stage: deploy
tags: [ "deploy" ]
needs:
- job-debian9-makefile-clang-package
only:
variables:
- $NIGHTLY_MASTER
- $DEPLOY_LINUX
script:
- rsync -rlv --ignore-existing build/OUTPUT/Packages/*.AppImage $DEPLOY_SERVER:$UPLOAD_ROOT_PATH/$LINUX_PLATFORM/$APP_FOLDER
- rsync -rlv build/OUTPUT/RELEASE $DEPLOY_SERVER:$UPLOAD_ROOT_PATH/$LINUX_PLATFORM
- rsync -rlv build/OUTPUT/RELEASE $MAIN_DEPLOY_SERVER:$UPLOAD_ROOT_PATH/$LINUX_PLATFORM
job-debian9-makefile-clang-plugins-deploy:
stage: deploy
tags: [ "deploy" ]
needs:
- job-debian9-makefile-clang
only:
variables:
- $DEPLOY_PLUGINS
script:
- rsync -rlv --ignore-existing build/OUTPUT/plugins/app/*.so $DEPLOY_SERVER:$UPLOAD_ROOT_PATH/$LINUX_PLATFORM/$APP_FOLDER/plugins/
#################################################
# Debug
#################################################
job-debian9-debug:
stage: deploy
tags: [ "docker-test-debian9-liblinphone-nuc" ]
image: gitlab.linphone.org:4567/bc/public/linphone-desktop/bc-dev-debian9-qt-fuse-wget-gpg2:$DEBIAN_9_QT_IMAGE_VERSION
dependencies: []
only:
variables:
- $DEBUG_DEBIAN9
script:
- sleep 10m

View file

@ -1,116 +0,0 @@
job-ubuntu-rolling-ninja-clang:
tags: [ "docker-ubuntu-rolling" ]
image: gitlab.linphone.org:4567/bc/public/linphone-desktop/bc-dev-ubuntu-rolling-qt-fuse-wget-gpg2:$UBUNTU_ROLLING_IMAGE_VERSION
except:
refs:
- schedules
variables:
CMAKE_GENERATOR: Ninja
CMAKE_OPTIONS: -DENABLE_PQCRYPTO=ON
CC: clang
CXX: clang++
extends: .job-linux-desktop
#################################################
# Nightly
#################################################
job-ubuntu-rolling-makefile-gcc:
tags: [ "docker-ubuntu-rolling" ]
image: gitlab.linphone.org:4567/bc/public/linphone-desktop/bc-dev-ubuntu-rolling-qt-fuse-wget-gpg2:$UBUNTU_ROLLING_IMAGE_VERSION
only:
variables:
- $NIGHTLY_MASTER
variables:
CMAKE_GENERATOR: Unix Makefiles
CMAKE_OPTIONS: -DENABLE_PQCRYPTO=ON
CC: gcc
CXX: g++
ADDITIONAL_BUILD_OPTIONS: -j$MAKEFILE_JOBS
extends: .job-linux-desktop
job-ubuntu-rolling-makefile-clang:
tags: [ "docker-ubuntu-rolling" ]
image: gitlab.linphone.org:4567/bc/public/linphone-desktop/bc-dev-ubuntu-rolling-qt-fuse-wget-gpg2:$UBUNTU_ROLLING_IMAGE_VERSION
only:
variables:
- $NIGHTLY_MASTER
variables:
CMAKE_GENERATOR: Unix Makefiles
CMAKE_OPTIONS: -DENABLE_PQCRYPTO=ON
CC: clang
CXX: clang++
ADDITIONAL_BUILD_OPTIONS: -j$MAKEFILE_JOBS
extends: .job-linux-desktop
job-ubuntu-rolling-ninja-gcc:
tags: [ "docker-ubuntu-rolling" ]
image: gitlab.linphone.org:4567/bc/public/linphone-desktop/bc-dev-ubuntu-rolling-qt-fuse-wget-gpg2:$UBUNTU_ROLLING_IMAGE_VERSION
only:
variables:
- $NIGHTLY_MASTER
variables:
CMAKE_GENERATOR: Ninja
CMAKE_OPTIONS: -DENABLE_PQCRYPTO=ON
CC: gcc
CXX: g++
extends: .job-linux-desktop
job-ubuntu-rolling-ninja-gcc-novideo:
only:
variables:
- $NIGHTLY_MASTER
variables:
CMAKE_OPTIONS: -DENABLE_VIDEO=NO -DENABLE_PQCRYPTO=OFF
extends: job-ubuntu-rolling-ninja-gcc
job-ubuntu-rolling-ninja-clang-novideo:
only:
variables:
- $NIGHTLY_MASTER
variables:
CMAKE_OPTIONS: -DENABLE_VIDEO=NO -DENABLE_PQCRYPTO=OFF
extends: job-ubuntu-rolling-ninja-clang
#################################################
# Package - Nightly
#################################################
#job-ubuntu-rolling-makefile-clang-package:
# stage: package
# tags: [ "docker-ubuntu-rolling" ]
# only:
# variables:
# - $NIGHTLY_MASTER
# - $DEPLOY_LINUX
# variables:
# CMAKE_OPTIONS: -DENABLE_APP_PACKAGING=YES -DENABLE_G729=ON
# extends: job-ubuntu-rolling-makefile-clang
# artifacts:
# paths:
# - build/OUTPUT/Packages/Linphone*.AppImage
# expire_in: 1 week
#################################################
# Deploy - Nightly
#################################################
#job-ubuntu-rolling-makefile-clang-deploy:
# stage: deploy
# tags: [ "docker-ubuntu-rolling" ]
# dependencies:
# - job-ubuntu-rolling-makefile-clang-package
# only:
# variables:
# - $NIGHTLY_MASTER
# - $DEPLOY_LINUX
# script:
# - scp build/OUTPUT/Packages/Linphone*.AppImage $DEPLOY_SERVER:$APPIMAGE_UPLOAD_DIRECTORY/

View file

@ -1,26 +0,0 @@
#################################################
# BUILD
#################################################
.build_all_linux_script: &build_all_linux_script |
cmake --version
export CC=$CC
export CXX=$CXX
mkdir -p build/OUTPUT
echo $CI_BUILD_TYPE
echo $CMAKE_GENERATOR
echo $DEFAULT_LINUX_CMAKE_OPTIONS
echo $CMAKE_SANITIZER_OPTIONS
cd build
cmake .. -G "$CMAKE_GENERATOR" -DCMAKE_VERBOSE_MAKEFILE=ON -DLINPHONESDK_PLATFORM=Desktop -DCMAKE_BUILD_TYPE=$CI_BUILD_TYPE $DEFAULT_LINUX_CMAKE_OPTIONS $CMAKE_OPTIONS $SCHEDULE_CMAKE_OPTIONS $CMAKE_SANITIZER_OPTIONS
cmake --build . --target install --config $CI_BUILD_TYPE $LBC_NODEBUG_OPTIONS
.job-linux-desktop:
stage: build
extends: .linux-prepare
script:
- *build_all_linux_script
artifacts:
paths:
- build/OUTPUT
expire_in: 1 week

View file

@ -1,34 +0,0 @@
.linux-prepare:
cache:
key: $CI_JOB_NAME
paths:
- ccache/
extends: .job-prepare
before_script:
##
## If a TUNNEL_USER_KEY is defined then start ssh-agent and add the key
##
- if ! [ -z ${TUNNEL_USER_KEY+x} ]; then eval $(ssh-agent -s); fi
- if ! [ -z ${TUNNEL_USER_KEY+x} ]; then echo "$TUNNEL_USER_KEY" | tr -d '\r' | ssh-add - > /dev/null; fi
- if ! [ -z ${TUNNEL_USER_KEY+x} ]; then mkdir -p ~/.ssh && chmod 700 ~/.ssh; fi
- if ! [ -z ${TUNNEL_USER_KEY+x} ]; then echo -e "Host gitlab.linphone.org\n\tStrictHostKeyChecking no\n\n" > ~/.ssh/config; fi
##
## Then configure ccache
##
- mkdir -p ccache
- echo "max_size = $CCACHE_SIZE" > ccache/ccache.conf
- echo $CCACHE_SIZE
- echo ${PWD}/ccache
- export CCACHE_BASEDIR=${PWD}
- export CCACHE_DIR=${PWD}/ccache
- ccache -s
after_script:
- if ! [ -z ${TUNNEL_USER_KEY+x} ]; then rm -rf ~/.ssh || true; fi
- export CCACHE_DIR=${PWD}/ccache
- ccache -s

View file

@ -1,163 +0,0 @@
#Build template to use in other job scripts without having to copy same code
#format = .className: &referenceName | scripts
#Use = scripts: -*referenceName
#Example : see .job-macosx-desktop for the default script and job-macosx-makefile-package for override
.build_all_script: &build_all_script |
ccache -s
export Qt5_DIR=~/Qt/5.15.2/clang_64/lib/cmake/Qt5
export PATH=~/Qt/5.15.2/clang_64/bin:$PATH
if [ -d "build" ]; then rm -rf build; fi;
mkdir -p build/OUTPUT
cd build
#SDK Building
echo $CI_BUILD_TYPE
echo $CMAKE_GENERATOR
echo $DEFAULT_MACOS_CMAKE_OPTIONS
echo $CMAKE_OPTIONS
echo $ADDITIONAL_BUILD_OPTIONS
cmake .. -G "$CMAKE_GENERATOR" -DLINPHONESDK_PLATFORM=Desktop -DCMAKE_OSX_DEPLOYMENT_TARGET=10.9 -DCMAKE_BUILD_TYPE=$CI_BUILD_TYPE $DEFAULT_MACOS_CMAKE_OPTIONS $XCODE_OPTIONS $CMAKE_OPTIONS $SCHEDULE_CMAKE_OPTIONS -DLINPHONE_BUILDER_SIGNING_IDENTITY="$MACOS_SIGNING_IDENTITY" -DLINPHONESDK_MACOS_ARCHS="$LINPHONESDK_MACOS_ARCHS" -DLINPHONESDK_OPENSSL_ROOT_DIR_X86_64="$LINPHONESDK_OPENSSL_ROOT_DIR_X86_64" -DLINPHONESDK_OPENSSL_ROOT_DIR_ARM64="$LINPHONESDK_OPENSSL_ROOT_DIR_ARM64"
cmake --build . --target install --config $CI_BUILD_TYPE $LBC_NODEBUG_OPTIONS -- $ADDITIONAL_BUILD_OPTIONS
ccache -s
.job-macosx-desktop:
stage: build
tags: [ "macmini-m1-xcode13" ]
script:
- *build_all_script
variables:
LINPHONESDK_MACOS_ARCHS: "x86_64"
LINPHONESDK_OPENSSL_ROOT_DIR_X86_64: "/usr/local/opt/openssl@1.1"
LINPHONESDK_OPENSSL_ROOT_DIR_ARM64: "/opt/homebrew/opt/openssl@1.1"
artifacts:
paths:
- build/OUTPUT
when: always
expire_in: 1 week
#################################################
# On each push
#################################################
job-macosx-ninja:
except:
refs:
- schedules
variables:
CMAKE_GENERATOR: Ninja
CMAKE_OPTIONS: -DENABLE_PQCRYPTO=ON
extends: .job-macosx-desktop
#################################################
# Nightly
#################################################
job-macosx-makefile:
only:
variables:
- $NIGHTLY_MASTER
- $DEPLOY_PLUGINS
variables:
CMAKE_GENERATOR: Unix Makefiles
CMAKE_OPTIONS: -DENABLE_PQCRYPTO=ON
ADDITIONAL_BUILD_OPTIONS: -j$MAKEFILE_JOBS
extends: .job-macosx-desktop
job-macosx-ninja-novideo:
only:
variables:
- $NIGHTLY_MASTER
variables:
CMAKE_OPTIONS: -DENABLE_VIDEO=NO -DENABLE_PQCRYPTO=ON
CMAKE_GENERATOR: Ninja
extends: .job-macosx-desktop
#job-macosx-xcode:
# extends: .job-macosx-desktop
# variables:
# XCODE_OPTIONS: -DLINPHONESDK_MACOS_BASE_URL=$MACOS_SNAPSHOTS_URL
# CMAKE_GENERATOR: Xcode
# ADDITIONAL_BUILD_OPTIONS: -IDEBuildOperationMaxNumberOfConcurrentCompileTasks=$MAX_NUMBER_TASK
# only:
# variables:
# - $NIGHTLY_MASTER
# - $DEPLOY_RUN_MACOSX
#
#################################################
# Package - Nightly
#################################################
# WAIT for QT6 for arm64
job-macosx-makefile-package:
stage: package
tags: [ "macmini-m1-xcode13" ]
dependencies: []
only:
variables:
- $NIGHTLY_MASTER
- $PACKAGE_MACOSX
- $DEPLOY_MACOSX
variables:
CMAKE_OPTIONS: -DENABLE_APP_PACKAGING=YES -DENABLE_G729=ON -DLINPHONE_SDK_MAKE_RELEASE_FILE_URL=$MAKE_RELEASE_FILE_URL/$MACOSX_PLATFORM/$APP_FOLDER
LINPHONESDK_MACOS_ARCHS: "x86_64"
extends: job-macosx-makefile
script:
- *build_all_script
artifacts:
when: always
paths:
- build/OUTPUT/*
when: always
expire_in: 1 week
job-macosx-codesigning:
stage: signing
tags: [ "macmini-m1-xcode13" ]
needs:
- job-macosx-makefile-package
only:
variables:
- $NIGHTLY_MASTER
- $PACKAGE_MACOSX
- $DEPLOY_MACOSX
script:
- cd build
- codesign --options runtime,library --verbose -s "$MACOS_SIGNING_IDENTITY" OUTPUT/Packages/*.dmg
- ./../tools/app_notarization.sh
artifacts:
when: always
paths:
- build/OUTPUT/*
when: always
expire_in: 1 week
#################################################
# Deploy - Nightly
#################################################
job-macosx-makefile-deploy:
stage: deploy
tags: [ "macmini-m1-xcode13" ]
needs:
- job-macosx-codesigning
only:
variables:
- $NIGHTLY_MASTER
- $DEPLOY_MACOSX
script:
- rsync -rlv --ignore-existing build/OUTPUT/Packages/Linphone*.dmg $DEPLOY_SERVER:$UPLOAD_ROOT_PATH/$MACOSX_PLATFORM/$APP_FOLDER
- rsync -rlv build/OUTPUT/RELEASE $DEPLOY_SERVER:$UPLOAD_ROOT_PATH/$MACOSX_PLATFORM
- rsync -rlv build/OUTPUT/RELEASE $MAIN_DEPLOY_SERVER:$UPLOAD_ROOT_PATH/$MACOSX_PLATFORM
job-macosx-makefile-plugins-deploy:
stage: deploy
tags: [ "macmini-m1-xcode13" ]
needs:
- job-macosx-makefile
only:
variables:
- $DEPLOY_PLUGINS
script:
- rsync -rlv --ignore-existing build/OUTPUT/plugins/app/*.dylib $DEPLOY_SERVER:$UPLOAD_ROOT_PATH/$MACOSX_PLATFORM/$APP_FOLDER/plugins

View file

@ -1,193 +0,0 @@
#################################################
# BUILD
#################################################
.windows-vs2019:
extends: .job-prepare
stage: build
tags: [ "windows-powershell" ]
rules:
- if: $CI_PIPELINE_SOURCE == "merge_request_event" && $DOCKER_UPDATE == null && $SKIP_WINDOWS == null
- if: $CI_PIPELINE_SOURCE == "schedule" && $DOCKER_UPDATE == null && $SKIP_WINDOWS == null
variables:
CMAKE_OPTIONS: -DLINPHONE_WINDOWS_SIGN_TOOL=$WINDOWS_SIGN_TOOL -DLINPHONE_WINDOWS_SIGN_TIMESTAMP_URL=$WINDOWS_SIGN_TIMESTAMP_URL -DENABLE_G729=ON -DENABLE_PQCRYPTO=ON
LINPHONESDK_PLATFORM: Desktop
OUTPUT_ZIP_FOLDER: win64
MINGW_TYPE: mingw64
CMAKE_GENERATOR: "Visual Studio 16 2019"
CMAKE_ARCHITECTURE : -A x64
script:
- Set-Variable -Name "PATH_TEMP" -Value ($(Get-ChildItem -Path Env:\PATH).value)
- echo $env:Path
#Remove MinGW of MSYS from PATH and add MINGW_TYPE for MSYS2
# We double the "\" to escape paths as -replace uses regular expressions
- $PATH_TEMP = $PATH_TEMP -replace "C:\\MinGW\\bin;" -replace "C:\\Strawberry\\c\\bin;" -replace "C:\\Program Files\\NASM"
- echo $PATH_TEMP
- $env:Path = ($PATH_TEMP + ";C:\msys64;C:\msys64\usr\bin;C:\msys64\" + $MINGW_TYPE + "\bin;" + $env:SIGNTOOL_ROOT + "\x64")
- If ($MINGW_TYPE -eq "mingw64") {Import-BatchEnvironment "C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Auxiliary\Build\vcvars64.bat"} Else {Import-BatchEnvironment "C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\Common7\Tools\VsDevCmd.bat"}
- If ($MINGW_TYPE -eq "mingw64") {$env:Path = ($env:Path + ";C:\Qt\5.15.2\msvc2019_64\bin")} Else {$env:Path = ($env:Path + ";C:\Qt\5.14.2\msvc2019\bin")}
- echo $env:Path
- If ( Test-Path -Path "build-desktop" ) {Remove-Item -recurse -force -path "build-desktop" }
- mkdir build-desktop
- cd build-desktop
#we launch the msvc-cl wrapper located in python scripts folder
#this wrapper relays only needed calls to the real compiler
#cache stats display
- C:\PROGRA~1\Python37\Scripts\cl -s
- Write-Output $CMAKE_ARCHITECTURE
- Write-Output $SCHEDULE_CMAKE_OPTIONS
- Write-Output $MAKEFILE_JOBS
- Write-Output $CMAKE_C_COMPILER
- Write-Output $CMAKE_CXX_COMPILER
- Write-Output $CMAKE_RC_COMPILER
- Write-Output $DEFAULT_CMAKE_OPTIONS
- Write-Output $CMAKE_OPTIONS
- Write-Output $CMAKE_ARCHITECTURE
- Write-Output $SCHEDULE_CMAKE_OPTIONS
- Write-Output $CMAKE_GENERATOR
- Write-Output $LINPHONESDK_PLATFORM
- Write-Output $MINGW_TYPE
- Write-Output $CLI_OUTPUT_INFO_NINJA
- Write-Output $Write-Output
- Write-Output $NINJA_BUILD_PARALLEL_LEVEL
- Write-Output $PARALLEL_OPTIONS
- Write-Output $NINJA_OPTIMIZATION
- Write-Output $NINJA_EXPLICIT_COMPILER_SET
- Write-Output $DISPLAY_NINJA_LOG
- Write-Output $SEARCH_NINJA_ERROR
- Write-Output $DISPLAY_SEARCH_STATUS
- Write-Output $SET_EXIT_CODE_BASED_ON_SEARCH_STATUS
- Write-Output $SHOW_SEARCH_STATUS_SCRIPT
- Write-Output $LAUNCH_SEARCH_STATUS_SCRIPT
#We are forced to use Invoke-Expression to explain to powershell that we don't want it to touch to spaces in arguments
#If we don't use it, '-A Win32' will be interpreted as "-A ' Win32'" thus making the build fail
- echo $LastExitCode
- Invoke-Expression "& cmake .. -G '$CMAKE_GENERATOR' -DLINPHONESDK_PLATFORM=$LINPHONESDK_PLATFORM -DENABLE_CSHARP_WRAPPER=YES -DCMAKE_BUILD_TYPE=$CI_BUILD_TYPE $NINJA_BUILD_PARALLEL_LEVEL $NINJA_EXPLICIT_COMPILER_SET $DEFAULT_CMAKE_OPTIONS $DEFAULT_WINDOWS_CMAKE_OPTIONS $CMAKE_OPTIONS $CMAKE_ARCHITECTURE $SCHEDULE_CMAKE_OPTIONS"
- echo $LastExitCode
- 'if (-not ($LastExitCode -eq 0)) {throw "Error: Configure failed"}'
- $CLI_OUTPUT_INFO_NINJA
#Warning : Ninja doesn't return an error code on Linker error.
#Store outputs in a file log
#Only in powershell 7 (Gitlab 14+)
# - cmake --build . --target install --config RelWithDebInfo --parallel $MAKEFILE_JOBS | Select-String -NotMatch -Raw -Pattern "inclusion du fichier"
- Write-Output $PARALLEL_OPTIONS
- Write-Output $NINJA_OPTIMIZATION
# /!\ By design, we must keep $NINJA_OPTIMIZATION as the last option of the line, because it contains an output redirect
- echo $LastExitCode
- Invoke-Expression "cmake --build . --target $BUILD_TARGET --config $CI_BUILD_TYPE $PARALLEL_OPTIONS $NINJA_OPTIMIZATION"
- if ($EXITS_ON_ERROR_MSVC) { Invoke-Expression "$EXITS_ON_ERROR_MSVC" } else { Write-Output "EXITS_ON_ERROR_MSVC is null" }
- if ($DISPLAY_NINJA_LOG) { Invoke-Expression "$DISPLAY_NINJA_LOG" } else { Write-Output "DISPLAY_NINJA_LOG is null" }
- if ($SEARCH_NINJA_ERROR) { Invoke-Expression "$SEARCH_NINJA_ERROR" } else { Write-Output "SEARCH_NINJA_ERROR is null" }
- if ($DISPLAY_SEARCH_STATUS) { Invoke-Expression "$DISPLAY_SEARCH_STATUS" } else { Write-Output "DISPLAY_SEARCH_STATUS is null" }
- if ($SET_EXIT_CODE_BASED_ON_SEARCH_STATUS) { Invoke-Expression "$SET_EXIT_CODE_BASED_ON_SEARCH_STATUS" } else { Write-Output "SET_EXIT_CODE_BASED_ON_SEARCH_STATUS is null" }
- if ($SHOW_SEARCH_STATUS_SCRIPT) { Invoke-Expression "$SHOW_SEARCH_STATUS_SCRIPT" } else { Write-Output "SHOW_SEARCH_STATUS_SCRIPT is null" }
- if ($LAUNCH_SEARCH_STATUS_SCRIPT) { Invoke-Expression "$LAUNCH_SEARCH_STATUS_SCRIPT" -ErrorAction stop } else { Write-Output "LAUNCH_SEARCH_STATUS_SCRIPT is null" }
- C:\PROGRA~1\Python37\Scripts\cl -s
- cd linphone-sdk
- mkdir $OUTPUT_ZIP_FOLDER
- Copy-Item -Path "*.zip" -Destination "$CI_PROJECT_DIR/build-desktop/linphone-sdk/$OUTPUT_ZIP_FOLDER" -Recurse
################
artifacts:
paths:
- build-desktop\ninja_buildlog.txt
- build-desktop\ninja_buildlog.txt
- build-desktop\invertSearch.ps1
- build-desktop\OUTPUT\*
when: always
expire_in: 1 week
.windows-vs2019-msvc:
extends: .windows-vs2019
variables:
CMAKE_OPTIONS: -DENABLE_UNIT_TESTS=ON -DLINPHONE_WINDOWS_SIGN_TOOL=$WINDOWS_SIGN_TOOL -DLINPHONE_WINDOWS_SIGN_TIMESTAMP_URL=$WINDOWS_SIGN_TIMESTAMP_URL -DENABLE_G729=ON -DENABLE_PQCRYPTO=ON
LINPHONESDK_PLATFORM: Desktop
CMAKE_GENERATOR: "Visual Studio 16 2019"
BUILD_TARGET: install
PARALLEL_OPTIONS: "-- /maxcpucount /nodeReuse:true /p:TrackFileAccess=false"
EXITS_ON_ERROR_MSVC: 'if (-not ($$LastExitCode -eq 0)) {throw "Error: Build failed"}'
.windows-vs2019-scheduled:
extends: .windows-vs2019-msvc
rules:
- if: $NIGHTLY_MASTER
- if: $NIGHTLY_RELEASE
- if: $ENABLE_WINDOWS_TESTS
before_script:
#cache disabled on scheduled builds since we dot not need the fastest build
- Set-Variable -Name "CLCACHE_DISABLE" -Value 1
######################################################
# JOBS
######################################################
vs2019-msvc-win64-windows:
extends: .windows-vs2019-msvc
variables:
CMAKE_C_COMPILER : cl.exe
CMAKE_CXX_COMPILER : cl.exe
CMAKE_RC_COMPILER : rc.exe
######################################################
# NIGHTLY
######################################################
## ON SCHEDULE ##
vs2019-win64-scheduled-windows:
extends: .windows-vs2019-scheduled
rules:
- if: $NIGHTLY_MASTER
- if: $NIGHTLY_RELEASE
- if: $DEPLOY_RUN_WINDOWS
#################################################
# PACKAGE
#################################################
#Remove . when packaging process is ready to use
vs2019-win64-package:
stage: package
extends: .windows-vs2019-msvc
dependencies: []
rules:
- if: $NIGHTLY_MASTER
- if: $NIGHTLY_RELEASE
- if: $PACKAGE_WINDOWS
- if: $DEPLOY_WINDOWS
variables:
CMAKE_OPTIONS: -DENABLE_APP_PACKAGING=YES -DLINPHONE_WINDOWS_SIGN_TOOL=$WINDOWS_SIGN_TOOL -DLINPHONE_WINDOWS_SIGN_TIMESTAMP_URL=$WINDOWS_SIGN_TIMESTAMP_URL -DENABLE_G729=ON -DLINPHONE_SDK_MAKE_RELEASE_FILE_URL=$MAKE_RELEASE_FILE_URL/$WINDOWS_PLATFORM/$APP_FOLDER -DENABLE_PQCRYPTO=ON
#################################################
# DEPLOY
#################################################
vs2019-win64-upload:
stage: deploy
tags: [ "windows"]
rules:
- if: $NIGHTLY_MASTER
- if: $DEPLOY_WINDOWS
needs:
- vs2019-win64-package
script:
- scp "build-desktop/OUTPUT/Packages/*.exe" "%DEPLOY_SERVER%:%UPLOAD_ROOT_PATH%/%WINDOWS_PLATFORM%/%APP_FOLDER%
- scp "build-desktop/OUTPUT/RELEASE" "%DEPLOY_SERVER%:%UPLOAD_ROOT_PATH%/%WINDOWS_PLATFORM%/
- scp "build-desktop/OUTPUT/RELEASE" "%MAIN_DEPLOY_SERVER%:%UPLOAD_ROOT_PATH%/%WINDOWS_PLATFORM%/
vs2019-win64-plugins-upload:
stage: deploy
tags: [ "windows"]
rules:
- if: $DEPLOY_PLUGINS
needs:
- vs2019-win64-scheduled-windows
script:
- scp "build-desktop/OUTPUT/plugins/app/*.dll" "%DEPLOY_SERVER%:%WINDOWS_UPLOAD_DIRECTORY%/plugins"

View file

@ -0,0 +1,111 @@
.factorize_ubuntu2204: &docker_image_platform_and_runner_tag
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:
rules:
- if: $CI_PIPELINE_SOURCE == "merge_request_event" && $DOCKER_UPDATE == null && $SKIP_LINUX == null
variables:
CMAKE_GENERATOR: Ninja
CMAKE_OPTIONS: -DENABLE_PQCRYPTO=ON
CC: gcc
CXX: g++
extends: .linux-desktop
<<: *docker_image_platform_and_runner_tag
#################################################
# Nightly
#################################################
ubuntu2204-makefile-gcc:
rules:
- !reference [.rules-merge-request-manual, rules]
- if: $NIGHTLY_MASTER
variables:
CMAKE_GENERATOR: Unix Makefiles
CMAKE_OPTIONS: -DENABLE_PQCRYPTO=ON
CC: gcc
CXX: g++
ADDITIONAL_BUILD_OPTIONS: -j$MAKEFILE_JOBS
extends: .linux-desktop
<<: *docker_image_platform_and_runner_tag
ubuntu2204-ninja-clang:
rules:
- !reference [.rules-merge-request-manual, rules]
- if: $NIGHTLY_MASTER
variables:
CMAKE_OPTIONS: -DENABLE_DOC=ON -DENABLE_G729=ON -DENABLE_PQCRYPTO=ON -DENABLE_GPL_THIRD_PARTIES=OFF
CMAKE_GENERATOR: Ninja
CC: clang
CXX: clang++
extends: .linux-desktop
allow_failure: true
<<: *docker_image_platform_and_runner_tag
ubuntu2204-ninja-clang-small:
rules:
- !reference [.rules-merge-request-manual, rules]
- if: $NIGHTLY_MASTER
variables:
CMAKE_OPTIONS: -DENABLE_VIDEO=NO -DENABLE_ADVANCED_IM=NO -DENABLE_DB_STORAGE=NO -DENABLE_PQCRYPTO=OFF
allow_failure: true
extends: ubuntu2204-ninja-clang
ubuntu2204-makefile-gcc-signed:
rules:
- !reference [.rules-merge-request-manual, rules]
- if: $NIGHTLY_MASTER
- if: $DEPLOY_PLUGINS
variables:
CMAKE_GENERATOR: Unix Makefiles
CC: gcc
CXX: g++
ADDITIONAL_BUILD_OPTIONS: -j$MAKEFILE_JOBS
extends: .linux-sign-build
<<: *docker_image_platform_and_runner_tag
#################################################
# Package - Nightly
#################################################
ubuntu2204-makefile-gcc-package:
tags: [ "docker-flat" ]
image: gitlab.linphone.org:4567/bc/public/linphone-desktop/bc-dev-ubuntu-22-04-lts:$UBUNTU_2204_IMAGE_VERSION
needs: []
rules:
- !reference [.rules-merge-request-manual, rules]
- if: $NIGHTLY_MASTER
- if: $PACKAGE_LINUX
- if: $DEPLOY_LINUX
variables:
CMAKE_GENERATOR: Unix Makefiles
CC: gcc
CXX: g++
extends: .linux-sign-package
#################################################
# Deploy - Nightly
#################################################
ubuntu2204-makefile-gcc-deploy:
extends: .linux-deploy
needs:
- ubuntu2204-makefile-gcc-package
only:
variables:
- $NIGHTLY_MASTER
- $DEPLOY_LINUX
ubuntu2204-makefile-gcc-plugins-deploy:
stage: deploy
tags: [ "deploy-flat" ]
needs:
- ubuntu2204-makefile-gcc
only:
variables:
- $DEPLOY_PLUGINS
script:
- rsync -rlv --ignore-existing build/OUTPUT/plugins/app/*.so $DEPLOY_SERVER:$UPLOAD_ROOT_PATH/$LINUX_PLATFORM/$APP_FOLDER/plugins/

View file

@ -0,0 +1,78 @@
#################################################
# BUILD
#################################################
.common_linux: &common_linux |
cmake --version
export CC=$CC
export CXX=$CXX
mkdir -p build/OUTPUT
echo $CI_BUILD_TYPE
echo $CMAKE_GENERATOR
echo $DEFAULT_LINUX_CMAKE_OPTIONS
echo $CMAKE_SANITIZER_OPTIONS
eval "$(qtchooser -qt=$QT_LINUX_VER-${QT_OPEN_SOURCE_DIRECTORY:-proprietary} -print-env)"
export PATH=${QTTOOLDIR}:$PATH
export Qt6_DIR=${QTLIBDIR}/cmake/Qt6
echo "Using Qt $QT_LINUX_VER at ${QTLIBDIR}"
cd build
.build_all_linux_script: &build_all_linux_script |
cmake .. -G "$CMAKE_GENERATOR" -DCMAKE_VERBOSE_MAKEFILE=ON -DLINPHONESDK_PLATFORM=Desktop -DCMAKE_BUILD_TYPE=$CI_BUILD_TYPE -DLINPHONEAPP_APPLICATION_NAME="$APPLICATION_NAME" -DLINPHONEAPP_EXECUABLE_NAME="$EXECUTABLE_NAME" $DEFAULT_LINUX_CMAKE_OPTIONS $CMAKE_OPTIONS $SCHEDULE_CMAKE_OPTIONS $CMAKE_SANITIZER_OPTIONS
cmake --build . --target install --config $CI_BUILD_TYPE $LBC_NODEBUG_OPTIONS
.common_signed_linux: &common_signed_linux |
echo "$GPG_SIGNING_PUB" > file.key && sed -i 's/\r /\n/g' file.key && chmod 600 file.key
gpg --import file.key
rm -f file.key
echo "$GPG_SIGNING_KEY" > file.key && sed -i 's/\r /\n/g' file.key && chmod 600 file.key
base64 -w 0 file.key | base64 -d | gpg --import --no-tty --batch --yes
rm -f file.key
.deploy_linux: &deploy_linux |
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
extends: .linux-prepare
script:
- *common_linux
- *build_all_linux_script
artifacts:
paths:
- build/OUTPUT
expire_in: 1 week
.linux-sign-build:
extends: .linux-desktop
variables:
CMAKE_OPTIONS: -DLINPHONE_BUILDER_SIGNING_IDENTITY=$GPG_SIGNING_KEYID -DENABLE_G729=ON -DENABLE_PQCRYPTO=ON -DENABLE_GPL_THIRD_PARTIES=OFF
APPIMAGETOOL_SIGN_PASSPHRASE: $GPG_SIGNING_PASS
script:
- *common_signed_linux
- *common_linux
- cmake .. -G "$CMAKE_GENERATOR" -DCMAKE_VERBOSE_MAKEFILE=ON -DLINPHONESDK_PLATFORM=Desktop -DCMAKE_BUILD_TYPE=$CI_BUILD_TYPE -DLINPHONEAPP_APPLICATION_NAME="$APPLICATION_NAME" -DLINPHONEAPP_EXECUABLE_NAME="$EXECUTABLE_NAME" $DEFAULT_LINUX_CMAKE_OPTIONS $CMAKE_OPTIONS $SCHEDULE_CMAKE_OPTIONS $CMAKE_SANITIZER_OPTIONS
- cmake --build . --target install --config $CI_BUILD_TYPE $LBC_NODEBUG_OPTIONS
.linux-sign-package:
stage: package
extends: .linux-desktop
variables:
CMAKE_OPTIONS: -DENABLE_APP_PACKAGING=YES -DLINPHONE_BUILDER_SIGNING_IDENTITY=$GPG_SIGNING_KEYID -DENABLE_G729=ON -DLINPHONE_SDK_MAKE_RELEASE_FILE_URL=$MAKE_RELEASE_FILE_URL/$LINUX_PLATFORM/$APP_FOLDER -DENABLE_PQCRYPTO=ON -DENABLE_GPL_THIRD_PARTIES=OFF
APPIMAGETOOL_SIGN_PASSPHRASE: $GPG_SIGNING_PASS
script:
- *common_signed_linux
- *common_linux
- cmake .. -G "$CMAKE_GENERATOR" -DCMAKE_VERBOSE_MAKEFILE=ON -DLINPHONESDK_PLATFORM=Desktop -DCMAKE_BUILD_TYPE=$CI_BUILD_TYPE -DLINPHONEAPP_APPLICATION_NAME="$APPLICATION_NAME" -DLINPHONEAPP_EXECUTABLE_NAME="$EXECUTABLE_NAME" $DEFAULT_LINUX_CMAKE_OPTIONS $CMAKE_OPTIONS $SCHEDULE_CMAKE_OPTIONS $CMAKE_SANITIZER_OPTIONS
- cmake --build . --target install --config $CI_BUILD_TYPE $LBC_NODEBUG_OPTIONS
.linux-deploy:
stage: deploy
tags: [ "deploy-flat" ]
script:
- *deploy_linux

View file

@ -0,0 +1,39 @@
.linux-prepare:
cache:
key: $CI_JOB_NAME
paths:
- ccache/
extends: .prepare
before_script:
##
## If a TUNNEL_USER_KEY is defined then start ssh-agent and add the key
##
- if ! [ -z ${TUNNEL_USER_KEY+x} ]; then eval $(ssh-agent -s); fi
- if ! [ -z ${TUNNEL_USER_KEY+x} ]; then echo "$TUNNEL_USER_KEY" | tr -d '\r' | ssh-add - > /dev/null; fi
- if ! [ -z ${TUNNEL_USER_KEY+x} ]; then mkdir -p ~/.ssh && chmod 700 ~/.ssh; fi
- if ! [ -z ${TUNNEL_USER_KEY+x} ]; then echo -e "Host gitlab.linphone.org\n\tStrictHostKeyChecking no\n\n" > ~/.ssh/config; fi
##
## Then configure ccache
##
- mkdir -p ccache
- echo "max_size = $CCACHE_SIZE" > ccache/ccache.conf
- echo $CCACHE_SIZE
- echo ${PWD}/ccache
- export CCACHE_BASEDIR=${PWD}
- export CCACHE_DIR=${PWD}/ccache
- ccache -s
- find $CI_PROJECT_DIR -name '.git' -exec bash -c 'git config --global --add safe.directory ${0%/.git}' {} \;
- git describe --debug || true
- cd external/linphone-sdk
- git describe --debug || true
- cd ../..
after_script:
- if ! [ -z ${TUNNEL_USER_KEY+x} ]; then rm -rf ~/.ssh || true; fi
- export CCACHE_DIR=${PWD}/ccache
- ccache -s

View file

@ -0,0 +1,171 @@
#Build template to use in other job scripts without having to copy same code
#format = .className: &referenceName | scripts
#Use = scripts: -*referenceName
#Example : see .macosx-desktop for the default script and macosx-makefile-package for override
.build_all_script: &build_all_script |
ccache -s
export Qt6_DIR=~/Qt/$QT_MAC_VER/lib/cmake/Qt6
echo $Qt6_DIR
export PATH=~/Qt/$QT_MAC_VER/bin:$PATH
echo $PATH
if [ -d "build" ]; then rm -rf build; fi;
mkdir -p build/OUTPUT
cd build
#SDK Building
echo $CI_BUILD_TYPE
echo $CMAKE_GENERATOR
echo $DEFAULT_MACOS_CMAKE_OPTIONS
echo $CMAKE_OPTIONS
echo $ADDITIONAL_BUILD_OPTIONS
echo $MAKE_RELEASE_FILE_URL
echo $RELEASE_FILE
cmake .. -G "$CMAKE_GENERATOR" -DCMAKE_OSX_DEPLOYMENT_TARGET=12.3 -DCMAKE_BUILD_TYPE=$CI_BUILD_TYPE $DEFAULT_MACOS_CMAKE_OPTIONS -DLINPHONE_BUILDER_SIGNING_IDENTITY="$MACOS_SIGNING_IDENTITY" -DLINPHONEAPP_APPLICATION_NAME="$APPLICATION_NAME" -DLINPHONEAPP_EXECUTABLE_NAME="$EXECUTABLE_NAME" $XCODE_OPTIONS $CMAKE_OPTIONS $SCHEDULE_CMAKE_OPTIONS $RELEASE_FILE
cmake --build . --target install --config $CI_BUILD_TYPE $LBC_NODEBUG_OPTIONS -- $ADDITIONAL_BUILD_OPTIONS
ccache -s
.macosx-desktop:
stage: build
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
script:
- *build_all_script
artifacts:
paths:
- build/OUTPUT/*
when: always
expire_in: 1 week
#################################################
# On each push
#################################################
#TODO: reactivate pcrypto when liboqs is fixed for 'Vortex' CPU.
macosx-ninja:
rules:
- if: ($CI_PIPELINE_SOURCE == "merge_request_event") && $DOCKER_UPDATE == null && $SKIP_MACOSX == null
variables:
CMAKE_GENERATOR: Ninja
CMAKE_OPTIONS: -DPython3_ROOT_DIR=/opt/bc/pip-packages/ -DENABLE_PQCRYPTO=OFF
extends: .macosx-desktop
#################################################
# Nightly
#################################################
macosx-makefile:
rules:
- !reference [.rules-merge-request-manual, rules]
- if: $NIGHTLY_MASTER
- if: $DEPLOY_PLUGINS
variables:
CMAKE_GENERATOR: Unix Makefiles
CMAKE_OPTIONS: -DPython3_ROOT_DIR=/opt/bc/pip-packages/ -DENABLE_PQCRYPTO=OFF
ADDITIONAL_BUILD_OPTIONS: -j$MAKEFILE_JOBS
extends: .macosx-desktop
macosx-ninja-novideo:
rules:
- !reference [.rules-merge-request-manual, rules]
- if: $NIGHTLY_MASTER
variables:
CMAKE_OPTIONS: -DPython3_ROOT_DIR=/opt/bc/pip-packages/ -DENABLE_VIDEO=OFF -DENABLE_PQCRYPTO=OFF
CMAKE_GENERATOR: Ninja
extends: .macosx-desktop
#macosx-xcode:
# extends: .macosx-desktop
# variables:
# XCODE_OPTIONS: -DPython3_ROOT_DIR=/opt/bc/pip-packages/ -DLINPHONESDK_MACOS_BASE_URL=$MACOS_SNAPSHOTS_URL
# CMAKE_GENERATOR: Xcode
# ADDITIONAL_BUILD_OPTIONS: -IDEBuildOperationMaxNumberOfConcurrentCompileTasks=$MAX_NUMBER_TASK
# only:
# variables:
# - $NIGHTLY_MASTER
# - $DEPLOY_RUN_MACOSX
#
#################################################
# Package - Nightly
#################################################
# WAIT for QT6 for arm64
macosx-ninja-package:
stage: package
tags: [ "macmini-m1-xcode15-flat" ]
needs: []
rules:
- !reference [.rules-merge-request-manual, rules]
- if: $CI_PIPELINE_SOURCE == "schedule" && $DOCKER_UPDATE == null && $SKIP_MACOSX == null
- if: $NIGHTLY_MASTER
- if: $PACKAGE_MACOSX
- if: $DEPLOY_MACOSX
variables:
CMAKE_OPTIONS: -DPython3_ROOT_DIR=/opt/bc/pip-packages/ -DENABLE_APP_PACKAGING=ON -DENABLE_GPL_THIRD_PARTIES=OFF -DENABLE_G729=ON
RELEASE_FILE: -DLINPHONE_SDK_MAKE_RELEASE_FILE_URL=$MAKE_RELEASE_FILE_URL/$MACOSX_PLATFORM/$APP_FOLDER
extends: macosx-ninja
script:
- if [[ $MAKE_RELEASE_FILE_URL == "" ]]; then export RELEASE_FILE=""; fi
- *build_all_script
artifacts:
when: always
paths:
- build/OUTPUT/*
when: always
expire_in: 1 week
macosx-codesigning:
stage: signing
tags: [ "macmini-m1-xcode15-flat" ]
needs:
- macosx-ninja-package
rules:
- !reference [.rules-merge-request-manual, rules]
- if: $NIGHTLY_MASTER
- if: $PACKAGE_MACOSX
- if: $DEPLOY_MACOSX
script:
- cd build
- codesign --timestamp --options runtime,library --verbose -s "$MACOS_SIGNING_IDENTITY" OUTPUT/macos/Packages/Linphone*.dmg
- ./../cmake/install/macos/app_notarization.sh
artifacts:
when: always
paths:
- build/OUTPUT/*
when: always
expire_in: 1 week
#################################################
# Deploy - Nightly
#################################################
macosx-deploy:
stage: deploy
tags: [ "macmini-m1-xcode15-flat" ]
needs:
- macosx-codesigning
only:
variables:
- $NIGHTLY_MASTER
- $DEPLOY_MACOSX
script:
- rsync -rlv --ignore-existing build/OUTPUT/macos/Packages/Linphone*.dmg $DEPLOY_SERVER:$UPLOAD_ROOT_PATH/$MACOSX_PLATFORM/$APP_FOLDER
- |-
if [[ $MAKE_RELEASE_FILE_URL != "" ]]; then
rsync -rlv build/OUTPUT/macos/Packages/RELEASE $DEPLOY_SERVER:$UPLOAD_ROOT_PATH/$MACOSX_PLATFORM
rsync -rlv build/OUTPUT/macos/Packages/RELEASE $MAIN_DEPLOY_SERVER:$UPLOAD_ROOT_PATH/$MACOSX_PLATFORM
fi
macosx-makefile-plugins-deploy:
stage: deploy
tags: [ "macmini-m1-xcode15-flat" ]
needs:
- macosx-makefile
only:
variables:
- $DEPLOY_PLUGINS
script:
- rsync -rlv --ignore-existing build/OUTPUT/plugins/app/*.dylib $DEPLOY_SERVER:$UPLOAD_ROOT_PATH/$MACOSX_PLATFORM/$APP_FOLDER/plugins

View file

@ -0,0 +1,5 @@
.rules-merge-request-manual:
rules:
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
when: manual
allow_failure: true

View file

@ -0,0 +1,262 @@
#################################################
# BUILD
#################################################
.windows-vs:
extends: .prepare
stage: build
rules:
- if: $CI_PIPELINE_SOURCE == "merge_request_event" && $DOCKER_UPDATE == null && $SKIP_WINDOWS == null
- if: $CI_PIPELINE_SOURCE == "schedule" && $DOCKER_UPDATE == null && $SKIP_WINDOWS == null
variables:
CMAKE_OPTIONS: -DENABLE_UNIT_TESTS=ON -DENABLE_G729=ON -DENABLE_PQCRYPTO=ON -DENABLE_GPL_THIRD_PARTIES=OFF
LINPHONESDK_PLATFORM: Desktop
OUTPUT_ZIP_FOLDER: win64
MINGW_TYPE: mingw64
BUILD_TARGET: install
CMAKE_C_COMPILER : cl.exe
CMAKE_CXX_COMPILER : cl.exe
CMAKE_RC_COMPILER : rc.exe
script:
- if ($MAKE_RELEASE_FILE_URL) { } else { $RELEASE_FILE = "" }
- echo $env:Path
- If ( Test-Path -Path "build-desktop" ) {Remove-Item -recurse -force -path "build-desktop" }
- mkdir build-desktop
- cd build-desktop
- sccache --show-stats
- sccache --zero-stats
- Write-Output $CMAKE_ARCHITECTURE
- Write-Output $SCHEDULE_CMAKE_OPTIONS
- Write-Output $MAKEFILE_JOBS
- Write-Output $CMAKE_C_COMPILER
- Write-Output $CMAKE_CXX_COMPILER
- Write-Output $CMAKE_RC_COMPILER
- Write-Output $DEFAULT_CMAKE_OPTIONS
- Write-Output $CMAKE_OPTIONS
- Write-Output $CMAKE_ARCHITECTURE
- Write-Output $SCHEDULE_CMAKE_OPTIONS
- Write-Output $CMAKE_GENERATOR
- Write-Output $LINPHONESDK_PLATFORM
- Write-Output $MINGW_TYPE
- Write-Output $CLI_OUTPUT_INFO_NINJA
- Write-Output $Write-Output
- Write-Output $NINJA_BUILD_PARALLEL_LEVEL
- Write-Output $PARALLEL_OPTIONS
- Write-Output $NINJA_OPTIMIZATION
- Write-Output $NINJA_EXPLICIT_COMPILER_SET
- Write-Output $DISPLAY_NINJA_LOG
- Write-Output $SEARCH_NINJA_ERROR
- Write-Output $DISPLAY_SEARCH_STATUS
- Write-Output $SET_EXIT_CODE_BASED_ON_SEARCH_STATUS
- Write-Output $SHOW_SEARCH_STATUS_SCRIPT
- Write-Output $LAUNCH_SEARCH_STATUS_SCRIPT
- Write-Output $MAKE_RELEASE_FILE_URL
- Write-Output $RELEASE_FILE
#We are forced to use Invoke-Expression to explain to powershell that we don't want it to touch to spaces in arguments
#If we don't use it, '-A Win32' will be interpreted as "-A ' Win32'" thus making the build fail
- echo $LastExitCode
- Invoke-Expression "& cmake .. -G '$CMAKE_GENERATOR' -DLINPHONESDK_PLATFORM=$LINPHONESDK_PLATFORM -DENABLE_CSHARP_WRAPPER=YES -DCMAKE_BUILD_TYPE=$CI_BUILD_TYPE -DLINPHONEAPP_APPLICATION_NAME='$APPLICATION_NAME' -DLINPHONEAPP_EXECUTABLE_NAME='$EXECUTABLE_NAME' $NINJA_BUILD_PARALLEL_LEVEL $NINJA_EXPLICIT_COMPILER_SET $DEFAULT_CMAKE_OPTIONS $DEFAULT_WINDOWS_CMAKE_OPTIONS $CMAKE_OPTIONS $CMAKE_ARCHITECTURE $SCHEDULE_CMAKE_OPTIONS $RELEASE_FILE"
- echo $LastExitCode
- 'if (-not ($LastExitCode -eq 0)) {throw "Error: Configure failed"}'
- $CLI_OUTPUT_INFO_NINJA
#Warning : Ninja doesn't return an error code on Linker error.
#Store outputs in a file log
#Only in powershell 7 (Gitlab 14+)
# - cmake --build . --target install --config RelWithDebInfo --parallel $MAKEFILE_JOBS | Select-String -NotMatch -Raw -Pattern "inclusion du fichier"
- Write-Output $PARALLEL_OPTIONS
- Write-Output $NINJA_OPTIMIZATION
# /!\ By design, we must keep $NINJA_OPTIMIZATION as the last option of the line, because it contains an output redirect
- echo $LastExitCode
- Invoke-Expression "cmake --build . --target $BUILD_TARGET --config $CI_BUILD_TYPE $PARALLEL_OPTIONS $NINJA_OPTIMIZATION"
- if ($EXITS_ON_ERROR_MSBUILD) { Invoke-Expression "$EXITS_ON_ERROR_MSBUILD" } else { Write-Output "EXITS_ON_ERROR_MSBUILD is null" }
- if ($DISPLAY_NINJA_LOG) { Invoke-Expression "$DISPLAY_NINJA_LOG" } else { Write-Output "DISPLAY_NINJA_LOG is null" }
- if ($SEARCH_NINJA_ERROR) { Invoke-Expression "$SEARCH_NINJA_ERROR" } else { Write-Output "SEARCH_NINJA_ERROR is null" }
- if ($DISPLAY_SEARCH_STATUS) { Invoke-Expression "$DISPLAY_SEARCH_STATUS" } else { Write-Output "DISPLAY_SEARCH_STATUS is null" }
- if ($SET_EXIT_CODE_BASED_ON_SEARCH_STATUS) { Invoke-Expression "$SET_EXIT_CODE_BASED_ON_SEARCH_STATUS" } else { Write-Output "SET_EXIT_CODE_BASED_ON_SEARCH_STATUS is null" }
- if ($SHOW_SEARCH_STATUS_SCRIPT) { Invoke-Expression "$SHOW_SEARCH_STATUS_SCRIPT" } else { Write-Output "SHOW_SEARCH_STATUS_SCRIPT is null" }
- if ($LAUNCH_SEARCH_STATUS_SCRIPT) { Invoke-Expression "$LAUNCH_SEARCH_STATUS_SCRIPT" -ErrorAction stop } else { Write-Output "LAUNCH_SEARCH_STATUS_SCRIPT is null" }
- sccache --show-stats
################
artifacts:
paths:
- build-desktop\ninja_buildlog.txt
- build-desktop\ninja_buildlog.txt
- build-desktop\invertSearch.ps1
- build-desktop\OUTPUT\*
- build-desktop\external\linphone-sdk\openh264-prefix\src\openh264-stamp\*
when: always
expire_in: 1 week
.windows-vs2022:
extends: .windows-vs
tags: [ "windows-powershell-vs-17-2022-flat" ]
.windows-codesigning:
extends: .prepare
tags: [ "windows-powershell-vs-17-2022-apps-flat" ]
.windows-msbuild-variables:
variables:
PARALLEL_OPTIONS: "-- /maxcpucount /nodeReuse:true /p:TrackFileAccess=false"
EXITS_ON_ERROR_MSBUILD: 'if (-not ($$LastExitCode -eq 0)) {throw "Error: Build failed"}'
CMAKE_ARCHITECTURE : -A x64
.windows-ninja-variables:
variables:
CLI_OUTPUT_INFO_NINJA: Write-Output -NoEnumerate "Building with Ninja. See ninja_buildlog.txt to get details before completing the build."
NINJA_BUILD_PARALLEL_LEVEL: -DCMAKE_BUILD_PARALLEL_LEVEL=$WINDOWS_PARALLEL_JOBS
PARALLEL_OPTIONS: "--parallel $WINDOWS_PARALLEL_JOBS"
NINJA_OPTIMIZATION: "| find /V \"inclusion du fichier\" > ninja_buildlog.txt; cmd /c \"exit /b 0\""
NINJA_EXPLICIT_COMPILER_SET: -DCMAKE_C_COMPILER="$CMAKE_C_COMPILER" -DCMAKE_CXX_COMPILER="$CMAKE_CXX_COMPILER" -DCMAKE_RC_COMPILER="$CMAKE_RC_COMPILER"
DISPLAY_NINJA_LOG: Get-Content ninja_buildlog.txt
SEARCH_NINJA_ERROR: 'Write-Output "`$$isFound = (Select-String -Pattern `"build stopped: subcommand failed`" -SimpleMatch -Quiet -Path ninja_buildlog.txt)" > invertSearch.ps1'
DISPLAY_SEARCH_STATUS: Write-Output 'echo $$isFound' >> invertSearch.ps1
SET_EXIT_CODE_BASED_ON_SEARCH_STATUS: Write-Output -NoEnumerate "If (`$(echo `$$isFound) -eq `"True`") {throw `"There was an error in the build`"} else {exit 0}" >> invertSearch.ps1
SHOW_SEARCH_STATUS_SCRIPT: Get-Content invertSearch.ps1
LAUNCH_SEARCH_STATUS_SCRIPT: .\invertSearch.ps1
CMAKE_GENERATOR: "Ninja"
.windows-msbuild-scheduled:
extends: .windows-msbuild-variables
rules:
- if: $NIGHTLY_MASTER
- if: $NIGHTLY_RELEASE
- if: $ENABLE_WINDOWS_TESTS
before_script:
#cache disabled on scheduled builds since we dot not need the fastest build
- Set-Variable -Name "CLCACHE_DISABLE" -Value 1
######################################################
# On each push
######################################################
.win64-mr-rules:
rules:
- if: ($CI_PIPELINE_SOURCE == "merge_request_event") && $DOCKER_UPDATE == null && $SKIP_WINDOWS == null
win64-msbuild-vs2022-windows:
rules:
- !reference [.rules-merge-request-manual, rules]
extends:
- .windows-vs2022
- .windows-msbuild-variables
- .win64-mr-rules
win64-ninja-vs2022-windows:
extends:
- .windows-vs2022
- .windows-ninja-variables
- .win64-mr-rules
######################################################
# NIGHTLY
######################################################
## ON SCHEDULE ##
.vs-scheduled-windows:
rules:
- !reference [.rules-merge-request-manual, rules]
- if: $NIGHTLY_MASTER
- if: $DEPLOY_PLUGINS
- if: $DEPLOY_RUN_WINDOWS
win64-ninja-vs2022-scheduled-windows:
extends:
- win64-ninja-vs2022-windows
- .vs-scheduled-windows
#################################################
# PACKAGE
#################################################
#Remove . when packaging process is ready to use
.vs-win64-package:
stage: package
needs: []
rules:
- !reference [.rules-merge-request-manual, rules]
- if: $NIGHTLY_MASTER
- if: $NIGHTLY_RELEASE
- if: $PACKAGE_WINDOWS
- if: $DEPLOY_WINDOWS
variables:
CMAKE_OPTIONS: -DENABLE_APP_PACKAGING=YES -DENABLE_G729=ON -DENABLE_PQCRYPTO=ON -DENABLE_GPL_THIRD_PARTIES=OFF
RELEASE_FILE: -DLINPHONE_SDK_MAKE_RELEASE_FILE_URL=$MAKE_RELEASE_FILE_URL/$WINDOWS_PLATFORM/$APP_FOLDER
win64-ninja-vs2022-package-windows:
variables:
CMAKE_GENERATOR: "Ninja"
CMAKE_ARCHITECTURE: ""
PARALLEL_OPTIONS: ""
extends:
- .windows-vs2022
- .windows-ninja-variables
- .vs-win64-package
#################################################
# SIGNING
#################################################
win64-codesigning:
stage: signing
allow_failure: true
extends:
- .windows-codesigning
needs:
- win64-ninja-vs2022-package-windows
variables:
MINGW_TYPE: mingw64
rules:
- !reference [.rules-merge-request-manual, rules]
- if: $NIGHTLY_MASTER
- if: $PACKAGE_WINDOWS
- if: $DEPLOY_WINDOWS
script:
- cd build-desktop/OUTPUT/Packages/
- Invoke-Expression "& ${WINDOWS_SIGN_TOOL} sign /fd SHA256 /t ${WINDOWS_SIGN_TIMESTAMP_URL} /sha1 ${WINDOWS_SIGN_HASH} *.exe"
- 'if (-not ($LastExitCode -eq 0)) {throw "Error: Signature failed"}'
artifacts:
paths:
- build-desktop\OUTPUT\Packages\*
when: always
expire_in: 1 week
#################################################
# DEPLOY
#################################################
.win64-upload:
stage: deploy
tags: [ "windows-powershell-flat" ]
rules:
- if: $NIGHTLY_MASTER
- if: $DEPLOY_WINDOWS
script:
- 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:
- .win64-upload
needs:
- win64-codesigning
.win64-plugins-upload:
stage: deploy
tags: [ "windows-flat" ]
rules:
- if: $DEPLOY_PLUGINS
script:
- scp "build-desktop/OUTPUT/plugins/app/*.dll" "%MAIN_DEPLOY_SERVER%:%WINDOWS_UPLOAD_DIRECTORY%/plugins"
win64-ninja-vs2022-plugins-upload:
extends: .win64-plugins-upload
needs:
- win64-ninja-vs2022-scheduled-windows

View file

@ -21,11 +21,7 @@ variables:
#CMAKE_OPTIONS: -DENABLE_LIME_X3DH=YES
# Docker image version
ARCHLINUX_IMAGE_VERSION: latestupdated
CENTOS_7_QT_IMAGE_VERSION: 20211012_add_qtwebview
DEBIAN_9_QT_IMAGE_VERSION: 20211027_update_qt_5.12.12
DEBIAN_10_IMAGE_VERSION: 20210217_python3
UBUNTU_ROLLING_IMAGE_VERSION: 20211012_add_qtwebview
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:
@ -41,16 +37,17 @@ workflow:
# Platforms to test
#################################################
.job-prepare:
.prepare:
variables:
ALL_JOB_VARIABLE: ""
include:
- '.gitlab-ci-files/job-linux-prepare.yml'
- '.gitlab-ci-files/job-linux-desktop.yml'
- '.gitlab-ci-files/job-linux-desktop-debian9.yml'
- '.gitlab-ci-files/job-windows-desktop.yml'
- '.gitlab-ci-files/job-macosx-desktop.yml'
- '.gitlab-ci-files/rules.yml'
- '.gitlab-ci-files/linux-prepare.yml'
- '.gitlab-ci-files/linux-desktop.yml'
- '.gitlab-ci-files/linux-desktop-ubuntu-2204.yml'
- '.gitlab-ci-files/windows-desktop.yml'
- '.gitlab-ci-files/macosx-desktop.yml'
stages:

5
.gitmodules vendored
View file

@ -1,6 +1,3 @@
[submodule "linphone-sdk"]
path = linphone-sdk
path = external/linphone-sdk
url = https://gitlab.linphone.org/BC/public/linphone-sdk.git
[submodule "plugins/contacts/contacts-api"]
path = plugins/contacts/contacts-api
url = https://gitlab.linphone.org/BC/public/linphone-desktop-plugins/contacts/contacts-api.git

View file

@ -1,8 +0,0 @@
[main]
host = https://www.transifex.com
[linphone-desktop.ts]
file_filter = linphone-app/assets/languages/<lang>.ts
source_file = linphone-app/assets/languages/en.ts
source_lang = en
type = QT

View file

@ -1,437 +1,39 @@
# Changelog
# Change Log
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
Group changes to describe their impact on the project, as follows:
## 5.0.3 - 2022-12-19
Added for new features.
Changed for changes in existing functionality.
Deprecated for once-stable features removed in upcoming releases.
Removed for deprecated features removed in this release.
Fixed for any bug fixes.
Security to invite users to upgrade in case of vulnerabilities.
### Fixed
- Missing SetThreadDescription entry point on Windows 7/8 (SDK update)
- Add more margin on message's IMDN that was behind the icon menu in chats.
- Remove JSON dependencies on unused Flexiapi.
- Crash at startup about missing contact address on account (SDK fix)
## [6.0.0] - 2025-04-17
## 5.0.2 - 2022-12-13
### Fixed
- Default Language didn't match with the system language (Qt bug).
## 5.0.1 - 2022-12-09
### Fixed
- RF3987 to allow IRI parsing in chats.
- Image display in chats from an URL.
- Display a notification of all kind of messages.
## 5.0.0 - 2022-12-07
6.0.0 release is a complete rework of Linphone Desktop, with only the call and contact list features availables
### Added
- Video conference and iCalendars.
- Make a meeting directly from a group chat.
- New call layouts.
- Display a waiting room before going into a conference.
- Log viewer.
- Read contacts from all friends lists.
- Option to set the display name in "using an account" tab of assistant.
- Long pressed buttons.
- Date and Time pickers.
- Phone dialpad on main window.
- Animated file in chats/notifications.
- Round progress bar for transferring a file and allow to cancel it.
- Hide all accounts if their custom parameter 'hidden' is set to 1.
- Right-click on a timeline will show a slide menu to do actions on the timeline.
- Post quantum ZRTP.
- Windows stack trace dumps into logs on crash.
- Mark as Read synchronized between devices.
- Merge messages into one notification to avoid spam.
- Design overhaul on calls.
- Audio devices can be changed while being in call.
- Use a cryptographic checksum when downloading openH264 from CISCO (Update to 2.2.0)
### Fixed
- Crash on exit.
- Crash when using no account.
- Many Windows crashs (camera, incall)
- Memory stability.
- Clean 3 chat behaviors : Leave chat room (in group info section of conversation menu), erase history (in conversation's menu), delete chat room (in slide menu, or if chat room is empty and left)
- On Mac, close windows instead of minimizing them.
- Running application detection on Install/Uninstall.
- SVG Icons in better quality.
- Event timestamps.
- Optimizations and more minor fixes.
## 4.4.10 - 2022-09-20
### Fixes
- Lime exceptions because of unknown boundaries.
- AppimageTool update for code signing.
## 4.4.9 - 2022-08-29
### Fixes
- Update SDK to fix a crash on startup due to a test on a removed participant device.
- Use default values for new accounts in settings panel.
### Added
- Add 'sip' scheme in authentication popup.
## 4.4.8 - 2022-07-05
### Fixes
- Display name are based on friends (coming from local or LDAP server) and caller address only.
- Running application detection for uninstalling.
## 4.4.7 - 2022-07-01
### Fixes
- When receiving a SIP URL, copy it in Smart search bar instead of openning conversation.
- Update SDK to prepare video conference and improve DTLS handshakes.
## 4.4.6 - 2022-06-14
### Fixed
- Url version check and selection synchronisation.
- Show display name of the caller if it exists instead of call logs.
## 4.4.4 - 2022-06-01
### Fixed
- Revert ordering messages from receiving time.
- Some crashes on Wasapi.
- Update SDK to 5.1.41
## 4.4.3 - 2022-05-30
### Fixed
- Crash on searchs with special characters
- Update SDK to 5.1.38
## 4.4.2 - 2022-05-25
### Added
- Based on LinphoneSDK 5.1.36
- Add Sanitizer build.
- Version types selection for version checker.
### Fixed
- Order messages from receiving time.
- Fix H264 download URL on Linux.
- Hide Admin status in One-to-one chats.
## 4.4.1 - 2022-04-06
### Fixed
- Fix codec downloading on Windows and popup progress bar.
## 4.4.0 - 2022-04-04
### Added
- Features:
* Messages features : Reply, forward (to contact, to a SIP address or to a timeline), Vocal record and play, multi contents, preview.
- Add a feedback on fetching remote provisioning when it failed.
- Option to enable message notifications.
- CPIM on basic chat rooms.
- Device name can be changed from settings.
- New event on new messages in chat and a shortcut to go to the end of chat if last message is not shown.
- Shortcut in Reply to message's origin.
- Allow redirected downloads (bzip2/OpenH264)
- Auto-download message files, editable in settings (10Mb as default)
- 64bits application on Windows
- Based on Linphone SDK 5.1
### Fixed
- Simplify filtering timelines with 2 modes (minimal or exhaustive) and on 3 kind of search : security level, simple/group chats, ephemerals.
- Sort timelines by taken account of unread events in chat rooms.
- Fix systemTrayIcon that could be cloned on each restart.
- Fix thumbnails display in notification.
- Fix errors on Action-Buttons on restart.
- Enable G729 on public builds.
- Take account of return key on Numpad.
- Huge messages are better shown and with less flickering.
- High CPU consumption on idle state.
- Hide deleted/terminated chat rooms.
- Adapt UserAgent with device name.
- Video freeze on network change.
- Support OpenGL 4.1 and GLSL 4.10.
- Fix some glitches on Apple M1.
- Audio errors in settings when using different audio format between input and output.
- Set default log size to 50MB
- Reduce ICE candidates on Windows.
- Show logs in console on Windows.
- Crash on the smart search bar.
## 4.3.2
### Fixed
- ALSA volumes can be view/changed while being in call.
- Remove constraints on actions (call/chat) that were based on friends capabilities.
- Unblock secure group chat activation.
- Unselect current contact if history call view is displayed.
- Show chat actions in history view.
- Group chat creation : If no groupchat capabilities has been found in recent contacts, ignore test on capability and display them.
## 4.3.1 - 2021-11-04
### Added
- Features:
* New version behavior : Manual check for new version, option to activate the automatic check and a way to set the URL.
* A banner is shown when copying text.
* Options to enable standard and secure chats.
* Add tunnel support if build.
* Overhaul of color managment and use monochrome images.
* Change Contact Edit and SIP Addresses selections to start a standard chat or a secure one.
* Call history button in the timeline panel.
* Timeout of incoming call notification is now based on `inc_timeout`
* More actions in contact edit panel (call/video call).
* Allow to make a readonly variable in configuration (only for enabling chats yet).
### Fixed
- Better quality of icons.
- Crash on start and on exit.
- Allow to use a secure chat room to be used when calling (set by context : encrypted call/secure chat enabled).
- History buttons that should not appear if chat room mode is not activated.
- Keep the fullscreen mode when receiving a notification.
- Clicking on the fullscreen action on the call window will go to the fullscreen if exists.
- Fix scrolling speed and add a cache in lists.
- Fix Mac crash by adding an option to deactivate mipmap.
- Add more translations.
- Mac: Enable automatic graphics switching indicating whether an OpenGL app may utilize the integrated GPU.
- Version checking that could request an update to older version.
- A crash on authentication with empty configs.
- Main search with UTF8
- When requested, remove all history of a chat room and not only desplayed entries.
- Fix missing qml variables.
- Add more debug logs.
- Use macqtdeploy when building in order to use binary without having enabling packaging.
## 4.3.0 - 2021-10-20
### Added
- Features:
* Chat groups with administrator mode, participants management and devices display.
* Secure chat rooms for 1-1 and group chat using LIME end-to-end encryption.
* Ephemerals Chat rooms (per-participant mode).
* Attended transfer.
* LDAP integration: settings allow remote LDAP servers to be configured. Contacts can then be searched in the smart search bar, and during incoming call the display name of the caller is automatically retrieved from the LDAP server.
* Address book connectors : custom plugins can now be imported from settings in order to be used to synchronize contacts.
- Enhance user experience :
* Show subject in notifications for group chats.
* Attended transfer.
* Chat area is no more fixed but adapts to content.
* Click on notification bubble in top left account lead to the call history view.
* Double-Click on avatar in conversation to fill the smart search bar with the participant address.
* Allow to hide or show the timeline panel.
* Allow to hide or show empty chat rooms in settings.
* Messages font can now be changed in settings.
* Sort contact list using System Locale.
* In fullscreen mode, the preview size can be changed by using mouse wheel.
* Echo calibration in settings view.
* Autostart for AppImage.
* Add more tooltips.
* Add a forgotten password link in assistant.
- Search and filtering features:
* Search in timeline from subject/addresses.
* Search in messages.
* Filter timelines by the kind of chat rooms (1-1, group chats) and modes (secure and ephemerals).
- Chat room management:
* Updatable subject by clicking on it.
* Upgrade security level by authenticating participants.
* Add more events in chat rooms like chat rooms status, participants updates, security level updates, ephemerals activations.
- In Chat, allow custom menu to appear by removing the repeating key when holding it. On Mac, there is an accent menu for this feature.
- Add URI handler configuration : `linphone-config` to fetch a configuration file.
- Fetch a configuration file from a CLI command/URI Handlers :
* sip:user@domain?method=call&fetch-config=base64(scheme://url)
* linphone-config://url
* linphone-config:fetch-config=base64(scheme://url)
* linphone --fetch-config=scheme://url
* linphone "<method> fetch-config=scheme://url"
- Options to audio codec can be used and stored.
- Devices can be selected in linphone configuration file from a regex rule.
- Opus can now use `packetlosspercentage` and `useinbandfec` configuration.
- A silence file have been added : `silence.mkv` and can be used to switch off some musics (hold_music).
- Use of new mediastreamer2 MSQOgl filter as video display backend (based on QQuickFramebufferObject).
- MSYS2 support for Windows.
### Fixed
- Cursor shape of mouse is changed when hovering on buttons.
- When clicking on a chat notification, it will close it.
- Persistent call bubble notifications.
- Fix on Missed calls and messages count bubbles.
- Unmatched room when using malformed username.
- Contact names handle special characters.
- UTF8 characters on Windows.
- Mark as Read only if in foreground.
- Show avatar and username once for a same kind of message.
- Load optimizations.
- Refactoring data modelisation and colors management.
- On Mac : Camera freeze and black screen when using third-party.
- Prevent opening call Window if the option to stay in background has been activated.
- Crash while searching contacts.
- Stop receiving messages when proxy has been deleted.
- Transfer menu of calls : Dynamic size for texts.
- XCode build wasn't fully supported.
- Sort languages in the UI settings.
## 4.2.5 - 2020-12-18
### Added
-iLBC support
### Fixed
- VP8 freeze
- Audio quality distortion
- OSX deployment target propagated to linphone SDK
## 4.2.4 - 2020-11-21
### Added
- Play DTMF when receiving it and show the Dialpad on outgoing call to allow sending DTMF
- Transport protocol deactivation has been replaced by not listening ports
- Show all call logs when clicking on the `previously` bar in the left panel
- A call log can be used to callback or add the contact in friends list
### Fixed
- Displaying names in UTF8
- Keep unsend typed message in memory when changing of chat room
- Log files have Qt logs
- Missing `sqlite3` backend
- Use the more generic `linphone` folder and not `Linphone` for installation
- Simplify build process to use install keyword
- Links errors like liblinphone++.so.10
## 4.2.3 - 2020-10-09
### Added
- Add support to tel and callto protocols
- Allow Pulseaudio to switch devices automatically. For example, it will mute all applications that have music when receive a call from Linphone.
### Fixed
- Contact name can contain special characters
- Avoid to reduce window if it is currently maximized when clicking on contacts
- Cleaner use of Windows registries
## 4.2.2 - 2020-07-30
### Fixed
- Crash on Opus
## 4.2.1 - 2020-07-03
### Fixed
- Crash on authentifications
- Multiple Popups are no longer ignored and are open in a StackView.
## 4.2.0 - 2020-06-26
### Added
- Added a `CLI` function in order to support `URI handlers` from browsers. Help is available with `linphone --cli-help`. (See also: https://wiki.linphone.org/xwiki/wiki/public/view/Linphone/URI%20Handlers%20%28Desktop%20only%29/).
- Improved general audio/video quality thanks to better rate control algorithms in liblinphone and mediastreamer2.
- More efficient echo cancellation.
- `OpenH264` codec can be downloaded and used in the application from Cisco website.
- `G729` codec can be used in the application.
- Improved High DPI Displays support for 4K screens.
- On multiscreens, when choosing full screen mode during a call, the call screen open in the current screen. The old behaviour kept the call screen in the primary screen.
- Detect audio/video hardware changes while using settings.
- Updatable audio/video devices while in call.
- Added an option to automatically show Dialpad.
- Dialpad supports A, B, C and D keys.
- Dialpad supports keyboard when hovering on it.
- DTMF sound played when sent.
- Added an option to keep windows in background when a call is received.
- Added an option to allow Linphone to be launched automatically with the system (autostart).
- Added an option to play sound notification when an incoming chat message is received.
- Added Call tools in Fullscreen mode (medias settings, security, mutable speaker).
- Audio settings display the microphone being used and allow you to adjust capture and playback gains.
- Conference participants are mutable by clicking on them.
- Added the possibility to record calls automatically.
- Moved logs folder without restart.
- Added caller and callee information into file names of recordings.
- Enhanced interface for switching between multiple SIP accounts: the timeline now shows activity for the currently selected SIP account only.
- Timeline uses current proxy config info and show data only on selected profile.
- Tooltips can be shown in multiple lines.
- Display the name of the caller in incoming notifications.
- Notifications are shown in all available screens.
- Display unread message count in system tray (Linphone icon).
- Display unread chat message count and missed calls in `Manage Accounts` dialog and in `Main Window`.
- Added a media parameter dialog in the `Call View` to select devices and set volume.
- Display a spinner when a message is being sent.
- Disabled screensaver on fullscreen video call.
- New logo, icons and installer assets.
- New Linux deployment (Appimage).
- Supports chinese, danish, french, english, german, hungarian, italian, japanese, lithuanian, portuguese, russian, spanish, swedish, turkish, ukrainian from community contributions.
- Use Native BZip2 instead of Embedded Minizip to extract `OpenH264` codec.
- App Nap avoiding for MacOs.
- Simplified building process.
- Contacts trust: contacts for which all devices have been validated through a ZRTP call with SAS exchange are now highlighted with a blue circle (and with a red one in case of mistrust). That trust is now handled at contact level (instead of conversation level in previous versions).
- Security focus: security & trust is more visible than ever, and unsecure conversations & calls are even more visible than before.
- CardDAV: you can configure as many CardDAV servers you want to synchronize you contacts in Linphone (in addition or in replacement of native addressbook import).
- OpenID: when used with a SSO compliant SIP server (such as Flexisip), we support single-sign-on login.
- MWI support: display and allow to call your voicemail when you have new messages (if supported by your VoIP provider and properly configured in your account params).
- CCMP support: if you configure a CCMP server URL in your accounts params, it will be used when scheduling meetings & to fetch list of meetings you've organized/been invited to.
- Devices list: check on which device your sip.linphone.org account is connected and the last connection date & time (like on subscribe.linphone.org).
### Changed
- Separated threads: Contrary to previous versions, our SDK is now running in it's own thread, meaning it won't freeze the UI anymore in case of heavy work, thus reducing the number of ANR and greatly increasing the fluidity of the app.
- Asymmetrical video : you no longer need to send your own camera feed to receive the one from the remote end of the call, and vice versa.
- Call transfer: Blind & Attended call transfer have been merged into one: during a call, if you initiate a transfer action, either pick another call to do the attended transfer or select a contact from the list (you can input a SIP URI not already in the suggestions list) to start a blind transfer.
- Settings: a lot of them are gone, the one that are still there have been reworked to increase user friendliness.
- 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.
- Upgraded to use QT 5.12.
- Depends on linphone-sdk project (numerous direct submodules removed).
- License changed from GPLv2 to GPLv3.
### Fixed
## [6.1.0] - XXXX-XX-XX
- Removed `:` separator from file names of recordings because it is not allowed on Windows.
- Avoided mark `as read` on selected chat rooms if window is not active.
- Search box in main page will not reset text when clicking on it.
- More stable account authentifications.
- Message status behaviour : Resuming status when changing logs, cursor shapes updates, bind the resend message action to error icon.
- Apple permissions that could lead to muted microphone.
- Incoming call notification window (sometimes not showing).
### Removed
- `Prepare.py` configuration.
- Remove useless splashscreen.
- `Minizip` dependencies.
- `Flatpak` support.
## 4.1.0 - 2017-07-19
### Added
- Add tooltips on `recording` and `screenshot` buttons in `Calls Window`.
- Show notifications on `recording` and `screenshot`.
- Show `XXX is typing...` in `Timeline` and `Chat View`.
- Handle correctly `SIGINT`.
- Handle clicks on SIP URI in chat messages.
- Show video framerate in `Calls Stats`.
- Add a `Logs` menu entry in `Settings Window`, it provides send, remove, activate buttons...
- Supports EXIF orientation for file transfer images preview.
- Echo canceller supports 48kHz.
- Better GUI when a proxy config is modified in `Settings Window`.
### Fixed
- Handle correctly ringer device changes in `Settings Window`.
- In `Video Settings`, display FPS field only in `custom preset` mode.
- Use now the directory containing user documents files for saved video/audio/screenshots.
- Update `Chat View` correctly if it is used in many windows.
- Update correctly selected language when app is restarted.
- Avoid a deadlock on Mac OS when a call ends in fullscreen mode.
- Application can be started from one binary only.
- Single instance is now supported with flatpak. (It uses D-Bus.)
### Changed
- Minimum supported Qt version is now 6.10.0

View file

@ -1,6 +1,6 @@
################################################################################
#
# Copyright (c) 2010-2020 Belledonne Communications SARL.
# Copyright (c) 2010-2024 Belledonne Communications SARL.
#
# This file is part of linphone-desktop
# (see https://www.linphone.org).
@ -20,7 +20,8 @@
#
################################################################################
cmake_minimum_required(VERSION 3.1)
cmake_minimum_required(VERSION 3.22)
get_cmake_property(vars CACHE_VARIABLES)
foreach(var ${vars})
@ -38,10 +39,21 @@ endforeach()
if(ENABLE_BUILD_VERBOSE)
message("User Args : ${USER_ARGS}")
endif()
if( APPLE )
if( NOT CMAKE_OSX_DEPLOYMENT_TARGET)
set(CMAKE_OSX_DEPLOYMENT_TARGET "10.9" CACHE STRING "Minimum OS X deployment version")
endif()
if(POLICY CMP0149)
# VS generator looks for most recent Windows SDK, ignoring
# CMAKE_SYSTEM_VERSION and allowing override by WindowsSDKVersion
# environment variable. New in 3.27. This is to allow override
# in the Windows CI builds.
# This MUST be set before any project() or or enable_language() command.
cmake_policy(SET CMP0149 NEW)
endif()
if(POLICY CMP0141)
# Changes the way debug info on Windows are stored (forces /Z7)
# This is a requirement to use build cache on Windows, since /Zi is another Microsoft cache, incompatible with any third party cache known to date
set(CMAKE_MSVC_DEBUG_INFORMATION_FORMAT Embedded)
cmake_policy(SET CMP0141 NEW)
endif()
project(linphoneqt)
@ -49,24 +61,52 @@ project(linphoneqt)
include(GNUInstallDirs)
include(CheckCXXCompilerFlag)
set(CMAKE_CXX_STANDARD 11)
# Prepare gobal CMAKE configuration specific to the current project
set(SDK_BUILD_DIR "${CMAKE_BINARY_DIR}/WORK") # SDK build in WORK. Keep all in it.
set(LINPHONE_OUTPUT_DIR "${CMAKE_BINARY_DIR}/linphone-sdk/desktop")
set(APPLICATION_OUTPUT_DIR "${CMAKE_BINARY_DIR}/OUTPUT")
set(CMAKE_PREFIX_PATH "${LINPHONE_OUTPUT_DIR};${APPLICATION_OUTPUT_DIR};${APPLICATION_OUTPUT_DIR}/include${PREFIX_PATH}")
if(WIN32)
set( CMAKE_PREFIX_PATH "${CMAKE_PREFIX_PATH};${APPLICATION_OUTPUT_DIR}/${CMAKE_INSTALL_BINDIR}")
elseif(APPLE)
set( CMAKE_PREFIX_PATH "${CMAKE_PREFIX_PATH};${APPLICATION_NAME}.app/Contents/Frameworks")
set(CMAKE_CXX_STANDARD 17)
if(LINPHONEAPP_INSTALL_PREFIX)
set(APPLICATION_OUTPUT_DIR "${LINPHONEAPP_INSTALL_PREFIX}")
else()
set( CMAKE_PREFIX_PATH "${CMAKE_PREFIX_PATH};${APPLICATION_OUTPUT_DIR}/${CMAKE_INSTALL_LIBDIR}")
set(APPLICATION_OUTPUT_DIR "${CMAKE_BINARY_DIR}/OUTPUT")
endif()
string(REPLACE ";" "|" PREFIX_PATH "${CMAKE_PREFIX_PATH}")
#set(PREFIX_PATH "${LINPHONE_OUTPUT_DIR}|${APPLICATION_OUTPUT_DIR}${PREFIX_PATH}")
set(CMAKE_INSTALL_PREFIX "${APPLICATION_OUTPUT_DIR}")
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]")
set(LINPHONESDK_BUILD_TYPE "Default")#Using Mac will remove all SDK targets.
set(ENABLE_FAT_BINARY "ON") # Disable XCFrameworks as it is not supported.
set(CMAKE_INSTALL_BINDIR "${LINPHONEAPP_APPLICATION_NAME}.app/Contents/MacOS")
set(CMAKE_INSTALL_LIBDIR "${LINPHONEAPP_APPLICATION_NAME}.app/Contents/Frameworks")
set(CMAKE_INSTALL_INCLUDEDIR "${LINPHONEAPP_APPLICATION_NAME}.app/Contents/Resources/include")
set(CMAKE_INSTALL_DATAROOTDIR "${LINPHONEAPP_APPLICATION_NAME}.app/Contents/Resources/share")
if( NOT CMAKE_OSX_DEPLOYMENT_TARGET)
#set(CMAKE_OSX_DEPLOYMENT_TARGET "10.15")#Qt: 'path' is unavailable: introduced in macOS 10.15
set(CMAKE_OSX_DEPLOYMENT_TARGET "12.3")#ScreenSharing: 'SCStreamConfiguration' has been introduced in macOS 12.3
endif()
set(LINPHONESDK_MACOS_ARCHS ${LINPHONEAPP_MACOS_ARCHS})
set(CMAKE_OSX_ARCHITECTURES ${LINPHONESDK_MACOS_ARCHS} CACHE STRING "")
elseif(WIN32)
set(LINPHONESDK_BUILD_TYPE "Default")
else()
endif()
if(NOT LINPHONE_OUTPUT_DIR)# set this variable only if you don't build the module
set(LINPHONE_OUTPUT_DIR "${CMAKE_INSTALL_PREFIX}")# Cannot be different from the current CMAKE_INSTALL_PREFIX
endif()
if( NOT QTKEYCHAIN_OUTPUT_DIR) # set this variable only if you don't build the module
set(QTKEYCHAIN_OUTPUT_DIR "${CMAKE_INSTALL_PREFIX}")# Cannot be different from the current CMAKE_INSTALL_PREFIX
endif()
# Avoid cmake warning if CMP0071 is not set.
if (POLICY CMP0071)
@ -75,6 +115,12 @@ endif ()
#set_property(GLOBAL PROPERTY USE_FOLDERS ON)
#------------------------------------------------------------------------------
# Prepare gobal CMAKE configuration specific to the current project
set(CMAKE_POSITION_INDEPENDENT_CODE ON)#Needed for Qt
if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)
set(CMAKE_INSTALL_PREFIX "${CMAKE_BINARY_DIR}/OUTPUT" CACHE PATH "Default linphone-app installation prefix" FORCE)
set(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT FALSE)
endif()
if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
message(STATUS "Setting build type to 'RelWithDebInfo' as none was specified")
@ -89,221 +135,146 @@ endif()
#-------------------------------------------------------------------------------
# SET OPTIONS
#-------------------------------------------------------------------------------
set(OPTION_LIST "")
#function doesn't work with strings value
function(add_option _OPTION_LIST OPTION DESC VALUE)
option(${OPTION} ${DESC} ${VALUE})
list(APPEND _OPTION_LIST ${${_OPTION_LIST}} "-D${OPTION}=${${OPTION}}")
set(${${_OPTION_LIST}} ${_OPTION_LIST} PARENT_SCOPE)
endfunction()
option(ENABLE_APP_LICENSE "Enable the license in packages." YES)
option(ENABLE_APP_PACKAGING "Enable packaging" NO)
option(ENABLE_APP_PACKAGE_ROOTCA "Embed the rootca file into the package" YES)
option(ENABLE_APP_WEBVIEW "Enable webviews." NO) #Webview is not fully supported because of deployments. Used for subscription.
option(ENABLE_BUILD_APP_PLUGINS "Enable the build of plugins" YES)
option(ENABLE_BUILD_EXAMPLES "Enable the build of examples" NO)
option(ENABLE_BUILD_VERBOSE "Enable the build generation to be more verbose" NO)
option(ENABLE_DAEMON "Enable the linphone daemon interface." NO)
option(ENABLE_FFMPEG "Build mediastreamer2 with ffmpeg video support." ON)
option(ENABLE_QRCODE "Enable QRCode support" NO)#Experimental
option(ENABLE_SANITIZER "Enable sanitizer." NO)
option(ENABLE_STRICT "Build with strict compilator flags e.g. -Wall -Werror" NO)
option(ENABLE_TESTS "Build with testing binaries of SDK" NO )
option(ENABLE_TESTS_COMPONENTS "Build libbctoolbox-tester" NO )
option(ENABLE_TOOLS "Enable tools of SDK" NO)
option(ENABLE_UNIT_TESTS "Enable unit test of SDK." NO )
option(ENABLE_UPDATE_CHECK "Enable update check." YES)
function(add_cache _OPTION_LIST OPTION DESC VALUE)
set(${OPTION} ${VALUE} CACHE STRING ${DESC})
list(APPEND _OPTION_LIST ${${_OPTION_LIST}} "-D${OPTION}=${${OPTION}}")
set(${${_OPTION_LIST}} ${_OPTION_LIST} PARENT_SCOPE)
endfunction()
option(ENABLE_OPENH264 "Enable the use of OpenH264 codec" YES)
option(ENABLE_NON_FREE_CODECS "Enable the use of non free codecs" YES)
add_option(OPTION_LIST ENABLE_APP_LICENSE "Enable the license in packages." ON)
add_option(OPTION_LIST ENABLE_APP_OAUTH2 "Build with OAuth2 support for remote provisioning." OFF) # Experimental.
add_option(OPTION_LIST ENABLE_APP_PACKAGING "Enable packaging" OFF)
add_option(OPTION_LIST ENABLE_APP_PACKAGE_ROOTCA "Embed the rootca file into the package" ON)
add_option(OPTION_LIST ENABLE_APP_PDF_VIEWER "Enable Pdf viewer. Only enable if the version of Qt have the module. Cannot be activated because of Qt find_package() make an error on unbound pdf." OFF)
add_option(OPTION_LIST ENABLE_APP_WEBVIEW "Enable webviews. Webview is not fully supported because of deployments. Used for subscription." OFF)
add_option(OPTION_LIST ENABLE_BUILD_APP_PLUGINS "Enable the build of plugins" ON)
add_option(OPTION_LIST ENABLE_BUILD_EXAMPLES "Enable the build of examples" OFF)
add_option(OPTION_LIST ENABLE_BUILD_VERBOSE "Enable the build generation to be more verbose" OFF)
add_option(OPTION_LIST ENABLE_CONSOLE_UI "Turn on or off compilation of console interface." OFF)
add_option(OPTION_LIST ENABLE_DAEMON "Enable the linphone daemon interface." OFF)
add_option(OPTION_LIST ENABLE_DOC "Enable API documentation generation." OFF)
add_option(OPTION_LIST ENABLE_FFMPEG "Build mediastreamer2 with ffmpeg video support. No more needed." OFF)
add_option(OPTION_LIST ENABLE_LDAP "Enable LDAP support." YES)
add_option(OPTION_LIST ENABLE_NON_FREE_CODECS "Enable the use of non free codecs" YES)
add_option(OPTION_LIST ENABLE_NON_FREE_FEATURES "Enable the use of non free codecs" ${ENABLE_NON_FREE_CODECS})
add_option(OPTION_LIST ENABLE_QT_KEYCHAIN "Build QtKeychain to manage VFS from System key stores." OFF)
add_option(OPTION_LIST ENABLE_QRCODE "Enable QRCode support" OFF)#Experimental
add_option(OPTION_LIST ENABLE_RELATIVE_PREFIX "Set Internal packages relative to the binary" ON)
add_option(OPTION_LIST ENABLE_SANITIZER "Enable sanitizer." OFF)
add_option(OPTION_LIST ENABLE_STRICT "Build with strict compilator flags e.g. -Wall -Werror" OFF)
add_option(OPTION_LIST ENABLE_TESTS "Build with testing binaries of SDK" OFF)
add_option(OPTION_LIST ENABLE_TESTS_COMPONENTS "Build libbctoolbox-tester" OFF)
add_option(OPTION_LIST ENABLE_TOOLS "Enable tools of SDK" OFF)
add_option(OPTION_LIST ENABLE_UNIT_TESTS "Enable unit test of SDK." OFF)
add_option(OPTION_LIST ENABLE_UPDATE_CHECK "Enable update check." ON)
add_option(OPTION_LIST ENABLE_VIDEO "Enable Video support." YES)
add_option(OPTION_LIST ENABLE_WINDOWS_TOOLS_CHECK "Enable tools checks on Windows for auto install." OFF)
option(ENABLE_VIDEO "Enable Video support." YES)
option(ENABLE_LDAP "Enable LDAP support." YES)
option(ENABLE_CONSOLE_UI "Turn on or off compilation of console interface." NO)
add_cache(OPTION_LIST LINPHONE_SDK_MAKE_RELEASE_FILE_URL "Make a RELEASE file that work along check_version and use this URL" "")
option(LINPHONE_SDK_MAKE_RELEASE_FILE_URL "Make a RELEASE file that work along check_version and use this URL" "")
add_option(OPTION_LIST ENABLE_OPENH264 "Enable the use of OpenH264 codec" ${ENABLE_VIDEO})
add_option(OPTION_LIST ENABLE_SCREENSHARING "Enable screen sharing." ${ENABLE_VIDEO})
if(WIN32 OR APPLE)
# QtKeychain
add_option(OPTION_LIST LIBSECRET_SUPPORT "Build with libsecret support" OFF) # Need libsecret-devel
if(WIN32)
add_cache(OPTION_LIST QTKEYCHAIN_TARGET_NAME "Override Qt6Keychain library name for a workaround with windeployqt" "EQt6Keychain")
else()
option(ENABLE_V4L "Ability to capture and display video using libv4l2 or libv4l." YES)
add_cache(OPTION_LIST QTKEYCHAIN_TARGET_NAME "Override Qt6Keychain library name" "Qt6Keychain")
endif()
option(ENABLE_RELATIVE_PREFIX "Set Internal packages relative to the binary" YES)
set(APP_OPTIONS "-DENABLE_UPDATE_CHECK=${ENABLE_UPDATE_CHECK}")
list(APPEND APP_OPTIONS "-DENABLE_APP_LICENSE=${ENABLE_APP_LICENSE}")
list(APPEND APP_OPTIONS "-DENABLE_APP_PACKAGING=${ENABLE_APP_PACKAGING}")
list(APPEND APP_OPTIONS "-DENABLE_APP_PACKAGE_ROOTCA=${ENABLE_APP_PACKAGE_ROOTCA}")
list(APPEND APP_OPTIONS "-DENABLE_APP_WEBVIEW=${ENABLE_APP_WEBVIEW}")
list(APPEND APP_OPTIONS "-DENABLE_BUILD_EXAMPLES=${ENABLE_BUILD_EXAMPLES}")
list(APPEND APP_OPTIONS "-DENABLE_BUILD_VERBOSE=${ENABLE_BUILD_VERBOSE}")
list(APPEND APP_OPTIONS "-DENABLE_CONSOLE_UI=${ENABLE_CONSOLE_UI}")
list(APPEND APP_OPTIONS "-DENABLE_DAEMON=${ENABLE_DAEMON}")
list(APPEND APP_OPTIONS "-DENABLE_FFMPEG=${ENABLE_FFMPEG}")
list(APPEND APP_OPTIONS "-DENABLE_FLEXIAPI=${ENABLE_QRCODE}")
list(APPEND APP_OPTIONS "-DENABLE_LDAP=${ENABLE_LDAP}")
list(APPEND APP_OPTIONS "-DENABLE_NON_FREE_CODECS=${ENABLE_NON_FREE_CODECS}")
list(APPEND APP_OPTIONS "-DENABLE_OPENH264=${ENABLE_OPENH264}")
list(APPEND APP_OPTIONS "-DENABLE_QRCODE=${ENABLE_QRCODE}")
list(APPEND APP_OPTIONS "-DENABLE_SANITIZER=${ENABLE_SANITIZER}")
list(APPEND APP_OPTIONS "-DENABLE_STRICT=${ENABLE_STRICT}")
list(APPEND APP_OPTIONS "-DENABLE_TESTS=${ENABLE_TESTS}")
list(APPEND APP_OPTIONS "-DENABLE_TESTS_COMPONENTS=${ENABLE_TESTS_COMPONENTS}")
list(APPEND APP_OPTIONS "-DENABLE_TOOLS=${ENABLE_TOOLS}")
list(APPEND APP_OPTIONS "-DENABLE_UNIT_TESTS=${ENABLE_UNIT_TESTS}")
list(APPEND APP_OPTIONS "-DENABLE_VIDEO=${ENABLE_VIDEO}")
if(LINPHONE_SDK_MAKE_RELEASE_FILE_URL)
list(APPEND APP_OPTIONS "-DLINPHONE_SDK_MAKE_RELEASE_FILE_URL=${LINPHONE_SDK_MAKE_RELEASE_FILE_URL}")
endif()
if(LINPHONESDK_MACOS_ARCHS)
list(APPEND APP_OPTIONS "-DLINPHONESDK_MACOS_ARCHS=${LINPHONESDK_MACOS_ARCHS}")
endif()
if(ENABLE_V4L)
list(APPEND APP_OPTIONS "-DENABLE_V4L=${ENABLE_V4L}")
endif()
list(APPEND APP_OPTIONS "-DENABLE_RELATIVE_PREFIX=${ENABLE_RELATIVE_PREFIX}")
list(APPEND APP_OPTIONS "-DLINPHONE_OUTPUT_DIR=${LINPHONE_OUTPUT_DIR}")
list(APPEND APP_OPTIONS "-DENABLE_QT_GL=${ENABLE_VIDEO}")#Activate on video
include(ExternalProject)
set(PROJECT_BUILD_COMMAND "")
if(CMAKE_BUILD_PARALLEL_LEVEL)
list(APPEND APP_OPTIONS "-DCMAKE_BUILD_PARALLEL_LEVEL=${CMAKE_BUILD_PARALLEL_LEVEL}")
if(${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.12.0") #CMAKE_BUILD_PARALLEL_LEVEL will not always work for External projects
list(APPEND PROJECT_BUILD_COMMAND "--parallel" "${CMAKE_BUILD_PARALLEL_LEVEL}")
endif()
endif()
if(CMAKE_VERBOSE_MAKEFILE)
list(APPEND APP_OPTIONS "-DCMAKE_VERBOSE_MAKEFILE=${CMAKE_VERBOSE_MAKEFILE}")
if(${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.14.0")
list(APPEND PROJECT_BUILD_COMMAND "--verbose")
endif()
endif()
if(UNIX AND NOT APPLE)
set(CMAKE_INSTALL_RPATH "$ORIGIN:$ORIGIN/lib64:$ORIGIN/../lib64:$ORIGIN/lib:$ORIGIN/../lib:${LINPHONE_OUTPUT_DIR}/${CMAKE_INSTALL_LIBDIR}")
list(APPEND APP_OPTIONS "-DCMAKE_INSTALL_RPATH=${CMAKE_INSTALL_RPATH}")
if(WIN32)
add_option(OPTION_LIST ENABLE_OPENSSL_EXPORT "Enable OpenSSL deployment" YES)
elseif(APPLE)
list(APPEND APP_OPTIONS "-DENABLE_FAT_BINARY=ON") #Disable XCFrameworks as it is not supported.
endif()
if(CMAKE_OSX_DEPLOYMENT_TARGET)
list(APPEND APP_OPTIONS "-DCMAKE_OSX_DEPLOYMENT_TARGET=${CMAKE_OSX_DEPLOYMENT_TARGET}")
endif()
list(APPEND APP_OPTIONS "-DCMAKE_CXX_STANDARD=${CMAKE_CXX_STANDARD}")
if(ENABLE_BUILD_APP_PLUGINS)
file(GLOB children "plugins/*")
set(dirlist "")
foreach(child ${children})
if(IS_DIRECTORY ${curdir}/${child} AND (ENABLE_BUILD_EXAMPLES OR NOT ${child} MATCHES "example"))
list(APPEND dirlist ${child})
endif()
endforeach()
list(LENGTH dirlist count)
if(NOT count)
set(ENABLE_BUILD_APP_PLUGINS OFF)
message(STATUS "No plugins found for the application to build")
endif()
endif()
if(NOT LINPHONE_QT_ONLY)
ExternalProject_Add(sdk PREFIX "${CMAKE_BINARY_DIR}/sdk"
SOURCE_DIR "${CMAKE_SOURCE_DIR}/linphone-sdk"
INSTALL_DIR "${LINPHONE_OUTPUT_DIR}"
STAMP_DIR "${SDK_BUILD_DIR}/stamp"
BINARY_DIR "${SDK_BUILD_DIR}"
STEP_TARGETS build
BUILD_COMMAND ${CMAKE_COMMAND} --build <BINARY_DIR> --config $<CONFIG> ${PROJECT_BUILD_COMMAND}
INSTALL_COMMAND ${CMAKE_COMMAND} -E echo "Install step is already done at build time."
LIST_SEPARATOR | # Use the alternate list separator
CMAKE_ARGS ${APP_OPTIONS} ${USER_ARGS} -DCMAKE_INSTALL_PREFIX:PATH=<INSTALL_DIR> -DCMAKE_PREFIX_PATH=${PREFIX_PATH}
BUILD_ALWAYS NO #${DO_BUILD}
)
ExternalProject_Add_Step(sdk force_build
COMMENT "Forcing build for 'desktop'"
DEPENDEES configure
DEPENDERS build
ALWAYS 1
)
endif()
include(FindPkgConfig)
set(APP_DEPENDS sdk)
find_package(Qt5 5.10 COMPONENTS Core REQUIRED)
if ( NOT Qt5_FOUND )
message(FATAL_ERROR "Minimum supported Qt5 version is 5.10!")
endif()
find_package(LinphoneCxx CONFIG QUIET)
find_package(Linphone CONFIG QUIET)
find_package(bctoolbox CONFIG QUIET)
find_package(belcard CONFIG QUIET)
find_package(Mediastreamer2 CONFIG QUIET)
find_package(ortp CONFIG QUIET)
if(NOT (LinphoneCxx_FOUND) OR NOT (Linphone_FOUND) OR NOT (bctoolbox_FOUND) OR NOT (belcard_FOUND) OR NOT (Mediastreamer2_FOUND) OR NOT (ortp_FOUND) OR FORCE_APP_EXTERNAL_PROJECTS)
message("Projects are set as External projects. You can start building them by using for example : cmake --build . --target install")
ExternalProject_Add(linphone-qt PREFIX "${CMAKE_BINARY_DIR}/linphone-app"
SOURCE_DIR "${CMAKE_SOURCE_DIR}/linphone-app"
INSTALL_DIR "${APPLICATION_OUTPUT_DIR}"
BINARY_DIR "${CMAKE_BINARY_DIR}/linphone-app"
DEPENDS ${APP_DEPENDS}
BUILD_COMMAND ${CMAKE_COMMAND} --build <BINARY_DIR> --config $<CONFIG> ${PROJECT_BUILD_COMMAND}
INSTALL_COMMAND ${CMAKE_COMMAND} -E echo "Install step is already done at build time."
LIST_SEPARATOR | # Use the alternate list separator
CMAKE_ARGS ${APP_OPTIONS} ${USER_ARGS} -DCMAKE_INSTALL_PREFIX:PATH=<INSTALL_DIR> -DCMAKE_PREFIX_PATH=${PREFIX_PATH}
# ${APP_OPTIONS}
BUILD_ALWAYS ON
)
if( ENABLE_BUILD_APP_PLUGINS)
ExternalProject_Add(app-plugins PREFIX "${CMAKE_BINARY_DIR}/plugins-app"
SOURCE_DIR "${CMAKE_SOURCE_DIR}/plugins"
INSTALL_DIR "${APPLICATION_OUTPUT_DIR}"
BINARY_DIR "${CMAKE_BINARY_DIR}/plugins-app"
DEPENDS linphone-qt
BUILD_COMMAND ${CMAKE_COMMAND} --build <BINARY_DIR> --config $<CONFIG> ${PROJECT_BUILD_COMMAND}
INSTALL_COMMAND ${CMAKE_COMMAND} -E echo "Install step is already done at build time."
LIST_SEPARATOR | # Use the alternate list separator
CMAKE_ARGS ${APP_OPTIONS} ${USER_ARGS} -DCMAKE_INSTALL_PREFIX:PATH=<INSTALL_DIR> -DCMAKE_PREFIX_PATH=${PREFIX_PATH}
)
endif()
install(CODE "message(STATUS Running install)")
set(AUTO_REGENERATION auto_regeneration)
if( ENABLE_BUILD_APP_PLUGINS)
add_custom_target(${AUTO_REGENERATION} ALL
COMMAND ${CMAKE_COMMAND} ${CMAKE_CURRENT_SOURCE_DIR}
DEPENDS app-plugins)
else()
add_custom_target(${AUTO_REGENERATION} ALL
COMMAND ${CMAKE_COMMAND} ${CMAKE_CURRENT_SOURCE_DIR}
DEPENDS linphone-qt)
endif()
add_option(OPTION_LIST ENABLE_OPENSSL_EXPORT "Enable OpenSSL deployment" OFF)
else()
message("Adding Linphone Desktop in an IDE-friendly state")
set(CMAKE_INSTALL_PREFIX "${APPLICATION_OUTPUT_DIR}")
add_subdirectory(${CMAKE_SOURCE_DIR}/linphone-app)
if(NOT LINPHONE_QT_ONLY)
add_dependencies(app-library ${APP_DEPENDS})
endif()
if( ENABLE_BUILD_APP_PLUGINS)
add_subdirectory(${CMAKE_SOURCE_DIR}/plugins "plugins-app")
endif()
add_option(OPTION_LIST ENABLE_V4L "Ability to capture and display video using libv4l2 or libv4l." ${ENABLE_VIDEO})
add_option(OPTION_LIST ENABLE_OPENSSL_EXPORT "Enable OpenSSL deployment" OFF)
endif()
ExternalProject_Add(linphone-qt-only PREFIX "${CMAKE_BINARY_DIR}/linphone-app"
SOURCE_DIR "${CMAKE_SOURCE_DIR}/linphone-app"
INSTALL_DIR "${APPLICATION_OUTPUT_DIR}"
BINARY_DIR "${CMAKE_BINARY_DIR}/linphone-app"
BUILD_COMMAND ${CMAKE_COMMAND} --build <BINARY_DIR> --config $<CONFIG> ${PROJECT_BUILD_COMMAND}
# INSTALL_COMMAND ${CMAKE_COMMAND} -E echo "Install step is already done at build time."
LIST_SEPARATOR | # Use the alternate list separator
CMAKE_ARGS ${APP_OPTIONS} ${USER_ARGS} -DCMAKE_INSTALL_PREFIX:PATH=<INSTALL_DIR> -DCMAKE_PREFIX_PATH=${PREFIX_PATH}
EXCLUDE_FROM_ALL ON
#BUILD_ALWAYS ON
)
# Set some SDK variables to configure the APP build as we want it
set(ENABLE_CXX_WRAPPER ON CACHE BOOL "Build the C++ wrapper for Liblinphone." FORCE)
set(ENABLE_CSHARP_WRAPPER OFF CACHE BOOL "Build the CSharp wrapper for Liblinphone." FORCE)
set(ENABLE_THEORA OFF)
set(ENABLE_QT_GL ${ENABLE_VIDEO})
find_package(Qt6 REQUIRED COMPONENTS Core Quick Widgets)
if(NOT Qt6_FOUND)
message(FATAL_ERROR "Minimum supported Qt6!")
endif()
set(LINPHONEAPP_BUILD_TYPE "Default" CACHE STRING "Type of build")
set_property(CACHE LINPHONEAPP_BUILD_TYPE PROPERTY STRINGS "Default" "Macos" "Normal")
if(LINPHONEAPP_BUILD_TYPE STREQUAL "Default")
if(APPLE)
set(LINPHONEAPP_BUILD_TYPE "Macos")
else()
set(LINPHONEAPP_BUILD_TYPE "Normal")
endif()
endif()
if(NOT APPLE OR MONO_ARCH)
add_custom_target(linphone-deps)
if(NOT LINPHONE_QT_ONLY)
function(add_external)
set(CMAKE_POLICY_DEFAULT_CMP0077 NEW) # Prevent project from overriding the options we just set here
add_subdirectory("external")
endfunction()
add_external()
endif()
function(add_linphone_app)
set(CMAKE_POLICY_DEFAULT_CMP0077 NEW) # Prevent project from overriding the options we just set here
if(UNIX)
set(CMAKE_INSTALL_RPATH "$ORIGIN:$ORIGIN/lib64:$ORIGIN/../lib64:$ORIGIN/lib:$ORIGIN/../lib")
endif()
add_subdirectory("Linphone")
endfunction()
add_linphone_app()
if(ENABLE_BUILD_APP_PLUGINS)
#add_subdirectory("plugins" "plugins-app")
endif()
if(NOT LINPHONE_QT_ONLY)
# Move root folders to app
#file(MAKE_DIRECTORY "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}")
#file(MAKE_DIRECTORY "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_INCLUDEDIR}")
#file(MAKE_DIRECTORY "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_DATAROOTDIR}")
#file(MAKE_DIRECTORY "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_BINDIR}")
#if(APPLE)
# install(CODE "execute_process(COMMAND rsync -a --force \"${CMAKE_INSTALL_PREFIX}/Frameworks\" \"${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_INCLUDEDIR}\" )") #Use rsync to bypass symlinks override issues of frameworks. copy_directory will fail without explicit error...
#endif()
#install(CODE "execute_process(COMMAND ${CMAKE_COMMAND} -E copy_directory \"${CMAKE_INSTALL_PREFIX}/include/\" \"${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_INCLUDEDIR}/\")")
#install(CODE "execute_process(COMMAND ${CMAKE_COMMAND} -E copy_directory \"${CMAKE_INSTALL_PREFIX}/share/\" \"${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_DATAROOTDIR}/\")")
#install(CODE "execute_process(COMMAND ${CMAKE_COMMAND} -E copy_directory \"${CMAKE_INSTALL_PREFIX}/mkspecs/\" \"${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_DATAROOTDIR}/\")")
endif()
include (cmake/install/install.cmake)
else()
include(cmake/TasksMacos.cmake)
endif()
# if (ENABLE_QT_KEYCHAIN)
# target_include_directories(${TARGET_NAME} SYSTEM PUBLIC ${PROJECT_SOURCE_DIR}/external/qtkeychain)
# target_link_libraries(${TARGET_NAME} PUBLIC ${QTKEYCHAIN_TARGET_NAME})
# message(STATUS "link libraries: ${TARGET_NAME} ${QTKEYCHAIN_TARGET_NAME}")
# message(STATUS "CMAKE_CURRENT_SOURCE_DIR: ${CMAKE_CURRENT_SOURCE_DIR}")
# message(STATUS "PROJECT_SOURCE_DIR: ${PROJECT_SOURCE_DIR}")
# message(STATUS "Contents of qtkeychain:")
# file(GLOB QTKEYCHAIN_HEADERS "${PROJECT_SOURCE_DIR}/external/qtkeychain/qtkeychain/*.h")
# message(STATUS "Found headers: ${QTKEYCHAIN_HEADERS}")
# endif()
file(COPY "${CMAKE_CURRENT_SOURCE_DIR}/cmake/hook/pre-commit" DESTINATION "${CMAKE_CURRENT_SOURCE_DIR}/.git/hooks/")

225
Linphone/CMakeLists.txt Normal file
View file

@ -0,0 +1,225 @@
cmake_minimum_required(VERSION 3.16)
project(Linphone VERSION 6.2.0 LANGUAGES CXX)
################################################################
# PACKAGES
################################################################
set(LINPHONE_PACKAGES Mediastreamer2 LinphoneCxx BCToolbox LibLinphone)
list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake/Modules")
foreach(PACKAGE ${LINPHONE_PACKAGES})
message(STATUS "Trying to find ${PACKAGE}")
find_package(${PACKAGE} REQUIRED)
if(NOT ${PACKAGE}_FOUND)
find_package(${PACKAGE} CONFIG REQUIRED)
endif()
endforeach()
set(TARGET_NAME Linphone)
set(APP_TARGETS ${LinphoneCxx_TARGET}
${Mediastreamer2_TARGET}#MediastreamerUtils
${LibLinphone_TARGET})#Liblinphone
set(QT_DEFAULT_MAJOR_VERSION 6)
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()
find_package(Qt6 REQUIRED COMPONENTS Core)
find_package(Qt6 REQUIRED COMPONENTS ${QT_PACKAGES})
find_package(Qt6 REQUIRED COMPONENTS LinguistTools)
if(NOT WIN32)
find_package(X11)
endif()
if(X11_FOUND)
list(APPEND APP_TARGETS X11::X11)
endif()
################################################################
# CONFIGS
################################################################
if(NOT WIN32)
add_compile_options(-Werror=deprecated-declarations)
endif()
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -DNDEBUG -DQT_NO_DEBUG")
set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO} -DQT_QML_DEBUG -DQT_DECLARATIVE_DEBUG")
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -DDEBUG -DQT_QML_DEBUG -DQT_DECLARATIVE_DEBUG" )
set(CMAKE_INCLUDE_CURRENT_DIR ON)#useful for config.h
if(NOT LINPHONEAPP_VERSION)
bc_compute_full_version(LINPHONEAPP_VERSION)
endif()
set(LINPHONE_MAJOR_VERSION)
set(LINPHONE_MINOR_VERSION)
set(LINPHONE_MICRO_VERSION)
set(LINPHONE_BRANCH_VERSION)
bc_parse_full_version(${LINPHONEAPP_VERSION} LINPHONE_MAJOR_VERSION LINPHONE_MINOR_VERSION LINPHONE_MICRO_VERSION LINPHONE_BRANCH_VERSION)
set(LINPHONEAPP_SHORT_VERSION "${LINPHONE_MAJOR_VERSION}.${LINPHONE_MINOR_VERSION}.${LINPHONE_MICRO_VERSION}")
set(GIT_BRANCH_NAME)
execute_process(
COMMAND git name-rev --name-only HEAD
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
OUTPUT_VARIABLE GIT_BRANCH_NAME
OUTPUT_STRIP_TRAILING_WHITESPACE
)
set(LINPHONESDK_VERSION)
execute_process(
COMMAND git describe
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/external/linphone-sdk
OUTPUT_VARIABLE LINPHONESDK_VERSION
OUTPUT_STRIP_TRAILING_WHITESPACE
)
include(application_info.cmake)
string(TIMESTAMP CURRENT_YEAR "%Y")
if(NOT APPLICATION_START_LICENCE OR "${CURRENT_YEAR}" STREQUAL "${APPLICATION_START_LICENCE}")
set(COPYRIGHT_RANGE_DATE "${APPLICATION_START_LICENCE}")
else()
set(COPYRIGHT_RANGE_DATE "${APPLICATION_START_LICENCE}-${CURRENT_YEAR}")
endif()
if(MEDIASTREAMER2_PLUGINS_LOCATION)
set(MSPLUGINS_DIR ${MEDIASTREAMER2_PLUGINS_LOCATION})
elseif(APPLE)
set(MSPLUGINS_DIR "Frameworks/mediastreamer2.framework/Versions/A/Libraries")
else()
set(MSPLUGINS_DIR "${CMAKE_INSTALL_LIBDIR}/mediastreamer/plugins")
endif()
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/config.h.cmake" "${CMAKE_CURRENT_BINARY_DIR}/config.h")
if(${Qt6_VERSION} VERSION_LESS "6.10.0")
message( FATAL_ERROR "Linphone requires Qt 6.10.0 or newer. Exiting CMake." )
endif()
qt6_standard_project_setup()
################################################################
# SOURCES
################################################################
set(_LINPHONEAPP_SOURCES main.cpp)
set(_LINPHONEAPP_QML_FILES)
set(_LINPHONEAPP_RC_FILES)
set(_LINPHONEAPP_QML_SINGLETONS)
add_subdirectory(data)
add_subdirectory(tool)
add_subdirectory(model)
add_subdirectory(view)
add_subdirectory(core)
set(LANGUAGES_DIRECTORY "data/languages")
set(I18N_FILENAME i18n.qrc)
set(LANGUAGES en fr de)
# Add languages support.
add_subdirectory("${LANGUAGES_DIRECTORY}" "data/languages")
qt_standard_project_setup(I18N_TRANSLATED_LANGUAGES ${LANGUAGES})
set(TS_FILES)
foreach (lang ${LANGUAGES})
list(APPEND TS_FILES "${CMAKE_CURRENT_SOURCE_DIR}/${LANGUAGES_DIRECTORY}/${lang}.ts")
endforeach()
qt_add_translations(${TARGET_NAME}
TS_FILES ${TS_FILES}
RESOURCE_PREFIX ${LANGUAGES_DIRECTORY})
# set application details
if(WIN32)
configure_file("${CMAKE_SOURCE_DIR}/cmake/install/windows/appDetailsWindows.rc.in" "${CMAKE_CURRENT_BINARY_DIR}/appDetailsWindows.rc")
set(_APPDETAILS_RC_FILE ${CMAKE_CURRENT_BINARY_DIR}/appDetailsWindows.rc)
endif()
#fonts.qrc is in data and not in data/font because we want to use 'font' in path and not in prefix.
#TODO make prefix working
set(_LINPHONEAPP_FONTS_FILES)
qt6_add_big_resources(_LINPHONEAPP_FONTS_FILES data/fonts.qrc)
# Have big_resource.qrc treated as a source file by Qt Creator
list(APPEND _LINPHONEAPP_FONTS_FILES data/fonts.qrc)
set_property(SOURCE data/fonts.qrc PROPERTY SKIP_AUTORCC ON)
qt6_add_executable(Linphone
${_LINPHONEAPP_SOURCES}
${_LINPHONEAPP_FONTS_FILES}
${_APPDETAILS_RC_FILE}
)
set_source_files_properties(${_LINPHONEAPP_QML_SINGLETONS} PROPERTIES QT_QML_SINGLETON_TYPE TRUE)
qt_policy(SET QTP0001 NEW)
qt6_add_qml_module(Linphone
URI Linphone
VERSION 1.0
QML_FILES ${_LINPHONEAPP_QML_FILES} ${_LINPHONEAPP_QML_SINGLETONS}
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
################################################################
# TARGETS LINKS
################################################################
if(APPLE)
configure_file("${CMAKE_SOURCE_DIR}/cmake/install/macos/Info.plist.in" "${CMAKE_BINARY_DIR}/cmake/install/macos/Info.plist" @ONLY)
endif()
set_target_properties(${TARGET_NAME} PROPERTIES
MACOSX_BUNDLE_GUI_IDENTIFIER org.linphone
MACOSX_BUNDLE_BUNDLE_VERSION ${PROJECT_VERSION}
MACOSX_BUNDLE_SHORT_VERSION_STRING ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}
MACOSX_BUNDLE TRUE
MACOSX_BUNDLE_INFO_PLIST ${CMAKE_BINARY_DIR}/cmake/install/macos/Info.plist
WIN32_EXECUTABLE TRUE
OUTPUT_NAME "${EXECUTABLE_NAME}"
# Added for Qt to set the correct path on run configurations.
ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/${CMAKE_INSTALL_LIBDIR}"
LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/${CMAKE_INSTALL_LIBDIR}"
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/${CMAKE_INSTALL_BINDIR}"
)
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)
target_link_libraries(${TARGET_NAME} PUBLIC Dwmapi)
endif()
elseif (APPLE)
target_link_libraries(${TARGET_NAME} PUBLIC "-framework Cocoa" "-framework IOKit" "-framework AVFoundation" "-framework ScreenCaptureKit")
endif()
foreach(T ${APP_TARGETS})
target_include_directories(${TARGET_NAME} SYSTEM PUBLIC $<TARGET_PROPERTY:${T},INTERFACE_INCLUDE_DIRECTORIES>)
target_link_libraries(${TARGET_NAME} PUBLIC ${T})
endforeach()
target_link_libraries(${TARGET_NAME} PRIVATE Qt6::Core)
foreach(T ${QT_PACKAGES})
target_link_libraries(${TARGET_NAME} PRIVATE Qt6::${T})
endforeach()

View file

@ -0,0 +1,11 @@
set(APPLICATION_DESCRIPTION "A libre SIP client")
set(APPLICATION_ID "com.belledonnecommunications.${LINPHONEAPP_EXECUTABLE_NAME}")
set(APPLICATION_NAME ${LINPHONEAPP_APPLICATION_NAME})
set(APPLICATION_URL "https://www.linphone.org")
set(APPLICATION_VENDOR "Belledonne Communications")
set(APPLICATION_LICENCE "GNU General Public License V3")
set(APPLICATION_LICENCE_URL "https://www.gnu.org/licenses/gpl-3.0.html")
set(APPLICATION_START_LICENCE "2010")
set(APPLICATION_SEMVER ${LINPHONEAPP_VERSION})
set(EXECUTABLE_NAME ${LINPHONEAPP_EXECUTABLE_NAME})

View file

@ -26,9 +26,16 @@
#cmakedefine APPLICATION_VENDOR "${APPLICATION_VENDOR}"
#cmakedefine APPLICATION_URL "${APPLICATION_URL}"
#cmakedefine APPLICATION_LICENCE "${APPLICATION_LICENCE}"
#cmakedefine APPLICATION_LICENCE_URL "${APPLICATION_LICENCE_URL}"
#cmakedefine APPLICATION_SEMVER "${APPLICATION_SEMVER}"
#cmakedefine COPYRIGHT_RANGE_DATE "${COPYRIGHT_RANGE_DATE}"
#cmakedefine ENABLE_UPDATE_CHECK 1
#cmakedefine EXECUTABLE_NAME "${EXECUTABLE_NAME}"
#cmakedefine MSPLUGINS_DIR "${MSPLUGINS_DIR}"
#cmakedefine ENABLE_APP_WEBVIEW "${ENABLE_APP_WEBVIEW}"
#cmakedefine ENABLE_SCREENSHARING "${ENABLE_SCREENSHARING}"
#cmakedefine QTKEYCHAIN_TARGET_NAME ${QTKEYCHAIN_TARGET_NAME}
#cmakedefine PDF_ENABLED
#cmakedefine LINPHONEAPP_SHORT_VERSION "${LINPHONEAPP_SHORT_VERSION}"
#cmakedefine GIT_BRANCH_NAME "${GIT_BRANCH_NAME}"
#cmakedefine LINPHONESDK_VERSION "${LINPHONESDK_VERSION}"

1569
Linphone/core/App.cpp Normal file

File diff suppressed because it is too large Load diff

238
Linphone/core/App.hpp Normal file
View file

@ -0,0 +1,238 @@
/*
* Copyright (c) 2010-2024 Belledonne Communications SARL.
*
* This file is part of linphone-desktop
* (see https://www.linphone.org).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <QAction>
#include <QCommandLineParser>
#include <QQmlApplicationEngine>
#include <QSharedPointer>
#include "core/account/AccountProxy.hpp"
#include "core/call/CallProxy.hpp"
#include "core/chat/ChatGui.hpp"
#include "core/setting/SettingsCore.hpp"
#include "core/singleapplication/singleapplication.h"
#include "model/cli/CliModel.hpp"
#include "model/core/CoreModel.hpp"
#include "tool/AbstractObject.hpp"
class CallGui;
class ChatGui;
class Thread;
class Notifier;
class QQuickWindow;
class QSystemTrayIcon;
class DefaultTranslatorCore;
class App : public SingleApplication, public AbstractObject {
Q_OBJECT
Q_PROPERTY(bool coreStarted READ getCoreStarted WRITE setCoreStarted NOTIFY coreStartedChanged)
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)
public:
App(int &argc, char *argv[]);
~App();
void setSelf(QSharedPointer<App>(me));
static App *getInstance();
static Thread *getLinphoneThread();
Notifier *getNotifier() const;
EventCountNotifier *getEventCountNotifier();
int getEventCount() const;
// App::postModelAsync(<lambda>) => run lambda in model thread and continue.
// App::postModelSync(<lambda>) => run lambda in current thread and block connection.
template <typename Func, typename... Args>
static auto postModelAsync(Func &&callable, Args &&...args) {
QMetaObject::invokeMethod(CoreModel::getInstance().get(), callable, args...);
}
template <typename Func>
static auto postModelAsync(Func &&callable) {
QMetaObject::invokeMethod(CoreModel::getInstance().get(), callable);
}
template <typename Func, typename... Args>
static auto postCoreAsync(Func &&callable, Args &&...args) {
QMetaObject::invokeMethod(App::getInstance(), callable, args...);
}
template <typename Func>
static auto postCoreAsync(Func &&callable) {
QMetaObject::invokeMethod(App::getInstance(), callable);
}
template <typename Func, typename... Args>
static auto postCoreSync(Func &&callable, Args &&...args) {
if (QThread::currentThread() == CoreModel::getInstance()->thread()) {
bool end = false;
postCoreAsync([&end, callable, args...]() mutable {
QMetaObject::invokeMethod(App::getInstance(), callable, args..., Qt::DirectConnection);
end = true;
});
while (!end)
qApp->processEvents();
} else {
QMetaObject::invokeMethod(App::getInstance(), callable, Qt::DirectConnection);
}
}
template <typename Func, typename... Args>
static auto postModelSync(Func &&callable, Args &&...args) {
if (QThread::currentThread() != CoreModel::getInstance()->thread()) {
bool end = false;
postModelAsync([&end, callable, args...]() mutable {
QMetaObject::invokeMethod(CoreModel::getInstance().get(), callable, args..., Qt::DirectConnection);
end = true;
});
while (!end)
qApp->processEvents();
} else {
QMetaObject::invokeMethod(CoreModel::getInstance().get(), callable, Qt::DirectConnection);
}
}
template <typename Func, typename... Args>
static auto postModelBlock(Func &&callable, Args &&...args) {
if (QThread::currentThread() != CoreModel::getInstance()->thread()) {
QMetaObject::invokeMethod(CoreModel::getInstance().get(), callable, args..., Qt::BlockingQueuedConnection);
} else {
QMetaObject::invokeMethod(CoreModel::getInstance().get(), callable, Qt::DirectConnection);
}
}
void clean();
void init();
void initCore();
void initLocale();
void initCppInterfaces();
void initFonts();
void restart();
bool autoStartEnabled();
void setSysTrayIcon();
QSystemTrayIcon *getSystemTrayIcon() const {
return mSystemTrayIcon;
}
void updateSysTrayCount(int n);
QLocale getLocale();
void onLoggerInitialized();
void sendCommand();
bool getCoreStarted() const;
void setCoreStarted(bool started);
QQuickWindow *getCallsWindow();
Q_INVOKABLE void handleAppActivity();
QQuickWindow *getOrCreateCallsWindow(QVariant callGui = QVariant());
void setCallsWindowProperty(const char *id, QVariant property);
void closeCallsWindow();
QQuickWindow *getMainWindow() const;
void setMainWindow(QQuickWindow *data);
QQuickWindow *getLastActiveWindow() const;
void setLastActiveWindow(QQuickWindow *data);
QSharedPointer<AccountList> getAccountList() const;
void setAccountList(QSharedPointer<AccountList> data);
Q_INVOKABLE AccountList *getAccounts() const;
QSharedPointer<CallList> getCallList() const;
void setCallList(QSharedPointer<CallList> data);
Q_INVOKABLE CallList *getCalls() const;
QSharedPointer<SettingsCore> getSettings() const;
void onExitOnCloseChanged(); // Can be used for UniqueConnection
void onAuthenticationRequested(const std::shared_ptr<linphone::Core> &core,
const std::shared_ptr<linphone::AuthInfo> &authInfo,
linphone::AuthMethod method);
QString getShortApplicationVersion();
QString getGitBranchName();
QString getSdkVersion();
QString getQtVersion() const;
Q_INVOKABLE void checkForUpdate(bool requestedByUser = false);
ChatGui *getCurrentChat() const;
void setCurrentChat(ChatGui *chat);
float getScreenRatio() const;
Q_INVOKABLE void setScreenRatio(float ratio);
#ifdef Q_OS_LINUX
Q_INVOKABLE void exportDesktopFile();
QString getApplicationPath() const;
bool generateDesktopFile(const QString &confPath, bool remove, bool openInBackground);
#elif defined(Q_OS_MACOS)
bool event(QEvent *event) override;
#endif
QQmlApplicationEngine *mEngine = nullptr;
bool notify(QObject *receiver, QEvent *event) override;
enum class StatusCode { gRestartCode = 1000, gDeleteDataCode = 1001 };
signals:
void mainWindowChanged();
void coreStartedChanged(bool coreStarted);
void accountsChanged();
void defaultAccountChanged();
void callsChanged();
void currentDateChanged();
void currentChatChanged();
// void executeCommand(QString command);
private:
void createCommandParser();
QAction *createMarkAsReadAction(QQuickWindow *window);
void setMacOSDockActions(); // Should only be called on MacOS
void setAutoStart(bool enabled);
void setLocale(QString configLocale);
QCommandLineParser *mParser = nullptr;
Thread *mLinphoneThread = nullptr;
Notifier *mNotifier = nullptr;
EventCountNotifier *mEventCountNotifier = nullptr;
QSystemTrayIcon *mSystemTrayIcon = nullptr;
QQuickWindow *mMainWindow = nullptr;
QQuickWindow *mCallsWindow = nullptr;
QQuickWindow *mLastActiveWindow = nullptr;
// Holds the current chat displayed in the view
// to know if we need to display the notification
ChatGui *mCurrentChat = nullptr;
QSharedPointer<SettingsCore> mSettings;
QSharedPointer<AccountList> mAccountList;
QSharedPointer<CallList> mCallList;
QSharedPointer<SafeConnection<App, CoreModel>> mCoreModelConnection;
QSharedPointer<SafeConnection<App, CliModel>> mCliModelConnection;
bool mAutoStart = false;
bool mCoreStarted = false;
bool mIsRestarting = false;
bool mPossiblyLookForAddedAccount = false;
QLocale mLocale = QLocale::system();
DefaultTranslatorCore *mTranslatorCore = nullptr;
DefaultTranslatorCore *mDefaultTranslatorCore = nullptr;
QTimer mDateUpdateTimer;
QDate mCurrentDate;
float mScreenRatio = 1;
DECLARE_ABSTRACT_OBJECT
};

View file

@ -0,0 +1,137 @@
list(APPEND _LINPHONEAPP_SOURCES
core/account/AccountCore.cpp
core/account/AccountGui.cpp
core/account/AccountList.cpp
core/account/AccountProxy.cpp
core/account/AccountDeviceCore.cpp
core/account/AccountDeviceGui.cpp
core/account/AccountDeviceList.cpp
core/account/AccountDeviceProxy.cpp
core/App.cpp
core/call/CallCore.cpp
core/call/CallGui.cpp
core/call/CallList.cpp
core/call/CallProxy.cpp
core/call-history/CallHistoryCore.cpp
core/call-history/CallHistoryGui.cpp
core/call-history/CallHistoryList.cpp
core/call-history/CallHistoryProxy.cpp
core/camera/CameraGui.cpp
core/camera/CameraDummy.cpp
core/camera/PreviewManager.cpp
core/chat/ChatCore.cpp
core/chat/ChatGui.cpp
core/chat/ChatList.cpp
core/chat/ChatProxy.cpp
core/chat/message/ChatMessageCore.cpp
core/chat/message/ChatMessageGui.cpp
core/chat/message/EventLogCore.cpp
core/chat/message/EventLogGui.cpp
core/chat/message/EventLogList.cpp
core/chat/message/EventLogProxy.cpp
core/chat/message/content/ChatMessageContentCore.cpp
core/chat/message/content/ChatMessageContentGui.cpp
core/chat/message/content/ChatMessageContentList.cpp
core/chat/message/content/ChatMessageContentProxy.cpp
core/chat/files/ChatMessageFileList.cpp
core/chat/files/ChatMessageFileProxy.cpp
core/chat/message/imdn/ImdnStatusList.cpp
core/chat/message/imdn/ImdnStatusProxy.cpp
core/emoji/EmojiList.cpp
core/emoji/EmojiModel.cpp
core/emoji/EmojiProxy.cpp
core/event-count-notifier/AbstractEventCountNotifier.cpp
core/fps-counter/FPSCounter.cpp
core/friend/FriendCore.cpp
core/friend/FriendGui.cpp
core/logger/QtLogger.cpp
core/login/LoginPage.cpp
core/notifier/Notifier.cpp
core/path/Paths.cpp
core/phone-number/PhoneNumber.cpp
core/phone-number/PhoneNumberList.cpp
core/phone-number/PhoneNumberProxy.cpp
core/register/RegisterPage.cpp
core/search/MagicSearchList.cpp
core/search/MagicSearchProxy.cpp
core/setting/SettingsCore.cpp
core/proxy/ListProxy.cpp
core/proxy/Proxy.cpp
core/proxy/SortFilterProxy.cpp
core/proxy/LimitProxy.cpp
core/variant/VariantList.cpp
core/conference/ConferenceCore.cpp
core/conference/ConferenceGui.cpp
core/conference/ConferenceInfoCore.cpp
core/conference/ConferenceInfoGui.cpp
core/conference/ConferenceInfoList.cpp
core/conference/ConferenceInfoProxy.cpp
core/timezone/TimeZoneList.cpp
core/timezone/TimeZoneProxy.cpp
core/timezone/TimeZone.cpp
core/translator/DefaultTranslatorCore.cpp
core/participant/ParticipantCore.cpp
core/participant/ParticipantGui.cpp
core/participant/ParticipantDeviceCore.cpp
core/participant/ParticipantDeviceGui.cpp
core/participant/ParticipantDeviceList.cpp
core/participant/ParticipantDeviceProxy.cpp
core/participant/ParticipantList.cpp
core/participant/ParticipantProxy.cpp
core/participant/ParticipantInfoList.cpp
core/participant/ParticipantInfoProxy.cpp
core/screen/ScreenList.cpp
core/screen/ScreenProxy.cpp
core/sound-player/SoundPlayerCore.cpp
core/sound-player/SoundPlayerGui.cpp
core/recorder/RecorderCore.cpp
core/recorder/RecorderGui.cpp
core/videoSource/VideoSourceDescriptorCore.cpp
core/videoSource/VideoSourceDescriptorGui.cpp
core/address-books/ldap/LdapCore.cpp
core/address-books/ldap/LdapGui.cpp
core/address-books/ldap/LdapProxy.cpp
core/address-books/ldap/LdapList.cpp
core/address-books/carddav/CarddavCore.cpp
core/address-books/carddav/CarddavGui.cpp
core/address-books/carddav/CarddavProxy.cpp
core/address-books/carddav/CarddavList.cpp
core/payload-type/PayloadTypeCore.cpp
core/payload-type/DownloadablePayloadTypeCore.cpp
core/payload-type/PayloadTypeGui.cpp
core/payload-type/PayloadTypeProxy.cpp
core/payload-type/PayloadTypeList.cpp
)
## Single Application
if(APPLE OR WIN32)
list(APPEND _LINPHONEAPP_SOURCES core/singleapplication/singleapplication.cpp
core/singleapplication/singleapplication_p.cpp)
else() # Use QDBus for Linux
list(APPEND _LINPHONEAPP_SOURCES
core/singleapplication/singleapplication.h #Added for Moc
core/singleapplication/SingleApplicationDBusPrivate.hpp
core/singleapplication/SingleApplicationDBus.cpp)
endif()
if(APPLE)
list(APPEND _LINPHONEAPP_SOURCES core/event-count-notifier/EventCountNotifierMacOs.m)
else()
list(APPEND _LINPHONEAPP_SOURCES core/event-count-notifier/EventCountNotifierSystemTrayIcon.cpp)
endif()
set(_LINPHONEAPP_SOURCES ${_LINPHONEAPP_SOURCES} PARENT_SCOPE)

View file

@ -0,0 +1,922 @@
/*
* Copyright (c) 2010-2024 Belledonne Communications SARL.
*
* This file is part of linphone-desktop
* (see https://www.linphone.org).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "AccountCore.hpp"
#include "core/App.hpp"
#include "model/object/VariantObject.hpp"
#include "model/tool/ToolModel.hpp"
#include "tool/Utils.hpp"
#include "tool/thread/SafeConnection.hpp"
#include <QHostInfo>
DEFINE_ABSTRACT_OBJECT(AccountCore)
QSharedPointer<AccountCore> AccountCore::create(const std::shared_ptr<linphone::Account> &account) {
auto model = QSharedPointer<AccountCore>(new AccountCore(account), &QObject::deleteLater);
model->moveToThread(App::getInstance()->thread());
model->setSelf(model);
return model;
}
AccountCore::AccountCore(const std::shared_ptr<linphone::Account> &account) : QObject(nullptr) {
App::getInstance()->mEngine->setObjectOwnership(this, QQmlEngine::CppOwnership);
// Should be call from model Thread
mustBeInLinphoneThread(getClassName());
// Init data
auto address = account->getContactAddress();
mContactAddress = address ? Utils::coreStringToAppString(account->getContactAddress()->asStringUriOnly()) : "";
auto params = account->getParams()->clone();
auto identityAddress = params->getIdentityAddress();
mIdentityAddress = identityAddress ? Utils::coreStringToAppString(identityAddress->asStringUriOnly()) : "";
mPictureUri = Utils::coreStringToAppString(params->getPictureUri());
mRegistrationState = LinphoneEnums::fromLinphone(account->getState());
mIsDefaultAccount = CoreModel::getInstance()->getCore()->getDefaultAccount() == account;
mUnreadNotifications = account->getMissedCallsCount() + account->getUnreadChatMessageCount();
mDisplayName = Utils::coreStringToAppString(identityAddress->getDisplayName());
if (mDisplayName.isEmpty()) {
mDisplayName = ToolModel::getDisplayName(identityAddress);
auto copyAddress = identityAddress->clone();
copyAddress->setDisplayName(Utils::appStringToCoreString(mDisplayName));
params->setIdentityAddress(copyAddress);
account->setParams(params);
}
mRegisterEnabled = params->registerEnabled();
mMwiServerAddress =
params->getMwiServerAddress() ? Utils::coreStringToAppString(params->getMwiServerAddress()->asString()) : "";
mTransports << "UDP"
<< "TCP"
<< "TLS"
<< "DTLS";
mTransport = LinphoneEnums::toString(LinphoneEnums::fromLinphone(params->getTransport()));
mRegistrarUri =
params->getServerAddress() ? Utils::coreStringToAppString(params->getServerAddress()->asString()) : "";
auto routesAddresses = params->getRoutesAddresses();
mOutboundProxyUri =
routesAddresses.empty() ? "" : Utils::coreStringToAppString(routesAddresses.front()->asString());
auto policy = params->getNatPolicy() ? params->getNatPolicy() : account->getCore()->createNatPolicy();
mStunServer = Utils::coreStringToAppString(policy->getStunServer());
mIceEnabled = policy->iceEnabled();
mAvpfEnabled = account->avpfEnabled();
mBundleModeEnabled = params->rtpBundleEnabled();
mExpire = params->getExpires();
mConferenceFactoryAddress = params->getConferenceFactoryAddress()
? Utils::coreStringToAppString(params->getConferenceFactoryAddress()->asString())
: "";
mAudioVideoConferenceFactoryAddress =
params->getAudioVideoConferenceFactoryAddress()
? Utils::coreStringToAppString(params->getAudioVideoConferenceFactoryAddress()->asString())
: "";
mLimeServerUrl = Utils::coreStringToAppString(params->getLimeServerUrl());
mCcmpServerUrl = Utils::coreStringToAppString(params->getCcmpServerUrl());
// Add listener
mAccountModel = Utils::makeQObject_ptr<AccountModel>(account); // OK
mAccountModel->setSelf(mAccountModel);
mExplicitPresence = LinphoneEnums::fromString(
Utils::coreStringToAppString(CoreModel::getInstance()->getCore()->getConfig()->getString(
ToolModel::configAccountSection(account), "explicit_presence", "")));
mPresenceNote = Utils::coreStringToAppString(CoreModel::getInstance()->getCore()->getConfig()->getString(
ToolModel::configAccountSection(account), "presence_note", ""));
mMaxPresenceNoteSize = CoreModel::getInstance()->getCore()->getConfig()->getInt(
ToolModel::configAccountSection(account), "max_presence_note_size", 140);
mPresence = mAccountModel->getPresence();
mNotificationsAllowed = mAccountModel->getNotificationsAllowed();
mDialPlan = Utils::createDialPlanVariant("", " ");
mDialPlans << mDialPlan;
for (auto dialPlan : linphone::Factory::get()->getDialPlans()) {
mDialPlans << Utils::createDialPlanVariant(
Utils::coreStringToAppString(dialPlan->getFlag()),
Utils::coreStringToAppString(dialPlan->getCountry() + " | +" + dialPlan->getCountryCallingCode()));
if (dialPlan->getCountryCallingCode() == account->getParams()->getInternationalPrefix()) {
mDialPlan = mDialPlans.last().toMap();
}
}
mVoicemailAddress =
params->getVoicemailAddress() ? Utils::coreStringToAppString(params->getVoicemailAddress()->asString()) : "";
INIT_CORE_MEMBER(VoicemailCount, mAccountModel)
INIT_CORE_MEMBER(ShowMwi, mAccountModel)
}
AccountCore::~AccountCore() {
mustBeInMainThread("~" + getClassName());
if (mAccountModel) emit mAccountModel->removeListener();
}
AccountCore::AccountCore(const AccountCore &accountCore) {
mContactAddress = accountCore.mContactAddress;
mIdentityAddress = accountCore.mIdentityAddress;
mPictureUri = accountCore.mPictureUri;
mDisplayName = accountCore.mDisplayName;
mDialPlans = accountCore.mDialPlans;
mDialPlan = accountCore.mDialPlan;
mRegisterEnabled = accountCore.mRegisterEnabled;
mIsDefaultAccount = accountCore.mIsDefaultAccount;
mRegistrationState = accountCore.mRegistrationState;
mUnreadNotifications = accountCore.mUnreadNotifications;
mUnreadCallNotifications = accountCore.mUnreadCallNotifications;
mUnreadMessageNotifications = accountCore.mUnreadMessageNotifications;
mDevices = accountCore.mDevices;
mNotificationsAllowed = accountCore.mNotificationsAllowed;
mMwiServerAddress = accountCore.mMwiServerAddress;
mVoicemailAddress = accountCore.mVoicemailAddress;
mTransport = accountCore.mTransport;
mTransports = accountCore.mTransports;
mRegistrarUri = accountCore.mRegistrarUri;
mOutboundProxyUri = accountCore.mOutboundProxyUri;
mStunServer = accountCore.mStunServer;
mIceEnabled = accountCore.mIceEnabled;
mAvpfEnabled = accountCore.mAvpfEnabled;
mBundleModeEnabled = accountCore.mBundleModeEnabled;
mExpire = accountCore.mExpire;
mConferenceFactoryAddress = accountCore.mConferenceFactoryAddress;
mAudioVideoConferenceFactoryAddress = accountCore.mAudioVideoConferenceFactoryAddress;
mLimeServerUrl = accountCore.mLimeServerUrl;
mCcmpServerUrl = accountCore.mCcmpServerUrl;
}
void AccountCore::setSelf(QSharedPointer<AccountCore> me) {
mAccountModelConnection = SafeConnection<AccountCore, AccountModel>::create(me, mAccountModel);
mAccountModelConnection->makeConnectToModel(
&AccountModel::registrationStateChanged, [this](const std::shared_ptr<linphone::Account> &account,
linphone::RegistrationState state, const std::string &message) {
mAccountModelConnection->invokeToCore(
[this, account, state, message]() { onRegistrationStateChanged(account, state, message); });
});
mAccountModelConnection->makeConnectToModel(
&AccountModel::conferenceInformationUpdated,
[this](const std::shared_ptr<linphone::Account> &account,
const std::list<std::shared_ptr<linphone::ConferenceInfo>> &infos) {
mAccountModelConnection->invokeToCore([this]() { emit conferenceInformationUpdated(); });
});
// From Model
mAccountModelConnection->makeConnectToModel(&AccountModel::defaultAccountChanged, [this](bool isDefault) {
mAccountModelConnection->invokeToCore([this, isDefault]() { onDefaultAccountChanged(isDefault); });
});
mAccountModelConnection->makeConnectToModel(&AccountModel::pictureUriChanged, [this](QString uri) {
mAccountModelConnection->invokeToCore([this, uri]() { onPictureUriChanged(uri); });
});
mAccountModelConnection->makeConnectToModel(
&AccountModel::unreadNotificationsChanged, [this](int unreadMessagesCount, int unreadCallsCount) {
mAccountModelConnection->invokeToCore([this, unreadMessagesCount, unreadCallsCount]() {
setUnreadNotifications(unreadMessagesCount + unreadCallsCount);
setUnreadCallNotifications(unreadCallsCount);
setUnreadMessageNotifications(unreadMessagesCount);
});
});
mAccountModelConnection->makeConnectToModel(&AccountModel::displayNameChanged, [this](QString displayName) {
mAccountModelConnection->invokeToCore([this, displayName]() { onDisplayNameChanged(displayName); });
});
mAccountModelConnection->makeConnectToModel(&AccountModel::dialPlanChanged, [this](int index) {
auto dialPlan = mDialPlans[index + 1].toMap();
mAccountModelConnection->invokeToCore([this, dialPlan]() { onDialPlanChanged(dialPlan); });
});
mAccountModelConnection->makeConnectToModel(&AccountModel::registerEnabledChanged, [this](bool enabled) {
mAccountModelConnection->invokeToCore([this, enabled]() { onRegisterEnabledChanged(enabled); });
});
mAccountModelConnection->makeConnectToModel(&AccountModel::notificationsAllowedChanged, [this](bool value) {
mAccountModelConnection->invokeToCore([this, value]() { onNotificationsAllowedChanged(value); });
});
mAccountModelConnection->makeConnectToModel(&AccountModel::mwiServerAddressChanged, [this](QString value) {
mAccountModelConnection->invokeToCore([this, value]() { onMwiServerAddressChanged(value); });
});
mAccountModelConnection->makeConnectToModel(&AccountModel::voicemailAddressChanged, [this](QString value) {
mAccountModelConnection->invokeToCore([this, value]() { onVoicemailAddressChanged(value); });
});
mAccountModelConnection->makeConnectToModel(&AccountModel::transportChanged, [this](linphone::TransportType value) {
mAccountModelConnection->invokeToCore(
[this, value]() { onTransportChanged(LinphoneEnums::toString(LinphoneEnums::fromLinphone(value))); });
});
mAccountModelConnection->makeConnectToModel(&AccountModel::registrarUriChanged, [this](QString value) {
mAccountModelConnection->invokeToCore([this, value]() { onRegistrarUriChanged(value); });
});
mAccountModelConnection->makeConnectToModel(&AccountModel::outboundProxyUriChanged, [this](QString value) {
mAccountModelConnection->invokeToCore([this, value]() { onOutboundProxyUriChanged(value); });
});
mAccountModelConnection->makeConnectToModel(&AccountModel::stunServerChanged, [this](QString value) {
mAccountModelConnection->invokeToCore([this, value]() { onStunServerChanged(value); });
});
mAccountModelConnection->makeConnectToModel(&AccountModel::iceEnabledChanged, [this](bool value) {
mAccountModelConnection->invokeToCore([this, value]() { onIceEnabledChanged(value); });
});
mAccountModelConnection->makeConnectToModel(&AccountModel::avpfEnabledChanged, [this](bool value) {
mAccountModelConnection->invokeToCore([this, value]() { onAvpfEnabledChanged(value); });
});
mAccountModelConnection->makeConnectToModel(&AccountModel::bundleModeEnabledChanged, [this](bool value) {
mAccountModelConnection->invokeToCore([this, value]() { onBundleModeEnabledChanged(value); });
});
mAccountModelConnection->makeConnectToModel(&AccountModel::expireChanged, [this](int value) {
mAccountModelConnection->invokeToCore([this, value]() { onExpireChanged(value); });
});
mAccountModelConnection->makeConnectToModel(&AccountModel::conferenceFactoryAddressChanged, [this](QString value) {
mAccountModelConnection->invokeToCore([this, value]() { onConferenceFactoryAddressChanged(value); });
});
mAccountModelConnection->makeConnectToModel(&AccountModel::audioVideoConferenceFactoryAddressChanged,
[this](QString value) {
mAccountModelConnection->invokeToCore([this, value]() {
onAudioVideoConferenceFactoryAddressChanged(value);
});
});
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(); }); });
mAccountModelConnection->makeConnectToModel(
&AccountModel::presenceChanged, [this](LinphoneEnums::Presence presence, bool userInitiated) {
mAccountModelConnection->invokeToCore([this, presence, userInitiated]() {
if (userInitiated) mExplicitPresence = presence;
else mExplicitPresence = LinphoneEnums::Presence::Undefined;
mPresence = presence;
emit presenceChanged();
});
});
// From GUI
mAccountModelConnection->makeConnectToCore(&AccountCore::lSetPictureUri, [this](QString uri) {
mAccountModelConnection->invokeToModel([this, uri]() { mAccountModel->setPictureUri(uri); });
});
mAccountModelConnection->makeConnectToCore(&AccountCore::lSetDefaultAccount, [this]() {
mAccountModelConnection->invokeToModel([this]() { mAccountModel->setDefault(); });
});
mAccountModelConnection->makeConnectToCore(&AccountCore::lResetMissedCalls, [this]() {
mAccountModelConnection->invokeToModel([this]() { mAccountModel->resetMissedCallsCount(); });
});
mAccountModelConnection->makeConnectToCore(&AccountCore::lResetUnreadMessages, [this]() {
mAccountModelConnection->invokeToModel([this]() {
auto chatRooms = mAccountModel->getChatRooms();
for (auto const &chatRoom : chatRooms) {
chatRoom->markAsRead();
}
});
});
mAccountModelConnection->makeConnectToCore(&AccountCore::lRefreshNotifications, [this]() {
mAccountModelConnection->invokeToModel([this]() { mAccountModel->refreshUnreadNotifications(); });
});
mAccountModelConnection->makeConnectToCore(&AccountCore::lSetDisplayName, [this](QString displayName) {
mAccountModelConnection->invokeToModel([this, displayName]() { mAccountModel->setDisplayName(displayName); });
});
mAccountModelConnection->makeConnectToCore(&AccountCore::lSetDialPlan, [this](QVariantMap dialPlan) {
auto dialPlanIndex = getDialPlanIndex(dialPlan);
mAccountModelConnection->invokeToModel(
[this, dialPlanIndex]() { mAccountModel->setDialPlan(dialPlanIndex - 1); });
});
mAccountModelConnection->makeConnectToCore(&AccountCore::lSetRegisterEnabled, [this](bool enabled) {
mAccountModelConnection->invokeToModel([this, enabled]() { mAccountModel->setRegisterEnabled(enabled); });
});
mAccountModelConnection->makeConnectToCore(&AccountCore::lSetNotificationsAllowed, [this](bool value) {
mAccountModelConnection->invokeToModel([this, value]() { mAccountModel->setNotificationsAllowed(value); });
});
mAccountModelConnection->makeConnectToCore(
&AccountCore::lSetPresence, [this](LinphoneEnums::Presence presence, bool userInitiated, bool resetToAuto) {
mAccountModelConnection->invokeToModel(
[this, presence, userInitiated, resetToAuto, presenceNote = mPresenceNote]() {
mAccountModel->setPresence(presence, userInitiated, resetToAuto, presenceNote);
});
});
DEFINE_CORE_GET_CONNECT(mAccountModelConnection, AccountCore, AccountModel, mAccountModel, int, voicemailCount,
VoicemailCount)
DEFINE_CORE_GET_CONNECT(mAccountModelConnection, AccountCore, AccountModel, mAccountModel, int, showMwi, ShowMwi)
// DEFINE_CORE_GETSET_CONNECT(mAccountModelConnection, AccountCore, AccountModel, mAccountModel, QString,
// voicemailAddress, VoicemailAddress)
mAccountModelConnection->makeConnectToModel(&AccountModel::voicemailAddressChanged, [this](QString value) {
mAccountModelConnection->invokeToCore([this, value]() { setVoicemailAddress(value); });
});
mCoreModelConnection = SafeConnection<AccountCore, CoreModel>::create(me, CoreModel::getInstance());
mCoreModelConnection->makeConnectToModel(&CoreModel::messageReadInChatRoom,
[this] { mAccountModel->refreshUnreadNotifications(); });
mAccountModelConnection->makeConnectToModel(&AccountModel::setValueFailed, [this](const QString &errorMessage) {
mAccountModelConnection->invokeToCore([this, errorMessage]() { emit setValueFailed(errorMessage); });
});
}
void AccountCore::reset(const AccountCore &accountCore) {
setUnreadNotifications(accountCore.mUnreadNotifications);
setUnreadCallNotifications(accountCore.mUnreadCallNotifications);
setUnreadMessageNotifications(accountCore.mUnreadMessageNotifications);
setMwiServerAddress(accountCore.mMwiServerAddress);
setVoicemailAddress(accountCore.mVoicemailAddress);
setTransport(accountCore.mTransport);
setRegistrarUri(accountCore.mRegistrarUri);
setOutboundProxyUri(accountCore.mOutboundProxyUri);
setStunServer(accountCore.mStunServer);
setIceEnabled(accountCore.mIceEnabled);
setAvpfEnabled(accountCore.mAvpfEnabled);
setBundleModeEnabled(accountCore.mBundleModeEnabled);
setExpire(accountCore.mExpire);
setConferenceFactoryAddress(accountCore.mConferenceFactoryAddress);
setAudioVideoConferenceFactoryAddress(accountCore.mAudioVideoConferenceFactoryAddress);
setLimeServerUrl(accountCore.mLimeServerUrl);
setCcmpServerUrl(accountCore.mCcmpServerUrl);
}
const std::shared_ptr<AccountModel> &AccountCore::getModel() const {
return mAccountModel;
}
QString AccountCore::getContactAddress() const {
return mContactAddress;
}
QString AccountCore::getIdentityAddress() const {
return mIdentityAddress;
}
QString AccountCore::getPictureUri() const {
return mPictureUri;
}
LinphoneEnums::RegistrationState AccountCore::getRegistrationState() const {
return mRegistrationState;
}
bool AccountCore::getIsDefaultAccount() const {
return mIsDefaultAccount;
}
int AccountCore::getUnreadNotifications() const {
return mUnreadNotifications;
}
void AccountCore::setUnreadNotifications(int unread) {
if (mUnreadNotifications != unread) {
mUnreadNotifications = unread;
emit unreadNotificationsChanged(unread);
}
}
int AccountCore::getUnreadCallNotifications() const {
return mUnreadCallNotifications;
}
void AccountCore::setUnreadCallNotifications(int unread) {
if (mUnreadCallNotifications != unread) {
mUnreadCallNotifications = unread;
emit unreadCallNotificationsChanged(unread);
}
}
int AccountCore::getUnreadMessageNotifications() const {
return mUnreadMessageNotifications;
}
void AccountCore::setUnreadMessageNotifications(int unread) {
if (mUnreadMessageNotifications != unread) {
mUnreadMessageNotifications = unread;
emit unreadMessageNotificationsChanged(unread);
}
}
void AccountCore::onRegistrationStateChanged(const std::shared_ptr<linphone::Account> &account,
linphone::RegistrationState state,
const std::string &message) {
mRegistrationState = LinphoneEnums::fromLinphone(state);
qDebug() << log().arg(Q_FUNC_INFO) << mRegistrationState;
emit registrationStateChanged(Utils::coreStringToAppString(message));
}
void AccountCore::onDefaultAccountChanged(bool isDefault) {
if (mIsDefaultAccount != isDefault) {
mIsDefaultAccount = isDefault;
emit defaultAccountChanged(mIsDefaultAccount);
}
}
void AccountCore::onPictureUriChanged(QString uri) {
if (uri != mPictureUri) {
mPictureUri = uri;
emit pictureUriChanged();
}
}
void AccountCore::removeAccount() {
mAccountModelConnection->invokeToModel([this]() { mAccountModel->removeAccount(); });
}
QString AccountCore::getDisplayName() const {
return mDisplayName;
}
void AccountCore::onDisplayNameChanged(QString displayName) {
if (displayName != mDisplayName) {
mDisplayName = displayName;
emit displayNameChanged();
}
}
QVariantList AccountCore::getDialPlans() {
return mDialPlans;
}
QVariantMap AccountCore::getDialPlan() const {
return mDialPlan;
}
void AccountCore::onDialPlanChanged(QVariantMap dialPlan) {
if (dialPlan != mDialPlan) {
mDialPlan = dialPlan;
emit dialPlanChanged();
}
}
int AccountCore::getDialPlanIndex(QVariantMap dialPlanString) {
return mDialPlans.indexOf(dialPlanString);
}
QString AccountCore::getHumanReadableRegistrationState() const {
switch (mRegistrationState) {
case LinphoneEnums::RegistrationState::Ok:
//: "Connecté"
return tr("drawer_menu_account_connection_status_connected");
case LinphoneEnums::RegistrationState::Refreshing:
// "En cours de rafraîchissement…"
return tr("drawer_menu_account_connection_status_refreshing");
case LinphoneEnums::RegistrationState::Progress:
// "Connexion…"
return tr("drawer_menu_account_connection_status_progress");
case LinphoneEnums::RegistrationState::Failed:
// "Erreur"
return tr("drawer_menu_account_connection_status_failed");
case LinphoneEnums::RegistrationState::None:
case LinphoneEnums::RegistrationState::Cleared:
// "Désactivé"
return tr("drawer_menu_account_connection_status_cleared");
default:
return " ";
}
}
QColor AccountCore::getRegistrationColor() const {
mustBeInMainThread(log().arg(Q_FUNC_INFO));
switch (mRegistrationState) {
case LinphoneEnums::RegistrationState::Ok:
return Utils::getDefaultStyleColor("success_500_main");
case LinphoneEnums::RegistrationState::Refreshing:
return Utils::getDefaultStyleColor("main2_500_main");
case LinphoneEnums::RegistrationState::Progress:
return Utils::getDefaultStyleColor("main2_500_main");
case LinphoneEnums::RegistrationState::Failed:
return Utils::getDefaultStyleColor("danger_500_main");
case LinphoneEnums::RegistrationState::None:
case LinphoneEnums::RegistrationState::Cleared:
return Utils::getDefaultStyleColor("warning_600");
default:
return " ";
}
}
QUrl AccountCore::getRegistrationIcon() const {
mustBeInMainThread(log().arg(Q_FUNC_INFO));
return Utils::getRegistrationStateIcon(mRegistrationState);
}
QString AccountCore::getHumanReadableRegistrationStateExplained() const {
switch (mRegistrationState) {
case LinphoneEnums::RegistrationState::Ok:
//: "Vous êtes en ligne et joignable."
return tr("manage_account_status_connected_summary");
case LinphoneEnums::RegistrationState::Failed:
//: "Erreur de connexion, vérifiez vos paramètres."
return tr("manage_account_status_failed_summary");
case LinphoneEnums::RegistrationState::None:
case LinphoneEnums::RegistrationState::Cleared:
//: "Compte désactivé, vous ne recevrez ni appel ni message."
return tr("manage_account_status_cleared_summary");
default:
return " ";
}
}
bool AccountCore::getRegisterEnabled() const {
return mRegisterEnabled;
}
void AccountCore::onRegisterEnabledChanged(bool enabled) {
if (enabled != mRegisterEnabled) {
mRegisterEnabled = enabled;
emit registerEnabledChanged();
}
}
bool AccountCore::getNotificationsAllowed() {
return mNotificationsAllowed;
}
QString AccountCore::getMwiServerAddress() {
return mMwiServerAddress;
}
QString AccountCore::getVoicemailAddress() {
return mVoicemailAddress;
}
QStringList AccountCore::getTransports() {
return mTransports;
}
QString AccountCore::getTransport() {
return mTransport;
}
QString AccountCore::getRegistrarUri() {
return mRegistrarUri;
}
QString AccountCore::getOutboundProxyUri() {
return mOutboundProxyUri;
}
QString AccountCore::getStunServer() {
return mStunServer;
}
bool AccountCore::getIceEnabled() {
return mIceEnabled;
}
bool AccountCore::getAvpfEnabled() {
return mAvpfEnabled;
}
bool AccountCore::getBundleModeEnabled() {
return mBundleModeEnabled;
}
int AccountCore::getExpire() {
return mExpire;
}
QString AccountCore::getConferenceFactoryAddress() {
return mConferenceFactoryAddress;
}
QString AccountCore::getAudioVideoConferenceFactoryAddress() {
return mAudioVideoConferenceFactoryAddress;
}
QString AccountCore::getLimeServerUrl() {
return mLimeServerUrl;
}
QString AccountCore::getCcmpServerUrl() {
return mCcmpServerUrl;
}
void AccountCore::setMwiServerAddress(QString value) {
if (mMwiServerAddress != value) {
mMwiServerAddress = value;
emit mwiServerAddressChanged();
setIsSaved(false);
}
}
void AccountCore::setVoicemailAddress(QString value) {
if (mVoicemailAddress != value) {
mVoicemailAddress = value;
emit voicemailAddressChanged();
setIsSaved(false);
}
}
void AccountCore::setTransport(QString value) {
if (mTransport != value) {
mTransport = value;
emit transportChanged();
setIsSaved(false);
}
}
void AccountCore::setRegistrarUri(QString value) {
if (mRegistrarUri != value) {
mRegistrarUri = value;
emit registrarUriChanged();
setIsSaved(false);
}
}
void AccountCore::setOutboundProxyUri(QString value) {
if (mOutboundProxyUri != value) {
mOutboundProxyUri = value;
emit outboundProxyUriChanged();
setIsSaved(false);
}
}
void AccountCore::setStunServer(QString value) {
if (mStunServer != value) {
mStunServer = value;
emit stunServerChanged();
setIsSaved(false);
}
}
void AccountCore::setIceEnabled(bool value) {
if (mIceEnabled != value) {
mIceEnabled = value;
emit iceEnabledChanged();
setIsSaved(false);
}
}
void AccountCore::setAvpfEnabled(bool value) {
if (mAvpfEnabled != value) {
mAvpfEnabled = value;
emit avpfEnabledChanged();
setIsSaved(false);
}
}
void AccountCore::setBundleModeEnabled(bool value) {
if (mBundleModeEnabled != value) {
mBundleModeEnabled = value;
emit bundleModeEnabledChanged();
setIsSaved(false);
}
}
void AccountCore::setExpire(int value) {
if (mExpire != value) {
mExpire = value;
emit expireChanged();
setIsSaved(false);
}
}
void AccountCore::setConferenceFactoryAddress(QString value) {
if (mConferenceFactoryAddress != value) {
mConferenceFactoryAddress = value;
emit conferenceFactoryAddressChanged();
setIsSaved(false);
}
}
void AccountCore::setAudioVideoConferenceFactoryAddress(QString value) {
if (mAudioVideoConferenceFactoryAddress != value) {
mAudioVideoConferenceFactoryAddress = value;
emit audioVideoConferenceFactoryAddressChanged();
setIsSaved(false);
}
}
void AccountCore::setLimeServerUrl(QString value) {
if (mLimeServerUrl != value) {
mLimeServerUrl = value;
emit limeServerUrlChanged();
setIsSaved(false);
}
}
void AccountCore::setCcmpServerUrl(QString value) {
if (mCcmpServerUrl != value) {
mCcmpServerUrl = value;
emit ccmpServerUrlChanged();
setIsSaved(false);
}
}
bool AccountCore::isSaved() const {
return mIsSaved;
}
void AccountCore::setIsSaved(bool saved) {
if (mIsSaved != saved) {
mIsSaved = saved;
emit isSavedChanged();
}
}
void AccountCore::onNotificationsAllowedChanged(bool value) {
if (value != mNotificationsAllowed) {
mNotificationsAllowed = value;
emit notificationsAllowedChanged();
}
}
void AccountCore::onMwiServerAddressChanged(QString value) {
if (value != mMwiServerAddress) {
mMwiServerAddress = value;
emit mwiServerAddressChanged();
}
}
void AccountCore::onVoicemailAddressChanged(QString value) {
if (value != mVoicemailAddress) {
mVoicemailAddress = value;
emit voicemailAddressChanged();
}
}
void AccountCore::onTransportChanged(QString value) {
if (value != mTransport) {
mTransport = value;
emit transportChanged();
}
}
void AccountCore::onRegistrarUriChanged(QString value) {
if (value != mRegistrarUri) {
mRegistrarUri = value;
emit registrarUriChanged();
}
}
void AccountCore::onOutboundProxyUriChanged(QString value) {
if (value != mOutboundProxyUri) {
mOutboundProxyUri = value;
emit outboundProxyUriChanged();
}
}
void AccountCore::onStunServerChanged(QString value) {
if (value != mStunServer) {
mStunServer = value;
emit stunServerChanged();
}
}
void AccountCore::onIceEnabledChanged(bool value) {
if (value != mIceEnabled) {
mIceEnabled = value;
emit iceEnabledChanged();
}
}
void AccountCore::onAvpfEnabledChanged(bool value) {
if (value != mAvpfEnabled) {
mAvpfEnabled = value;
emit avpfEnabledChanged();
}
}
void AccountCore::onBundleModeEnabledChanged(bool value) {
if (value != mBundleModeEnabled) {
mBundleModeEnabled = value;
emit bundleModeEnabledChanged();
}
}
void AccountCore::onExpireChanged(int value) {
if (value != mExpire) {
mExpire = value;
emit expireChanged();
}
}
void AccountCore::onConferenceFactoryAddressChanged(QString value) {
if (value != mConferenceFactoryAddress) {
mConferenceFactoryAddress = value;
emit conferenceFactoryAddressChanged();
}
}
void AccountCore::onAudioVideoConferenceFactoryAddressChanged(QString value) {
if (value != mAudioVideoConferenceFactoryAddress) {
mAudioVideoConferenceFactoryAddress = value;
emit audioVideoConferenceFactoryAddressChanged();
}
if (mIsDefaultAccount) {
SettingsModel::getInstance()->setDisableMeetingsFeature(value.isEmpty());
}
}
void AccountCore::onLimeServerUrlChanged(QString value) {
if (value != mLimeServerUrl) {
mLimeServerUrl = value;
emit limeServerUrlChanged();
}
}
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);
LinphoneEnums::TransportType transport;
LinphoneEnums::fromString(mTransport, &transport);
model->setTransport(LinphoneEnums::toLinphone(transport), true);
model->setRegistrarUri(mRegistrarUri);
model->setOutboundProxyUri(mOutboundProxyUri);
model->setStunServer(mStunServer);
model->setIceEnabled(mIceEnabled);
model->setAvpfEnabled(mAvpfEnabled);
model->setBundleModeEnabled(mBundleModeEnabled);
model->setExpire(mExpire);
model->setConferenceFactoryAddress(mConferenceFactoryAddress);
model->setAudioVideoConferenceFactoryAddress(mAudioVideoConferenceFactoryAddress);
model->setLimeServerUrl(mLimeServerUrl);
model->setCcmpServerUrl(mCcmpServerUrl);
model->setVoicemailAddress(mVoicemailAddress);
}
void AccountCore::writeFromModel(const std::shared_ptr<AccountModel> &model) {
mustBeInLinphoneThread(getClassName() + Q_FUNC_INFO);
setUnreadCallNotifications(model->getMissedCallsCount());
setUnreadMessageNotifications(model->getUnreadMessagesCount());
onMwiServerAddressChanged(model->getMwiServerAddress());
onTransportChanged(LinphoneEnums::toString(LinphoneEnums::fromLinphone(model->getTransport())));
onRegistrarUriChanged(model->getRegistrarUri());
onOutboundProxyUriChanged(model->getOutboundProxyUri());
onStunServerChanged(model->getStunServer());
onIceEnabledChanged(model->getIceEnabled());
onAvpfEnabledChanged(model->getAvpfEnabled());
onBundleModeEnabledChanged(model->getBundleModeEnabled());
onExpireChanged(model->getExpire());
onConferenceFactoryAddressChanged(model->getConferenceFactoryAddress());
onAudioVideoConferenceFactoryAddressChanged(model->getAudioVideoConferenceFactoryAddress());
onLimeServerUrlChanged(model->getLimeServerUrl());
onCcmpServerUrlChanged(model->getCcmpServerUrl());
onVoicemailAddressChanged(model->getVoicemailAddress());
}
void AccountCore::save() {
mustBeInMainThread(getClassName() + Q_FUNC_INFO);
if (mAccountModel) {
AccountCore *thisCopy = new AccountCore(*this);
mAccountModelConnection->invokeToModel([this, thisCopy] {
mustBeInLinphoneThread(getClassName() + Q_FUNC_INFO);
thisCopy->writeIntoModel(mAccountModel);
thisCopy->deleteLater();
mAccountModelConnection->invokeToCore([this, thisCopy]() {
setIsSaved(true);
undo(); // Reset new values because some values can be invalid and not changed.
});
});
}
}
void AccountCore::undo() {
if (mAccountModel) {
mAccountModelConnection->invokeToModel([this] {
AccountCore *account = new AccountCore(*this);
account->writeFromModel(mAccountModel);
account->moveToThread(App::getInstance()->thread());
mAccountModelConnection->invokeToCore([this, account]() {
this->reset(*account);
account->deleteLater();
});
});
}
}
LinphoneEnums::Presence AccountCore::getPresence() {
return mPresence;
}
QColor AccountCore::getPresenceColor() {
return Utils::getPresenceColor(mPresence);
}
QUrl AccountCore::getPresenceIcon() {
return Utils::getPresenceIcon(mPresence);
}
QString AccountCore::getPresenceStatus() {
return Utils::getPresenceStatus(mPresence);
}
void AccountCore::resetToAutomaticPresence() {
emit lSetPresence(LinphoneEnums::Presence::Online, false, true);
}
LinphoneEnums::Presence AccountCore::getExplicitPresence() {
return mExplicitPresence;
}
void AccountCore::setPresenceNote(QString presenceNote) {
if (presenceNote != mPresenceNote) {
mPresenceNote = presenceNote;
emit lSetPresence(mPresence, mExplicitPresence != LinphoneEnums::Presence::Undefined, false);
}
}
QString AccountCore::getPresenceNote() {
return mPresenceNote;
}

View file

@ -0,0 +1,291 @@
/*
* Copyright (c) 2010-2024 Belledonne Communications SARL.
*
* This file is part of linphone-desktop
* (see https://www.linphone.org).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef ACCOUNT_CORE_H_
#define ACCOUNT_CORE_H_
#include "model/account/AccountModel.hpp"
#include "tool/LinphoneEnums.hpp"
#include "tool/thread/SafeConnection.hpp"
#include <QObject>
#include <QSharedPointer>
#include <linphone++/linphone.hh>
class AccountCore : public QObject, public AbstractObject {
Q_OBJECT
public:
Q_PROPERTY(QString contactAddress READ getContactAddress CONSTANT)
Q_PROPERTY(QString identityAddress READ getIdentityAddress CONSTANT)
Q_PROPERTY(QString pictureUri READ getPictureUri WRITE lSetPictureUri NOTIFY pictureUriChanged)
Q_PROPERTY(
LinphoneEnums::RegistrationState registrationState READ getRegistrationState NOTIFY registrationStateChanged)
Q_PROPERTY(bool isDefaultAccount READ getIsDefaultAccount NOTIFY defaultAccountChanged)
Q_PROPERTY(int unreadNotifications READ getUnreadNotifications NOTIFY unreadNotificationsChanged)
Q_PROPERTY(int unreadCallNotifications READ getUnreadCallNotifications NOTIFY unreadCallNotificationsChanged)
Q_PROPERTY(
int unreadMessageNotifications READ getUnreadMessageNotifications NOTIFY unreadMessageNotificationsChanged)
Q_PROPERTY(QString displayName READ getDisplayName WRITE lSetDisplayName NOTIFY displayNameChanged)
Q_PROPERTY(QVariantList dialPlans READ getDialPlans CONSTANT)
Q_PROPERTY(QVariantMap dialPlan READ getDialPlan WRITE lSetDialPlan NOTIFY dialPlanChanged)
Q_PROPERTY(
QString humaneReadableRegistrationState READ getHumanReadableRegistrationState NOTIFY registrationStateChanged)
Q_PROPERTY(QString humaneReadableRegistrationStateExplained READ getHumanReadableRegistrationStateExplained NOTIFY
registrationStateChanged)
Q_PROPERTY(bool registerEnabled READ getRegisterEnabled WRITE lSetRegisterEnabled NOTIFY registerEnabledChanged)
Q_PROPERTY(QVariantList devices MEMBER mDevices NOTIFY devicesChanged)
Q_PROPERTY(bool notificationsAllowed READ getNotificationsAllowed WRITE lSetNotificationsAllowed NOTIFY
notificationsAllowedChanged)
Q_PROPERTY(
QString mwiServerAddress READ getMwiServerAddress WRITE setMwiServerAddress NOTIFY mwiServerAddressChanged)
Q_PROPERTY(QStringList transports READ getTransports CONSTANT)
Q_PROPERTY(QString transport READ getTransport WRITE setTransport NOTIFY transportChanged)
Q_PROPERTY(QString registrarUri READ getRegistrarUri WRITE setRegistrarUri NOTIFY registrarUriChanged)
Q_PROPERTY(
QString outboundProxyUri READ getOutboundProxyUri WRITE setOutboundProxyUri NOTIFY outboundProxyUriChanged)
Q_PROPERTY(QString stunServer READ getStunServer WRITE setStunServer NOTIFY stunServerChanged)
Q_PROPERTY(bool iceEnabled READ getIceEnabled WRITE setIceEnabled NOTIFY iceEnabledChanged)
Q_PROPERTY(bool avpfEnabled READ getAvpfEnabled WRITE setAvpfEnabled NOTIFY avpfEnabledChanged)
Q_PROPERTY(
bool bundleModeEnabled READ getBundleModeEnabled WRITE setBundleModeEnabled NOTIFY bundleModeEnabledChanged)
Q_PROPERTY(int expire READ getExpire WRITE setExpire NOTIFY expireChanged)
Q_PROPERTY(QString conferenceFactoryAddress READ getConferenceFactoryAddress WRITE setConferenceFactoryAddress
NOTIFY conferenceFactoryAddressChanged)
Q_PROPERTY(QString audioVideoConferenceFactoryAddress READ getAudioVideoConferenceFactoryAddress WRITE
setAudioVideoConferenceFactoryAddress NOTIFY audioVideoConferenceFactoryAddressChanged)
Q_PROPERTY(QString limeServerUrl READ getLimeServerUrl WRITE setLimeServerUrl NOTIFY limeServerUrlChanged)
Q_PROPERTY(bool isSaved READ isSaved WRITE setIsSaved NOTIFY isSavedChanged)
Q_PROPERTY(
QString voicemailAddress READ getVoicemailAddress WRITE setVoicemailAddress NOTIFY voicemailAddressChanged)
Q_PROPERTY(LinphoneEnums::Presence presence READ getPresence WRITE lSetPresence NOTIFY presenceChanged)
Q_PROPERTY(QColor presenceColor READ getPresenceColor NOTIFY presenceChanged)
Q_PROPERTY(QUrl presenceIcon READ getPresenceIcon NOTIFY presenceChanged)
Q_PROPERTY(QString presenceStatus READ getPresenceStatus NOTIFY presenceChanged)
Q_PROPERTY(QColor registrationColor READ getRegistrationColor NOTIFY registrationStateChanged)
Q_PROPERTY(QUrl registrationIcon READ getRegistrationIcon NOTIFY registrationStateChanged)
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);
// Should be call from model Thread. Will be automatically in App thread after initialization
AccountCore(const std::shared_ptr<linphone::Account> &account);
~AccountCore();
AccountCore(const AccountCore &accountCore);
void setSelf(QSharedPointer<AccountCore> me);
void reset(const AccountCore &accountCore);
const std::shared_ptr<AccountModel> &getModel() const;
QString getContactAddress() const;
QString getIdentityAddress() const;
QString getPictureUri() const;
void onPictureUriChanged(QString uri);
LinphoneEnums::RegistrationState getRegistrationState() const;
bool getIsDefaultAccount() const;
int getUnreadNotifications() const;
void setUnreadNotifications(int unread);
int getUnreadCallNotifications() const;
void setUnreadCallNotifications(int unread);
int getUnreadMessageNotifications() const;
void setUnreadMessageNotifications(int unread);
void onRegistrationStateChanged(const std::shared_ptr<linphone::Account> &account,
linphone::RegistrationState state,
const std::string &message);
void onDefaultAccountChanged(bool isDefault);
Q_INVOKABLE void removeAccount();
QString getDisplayName() const;
void onDisplayNameChanged(QString displayName);
QVariantList getDialPlans();
int getDialPlanIndex(QVariantMap dialPlanString);
QVariantMap getDialPlan() const;
void onDialPlanChanged(QVariantMap internationalPrefix);
QString getHumanReadableRegistrationState() const;
QString getHumanReadableRegistrationStateExplained() const;
QColor getRegistrationColor() const;
QUrl getRegistrationIcon() const;
bool getRegisterEnabled() const;
void onRegisterEnabledChanged(bool enabled);
bool getNotificationsAllowed();
QString getMwiServerAddress();
QString getTransport();
QStringList getTransports();
QString getRegistrarUri();
QString getOutboundProxyUri();
QString getStunServer();
bool getIceEnabled();
bool getAvpfEnabled();
bool getBundleModeEnabled();
int getExpire();
QString getConferenceFactoryAddress();
QString getAudioVideoConferenceFactoryAddress();
QString getLimeServerUrl();
QString getVoicemailAddress();
QString getCcmpServerUrl();
void setMwiServerAddress(QString value);
void setTransport(QString value);
void setRegistrarUri(QString value);
void setOutboundProxyUri(QString value);
void setStunServer(QString value);
void setIceEnabled(bool value);
void setAvpfEnabled(bool value);
void setBundleModeEnabled(bool value);
void setExpire(int value);
void setConferenceFactoryAddress(QString value);
void setAudioVideoConferenceFactoryAddress(QString value);
void setLimeServerUrl(QString value);
void setVoicemailAddress(QString value);
void setCcmpServerUrl(QString value);
bool isSaved() const;
void setIsSaved(bool saved);
void onNotificationsAllowedChanged(bool value);
void onMwiServerAddressChanged(QString value);
void onVoicemailAddressChanged(QString value);
void onTransportChanged(QString value);
void onRegistrarUriChanged(QString value);
void onOutboundProxyUriChanged(QString value);
void onStunServerChanged(QString value);
void onIceEnabledChanged(bool value);
void onAvpfEnabledChanged(bool value);
void onBundleModeEnabledChanged(bool value);
void onExpireChanged(int value);
void onConferenceFactoryAddressChanged(QString value);
void onAudioVideoConferenceFactoryAddressChanged(QString value);
void onLimeServerUrlChanged(QString value);
void onCcmpServerUrlChanged(QString value);
DECLARE_CORE_GET(bool, showMwi, ShowMwi)
Q_INVOKABLE void save();
Q_INVOKABLE void undo();
QColor getPresenceColor();
QUrl getPresenceIcon();
QString getPresenceStatus();
LinphoneEnums::Presence getPresence();
Q_INVOKABLE void resetToAutomaticPresence();
LinphoneEnums::Presence getExplicitPresence();
void setPresenceNote(QString presenceNote);
QString getPresenceNote();
signals:
void pictureUriChanged();
void registrationStateChanged(const QString &message);
void conferenceInformationUpdated();
void defaultAccountChanged(bool isDefault);
void unreadNotificationsChanged(int unread);
void unreadCallNotificationsChanged(int unread);
void unreadMessageNotificationsChanged(int unread);
void displayNameChanged();
void dialPlanChanged();
void registerEnabledChanged();
void allAddressesChanged();
void devicesChanged();
void notificationsAllowedChanged();
void mwiServerAddressChanged();
void transportChanged();
void registrarUriChanged();
void outboundProxyUriChanged();
void stunServerChanged();
void iceEnabledChanged();
void avpfEnabledChanged();
void bundleModeEnabledChanged();
void expireChanged();
void conferenceFactoryAddressChanged();
void audioVideoConferenceFactoryAddressChanged();
void limeServerUrlChanged();
void removed();
void isSavedChanged();
void voicemailAddressChanged();
void presenceChanged();
void ccmpServerUrlChanged();
void setValueFailed(const QString &error);
// Account requests
void lSetPictureUri(QString pictureUri);
void lSetDefaultAccount();
void lResetMissedCalls();
void lResetUnreadMessages();
void lRefreshNotifications();
void lSetDisplayName(QString displayName);
void lSetDialPlan(QVariantMap internationalPrefix);
void lSetRegisterEnabled(bool enabled);
void lSetNotificationsAllowed(bool value);
void lSetPresence(LinphoneEnums::Presence presence, bool userInitiated = true, bool resetToAuto = false);
protected:
void writeIntoModel(std::shared_ptr<AccountModel> model) const;
void writeFromModel(const std::shared_ptr<AccountModel> &model);
private:
QString mContactAddress;
QString mIdentityAddress;
QString mPictureUri;
QString mDisplayName;
QVariantList mDialPlans;
QVariantMap mDialPlan;
bool mRegisterEnabled;
bool mIsDefaultAccount = false;
LinphoneEnums::RegistrationState mRegistrationState;
int mUnreadNotifications = 0;
int mUnreadCallNotifications = 0;
int mUnreadMessageNotifications = 0;
QVariantList mDevices;
bool mNotificationsAllowed;
QString mMwiServerAddress;
QString mTransport;
QStringList mTransports;
QString mRegistrarUri;
QString mOutboundProxyUri;
QString mStunServer;
bool mIceEnabled;
bool mAvpfEnabled;
bool mBundleModeEnabled;
int mExpire;
QString mConferenceFactoryAddress;
QString mAudioVideoConferenceFactoryAddress;
QString mLimeServerUrl;
QString mVoicemailAddress;
QString mCcmpServerUrl;
LinphoneEnums::Presence mPresence = LinphoneEnums::Presence::Undefined;
LinphoneEnums::Presence mExplicitPresence;
QString mPresenceNote;
int mMaxPresenceNoteSize;
bool mIsSaved = true;
std::shared_ptr<AccountModel> mAccountModel;
QSharedPointer<SafeConnection<AccountCore, AccountModel>> mAccountModelConnection;
QSharedPointer<SafeConnection<AccountCore, CoreModel>> mCoreModelConnection;
DECLARE_ABSTRACT_OBJECT
};
#endif

View file

@ -0,0 +1,65 @@
/*
* Copyright (c) 2010-2024 Belledonne Communications SARL.
*
* This file is part of linphone-desktop
* (see https://www.linphone.org).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "AccountDeviceCore.hpp"
#include "core/App.hpp"
#include "tool/Utils.hpp"
#include "tool/thread/SafeConnection.hpp"
DEFINE_ABSTRACT_OBJECT(AccountDeviceCore)
AccountDeviceCore::AccountDeviceCore(QString name, QString userAgent, QDateTime last) : QObject(nullptr) {
App::getInstance()->mEngine->setObjectOwnership(this, QQmlEngine::CppOwnership);
mustBeInLinphoneThread(getClassName());
mDeviceName = name;
mUserAgent = userAgent;
mLastUpdateTimestamp = last;
}
QSharedPointer<AccountDeviceCore> AccountDeviceCore::createDummy(QString name, QString userAgent, QDateTime last) {
auto core = QSharedPointer<AccountDeviceCore>(new AccountDeviceCore(name, userAgent, last));
core->moveToThread(App::getInstance()->thread());
return core;
}
QSharedPointer<AccountDeviceCore> AccountDeviceCore::create(const std::shared_ptr<linphone::AccountDevice> &device) {
mustBeInLinphoneThread(Q_FUNC_INFO);
auto core = QSharedPointer<AccountDeviceCore>(new AccountDeviceCore(device));
core->moveToThread(App::getInstance()->thread());
return core;
}
AccountDeviceCore::AccountDeviceCore(const std::shared_ptr<linphone::AccountDevice> &device) : QObject(nullptr) {
App::getInstance()->mEngine->setObjectOwnership(this, QQmlEngine::CppOwnership);
mustBeInLinphoneThread(getClassName());
mAccountDeviceModel = Utils::makeQObject_ptr<AccountDeviceModel>(device);
mDeviceName = Utils::coreStringToAppString(device->getName());
mUserAgent = Utils::coreStringToAppString(device->getUserAgent());
mLastUpdateTimestamp = QDateTime::fromSecsSinceEpoch(device->getLastUpdateTimestamp());
}
AccountDeviceCore::~AccountDeviceCore() {
mustBeInMainThread("~" + getClassName());
}
const std::shared_ptr<AccountDeviceModel> &AccountDeviceCore::getModel() const {
return mAccountDeviceModel;
}

View file

@ -0,0 +1,57 @@
/*
* Copyright (c) 2010-2024 Belledonne Communications SARL.
*
* This file is part of linphone-desktop
* (see https://www.linphone.org).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef ACCOUNT_DEVICE_CORE_H_
#define ACCOUNT_DEVICE_CORE_H_
#include "model/account/AccountDeviceModel.hpp"
#include "tool/AbstractObject.hpp"
#include <QDateTime>
#include <QObject>
#include <QSharedPointer>
#include <linphone++/linphone.hh>
class AccountDeviceCore : public QObject, public AbstractObject {
Q_OBJECT
Q_PROPERTY(QString deviceName MEMBER mDeviceName CONSTANT)
Q_PROPERTY(QString userAgent MEMBER mUserAgent CONSTANT)
Q_PROPERTY(QDateTime lastUpdateTimestamp MEMBER mLastUpdateTimestamp CONSTANT)
public:
static QSharedPointer<AccountDeviceCore> create(const std::shared_ptr<linphone::AccountDevice> &device);
AccountDeviceCore(const std::shared_ptr<linphone::AccountDevice> &device);
AccountDeviceCore(QString name, QString userAgent, QDateTime last);
static QSharedPointer<AccountDeviceCore> createDummy(QString name, QString userAgent, QDateTime last);
~AccountDeviceCore();
const std::shared_ptr<AccountDeviceModel> &getModel() const;
private:
QString mDeviceName;
QString mUserAgent;
QDateTime mLastUpdateTimestamp;
std::shared_ptr<AccountDeviceModel> mAccountDeviceModel;
DECLARE_ABSTRACT_OBJECT
};
#endif

View file

@ -0,0 +1,38 @@
/*
* Copyright (c) 2010-2024 Belledonne Communications SARL.
*
* This file is part of linphone-desktop
* (see https://www.linphone.org).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "AccountDeviceGui.hpp"
#include "core/App.hpp"
DEFINE_ABSTRACT_OBJECT(AccountDeviceGui)
AccountDeviceGui::AccountDeviceGui(QSharedPointer<AccountDeviceCore> core) {
App::getInstance()->mEngine->setObjectOwnership(this, QQmlEngine::JavaScriptOwnership);
mCore = core;
if (isInLinphoneThread()) moveToThread(App::getInstance()->thread());
}
AccountDeviceGui::~AccountDeviceGui() {
mustBeInMainThread("~" + getClassName());
}
AccountDeviceCore *AccountDeviceGui::getCore() const {
return mCore.get();
}

View file

@ -0,0 +1,41 @@
/*
* Copyright (c) 2010-2024 Belledonne Communications SARL.
*
* This file is part of linphone-desktop
* (see https://www.linphone.org).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef ACCOUNT_DEVICE_GUI_H_
#define ACCOUNT_DEVICE_GUI_H_
#include "AccountDeviceCore.hpp"
#include <QObject>
#include <QSharedPointer>
class AccountDeviceGui : public QObject, public AbstractObject {
Q_OBJECT
Q_PROPERTY(AccountDeviceCore *core READ getCore CONSTANT)
public:
AccountDeviceGui(QSharedPointer<AccountDeviceCore> core);
~AccountDeviceGui();
AccountDeviceCore *getCore() const;
QSharedPointer<AccountDeviceCore> mCore;
DECLARE_ABSTRACT_OBJECT
};
#endif

View file

@ -0,0 +1,189 @@
/*
* Copyright (c) 2024 Belledonne Communications SARL.
*
* This file is part of linphone-desktop
* (see https://www.linphone.org).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "AccountDeviceList.hpp"
#include "core/App.hpp"
#include "core/account/AccountDeviceGui.hpp"
#include "tool/Utils.hpp"
#include <QQmlApplicationEngine>
#include <algorithm>
DEFINE_ABSTRACT_OBJECT(AccountDeviceList)
QSharedPointer<AccountDeviceList> AccountDeviceList::create() {
auto model = QSharedPointer<AccountDeviceList>(new AccountDeviceList(), &QObject::deleteLater);
model->moveToThread(App::getInstance()->thread());
model->setSelf(model);
return model;
}
QSharedPointer<AccountDeviceList> AccountDeviceList::create(const QSharedPointer<AccountCore> &account) {
auto model = create();
model->setAccount(account);
return model;
}
AccountDeviceList::AccountDeviceList() {
App::getInstance()->mEngine->setObjectOwnership(this, QQmlEngine::CppOwnership);
}
AccountDeviceList::~AccountDeviceList() {
}
QList<QSharedPointer<AccountDeviceCore>>
AccountDeviceList::buildDevices(const std::list<std::shared_ptr<linphone::AccountDevice>> &devicesList) {
mustBeInLinphoneThread(log().arg(Q_FUNC_INFO));
QList<QSharedPointer<AccountDeviceCore>> devices;
for (auto &device : devicesList) {
auto deviceCore = AccountDeviceCore::create(device);
devices << deviceCore;
}
return devices;
}
const QSharedPointer<AccountCore> &AccountDeviceList::getAccount() const {
return mAccountCore;
}
void AccountDeviceList::setAccount(const QSharedPointer<AccountCore> &accountCore) {
mustBeInMainThread(log().arg(Q_FUNC_INFO));
if (mAccountCore != accountCore) {
mAccountCore = accountCore;
lDebug() << log().arg("Set account model") << mAccountCore.get();
// oldConnect.unlock();
if (mAccountCore) refreshDevices();
// }
}
}
void AccountDeviceList::refreshDevices() {
mustBeInMainThread(log().arg(Q_FUNC_INFO));
resetData();
if (mAccountCore) {
auto requestDeviceList = [this] {
if (!mAccountManagerServicesModelConnection) return;
mAccountManagerServicesModelConnection->invokeToModel([this]() {
auto identityAddress = mAccountCore->getModel()->getMonitor()->getParams()->getIdentityAddress();
auto authinfo = mAccountCore->getModel()->getMonitor()->findAuthInfo();
qDebug() << "[AccountDeviceList] request devices for address" << identityAddress->asStringUriOnly();
mAccountManagerServicesModel->getDeviceList(identityAddress);
});
};
if (mIsComponentReady) {
requestDeviceList();
} else {
connect(this, &AccountDeviceList::componentReady, this, requestDeviceList, Qt::SingleShotConnection);
}
}
}
void AccountDeviceList::setDevices(QList<QSharedPointer<AccountDeviceCore>> devices) {
mustBeInMainThread(log().arg(Q_FUNC_INFO));
add(devices);
lDebug() << log().arg("Add %1 devices").arg(devices.size());
emit devicesSet();
}
void AccountDeviceList::deleteDevice(AccountDeviceGui *deviceGui) {
auto requestDeviceDeletion = [this, deviceGui] {
if (!mAccountManagerServicesModelConnection) return;
auto deviceCore = deviceGui->getCore();
auto deviceModel = deviceCore->getModel();
mAccountManagerServicesModelConnection->invokeToModel([this, deviceModel]() {
auto linphoneDevice = deviceModel->getDevice();
auto identityAddress = mAccountCore->getModel()->getMonitor()->getParams()->getIdentityAddress();
auto authinfo = mAccountCore->getModel()->getMonitor()->findAuthInfo();
qDebug() << "[AccountDeviceList] delete device" << linphoneDevice->getName() << "of address"
<< identityAddress->asStringUriOnly();
mAccountManagerServicesModel->deleteDevice(identityAddress, linphoneDevice);
});
};
if (mIsComponentReady) {
requestDeviceDeletion();
} else {
connect(this, &AccountDeviceList::componentReady, this, requestDeviceDeletion);
}
}
void AccountDeviceList::setSelf(QSharedPointer<AccountDeviceList> me) {
if (mCoreModelConnection) mCoreModelConnection->disconnect();
mCoreModelConnection = SafeConnection<AccountDeviceList, CoreModel>::create(me, CoreModel::getInstance());
mCoreModelConnection->invokeToModel([=] {
auto core = CoreModel::getInstance()->getCore();
auto ams = core->createAccountManagerServices();
auto amsModel = Utils::makeQObject_ptr<AccountManagerServicesModel>(ams);
mCoreModelConnection->invokeToCore([this, amsModel, me]() {
mAccountManagerServicesModel = amsModel;
if (mAccountManagerServicesModelConnection) mAccountManagerServicesModelConnection->disconnect();
mAccountManagerServicesModelConnection =
SafeConnection<AccountDeviceList, AccountManagerServicesModel>::create(me,
mAccountManagerServicesModel);
mAccountManagerServicesModelConnection->makeConnectToModel(
&AccountManagerServicesModel::requestSuccessfull,
[this](const std::shared_ptr<const linphone::AccountManagerServicesRequest> &request,
const std::string &data) {
if (request->getType() == linphone::AccountManagerServicesRequest::Type::DeleteDevice) {
mAccountManagerServicesModelConnection->invokeToCore([this] { refreshDevices(); });
}
});
mAccountManagerServicesModelConnection->makeConnectToModel(
&AccountManagerServicesModel::requestError,
[this](const std::shared_ptr<const linphone::AccountManagerServicesRequest> &request, int statusCode,
const std::string &errorMessage,
const std::shared_ptr<const linphone::Dictionary> &parameterErrors) {
lDebug() << "REQUEST ERROR" << errorMessage << "/" << int(request->getType());
QString message = QString::fromStdString(errorMessage);
if (request->getType() == linphone::AccountManagerServicesRequest::Type::GetDevicesList) {
//: "Erreur lors de la récupération des appareils"
message = tr("manage_account_no_device_found_error_message");
}
emit requestError(message);
});
mAccountManagerServicesModelConnection->makeConnectToModel(
&AccountManagerServicesModel::devicesListFetched,
[this](const std::shared_ptr<const linphone::AccountManagerServicesRequest> &request,
const std::list<std::shared_ptr<linphone::AccountDevice>> &devicesList) {
mAccountManagerServicesModelConnection->invokeToModel([this, request, devicesList]() {
if (request->getType() == linphone::AccountManagerServicesRequest::Type::GetDevicesList) {
QList<QSharedPointer<AccountDeviceCore>> devices;
for (auto &device : devicesList) {
auto deviceCore = AccountDeviceCore::create(device);
devices << deviceCore;
}
// auto devices = buildDevices(devicesList);
mAccountManagerServicesModelConnection->invokeToCore(
[this, devices]() { setDevices(devices); });
}
});
});
mIsComponentReady = true;
emit componentReady();
});
});
}
QVariant AccountDeviceList::data(const QModelIndex &index, int role) const {
int row = index.row();
if (!index.isValid() || row < 0 || row >= rowCount()) return QVariant();
if (role == Qt::DisplayRole)
return QVariant::fromValue(new AccountDeviceGui(mList[row].objectCast<AccountDeviceCore>()));
return QVariant();
}

View file

@ -0,0 +1,73 @@
/*
* Copyright (c) 2021 Belledonne Communications SARL.
*
* This file is part of linphone-desktop
* (see https://www.linphone.org).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef ACCOUNT_DEVICE_LIST_H_
#define ACCOUNT_DEVICE_LIST_H_
#include "AccountDeviceCore.hpp"
#include "core/account/AccountGui.hpp"
#include "core/proxy/ListProxy.hpp"
#include "model/account/AccountManagerServicesModel.hpp"
#include "tool/AbstractObject.hpp"
#include "tool/thread/SafeConnection.hpp"
class AccountDeviceGui;
class AccountDeviceList : public ListProxy, public AbstractObject {
Q_OBJECT
public:
static QSharedPointer<AccountDeviceList> create();
static QSharedPointer<AccountDeviceList> create(const QSharedPointer<AccountCore> &accountCore);
AccountDeviceList();
~AccountDeviceList();
QList<QSharedPointer<AccountDeviceCore>>
buildDevices(const std::list<std::shared_ptr<linphone::AccountDevice>> &devicesList);
const QSharedPointer<AccountCore> &getAccount() const;
void setAccount(const QSharedPointer<AccountCore> &accountCore);
void refreshDevices();
void setDevices(QList<QSharedPointer<AccountDeviceCore>> devices);
void deleteDevice(AccountDeviceGui *deviceGui);
void setSelf(QSharedPointer<AccountDeviceList> me);
virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
signals:
void componentReady();
void devicesSet();
void requestError(QString errorMessage = QString());
private:
QSharedPointer<AccountCore> mAccountCore;
std::shared_ptr<AccountManagerServicesModel> mAccountManagerServicesModel;
QSharedPointer<SafeConnection<AccountDeviceList, AccountManagerServicesModel>>
mAccountManagerServicesModelConnection;
QSharedPointer<SafeConnection<AccountDeviceList, CoreModel>> mCoreModelConnection;
bool mIsComponentReady = false;
DECLARE_ABSTRACT_OBJECT
};
Q_DECLARE_METATYPE(QSharedPointer<AccountDeviceList>);
#endif // ACCOUNT_DEVICE_LIST_H_

View file

@ -0,0 +1,68 @@
/*
* Copyright (c) 2024 Belledonne Communications SARL.
*
* This file is part of linphone-desktop
* (see https://www.linphone.org).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "AccountDeviceProxy.hpp"
#include "AccountDeviceList.hpp"
#include "core/App.hpp"
#include "tool/Utils.hpp"
#include <QQmlApplicationEngine>
// =============================================================================
DEFINE_ABSTRACT_OBJECT(AccountDeviceProxy)
DEFINE_GUI_OBJECT(AccountDeviceProxy)
AccountDeviceProxy::AccountDeviceProxy(QObject *parent) : LimitProxy(parent) {
mAccountDeviceList = AccountDeviceList::create();
connect(this, &AccountDeviceProxy::sourceModelChanged, this, [this] {
auto model = getListModel<AccountDeviceList>();
if (model) {
connect(model, &AccountDeviceList::devicesSet, this, &AccountDeviceProxy::devicesSet);
connect(model, &AccountDeviceList::requestError, this, &AccountDeviceProxy::requestError);
}
});
setSourceModels(new SortFilterList(mAccountDeviceList.get(), Qt::DescendingOrder));
}
AccountDeviceProxy::~AccountDeviceProxy() {
// setSourceModel(nullptr);
}
AccountGui *AccountDeviceProxy::getAccount() const {
auto account = mAccountDeviceList->getAccount();
return account ? new AccountGui(account) : nullptr;
}
void AccountDeviceProxy::setAccount(AccountGui *accountGui) {
mAccountDeviceList->setAccount(accountGui ? accountGui->mCore : nullptr);
}
void AccountDeviceProxy::deleteDevice(AccountDeviceGui *device) {
mAccountDeviceList->deleteDevice(device);
}
bool AccountDeviceProxy::SortFilterList::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const {
return true;
}
bool AccountDeviceProxy::SortFilterList::lessThan(const QModelIndex &sourceLeft, const QModelIndex &sourceRight) const {
return sourceLeft.row() < sourceRight.row();
}

View file

@ -0,0 +1,62 @@
/*
* Copyright (c) 2024 Belledonne Communications SARL.
*
* This file is part of linphone-desktop
* (see https://www.linphone.org).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef ACCOUNT_DEVICE_PROXY_MODEL_H_
#define ACCOUNT_DEVICE_PROXY_MODEL_H_
#include "../proxy/LimitProxy.hpp"
#include "core/account/AccountDeviceGui.hpp"
#include "core/account/AccountGui.hpp"
#include "tool/AbstractObject.hpp"
class AccountDeviceList;
class AccountDeviceGui;
class AccountDeviceProxy : public LimitProxy, public AbstractObject {
Q_OBJECT
Q_PROPERTY(AccountGui *account READ getAccount WRITE setAccount NOTIFY accountChanged)
public:
DECLARE_GUI_OBJECT
DECLARE_SORTFILTER_CLASS()
AccountDeviceProxy(QObject *parent = Q_NULLPTR);
~AccountDeviceProxy();
AccountGui *getAccount() const;
void setAccount(AccountGui *accountGui);
Q_INVOKABLE void deleteDevice(AccountDeviceGui *device);
protected:
signals:
void lUpdate();
void accountChanged();
void devicesSet();
void requestError(QString message = QString());
private:
QString mSearchText;
QSharedPointer<AccountDeviceList> mAccountDeviceList;
QSharedPointer<SafeConnection<AccountDeviceProxy, CoreModel>> mCoreModelConnection;
DECLARE_ABSTRACT_OBJECT
};
#endif

View file

@ -0,0 +1,38 @@
/*
* Copyright (c) 2010-2024 Belledonne Communications SARL.
*
* This file is part of linphone-desktop
* (see https://www.linphone.org).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "AccountGui.hpp"
#include "core/App.hpp"
DEFINE_ABSTRACT_OBJECT(AccountGui)
AccountGui::AccountGui(QSharedPointer<AccountCore> core) {
App::getInstance()->mEngine->setObjectOwnership(this, QQmlEngine::JavaScriptOwnership);
mCore = core;
if (isInLinphoneThread()) moveToThread(App::getInstance()->thread());
}
AccountGui::~AccountGui() {
mustBeInMainThread("~" + getClassName());
}
AccountCore *AccountGui::getCore() const {
return mCore.get();
}

View file

@ -0,0 +1,41 @@
/*
* Copyright (c) 2010-2024 Belledonne Communications SARL.
*
* This file is part of linphone-desktop
* (see https://www.linphone.org).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef ACCOUNT_GUI_H_
#define ACCOUNT_GUI_H_
#include "AccountCore.hpp"
#include <QObject>
#include <QSharedPointer>
class AccountGui : public QObject, public AbstractObject {
Q_OBJECT
Q_PROPERTY(AccountCore *core READ getCore CONSTANT)
public:
AccountGui(QSharedPointer<AccountCore> core);
~AccountGui();
AccountCore *getCore() const;
QSharedPointer<AccountCore> mCore;
DECLARE_ABSTRACT_OBJECT
};
#endif

View file

@ -0,0 +1,190 @@
/*
* Copyright (c) 2010-2024 Belledonne Communications SARL.
*
* This file is part of linphone-desktop
* (see https://www.linphone.org).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "AccountList.hpp"
#include "AccountCore.hpp"
#include "AccountGui.hpp"
#include "core/App.hpp"
#include <QSharedPointer>
#include <linphone++/linphone.hh>
// =============================================================================
DEFINE_ABSTRACT_OBJECT(AccountList)
QSharedPointer<AccountList> AccountList::create() {
auto model = QSharedPointer<AccountList>(new AccountList(), &QObject::deleteLater);
model->moveToThread(App::getInstance()->thread());
model->setSelf(model);
return model;
}
AccountList::AccountList(QObject *parent) : ListProxy(parent) {
mustBeInMainThread(getClassName());
App::getInstance()->mEngine->setObjectOwnership(this, QQmlEngine::CppOwnership);
}
AccountList::~AccountList() {
mustBeInMainThread("~" + getClassName());
mModelConnection = nullptr;
}
void AccountList::setSelf(QSharedPointer<AccountList> me) {
mModelConnection = SafeConnection<AccountList, CoreModel>::create(me, CoreModel::getInstance());
mModelConnection->makeConnectToCore(&AccountList::lUpdate, [this](bool isInitialization) {
mModelConnection->invokeToModel([this, isInitialization]() {
// Avoid copy to lambdas
QList<QSharedPointer<AccountCore>> *accounts = new QList<QSharedPointer<AccountCore>>();
mustBeInLinphoneThread(getClassName());
auto linphoneAccounts = CoreModel::getInstance()->getCore()->getAccountList();
auto defaultAccount = CoreModel::getInstance()->getCore()->getDefaultAccount();
QSharedPointer<AccountCore> defaultAccountCore;
for (auto it : linphoneAccounts) {
auto model = AccountCore::create(it);
if (it == defaultAccount) defaultAccountCore = model;
accounts->push_back(model);
connect(model.get(), &AccountCore::unreadNotificationsChanged, this,
[this] { emit unreadNotificationsChanged(); });
connect(model.get(), &AccountCore::removed, this, [this, model]() {
disconnect(model.get(), &AccountCore::unreadNotificationsChanged, this, nullptr);
});
}
mModelConnection->invokeToCore([this, accounts, defaultAccountCore, isInitialization]() {
mustBeInMainThread(getClassName());
resetData<AccountCore>(*accounts);
setHaveAccount(accounts->size() > 0);
setDefaultAccount(defaultAccountCore);
if (isInitialization) setInitialized(true);
for (const QSharedPointer<AccountCore> &accountCore : *accounts) {
if (accountCore->getExplicitPresence() != LinphoneEnums::Presence::Undefined)
emit accountCore->lSetPresence(accountCore->getExplicitPresence(), true, false);
}
delete accounts;
});
// Update notification count at startup
if (isInitialization) emit unreadNotificationsChanged();
});
});
mModelConnection->makeConnectToModel(
&CoreModel::defaultAccountChanged,
[this](const std::shared_ptr<linphone::Core> &core, const std::shared_ptr<linphone::Account> &account) {
if (account && account->getParams()->getIdentityAddress()) {
auto address =
Utils::coreStringToAppString(account->getParams()->getIdentityAddress()->asStringUriOnly());
auto model = findAccountByAddress(address);
mModelConnection->invokeToCore([this, model]() { setDefaultAccount(model); });
} else mModelConnection->invokeToCore([this]() { setDefaultAccount(nullptr); });
});
mModelConnection->makeConnectToModel(&CoreModel::accountRemoved, [this] { emit lUpdate(); });
mModelConnection->makeConnectToModel(&CoreModel::accountAdded, [this] { emit lUpdate(true); });
// force initialization on bearer account added to automatically go on the main page
// with the open id account
mModelConnection->makeConnectToModel(&CoreModel::bearerAccountAdded, [this] {
setInitialized(false);
emit lUpdate(true);
});
mModelConnection->makeConnectToModel(
&CoreModel::globalStateChanged,
[this](const std::shared_ptr<linphone::Core> &core, linphone::GlobalState gstate, const std::string &message) {
mustBeInLinphoneThread(log().arg(Q_FUNC_INFO));
if (gstate == linphone::GlobalState::On) {
emit lUpdate();
}
});
lUpdate(true);
}
QSharedPointer<AccountCore> AccountList::getDefaultAccountCore() const {
return mDefaultAccount;
}
AccountGui *AccountList::getDefaultAccount() const {
auto account = getDefaultAccountCore();
if (account) return new AccountGui(account);
else return nullptr;
}
void AccountList::setDefaultAccount(QSharedPointer<AccountCore> account) {
if (mDefaultAccount != account) {
mDefaultAccount = account;
emit defaultAccountChanged();
}
}
QSharedPointer<AccountCore> AccountList::findAccountByAddress(const QString &address) {
for (auto &item : getSharedList<AccountCore>()) {
if (item->getIdentityAddress() == address) {
return item;
}
}
return nullptr;
}
AccountGui *AccountList::firstAccount() {
for (auto &item : mList) {
if (auto isAccount = item.objectCast<AccountCore>()) {
return new AccountGui(isAccount);
}
}
return nullptr;
}
bool AccountList::getHaveAccount() const {
return mHaveAccount;
}
void AccountList::setHaveAccount(bool haveAccount) {
if (mHaveAccount != haveAccount) {
mHaveAccount = haveAccount;
emit haveAccountChanged();
}
}
bool AccountList::isInitialized() const {
return mIsInitialized;
}
void AccountList::setInitialized(bool init) {
if (mIsInitialized != init) {
mIsInitialized = init;
emit initializedChanged(mIsInitialized);
}
}
void AccountList::lResetMissedCalls() {
for (auto &accountCore : getSharedList<AccountCore>()) {
accountCore->lResetMissedCalls();
}
}
void AccountList::lResetUnreadMessages() {
for (auto &accountCore : getSharedList<AccountCore>()) {
emit accountCore->lResetUnreadMessages();
}
}
QVariant AccountList::data(const QModelIndex &index, int role) const {
int row = index.row();
if (!index.isValid() || row < 0 || row >= mList.count()) return QVariant();
if (role == Qt::DisplayRole) return QVariant::fromValue(new AccountGui(mList[row].objectCast<AccountCore>()));
return QVariant();
}

View file

@ -0,0 +1,73 @@
/*
* Copyright (c) 2010-2024 Belledonne Communications SARL.
*
* This file is part of linphone-desktop
* (see https://www.linphone.org).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef ACCOUNT_LIST_H_
#define ACCOUNT_LIST_H_
#include "../proxy/ListProxy.hpp"
#include "tool/AbstractObject.hpp"
#include "tool/thread/SafeConnection.hpp"
#include <QLocale>
class AccountGui;
class AccountCore;
class CoreModel;
// =============================================================================
class AccountList : public ListProxy, public AbstractObject {
Q_OBJECT
public:
static QSharedPointer<AccountList> create();
AccountList(QObject *parent = Q_NULLPTR);
~AccountList();
void setSelf(QSharedPointer<AccountList> me);
AccountGui *getDefaultAccount() const;
QSharedPointer<AccountCore> getDefaultAccountCore() const;
void setDefaultAccount(QSharedPointer<AccountCore> account);
QSharedPointer<AccountCore> findAccountByAddress(const QString &address);
AccountGui *firstAccount();
bool getHaveAccount() const;
void setHaveAccount(bool haveAccount);
bool isInitialized() const;
void setInitialized(bool init);
void lResetMissedCalls(); // Reset missed calls of all accounts
void lResetUnreadMessages(); // Reset unread messages of all accounts
virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
signals:
void lUpdate(bool isInitialization = false);
void haveAccountChanged();
void defaultAccountChanged();
void initializedChanged(bool init);
void unreadNotificationsChanged();
private:
bool mHaveAccount = false;
bool mIsInitialized = false;
QSharedPointer<AccountCore> mDefaultAccount;
QSharedPointer<SafeConnection<AccountList, CoreModel>> mModelConnection;
DECLARE_ABSTRACT_OBJECT
};
#endif

View file

@ -0,0 +1,113 @@
/*
* Copyright (c) 2010-2024 Belledonne Communications SARL.
*
* This file is part of linphone-desktop
* (see https://www.linphone.org).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "AccountProxy.hpp"
#include "AccountGui.hpp"
#include "AccountList.hpp"
#include "core/App.hpp"
AccountProxy::AccountProxy(QObject *parent) : LimitProxy(parent) {
connect(this, &AccountProxy::initializedChanged, this, &AccountProxy::resetDefaultAccount);
connect(this, &AccountProxy::initializedChanged, this, &AccountProxy::haveAccountChanged);
}
AccountProxy::~AccountProxy() {
}
AccountGui *AccountProxy::getDefaultAccount() {
if (!mDefaultAccount) {
auto model = getListModel<AccountList>();
if (model) mDefaultAccount = model->getDefaultAccountCore();
}
return new AccountGui(mDefaultAccount);
}
// Reset the default account to let UI build its new object if needed.
void AccountProxy::resetDefaultAccount() {
mDefaultAccount = nullptr;
emit this->defaultAccountChanged(); // Warn the UI
}
AccountGui *AccountProxy::firstAccount() {
auto model = getListModel<AccountList>();
if (model) return model->firstAccount();
else return nullptr;
}
bool AccountProxy::getHaveAccount() const {
auto model = getListModel<AccountList>();
if (model) return model->getHaveAccount();
else return false;
}
bool AccountProxy::isInitialized() const {
return mInitialized;
}
void AccountProxy::setInitialized(bool init) {
if (mInitialized != init) {
mInitialized = init;
emit initializedChanged();
}
}
void AccountProxy::setSourceModel(QAbstractItemModel *model) {
auto oldAccountList = getListModel<AccountList>();
if (oldAccountList) {
disconnect(oldAccountList);
}
auto newAccountList = dynamic_cast<AccountList *>(model);
if (newAccountList) {
connect(newAccountList, &AccountList::initializedChanged, this, [this](bool init) {
qDebug() << "AccountProxy initialized";
setInitialized(init);
});
connect(newAccountList, &AccountList::countChanged, this, &AccountProxy::resetDefaultAccount,
Qt::QueuedConnection);
connect(newAccountList, &AccountList::defaultAccountChanged, this, &AccountProxy::resetDefaultAccount,
Qt::QueuedConnection);
connect(newAccountList, &AccountList::haveAccountChanged, this, &AccountProxy::haveAccountChanged,
Qt::QueuedConnection);
}
setSourceModels(new SortFilterList(model, Qt::AscendingOrder));
if (newAccountList) setInitialized(newAccountList->isInitialized());
}
//------------------------------------------------------------------------------------------
bool AccountProxy::SortFilterList::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const {
bool show = (mFilterText.isEmpty() || mFilterText == "*");
if (!show) {
QRegularExpression search(QRegularExpression::escape(mFilterText),
QRegularExpression::CaseInsensitiveOption |
QRegularExpression::UseUnicodePropertiesOption);
auto account = getItemAtSource<AccountList, AccountCore>(sourceRow);
show = account->getIdentityAddress().contains(search);
}
return show;
}
bool AccountProxy::SortFilterList::lessThan(const QModelIndex &sourceLeft, const QModelIndex &sourceRight) const {
auto l = getItemAtSource<AccountList, AccountCore>(sourceLeft.row());
auto r = getItemAtSource<AccountList, AccountCore>(sourceRight.row());
return l->getIdentityAddress() < r->getIdentityAddress();
}

View file

@ -0,0 +1,65 @@
/*
* Copyright (c) 2010-2024 Belledonne Communications SARL.
*
* This file is part of linphone-desktop
* (see https://www.linphone.org).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef ACCOUNT_PROXY_H_
#define ACCOUNT_PROXY_H_
#include "../proxy/LimitProxy.hpp"
#include "../proxy/SortFilterProxy.hpp"
#include "core/account/AccountGui.hpp"
#include "core/account/AccountList.hpp"
// =============================================================================
class AccountProxy : public LimitProxy {
Q_OBJECT
Q_PROPERTY(AccountGui *defaultAccount READ getDefaultAccount NOTIFY defaultAccountChanged)
Q_PROPERTY(bool haveAccount READ getHaveAccount NOTIFY haveAccountChanged)
Q_PROPERTY(bool isInitialized READ isInitialized NOTIFY initializedChanged)
public:
DECLARE_SORTFILTER_CLASS()
AccountProxy(QObject *parent = Q_NULLPTR);
~AccountProxy();
AccountGui *getDefaultAccount(); // Get a new object from List or give the stored one.
void resetDefaultAccount(); // Reset the default account to let UI build its new object if needed.
Q_INVOKABLE AccountGui *firstAccount();
bool getHaveAccount() const;
bool isInitialized() const;
void setInitialized(bool init);
void setSourceModel(QAbstractItemModel *sourceModel) override;
signals:
void defaultAccountChanged();
void haveAccountChanged();
void initializedChanged();
protected:
bool mInitialized = false;
QSharedPointer<AccountCore> mDefaultAccount; // When null, a new UI object is build from List
};
#endif

View file

@ -0,0 +1,86 @@
/*
* Copyright (c) 2010-2024 Belledonne Communications SARL.
*
* This file is part of linphone-desktop
* (see https://www.linphone.org).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "CarddavCore.hpp"
#include "core/App.hpp"
DEFINE_ABSTRACT_OBJECT(CarddavCore)
QSharedPointer<CarddavCore> CarddavCore::create(const std::shared_ptr<linphone::FriendList> &carddavFriendList) {
auto sharedPointer = QSharedPointer<CarddavCore>(new CarddavCore(carddavFriendList), &QObject::deleteLater);
sharedPointer->setSelf(sharedPointer);
sharedPointer->moveToThread(App::getInstance()->thread());
return sharedPointer;
}
CarddavCore::CarddavCore(const std::shared_ptr<linphone::FriendList> &carddavFriendList) : QObject(nullptr) {
App::getInstance()->mEngine->setObjectOwnership(this, QQmlEngine::CppOwnership);
mustBeInLinphoneThread(log().arg(Q_FUNC_INFO));
mCarddavModel = CarddavModel::create(carddavFriendList);
if (carddavFriendList) {
mDisplayName = Utils::coreStringToAppString(carddavFriendList->getDisplayName());
mUri = Utils::coreStringToAppString(carddavFriendList->getUri());
}
mStoreNewFriendsInIt = mCarddavModel->storeNewFriendsInIt();
}
CarddavCore::~CarddavCore() {
mustBeInMainThread(log().arg(Q_FUNC_INFO));
}
void CarddavCore::save() {
if (!mUri.startsWith("http://") && !mUri.startsWith("https://")) {
mUri = "https://" + mUri;
emit uriChanged();
}
auto displayName = Utils::appStringToCoreString(mDisplayName);
auto uri = Utils::appStringToCoreString(mUri);
auto username = Utils::appStringToCoreString(mUsername);
auto password = Utils::appStringToCoreString(mPassword);
auto realm = Utils::appStringToCoreString(mRealm);
auto storeNewFriendsInIt = mStoreNewFriendsInIt;
mCarddavModelConnection->invokeToModel([this, displayName, uri, username, password, realm, storeNewFriendsInIt]() {
mustBeInLinphoneThread(log().arg(Q_FUNC_INFO));
mCarddavModel->save(displayName, uri, username, password, realm, storeNewFriendsInIt);
});
}
void CarddavCore::remove() {
mCarddavModelConnection->invokeToModel([this]() {
mustBeInLinphoneThread(log().arg(Q_FUNC_INFO));
mCarddavModel->remove();
});
}
void CarddavCore::setSelf(QSharedPointer<CarddavCore> me) {
mCarddavModelConnection = SafeConnection<CarddavCore, CarddavModel>::create(me, mCarddavModel);
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);
});
});
}
bool CarddavCore::isValid() {
return !mDisplayName.isEmpty() && !mUri.isEmpty(); // Auth info is optional.
}

View file

@ -0,0 +1,62 @@
/*
* Copyright (c) 2010-2024 Belledonne Communications SARL.
*
* This file is part of linphone-desktop
* (see https://www.linphone.org).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef CARDDAV_CORE_H_
#define CARDDAV_CORE_H_
#include "model/address-books/carddav/CarddavModel.hpp"
#include "tool/AbstractObject.hpp"
#include "tool/thread/SafeConnection.hpp"
#include <QObject>
#include <QSharedPointer>
#include <linphone++/linphone.hh>
class CarddavCore : public QObject, public AbstractObject {
Q_OBJECT
public:
static QSharedPointer<CarddavCore> create(const std::shared_ptr<linphone::FriendList> &carddavFriendList);
CarddavCore(const std::shared_ptr<linphone::FriendList> &carddavFriendList);
~CarddavCore();
void setSelf(QSharedPointer<CarddavCore> me);
Q_INVOKABLE void remove();
Q_INVOKABLE void save();
Q_INVOKABLE bool isValid();
DECLARE_CORE_MEMBER(QString, displayName, DisplayName)
DECLARE_CORE_MEMBER(QString, uri, Uri)
DECLARE_CORE_MEMBER(QString, username, Username)
DECLARE_CORE_MEMBER(QString, password, Password)
DECLARE_CORE_MEMBER(QString, realm, Realm)
DECLARE_CORE_MEMBER(bool, storeNewFriendsInIt, StoreNewFriendsInIt)
signals:
void saved(bool success, QString message);
private:
std::shared_ptr<CarddavModel> mCarddavModel;
QSharedPointer<SafeConnection<CarddavCore, CarddavModel>> mCarddavModelConnection;
DECLARE_ABSTRACT_OBJECT
};
Q_DECLARE_METATYPE(CarddavCore *)
#endif

View file

@ -0,0 +1,46 @@
/*
* Copyright (c) 2010-2024 Belledonne Communications SARL.
*
* This file is part of linphone-desktop
* (see https://www.linphone.org).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "CarddavGui.hpp"
#include "core/App.hpp"
DEFINE_ABSTRACT_OBJECT(CarddavGui)
CarddavGui::CarddavGui(QSharedPointer<CarddavCore> core) {
App::getInstance()->mEngine->setObjectOwnership(this, QQmlEngine::JavaScriptOwnership);
mCore = core;
if (isInLinphoneThread()) moveToThread(App::getInstance()->thread());
}
CarddavGui::CarddavGui(QObject *parent) : QObject(parent) {
mustBeInMainThread(log().arg(Q_FUNC_INFO));
App::postModelSync([this]() {
mustBeInLinphoneThread(log().arg(Q_FUNC_INFO));
mCore = CarddavCore::create(nullptr);
});
}
CarddavGui::~CarddavGui() {
mustBeInMainThread("~" + getClassName());
}
CarddavCore *CarddavGui::getCore() const {
return mCore.get();
}

View file

@ -0,0 +1,42 @@
/*
* Copyright (c) 2010-2024 Belledonne Communications SARL.
*
* This file is part of linphone-desktop
* (see https://www.linphone.org).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef CARDDAV_GUI_H_
#define CARDDAV_GUI_H_
#include "CarddavCore.hpp"
#include <QObject>
#include <QSharedPointer>
class CarddavGui : public QObject, public AbstractObject {
Q_OBJECT
Q_PROPERTY(CarddavCore *core READ getCore CONSTANT)
public:
CarddavGui(QSharedPointer<CarddavCore> core);
CarddavGui(QObject *parent = nullptr);
~CarddavGui();
CarddavCore *getCore() const;
QSharedPointer<CarddavCore> mCore;
DECLARE_ABSTRACT_OBJECT
};
#endif

View file

@ -0,0 +1,108 @@
/*
* Copyright (c) 2010-2024 Belledonne Communications SARL.
*
* This file is part of linphone-desktop
* (see https://www.linphone.org).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "CarddavList.hpp"
#include "CarddavGui.hpp"
#include "core/App.hpp"
#include "model/object/VariantObject.hpp"
#include <QSharedPointer>
#include <linphone++/linphone.hh>
// =============================================================================
DEFINE_ABSTRACT_OBJECT(CarddavList)
QSharedPointer<CarddavList> CarddavList::create() {
auto model = QSharedPointer<CarddavList>(new CarddavList(), &QObject::deleteLater);
model->moveToThread(App::getInstance()->thread());
model->setSelf(model);
return model;
}
QSharedPointer<CarddavCore>
CarddavList::createCarddavCore(const std::shared_ptr<linphone::FriendList> &carddavFriendList) {
auto CarddavCore = CarddavCore::create(carddavFriendList);
return CarddavCore;
}
CarddavList::CarddavList(QObject *parent) : ListProxy(parent) {
mustBeInMainThread(getClassName());
App::getInstance()->mEngine->setObjectOwnership(this, QQmlEngine::CppOwnership);
}
CarddavList::~CarddavList() {
mustBeInMainThread("~" + getClassName());
mModelConnection = nullptr;
}
void CarddavList::setSelf(QSharedPointer<CarddavList> me) {
mModelConnection = SafeConnection<CarddavList, CoreModel>::create(me, CoreModel::getInstance());
mModelConnection->makeConnectToCore(&CarddavList::lUpdate, [this]() {
mModelConnection->invokeToModel([this]() {
mustBeInLinphoneThread(getClassName());
QList<QSharedPointer<CarddavCore>> *carddavs = new QList<QSharedPointer<CarddavCore>>();
for (auto friendList : CoreModel::getInstance()->getCore()->getFriendsLists()) {
if (friendList->getType() == linphone::FriendList::Type::CardDAV) {
auto model = createCarddavCore(friendList);
carddavs->push_back(model);
}
}
mModelConnection->invokeToCore([this, carddavs]() {
mustBeInMainThread(getClassName());
resetData<CarddavCore>(*carddavs);
delete carddavs;
});
});
});
mModelConnection->makeConnectToModel(
&CoreModel::friendListRemoved,
[this](const std::shared_ptr<linphone::Core> &core, const std::shared_ptr<linphone::FriendList> &friendList) {
mustBeInLinphoneThread(log().arg(Q_FUNC_INFO));
emit lUpdate();
});
emit lUpdate();
}
void CarddavList::removeAllEntries() {
beginResetModel();
for (auto it = mList.rbegin(); it != mList.rend(); ++it) {
auto carddavFriendList = it->objectCast<CarddavCore>();
carddavFriendList->remove();
}
mList.clear();
endResetModel();
}
void CarddavList::remove(const int &row) {
beginRemoveRows(QModelIndex(), row, row);
mList.takeAt(row).objectCast<CarddavCore>()->remove();
endRemoveRows();
}
QVariant CarddavList::data(const QModelIndex &index, int role) const {
int row = index.row();
if (!index.isValid() || row < 0 || row >= mList.count()) return QVariant();
if (role == Qt::DisplayRole) {
return QVariant::fromValue(new CarddavGui(mList[row].objectCast<CarddavCore>()));
}
return QVariant();
}

View file

@ -0,0 +1,58 @@
/*
* Copyright (c) 2010-2024 Belledonne Communications SARL.
*
* This file is part of linphone-desktop
* (see https://www.linphone.org).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef CARDDAV_LIST_H_
#define CARDDAV_LIST_H_
#include "../../proxy/ListProxy.hpp"
#include "tool/AbstractObject.hpp"
#include "tool/thread/SafeConnection.hpp"
#include <QLocale>
class CarddavCore;
class CoreModel;
// =============================================================================
class CarddavList : public ListProxy, public AbstractObject {
Q_OBJECT
public:
static QSharedPointer<CarddavList> create();
// Create a CarddavCore and make connections to List.
QSharedPointer<CarddavCore> createCarddavCore(const std::shared_ptr<linphone::FriendList> &carddavFriendList);
CarddavList(QObject *parent = Q_NULLPTR);
~CarddavList();
void setSelf(QSharedPointer<CarddavList> me);
void removeAllEntries();
void remove(const int &row);
virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
signals:
void lUpdate();
private:
QSharedPointer<SafeConnection<CarddavList, CoreModel>> mModelConnection;
DECLARE_ABSTRACT_OBJECT
};
#endif

View file

@ -0,0 +1,65 @@
/*
* Copyright (c) 2010-2024 Belledonne Communications SARL.
*
* This file is part of linphone-desktop
* (see https://www.linphone.org).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "CarddavProxy.hpp"
#include "CarddavGui.hpp"
#include "CarddavList.hpp"
DEFINE_ABSTRACT_OBJECT(CarddavProxy)
CarddavProxy::CarddavProxy(QObject *parent) : LimitProxy(parent) {
mCarddavList = CarddavList::create();
setSourceModels(new SortFilterList(mCarddavList.get(), Qt::AscendingOrder));
}
CarddavProxy::~CarddavProxy() {
setSourceModel(nullptr);
}
void CarddavProxy::removeAllEntries() {
static_cast<CarddavList *>(sourceModel())->removeAllEntries();
}
void CarddavProxy::removeEntriesWithFilter() {
QList<QSharedPointer<CarddavCore>> itemList(rowCount());
for (auto i = rowCount() - 1; i >= 0; --i) {
auto item = getItemAt<SortFilterList, CarddavList, CarddavCore>(i);
itemList[i] = item;
}
for (auto item : itemList) {
mCarddavList->ListProxy::remove(item.get());
if (item) item->remove();
}
}
bool CarddavProxy::SortFilterList::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const {
return true;
}
bool CarddavProxy::SortFilterList::lessThan(const QModelIndex &sourceLeft, const QModelIndex &sourceRight) const {
auto l = getItemAtSource<CarddavList, CarddavCore>(sourceLeft.row());
auto r = getItemAtSource<CarddavList, CarddavCore>(sourceRight.row());
return l->mDisplayName < r->mDisplayName;
}
void CarddavProxy::updateView() {
mCarddavList->lUpdate();
}

View file

@ -0,0 +1,50 @@
/*
* Copyright (c) 2010-2024 Belledonne Communications SARL.
*
* This file is part of linphone-desktop
* (see https://www.linphone.org).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef CARDDAV_PROXY_H_
#define CARDDAV_PROXY_H_
#include "../../proxy/LimitProxy.hpp"
#include "CarddavGui.hpp"
#include "CarddavList.hpp"
#include "tool/AbstractObject.hpp"
// =============================================================================
class CarddavProxy : public LimitProxy, public AbstractObject {
Q_OBJECT
public:
DECLARE_SORTFILTER_CLASS()
CarddavProxy(QObject *parent = Q_NULLPTR);
~CarddavProxy();
Q_INVOKABLE void removeAllEntries();
Q_INVOKABLE void removeEntriesWithFilter();
Q_INVOKABLE void updateView();
protected:
QSharedPointer<CarddavList> mCarddavList;
DECLARE_ABSTRACT_OBJECT
};
#endif

View file

@ -0,0 +1,151 @@
/*
* Copyright (c) 2010-2024 Belledonne Communications SARL.
*
* This file is part of linphone-desktop
* (see https://www.linphone.org).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "LdapCore.hpp"
#include "core/App.hpp"
DEFINE_ABSTRACT_OBJECT(LdapCore)
QSharedPointer<LdapCore> LdapCore::create(const std::shared_ptr<linphone::RemoteContactDirectory> &ldap) {
auto sharedPointer = QSharedPointer<LdapCore>(new LdapCore(ldap), &QObject::deleteLater);
sharedPointer->setSelf(sharedPointer);
sharedPointer->moveToThread(App::getInstance()->thread());
return sharedPointer;
}
LdapCore::LdapCore(const std::shared_ptr<linphone::RemoteContactDirectory> &ldap) : QObject(nullptr) {
App::getInstance()->mEngine->setObjectOwnership(this, QQmlEngine::CppOwnership);
mustBeInLinphoneThread(log().arg(Q_FUNC_INFO));
mLdapModel = Utils::makeQObject_ptr<LdapModel>(ldap);
INIT_CORE_MEMBER(ServerUrl, mLdapModel)
INIT_CORE_MEMBER(BindDn, mLdapModel)
INIT_CORE_MEMBER(Password, mLdapModel)
INIT_CORE_MEMBER(AuthMethod, mLdapModel)
INIT_CORE_MEMBER(Tls, mLdapModel)
INIT_CORE_MEMBER(ServerCertificatesVerificationMode, mLdapModel)
INIT_CORE_MEMBER(BaseObject, mLdapModel)
INIT_CORE_MEMBER(Filter, mLdapModel)
INIT_CORE_MEMBER(Limit, mLdapModel)
INIT_CORE_MEMBER(Timeout, mLdapModel)
INIT_CORE_MEMBER(Delay, mLdapModel)
INIT_CORE_MEMBER(MinCharacters, mLdapModel)
INIT_CORE_MEMBER(NameAttribute, mLdapModel)
INIT_CORE_MEMBER(SipAttribute, mLdapModel)
INIT_CORE_MEMBER(SipDomain, mLdapModel)
INIT_CORE_MEMBER(Debug, mLdapModel)
INIT_CORE_MEMBER(Enabled, mLdapModel)
}
LdapCore::~LdapCore() {
mustBeInMainThread(log().arg(Q_FUNC_INFO));
}
void LdapCore::remove() {
mLdapModelConnection->invokeToModel([this]() {
mustBeInLinphoneThread(log().arg(Q_FUNC_INFO));
mLdapModel->remove();
});
}
bool LdapCore::isValid() {
return !mServerUrl.isEmpty() && !mBaseObject.isEmpty();
}
void LdapCore::setSelf(QSharedPointer<LdapCore> me) {
mLdapModelConnection = SafeConnection<LdapCore, LdapModel>::create(me, mLdapModel);
DEFINE_CORE_GETSET_CONNECT(mLdapModelConnection, LdapCore, LdapModel, mLdapModel, QString, serverUrl, ServerUrl)
DEFINE_CORE_GETSET_CONNECT(mLdapModelConnection, LdapCore, LdapModel, mLdapModel, QString, bindDn, BindDn)
DEFINE_CORE_GETSET_CONNECT(mLdapModelConnection, LdapCore, LdapModel, mLdapModel, QString, password, Password)
DEFINE_CORE_GETSET_CONNECT(mLdapModelConnection, LdapCore, LdapModel, mLdapModel, linphone::Ldap::AuthMethod,
authMethod, AuthMethod)
DEFINE_CORE_GETSET_CONNECT(mLdapModelConnection, LdapCore, LdapModel, mLdapModel, bool, tls, Tls)
DEFINE_CORE_GETSET_CONNECT(mLdapModelConnection, LdapCore, LdapModel, mLdapModel,
linphone::Ldap::CertVerificationMode, serverCertificatesVerificationMode,
ServerCertificatesVerificationMode)
DEFINE_CORE_GETSET_CONNECT(mLdapModelConnection, LdapCore, LdapModel, mLdapModel, QString, baseObject, BaseObject)
DEFINE_CORE_GETSET_CONNECT(mLdapModelConnection, LdapCore, LdapModel, mLdapModel, QString, filter, Filter)
DEFINE_CORE_GETSET_CONNECT(mLdapModelConnection, LdapCore, LdapModel, mLdapModel, int, limit, Limit)
DEFINE_CORE_GETSET_CONNECT(mLdapModelConnection, LdapCore, LdapModel, mLdapModel, int, timeout, Timeout)
DEFINE_CORE_GETSET_CONNECT(mLdapModelConnection, LdapCore, LdapModel, mLdapModel, int, delay, Delay)
DEFINE_CORE_GETSET_CONNECT(mLdapModelConnection, LdapCore, LdapModel, mLdapModel, int, minCharacters, MinCharacters)
DEFINE_CORE_GETSET_CONNECT(mLdapModelConnection, LdapCore, LdapModel, mLdapModel, QString, nameAttribute,
NameAttribute)
DEFINE_CORE_GETSET_CONNECT(mLdapModelConnection, LdapCore, LdapModel, mLdapModel, QString, sipAttribute,
SipAttribute)
DEFINE_CORE_GETSET_CONNECT(mLdapModelConnection, LdapCore, LdapModel, mLdapModel, QString, sipDomain, SipDomain)
DEFINE_CORE_GETSET_CONNECT(mLdapModelConnection, LdapCore, LdapModel, mLdapModel, bool, debug, Debug)
mLdapModelConnection->makeConnectToModel(&LdapModel::saved, [this]() {
mLdapModelConnection->invokeToCore([this]() { emit App::getInstance()->getSettings()->ldapConfigChanged(); });
});
mLdapModelConnection->makeConnectToModel(&LdapModel::removed, [this]() {
mLdapModelConnection->invokeToCore([this]() { emit App::getInstance()->getSettings()->ldapConfigChanged(); });
});
}
bool LdapCore::isEnabled() const {
return mEnabled;
}
void LdapCore::setEnabled(bool enabled) {
if (mEnabled != enabled) {
mEnabled = enabled;
emit enabledChanged();
setSaved(false);
}
}
bool LdapCore::isSaved() const {
return mIsSaved;
}
void LdapCore::setSaved(bool saved) {
if (mIsSaved != saved) {
mIsSaved = saved;
emit savedChanged();
}
}
void LdapCore::writeIntoModel(std::shared_ptr<LdapModel> model) const {
mustBeInLinphoneThread(getClassName() + Q_FUNC_INFO);
model->setEnabled(mEnabled);
}
void LdapCore::writeFromModel(const std::shared_ptr<LdapModel> &model) {
mustBeInLinphoneThread(getClassName() + Q_FUNC_INFO);
setEnabled(model->getEnabled());
}
void LdapCore::save() {
mLdapModelConnection->invokeToModel([this]() {
mustBeInLinphoneThread(log().arg(Q_FUNC_INFO));
writeIntoModel(mLdapModel);
mLdapModel->save();
mLdapModelConnection->invokeToCore([this] { setSaved(true); });
});
}
void LdapCore::undo() {
if (mLdapModel) {
mLdapModelConnection->invokeToModel([this] { writeFromModel(mLdapModel); });
}
}

View file

@ -0,0 +1,92 @@
/*
* Copyright (c) 2010-2024 Belledonne Communications SARL.
*
* This file is part of linphone-desktop
* (see https://www.linphone.org).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef LDAP_CORE_H_
#define LDAP_CORE_H_
#include "model/address-books/ldap/LdapModel.hpp"
#include "tool/AbstractObject.hpp"
#include "tool/thread/SafeConnection.hpp"
#include <QObject>
#include <QSharedPointer>
#include <linphone++/linphone.hh>
class LdapCore : public QObject, public AbstractObject {
Q_OBJECT
public:
static QSharedPointer<LdapCore> create(const std::shared_ptr<linphone::RemoteContactDirectory> &ldap);
LdapCore(const std::shared_ptr<linphone::RemoteContactDirectory> &ldap);
~LdapCore();
void setSelf(QSharedPointer<LdapCore> me);
Q_INVOKABLE void remove();
Q_INVOKABLE void save();
Q_INVOKABLE void undo();
Q_INVOKABLE bool isValid();
Q_PROPERTY(bool enabled READ isEnabled WRITE setEnabled NOTIFY enabledChanged)
Q_PROPERTY(bool saved READ isSaved WRITE setSaved NOTIFY savedChanged)
DECLARE_CORE_GETSET_MEMBER(QString, serverUrl, ServerUrl)
DECLARE_CORE_GETSET_MEMBER(QString, bindDn, BindDn)
DECLARE_CORE_GETSET_MEMBER(QString, password, Password)
DECLARE_CORE_GETSET_MEMBER(linphone::Ldap::AuthMethod, authMethod, AuthMethod)
DECLARE_CORE_GETSET_MEMBER(bool, tls, Tls)
DECLARE_CORE_GETSET_MEMBER(linphone::Ldap::CertVerificationMode,
serverCertificatesVerificationMode,
ServerCertificatesVerificationMode)
DECLARE_CORE_GETSET_MEMBER(QString, baseObject, BaseObject)
DECLARE_CORE_GETSET_MEMBER(QString, filter, Filter)
DECLARE_CORE_GETSET_MEMBER(int, limit, Limit)
DECLARE_CORE_GETSET_MEMBER(int, timeout, Timeout)
DECLARE_CORE_GETSET_MEMBER(int, delay, Delay)
DECLARE_CORE_GETSET_MEMBER(int, minCharacters, MinCharacters)
DECLARE_CORE_GETSET_MEMBER(QString, nameAttribute, NameAttribute)
DECLARE_CORE_GETSET_MEMBER(QString, sipAttribute, SipAttribute)
DECLARE_CORE_GETSET_MEMBER(QString, sipDomain, SipDomain)
DECLARE_CORE_GETSET_MEMBER(bool, debug, Debug)
public:
bool isEnabled() const;
void setEnabled(bool enabled);
bool isSaved() const;
void setSaved(bool saved);
signals:
void enabledChanged();
void savedChanged();
protected:
void writeIntoModel(std::shared_ptr<LdapModel> model) const;
void writeFromModel(const std::shared_ptr<LdapModel> &model);
private:
bool mEnabled = false;
bool mIsSaved = false;
std::shared_ptr<LdapModel> mLdapModel;
QSharedPointer<SafeConnection<LdapCore, LdapModel>> mLdapModelConnection;
DECLARE_ABSTRACT_OBJECT
};
Q_DECLARE_METATYPE(LdapCore *)
#endif

View file

@ -0,0 +1,46 @@
/*
* Copyright (c) 2010-2024 Belledonne Communications SARL.
*
* This file is part of linphone-desktop
* (see https://www.linphone.org).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "LdapGui.hpp"
#include "core/App.hpp"
DEFINE_ABSTRACT_OBJECT(LdapGui)
LdapGui::LdapGui(QSharedPointer<LdapCore> core) {
App::getInstance()->mEngine->setObjectOwnership(this, QQmlEngine::JavaScriptOwnership);
mCore = core;
if (isInLinphoneThread()) moveToThread(App::getInstance()->thread());
}
LdapGui::LdapGui(QObject *parent) : QObject(parent) {
mustBeInMainThread(getClassName());
App::postModelSync([this]() {
mustBeInLinphoneThread(log().arg(Q_FUNC_INFO));
mCore = LdapCore::create(nullptr);
});
}
LdapGui::~LdapGui() {
mustBeInMainThread("~" + getClassName());
}
LdapCore *LdapGui::getCore() const {
return mCore.get();
}

View file

@ -0,0 +1,42 @@
/*
* Copyright (c) 2010-2024 Belledonne Communications SARL.
*
* This file is part of linphone-desktop
* (see https://www.linphone.org).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef LDAP_GUI_H_
#define LDAP_GUI_H_
#include "LdapCore.hpp"
#include <QObject>
#include <QSharedPointer>
class LdapGui : public QObject, public AbstractObject {
Q_OBJECT
Q_PROPERTY(LdapCore *core READ getCore CONSTANT)
public:
LdapGui(QSharedPointer<LdapCore> core);
LdapGui(QObject *parent = nullptr);
~LdapGui();
LdapCore *getCore() const;
QSharedPointer<LdapCore> mCore;
DECLARE_ABSTRACT_OBJECT
};
#endif

View file

@ -0,0 +1,99 @@
/*
* Copyright (c) 2010-2024 Belledonne Communications SARL.
*
* This file is part of linphone-desktop
* (see https://www.linphone.org).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "LdapList.hpp"
#include "LdapGui.hpp"
#include "core/App.hpp"
#include "model/object/VariantObject.hpp"
#include <QSharedPointer>
#include <linphone++/linphone.hh>
// =============================================================================
DEFINE_ABSTRACT_OBJECT(LdapList)
QSharedPointer<LdapList> LdapList::create() {
auto model = QSharedPointer<LdapList>(new LdapList(), &QObject::deleteLater);
model->moveToThread(App::getInstance()->thread());
model->setSelf(model);
return model;
}
QSharedPointer<LdapCore> LdapList::createLdapCore(const std::shared_ptr<linphone::RemoteContactDirectory> &ldap) {
auto LdapCore = LdapCore::create(ldap);
return LdapCore;
}
LdapList::LdapList(QObject *parent) : ListProxy(parent) {
mustBeInMainThread(getClassName());
App::getInstance()->mEngine->setObjectOwnership(this, QQmlEngine::CppOwnership);
}
LdapList::~LdapList() {
mustBeInMainThread("~" + getClassName());
mModelConnection = nullptr;
}
void LdapList::setSelf(QSharedPointer<LdapList> me) {
mModelConnection = SafeConnection<LdapList, CoreModel>::create(me, CoreModel::getInstance());
mModelConnection->makeConnectToCore(&LdapList::lUpdate, [this]() {
mModelConnection->invokeToModel([this]() {
QList<QSharedPointer<LdapCore>> *ldaps = new QList<QSharedPointer<LdapCore>>();
mustBeInLinphoneThread(getClassName());
for (auto server : CoreModel::getInstance()->getCore()->getRemoteContactDirectories()) {
if (server->getType() == linphone::RemoteContactDirectory::Type::Ldap) {
auto model = createLdapCore(server);
ldaps->push_back(model);
}
}
mModelConnection->invokeToCore([this, ldaps]() {
mustBeInMainThread(getClassName());
resetData<LdapCore>(*ldaps);
delete ldaps;
});
});
});
emit lUpdate();
}
void LdapList::removeAllEntries() {
beginResetModel();
for (auto it = mList.rbegin(); it != mList.rend(); ++it) {
auto ldap = it->objectCast<LdapCore>();
ldap->remove();
}
mList.clear();
endResetModel();
}
void LdapList::remove(const int &row) {
beginRemoveRows(QModelIndex(), row, row);
mList.takeAt(row).objectCast<LdapCore>()->remove();
endRemoveRows();
}
QVariant LdapList::data(const QModelIndex &index, int role) const {
int row = index.row();
if (!index.isValid() || row < 0 || row >= mList.count()) return QVariant();
if (role == Qt::DisplayRole) {
return QVariant::fromValue(new LdapGui(mList[row].objectCast<LdapCore>()));
}
return QVariant();
}

View file

@ -0,0 +1,58 @@
/*
* Copyright (c) 2010-2024 Belledonne Communications SARL.
*
* This file is part of linphone-desktop
* (see https://www.linphone.org).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef LDAP_LIST_H_
#define LDAP_LIST_H_
#include "../../proxy/ListProxy.hpp"
#include "tool/AbstractObject.hpp"
#include "tool/thread/SafeConnection.hpp"
#include <QLocale>
class LdapCore;
class CoreModel;
// =============================================================================
class LdapList : public ListProxy, public AbstractObject {
Q_OBJECT
public:
static QSharedPointer<LdapList> create();
// Create a LdapCore and make connections to List.
QSharedPointer<LdapCore> createLdapCore(const std::shared_ptr<linphone::RemoteContactDirectory> &ldap);
LdapList(QObject *parent = Q_NULLPTR);
~LdapList();
void setSelf(QSharedPointer<LdapList> me);
void removeAllEntries();
void remove(const int &row);
virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
signals:
void lUpdate();
private:
QSharedPointer<SafeConnection<LdapList, CoreModel>> mModelConnection;
DECLARE_ABSTRACT_OBJECT
};
#endif

View file

@ -0,0 +1,64 @@
/*
* Copyright (c) 2010-2024 Belledonne Communications SARL.
*
* This file is part of linphone-desktop
* (see https://www.linphone.org).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "LdapProxy.hpp"
#include "LdapCore.hpp"
#include "LdapList.hpp"
DEFINE_ABSTRACT_OBJECT(LdapProxy)
LdapProxy::LdapProxy(QObject *parent) : LimitProxy(parent) {
mLdapList = LdapList::create();
setSourceModels(new SortFilterList(mLdapList.get(), Qt::AscendingOrder));
}
LdapProxy::~LdapProxy() {
setSourceModel(nullptr);
}
void LdapProxy::removeAllEntries() {
getListModel<LdapList>()->removeAllEntries();
}
void LdapProxy::removeEntriesWithFilter() {
QList<QSharedPointer<LdapCore>> itemList(rowCount());
for (auto i = rowCount() - 1; i >= 0; --i) {
auto item = getItemAt<SortFilterList, LdapList, LdapCore>(i);
itemList[i] = item;
}
for (auto item : itemList) {
mLdapList->ListProxy::remove(item.get());
}
}
bool LdapProxy::SortFilterList::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const {
return true;
}
bool LdapProxy::SortFilterList::lessThan(const QModelIndex &sourceLeft, const QModelIndex &sourceRight) const {
auto l = getItemAtSource<LdapList, LdapCore>(sourceLeft.row());
auto r = getItemAtSource<LdapList, LdapCore>(sourceRight.row());
return l->mSipDomain < r->mSipDomain;
}
void LdapProxy::updateView() {
mLdapList->lUpdate();
}

View file

@ -0,0 +1,52 @@
/*
* Copyright (c) 2010-2024 Belledonne Communications SARL.
*
* This file is part of linphone-desktop
* (see https://www.linphone.org).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef LDAP_PROXY_H_
#define LDAP_PROXY_H_
#include "../../proxy/LimitProxy.hpp"
#include "LdapList.hpp"
#include "tool/AbstractObject.hpp"
// =============================================================================
class LdapProxy : public LimitProxy, public AbstractObject {
Q_OBJECT
public:
DECLARE_SORTFILTER_CLASS()
LdapProxy(QObject *parent = Q_NULLPTR);
~LdapProxy();
Q_INVOKABLE void removeAllEntries();
Q_INVOKABLE void removeEntriesWithFilter();
Q_INVOKABLE void updateView();
signals:
void filterTextChanged();
protected:
QSharedPointer<LdapList> mLdapList;
DECLARE_ABSTRACT_OBJECT
};
#endif

View file

@ -0,0 +1,184 @@
/*
* Copyright (c) 2010-2024 Belledonne Communications SARL.
*
* This file is part of linphone-desktop
* (see https://www.linphone.org).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "CallHistoryCore.hpp"
#include "core/App.hpp"
#include "core/conference/ConferenceInfoCore.hpp"
#include "core/friend/FriendGui.hpp"
#include "model/call-history/CallHistoryModel.hpp"
#include "model/object/VariantObject.hpp"
#include "model/tool/ToolModel.hpp"
#include "tool/Utils.hpp"
#include "tool/thread/SafeConnection.hpp"
#include <QDateTime>
DEFINE_ABSTRACT_OBJECT(CallHistoryCore)
QSharedPointer<CallHistoryCore> CallHistoryCore::create(const std::shared_ptr<linphone::CallLog> &callLog) {
auto sharedPointer = QSharedPointer<CallHistoryCore>(new CallHistoryCore(callLog), &QObject::deleteLater);
sharedPointer->setSelf(sharedPointer);
sharedPointer->moveToThread(App::getInstance()->thread());
return sharedPointer;
}
CallHistoryCore::CallHistoryCore(const std::shared_ptr<linphone::CallLog> &callLog) : QObject(nullptr) {
// lDebug()<< "[CallHistoryCore] new" << this;
App::getInstance()->mEngine->setObjectOwnership(this, QQmlEngine::CppOwnership);
// Should be call from model Thread
mustBeInLinphoneThread(getClassName());
mCallHistoryModel = std::make_shared<CallHistoryModel>(callLog);
auto addr = callLog->getRemoteAddress();
mStatus = LinphoneEnums::fromLinphone(callLog->getStatus());
mDate = QDateTime::fromMSecsSinceEpoch(callLog->getStartDate() * 1000);
mIsOutgoing = callLog->getDir() == linphone::Call::Dir::Outgoing;
mDuration = QString::number(callLog->getDuration());
mIsConference = callLog->wasConference();
mCallId = Utils::coreStringToAppString(callLog->getCallId());
if (mIsConference) {
auto confinfo = callLog->getConferenceInfo();
mConferenceInfo = ConferenceInfoCore::create(confinfo);
mRemoteAddress = Utils::coreStringToAppString(confinfo->getUri()->asStringUriOnly());
mDisplayName = Utils::coreStringToAppString(confinfo->getSubject());
} else {
mRemoteAddress = Utils::coreStringToAppString(addr->asStringUriOnly());
auto linphoneFriend = ToolModel::findFriendByAddress(addr);
if (linphoneFriend) {
mFriendModel = Utils::makeQObject_ptr<FriendModel>(linphoneFriend);
mDisplayName = mFriendModel->getFullName();
} else {
mDisplayName = ToolModel::getDisplayName(addr);
}
}
}
CallHistoryCore::~CallHistoryCore() {
// lDebug()<< "[CallHistoryCore] delete" << this;
mustBeInMainThread("~" + getClassName());
}
void CallHistoryCore::setSelf(QSharedPointer<CallHistoryCore> me) {
mHistoryModelConnection = SafeConnection<CallHistoryCore, CallHistoryModel>::create(me, mCallHistoryModel);
mCoreModelConnection = SafeConnection<CallHistoryCore, CoreModel>::create(me, CoreModel::getInstance());
if (mFriendModel) {
mFriendModelConnection = SafeConnection<CallHistoryCore, FriendModel>::create(me, mFriendModel);
mFriendModelConnection->makeConnectToModel(&FriendModel::fullNameChanged, [this]() {
auto fullName = mFriendModel->getFullName();
mCoreModelConnection->invokeToCore([this, fullName]() {
if (fullName != mDisplayName) {
mDisplayName = fullName;
emit displayNameChanged();
}
});
});
}
auto update = [this, remoteAddress = mRemoteAddress](const std::shared_ptr<linphone::Friend> &updatedFriend) {
auto friendModel = Utils::makeQObject_ptr<FriendModel>(updatedFriend);
auto displayName = friendModel->getFullName();
auto fAddress = ToolModel::interpretUrl(remoteAddress);
bool isThisFriend = false;
for (auto f : friendModel->getAddresses()) {
if (f->weakEqual(fAddress)) {
isThisFriend = true;
break;
}
}
if (isThisFriend)
mCoreModelConnection->invokeToCore([this, friendModel, displayName]() {
mFriendModel = friendModel;
auto me = mCoreModelConnection->mCore.mQData; // Locked from previous call.
mFriendModelConnection = SafeConnection<CallHistoryCore, FriendModel>::create(me, mFriendModel);
mFriendModelConnection->makeConnectToModel(&FriendModel::fullNameChanged, [this]() {
auto fullName = mFriendModel->getFullName();
mCoreModelConnection->invokeToCore([this, fullName]() {
if (fullName != mDisplayName) {
mDisplayName = fullName;
emit displayNameChanged();
}
});
});
if (displayName != mDisplayName) {
mDisplayName = displayName;
emit displayNameChanged();
}
emit friendUpdated();
});
};
if (!ToolModel::findFriendByAddress(mRemoteAddress))
mCoreModelConnection->makeConnectToModel(&CoreModel::friendCreated, update);
mCoreModelConnection->makeConnectToModel(&CoreModel::friendUpdated, update);
mCoreModelConnection->makeConnectToModel(&CoreModel::friendRemoved, &CallHistoryCore::onRemoved);
// Update display name when display name has been requested from magic search cause not found in linphone friends
// (required to get the right display name if ldap friends cleared)
// This replace the display name set by a user by a default one, use the linphone address to
// get a correct display name
// mCoreModelConnection->makeConnectToModel(&CoreModel::magicSearchResultReceived,
// [this, remoteAddress = mRemoteAddress] {
// auto displayName = ToolModel::getDisplayName(remoteAddress);
// mCoreModelConnection->invokeToCore([this, displayName]() {
// mDisplayName = displayName;
// emit displayNameChanged();
// });
// });
}
ConferenceInfoGui *CallHistoryCore::getConferenceInfoGui() const {
return mConferenceInfo ? new ConferenceInfoGui(mConferenceInfo) : nullptr;
}
QString CallHistoryCore::getDuration() const {
return mDuration;
}
void CallHistoryCore::setDuration(const QString &duration) {
mustBeInMainThread(log().arg(Q_FUNC_INFO));
if (mDuration != duration) {
mDuration = duration;
emit durationChanged(mDuration);
}
}
void CallHistoryCore::remove() {
mHistoryModelConnection->invokeToModel([this]() {
mCallHistoryModel->removeCallHistory();
emit removed();
});
}
void CallHistoryCore::onRemoved(const std::shared_ptr<linphone::Friend> &updatedFriend) {
mustBeInLinphoneThread(log().arg(Q_FUNC_INFO));
auto fAddress = ToolModel::interpretUrl(mRemoteAddress);
bool isThisFriend = mFriendModel && updatedFriend == mFriendModel->getFriend();
if (!isThisFriend)
for (auto f : updatedFriend->getAddresses()) {
if (f->weakEqual(fAddress)) {
isThisFriend = true;
break;
}
}
if (isThisFriend) {
mFriendModel = nullptr;
mFriendModelConnection = nullptr;
mDisplayName = ToolModel::getDisplayName(fAddress);
emit displayNameChanged();
emit friendUpdated();
}
};

View file

@ -0,0 +1,88 @@
/*
* Copyright (c) 2010-2024 Belledonne Communications SARL.
*
* This file is part of linphone-desktop
* (see https://www.linphone.org).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef CALL_HISTORY_CORE_H_
#define CALL_HISTORY_CORE_H_
#include "core/conference/ConferenceInfoGui.hpp"
#include "tool/LinphoneEnums.hpp"
#include "tool/thread/SafeConnection.hpp"
#include <QDateTime>
#include <QObject>
#include <QSharedPointer>
#include <linphone++/linphone.hh>
class CallHistoryModel;
class FriendModel;
class CallHistoryCore : public QObject, public AbstractObject {
Q_OBJECT
Q_PROPERTY(QString displayName MEMBER mDisplayName NOTIFY displayNameChanged)
Q_PROPERTY(QString remoteAddress MEMBER mRemoteAddress CONSTANT)
Q_PROPERTY(bool isOutgoing MEMBER mIsOutgoing CONSTANT)
Q_PROPERTY(bool isConference MEMBER mIsConference CONSTANT)
Q_PROPERTY(ConferenceInfoGui *conferenceInfo READ getConferenceInfoGui CONSTANT)
Q_PROPERTY(QDateTime date MEMBER mDate CONSTANT)
Q_PROPERTY(LinphoneEnums::CallStatus status MEMBER mStatus CONSTANT)
Q_PROPERTY(QString duration READ getDuration WRITE setDuration NOTIFY durationChanged)
public:
static QSharedPointer<CallHistoryCore> create(const std::shared_ptr<linphone::CallLog> &callLogs);
CallHistoryCore(const std::shared_ptr<linphone::CallLog> &callLog);
~CallHistoryCore();
void setSelf(QSharedPointer<CallHistoryCore> me);
ConferenceInfoGui *getConferenceInfoGui() const;
QString getDuration() const;
void setDuration(const QString &duration);
void onRemoved(const std::shared_ptr<linphone::Friend> &updatedFriend);
Q_INVOKABLE void remove();
QString mRemoteAddress;
QString mDisplayName;
QDateTime mDate;
bool mIsOutgoing;
bool mIsConference = false;
LinphoneEnums::CallStatus mStatus;
QString mCallId;
signals:
void durationChanged(QString duration);
void displayNameChanged();
void friendUpdated(); // When a friend is created, this log is linked to it.
void removed();
private:
QString mDuration;
QSharedPointer<ConferenceInfoCore> mConferenceInfo = nullptr;
std::shared_ptr<CallHistoryModel> mCallHistoryModel;
std::shared_ptr<FriendModel> mFriendModel;
QSharedPointer<SafeConnection<CallHistoryCore, FriendModel>> mFriendModelConnection;
QSharedPointer<SafeConnection<CallHistoryCore, CallHistoryModel>> mHistoryModelConnection;
QSharedPointer<SafeConnection<CallHistoryCore, CoreModel>> mCoreModelConnection;
DECLARE_ABSTRACT_OBJECT
};
Q_DECLARE_METATYPE(CallHistoryCore *)
#endif

View file

@ -0,0 +1,40 @@
/*
* Copyright (c) 2010-2024 Belledonne Communications SARL.
*
* This file is part of linphone-desktop
* (see https://www.linphone.org).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "CallHistoryGui.hpp"
#include "core/App.hpp"
DEFINE_ABSTRACT_OBJECT(CallHistoryGui)
CallHistoryGui::CallHistoryGui(QSharedPointer<CallHistoryCore> core) {
// lDebug()<< "[CallHistoryGui] new" << this;
App::getInstance()->mEngine->setObjectOwnership(this, QQmlEngine::JavaScriptOwnership);
mCore = core;
if (isInLinphoneThread()) moveToThread(App::getInstance()->thread());
}
CallHistoryGui::~CallHistoryGui() {
mustBeInMainThread("~" + getClassName());
// lDebug()<< "[CallHistoryGui] delete" << this;
}
CallHistoryCore *CallHistoryGui::getCore() const {
return mCore.get();
}

View file

@ -0,0 +1,41 @@
/*
* Copyright (c) 2010-2024 Belledonne Communications SARL.
*
* This file is part of linphone-desktop
* (see https://www.linphone.org).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef CALL_HISTORY_GUI_H_
#define CALL_HISTORY_GUI_H_
#include "CallHistoryCore.hpp"
#include <QObject>
#include <QSharedPointer>
class CallHistoryGui : public QObject, public AbstractObject {
Q_OBJECT
Q_PROPERTY(CallHistoryCore *core READ getCore CONSTANT)
public:
CallHistoryGui(QSharedPointer<CallHistoryCore> core);
~CallHistoryGui();
CallHistoryCore *getCore() const;
QSharedPointer<CallHistoryCore> mCore;
DECLARE_ABSTRACT_OBJECT
};
#endif

View file

@ -0,0 +1,165 @@
/*
* Copyright (c) 2010-2024 Belledonne Communications SARL.
*
* This file is part of linphone-desktop
* (see https://www.linphone.org).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "CallHistoryList.hpp"
#include "CallHistoryGui.hpp"
#include "core/App.hpp"
#include "model/object/VariantObject.hpp"
#include "model/tool/ToolModel.hpp"
#include <QSharedPointer>
#include <linphone++/linphone.hh>
// =============================================================================
DEFINE_ABSTRACT_OBJECT(CallHistoryList)
QSharedPointer<CallHistoryList> CallHistoryList::create() {
auto model = QSharedPointer<CallHistoryList>(new CallHistoryList(), &QObject::deleteLater);
model->moveToThread(App::getInstance()->thread());
model->setSelf(model);
return model;
}
QSharedPointer<CallHistoryCore>
CallHistoryList::createCallHistoryCore(const std::shared_ptr<linphone::CallLog> &callLog) {
auto callHistoryCore = CallHistoryCore::create(callLog);
return callHistoryCore;
}
CallHistoryList::CallHistoryList(QObject *parent) : ListProxy(parent) {
mustBeInMainThread(getClassName());
App::getInstance()->mEngine->setObjectOwnership(this, QQmlEngine::CppOwnership);
}
CallHistoryList::~CallHistoryList() {
mustBeInMainThread("~" + getClassName());
mModelConnection = nullptr;
}
void CallHistoryList::setSelf(QSharedPointer<CallHistoryList> me) {
mModelConnection = SafeConnection<CallHistoryList, CoreModel>::create(me, CoreModel::getInstance());
mModelConnection->makeConnectToCore(&CallHistoryList::lUpdate, [this]() {
clearData();
emit listAboutToBeReset();
mModelConnection->invokeToModel([this]() {
mustBeInLinphoneThread(getClassName());
// Avoid copy to lambdas
QList<QSharedPointer<CallHistoryCore>> *callLogs = new QList<QSharedPointer<CallHistoryCore>>();
std::list<std::shared_ptr<linphone::CallLog>> linphoneCallLogs;
if (auto account = CoreModel::getInstance()->getCore()->getDefaultAccount()) {
linphoneCallLogs = account->getCallLogs();
}
for (auto it : linphoneCallLogs) {
auto model = createCallHistoryCore(it);
toConnect(model.get());
callLogs->push_back(model);
}
mModelConnection->invokeToCore([this, callLogs]() {
mustBeInMainThread(getClassName());
resetData<CallHistoryCore>(*callLogs);
delete callLogs;
});
});
});
mModelConnection->makeConnectToModel(&CoreModel::defaultAccountChanged,
[this]() { mModelConnection->invokeToCore([this]() { lUpdate(); }); });
mModelConnection->makeConnectToModel(
&CoreModel::callLogUpdated,
[this](const std::shared_ptr<linphone::Core> &core, const std::shared_ptr<linphone::CallLog> &callLog) {
QSharedPointer<CallHistoryCore> *callLogs = new QSharedPointer<CallHistoryCore>[1];
auto model = createCallHistoryCore(callLog);
callLogs[0] = model;
mModelConnection->invokeToCore([this, callLogs]() {
auto oldLog = std::find_if(mList.begin(), mList.end(), [callLogs](QSharedPointer<QObject> log) {
return (*callLogs)->mCallId == log.objectCast<CallHistoryCore>()->mCallId;
});
toConnect(callLogs->get());
if (oldLog == mList.end()) { // New
prepend(*callLogs);
} else { // Update (status, duration, etc …)
replace(oldLog->objectCast<CallHistoryCore>(), *callLogs);
}
delete[] callLogs;
});
});
mModelConnection->makeConnectToCore(&CallHistoryList::lRemoveEntriesForAddress, [this](QString address) {
mModelConnection->invokeToModel([this, address]() {
if (auto account = CoreModel::getInstance()->getCore()->getDefaultAccount()) {
auto linAddress = ToolModel::interpretUrl(address);
if (linAddress) {
auto core = CoreModel::getInstance()->getCore();
auto accountAddress = account->getParams() ? account->getParams()->getIdentityAddress() : nullptr;
if (accountAddress)
for (auto &callLog : core->getCallHistory(linAddress, accountAddress)) {
core->removeCallLog(callLog);
}
}
}
});
});
mModelConnection->makeConnectToCore(&CallHistoryList::lRemoveAllEntries, [this]() {
mModelConnection->invokeToModel([this]() {
if (auto account = CoreModel::getInstance()->getCore()->getDefaultAccount()) {
account->clearCallLogs();
}
});
});
emit lUpdate();
}
void CallHistoryList::toConnect(CallHistoryCore *data) {
connect(data, &CallHistoryCore::removed, this, [this, data]() { ListProxy::remove(data); });
}
void CallHistoryList::removeAllEntries() {
beginResetModel();
for (auto it = mList.rbegin(); it != mList.rend(); ++it) {
auto callHistory = it->objectCast<CallHistoryCore>();
callHistory->remove();
}
mList.clear();
endResetModel();
emit lRemoveAllEntries();
}
void CallHistoryList::removeEntriesWithFilter(QString filter) {
for (auto it = mList.rbegin(); it != mList.rend(); ++it) {
auto callHistory = it->objectCast<CallHistoryCore>();
if (callHistory->mDisplayName.contains(filter) || callHistory->mRemoteAddress.contains(filter)) {
callHistory->remove();
}
}
emit lRemoveEntriesForAddress(filter);
}
void CallHistoryList::remove(const int &row) {
auto item = mList[row].objectCast<CallHistoryCore>();
if (item) item->remove();
}
QVariant CallHistoryList::data(const QModelIndex &index, int role) const {
int row = index.row();
if (!index.isValid() || row < 0 || row >= mList.count()) return QVariant();
if (role == Qt::DisplayRole) {
return QVariant::fromValue(new CallHistoryGui(mList[row].objectCast<CallHistoryCore>()));
}
return QVariant();
}

View file

@ -0,0 +1,76 @@
/*
* Copyright (c) 2010-2024 Belledonne Communications SARL.
*
* This file is part of linphone-desktop
* (see https://www.linphone.org).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef CALL_HISTORY_LIST_H_
#define CALL_HISTORY_LIST_H_
#include "../proxy/ListProxy.hpp"
#include "tool/AbstractObject.hpp"
#include "tool/thread/SafeConnection.hpp"
#include <QLocale>
class CallGui;
class CallHistoryCore;
class CoreModel;
// =============================================================================
class CallHistoryList : public ListProxy, public AbstractObject {
Q_OBJECT
public:
static QSharedPointer<CallHistoryList> create();
// Create a CallHistoryCore and make connections to List.
QSharedPointer<CallHistoryCore> createCallHistoryCore(const std::shared_ptr<linphone::CallLog> &callLog);
CallHistoryList(QObject *parent = Q_NULLPTR);
~CallHistoryList();
void setSelf(QSharedPointer<CallHistoryList> me);
void toConnect(CallHistoryCore *data);
void removeAllEntries();
void removeEntriesWithFilter(QString filter);
void remove(const int &row);
virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
// virtual QHash<int, QByteArray> roleNames() const override {
// QHash<int, QByteArray> roles;
// roles[Qt::DisplayRole] = "gui";
// roles[Qt::DisplayRole + 1] = "name";
// roles[Qt::DisplayRole + 2] = "date";
// return roles;
// }
// void displayMore();
signals:
void lUpdate();
void lRemoveEntriesForAddress(QString address);
void lRemoveAllEntries();
void listAboutToBeReset();
private:
// Check the state from CallHistoryCore: sender() must be a CallHistoryCore.
void onStatusChanged();
bool mHaveCallHistory = false;
QSharedPointer<SafeConnection<CallHistoryList, CoreModel>> mModelConnection;
DECLARE_ABSTRACT_OBJECT
};
#endif

View file

@ -0,0 +1,70 @@
/*
* Copyright (c) 2010-2024 Belledonne Communications SARL.
*
* This file is part of linphone-desktop
* (see https://www.linphone.org).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "CallHistoryProxy.hpp"
#include "CallHistoryGui.hpp"
#include "CallHistoryList.hpp"
#include "core/App.hpp"
DEFINE_ABSTRACT_OBJECT(CallHistoryProxy)
CallHistoryProxy::CallHistoryProxy(QObject *parent) : LimitProxy(parent) {
mHistoryList = CallHistoryList::create();
connect(mHistoryList.get(), &CallHistoryList::listAboutToBeReset, this, &CallHistoryProxy::listAboutToBeReset);
setSourceModels(new SortFilterList(mHistoryList.get(), Qt::DescendingOrder));
connect(App::getInstance(), &App::currentDateChanged, this, [this] { emit mHistoryList->lUpdate(); });
}
CallHistoryProxy::~CallHistoryProxy() {
}
void CallHistoryProxy::removeAllEntries() {
mHistoryList->removeAllEntries();
}
void CallHistoryProxy::removeEntriesWithFilter(QString filter) {
mHistoryList->removeEntriesWithFilter(filter);
}
void CallHistoryProxy::reload() {
emit mHistoryList->lUpdate();
}
//------------------------------------------------------------------------------------------
bool CallHistoryProxy::SortFilterList::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const {
bool show = (mFilterText.isEmpty() || mFilterText == "*");
if (!show) {
QRegularExpression search(QRegularExpression::escape(mFilterText),
QRegularExpression::CaseInsensitiveOption |
QRegularExpression::UseUnicodePropertiesOption);
auto callLog = getItemAtSource<CallHistoryList, CallHistoryCore>(sourceRow);
show = callLog && (callLog->mDisplayName.contains(search) || callLog->mRemoteAddress.contains(search));
}
return show;
}
bool CallHistoryProxy::SortFilterList::lessThan(const QModelIndex &sourceLeft, const QModelIndex &sourceRight) const {
auto l = getItemAtSource<CallHistoryList, CallHistoryCore>(sourceLeft.row());
auto r = getItemAtSource<CallHistoryList, CallHistoryCore>(sourceRight.row());
return l->mDate < r->mDate;
}

View file

@ -0,0 +1,53 @@
/*
* Copyright (c) 2010-2024 Belledonne Communications SARL.
*
* This file is part of linphone-desktop
* (see https://www.linphone.org).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef CALL_HISTORY_PROXY_H_
#define CALL_HISTORY_PROXY_H_
#include "../proxy/LimitProxy.hpp"
#include "../proxy/SortFilterProxy.hpp"
#include "CallHistoryList.hpp"
#include "tool/AbstractObject.hpp"
#include <QSortFilterProxyModel>
// =============================================================================
class CallHistoryProxy : public LimitProxy, public AbstractObject {
Q_OBJECT
public:
DECLARE_SORTFILTER_CLASS()
CallHistoryProxy(QObject *parent = Q_NULLPTR);
~CallHistoryProxy();
Q_INVOKABLE void removeAllEntries();
Q_INVOKABLE void removeEntriesWithFilter(QString filter);
Q_INVOKABLE void reload();
signals:
void listAboutToBeReset();
protected:
QSharedPointer<CallHistoryList> mHistoryList;
DECLARE_ABSTRACT_OBJECT
};
#endif

View file

@ -0,0 +1,893 @@
/*
* Copyright (c) 2010-2026 Belledonne Communications SARL.
*
* This file is part of linphone-desktop
* (see https://www.linphone.org).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "CallCore.hpp"
#include "core/App.hpp"
#include "core/conference/ConferenceCore.hpp"
#include "core/conference/ConferenceGui.hpp"
#include "core/friend/FriendCore.hpp"
#include "core/setting/SettingsCore.hpp"
#include "model/tool/ToolModel.hpp"
#include "tool/Utils.hpp"
#include "tool/thread/SafeConnection.hpp"
#include <QQuickWindow>
DEFINE_ABSTRACT_OBJECT(CallCore)
/***********************************************************************/
ZrtpStats ZrtpStats::operator=(ZrtpStats s) {
mCipherAlgorithm = s.mCipherAlgorithm;
mKeyAgreementAlgorithm = s.mKeyAgreementAlgorithm;
mHashAlgorithm = s.mHashAlgorithm;
mAuthenticationAlgorithm = s.mAuthenticationAlgorithm;
mSasAlgorithm = s.mSasAlgorithm;
mIsPostQuantum = s.mIsPostQuantum;
return *this;
}
bool ZrtpStats::operator==(ZrtpStats s) {
return s.mCipherAlgorithm == mCipherAlgorithm && s.mKeyAgreementAlgorithm == mKeyAgreementAlgorithm &&
s.mHashAlgorithm == mHashAlgorithm && s.mAuthenticationAlgorithm == mAuthenticationAlgorithm &&
s.mSasAlgorithm == mSasAlgorithm && s.mIsPostQuantum == mIsPostQuantum;
}
bool ZrtpStats::operator!=(ZrtpStats s) {
return s.mCipherAlgorithm != mCipherAlgorithm || s.mKeyAgreementAlgorithm != mKeyAgreementAlgorithm ||
s.mHashAlgorithm != mHashAlgorithm || s.mAuthenticationAlgorithm != mAuthenticationAlgorithm ||
s.mSasAlgorithm != mSasAlgorithm || s.mIsPostQuantum != mIsPostQuantum;
}
AudioStats AudioStats::operator=(AudioStats s) {
mCodec = s.mCodec;
mBandwidth = s.mBandwidth;
mJitterBufferSize = s.mJitterBufferSize;
mLossRate = s.mLossRate;
return *this;
}
bool AudioStats::operator==(AudioStats s) {
return s.mCodec == mCodec && s.mBandwidth == mBandwidth && s.mLossRate == mLossRate &&
s.mJitterBufferSize == mJitterBufferSize;
}
bool AudioStats::operator!=(AudioStats s) {
return s.mCodec != mCodec || s.mBandwidth != mBandwidth || s.mLossRate != mLossRate ||
s.mJitterBufferSize != mJitterBufferSize;
}
VideoStats VideoStats::operator=(VideoStats s) {
mCodec = s.mCodec;
mBandwidth = s.mBandwidth;
mResolution = s.mResolution;
mFps = s.mFps;
mLossRate = s.mLossRate;
return *this;
}
bool VideoStats::operator==(VideoStats s) {
return s.mCodec == mCodec && s.mBandwidth == mBandwidth && s.mResolution == mResolution && s.mFps == mFps &&
s.mLossRate == mLossRate;
}
bool VideoStats::operator!=(VideoStats s) {
return s.mCodec != mCodec || s.mBandwidth != mBandwidth || s.mResolution != mResolution || s.mFps != mFps ||
s.mLossRate != mLossRate;
}
/***********************************************************************/
QSharedPointer<CallCore> CallCore::create(const std::shared_ptr<linphone::Call> &call) {
auto sharedPointer = QSharedPointer<CallCore>(new CallCore(call), &QObject::deleteLater);
sharedPointer->setSelf(sharedPointer);
sharedPointer->moveToThread(App::getInstance()->thread());
return sharedPointer;
}
CallCore::CallCore(const std::shared_ptr<linphone::Call> &call) : QObject(nullptr) {
lDebug() << "[CallCore] new" << this;
App::getInstance()->mEngine->setObjectOwnership(this, QQmlEngine::CppOwnership);
// Should be call from model Thread
mustBeInLinphoneThread(getClassName());
mDir = LinphoneEnums::fromLinphone(call->getDir());
mCallModel = Utils::makeQObject_ptr<CallModel>(call);
mCallModel->setSelf(mCallModel);
mDuration = call->getDuration();
mIsStarted = mDuration > 0;
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 =
videoDirection == linphone::MediaDirection::SendOnly || videoDirection == linphone::MediaDirection::SendRecv;
mState = LinphoneEnums::fromLinphone(call->getState());
auto remoteAddress = call->getCallLog()->getRemoteAddress();
mRemoteAddress = Utils::coreStringToAppString(remoteAddress->asStringUriOnly());
mRemoteUsername = Utils::coreStringToAppString(remoteAddress->getUsername());
auto linphoneFriend = ToolModel::findFriendByAddress(remoteAddress);
if (linphoneFriend)
mRemoteName = Utils::coreStringToAppString(
linphoneFriend->getVcard() ? linphoneFriend->getVcard()->getFullName() : linphoneFriend->getName());
if (mRemoteName.isEmpty()) mRemoteName = ToolModel::getDisplayName(remoteAddress);
mShouldFindRemoteFriend = !linphoneFriend;
if (mShouldFindRemoteFriend) {
mShouldFindRemoteFriend = CoreModel::getInstance()->getCore()->getRemoteContactDirectories().size() > 0;
}
mLocalAddress = Utils::coreStringToAppString(call->getCallLog()->getLocalAddress()->asStringUriOnly());
mStatus = LinphoneEnums::fromLinphone(call->getCallLog()->getStatus());
mTransferState = LinphoneEnums::fromLinphone(call->getTransferState());
mLocalToken = Utils::coreStringToAppString(mCallModel->getLocalAtuhenticationToken());
mRemoteTokens = mCallModel->getRemoteAtuhenticationTokens();
mEncryption = LinphoneEnums::fromLinphone(callParams->getMediaEncryption());
auto tokenVerified = call->getAuthenticationTokenVerified();
mIsMismatch = call->getZrtpCacheMismatchFlag();
mIsSecured = (mEncryption == LinphoneEnums::MediaEncryption::Zrtp && tokenVerified) ||
mEncryption == LinphoneEnums::MediaEncryption::Srtp ||
mEncryption == LinphoneEnums::MediaEncryption::Dtls;
if (mEncryption == LinphoneEnums::MediaEncryption::Zrtp) {
auto stats = call->getStats(linphone::StreamType::Audio);
if (stats) {
mZrtpStats.mCipherAlgorithm = Utils::coreStringToAppString(stats->getZrtpCipherAlgo());
mZrtpStats.mKeyAgreementAlgorithm = Utils::coreStringToAppString(stats->getZrtpKeyAgreementAlgo());
mZrtpStats.mHashAlgorithm = Utils::coreStringToAppString(stats->getZrtpHashAlgo());
mZrtpStats.mAuthenticationAlgorithm = Utils::coreStringToAppString(stats->getZrtpAuthTagAlgo());
mZrtpStats.mSasAlgorithm = Utils::coreStringToAppString(stats->getZrtpSasAlgo());
mZrtpStats.mIsPostQuantum = stats->isZrtpKeyAgreementAlgoPostQuantum();
}
}
auto conference = call->getConference();
mIsConference = conference != nullptr;
if (mIsConference) {
mConference = ConferenceCore::create(conference);
}
mMicrophoneMuted = conference ? conference->getMicrophoneMuted() : call->getMicrophoneMuted();
mSpeakerMuted = call->getSpeakerMuted();
mPaused = mState == LinphoneEnums::CallState::Pausing || mState == LinphoneEnums::CallState::Paused ||
mState == LinphoneEnums::CallState::PausedByRemote;
mRecording = callParams && callParams->isRecording();
mRemoteRecording = call->getRemoteParams() && call->getRemoteParams()->isRecording();
auto settingsModel = SettingsModel::getInstance();
mMicrophoneVolume = call->getRecordVolume();
mRecordable = mState == LinphoneEnums::CallState::StreamsRunning;
mConferenceVideoLayout = LinphoneEnums::fromLinphone(SettingsModel::getInstance()->getDefaultConferenceLayout());
auto videoSource = call->getVideoSource();
mVideoSourceDescriptor = VideoSourceDescriptorCore::create(videoSource ? videoSource->clone() : nullptr);
}
CallCore::~CallCore() {
lDebug() << "[CallCore] delete" << this;
mustBeInMainThread("~" + getClassName());
emit mCallModel->removeListener();
}
void CallCore::setSelf(QSharedPointer<CallCore> me) {
mCallModelConnection = SafeConnection<CallCore, CallModel>::create(me, mCallModel);
mCallModelConnection->makeConnectToCore(&CallCore::lSetMicrophoneMuted, [this](bool isMuted) {
mCallModelConnection->invokeToModel([this, isMuted]() { mCallModel->setMicrophoneMuted(isMuted); });
});
mCallModelConnection->makeConnectToModel(&CallModel::microphoneMutedChanged, [this](bool isMuted) {
mCallModelConnection->invokeToCore([this, isMuted]() { setMicrophoneMuted(isMuted); });
});
mCallModelConnection->makeConnectToModel(&CallModel::remoteVideoEnabledChanged, [this](bool enabled) {
mCallModelConnection->invokeToCore([this, enabled]() { setRemoteVideoEnabled(enabled); });
});
mCallModelConnection->makeConnectToCore(&CallCore::lSetSpeakerMuted, [this](bool isMuted) {
mCallModelConnection->invokeToModel([this, isMuted]() { mCallModel->setSpeakerMuted(isMuted); });
});
mCallModelConnection->makeConnectToModel(&CallModel::speakerMutedChanged, [this](bool isMuted) {
mCallModelConnection->invokeToCore([this, isMuted]() { setSpeakerMuted(isMuted); });
});
mCallModelConnection->makeConnectToCore(&CallCore::lSetCameraEnabled, [this](bool enabled) {
mCallModelConnection->invokeToModel([this, enabled]() { mCallModel->setCameraEnabled(enabled); });
});
mCallModelConnection->makeConnectToCore(&CallCore::lStartRecording, [this]() {
mCallModelConnection->invokeToModel([this]() { mCallModel->startRecording(); });
});
mCallModelConnection->makeConnectToCore(&CallCore::lStopRecording, [this]() {
mCallModelConnection->invokeToModel([this]() { mCallModel->stopRecording(); });
});
mCallModelConnection->makeConnectToModel(
&CallModel::recordingChanged, [this](const std::shared_ptr<linphone::Call> &call, bool 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(recordFile), true,
App::getInstance()->getOrCreateCallsWindow());
}
});
});
mCallModelConnection->makeConnectToCore(&CallCore::lCheckAuthenticationTokenSelected, [this](const QString &token) {
mCallModelConnection->invokeToModel([this, token]() { mCallModel->checkAuthenticationToken(token); });
});
mCallModelConnection->makeConnectToCore(&CallCore::lSkipZrtpAuthentication, [this]() {
mCallModelConnection->invokeToModel([this]() { mCallModel->skipZrtpAuthentication(); });
});
mCallModelConnection->makeConnectToModel(&CallModel::authenticationTokenVerified,
[this](const std::shared_ptr<linphone::Call> &call, bool verified) {
mCallModelConnection->invokeToCore([this, verified]() {
setTokenVerified(verified);
setIsMismatch(!verified);
emit tokenVerified();
});
});
mCallModelConnection->makeConnectToModel(&CallModel::remoteRecording,
[this](const std::shared_ptr<linphone::Call> &call, bool recording) {
bool confRecording = false;
if (call->getConference()) {
confRecording = call->getConference()->isRecording();
}
mCallModelConnection->invokeToCore([this, recording, confRecording]() {
if (mConference) mConference->setRecording(confRecording);
setRemoteRecording(recording);
});
});
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); });
});
mCallModelConnection->makeConnectToModel(&CallModel::qualityUpdated, [this](float quality) {
mCallModelConnection->invokeToCore([this, quality]() { setCurrentQuality(quality); });
});
mCallModelConnection->makeConnectToModel(&CallModel::microphoneVolumeChanged, [this](float volume) {
mCallModelConnection->invokeToCore([this, volume]() { setMicrophoneVolume(volume); });
});
mCallModelConnection->makeConnectToModel(
&CallModel::errorMessageChanged, [this](const QString &errorMessage) { setLastErrorMessage(errorMessage); });
mCallModelConnection->makeConnectToModel(&CallModel::stateChanged, [this](std::shared_ptr<linphone::Call> call,
linphone::Call::State state,
const std::string &message) {
bool isConf = call && call->getConference() != nullptr;
auto subject = call->getConference() ? Utils::coreStringToAppString(call->getConference()->getSubject()) : "";
mCallModelConnection->invokeToCore([this, state, subject, isConf]() {
lDebug() << log().arg("::onStateChanged") << LinphoneEnums::fromLinphone(state);
setRecordable(state == linphone::Call::State::StreamsRunning);
setPaused(state == linphone::Call::State::Paused || state == linphone::Call::State::PausedByRemote);
if (mConference) mConference->setSubject(subject);
// The conference object is not ready until the StreamRunning status,
// so it can't be used at this point
setIsConference(isConf);
});
mCallModelConnection->invokeToCore([this, state, message]() { setState(LinphoneEnums::fromLinphone(state)); });
});
mCallModelConnection->makeConnectToModel(&CallModel::statusChanged, [this](linphone::Call::Status status) {
mCallModelConnection->invokeToCore([this, status]() { setStatus(LinphoneEnums::fromLinphone(status)); });
});
mCallModelConnection->makeConnectToCore(&CallCore::lSetPaused, [this](bool paused) {
mCallModelConnection->invokeToModel([this, paused]() { mCallModel->setPaused(paused); });
});
mCallModelConnection->makeConnectToCore(&CallCore::lTransferCall, [this](QString address) {
mCallModelConnection->invokeToModel([this, address]() {
auto linAddr = ToolModel::interpretUrl(address);
if (linAddr) mCallModel->transferTo(linAddr);
});
});
mCallModelConnection->makeConnectToCore(&CallCore::lTransferCallToAnother, [this](QString uri) {
mCallModelConnection->invokeToModel([this, uri]() {
auto linCall = ToolModel::getCallByRemoteAddress(uri);
if (linCall) mCallModel->transferToAnother(linCall);
});
});
mCallModelConnection->makeConnectToModel(
&CallModel::transferStateChanged,
[this](const std::shared_ptr<linphone::Call> &call, linphone::Call::State state) {
mCallModelConnection->invokeToCore(
[this, state]() { setTransferState(LinphoneEnums::fromLinphone(state)); });
});
mCallModelConnection->makeConnectToModel(
&CallModel::encryptionChanged,
[this](const std::shared_ptr<linphone::Call> &call, bool on, const std::string &authenticationToken) {
auto encryption = LinphoneEnums::fromLinphone(call->getCurrentParams()->getMediaEncryption());
auto tokenVerified = mCallModel->getAuthenticationTokenVerified();
auto isCaseMismatch = mCallModel->getZrtpCaseMismatch();
auto localToken = Utils::coreStringToAppString(mCallModel->getLocalAtuhenticationToken());
QStringList remoteTokens = mCallModel->getRemoteAtuhenticationTokens();
mCallModelConnection->invokeToCore(
[this, call, encryption, tokenVerified, localToken, remoteTokens, isCaseMismatch]() {
setLocalToken(localToken);
setRemoteTokens(remoteTokens);
setIsMismatch(isCaseMismatch);
setTokenVerified(tokenVerified);
setEncryption(encryption);
});
auto mediaEncryption = call->getParams()->getMediaEncryption();
if (mediaEncryption == linphone::MediaEncryption::ZRTP) {
auto stats = call->getAudioStats();
ZrtpStats zrtpStats;
zrtpStats.mCipherAlgorithm = Utils::coreStringToAppString(stats->getZrtpCipherAlgo());
zrtpStats.mKeyAgreementAlgorithm = Utils::coreStringToAppString(stats->getZrtpKeyAgreementAlgo());
zrtpStats.mHashAlgorithm = Utils::coreStringToAppString(stats->getZrtpHashAlgo());
zrtpStats.mAuthenticationAlgorithm = Utils::coreStringToAppString(stats->getZrtpAuthTagAlgo());
zrtpStats.mSasAlgorithm = Utils::coreStringToAppString(stats->getZrtpSasAlgo());
zrtpStats.mIsPostQuantum = stats->isZrtpKeyAgreementAlgoPostQuantum();
mCallModelConnection->invokeToCore([this, zrtpStats]() { setZrtpStats(zrtpStats); });
}
});
mCallModelConnection->makeConnectToCore(&CallCore::lSetInputAudioDevice, [this](QString id) {
mCallModelConnection->invokeToModel([this, id]() {
auto device = ToolModel::findAudioDevice(id, linphone::AudioDevice::Capabilities::CapabilityRecord);
if (device) mCallModel->setInputAudioDevice(device);
});
});
mCallModelConnection->makeConnectToModel(&CallModel::inputAudioDeviceChanged, [this](const std::string &id) {
mCallModelConnection->invokeToCore([this, id]() {});
});
mCallModelConnection->makeConnectToCore(&CallCore::lSetOutputAudioDevice, [this](QString id) {
mCallModelConnection->invokeToModel([this, id]() {
auto device = ToolModel::findAudioDevice(id, linphone::AudioDevice::Capabilities::CapabilityPlay);
if (device) mCallModel->setOutputAudioDevice(device);
});
});
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); });
});
mCallModelConnection->makeConnectToCore(&CallCore::lAccept, [this](bool withVideo) {
mCallModelConnection->invokeToModel([this, withVideo]() { mCallModel->accept(withVideo); });
});
mCallModelConnection->makeConnectToCore(
&CallCore::lDecline, [this]() { mCallModelConnection->invokeToModel([this]() { mCallModel->decline(); }); });
mCallModelConnection->makeConnectToCore(&CallCore::lTerminate, [this]() {
mCallModelConnection->invokeToModel([this]() { mCallModel->terminate(); });
});
mCallModelConnection->makeConnectToCore(&CallCore::lTerminateAllCalls, [this]() {
mCallModelConnection->invokeToModel([this]() { mCallModel->terminateAllCalls(); });
});
mCallModelConnection->makeConnectToModel(
&CallModel::conferenceVideoLayoutChanged, [this](LinphoneEnums::ConferenceLayout layout) {
mCallModelConnection->invokeToCore([this, layout]() { setConferenceVideoLayout(layout); });
});
mCallModelConnection->makeConnectToCore(
&CallCore::lSetConferenceVideoLayout, [this](LinphoneEnums::ConferenceLayout layout) {
mCallModelConnection->invokeToModel([this, layout]() { mCallModel->changeConferenceVideoLayout(layout); });
});
mCallModelConnection->makeConnectToCore(
&CallCore::lSetVideoSourceDescriptor, [this](VideoSourceDescriptorGui *gui) {
mCallModelConnection->invokeToModel(
[this, model = gui->getCore()->getModel()]() { mCallModel->setVideoSourceDescriptorModel(model); });
});
mCallModelConnection->makeConnectToCore(&CallCore::lSendDtmf, [this](QString dtmf) {
mCallModelConnection->invokeToModel([this, dtmf]() { mCallModel->sendDtmf(dtmf); });
});
mCallModelConnection->makeConnectToModel(&CallModel::videoDescriptorChanged, [this]() {
auto videoSource = mCallModel->getMonitor()->getVideoSource();
auto core = VideoSourceDescriptorCore::create(videoSource ? videoSource->clone() : nullptr);
mCallModelConnection->invokeToCore([this, core]() { setVideoSourceDescriptor(core); });
});
mCallModelConnection->makeConnectToModel(
&CallModel::statsUpdated,
[this](const std::shared_ptr<linphone::Call> &call, const std::shared_ptr<const linphone::CallStats> &stats) {
if (stats->getType() == linphone::StreamType::Audio) {
AudioStats audioStats;
auto playloadType = call->getCurrentParams()->getUsedAudioPayloadType();
auto codecType = playloadType ? playloadType->getMimeType() : "";
auto codecRate = playloadType ? playloadType->getClockRate() / 1000 : 0;
audioStats.mCodec =
//: "Codec: %1 / %2 kHz"
tr("call_stats_codec_label").arg(Utils::coreStringToAppString(codecType)).arg(codecRate);
auto linAudioStats = call->getAudioStats();
if (linAudioStats) {
//: "Bande passante : %1 %2 kbits/s %3 %4 kbits/s"
audioStats.mBandwidth = tr("call_stats_bandwidth_label")
.arg("")
.arg(round(linAudioStats->getUploadBandwidth()))
.arg("")
.arg(round(linAudioStats->getDownloadBandwidth()));
//: "Taux de perte: %1% %2%"
audioStats.mLossRate = tr("call_stats_loss_rate_label")
.arg(linAudioStats->getSenderLossRate())
.arg(linAudioStats->getReceiverLossRate());
//: "Tampon de gigue: %1 ms"
audioStats.mJitterBufferSize =
tr("call_stats_jitter_buffer_label").arg(linAudioStats->getJitterBufferSizeMs());
}
setAudioStats(audioStats);
} else if (stats->getType() == linphone::StreamType::Video) {
VideoStats videoStats;
auto params = call->getCurrentParams();
auto playloadType = params->getUsedVideoPayloadType();
auto codecType = playloadType ? playloadType->getMimeType() : "";
auto codecRate = playloadType ? playloadType->getClockRate() / 1000 : 0;
videoStats.mCodec =
tr("call_stats_codec_label").arg(Utils::coreStringToAppString(codecType)).arg(codecRate);
auto linVideoStats = call->getVideoStats();
if (stats) {
videoStats.mBandwidth = tr("call_stats_bandwidth_label")
.arg("")
.arg(round(linVideoStats->getUploadBandwidth()))
.arg("")
.arg(round(linVideoStats->getDownloadBandwidth()));
videoStats.mLossRate = tr("call_stats_loss_rate_label")
.arg(linVideoStats->getSenderLossRate())
.arg(linVideoStats->getReceiverLossRate());
}
auto sentResolution =
params->getSentVideoDefinition() ? params->getSentVideoDefinition()->getName() : "";
auto receivedResolution =
params->getReceivedVideoDefinition() ? params->getReceivedVideoDefinition()->getName() : "";
//: "Définition vidéo : %1 %2 %3 %4"
videoStats.mResolution = tr("call_stats_resolution_label")
.arg("", Utils::coreStringToAppString(sentResolution), "",
Utils::coreStringToAppString(receivedResolution));
auto sentFps = params->getSentFramerate();
auto receivedFps = params->getReceivedFramerate();
//: "FPS : %1 %2 %3 %4"
videoStats.mFps = tr("call_stats_fps_label").arg("").arg(sentFps).arg("").arg(receivedFps);
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);
}
DEFINE_GET_SET_API(CallCore, bool, isStarted, IsStarted)
QString CallCore::getRemoteAddress() const {
return mRemoteAddress;
}
QString CallCore::getLocalAddress() const {
return mLocalAddress;
}
LinphoneEnums::CallStatus CallCore::getStatus() const {
return mStatus;
}
void CallCore::setStatus(LinphoneEnums::CallStatus status) {
mustBeInMainThread(log().arg(Q_FUNC_INFO));
if (mStatus != status) {
mStatus = status;
emit statusChanged(mStatus);
}
}
LinphoneEnums::CallDir CallCore::getDir() const {
return mDir;
}
void CallCore::setDir(LinphoneEnums::CallDir dir) {
mustBeInMainThread(log().arg(Q_FUNC_INFO));
if (mDir != dir) {
mDir = dir;
emit dirChanged(mDir);
}
}
LinphoneEnums::CallState CallCore::getState() const {
return mState;
}
void CallCore::setState(LinphoneEnums::CallState state) {
mustBeInMainThread(log().arg(Q_FUNC_INFO));
if (mState != state) {
mState = state;
emit stateChanged(mState);
}
}
QString CallCore::getLastErrorMessage() const {
return mLastErrorMessage;
}
void CallCore::setLastErrorMessage(const QString &message) {
if (mLastErrorMessage != message) {
mLastErrorMessage = message;
emit lastErrorMessageChanged();
}
}
int CallCore::getDuration() const {
return mDuration;
}
void CallCore::setDuration(int duration) {
if (mDuration != duration) {
mDuration = duration;
setIsStarted(mDuration > 0);
emit durationChanged(mDuration);
}
}
float CallCore::getCurrentQuality() const {
return mQuality;
}
void CallCore::setCurrentQuality(float quality) {
if (mQuality != quality) {
mQuality = quality;
emit qualityChanged(mQuality);
}
}
bool CallCore::getSpeakerMuted() const {
return mSpeakerMuted;
}
void CallCore::setSpeakerMuted(bool isMuted) {
if (mSpeakerMuted != isMuted) {
mSpeakerMuted = isMuted;
emit speakerMutedChanged();
}
}
bool CallCore::getMicrophoneMuted() const {
return mMicrophoneMuted;
}
void CallCore::setMicrophoneMuted(bool isMuted) {
if (mMicrophoneMuted != isMuted) {
mMicrophoneMuted = isMuted;
emit microphoneMutedChanged();
}
}
bool CallCore::getLocalVideoEnabled() const {
return mLocalVideoEnabled;
}
void CallCore::setLocalVideoEnabled(bool enabled) {
if (mLocalVideoEnabled != enabled) {
mLocalVideoEnabled = enabled;
lDebug() << "LocalVideoEnabled: " << mLocalVideoEnabled;
emit localVideoEnabledChanged();
}
}
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;
}
void CallCore::setPaused(bool paused) {
if (mPaused != paused) {
mPaused = paused;
emit pausedChanged();
}
}
bool CallCore::getTokenVerified() const {
return mTokenVerified;
}
void CallCore::setTokenVerified(bool verified) {
if (mTokenVerified != verified) {
mTokenVerified = verified;
emit securityUpdated();
}
}
bool CallCore::isMismatch() const {
return mIsMismatch;
}
void CallCore::setIsMismatch(bool mismatch) {
if (mIsMismatch != mismatch) {
mIsMismatch = mismatch;
emit securityUpdated();
}
}
ConferenceGui *CallCore::getConferenceGui() const {
return mConference ? new ConferenceGui(mConference) : nullptr;
}
QSharedPointer<ConferenceCore> CallCore::getConferenceCore() const {
return mConference;
}
void CallCore::setConference(const QSharedPointer<ConferenceCore> &conference) {
mustBeInMainThread(log().arg(Q_FUNC_INFO));
if (mConference != conference) {
mConference = conference;
lDebug() << "[CallCore] Set conference : " << mConference;
setIsConference(conference != nullptr);
emit conferenceChanged();
}
}
void CallCore::setIsConference(bool isConf) {
mustBeInMainThread(log().arg(Q_FUNC_INFO));
if (mIsConference != isConf) {
mIsConference = isConf;
emit isConferenceChanged();
}
}
bool CallCore::isConference() const {
return mIsConference;
}
QString CallCore::getLocalToken() {
return mLocalToken;
}
QStringList CallCore::getRemoteTokens() {
return mRemoteTokens;
}
void CallCore::setLocalToken(const QString &Token) {
if (mLocalToken != Token) {
mLocalToken = Token;
emit localTokenChanged();
}
}
void CallCore::setRemoteTokens(const QStringList &token) {
if (mRemoteTokens != token) {
mRemoteTokens = token;
emit remoteTokensChanged();
}
}
LinphoneEnums::MediaEncryption CallCore::getEncryption() const {
return mEncryption;
}
QString CallCore::getEncryptionString() const {
switch (mEncryption) {
case LinphoneEnums::MediaEncryption::Dtls:
//: DTLS
return tr("media_encryption_dtls");
case LinphoneEnums::MediaEncryption::None:
//: None
return tr("media_encryption_none");
case LinphoneEnums::MediaEncryption::Srtp:
//: SRTP
return tr("media_encryption_srtp");
case LinphoneEnums::MediaEncryption::Zrtp:
//: "ZRTP - Post quantique"
return tr("media_encryption_post_quantum");
default:
return QString();
}
}
void CallCore::setEncryption(LinphoneEnums::MediaEncryption encryption) {
if (mEncryption != encryption) {
mEncryption = encryption;
emit securityUpdated();
}
}
bool CallCore::getRemoteVideoEnabled() const {
return mRemoteVideoEnabled;
}
void CallCore::setRemoteVideoEnabled(bool enabled) {
if (mRemoteVideoEnabled != enabled) {
mRemoteVideoEnabled = enabled;
emit remoteVideoEnabledChanged(mRemoteVideoEnabled);
}
}
bool CallCore::getRecording() const {
return mRecording;
}
void CallCore::setRecording(bool recording) {
if (mRecording != recording) {
mRecording = recording;
emit recordingChanged();
}
}
bool CallCore::getRemoteRecording() const {
return mRemoteRecording;
}
void CallCore::setRemoteRecording(bool recording) {
if (mRemoteRecording != recording) {
mRemoteRecording = recording;
emit remoteRecordingChanged();
}
}
bool CallCore::getRecordable() const {
return mRecordable;
}
void CallCore::setRecordable(bool recordable) {
if (mRecordable != recordable) {
mRecordable = recordable;
emit recordableChanged();
}
}
float CallCore::getMicrophoneVolume() const {
return mMicrophoneVolume;
}
void CallCore::setMicrophoneVolume(float vol) {
if (mMicrophoneVolume != vol) {
mMicrophoneVolume = vol;
emit microphoneVolumeChanged();
}
}
LinphoneEnums::CallState CallCore::getTransferState() const {
return mTransferState;
}
void CallCore::setTransferState(LinphoneEnums::CallState state) {
if (mTransferState != state) {
mTransferState = state;
emit transferStateChanged();
}
}
LinphoneEnums::ConferenceLayout CallCore::getConferenceVideoLayout() const {
return mConferenceVideoLayout;
}
VideoSourceDescriptorGui *CallCore::getVideoSourceDescriptorGui() const {
return new VideoSourceDescriptorGui(mVideoSourceDescriptor);
}
void CallCore::setVideoSourceDescriptor(QSharedPointer<VideoSourceDescriptorCore> core) {
if (mVideoSourceDescriptor != core) {
mVideoSourceDescriptor = core;
emit videoSourceDescriptorChanged();
}
}
void CallCore::setConferenceVideoLayout(LinphoneEnums::ConferenceLayout layout) {
mustBeInMainThread(log().arg(Q_FUNC_INFO));
if (mConferenceVideoLayout != layout) {
mConferenceVideoLayout = layout;
emit conferenceVideoLayoutChanged();
}
}
std::shared_ptr<CallModel> CallCore::getModel() const {
return mCallModel;
}
ZrtpStats CallCore::getZrtpStats() const {
return mZrtpStats;
}
void CallCore::setZrtpStats(ZrtpStats stats) {
if (stats != mZrtpStats) {
mZrtpStats = stats;
emit zrtpStatsChanged();
}
}
AudioStats CallCore::getAudioStats() const {
return mAudioStats;
}
void CallCore::setAudioStats(AudioStats stats) {
if (stats != mAudioStats) {
mAudioStats = stats;
emit audioStatsChanged();
}
}
VideoStats CallCore::getVideoStats() const {
return mVideoStats;
}
void CallCore::setVideoStats(VideoStats stats) {
if (stats != mVideoStats) {
mVideoStats = stats;
emit videoStatsChanged();
}
}
void CallCore::findRemoteFriend(QSharedPointer<CallCore> me) {
mustBeInLinphoneThread(log().arg(Q_FUNC_INFO));
auto linphoneSearch = CoreModel::getInstance()->getCore()->createMagicSearch();
linphoneSearch->setLimitedSearch(true);
mRemoteMagicSearchModel = Utils::makeQObject_ptr<MagicSearchModel>(linphoneSearch);
mRemoteMagicSearchModel->setSelf(mRemoteMagicSearchModel);
mRemoteMagicSearchModelConnection = SafeConnection<CallCore, MagicSearchModel>::create(me, mRemoteMagicSearchModel);
mRemoteMagicSearchModelConnection->makeConnectToModel(
&MagicSearchModel::searchResultsReceived,
[this, remoteAdress = mRemoteAddress](const std::list<std::shared_ptr<linphone::SearchResult>> &results) {
mustBeInLinphoneThread(log().arg(Q_FUNC_INFO));
QString name;
auto remoteFriend = ToolModel::findFriendByAddress(remoteAdress); // Priorize what is stored.
if (!remoteFriend && results.size() > 0) remoteFriend = results.front()->getFriend(); // Then result friend.
if (remoteFriend) name = Utils::coreStringToAppString(remoteFriend->getName());
else if (results.size() > 0) // Then result address.
name = Utils::coreStringToAppString(results.front()->getAddress()->getDisplayName());
if (name.isEmpty() && results.size() > 0)
name = Utils::coreStringToAppString(results.front()->getAddress()->getUsername());
if (!name.isEmpty())
mRemoteMagicSearchModelConnection->invokeToCore([this, name]() {
mustBeInMainThread(log().arg(Q_FUNC_INFO));
if (name != mRemoteName) {
mRemoteName = name;
emit remoteNameChanged();
}
});
});
bool ldapSearch = SettingsModel::getInstance()->getUsernameOnlyForLdapLookupsInCalls();
bool cardDAVSearch = SettingsModel::getInstance()->getUsernameOnlyForCardDAVLookupsInCalls();
mRemoteMagicSearchModel->search(ldapSearch || cardDAVSearch ? mRemoteUsername : mRemoteAddress,
(ldapSearch ? (int)LinphoneEnums::MagicSearchSource::LdapServers : 0) |
(cardDAVSearch ? (int)LinphoneEnums::MagicSearchSource::RemoteCardDAV : 0),
LinphoneEnums::MagicSearchAggregation::Friend, -1);
}

View file

@ -0,0 +1,360 @@
/*
* Copyright (c) 2010-2024 Belledonne Communications SARL.
*
* This file is part of linphone-desktop
* (see https://www.linphone.org).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef CALL_CORE_H_
#define CALL_CORE_H_
#include "core/conference/ConferenceCore.hpp"
#include "core/conference/ConferenceGui.hpp"
#include "core/videoSource/VideoSourceDescriptorGui.hpp"
#include "model/call/CallModel.hpp"
#include "model/search/MagicSearchModel.hpp"
#include "tool/LinphoneEnums.hpp"
#include "tool/thread/SafeConnection.hpp"
#include <QObject>
#include <QSharedPointer>
#include <linphone++/linphone.hh>
struct ZrtpStats {
Q_GADGET
Q_PROPERTY(QString cipherAlgo MEMBER mCipherAlgorithm)
Q_PROPERTY(QString keyAgreementAlgo MEMBER mKeyAgreementAlgorithm)
Q_PROPERTY(QString hashAlgo MEMBER mHashAlgorithm)
Q_PROPERTY(QString authenticationAlgo MEMBER mAuthenticationAlgorithm)
Q_PROPERTY(QString sasAlgo MEMBER mSasAlgorithm)
Q_PROPERTY(bool isPostQuantum MEMBER mIsPostQuantum)
public:
bool mIsPostQuantum = false;
QString mCipherAlgorithm;
QString mKeyAgreementAlgorithm;
QString mHashAlgorithm;
QString mAuthenticationAlgorithm;
QString mSasAlgorithm;
ZrtpStats operator=(ZrtpStats s);
bool operator==(ZrtpStats s);
bool operator!=(ZrtpStats s);
};
struct AudioStats {
Q_GADGET
Q_PROPERTY(QString codec MEMBER mCodec)
Q_PROPERTY(QString bandwidth MEMBER mBandwidth)
Q_PROPERTY(QString lossRate MEMBER mLossRate)
Q_PROPERTY(QString jitterBufferSize MEMBER mJitterBufferSize)
public:
QString mCodec;
QString mBandwidth;
QString mLossRate;
QString mJitterBufferSize;
AudioStats operator=(AudioStats s);
bool operator==(AudioStats s);
bool operator!=(AudioStats s);
};
struct VideoStats {
Q_GADGET
Q_PROPERTY(QString codec MEMBER mCodec)
Q_PROPERTY(QString bandwidth MEMBER mBandwidth)
Q_PROPERTY(QString resolution MEMBER mResolution)
Q_PROPERTY(QString fps MEMBER mFps)
Q_PROPERTY(QString lossRate MEMBER mLossRate)
public:
QString mCodec;
QString mBandwidth;
QString mResolution;
QString mFps;
QString mLossRate;
VideoStats operator=(VideoStats s);
bool operator==(VideoStats s);
bool operator!=(VideoStats s);
};
class CallCore : public QObject, public AbstractObject {
Q_OBJECT
public:
Q_PROPERTY(LinphoneEnums::CallStatus status READ getStatus NOTIFY statusChanged)
Q_PROPERTY(LinphoneEnums::CallDir dir READ getDir NOTIFY dirChanged)
Q_PROPERTY(LinphoneEnums::CallState state READ getState NOTIFY stateChanged)
Q_PROPERTY(QString lastErrorMessage READ getLastErrorMessage NOTIFY lastErrorMessageChanged)
Q_PROPERTY(int duration READ getDuration NOTIFY durationChanged)
Q_PROPERTY(int quality READ getCurrentQuality NOTIFY qualityChanged)
Q_PROPERTY(bool speakerMuted READ getSpeakerMuted WRITE lSetSpeakerMuted NOTIFY speakerMutedChanged)
Q_PROPERTY(bool microphoneMuted READ getMicrophoneMuted WRITE lSetMicrophoneMuted NOTIFY microphoneMutedChanged)
Q_PROPERTY(bool paused READ getPaused WRITE lSetPaused NOTIFY pausedChanged)
Q_PROPERTY(QString remoteName MEMBER mRemoteName NOTIFY remoteNameChanged)
Q_PROPERTY(QString remoteAddress READ getRemoteAddress CONSTANT)
Q_PROPERTY(QString localAddress READ getLocalAddress CONSTANT)
Q_PROPERTY(bool tokenVerified READ getTokenVerified WRITE setTokenVerified NOTIFY securityUpdated)
Q_PROPERTY(bool isMismatch READ isMismatch WRITE setIsMismatch NOTIFY securityUpdated)
Q_PROPERTY(LinphoneEnums::MediaEncryption encryption READ getEncryption NOTIFY securityUpdated)
Q_PROPERTY(QString encryptionString READ getEncryptionString NOTIFY securityUpdated)
Q_PROPERTY(QString localToken READ getLocalToken WRITE setLocalToken MEMBER mLocalToken NOTIFY localTokenChanged)
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 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)
Q_PROPERTY(float microVolume READ getMicrophoneVolume WRITE setMicrophoneVolume NOTIFY microphoneVolumeChanged)
Q_PROPERTY(LinphoneEnums::CallState transferState READ getTransferState NOTIFY transferStateChanged)
Q_PROPERTY(ConferenceGui *conference READ getConferenceGui NOTIFY conferenceChanged)
Q_PROPERTY(bool isConference READ isConference NOTIFY isConferenceChanged)
Q_PROPERTY(LinphoneEnums::ConferenceLayout conferenceVideoLayout READ getConferenceVideoLayout WRITE
lSetConferenceVideoLayout NOTIFY conferenceVideoLayoutChanged)
Q_PROPERTY(VideoSourceDescriptorGui *videoSourceDescriptor READ getVideoSourceDescriptorGui WRITE
lSetVideoSourceDescriptor NOTIFY videoSourceDescriptorChanged)
Q_PROPERTY(ZrtpStats zrtpStats READ getZrtpStats WRITE setZrtpStats NOTIFY zrtpStatsChanged)
Q_PROPERTY(AudioStats audioStats READ getAudioStats WRITE setAudioStats NOTIFY audioStatsChanged)
Q_PROPERTY(VideoStats videoStats READ getVideoStats WRITE setVideoStats NOTIFY videoStatsChanged)
DECLARE_GUI_GETSET(bool, isStarted, IsStarted)
// Should be call from model Thread. Will be automatically in App thread after initialization
static QSharedPointer<CallCore> create(const std::shared_ptr<linphone::Call> &call);
CallCore(const std::shared_ptr<linphone::Call> &call);
~CallCore();
void setSelf(QSharedPointer<CallCore> me);
QString getRemoteAddress() const;
QString getLocalAddress() const;
LinphoneEnums::CallStatus getStatus() const;
void setStatus(LinphoneEnums::CallStatus status);
LinphoneEnums::CallDir getDir() const;
void setDir(LinphoneEnums::CallDir dir);
LinphoneEnums::CallState getState() const;
void setState(LinphoneEnums::CallState state);
QString getLastErrorMessage() const;
void setLastErrorMessage(const QString &message);
int getDuration() const;
void setDuration(int duration);
float getCurrentQuality() const;
void setCurrentQuality(float quality);
bool getSpeakerMuted() const;
void setSpeakerMuted(bool isMuted);
bool getMicrophoneMuted() const;
void setMicrophoneMuted(bool isMuted);
bool getPaused() const;
void setPaused(bool paused);
bool getTokenVerified() const;
void setTokenVerified(bool verified);
bool isMismatch() const;
void setIsMismatch(bool mismatch);
ConferenceGui *getConferenceGui() const;
QSharedPointer<ConferenceCore> getConferenceCore() const;
void setConference(const QSharedPointer<ConferenceCore> &conference);
void setIsConference(bool isConf);
bool isConference() const;
QString getLocalToken();
void setLocalToken(const QString &token);
QStringList getRemoteTokens();
void setRemoteTokens(const QStringList &Tokens);
LinphoneEnums::MediaEncryption getEncryption() const;
QString getEncryptionString() const;
void setEncryption(LinphoneEnums::MediaEncryption encryption);
bool getRemoteVideoEnabled() const;
void setRemoteVideoEnabled(bool enabled);
bool getLocalVideoEnabled() const;
void setLocalVideoEnabled(bool enabled);
bool getCameraEnabled() const;
void setCameraEnabled(bool enabled);
bool getRecording() const;
void setRecording(bool recording);
bool getRemoteRecording() const;
void setRemoteRecording(bool recording);
bool getRecordable() const;
void setRecordable(bool recordable);
float getMicrophoneVolume() const;
void setMicrophoneVolume(float vol);
QString getInputDeviceName() const;
void setInputDeviceName(const QString &id);
LinphoneEnums::CallState getTransferState() const;
void setTransferState(LinphoneEnums::CallState state);
LinphoneEnums::ConferenceLayout getConferenceVideoLayout() const;
void setConferenceVideoLayout(LinphoneEnums::ConferenceLayout layout);
VideoSourceDescriptorGui *getVideoSourceDescriptorGui() const;
void setVideoSourceDescriptor(QSharedPointer<VideoSourceDescriptorCore> core);
std::shared_ptr<CallModel> getModel() const;
ZrtpStats getZrtpStats() const;
void setZrtpStats(ZrtpStats stats);
AudioStats getAudioStats() const;
void setAudioStats(AudioStats stats);
VideoStats getVideoStats() const;
void setVideoStats(VideoStats stats);
void findRemoteFriend(QSharedPointer<CallCore> me);
signals:
void statusChanged(LinphoneEnums::CallStatus status);
void stateChanged(LinphoneEnums::CallState state);
void dirChanged(LinphoneEnums::CallDir dir);
void lastErrorMessageChanged();
void durationChanged(int duration);
void qualityChanged(float quality);
void speakerMutedChanged();
void microphoneMutedChanged();
void pausedChanged();
void transferStateChanged();
void securityUpdated();
void tokenVerified();
void localTokenChanged();
void remoteTokensChanged();
void remoteVideoEnabledChanged(bool remoteVideoEnabled);
void localVideoEnabledChanged();
void cameraEnabledChanged();
void recordingChanged();
void remoteRecordingChanged();
void recordableChanged();
void microphoneVolumeChanged();
void conferenceChanged();
void isConferenceChanged();
void conferenceVideoLayoutChanged();
void videoSourceDescriptorChanged();
void zrtpStatsChanged();
void audioStatsChanged();
void videoStatsChanged();
void remoteNameChanged();
// Linphone commands
void lAccept(bool withVideo); // Accept an incoming call
void lDecline(); // Decline an incoming call
void lTerminate(); // Hangup a call
void lTerminateAllCalls(); // Hangup all calls
void lSetSpeakerMuted(bool muted);
void lSetMicrophoneMuted(bool isMuted);
void lSetCameraEnabled(bool enabled);
void lSetVideoEnabled(bool enabled);
void lSetPaused(bool paused);
void lTransferCall(QString address);
void lTransferCallToAnother(QString uri);
void lStartRecording();
void lStopRecording();
void lCheckAuthenticationTokenSelected(const QString &token);
void lSkipZrtpAuthentication();
void lSetInputAudioDevice(QString id);
void lSetOutputAudioDevice(QString id);
void lSetConferenceVideoLayout(LinphoneEnums::ConferenceLayout layout);
void lSetVideoSourceDescriptor(VideoSourceDescriptorGui *gui);
void lSendDtmf(QString dtmf);
/* TODO
Q_INVOKABLE void acceptWithVideo();
Q_INVOKABLE void askForTransfer();
Q_INVOKABLE void askForAttendedTransfer();
Q_INVOKABLE bool transferTo(const QString &sipAddress);
Q_INVOKABLE bool transferToAnother(const QString &remoteAddress);
Q_INVOKABLE bool getRemoteVideoEnabled() const;
Q_INVOKABLE void acceptVideoRequest();
Q_INVOKABLE void rejectVideoRequest();
Q_INVOKABLE void takeSnapshot();
Q_INVOKABLE void verifyAuthenticationToken(bool verify);
Q_INVOKABLE void updateStreams();
*/
private:
std::shared_ptr<CallModel> mCallModel;
QSharedPointer<ConferenceCore> mConference;
QSharedPointer<VideoSourceDescriptorCore> mVideoSourceDescriptor;
LinphoneEnums::CallStatus mStatus;
LinphoneEnums::CallState mState;
LinphoneEnums::CallState mTransferState;
LinphoneEnums::CallDir mDir;
LinphoneEnums::ConferenceLayout mConferenceVideoLayout;
LinphoneEnums::MediaEncryption mEncryption;
QString mLastErrorMessage;
QString mRemoteName;
QString mRemoteUsername;
QString mRemoteAddress;
QString mLocalAddress;
bool mTokenVerified = false;
bool mIsSecured = false;
bool mIsMismatch = false;
int mDuration = 0;
float mQuality = 0;
bool mSpeakerMuted = false;
bool mMicrophoneMuted = false;
bool mLocalVideoEnabled = false;
bool mCameraEnabled = false;
bool mVideoEnabled = false;
bool mPaused = false;
bool mRemoteVideoEnabled = false;
bool mRecording = false;
bool mRemoteRecording = false;
bool mRecordable = false;
bool mIsConference = false;
QString mLocalToken;
QStringList mRemoteTokens;
float mMicrophoneVolume;
QSharedPointer<SafeConnection<CallCore, CallModel>> mCallModelConnection;
ZrtpStats mZrtpStats;
AudioStats mAudioStats;
VideoStats mVideoStats;
std::shared_ptr<MagicSearchModel> mRemoteMagicSearchModel;
bool mShouldFindRemoteFriend;
QSharedPointer<SafeConnection<CallCore, MagicSearchModel>> mRemoteMagicSearchModelConnection;
DECLARE_ABSTRACT_OBJECT
};
Q_DECLARE_METATYPE(CallCore *)
#endif

View file

@ -0,0 +1,38 @@
/*
* Copyright (c) 2010-2024 Belledonne Communications SARL.
*
* This file is part of linphone-desktop
* (see https://www.linphone.org).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "CallGui.hpp"
#include "core/App.hpp"
DEFINE_ABSTRACT_OBJECT(CallGui)
CallGui::CallGui(QSharedPointer<CallCore> core) {
App::getInstance()->mEngine->setObjectOwnership(this, QQmlEngine::JavaScriptOwnership);
mCore = core;
if (isInLinphoneThread()) moveToThread(App::getInstance()->thread());
}
CallGui::~CallGui() {
mustBeInMainThread("~" + getClassName());
}
CallCore *CallGui::getCore() const {
return mCore.get();
}

View file

@ -0,0 +1,41 @@
/*
* Copyright (c) 2010-2024 Belledonne Communications SARL.
*
* This file is part of linphone-desktop
* (see https://www.linphone.org).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef CALL_GUI_H_
#define CALL_GUI_H_
#include "CallCore.hpp"
#include <QObject>
#include <QSharedPointer>
class CallGui : public QObject, public AbstractObject {
Q_OBJECT
Q_PROPERTY(CallCore *core READ getCore CONSTANT)
public:
CallGui(QSharedPointer<CallCore> core);
~CallGui();
CallCore *getCore() const;
QSharedPointer<CallCore> mCore;
DECLARE_ABSTRACT_OBJECT
};
#endif

View file

@ -0,0 +1,231 @@
/*
* Copyright (c) 2010-2024 Belledonne Communications SARL.
*
* This file is part of linphone-desktop
* (see https://www.linphone.org).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "CallList.hpp"
#include "CallCore.hpp"
#include "CallGui.hpp"
#include "core/App.hpp"
#include "model/tool/ToolModel.hpp"
#include <QSharedPointer>
#include <linphone++/linphone.hh>
// =============================================================================
DEFINE_ABSTRACT_OBJECT(CallList)
QSharedPointer<CallList> CallList::create() {
auto model = QSharedPointer<CallList>(new CallList(), &QObject::deleteLater);
model->moveToThread(App::getInstance()->thread());
model->setSelf(model);
return model;
}
QSharedPointer<CallCore> CallList::createCallCore(const std::shared_ptr<linphone::Call> &call) {
auto callCore = CallCore::create(call);
connect(callCore.get(), &CallCore::stateChanged, this, &CallList::onStateChanged);
return callCore;
}
CallList::CallList(QObject *parent) : ListProxy(parent) {
mustBeInMainThread(getClassName());
App::getInstance()->mEngine->setObjectOwnership(this, QQmlEngine::CppOwnership);
}
CallList::~CallList() {
mustBeInMainThread("~" + getClassName());
mModelConnection = nullptr;
}
void CallList::setSelf(QSharedPointer<CallList> me) {
mModelConnection = SafeConnection<CallList, CoreModel>::create(me, CoreModel::getInstance());
mModelConnection->makeConnectToCore(&CallList::lUpdate, [this]() {
mModelConnection->invokeToModel([this]() {
// Avoid copy to lambdas
QList<QSharedPointer<CallCore>> *calls = new QList<QSharedPointer<CallCore>>();
mustBeInLinphoneThread(getClassName());
auto linphoneCalls = CoreModel::getInstance()->getCore()->getCalls();
auto currentCall = CoreModel::getInstance()->getCore()->getCurrentCall();
QSharedPointer<CallCore> currentCallCore;
for (auto it : linphoneCalls) {
auto model = createCallCore(it);
if (it == currentCall) currentCallCore = model;
calls->push_back(model);
}
mModelConnection->invokeToCore([this, calls, currentCallCore]() {
mustBeInMainThread(getClassName());
resetData<CallCore>(*calls);
setHaveCall(calls->size() > 0);
setCurrentCallCore(currentCallCore);
delete calls;
});
});
});
mModelConnection->makeConnectToCore(&CallList::lMergeAll, [this]() {
mModelConnection->invokeToModel([this]() {
auto core = CoreModel::getInstance()->getCore();
auto currentCalls = CoreModel::getInstance()->getCore()->getCalls();
std::shared_ptr<linphone::Conference> conference = nullptr;
// Search a managable conference from calls
for (auto call : currentCalls) {
auto dbConference = call->getConference();
if (dbConference && dbConference->getMe()->isAdmin()) {
conference = dbConference;
break;
}
}
auto currentCall = CoreModel::getInstance()->getCore()->getCurrentCall();
bool enablingVideo = false;
if (currentCall) enablingVideo = currentCall->getCurrentParams()->videoEnabled();
if (!conference) {
auto audioVideoConfFactoryUri =
core->getDefaultAccount()->getParams()->getAudioVideoConferenceFactoryAddress();
QString subject = audioVideoConfFactoryUri
//: Remote group call
? tr("remote_group_call")
//: "Local group call"
: tr("local_group_call");
auto conference = ToolModel::createConference(subject, nullptr);
if (!conference) {
lWarning() << log().arg("Failed to merge calls");
mModelConnection->invokeToCore([] {
Utils::showInformationPopup(tr("info_popup_error_title"),
//: Failed to merge calls !
tr("info_popup_merge_calls_failed_message"), false);
});
return;
} else {
conference->addParticipants(currentCalls);
}
}
// emit lUpdate();
});
});
mModelConnection->makeConnectToModel(&CoreModel::firstCallStarted,
[this]() { mModelConnection->invokeToCore([this]() { lUpdate(); }); });
mModelConnection->makeConnectToModel(&CoreModel::lastCallEnded, [this]() {
mModelConnection->invokeToCore([this]() {
setHaveCall(false);
setCurrentCall(nullptr);
});
});
mModelConnection->makeConnectToModel(&CoreModel::callCreated, [this](const std::shared_ptr<linphone::Call> &call) {
auto model = createCallCore(call);
mModelConnection->invokeToCore([this, model]() {
// We set the current here and not on firstCallStarted event because we don't want to add unicity check
// while keeping the same model between list and current call.
if (mList.size() == 0) setCurrentCallCore(model);
add(model);
});
});
lUpdate();
}
QSharedPointer<CallCore> CallList::getCurrentCallCore() const {
return mCurrentCall;
}
CallGui *CallList::getCurrentCall() const {
auto call = getCurrentCallCore();
if (call) return new CallGui(call);
else return nullptr;
}
void CallList::setCurrentCall(CallGui *callGui) {
setCurrentCallCore(callGui ? callGui->mCore : nullptr);
}
void CallList::setCurrentCallCore(QSharedPointer<CallCore> call) {
if (mCurrentCall != call) {
mCurrentCall = call;
emit currentCallChanged();
}
}
bool CallList::getHaveCall() const {
return mHaveCall;
}
void CallList::setHaveCall(bool haveCall) {
if (mHaveCall != haveCall) {
mHaveCall = haveCall;
emit haveCallChanged();
}
}
QSharedPointer<CallCore> CallList::getNextCall() {
auto currentCall = getCurrentCallCore();
for (auto &item : getSharedList<CallCore>()) {
if (item != currentCall) return item;
}
return nullptr;
}
QSharedPointer<CallCore> CallList::getFirstIncommingPendingCall() {
auto callList = getSharedList<CallCore>();
auto it = std::find_if(callList.begin(), callList.end(), [](const QSharedPointer<CallCore> call) {
return call->getState() == LinphoneEnums::CallState::IncomingReceived;
});
if (it == callList.end()) return nullptr;
return *it;
}
void CallList::onStateChanged() {
auto call = dynamic_cast<CallCore *>(sender());
switch (call->getState()) {
case LinphoneEnums::CallState::StreamsRunning:
case LinphoneEnums::CallState::Resuming: {
auto sharedCall = get(call);
setCurrentCallCore(sharedCall ? sharedCall.objectCast<CallCore>() : nullptr);
break;
}
case LinphoneEnums::CallState::Released: {
auto sharedCall = get(call);
if (sharedCall) {
auto currentCall = getCurrentCallCore();
sharedCall->disconnect(this);
// Update current call
if (currentCall == sharedCall) {
auto nextCall = getNextCall();
if (nextCall) {
// Unpause the next call. The current call will change on resume.
// Assumption: All calls that are not the current are paused.
nextCall->lSetPaused(false);
}
setCurrentCallCore(nextCall);
}
bool removed = remove(sharedCall);
}
break;
}
default: {
}
}
}
QVariant CallList::data(const QModelIndex &index, int role) const {
int row = index.row();
if (!index.isValid() || row < 0 || row >= mList.count()) return QVariant();
if (role == Qt::DisplayRole) return QVariant::fromValue(new CallGui(mList[row].objectCast<CallCore>()));
return QVariant();
}

View file

@ -0,0 +1,77 @@
/*
* Copyright (c) 2010-2024 Belledonne Communications SARL.
*
* This file is part of linphone-desktop
* (see https://www.linphone.org).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef CALL_LIST_H_
#define CALL_LIST_H_
#include "../proxy/ListProxy.hpp"
#include "tool/AbstractObject.hpp"
#include "tool/thread/SafeConnection.hpp"
#include <QLocale>
class CallGui;
class CallCore;
class CoreModel;
// =============================================================================
class CallList : public ListProxy, public AbstractObject {
Q_OBJECT
Q_PROPERTY(CallGui *currentCall READ getCurrentCall WRITE setCurrentCall NOTIFY currentCallChanged)
public:
static QSharedPointer<CallList> create();
// Create a CallCore and make connections to List.
QSharedPointer<CallCore> createCallCore(const std::shared_ptr<linphone::Call> &call);
CallList(QObject *parent = Q_NULLPTR);
~CallList();
void setSelf(QSharedPointer<CallList> me);
CallGui *getCurrentCall() const; // Used for Ui
QSharedPointer<CallCore> getCurrentCallCore() const;
void setCurrentCall(CallGui *callGui);
void setCurrentCallCore(QSharedPointer<CallCore> call);
bool getHaveCall() const;
void setHaveCall(bool haveCall);
// Get the next call after the current one. Used to switch the current call.
// At the moment, it select the last call in the list.
QSharedPointer<CallCore> getNextCall();
QSharedPointer<CallCore> getFirstIncommingPendingCall();
virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
signals:
void lUpdate();
void lMergeAll();
void haveCallChanged();
void currentCallChanged();
private:
// Check the state from CallCore: sender() must be a CallCore.
void onStateChanged();
bool mHaveCall = false;
QSharedPointer<CallCore> mCurrentCall;
QSharedPointer<SafeConnection<CallList, CoreModel>> mModelConnection;
DECLARE_ABSTRACT_OBJECT
};
#endif

View file

@ -0,0 +1,100 @@
/*
* Copyright (c) 2010-2024 Belledonne Communications SARL.
*
* This file is part of linphone-desktop
* (see https://www.linphone.org).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "CallProxy.hpp"
#include "CallGui.hpp"
#include "CallList.hpp"
#include "core/App.hpp"
DEFINE_ABSTRACT_OBJECT(CallProxy)
CallProxy::CallProxy() : SortFilterProxy() {
mShowCurrentCall = true;
}
CallProxy::~CallProxy() {
}
CallGui *CallProxy::getCurrentCall() {
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) {
qobject_cast<CallList *>(sourceModel())->setCurrentCall(call);
}
// Reset the default account to let UI build its new object if needed.
void CallProxy::resetCurrentCall() {
mCurrentCall = nullptr;
emit this->currentCallChanged(); // Warn the UI
}
bool CallProxy::getHaveCall() const {
auto model = qobject_cast<CallList *>(sourceModel());
return model ? model->getHaveCall() : false;
}
void CallProxy::setSourceModel(QAbstractItemModel *model) {
auto oldCallList = qobject_cast<CallList *>(sourceModel());
if (oldCallList) {
disconnect(oldCallList);
}
auto newCallList = dynamic_cast<CallList *>(model);
if (newCallList) {
connect(newCallList, &CallList::currentCallChanged, this, &CallProxy::resetCurrentCall, Qt::QueuedConnection);
connect(newCallList, &CallList::haveCallChanged, this, &CallProxy::haveCallChanged, Qt::QueuedConnection);
connect(this, &CallProxy::lMergeAll, newCallList, &CallList::lMergeAll);
}
QSortFilterProxyModel::setSourceModel(model);
}
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);
show = call->getRemoteAddress().contains(search);
}
return show;
}
bool CallProxy::lessThan(const QModelIndex &sourceLeft, const QModelIndex &sourceRight) const {
auto l = getItemAtSource<CallList, CallCore>(sourceLeft.row());
auto r = getItemAtSource<CallList, CallCore>(sourceRight.row());
return l->getRemoteAddress() < r->getRemoteAddress();
}

View file

@ -0,0 +1,71 @@
/*
* Copyright (c) 2010-2024 Belledonne Communications SARL.
*
* This file is part of linphone-desktop
* (see https://www.linphone.org).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef CALL_PROXY_H_
#define CALL_PROXY_H_
#include "../proxy/LimitProxy.hpp"
#include "core/call/CallGui.hpp"
#include "core/call/CallList.hpp"
#include "tool/AbstractObject.hpp"
// =============================================================================
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:
CallProxy();
~CallProxy();
// Get a new object from List or give the stored one.
CallGui *getCurrentCall();
// TODO for manual setting. Changing the currentCall is automatically done by call->onStateChanged() on
// StreamRunning and Resuming
void setCurrentCall(CallGui *call);
void resetCurrentCall(); // Reset the default account to let UI build its new object if needed.
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
};
#endif

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2010-2020 Belledonne Communications SARL.
* Copyright (c) 2010-2024 Belledonne Communications SARL.
*
* This file is part of linphone-desktop
* (see https://www.linphone.org).
@ -23,22 +23,19 @@
#include <QThread>
#include <QTimer>
#include "components/core/CoreManager.hpp"
#include "CameraDummy.hpp"
// =============================================================================
CameraDummy::CameraDummy(){
CameraDummy::CameraDummy() {
}
QOpenGLFramebufferObject *CameraDummy::createFramebufferObject (const QSize &size){
QOpenGLFramebufferObject *CameraDummy::createFramebufferObject(const QSize &size) {
return new QOpenGLFramebufferObject(size);
}
void CameraDummy::render (){
void CameraDummy::render() {
}
void CameraDummy::synchronize (QQuickFramebufferObject *item){
}
void CameraDummy::synchronize(QQuickFramebufferObject *item) {
}

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2022 Belledonne Communications SARL.
* Copyright (c) 2024 Belledonne Communications SARL.
*
* This file is part of linphone-desktop
* (see https://www.linphone.org).
@ -26,12 +26,12 @@
// =============================================================================
class CameraDummy : public QQuickFramebufferObject::Renderer{
class CameraDummy : public QQuickFramebufferObject::Renderer {
public:
CameraDummy();
QOpenGLFramebufferObject *createFramebufferObject (const QSize &size) override;
void render () override;
void synchronize (QQuickFramebufferObject *item) override;
QOpenGLFramebufferObject *createFramebufferObject(const QSize &size) override;
void render() override;
void synchronize(QQuickFramebufferObject *item) override;
};
#endif

View file

@ -0,0 +1,273 @@
/*
* Copyright (c) 2010-2024 Belledonne Communications SARL.
*
* This file is part of linphone-desktop
* (see https://www.linphone.org).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <QOpenGLFramebufferObject>
#include <QQuickWindow>
#include <QThread>
#include <QTimer>
#include "CameraDummy.hpp"
#include "CameraGui.hpp"
#include "PreviewManager.hpp"
#include "core/App.hpp"
#include "core/call/CallCore.hpp"
#include "core/call/CallGui.hpp"
#include "core/participant/ParticipantDeviceCore.hpp"
#include "core/participant/ParticipantDeviceGui.hpp"
#include "tool/Utils.hpp"
DEFINE_ABSTRACT_OBJECT(CameraGui)
DEFINE_GUI_OBJECT(CameraGui)
// =============================================================================
CameraGui::CameraGui(QQuickItem *parent) : QQuickFramebufferObject(parent) {
mustBeInMainThread(getClassName());
// The fbo content must be y-mirrored because the ms rendering is y-inverted.
setMirrorVertically(true);
mRefreshTimer.setInterval(1000 / mMaxFps);
connect(&mRefreshTimer, &QTimer::timeout, this, &QQuickFramebufferObject::update, Qt::QueuedConnection);
mRefreshTimer.start();
}
// TODO : Deactivate only if there are no previews to display (Could be open in settings and calls)
CameraGui::~CameraGui() {
mustBeInMainThread("~" + getClassName());
mRefreshTimer.stop();
setWindowIdLocation(None);
}
// Hack for Qt constness on create Renderer.
// We need to store the renderer in order to update the SDK filters with this renderer.
QMap<const CameraGui *, QQuickFramebufferObject::Renderer *> gRenderers;
QMutex gRenderesLock;
//-------------------------------------------------------------
void CameraGui::refreshLastRenderer() {
gRenderesLock.lock();
if (gRenderers.contains(this)) setRenderer(gRenderers[this]);
else clearRenderer();
updateSDKRenderer();
gRenderesLock.unlock();
}
void CameraGui::setRenderer(QQuickFramebufferObject::Renderer *renderer) {
mLastRenderer = renderer;
}
void CameraGui::clearRenderer() {
mLastRenderer = nullptr;
}
QQuickFramebufferObject::Renderer *CameraGui::createRenderer() const {
QQuickFramebufferObject::Renderer *renderer = NULL;
lDebug() << log().arg("CreateRenderer");
// A renderer is mandatory, we cannot wait async.
switch (getSourceLocation()) {
case CorePreview: {
// if (resetWindowId) PreviewManager::getInstance()->unsubscribe(this);
renderer = PreviewManager::getInstance()->subscribe(this);
//(QQuickFramebufferObject::Renderer *)CoreModel::getInstance()->getCore()->createNativePreviewWindowId();
} break;
case Call: {
App::postModelBlock([qmlName = mQmlName, callGui = mCallGui, &renderer]() {
auto call = callGui->getCore()->getModel()->getMonitor();
if (call) {
lInfo() << "[Camera] (" << qmlName << ") Camera create from CallModel";
renderer = (QQuickFramebufferObject::Renderer *)call->createNativeVideoWindowId();
}
});
} break;
case Device: {
App::postModelBlock([qmlName = mQmlName, participantDeviceGui = mParticipantDeviceGui, &renderer]() {
auto device = participantDeviceGui->getCore()->getModel()->getMonitor();
if (device) {
lInfo() << "[Camera] (" << qmlName << ") Camera create from ParticipantDeviceModel";
renderer = (QQuickFramebufferObject::Renderer *)device->createNativeVideoWindowId();
}
});
} break;
default: {
}
}
// Storing Qt renderer
gRenderesLock.lock();
gRenderers[this] = renderer;
gRenderesLock.unlock();
QTimer::singleShot(
1, this, &CameraGui::refreshLastRenderer); // Assign new renderer to the current CameraGui (bypassing constness)
if (!renderer) {
lInfo() << log().arg("(%1) Setting Camera to Dummy, %2").arg(mQmlName).arg(getSourceLocation());
QTimer::singleShot(1, this, &CameraGui::isNotReady);
renderer = new CameraDummy(); // Used to fill a renderer to avoid pushing a NULL.
QTimer::singleShot(1000, this, &CameraGui::requestNewRenderer);
} else QTimer::singleShot(1, this, &CameraGui::isReady); // Hack because of constness of createRenderer()
return renderer;
}
void CameraGui::updateSDKRenderer() {
updateSDKRenderer(mLastRenderer);
}
void CameraGui::updateSDKRenderer(QQuickFramebufferObject::Renderer *renderer) {
lDebug() << log().arg("Apply Qt Renderer to SDK") << renderer;
switch (getSourceLocation()) {
case CorePreview: {
} break;
case Call: {
App::postModelAsync([qmlName = mQmlName, callGui = mCallGui, renderer]() {
auto call = callGui->getCore()->getModel()->getMonitor();
if (call) {
lInfo() << "[Camera] (" << qmlName << ") Camera to CallModel";
call->setNativeVideoWindowId(renderer);
}
});
} break;
case Device: {
App::postModelAsync([qmlName = mQmlName, participantDeviceGui = mParticipantDeviceGui, renderer]() {
auto device = participantDeviceGui->getCore()->getModel()->getMonitor();
if (device) {
lInfo() << "[Camera] (" << qmlName << ") Camera to ParticipantDevice";
device->setNativeVideoWindowId(renderer);
}
});
} break;
default: {
}
}
}
void CameraGui::resetWindowId() {
updateSDKRenderer(nullptr);
}
void CameraGui::checkVideoDefinition() { /*
if (mWindowIdLocation == WindowIdLocation::CorePreview) {
auto videoDefinition = CoreManager::getInstance()->getSettingsModel()->getCurrentPreviewVideoDefinition();
if (videoDefinition["width"] != mLastVideoDefinition["width"] ||
videoDefinition["height"] != mLastVideoDefinition["height"]) {
mLastVideoDefinition = videoDefinition;
emit videoDefinitionChanged();
}
}*/
}
bool CameraGui::getIsReady() const {
return mIsReady;
}
void CameraGui::setIsReady(bool isReady) {
if (mIsReady != isReady) {
lDebug() << log().arg("Set IsReady") << isReady;
mIsReady = isReady;
emit isReadyChanged(mIsReady);
}
}
void CameraGui::isReady() {
setIsReady(true);
}
void CameraGui::isNotReady() {
setIsReady(false);
}
bool CameraGui::getIsPreview() const {
return mIsPreview;
}
void CameraGui::setIsPreview(bool status) {
if (mIsPreview != status) {
mIsPreview = status;
updateWindowIdLocation();
update();
emit isPreviewChanged(status);
}
}
CallGui *CameraGui::getCallGui() const {
return mCallGui;
}
void CameraGui::setCallGui(CallGui *callGui) {
if (mCallGui != callGui) {
if (mCallGui) disconnect(mCallGui->getCore(), &CallCore::stateChanged, this, &CameraGui::callStateChanged);
mCallGui = callGui;
if (mCallGui) connect(mCallGui->getCore(), &CallCore::stateChanged, this, &CameraGui::callStateChanged);
lDebug() << log().arg("Set Call") << mCallGui;
emit callGuiChanged(mCallGui);
updateWindowIdLocation();
}
}
ParticipantDeviceGui *CameraGui::getParticipantDeviceGui() const {
return mParticipantDeviceGui;
}
void CameraGui::setParticipantDeviceGui(ParticipantDeviceGui *deviceGui) {
if (mParticipantDeviceGui != deviceGui) {
mParticipantDeviceGui = deviceGui;
lDebug() << log().arg("Set Device") << mParticipantDeviceGui;
// setIsPreview(mParticipantDeviceGui->getCore()->isLocal());
emit participantDeviceGuiChanged(mParticipantDeviceGui);
updateWindowIdLocation();
}
}
CameraGui::WindowIdLocation CameraGui::getSourceLocation() const {
return mWindowIdLocation;
}
void CameraGui::setWindowIdLocation(const WindowIdLocation &location) {
if (mWindowIdLocation != location) {
lDebug() << log().arg("Update Window Id location from %2 to %3").arg(mWindowIdLocation).arg(location);
if (mWindowIdLocation == CorePreview) PreviewManager::getInstance()->unsubscribe(this);
// else if (mWindowIdLocation != None) resetWindowId(); // Location change: Reset old window ID.
resetWindowId();
mWindowIdLocation = location;
if (mWindowIdLocation == CorePreview) PreviewManager::getInstance()->subscribe(this);
else updateSDKRenderer();
// QTimer::singleShot(100, this, &CameraGui::requestNewRenderer);
// if (mWindowIdLocation == WindowIdLocation::CorePreview) {
// mLastVideoDefinition =
// CoreManager::getInstance()->getSettingsModel()->getCurrentPreviewVideoDefinition(); emit
// videoDefinitionChanged(); mLastVideoDefinitionChecker.start();
// } else mLastVideoDefinitionChecker.stop();
}
}
void CameraGui::updateWindowIdLocation() {
bool useDefaultWindow = true;
if (mIsPreview) setWindowIdLocation(WindowIdLocation::CorePreview);
else if (mCallGui) setWindowIdLocation(WindowIdLocation::Call);
else if (mParticipantDeviceGui && !mParticipantDeviceGui->getCore()->isLocal())
setWindowIdLocation(WindowIdLocation::Device);
else setWindowIdLocation(WindowIdLocation::CorePreview);
}
void CameraGui::callStateChanged(LinphoneEnums::CallState state) {
if (getSourceLocation() == CorePreview && state == LinphoneEnums::CallState::Connected) {
if (!getIsReady()) {
lDebug() << log().arg("Request new renderer because of not being Ready on CallState as Connected");
emit requestNewRenderer();
}
}
}

View file

@ -0,0 +1,112 @@
/*
* Copyright (c) 2010-2024 Belledonne Communications SARL.
*
* This file is part of linphone-desktop
* (see https://www.linphone.org).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef CAMERA_GUI_H_
#define CAMERA_GUI_H_
#include <memory>
#include "tool/AbstractObject.hpp"
#include <QMutex>
#include <QQuickFramebufferObject>
#include <QTimer>
#include "core/participant/ParticipantDeviceGui.hpp"
// =============================================================================
class CallGui;
class CameraGui : public QQuickFramebufferObject, public AbstractObject {
Q_OBJECT
Q_PROPERTY(CallGui *call READ getCallGui WRITE setCallGui NOTIFY callGuiChanged)
Q_PROPERTY(ParticipantDeviceGui *participantDevice READ getParticipantDeviceGui WRITE setParticipantDeviceGui NOTIFY
participantDeviceGuiChanged)
Q_PROPERTY(bool isPreview READ getIsPreview WRITE setIsPreview NOTIFY isPreviewChanged)
Q_PROPERTY(bool isReady READ getIsReady WRITE setIsReady NOTIFY isReadyChanged)
// Q_PROPERTY(SoundPlayer * linphonePlayer READ getLinphonePlayer WRITE setLinphonePlayer NOTIFY
// linphonePlayerChanged)
typedef enum { None = -1, CorePreview = 0, Call, Device, Player, Core } WindowIdLocation;
public:
CameraGui(QQuickItem *parent = Q_NULLPTR);
virtual ~CameraGui();
QQuickFramebufferObject::Renderer *createRenderer() const override;
Q_INVOKABLE void resetWindowId();
void checkVideoDefinition();
bool getIsReady() const;
void setIsReady(bool isReady);
void isReady();
void isNotReady();
bool getIsPreview() const;
void setIsPreview(bool status);
CallGui *getCallGui() const;
void setCallGui(CallGui *callGui);
ParticipantDeviceGui *getParticipantDeviceGui() const;
void setParticipantDeviceGui(ParticipantDeviceGui *participantDeviceGui);
WindowIdLocation getSourceLocation() const;
void setWindowIdLocation(const WindowIdLocation &location);
void updateWindowIdLocation();
void removeParticipantDeviceModel();
void removeCallModel();
void removeLinphonePlayer();
void callStateChanged(LinphoneEnums::CallState state);
void setRenderer(QQuickFramebufferObject::Renderer *);
void refreshLastRenderer(); // Lookup in stocked renderer and link it.
void clearRenderer();
void updateSDKRenderer();
void updateSDKRenderer(QQuickFramebufferObject::Renderer *renderer);
signals:
void requestNewRenderer();
void isReadyChanged(bool isReady);
void callGuiChanged(CallGui *callGui);
void isPreviewChanged(bool isPreview);
void isReadyChanged();
void participantDeviceGuiChanged(ParticipantDeviceGui *participantDeviceGui);
void videoDefinitionChanged();
// void linphonePlayerChanged(SoundPlayer * linphonePlayer);
private:
bool mIsPreview = false;
bool mIsReady = false;
QTimer mRefreshTimer;
int mMaxFps = 30;
QVariantMap mLastVideoDefinition;
QTimer mLastVideoDefinitionChecker;
CallGui *mCallGui = nullptr;
ParticipantDeviceGui *mParticipantDeviceGui = nullptr;
QQuickFramebufferObject::Renderer *mLastRenderer = nullptr;
WindowIdLocation mWindowIdLocation = None;
mutable bool mIsWindowIdSet = false;
DECLARE_ABSTRACT_OBJECT
DECLARE_GUI_OBJECT
};
#endif

View file

@ -0,0 +1,136 @@
/*
* Copyright (c) 2010-2024 Belledonne Communications SARL.
*
* This file is part of linphone-desktop
* (see https://www.linphone.org).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <QOpenGLFramebufferObject>
#include <QQuickWindow>
#include <QThread>
#include <QTimer>
#include "../App.hpp"
#include "PreviewManager.hpp"
#include "tool/Utils.hpp"
DEFINE_ABSTRACT_OBJECT(PreviewManager)
// =============================================================================
PreviewManager *PreviewManager::gInstance = nullptr;
PreviewManager::PreviewManager(QObject *parent) : QObject(parent) {
}
PreviewManager::~PreviewManager() {
}
PreviewManager *PreviewManager::getInstance() {
if (gInstance) return gInstance;
else {
gInstance = new PreviewManager();
return gInstance;
}
}
// Create a Renderer from SDK preview
QQuickFramebufferObject::Renderer *PreviewManager::subscribe(const CameraGui *candidate) {
QQuickFramebufferObject::Renderer *renderer = nullptr;
mCounterMutex.lock();
if (mCandidates.size() == 0) {
activate();
}
auto itCandidate =
std::find_if(mCandidates.begin(), mCandidates.end(),
[candidate](const QPair<const CameraGui *, QQuickFramebufferObject::Renderer *> &item) {
return item.first == candidate;
});
if (itCandidate == mCandidates.end()) {
connect(candidate, &QObject::destroyed, this, qOverload<QObject *>(&PreviewManager::unsubscribe));
mCandidates.append({candidate, nullptr});
itCandidate = mCandidates.end() - 1;
lDebug() << log().arg("Subscribing New") << itCandidate->first->getQmlName();
} else {
lDebug() << log().arg("Resubscribing") << itCandidate->first->getQmlName();
}
mCounterMutex.unlock();
App::postModelBlock([&renderer, isFirst = (itCandidate == mCandidates.begin()),
name = itCandidate->first->getQmlName()]() {
renderer =
(QQuickFramebufferObject::Renderer *)CoreModel::getInstance()->getCore()->createNativePreviewWindowId(
nullptr);
if (!renderer) { // TODO debug
renderer =
(QQuickFramebufferObject::Renderer *)CoreModel::getInstance()->getCore()->createNativePreviewWindowId(
nullptr);
}
if (isFirst) {
lDebug() << "[PreviewManager] " << name << " Set Native Preview Id with " << renderer;
CoreModel::getInstance()->getCore()->setNativePreviewWindowId(renderer);
}
});
mCounterMutex.lock();
itCandidate->second = renderer;
mCounterMutex.unlock();
return renderer;
}
void PreviewManager::unsubscribe(const CameraGui *candidate) { // If nullptr, Use of sender()
mCounterMutex.lock();
auto itCandidate = std::find_if(mCandidates.begin(), mCandidates.end(),
[candidate = (candidate ? candidate : sender())](
const QPair<const CameraGui *, QQuickFramebufferObject::Renderer *> &item) {
return item.first == candidate;
});
if (itCandidate != mCandidates.end()) {
lDebug() << log().arg("Unsubscribing") << itCandidate->first->getQmlName();
disconnect(candidate, nullptr, this, nullptr);
if (mCandidates.size() == 1) {
mCandidates.erase(itCandidate);
deactivate();
} else if (mCandidates.begin() == itCandidate) {
mCandidates.erase(itCandidate);
lDebug() << log().arg("Update") << mCandidates.first().first->getQmlName();
auto renderer = mCandidates.first().second;
if (renderer)
App::postModelBlock([renderer = mCandidates.first().second]() {
CoreModel::getInstance()->getCore()->setNativePreviewWindowId(renderer);
});
} else {
mCandidates.erase(itCandidate);
}
}
mCounterMutex.unlock();
}
void PreviewManager::unsubscribe(QObject *sender) {
unsubscribe(dynamic_cast<CameraGui *>(sender));
}
void PreviewManager::activate() {
App::postModelAsync([]() {
lDebug() << "[PreviewManager] Activation";
CoreModel::getInstance()->getCore()->enableVideoPreview(true);
});
}
void PreviewManager::deactivate() {
App::postModelAsync([]() {
lDebug() << "[PreviewManager] Deactivation";
CoreModel::getInstance()->getCore()->enableVideoPreview(false);
});
}

View file

@ -0,0 +1,62 @@
/*
* Copyright (c) 2024 Belledonne Communications SARL.
*
* This file is part of linphone-desktop
* (see https://www.linphone.org).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef PREVIEW_MANAGER_H_
#define PREVIEW_MANAGER_H_
#include "CameraGui.hpp"
#include "tool/AbstractObject.hpp"
#include <QMutex>
#include <QObject>
#include <QPair>
#include <QQuickFramebufferObject>
// Manage the SDK preview as a singleton.
// The goal is to process the limitation that only one preview can be displayed.
// On asynchronized application, the destruction of a previous Preview can be done AFTER the creation on a new Preview
// Sticker.
// =============================================================================
class PreviewManager : public QObject, public AbstractObject {
Q_OBJECT
public:
PreviewManager(QObject *parent = nullptr);
virtual ~PreviewManager();
static PreviewManager *getInstance();
QQuickFramebufferObject::Renderer *subscribe(const CameraGui *candidate);
void unsubscribe(const CameraGui *candidate);
void activate();
void deactivate();
public slots:
void unsubscribe(QObject *sender);
private:
QMutex mCounterMutex;
QList<QPair<const CameraGui *, QQuickFramebufferObject::Renderer *>> mCandidates;
static PreviewManager *gInstance;
QQuickFramebufferObject::Renderer *mPreviewRenderer = nullptr;
DECLARE_ABSTRACT_OBJECT
};
#endif

View file

@ -0,0 +1,686 @@
/*
* Copyright (c) 2010-2024 Belledonne Communications SARL.
*
* This file is part of linphone-desktop
* (see https://www.linphone.org).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "ChatCore.hpp"
#include "core/App.hpp"
#include "core/chat/message/content/ChatMessageContentGui.hpp"
#include "core/friend/FriendCore.hpp"
#include "core/setting/SettingsCore.hpp"
#include "model/chat/message/EventLogModel.hpp"
#include "model/core/CoreModel.hpp"
#include "model/friend/FriendModel.hpp"
#include "model/tool/ToolModel.hpp"
#include "tool/Utils.hpp"
#include <QQuickWindow>
DEFINE_ABSTRACT_OBJECT(ChatCore)
/***********************************************************************/
QSharedPointer<ChatCore> ChatCore::create(const std::shared_ptr<linphone::ChatRoom> &chatRoom) {
auto sharedPointer = QSharedPointer<ChatCore>(new ChatCore(chatRoom), &QObject::deleteLater);
sharedPointer->setSelf(sharedPointer);
sharedPointer->moveToThread(App::getInstance()->thread());
return sharedPointer;
}
ChatCore::ChatCore(const std::shared_ptr<linphone::ChatRoom> &chatRoom) : QObject(nullptr) {
// lDebug() << "[ChatCore] new" << this;
mustBeInLinphoneThread(getClassName());
App::getInstance()->mEngine->setObjectOwnership(this, QQmlEngine::CppOwnership);
mLastUpdatedTime = QDateTime::fromSecsSinceEpoch(chatRoom->getLastUpdateTime());
auto chatRoomAddress = chatRoom->getPeerAddress();
mChatRoomAddress = Utils::coreStringToAppString(chatRoomAddress->asStringUriOnly());
if (chatRoom->hasCapability((int)linphone::ChatRoom::Capabilities::Basic)) {
mTitle = ToolModel::getDisplayName(chatRoomAddress);
mAvatarUri = ToolModel::getDisplayName(chatRoomAddress);
mParticipantAddress = Utils::coreStringToAppString(chatRoomAddress->asStringUriOnly());
mIsGroupChat = false;
mIsBasic = true;
mConferenceJoined = true;
} else {
mIsBasic = false;
auto participants = chatRoom->getParticipants();
if (chatRoom->hasCapability((int)linphone::ChatRoom::Capabilities::OneToOne)) {
if (participants.size() > 0) {
auto peer = participants.front();
auto peerAddress = peer->getAddress();
if (peer) mTitle = ToolModel::getDisplayName(peerAddress);
mAvatarUri = ToolModel::getDisplayName(peerAddress);
if (participants.size() == 1) {
if (peerAddress) mParticipantAddress = Utils::coreStringToAppString(peerAddress->asStringUriOnly());
}
}
mIsGroupChat = false;
} else if (chatRoom->hasCapability((int)linphone::ChatRoom::Capabilities::Conference)) {
mTitle = Utils::coreStringToAppString(chatRoom->getSubject());
mAvatarUri = Utils::coreStringToAppString(chatRoom->getSubject());
mIsGroupChat = true;
mMeAdmin = chatRoom->getMe() && chatRoom->getMe()->isAdmin();
}
mConferenceJoined = participants.size() != 0;
}
mUnreadMessagesCount = chatRoom->getUnreadMessagesCount();
connect(this, &ChatCore::unreadMessagesCountChanged, this, [this] {
if (mUnreadMessagesCount == 0) emit lMarkAsRead();
});
mChatModel = Utils::makeQObject_ptr<ChatModel>(chatRoom);
mChatModel->setSelf(mChatModel);
auto lastMessage = chatRoom->getLastMessageInHistory();
mLastMessage = lastMessage ? ChatMessageCore::create(lastMessage) : nullptr;
int filter = mIsGroupChat ? static_cast<int>(linphone::ChatRoom::HistoryFilter::ChatMessage) |
static_cast<int>(linphone::ChatRoom::HistoryFilter::InfoNoDevice)
: static_cast<int>(linphone::ChatRoom::HistoryFilter::ChatMessage);
mIdentifier = Utils::coreStringToAppString(chatRoom->getIdentifier());
mChatRoomState = LinphoneEnums::fromLinphone(chatRoom->getState());
mIsEncrypted = chatRoom->hasCapability((int)linphone::ChatRoom::Capabilities::Encrypted);
auto localAccount = ToolModel::findAccount(chatRoom->getLocalAddress());
mLocalAddress = Utils::coreStringToAppString(chatRoom->getLocalAddress()->asStringUriOnly());
bool associatedAccountHasIMEncryptionMandatory =
localAccount && localAccount->getParams() &&
localAccount->getParams()->getInstantMessagingEncryptionMandatory();
mIsReadOnly = chatRoom->isReadOnly() || (!mIsEncrypted && associatedAccountHasIMEncryptionMandatory);
connect(this, &ChatCore::eventsInserted, this, &ChatCore::lUpdateLastMessage);
mEphemeralEnabled = chatRoom->ephemeralEnabled();
mEphemeralLifetime = chatRoom->ephemeralEnabled() ? chatRoom->getEphemeralLifetime() : 0;
mIsMuted = chatRoom->getMuted();
mParticipants = buildParticipants(chatRoom);
connect(this, &ChatCore::participantsChanged, this, [this] {
// refresh secured status of the chatroom
setIsSecured(computeSecuredStatus());
});
mIsSecured = computeSecuredStatus();
}
ChatCore::~ChatCore() {
lDebug() << "[ChatCore] delete" << this;
mustBeInMainThread("~" + getClassName());
if (mChatModelConnection) mChatModelConnection->disconnect();
emit mChatModel->removeListener();
}
void ChatCore::setSelf(QSharedPointer<ChatCore> me) {
mChatModelConnection = SafeConnection<ChatCore, ChatModel>::create(me, mChatModel);
mChatModelConnection->makeConnectToCore(&ChatCore::lDeleteHistory, [this]() {
mChatModelConnection->invokeToModel([this]() { mChatModel->deleteHistory(); });
});
mChatModelConnection->makeConnectToCore(&ChatCore::lDeleteMessage, [this](ChatMessageGui *message) {
mChatModelConnection->invokeToModel([this, core = message ? message->mCore : nullptr]() {
auto messageModel = core ? core->getModel() : nullptr;
if (messageModel) {
mChatModel->deleteMessage(messageModel->getMonitor());
}
});
});
mChatModelConnection->makeConnectToCore(
&ChatCore::lLeave, [this]() { mChatModelConnection->invokeToModel([this]() { mChatModel->leave(); }); });
mChatModelConnection->makeConnectToModel(&ChatModel::historyDeleted, [this]() {
mChatModelConnection->invokeToCore([this]() {
emit eventListCleared();
//: Deleted
Utils::showInformationPopup(tr("info_toast_deleted_title"),
//: Message history has been deleted
tr("info_toast_deleted_message_history"), true);
});
});
mChatModelConnection->makeConnectToCore(&ChatCore::lUpdateUnreadCount, [this]() {
mChatModelConnection->invokeToModel([this]() {
auto count = mChatModel->getUnreadMessagesCount();
mChatModelConnection->invokeToCore([this, count] { setUnreadMessagesCount(count); });
});
});
mChatModelConnection->makeConnectToCore(&ChatCore::lUpdateLastUpdatedTime, [this]() {
mChatModelConnection->invokeToModel([this]() {
auto time = mChatModel->getLastUpdateTime();
mChatModelConnection->invokeToCore([this, time]() { setLastUpdatedTime(time); });
});
});
mChatModelConnection->makeConnectToCore(&ChatCore::lDelete, [this]() {
mChatModelConnection->invokeToModel([this]() { mChatModel->deleteChatRoom(); });
});
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);
});
});
mChatModelConnection->makeConnectToModel(
&ChatModel::conferenceJoined, [this](const std::shared_ptr<linphone::ChatRoom> &chatRoom,
const std::shared_ptr<const linphone::EventLog> &eventLog) {
auto participants = buildParticipants(chatRoom);
if (chatRoom->hasCapability((int)linphone::ChatRoom::Capabilities::OneToOne)) {
QString title, avatarUri;
auto linParticipants = chatRoom->getParticipants();
if (linParticipants.size() > 0) {
auto peer = linParticipants.front();
if (peer) title = ToolModel::getDisplayName(peer->getAddress());
avatarUri = ToolModel::getDisplayName(peer->getAddress());
if (linParticipants.size() == 1) {
auto peerAddress = peer->getAddress();
if (peerAddress)
mParticipantAddress = Utils::coreStringToAppString(peerAddress->asStringUriOnly());
}
}
mChatModelConnection->invokeToCore([this, title, avatarUri]() {
setTitle(title);
setAvatarUri(avatarUri);
mConferenceJoined = true;
emit conferenceJoined();
});
}
mChatModelConnection->invokeToCore([this, participants]() { setParticipants(participants); });
});
// Events (excluding messages)
mChatModelConnection->makeConnectToModel(
&ChatModel::newEvent, [this](const std::shared_ptr<linphone::ChatRoom> &chatRoom,
const std::shared_ptr<const linphone::EventLog> &eventLog) {
if (mChatModel->getMonitor() != chatRoom) return;
lDebug() << "EVENT LOG RECEIVED IN CHATROOM" << mChatModel->getTitle();
auto event = EventLogCore::create(eventLog, chatRoom);
if (event->isHandled()) {
mChatModelConnection->invokeToCore([this, event]() { emit eventsInserted({event}); });
}
mChatModelConnection->invokeToCore([this, event]() { emit lUpdateLastUpdatedTime(); });
});
// Chat messages
mChatModelConnection->makeConnectToModel(
&ChatModel::chatMessagesReceived, [this](const std::shared_ptr<linphone::ChatRoom> &chatRoom,
const std::list<std::shared_ptr<linphone::EventLog>> &eventsLog) {
if (mChatModel->getMonitor() != chatRoom) return;
lDebug() << "CHAT MESSAGE RECEIVED IN CHATROOM" << mChatModel->getTitle();
QList<QSharedPointer<EventLogCore>> list;
for (auto &e : eventsLog) {
auto event = EventLogCore::create(e, chatRoom);
list.push_back(event);
}
mChatModelConnection->invokeToCore([this, list]() {
emit eventsInserted(list);
emit lUpdateUnreadCount();
emit lUpdateLastUpdatedTime();
});
});
mChatModelConnection->makeConnectToCore(&ChatCore::lMarkAsRead, [this]() {
auto lastActiveWindow = Utils::getLastActiveWindow();
if (lastActiveWindow && lastActiveWindow->isActive())
mChatModelConnection->invokeToModel([this]() { mChatModel->markAsRead(); });
else {
connect(lastActiveWindow, &QQuickWindow::activeChanged, this, [this, lastActiveWindow] {
if (lastActiveWindow->isActive()) {
disconnect(lastActiveWindow, &QQuickWindow::activeChanged, this, nullptr);
mChatModelConnection->invokeToModel([this, lastActiveWindow] { mChatModel->markAsRead(); });
}
});
}
});
mChatModelConnection->makeConnectToModel(&ChatModel::messagesRead, [this]() {
auto unread = mChatModel->getUnreadMessagesCount();
mChatModelConnection->invokeToCore([this, unread]() { setUnreadMessagesCount(unread); });
});
mChatModelConnection->makeConnectToCore(&ChatCore::lUpdateLastMessage, [this]() {
auto lastMessageModel = mLastMessage ? mLastMessage->getModel() : nullptr;
mChatModelConnection->invokeToModel([this, lastMessageModel]() {
auto linphoneMessage = mChatModel->getLastChatMessage();
if (linphoneMessage && (!lastMessageModel || lastMessageModel->getMonitor() != linphoneMessage)) {
auto chatMessageCore = ChatMessageCore::create(linphoneMessage);
mChatModelConnection->invokeToCore([this, chatMessageCore]() { setLastMessage(chatMessageCore); });
}
});
});
mChatModelConnection->makeConnectToCore(&ChatCore::lSendTextMessage, [this](QString message) {
if (Utils::isEmptyMessage(message)) return;
mChatModelConnection->invokeToModel([this, message]() {
auto linMessage = mChatModel->createTextMessageFromText(message);
linMessage->send();
});
});
mChatModelConnection->makeConnectToCore(&ChatCore::lSendMessage, [this](QString message, QVariantList files) {
if (Utils::isEmptyMessage(message) && files.size() == 0) return;
QList<std::shared_ptr<ChatMessageContentModel>> filesContent;
for (auto &file : files) {
auto contentGui = qvariant_cast<ChatMessageContentGui *>(file);
if (contentGui) {
auto contentCore = contentGui->mCore;
filesContent.append(contentCore->getContentModel());
}
}
mChatModelConnection->invokeToModel([this, message, filesContent]() {
auto linMessage = mChatModel->createMessage(message, filesContent);
linMessage->send();
});
});
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, chatRoom);
mChatModelConnection->invokeToCore([this, event]() { emit eventsInserted({event}); });
});
mChatModelConnection->makeConnectToCore(
&ChatCore::lCompose, [this]() { mChatModelConnection->invokeToModel([this]() { mChatModel->compose(); }); });
mChatModelConnection->makeConnectToModel(
&ChatModel::isComposingReceived,
[this](const std::shared_ptr<linphone::ChatRoom> &chatRoom,
const std::shared_ptr<const linphone::Address> &remoteAddress, bool isComposing) {
if (mChatModel->getMonitor() != chatRoom) return;
QString name = isComposing ? ToolModel::getDisplayName(remoteAddress) : QString();
auto remoteAddr = remoteAddress;
// remoteAddr->clean();
mChatModelConnection->invokeToCore(
[this, name, address = Utils::coreStringToAppString(remoteAddr->asStringUriOnly())]() {
setComposingName(name);
setComposingAddress(address);
});
});
mChatModelConnection->makeConnectToCore(&ChatCore::lSetMuted, [this](bool muted) {
mChatModelConnection->invokeToModel([this, muted]() { mChatModel->setMuted(muted); });
});
mChatModelConnection->makeConnectToModel(&ChatModel::mutedChanged, [this](bool muted) {
mChatModelConnection->invokeToCore([this, muted]() {
if (mIsMuted != muted) {
mIsMuted = muted;
emit mutedChanged();
}
});
});
mChatModelConnection->makeConnectToCore(&ChatCore::lEnableEphemeral, [this](bool enable) {
mChatModelConnection->invokeToModel([this, enable]() { mChatModel->enableEphemeral(enable); });
});
mChatModelConnection->makeConnectToModel(&ChatModel::ephemeralEnableChanged, [this](bool enable) {
mChatModelConnection->invokeToCore([this, enable]() {
if (mEphemeralEnabled != enable) {
mEphemeralEnabled = enable;
emit ephemeralEnabledChanged();
}
});
});
mChatModelConnection->makeConnectToCore(&ChatCore::lSetEphemeralLifetime, [this](int time) {
mChatModelConnection->invokeToModel([this, time]() { mChatModel->setEphemeralLifetime(time); });
});
mChatModelConnection->makeConnectToModel(&ChatModel::ephemeralLifetimeChanged, [this](int time) {
mChatModelConnection->invokeToCore([this, time]() {
if (mEphemeralLifetime != time) {
mEphemeralLifetime = time;
emit ephemeralLifetimeChanged();
}
});
});
mChatModelConnection->makeConnectToCore(&ChatCore::lSetSubject, [this](QString subject) {
mChatModelConnection->invokeToModel([this, subject]() { mChatModel->setSubject(subject); });
});
mChatModelConnection->makeConnectToModel(
&ChatModel::subjectChanged, [this](const std::shared_ptr<linphone::ChatRoom> &chatRoom,
const std::shared_ptr<const linphone::EventLog> &eventLog) {
QString subject = Utils::coreStringToAppString(chatRoom->getSubject());
mChatModelConnection->invokeToCore([this, subject]() { setTitle(subject); });
});
mChatModelConnection->makeConnectToModel(
&ChatModel::participantAdded, [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::participantRemoved, [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); });
});
mChatModelConnection->makeConnectToCore(&ChatCore::lSetParticipantsAddresses, [this](QStringList addresses) {
mChatModelConnection->invokeToModel([this, addresses]() { mChatModel->setParticipantAddresses(addresses); });
});
mChatModelConnection->makeConnectToCore(&ChatCore::lToggleParticipantAdminStatusAtIndex, [this](int index) {
mChatModelConnection->invokeToModel(
[this, index]() { mChatModel->toggleParticipantAdminStatusAtIndex(index); });
});
mCoreModelConnection = SafeConnection<ChatCore, CoreModel>::create(me, CoreModel::getInstance());
if (!ToolModel::findFriendByAddress(mParticipantAddress))
mCoreModelConnection->makeConnectToModel(&CoreModel::friendCreated,
[this](std::shared_ptr<linphone::Friend> f) { updateInfo(f); });
mCoreModelConnection->makeConnectToModel(&CoreModel::friendUpdated,
[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 {
return mLastUpdatedTime;
}
void ChatCore::setLastUpdatedTime(QDateTime time) {
if (mLastUpdatedTime != time) {
mLastUpdatedTime = time;
emit lastUpdatedTimeChanged(time);
}
}
QString ChatCore::getTitle() const {
return mTitle;
}
void ChatCore::setTitle(QString title) {
if (mTitle != title) {
mTitle = title;
emit titleChanged(title);
}
}
QString ChatCore::getSendingText() const {
return mSendingText;
}
void ChatCore::setSendingText(const QString &text) {
if (mSendingText != text) {
mSendingText = text;
emit sendingTextChanged(text);
}
}
bool ChatCore::isGroupChat() const {
return mIsGroupChat;
}
bool ChatCore::isEncrypted() const {
return mIsEncrypted;
}
QString ChatCore::getIdentifier() const {
return mIdentifier;
}
QString ChatCore::getParticipantAddress() const {
return mParticipantAddress;
}
QString ChatCore::getChatRoomAddress() const {
return mChatRoomAddress;
}
QString ChatCore::getAvatarUri() const {
return mAvatarUri;
}
void ChatCore::setAvatarUri(QString avatarUri) {
if (mAvatarUri != avatarUri) {
mAvatarUri = avatarUri;
emit avatarUriChanged();
}
}
QString ChatCore::getLastMessageText() const {
return mLastMessage ? mLastMessage->getText() : QString();
}
LinphoneEnums::ChatMessageState ChatCore::getLastMessageState() const {
return mLastMessage ? mLastMessage->getMessageState() : LinphoneEnums::ChatMessageState::StateIdle;
}
LinphoneEnums::ChatRoomState ChatCore::getChatRoomState() const {
return mChatRoomState;
}
void ChatCore::setChatRoomState(LinphoneEnums::ChatRoomState state) {
if (mChatRoomState != state) {
mChatRoomState = state;
emit chatRoomStateChanged();
}
}
void ChatCore::setIsReadOnly(bool readOnly) {
if (mIsReadOnly != readOnly) {
mIsReadOnly = readOnly;
emit readOnlyChanged();
}
}
bool ChatCore::getIsReadOnly() const {
return mIsReadOnly;
}
ChatMessageGui *ChatCore::getLastMessage() const {
return mLastMessage ? new ChatMessageGui(mLastMessage) : nullptr;
}
void ChatCore::setLastMessage(QSharedPointer<ChatMessageCore> lastMessage) {
if (mLastMessage != lastMessage) {
if (mLastMessage) disconnect(mLastMessage.get(), &ChatMessageCore::messageStateChanged, this, nullptr);
mLastMessage = lastMessage;
connect(mLastMessage.get(), &ChatMessageCore::messageStateChanged, this, &ChatCore::lastMessageChanged);
emit lastMessageChanged();
}
}
int ChatCore::getUnreadMessagesCount() const {
return mUnreadMessagesCount;
}
void ChatCore::setUnreadMessagesCount(int count) {
if (mUnreadMessagesCount != count) {
mUnreadMessagesCount = count;
emit unreadMessagesCountChanged(count);
}
}
QString ChatCore::getComposingName() const {
return mComposingName;
}
void ChatCore::setComposingName(QString composingName) {
if (mComposingAddress != composingName) {
mComposingName = composingName;
emit composingUserChanged();
}
}
void ChatCore::setComposingAddress(QString composingAddress) {
if (mComposingAddress != composingAddress) {
mComposingAddress = composingAddress;
emit composingUserChanged();
}
}
QString ChatCore::getComposingAddress() const {
return mComposingAddress;
}
QList<QSharedPointer<ChatMessageContentCore>> ChatCore::getFileList() const {
return mFileList;
}
void ChatCore::resetFileList(QList<QSharedPointer<ChatMessageContentCore>> list) {
mFileList = list;
emit fileListChanged();
}
std::shared_ptr<ChatModel> ChatCore::getModel() const {
return mChatModel;
}
bool ChatCore::isMuted() const {
return mIsMuted;
}
bool ChatCore::isEphemeralEnabled() const {
return mEphemeralEnabled;
}
int ChatCore::getEphemeralLifetime() const {
return mEphemeralLifetime;
}
void ChatCore::setMeAdmin(bool admin) {
if (mMeAdmin != admin) {
mMeAdmin = admin;
emit meAdminChanged();
}
}
bool ChatCore::getMeAdmin() const {
return mMeAdmin;
}
bool ChatCore::isSecured() const {
return mIsSecured;
}
void ChatCore::setIsSecured(bool secured) {
if (mIsSecured != secured) {
mIsSecured = secured;
emit isSecuredChanged();
}
}
bool ChatCore::computeSecuredStatus() const {
if (mParticipants.size() == 0) return false;
for (auto &participant : mParticipants) {
if (participant->getSecurityLevel() != LinphoneEnums::SecurityLevel::EndToEndEncryptedAndVerified) return false;
}
return true;
}
QVariantList ChatCore::getParticipantsGui() const {
QVariantList result;
for (auto participantCore : mParticipants) {
auto participantGui = new ParticipantGui(participantCore);
result.append(QVariant::fromValue(participantGui));
}
return result;
}
QStringList ChatCore::getParticipantsAddresses() const {
QStringList result;
for (auto participantCore : mParticipants) {
result.append(participantCore->getSipAddress());
}
return result;
}
void ChatCore::setParticipants(QList<QSharedPointer<ParticipantCore>> participants) {
mustBeInMainThread(log().arg(Q_FUNC_INFO));
mParticipants = participants;
emit participantsChanged();
}
QList<QSharedPointer<ParticipantCore>>
ChatCore::buildParticipants(const std::shared_ptr<linphone::ChatRoom> &chatRoom) const {
mustBeInLinphoneThread(log().arg(Q_FUNC_INFO));
QList<QSharedPointer<ParticipantCore>> result;
for (auto participant : chatRoom->getParticipants()) {
auto participantCore = ParticipantCore::create(participant);
result.append(participantCore);
}
return result;
}
QList<QSharedPointer<ParticipantCore>> ChatCore::getParticipants() const {
return mParticipants;
}
QString ChatCore::getLocalAddress() const {
return mLocalAddress;
}
void ChatCore::updateInfo(const std::shared_ptr<linphone::Friend> &updatedFriend, bool isRemoval) {
mustBeInLinphoneThread(log().arg(Q_FUNC_INFO));
auto fAddress = ToolModel::interpretUrl(mParticipantAddress);
bool isThisFriend = mFriendModel && updatedFriend == mFriendModel->getFriend();
if (!isThisFriend)
for (auto f : updatedFriend->getAddresses()) {
if (f->weakEqual(fAddress)) {
isThisFriend = true;
break;
}
}
if (isThisFriend) {
if (isRemoval) {
mFriendModel = nullptr;
}
int capabilities = mChatModel->getCapabilities();
auto chatroom = mChatModel->getMonitor();
auto chatRoomAddress = chatroom->getPeerAddress();
if (mChatModel->hasCapability((int)linphone::ChatRoom::Capabilities::Basic)) {
auto title = ToolModel::getDisplayName(chatRoomAddress);
auto avatarUri = ToolModel::getDisplayName(chatRoomAddress);
mChatModelConnection->invokeToCore([this, title, avatarUri] {
setTitle(title);
setAvatarUri(avatarUri);
});
} else {
if (mChatModel->hasCapability((int)linphone::ChatRoom::Capabilities::OneToOne)) {
auto participants = chatroom->getParticipants();
if (participants.size() > 0) {
auto peer = participants.front();
if (peer) {
auto title = ToolModel::getDisplayName(peer->getAddress());
auto avatarUri = ToolModel::getDisplayName(peer->getAddress());
mChatModelConnection->invokeToCore([this, title, avatarUri] {
setTitle(title);
setAvatarUri(avatarUri);
});
}
}
} else if (mChatModel->hasCapability((int)linphone::ChatRoom::Capabilities::Conference)) {
auto title = Utils::coreStringToAppString(chatroom->getSubject());
auto avatarUri = Utils::coreStringToAppString(chatroom->getSubject());
mChatModelConnection->invokeToCore([this, title, avatarUri] {
setTitle(title);
setAvatarUri(avatarUri);
});
}
}
}
}

View file

@ -0,0 +1,236 @@
/*
* Copyright (c) 2010-2024 Belledonne Communications SARL.
*
* This file is part of linphone-desktop
* (see https://www.linphone.org).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef CHAT_CORE_H_
#define CHAT_CORE_H_
#include "core/chat/message/EventLogGui.hpp"
#include "core/participant/ParticipantCore.hpp"
#include "message/ChatMessageGui.hpp"
#include "model/chat/ChatModel.hpp"
#include "model/search/MagicSearchModel.hpp"
#include "tool/LinphoneEnums.hpp"
#include "tool/thread/SafeConnection.hpp"
#include <QObject>
#include <QSharedPointer>
#include <linphone++/linphone.hh>
class EventLogCore;
class FriendModel;
class AccountCore;
class ChatCore : public QObject, public AbstractObject {
Q_OBJECT
public:
Q_PROPERTY(QString title READ getTitle WRITE setTitle NOTIFY titleChanged)
Q_PROPERTY(QString identifier READ getIdentifier CONSTANT)
Q_PROPERTY(QString peerAddress READ getParticipantAddress CONSTANT)
Q_PROPERTY(QString chatRoomAddress READ getChatRoomAddress CONSTANT)
Q_PROPERTY(QString avatarUri READ getAvatarUri WRITE setAvatarUri NOTIFY avatarUriChanged)
Q_PROPERTY(QDateTime lastUpdatedTime READ getLastUpdatedTime WRITE setLastUpdatedTime NOTIFY lastUpdatedTimeChanged)
Q_PROPERTY(QString lastMessageText READ getLastMessageText NOTIFY lastMessageChanged)
Q_PROPERTY(ChatMessageGui *lastMessage READ getLastMessage NOTIFY lastMessageChanged)
Q_PROPERTY(LinphoneEnums::ChatMessageState lastMessageState READ getLastMessageState NOTIFY lastMessageChanged)
Q_PROPERTY(LinphoneEnums::ChatRoomState state READ getChatRoomState NOTIFY chatRoomStateChanged)
Q_PROPERTY(int unreadMessagesCount READ getUnreadMessagesCount WRITE setUnreadMessagesCount NOTIFY
unreadMessagesCountChanged)
Q_PROPERTY(QString composingName READ getComposingName WRITE setComposingName NOTIFY composingUserChanged)
Q_PROPERTY(QString composingAddress READ getComposingAddress WRITE setComposingAddress NOTIFY composingUserChanged)
Q_PROPERTY(bool isGroupChat READ isGroupChat CONSTANT)
Q_PROPERTY(bool isEncrypted READ isEncrypted CONSTANT)
Q_PROPERTY(bool isReadOnly READ getIsReadOnly WRITE setIsReadOnly NOTIFY readOnlyChanged)
Q_PROPERTY(bool isSecured READ isSecured WRITE setIsSecured NOTIFY isSecuredChanged)
Q_PROPERTY(bool isBasic MEMBER mIsBasic CONSTANT)
Q_PROPERTY(QString sendingText READ getSendingText WRITE setSendingText NOTIFY sendingTextChanged)
Q_PROPERTY(bool ephemeralEnabled READ isEphemeralEnabled WRITE lEnableEphemeral NOTIFY ephemeralEnabledChanged)
Q_PROPERTY(
int ephemeralLifetime READ getEphemeralLifetime WRITE lSetEphemeralLifetime NOTIFY ephemeralLifetimeChanged)
Q_PROPERTY(bool muted READ isMuted WRITE lSetMuted NOTIFY mutedChanged)
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
participantsChanged)
Q_PROPERTY(QList<QSharedPointer<ChatMessageContentCore>> fileList READ getFileList NOTIFY fileListChanged)
// Should be call from model Thread. Will be automatically in App thread after initialization
static QSharedPointer<ChatCore> create(const std::shared_ptr<linphone::ChatRoom> &chatRoom);
ChatCore(const std::shared_ptr<linphone::ChatRoom> &chatRoom);
~ChatCore();
void setSelf(QSharedPointer<ChatCore> me);
QDateTime getLastUpdatedTime() const;
void setLastUpdatedTime(QDateTime time);
QString getTitle() const;
void setTitle(QString title);
bool isGroupChat() const;
bool isEncrypted() const;
bool isMuted() const;
bool isEphemeralEnabled() const;
int getEphemeralLifetime() const;
QString getIdentifier() const;
QString getSendingText() const;
void setSendingText(const QString &text);
ChatMessageGui *getLastMessage() const;
QString getLastMessageText() const;
QList<QSharedPointer<ChatMessageContentCore>> getFileList() const;
void resetFileList(QList<QSharedPointer<ChatMessageContentCore>> list);
LinphoneEnums::ChatMessageState getLastMessageState() const;
LinphoneEnums::ChatRoomState getChatRoomState() const;
void setChatRoomState(LinphoneEnums::ChatRoomState state);
bool getIsReadOnly() const;
void setIsReadOnly(bool readOnly);
QSharedPointer<ChatMessageCore> getLastMessageCore() const;
void setLastMessage(QSharedPointer<ChatMessageCore> lastMessage);
int getUnreadMessagesCount() const;
void setUnreadMessagesCount(int count);
QString getChatRoomAddress() const;
QString getParticipantAddress() const;
bool getMeAdmin() const;
void setMeAdmin(bool admin);
bool isSecured() const;
void setIsSecured(bool secured);
bool computeSecuredStatus() const;
// void resetEventLogList(QList<QSharedPointer<EventLogCore>> list);
// void appendEventLogToEventLogList(QSharedPointer<EventLogCore> event);
// void appendEventLogsToEventLogList(QList<QSharedPointer<EventLogCore>> list);
// void removeEventLogsFromEventLogList(QList<QSharedPointer<EventLogCore>> list);
// void clearEventLogList();
QString getAvatarUri() const;
void setAvatarUri(QString avatarUri);
QString getComposingName() const;
QString getComposingAddress() const;
void setComposingName(QString composingName);
void setComposingAddress(QString composingAddress);
std::shared_ptr<ChatModel> getModel() const;
void setParticipants(QList<QSharedPointer<ParticipantCore>> participants);
QList<QSharedPointer<ParticipantCore>> buildParticipants(const std::shared_ptr<linphone::ChatRoom> &chatRoom) const;
QList<QSharedPointer<ParticipantCore>> getParticipants() const;
QVariantList getParticipantsGui() const;
QStringList getParticipantsAddresses() const;
QString getLocalAddress() const;
void updateInfo(const std::shared_ptr<linphone::Friend> &updatedFriend, bool isRemoval = false);
signals:
// used to close all the notifications when one is clicked
void messageOpen();
void lastUpdatedTimeChanged(QDateTime time);
void lastMessageChanged();
void titleChanged(QString title);
void unreadMessagesCountChanged(int count);
void eventListCleared();
void eventsInserted(QList<QSharedPointer<EventLogCore>> list);
void avatarUriChanged();
void deleted();
void composingUserChanged();
void chatRoomStateChanged();
void readOnlyChanged();
void sendingTextChanged(QString text);
void mutedChanged();
void ephemeralEnabledChanged();
void ephemeralLifetimeChanged();
void meAdminChanged();
void participantsChanged();
void fileListChanged();
void isSecuredChanged();
void conferenceJoined();
void lDeleteMessage(ChatMessageGui *message);
void lDelete();
void lDeleteHistory();
void lMarkAsRead();
void lUpdateLastMessage();
void lUpdateUnreadCount();
void lUpdateLastUpdatedTime();
void lSendTextMessage(QString message);
void lSendMessage(QString message, QVariantList files);
void lSendVoiceMessage();
void lCompose();
void lLeave();
void lSetMuted(bool muted);
void lEnableEphemeral(bool enable);
void lSetEphemeralLifetime(int time);
void lSetSubject(QString subject);
void lRemoveParticipantAtIndex(int index);
void lSetParticipantsAddresses(QStringList addresses);
void lToggleParticipantAdminStatusAtIndex(int index);
private:
QString id;
QDateTime mLastUpdatedTime;
QString mParticipantAddress;
QString mChatRoomAddress;
QString mTitle;
QString mIdentifier;
QString mAvatarUri;
QString mSendingText;
int mUnreadMessagesCount;
QString mComposingName;
QString mComposingAddress;
QString mLocalAddress;
bool mIsGroupChat = false;
bool mIsEncrypted = false;
bool mIsReadOnly = false;
bool mEphemeralEnabled = false;
// ChatRoom is secured if all its participants are
// EndToEndEncryptedAndVerified friends
bool mIsSecured = false;
bool mIsBasic = false;
int mEphemeralLifetime = 0;
QList<QSharedPointer<ChatMessageContentCore>> mFileList;
bool mIsMuted = false;
bool mMeAdmin = false;
bool mConferenceJoined = false;
QList<QSharedPointer<ParticipantCore>> mParticipants;
LinphoneEnums::ChatRoomState mChatRoomState;
std::shared_ptr<ChatModel> mChatModel;
QSharedPointer<ChatMessageCore> mLastMessage;
std::shared_ptr<FriendModel> mFriendModel;
QSharedPointer<SafeConnection<ChatCore, ChatModel>> mChatModelConnection;
QSharedPointer<SafeConnection<ChatCore, CoreModel>> mCoreModelConnection;
DECLARE_ABSTRACT_OBJECT
};
Q_DECLARE_METATYPE(ChatCore *)
#endif

View file

@ -0,0 +1,38 @@
/*
* Copyright (c) 2010-2024 Belledonne Communications SARL.
*
* This file is part of linphone-desktop
* (see https://www.linphone.org).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "ChatGui.hpp"
#include "core/App.hpp"
DEFINE_ABSTRACT_OBJECT(ChatGui)
ChatGui::ChatGui(QSharedPointer<ChatCore> core) {
App::getInstance()->mEngine->setObjectOwnership(this, QQmlEngine::JavaScriptOwnership);
mCore = core;
if (isInLinphoneThread()) moveToThread(App::getInstance()->thread());
}
ChatGui::~ChatGui() {
mustBeInMainThread("~" + getClassName());
}
ChatCore *ChatGui::getCore() const {
return mCore.get();
}

View file

@ -0,0 +1,41 @@
/*
* Copyright (c) 2010-2024 Belledonne Communications SARL.
*
* This file is part of linphone-desktop
* (see https://www.linphone.org).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef CHAT_GUI_H_
#define CHAT_GUI_H_
#include "ChatCore.hpp"
#include <QObject>
#include <QSharedPointer>
class ChatGui : public QObject, public AbstractObject {
Q_OBJECT
Q_PROPERTY(ChatCore *core READ getCore CONSTANT)
public:
ChatGui(QSharedPointer<ChatCore> core);
~ChatGui();
ChatCore *getCore() const;
QSharedPointer<ChatCore> mCore;
DECLARE_ABSTRACT_OBJECT
};
#endif

View file

@ -0,0 +1,209 @@
/*
* Copyright (c) 2010-2024 Belledonne Communications SARL.
*
* This file is part of linphone-desktop
* (see https://www.linphone.org).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "ChatList.hpp"
#include "ChatCore.hpp"
#include "ChatGui.hpp"
#include "core/App.hpp"
#include "model/tool/ToolModel.hpp"
#include <QSharedPointer>
#include <linphone++/linphone.hh>
// =============================================================================
DEFINE_ABSTRACT_OBJECT(ChatList)
QSharedPointer<ChatList> ChatList::create() {
auto model = QSharedPointer<ChatList>(new ChatList(), &QObject::deleteLater);
model->moveToThread(App::getInstance()->thread());
model->setSelf(model);
return model;
}
QSharedPointer<ChatCore> ChatList::createChatCore(const std::shared_ptr<linphone::ChatRoom> &chatroom) {
auto chatCore = ChatCore::create(chatroom);
return chatCore;
}
ChatList::ChatList(QObject *parent) : ListProxy(parent) {
mustBeInMainThread(getClassName());
App::getInstance()->mEngine->setObjectOwnership(this, QQmlEngine::CppOwnership);
}
ChatList::~ChatList() {
mustBeInMainThread("~" + getClassName());
mModelConnection = nullptr;
}
void ChatList::connectItem(QSharedPointer<ChatCore> chat) {
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);
if (i != -1) {
auto modelIndex = index(i);
emit dataChanged(modelIndex, modelIndex);
}
};
connect(chat.get(), &ChatCore::unreadMessagesCountChanged, this, [this, dataChange] {
dataChange();
auto defaultAccount = App::getInstance()->getAccountList()->getDefaultAccountCore();
if (defaultAccount) emit defaultAccount->lRefreshNotifications();
});
connect(chat.get(), &ChatCore::lastUpdatedTimeChanged, this, dataChange);
connect(chat.get(), &ChatCore::lastMessageChanged, this, dataChange);
}
void ChatList::setSelf(QSharedPointer<ChatList> me) {
mModelConnection = SafeConnection<ChatList, CoreModel>::create(me, CoreModel::getInstance());
mModelConnection->makeConnectToCore(&ChatList::lUpdate, [this]() {
if (mIsUpdating) {
connect(this, &ChatList::isUpdatingChanged, this, [this] {
if (!mIsUpdating) {
disconnect(this, &ChatList::isUpdatingChanged, this, nullptr);
lUpdate();
}
});
return;
}
setIsUpdating(true);
mModelConnection->invokeToModel([this]() {
mustBeInLinphoneThread(getClassName());
beginResetModel();
// Avoid copy to lambdas
QList<QSharedPointer<ChatCore>> *chats = new QList<QSharedPointer<ChatCore>>();
auto currentAccount = CoreModel::getInstance()->getCore()->getDefaultAccount();
if (!currentAccount) {
setIsUpdating(false);
endResetModel();
return;
}
auto linphoneChatRooms = currentAccount->filterChatRooms(Utils::appStringToCoreString(mFilter));
for (auto it : linphoneChatRooms) {
auto model = createChatCore(it);
chats->push_back(model);
}
mModelConnection->invokeToCore([this, chats]() {
mustBeInMainThread(getClassName());
for (auto &chat : getSharedList<ChatCore>()) {
if (chat) {
disconnect(chat.get(), &ChatCore::deleted, this, nullptr);
disconnect(chat.get(), &ChatCore::unreadMessagesCountChanged, this, nullptr);
disconnect(chat.get(), &ChatCore::lastUpdatedTimeChanged, this, nullptr);
disconnect(chat.get(), &ChatCore::lastMessageChanged, this, nullptr);
}
}
mList.clear();
for (auto &chat : *chats) {
connectItem(chat);
}
add(*chats);
endResetModel();
setIsUpdating(false);
delete chats;
});
});
});
mModelConnection->makeConnectToModel(
&CoreModel::defaultAccountChanged,
[this](std::shared_ptr<linphone::Core> core, std::shared_ptr<linphone::Account> account) { lUpdate(); });
auto addChatToList = [this](const std::shared_ptr<linphone::Core> &core,
const std::shared_ptr<linphone::ChatRoom> &room,
const std::shared_ptr<linphone::ChatMessage> &message) {
mustBeInLinphoneThread(log().arg(Q_FUNC_INFO));
if (!message) return;
if (room->getAccount() != core->getDefaultAccount()) {
qWarning() << log().arg("Chat room does not refer to current account, return");
return;
}
auto chatCore = ChatCore::create(room);
mModelConnection->invokeToCore([this, chatCore] { addChatInList(chatCore); });
};
mModelConnection->makeConnectToModel(&CoreModel::messageReceived,
[this, addChatToList](const std::shared_ptr<linphone::Core> &core,
const std::shared_ptr<linphone::ChatRoom> &room,
const std::shared_ptr<linphone::ChatMessage> &message) {
addChatToList(core, room, message);
});
mModelConnection->makeConnectToModel(
&CoreModel::messagesReceived,
[this, addChatToList](const std::shared_ptr<linphone::Core> &core,
const std::shared_ptr<linphone::ChatRoom> &room,
const std::list<std::shared_ptr<linphone::ChatMessage>> &messages) {
addChatToList(core, room, messages.front());
});
mModelConnection->makeConnectToModel(
&CoreModel::newMessageReaction,
[this, addChatToList](const std::shared_ptr<linphone::Core> &core,
const std::shared_ptr<linphone::ChatRoom> &room,
const std::shared_ptr<linphone::ChatMessage> &message,
const std::shared_ptr<const linphone::ChatMessageReaction> &reaction) {
addChatToList(core, room, message);
});
connect(this, &ChatList::filterChanged, [this](QString filter) {
mFilter = filter;
lUpdate();
});
lUpdate();
}
int ChatList::findChatIndex(ChatGui *chatGui) {
if (!chatGui) return -1;
auto core = chatGui->mCore;
auto chatList = getSharedList<ChatCore>();
auto it = std::find_if(chatList.begin(), chatList.end(), [core](const QSharedPointer<ChatCore> item) {
return item->getIdentifier() == core->getIdentifier();
});
return it == chatList.end() ? -1 : std::distance(chatList.begin(), it);
}
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->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 {
int row = index.row();
if (!index.isValid() || row < 0 || row >= mList.count()) return QVariant();
if (role == Qt::DisplayRole) return QVariant::fromValue(new ChatGui(mList[row].objectCast<ChatCore>()));
return QVariant();
}

View file

@ -0,0 +1,60 @@
/*
* Copyright (c) 2010-2024 Belledonne Communications SARL.
*
* This file is part of linphone-desktop
* (see https://www.linphone.org).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef CHAT_LIST_H_
#define CHAT_LIST_H_
#include "../proxy/ListProxy.hpp"
#include "tool/AbstractObject.hpp"
#include "tool/thread/SafeConnection.hpp"
#include <QLocale>
class ChatGui;
class ChatCore;
// =============================================================================
class ChatList : public ListProxy, public AbstractObject {
Q_OBJECT
public:
static QSharedPointer<ChatList> create();
// Create a ChatCore and make connections to List.
QSharedPointer<ChatCore> createChatCore(const std::shared_ptr<linphone::ChatRoom> &chatroom);
ChatList(QObject *parent = Q_NULLPTR);
~ChatList();
void setSelf(QSharedPointer<ChatList> me);
void connectItem(QSharedPointer<ChatCore> chat);
int findChatIndex(ChatGui *chat);
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 chatAdded();
void chatUpdated();
private:
QString mFilter;
QSharedPointer<SafeConnection<ChatList, CoreModel>> mModelConnection;
DECLARE_ABSTRACT_OBJECT
};
#endif

View file

@ -0,0 +1,81 @@
/*
* Copyright (c) 2010-2024 Belledonne Communications SARL.
*
* This file is part of linphone-desktop
* (see https://www.linphone.org).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "ChatProxy.hpp"
#include "ChatGui.hpp"
#include "ChatList.hpp"
#include "core/App.hpp"
DEFINE_ABSTRACT_OBJECT(ChatProxy)
ChatProxy::ChatProxy(QObject *parent) {
mList = ChatList::create();
setSourceModel(mList.get());
setDynamicSortFilter(true);
}
ChatProxy::~ChatProxy() {
}
void ChatProxy::setSourceModel(QAbstractItemModel *model) {
auto oldChatList = dynamic_cast<ChatList *>(sourceModel());
if (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::chatAdded, this, [this] { invalidate(); });
connect(newChatList, &ChatList::dataChanged, this, [this] { invalidate(); });
}
QSortFilterProxyModel::setSourceModel(newChatList);
sort(0);
}
int ChatProxy::findChatIndex(ChatGui *chatGui) {
auto chatList = dynamic_cast<ChatList *>(sourceModel());
if (chatList) {
auto listIndex = chatList->findChatIndex(chatGui);
if (listIndex != -1) {
listIndex = mapFromSource(chatList->index(listIndex, 0)).row();
return listIndex;
}
}
return -1;
}
bool ChatProxy::addChatInList(ChatGui *chatGui) {
auto chatList = dynamic_cast<ChatList *>(sourceModel());
if (chatList && chatGui) {
return chatList->addChatInList(chatGui->mCore);
}
return false;
}
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();
return false;
}

View file

@ -0,0 +1,50 @@
/*
* Copyright (c) 2010-2024 Belledonne Communications SARL.
*
* This file is part of linphone-desktop
* (see https://www.linphone.org).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef CHAT_PROXY_H_
#define CHAT_PROXY_H_
#include "../proxy/SortFilterProxy.hpp"
#include "core/chat/ChatGui.hpp"
#include "core/chat/ChatList.hpp"
#include "tool/AbstractObject.hpp"
// =============================================================================
class ChatProxy : public SortFilterProxy, public AbstractObject {
Q_OBJECT
public:
ChatProxy(QObject *parent = Q_NULLPTR);
~ChatProxy();
void setSourceModel(QAbstractItemModel *sourceModel) override;
bool lessThan(const QModelIndex &sourceLeft, const QModelIndex &sourceRight) const override;
Q_INVOKABLE int findChatIndex(ChatGui *chatGui);
Q_INVOKABLE bool addChatInList(ChatGui *chatGui);
protected:
QSharedPointer<ChatList> mList;
DECLARE_ABSTRACT_OBJECT
};
#endif

View file

@ -0,0 +1,127 @@
/*
* Copyright (c) 2010-2024 Belledonne Communications SARL.
*
* This file is part of linphone-desktop
* (see https://www.linphone.org).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "ChatMessageFileList.hpp"
#include "core/App.hpp"
#include "core/chat/message/content/ChatMessageContentCore.hpp"
#include <QSharedPointer>
#include <linphone++/linphone.hh>
// =============================================================================
DEFINE_ABSTRACT_OBJECT(ChatMessageFileList)
QSharedPointer<ChatMessageFileList> ChatMessageFileList::create() {
auto model = QSharedPointer<ChatMessageFileList>(new ChatMessageFileList(), &QObject::deleteLater);
model->setSelf(model);
model->moveToThread(App::getInstance()->thread());
return model;
}
ChatMessageFileList::ChatMessageFileList(QObject *parent) : ListProxy(parent) {
mustBeInMainThread(getClassName());
App::getInstance()->mEngine->setObjectOwnership(this, QQmlEngine::CppOwnership);
}
ChatMessageFileList::~ChatMessageFileList() {
mustBeInMainThread("~" + getClassName());
mList.clear();
}
void ChatMessageFileList::setSelf(QSharedPointer<ChatMessageFileList> me) {
mCoreModelConnection = SafeConnection<ChatMessageFileList, CoreModel>::create(me, CoreModel::getInstance());
mCoreModelConnection->makeConnectToCore(&ChatMessageFileList::lUpdate, [this]() {
mustBeInMainThread(log().arg(Q_FUNC_INFO));
beginResetModel();
mList.clear();
if (!mChat) {
endResetModel();
return;
}
auto chatModel = mChat->getModel();
if (!chatModel) {
endResetModel();
return;
}
mCoreModelConnection->invokeToModel([this, chatModel]() {
mustBeInLinphoneThread(log().arg(Q_FUNC_INFO));
std::list<std::shared_ptr<linphone::Content>> medias;
std::list<std::shared_ptr<linphone::Content>> docs;
QList<QSharedPointer<ChatMessageContentCore>> *contents =
new QList<QSharedPointer<ChatMessageContentCore>>();
if (mFilterType == (int)FilterContentType::Medias) {
medias = chatModel->getSharedMedias();
} else if (mFilterType == (int)FilterContentType::Documents) {
docs = chatModel->getSharedDocuments();
} else {
medias = chatModel->getSharedMedias();
docs = chatModel->getSharedDocuments();
}
for (auto it : medias) {
auto model = ChatMessageContentCore::create(it, nullptr);
contents->push_back(model);
}
for (auto it : docs) {
auto model = ChatMessageContentCore::create(it, nullptr);
contents->push_back(model);
}
mCoreModelConnection->invokeToCore([this, contents] {
for (auto i : *contents)
mList << i.template objectCast<QObject>();
endResetModel();
});
});
});
}
QSharedPointer<ChatCore> ChatMessageFileList::getChatCore() const {
return mChat;
}
void ChatMessageFileList::setChatCore(QSharedPointer<ChatCore> chatCore) {
if (mChat != chatCore) {
// if (mChat) disconnect(mChat.get());
mChat = chatCore;
// if (mChat) connect(mChat.get(), &ChatCore::fileListChanged, this, lUpdate);
lUpdate();
emit chatChanged();
}
}
int ChatMessageFileList::getFilterType() const {
return mFilterType;
}
void ChatMessageFileList::setFilterType(int filterType) {
if (mFilterType != filterType) {
mFilterType = filterType;
lUpdate();
}
}
QVariant ChatMessageFileList::data(const QModelIndex &index, int role) const {
int row = index.row();
if (!index.isValid() || row < 0 || row >= mList.count()) return QVariant();
if (role == Qt::DisplayRole)
return QVariant::fromValue(new ChatMessageContentGui(mList[row].objectCast<ChatMessageContentCore>()));
return QVariant();
}

View file

@ -0,0 +1,61 @@
/*
* Copyright (c) 2010-2024 Belledonne Communications SARL.
*
* This file is part of linphone-desktop
* (see https://www.linphone.org).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef CHAT_MESSAGE_FILE_LIST_H_
#define CHAT_MESSAGE_FILE_LIST_H_
#include "core/chat/ChatCore.hpp"
#include "core/proxy/ListProxy.hpp"
#include "tool/AbstractObject.hpp"
#include "tool/thread/SafeConnection.hpp"
#include <QLocale>
// =============================================================================
class ChatMessageFileList : public ListProxy, public AbstractObject {
Q_OBJECT
public:
enum class FilterContentType { All = 0, Medias = 1, Documents = 2 };
static QSharedPointer<ChatMessageFileList> create();
ChatMessageFileList(QObject *parent = Q_NULLPTR);
~ChatMessageFileList();
void setSelf(QSharedPointer<ChatMessageFileList> me);
QSharedPointer<ChatCore> getChatCore() const;
void setChatCore(QSharedPointer<ChatCore> chatCore);
int getFilterType() const;
void setFilterType(int filterType);
virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
signals:
void chatChanged();
void lUpdate();
void filterTypeChanged();
private:
int mFilterType;
QSharedPointer<ChatCore> mChat;
QSharedPointer<SafeConnection<ChatMessageFileList, CoreModel>> mCoreModelConnection;
DECLARE_ABSTRACT_OBJECT
};
#endif

View file

@ -0,0 +1,65 @@
/*
* Copyright (c) 2010-2024 Belledonne Communications SARL.
*
* This file is part of linphone-desktop
* (see https://www.linphone.org).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "ChatMessageFileProxy.hpp"
#include "core/App.hpp"
#include "core/chat/ChatGui.hpp"
DEFINE_ABSTRACT_OBJECT(ChatMessageFileProxy)
ChatMessageFileProxy::ChatMessageFileProxy(QObject *parent) : LimitProxy(parent) {
mList = ChatMessageFileList::create();
connect(mList.get(), &ChatMessageFileList::chatChanged, this, &ChatMessageFileProxy::chatGuiChanged);
connect(this, &ChatMessageFileProxy::filterTypeChanged, this, [this] { invalidate(); });
setSourceModels(new SortFilterList(mList.get()));
}
ChatMessageFileProxy::~ChatMessageFileProxy() {
}
ChatGui *ChatMessageFileProxy::getChatGui() const {
return mList && mList->getChatCore() ? new ChatGui(mList->getChatCore()) : nullptr;
}
void ChatMessageFileProxy::setChatGui(ChatGui *chat) const {
if (mList) mList->setChatCore(chat ? chat->mCore : nullptr);
}
bool ChatMessageFileProxy::SortFilterList::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const {
if (getFilterType() == (int)FilterContentType::All) return true;
else {
auto contentCore = getItemAtSource<ChatMessageFileList, ChatMessageContentCore>(sourceRow);
if (!contentCore) return false;
bool isMedia = Utils::isVideo(contentCore->getFilePath()) || Utils::isImage(contentCore->getFilePath()) ||
Utils::isAnimatedImage(contentCore->getFilePath());
if (getFilterType() == (int)FilterContentType::Medias) {
return isMedia;
} else {
return !isMedia;
}
return false;
}
}
bool ChatMessageFileProxy::SortFilterList::lessThan(const QModelIndex &sourceLeft,
const QModelIndex &sourceRight) const {
return true;
}

View file

@ -0,0 +1,56 @@
/*
* Copyright (c) 2010-2024 Belledonne Communications SARL.
*
* This file is part of linphone-desktop
* (see https://www.linphone.org).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef CHAT_MESSAGE_FILE_PROXY_H_
#define CHAT_MESSAGE_FILE_PROXY_H_
#include "ChatMessageFileList.hpp"
#include "core/chat/message/ChatMessageCore.hpp"
#include "core/proxy/LimitProxy.hpp"
#include "tool/AbstractObject.hpp"
// =============================================================================
class ChatMessageFileProxy : public LimitProxy, public AbstractObject {
Q_OBJECT
Q_PROPERTY(ChatGui *chat READ getChatGui WRITE setChatGui NOTIFY chatGuiChanged)
public:
enum class FilterContentType { All = 0, Medias = 1, Documents = 2 };
Q_ENUM(FilterContentType)
DECLARE_SORTFILTER_CLASS()
ChatMessageFileProxy(QObject *parent = Q_NULLPTR);
~ChatMessageFileProxy();
ChatGui *getChatGui() const;
void setChatGui(ChatGui *chat) const;
signals:
void chatGuiChanged();
void filterChanged();
protected:
QSharedPointer<ChatMessageFileList> mList;
DECLARE_ABSTRACT_OBJECT
};
#endif

View file

@ -0,0 +1,694 @@
/*
* Copyright (c) 2010-2024 Belledonne Communications SARL.
*
* This file is part of linphone-desktop
* (see https://www.linphone.org).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "ChatMessageCore.hpp"
#include "core/App.hpp"
#include "model/tool/ToolModel.hpp"
#include <QQuickWindow>
DEFINE_ABSTRACT_OBJECT(ChatMessageCore)
/***********************************************************************/
ImdnStatus ImdnStatus::operator=(ImdnStatus r) {
mAddress = r.mAddress;
mState = r.mState;
mLastUpdatedTime = r.mLastUpdatedTime;
return *this;
}
bool ImdnStatus::operator==(const ImdnStatus &r) const {
return r.mState == mState && r.mAddress == mAddress && r.mLastUpdatedTime == mLastUpdatedTime;
}
bool ImdnStatus::operator!=(ImdnStatus r) {
return r.mState != mState || r.mAddress != mAddress || r.mLastUpdatedTime != mLastUpdatedTime;
}
ImdnStatus::ImdnStatus() {
}
ImdnStatus::ImdnStatus(const QString &address,
const LinphoneEnums::ChatMessageState &state,
QDateTime lastUpdatedTime) {
mState = state;
mAddress = address;
mLastUpdatedTime = lastUpdatedTime;
}
ImdnStatus ImdnStatus::createMessageImdnStatusVariant(const QString &address,
const LinphoneEnums::ChatMessageState &state,
QDateTime lastUpdatedTime) {
ImdnStatus s;
s.mState = state;
s.mAddress = address;
s.mLastUpdatedTime = lastUpdatedTime;
return s;
}
QVariant createImdnStatusSingletonVariant(const LinphoneEnums::ChatMessageState &state, int count = 1) {
QVariantMap map;
map.insert("state", QVariant::fromValue(state));
map.insert("count", count);
return map;
}
/***********************************************************************/
Reaction Reaction::operator=(Reaction r) {
mAddress = r.mAddress;
mBody = r.mBody;
return *this;
}
bool Reaction::operator==(const Reaction &r) const {
return r.mBody == mBody && r.mAddress == mAddress;
}
bool Reaction::operator!=(Reaction r) {
return r.mBody != mBody || r.mAddress != mAddress;
}
Reaction Reaction::createMessageReactionVariant(const QString &body, const QString &address) {
Reaction r;
r.mBody = body;
r.mAddress = address;
return r;
}
QVariant createReactionSingletonVariant(const QString &body, int count = 1) {
QVariantMap map;
map.insert("body", body);
map.insert("count", count);
return map;
}
/***********************************************************************/
QSharedPointer<ChatMessageCore> ChatMessageCore::create(const std::shared_ptr<linphone::ChatMessage> &chatmessage) {
auto sharedPointer = QSharedPointer<ChatMessageCore>(new ChatMessageCore(chatmessage), &QObject::deleteLater);
sharedPointer->setSelf(sharedPointer);
sharedPointer->moveToThread(App::getInstance()->thread());
return sharedPointer;
}
ChatMessageCore::ChatMessageCore(const std::shared_ptr<linphone::ChatMessage> &chatmessage) {
// lDebug() << "[ChatMessageCore] new" << this;
mustBeInLinphoneThread(getClassName());
App::getInstance()->mEngine->setObjectOwnership(this, QQmlEngine::CppOwnership);
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();
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;
}
}
//: "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);
}
}
}
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);
}
}
ChatMessageCore::~ChatMessageCore() {
}
void ChatMessageCore::setSelf(QSharedPointer<ChatMessageCore> me) {
mChatMessageModelConnection = SafeConnection<ChatMessageCore, ChatMessageModel>::create(me, mChatMessageModel);
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
if (deletedByUser)
Utils::showInformationPopup(tr("info_toast_deleted_title"),
//: The message has been deleted
tr("info_toast_deleted_message"), true);
emit deleted();
});
});
mChatMessageModelConnection->makeConnectToCore(&ChatMessageCore::lMarkAsRead, [this] {
auto mainWindow = Utils::getMainWindow();
if (mainWindow->isActive())
mChatMessageModelConnection->invokeToModel([this] { mChatMessageModel->markAsRead(); });
else {
connect(mainWindow, &QQuickWindow::activeChanged, this, [this, mainWindow] {
if (mainWindow->isActive()) {
disconnect(mainWindow, &QQuickWindow::activeChanged, this, nullptr);
mChatMessageModelConnection->invokeToModel([this, mainWindow] { mChatMessageModel->markAsRead(); });
}
});
}
});
mChatMessageModelConnection->makeConnectToModel(&ChatMessageModel::messageRead, [this]() {
mChatMessageModelConnection->invokeToCore([this] { setIsRead(true); });
});
mChatMessageModelConnection->makeConnectToCore(&ChatMessageCore::lSendReaction, [this](const QString &reaction) {
mChatMessageModelConnection->invokeToModel([this, reaction] { mChatMessageModel->sendReaction(reaction); });
});
mChatMessageModelConnection->makeConnectToCore(&ChatMessageCore::lRemoveReaction, [this]() {
mChatMessageModelConnection->invokeToModel([this] { mChatMessageModel->removeReaction(); });
});
mChatMessageModelConnection->makeConnectToCore(&ChatMessageCore::lSend, [this]() {
mChatMessageModelConnection->invokeToModel([this] { mChatMessageModel->send(); });
});
mChatMessageModelConnection->makeConnectToModel(
&ChatMessageModel::newMessageReaction,
[this](const std::shared_ptr<linphone::ChatMessage> &message,
const std::shared_ptr<const linphone::ChatMessageReaction> &reaction) {
auto ownReac = message->getOwnReaction();
auto own = ownReac ? Utils::coreStringToAppString(message->getOwnReaction()->getBody()) : QString();
// We must reset all the reactions each time cause reactionRemoved is not emitted
// when someone change its current reaction
QList<Reaction> reactions;
for (auto &reaction : message->getReactions()) {
if (reaction) {
auto fromAddr = reaction->getFromAddress()->clone();
fromAddr->clean();
reactions.append(Reaction::createMessageReactionVariant(
Utils::coreStringToAppString(reaction->getBody()),
Utils::coreStringToAppString(fromAddr->asStringUriOnly())));
}
}
mChatMessageModelConnection->invokeToCore([this, own, reactions] {
setOwnReaction(own);
setReactions(reactions);
});
});
mChatMessageModelConnection->makeConnectToModel(
&ChatMessageModel::reactionRemoved, [this](const std::shared_ptr<linphone::ChatMessage> &message,
const std::shared_ptr<const linphone::Address> &address) {
auto reac = message->getOwnReaction();
auto own = reac ? Utils::coreStringToAppString(message->getOwnReaction()->getBody()) : QString();
auto addr = address->clone();
addr->clean();
QString addressString = Utils::coreStringToAppString(addr->asStringUriOnly());
mChatMessageModelConnection->invokeToCore([this, own, addressString] {
removeReaction(addressString);
setOwnReaction(own);
});
});
mChatMessageModelConnection->makeConnectToModel(
&ChatMessageModel::msgStateChanged,
[this](const std::shared_ptr<linphone::ChatMessage> &message, linphone::ChatMessage::State state) {
auto imdnStatusList = computeDeliveryStatus(message);
auto msgState = LinphoneEnums::fromLinphone(state);
mChatMessageModelConnection->invokeToCore([this, msgState, imdnStatusList] {
setImdnStatusList(imdnStatusList);
setMessageState(msgState);
});
});
mChatMessageModelConnection->makeConnectToModel(
&ChatMessageModel::fileTransferProgressIndication,
[this](const std::shared_ptr<linphone::ChatMessage> &message, const std::shared_ptr<linphone::Content> &content,
size_t offset, size_t total) {
mChatMessageModelConnection->invokeToCore([this, content, offset, total] {
auto it =
std::find_if(mChatMessageContentList.begin(), mChatMessageContentList.end(),
[content](QSharedPointer<ChatMessageContentCore> item) {
return item->getContentModel()->getContent()->getName() == content->getName();
});
if (it != mChatMessageContentList.end()) {
auto contentCore = mChatMessageContentList.at(std::distance(mChatMessageContentList.begin(), it));
assert(contentCore);
contentCore->setFileOffset(offset);
}
});
});
mChatMessageModelConnection->makeConnectToModel(
&ChatMessageModel::fileTransferTerminated, [this](const std::shared_ptr<linphone::ChatMessage> &message,
const std::shared_ptr<linphone::Content> &content) {
mChatMessageModelConnection->invokeToCore([this, content] {
auto it =
std::find_if(mChatMessageContentList.begin(), mChatMessageContentList.end(),
[content](QSharedPointer<ChatMessageContentCore> item) {
return item->getContentModel()->getContent()->getName() == content->getName();
});
if (it != mChatMessageContentList.end()) {
auto contentCore = mChatMessageContentList.at(std::distance(mChatMessageContentList.begin(), it));
assert(contentCore);
contentCore->setWasDownloaded(true);
}
});
});
mChatMessageModelConnection->makeConnectToModel(
&ChatMessageModel::fileTransferRecv,
[this](const std::shared_ptr<linphone::ChatMessage> &message, const std::shared_ptr<linphone::Content> &content,
const std::shared_ptr<const linphone::Buffer> &buffer) { lInfo() << log().arg("transfer received"); });
mChatMessageModelConnection->makeConnectToModel(
&ChatMessageModel::fileTransferSend,
[this](const std::shared_ptr<linphone::ChatMessage> &message, const std::shared_ptr<linphone::Content> &content,
size_t offset, size_t size) { lInfo() << log().arg("transfer send"); });
mChatMessageModelConnection->makeConnectToModel(
&ChatMessageModel::fileTransferSendChunk,
[this](const std::shared_ptr<linphone::ChatMessage> &message, const std::shared_ptr<linphone::Content> &content,
size_t offset, size_t size,
const std::shared_ptr<linphone::Buffer> &buffer) { lInfo() << log().arg("transfer send chunk"); });
mChatMessageModelConnection->makeConnectToModel(
&ChatMessageModel::participantImdnStateChanged,
[this](const std::shared_ptr<linphone::ChatMessage> &message,
const std::shared_ptr<const linphone::ParticipantImdnState> &state) {
auto imdnStatusList = computeDeliveryStatus(message);
mChatMessageModelConnection->invokeToCore([this, imdnStatusList] { setImdnStatusList(imdnStatusList); });
});
mChatMessageModelConnection->makeConnectToModel(
&ChatMessageModel::ephemeralMessageTimeUpdated,
[this](const std::shared_ptr<linphone::ChatMessage> &message, int expireTime) {
auto now = QDateTime::currentDateTime();
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) {
mustBeInLinphoneThread(log().arg(Q_FUNC_INFO));
QList<ImdnStatus> imdnStatusList;
auto createImdnStatus = [this](std::shared_ptr<linphone::ParticipantImdnState> participant,
linphone::ChatMessage::State state) -> ImdnStatus {
auto address = participant->getParticipant() ? participant->getParticipant()->getAddress()->clone() : nullptr;
auto lastUpdated = QDateTime::fromSecsSinceEpoch(participant->getStateChangeTime());
if (address) {
address->clean();
auto addrString = Utils::coreStringToAppString(address->asStringUriOnly());
auto imdn =
ImdnStatus::createMessageImdnStatusVariant(addrString, LinphoneEnums::fromLinphone(state), lastUpdated);
return imdn;
}
return ImdnStatus();
};
// Read
for (auto &participant : message->getParticipantsByImdnState(linphone::ChatMessage::State::Displayed)) {
auto imdn = createImdnStatus(participant, linphone::ChatMessage::State::Displayed);
imdnStatusList.append(imdn);
}
// Received
for (auto &participant : message->getParticipantsByImdnState(linphone::ChatMessage::State::DeliveredToUser)) {
auto imdn = createImdnStatus(participant, linphone::ChatMessage::State::DeliveredToUser);
imdnStatusList.append(imdn);
}
// Sent
for (auto &participant : message->getParticipantsByImdnState(linphone::ChatMessage::State::Delivered)) {
auto imdn = createImdnStatus(participant, linphone::ChatMessage::State::Delivered);
imdnStatusList.append(imdn);
}
// Error
for (auto &participant : message->getParticipantsByImdnState(linphone::ChatMessage::State::NotDelivered)) {
auto imdn = createImdnStatus(participant, linphone::ChatMessage::State::NotDelivered);
imdnStatusList.append(imdn);
}
return imdnStatusList;
}
QDateTime ChatMessageCore::getTimestamp() const {
return mTimestamp;
}
QString ChatMessageCore::getText() const {
return mText;
}
void ChatMessageCore::setText(QString text) {
if (mText != text) {
mText = text;
emit textChanged(text);
}
}
QString ChatMessageCore::getPeerAddress() const {
return mPeerAddress;
}
QString ChatMessageCore::getPeerName() const {
return mPeerName;
}
QString ChatMessageCore::getFromAddress() const {
return mFromAddress;
}
QString ChatMessageCore::getFromName() const {
return mFromName;
}
QString ChatMessageCore::getToAddress() const {
return mToAddress;
}
QString ChatMessageCore::getToName() const {
return mToName;
}
QString ChatMessageCore::getMessageId() const {
return mMessageId;
}
bool ChatMessageCore::isRemoteMessage() const {
return mIsRemoteMessage;
}
bool ChatMessageCore::isFromChatGroup() const {
return mIsFromChatGroup;
}
bool ChatMessageCore::isEphemeral() const {
return mIsEphemeral;
}
int ChatMessageCore::getEphemeralDuration() const {
return mEphemeralDuration;
}
void ChatMessageCore::setEphemeralDuration(int duration) {
if (mEphemeralDuration != duration) {
mEphemeralDuration = duration;
emit ephemeralDurationChanged(duration);
}
}
bool ChatMessageCore::hasFileContent() const {
return mHasFileContent;
}
bool ChatMessageCore::isRead() const {
return mIsRead;
}
void ChatMessageCore::setIsRead(bool read) {
if (mIsRead != read) {
mIsRead = read;
emit isReadChanged(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;
}
void ChatMessageCore::setOwnReaction(const QString &reaction) {
if (mOwnReaction != reaction) {
mOwnReaction = reaction;
emit messageReactionChanged();
}
}
QString ChatMessageCore::getTotalReactionsLabel() const {
return mTotalReactionsLabel;
}
QList<Reaction> ChatMessageCore::getReactions() const {
return mReactions;
}
QList<QVariant> ChatMessageCore::getReactionsSingleton() const {
return mReactionsSingletonMap;
}
QStringList ChatMessageCore::getReactionsSingletonAsStrings() const {
QStringList reacStringList;
int totalCount = 0;
for (auto &reac : mReactionsSingletonMap) {
auto map = reac.toMap();
auto count = map["count"].toInt();
totalCount += count;
reacStringList.append(QString("%1 %2").arg(map["body"].toString()).arg(count));
}
reacStringList.prepend(QString("%1 %2").arg(mTotalReactionsLabel).arg(totalCount));
return reacStringList;
}
QList<QSharedPointer<ChatMessageContentCore>> ChatMessageCore::getChatMessageContentList() const {
return mChatMessageContentList;
}
void ChatMessageCore::setReactions(const QList<Reaction> &reactions) {
mustBeInMainThread(log().arg(Q_FUNC_INFO));
mReactions = reactions;
emit messageReactionChanged();
}
void ChatMessageCore::resetReactionsSingleton() {
mReactionsSingletonMap.clear();
for (auto &reac : mReactions) {
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);
}
}
emit singletonReactionMapChanged();
}
void ChatMessageCore::removeReaction(const Reaction &reaction) {
int i = 0;
for (const auto &r : mReactions) {
if (reaction == r) {
mReactions.removeAt(i);
emit messageReactionChanged();
}
++i;
}
}
void ChatMessageCore::removeOneReactionFromSingletonMap(const QString &body) {
auto it = std::find_if(mReactionsSingletonMap.begin(), mReactionsSingletonMap.end(), [body](QVariant data) {
auto dataBody = data.toMap()["body"].toString();
return body == dataBody;
});
if (it != mReactionsSingletonMap.end()) {
auto map = it->toMap();
auto count = map["count"].toInt();
if (count <= 1) mReactionsSingletonMap.erase(it);
else {
--count;
map.remove("count");
map.insert("count", count);
}
emit messageReactionChanged();
}
}
void ChatMessageCore::removeReaction(const QString &address) {
int n = mReactions.removeIf([address, this](Reaction r) {
if (r.mAddress == address) {
removeOneReactionFromSingletonMap(r.mBody);
return true;
}
return false;
});
if (n > 0) emit messageReactionChanged();
}
LinphoneEnums::ChatMessageState ChatMessageCore::getMessageState() const {
return mMessageState;
}
void ChatMessageCore::setMessageState(LinphoneEnums::ChatMessageState state) {
if (mMessageState != state) {
mMessageState = state;
emit messageStateChanged();
}
}
QList<ImdnStatus> ChatMessageCore::getImdnStatusList() const {
return mImdnStatusList;
}
void ChatMessageCore::setImdnStatusList(QList<ImdnStatus> status) {
mImdnStatusList = status;
emit imdnStatusListChanged();
}
QStringList ChatMessageCore::getImdnStatusListLabels() const {
QStringList statusList;
int count = 0;
auto imdnSingletons = getImdnStatusAsSingletons();
for (auto &status : imdnSingletons) {
auto map = status.toMap();
auto val = map["state"].value<LinphoneEnums::ChatMessageState>();
auto count = map["count"].toInt();
statusList.append(QString("%1 %2").arg(LinphoneEnums::toString(val)).arg(count));
}
return statusList;
}
QVariantList ChatMessageCore::getImdnStatusAsSingletons() const {
QVariantList statusSingletons;
statusSingletons.append(createImdnStatusSingletonVariant(LinphoneEnums::ChatMessageState::StateDisplayed, 0));
statusSingletons.append(createImdnStatusSingletonVariant(LinphoneEnums::ChatMessageState::StateDeliveredToUser, 0));
statusSingletons.append(createImdnStatusSingletonVariant(LinphoneEnums::ChatMessageState::StateDelivered, 0));
statusSingletons.append(createImdnStatusSingletonVariant(LinphoneEnums::ChatMessageState::StateNotDelivered, 0));
for (auto &stat : mImdnStatusList) {
auto it = std::find_if(statusSingletons.begin(), statusSingletons.end(), [state = stat.mState](QVariant data) {
auto dataState = data.toMap()["state"].value<LinphoneEnums::ChatMessageState>();
return state == dataState;
});
if (it == statusSingletons.end()) {
if (!stat.mAddress.isEmpty()) statusSingletons.append(createImdnStatusSingletonVariant(stat.mState, 1));
} else {
auto map = it->toMap();
auto count = map["count"].toInt();
++count;
map.remove("count");
map.insert("count", count);
int index = std::distance(statusSingletons.begin(), it);
statusSingletons.replace(index, map);
}
}
return statusSingletons;
}
std::shared_ptr<ChatMessageModel> ChatMessageCore::getModel() const {
return mChatMessageModel;
}
// ConferenceInfoGui *ChatMessageCore::getConferenceInfoGui() const {
// return mConferenceInfo ? new ConferenceInfoGui(mConferenceInfo) : nullptr;
// }
ChatMessageContentGui *ChatMessageCore::getVoiceRecordingContent() const {
return new ChatMessageContentGui(mVoiceRecordingContent);
}

View file

@ -0,0 +1,246 @@
/*
* Copyright (c) 2010-2024 Belledonne Communications SARL.
*
* This file is part of linphone-desktop
* (see https://www.linphone.org).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef CHAT_MESSAGE_CORE_H_
#define CHAT_MESSAGE_CORE_H_
#include "EventLogCore.hpp"
#include "core/chat/message/content/ChatMessageContentGui.hpp"
#include "core/chat/message/content/ChatMessageContentProxy.hpp"
#include "core/conference/ConferenceInfoCore.hpp"
#include "core/conference/ConferenceInfoGui.hpp"
#include "model/chat/message/ChatMessageModel.hpp"
#include "tool/AbstractObject.hpp"
#include "tool/thread/SafeConnection.hpp"
#include <QObject>
#include <QSharedPointer>
#include <linphone++/linphone.hh>
struct ImdnStatus {
Q_GADGET
Q_PROPERTY(QString address MEMBER mAddress)
Q_PROPERTY(LinphoneEnums::ChatMessageState state MEMBER mState)
Q_PROPERTY(QDateTime lastUpdatedTime MEMBER mLastUpdatedTime)
public:
QString mAddress;
LinphoneEnums::ChatMessageState mState;
QDateTime mLastUpdatedTime;
ImdnStatus(const QString &address, const LinphoneEnums::ChatMessageState &state, QDateTime mLastUpdatedTime);
ImdnStatus();
ImdnStatus operator=(ImdnStatus r);
bool operator==(const ImdnStatus &r) const;
bool operator!=(ImdnStatus r);
static ImdnStatus createMessageImdnStatusVariant(const QString &address,
const LinphoneEnums::ChatMessageState &state,
QDateTime mLastUpdatedTime);
};
struct Reaction {
Q_GADGET
Q_PROPERTY(QString body MEMBER mBody)
Q_PROPERTY(QString address MEMBER mAddress)
public:
QString mBody;
QString mAddress;
Reaction operator=(Reaction r);
bool operator==(const Reaction &r) const;
bool operator!=(Reaction r);
static Reaction createMessageReactionVariant(const QString &body, const QString &address);
};
class ChatCore;
class EventLogCore;
class ChatMessageCore : public QObject, public AbstractObject {
Q_OBJECT
Q_PROPERTY(QDateTime timestamp READ getTimestamp CONSTANT)
Q_PROPERTY(QString text READ getText WRITE setText NOTIFY textChanged)
Q_PROPERTY(QString utf8Text MEMBER mUtf8Text CONSTANT)
Q_PROPERTY(bool hasTextContent MEMBER mHasTextContent CONSTANT)
Q_PROPERTY(QString peerAddress READ getPeerAddress CONSTANT)
Q_PROPERTY(QString fromAddress READ getFromAddress CONSTANT)
Q_PROPERTY(QString toAddress READ getToAddress CONSTANT)
Q_PROPERTY(QString peerName READ getPeerName CONSTANT)
Q_PROPERTY(QString fromName READ getFromName CONSTANT)
Q_PROPERTY(QString totalReactionsLabel READ getTotalReactionsLabel CONSTANT)
Q_PROPERTY(LinphoneEnums::ChatMessageState messageState READ getMessageState WRITE setMessageState NOTIFY
messageStateChanged)
Q_PROPERTY(bool isRemoteMessage READ isRemoteMessage CONSTANT)
Q_PROPERTY(bool isFromChatGroup READ isFromChatGroup CONSTANT)
Q_PROPERTY(bool isEphemeral READ isEphemeral CONSTANT)
Q_PROPERTY(
int ephemeralDuration READ getEphemeralDuration WRITE setEphemeralDuration NOTIFY ephemeralDurationChanged)
Q_PROPERTY(bool isRead READ isRead WRITE setIsRead NOTIFY isReadChanged)
Q_PROPERTY(QString ownReaction READ getOwnReaction WRITE setOwnReaction NOTIFY messageReactionChanged)
Q_PROPERTY(QStringList imdnStatusListAsString READ getImdnStatusListLabels NOTIFY imdnStatusListChanged)
Q_PROPERTY(QList<ImdnStatus> imdnStatusList READ getImdnStatusList NOTIFY imdnStatusListChanged)
Q_PROPERTY(QVariantList imdnStatusAsSingletons READ getImdnStatusAsSingletons NOTIFY imdnStatusListChanged)
Q_PROPERTY(QList<Reaction> reactions READ getReactions WRITE setReactions NOTIFY messageReactionChanged)
Q_PROPERTY(QList<QVariant> reactionsSingleton READ getReactionsSingleton NOTIFY singletonReactionMapChanged)
Q_PROPERTY(
QStringList reactionsSingletonAsStrings READ getReactionsSingletonAsStrings NOTIFY singletonReactionMapChanged)
Q_PROPERTY(bool isForward MEMBER mIsForward CONSTANT)
Q_PROPERTY(bool isReply MEMBER mIsReply CONSTANT)
Q_PROPERTY(QString replyText MEMBER mReplyText CONSTANT)
Q_PROPERTY(QString repliedToName MEMBER mRepliedToName CONSTANT)
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);
ChatMessageCore(const std::shared_ptr<linphone::ChatMessage> &chatmessage);
~ChatMessageCore();
void setSelf(QSharedPointer<ChatMessageCore> me);
QList<ImdnStatus> computeDeliveryStatus(const std::shared_ptr<linphone::ChatMessage> &message);
QString getText() const;
void setText(QString text);
QString getPeerAddress() const;
QString getPeerName() const;
QString getFromAddress() const;
QString getFromName() const;
QString getToAddress() const;
QString getToName() const;
QString getMessageId() const;
bool isRemoteMessage() const;
bool isFromChatGroup() const;
bool isEphemeral() const;
int getEphemeralDuration() const;
void setEphemeralDuration(int duration);
QDateTime getTimestamp() const;
bool hasFileContent() const;
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;
QList<Reaction> getReactions() const;
QList<QVariant> getReactionsSingleton() const;
QStringList getReactionsSingletonAsStrings() const;
QList<QSharedPointer<ChatMessageContentCore>> getChatMessageContentList() const;
void removeOneReactionFromSingletonMap(const QString &body);
void resetReactionsSingleton();
void setReactions(const QList<Reaction> &reactions);
void removeReaction(const Reaction &reaction);
void removeReaction(const QString &address);
LinphoneEnums::ChatMessageState getMessageState() const;
void setMessageState(LinphoneEnums::ChatMessageState state);
QList<ImdnStatus> getImdnStatusList() const;
void setImdnStatusList(QList<ImdnStatus> status);
QVariantList getImdnStatusAsSingletons() const;
QStringList getImdnStatusListLabels() const;
std::shared_ptr<ChatMessageModel> getModel() const;
Q_INVOKABLE ChatMessageContentGui *getVoiceRecordingContent() const;
signals:
void textChanged(QString text);
void utf8TextChanged(QString text);
void isReadChanged(bool read);
void isRemoteMessageChanged(bool isRemote);
void messageStateChanged();
void imdnStatusListChanged();
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);
void lRemoveReaction();
void lSend();
private:
DECLARE_ABSTRACT_OBJECT
QString mText;
QString mUtf8Text;
bool mHasTextContent;
QString mPeerAddress;
QString mFromAddress;
QString mToAddress;
QString mFromName;
QString mToName;
QString mPeerName;
QString mMessageId;
QString mOwnReaction;
QList<Reaction> mReactions;
QList<ImdnStatus> mImdnStatusList;
QList<QVariant> mReactionsSingletonMap;
QDateTime mTimestamp;
bool mIsRemoteMessage = false;
bool mIsFromChatGroup = false;
bool mIsRead = false;
bool mIsForward = false;
bool mIsReply = false;
QString mReplyText;
QString mRepliedToName;
bool mHasFileContent = false;
bool mIsCalendarInvite = false;
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;
LinphoneEnums::ChatMessageState mMessageState;
QList<QSharedPointer<ChatMessageContentCore>> mChatMessageContentList;
// for voice recording creation message
QSharedPointer<ChatMessageContentCore> mVoiceRecordingContent;
// QSharedPointer<ConferenceInfoCore> mConferenceInfo = nullptr;
std::shared_ptr<ChatMessageModel> mChatMessageModel;
QSharedPointer<SafeConnection<ChatMessageCore, ChatMessageModel>> mChatMessageModelConnection;
};
#endif // CHAT_MESSAGE_CORE_H_

View file

@ -0,0 +1,39 @@
/*
* Copyright (c) 2010-2024 Belledonne Communications SARL.
*
* This file is part of linphone-desktop
* (see https://www.linphone.org).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "ChatMessageGui.hpp"
#include "ChatMessageCore.hpp"
#include "core/App.hpp"
DEFINE_ABSTRACT_OBJECT(ChatMessageGui)
ChatMessageGui::ChatMessageGui(QSharedPointer<ChatMessageCore> core) {
App::getInstance()->mEngine->setObjectOwnership(this, QQmlEngine::JavaScriptOwnership);
mCore = core;
if (isInLinphoneThread()) moveToThread(App::getInstance()->thread());
}
ChatMessageGui::~ChatMessageGui() {
mustBeInMainThread("~" + getClassName());
}
ChatMessageCore *ChatMessageGui::getCore() const {
return mCore.get();
}

View file

@ -0,0 +1,41 @@
/*
* Copyright (c) 2010-2024 Belledonne Communications SARL.
*
* This file is part of linphone-desktop
* (see https://www.linphone.org).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef CHAT_MESSAGE_GUI_H_
#define CHAT_MESSAGE_GUI_H_
#include "ChatMessageCore.hpp"
#include <QObject>
#include <QSharedPointer>
class ChatMessageGui : public QObject, public AbstractObject {
Q_OBJECT
Q_PROPERTY(ChatMessageCore *core READ getCore CONSTANT)
public:
ChatMessageGui(QSharedPointer<ChatMessageCore> core);
~ChatMessageGui();
ChatMessageCore *getCore() const;
QSharedPointer<ChatMessageCore> mCore;
DECLARE_ABSTRACT_OBJECT
};
#endif

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