mirror of
https://gitlab.linphone.org/BC/public/linphone-desktop.git
synced 2026-01-17 19:38:09 +00:00
Compare commits
328 commits
6.0.1-Call
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1156bd78c0 | ||
|
|
befa4a2635 | ||
|
|
0cf3938dc3 | ||
|
|
c3b160ec3e | ||
|
|
dc1ec216e8 | ||
|
|
0bfa29dc55 | ||
|
|
0df7065b5e | ||
|
|
6d9b5efcc5 | ||
|
|
50ec67298e | ||
|
|
1bae93aab5 | ||
|
|
d40045d5bb | ||
|
|
13ec790648 | ||
|
|
528dc1e2bd | ||
|
|
31726b46cd | ||
|
|
876fdbe619 | ||
|
|
e849891548 | ||
|
|
c672762b63 | ||
|
|
e23a49fbd3 | ||
|
|
a9a1249ecd | ||
|
|
ed57ec1ea5 | ||
|
|
d4c1387c43 | ||
|
|
88aea7ba25 | ||
|
|
605ffdcdd5 | ||
|
|
21e8e2aaba | ||
|
|
60517741a2 | ||
|
|
5a90959125 | ||
|
|
c8428d6ade | ||
|
|
b2ae9213a2 | ||
|
|
d39a84ca4e | ||
|
|
dfc88b7657 | ||
|
|
f405754e24 | ||
|
|
cca8db9055 | ||
|
|
8ee8058065 | ||
|
|
96b20f42e2 | ||
|
|
5bbffa79d8 | ||
|
|
e3edeb1bcf | ||
|
|
7c2e9f6c12 | ||
|
|
ca4bdd3736 | ||
|
|
251f711250 | ||
|
|
37db390d5c | ||
|
|
04d2744bf2 | ||
|
|
a7ba374b8f | ||
|
|
29691485bf | ||
|
|
735c473b3c | ||
|
|
bba3edd4b6 | ||
|
|
514c337192 | ||
|
|
fea9a1b7be | ||
|
|
afd3514965 | ||
|
|
3f5797f453 | ||
|
|
db5f6dc2af | ||
|
|
d0cf951fe4 | ||
|
|
b802fec33c | ||
|
|
3c264fd3ee | ||
|
|
72e32ec160 | ||
|
|
bfbafab84b | ||
|
|
7a4adbcbb4 | ||
|
|
fa3ef0b1a8 | ||
|
|
2fc4439e16 | ||
|
|
0b9a0ead2a | ||
|
|
3870037905 | ||
|
|
e113058ae9 | ||
|
|
a4b38e4fd1 | ||
|
|
a0850b9967 | ||
|
|
bc5022c8f5 | ||
|
|
ac03de6663 | ||
|
|
58eb93d13b | ||
|
|
11487b3aeb | ||
|
|
ca73193f6c | ||
|
|
f2e49f21b0 | ||
|
|
a7c8e8db90 | ||
|
|
afc03daa22 | ||
|
|
74b2cf299b | ||
|
|
fcdbcdc9c1 | ||
|
|
3dbea1ccb2 | ||
|
|
d741e79e2e | ||
|
|
b3b40d6f99 | ||
|
|
d957fae94e | ||
|
|
e224f24e92 | ||
|
|
cef650fb6a | ||
|
|
9e10d5e9bd | ||
|
|
36c783c9e5 | ||
|
|
0d2e83a60d | ||
|
|
c70426f770 | ||
|
|
0c87a8d94e | ||
|
|
6f4e925766 | ||
|
|
99c2a6ddc6 | ||
|
|
cbd91b868d | ||
|
|
80bf126b23 | ||
|
|
db1f04350a | ||
|
|
08f2292881 | ||
|
|
229fbe1713 | ||
|
|
2d9f568e3d | ||
|
|
d4ce80f8c6 | ||
|
|
eb9fa8aefe | ||
|
|
cb63b1a112 | ||
|
|
b3135ea177 | ||
|
|
c0a150f3e1 | ||
|
|
8e10decd65 | ||
|
|
55a54dc10e | ||
|
|
11c586792d | ||
|
|
95d5345450 | ||
|
|
9d3bd909ca | ||
|
|
300bf3e409 | ||
|
|
a74ef62634 | ||
|
|
b17bc8cc27 | ||
|
|
7825646edd | ||
|
|
4a05b727c6 | ||
|
|
453d16250a | ||
|
|
4e81981c07 | ||
|
|
af373148e3 | ||
|
|
5f292ad545 | ||
|
|
89b9d62297 | ||
|
|
396bd56d50 | ||
|
|
97b1d11adb | ||
|
|
a9a78cb4bf | ||
|
|
1f97112306 | ||
|
|
82679ab997 | ||
|
|
9fd686c2ed | ||
|
|
c4db4d132d | ||
|
|
7e5f037332 | ||
|
|
89122ff92d | ||
|
|
295dbcb4c3 | ||
|
|
8a2e842cd7 | ||
|
|
924224abc5 | ||
|
|
580819df3a | ||
|
|
faa8f73230 | ||
|
|
641a081e60 | ||
|
|
c9d058e6e3 | ||
|
|
013ba607ec | ||
|
|
e901f0046b | ||
|
|
598b45bbf4 | ||
|
|
285e6645f8 | ||
|
|
8452ecbdfd | ||
|
|
5b7dc1bcc8 | ||
|
|
77da7183f4 | ||
|
|
4541ee3079 | ||
|
|
4fca0a4140 | ||
|
|
23234bafc4 | ||
|
|
73358b7a25 | ||
|
|
c007e975aa | ||
|
|
97d8bd621e | ||
|
|
2560691c84 | ||
|
|
5da7a9fd6b | ||
|
|
a819d134fb | ||
|
|
097c48ebba | ||
|
|
452ef79ac0 | ||
|
|
cdb23ce0a4 | ||
|
|
2a57289e18 | ||
|
|
46f20a3dc1 | ||
|
|
08c9b0daaa | ||
|
|
f8119e607d | ||
|
|
a3996fc33c | ||
|
|
3866d7ecdd | ||
|
|
123c9022ec | ||
|
|
6c6cab6661 | ||
|
|
c9065e1f12 | ||
|
|
251f404ced | ||
|
|
bad52def4d | ||
|
|
12e1e51c0c | ||
|
|
1f529f333e | ||
|
|
a457ac2c82 | ||
|
|
cbc9c5e2b9 | ||
|
|
d5841e45ec | ||
|
|
d3d352948b | ||
|
|
f30d8f3d7f | ||
|
|
f974a78fb9 | ||
|
|
13ef034516 | ||
|
|
1a4da6fb18 | ||
|
|
139a3531a4 | ||
|
|
3c85d25dfc | ||
|
|
a406bac4a9 | ||
|
|
2f52844fca | ||
|
|
6ecf1f394e | ||
|
|
e3aebd3c00 | ||
|
|
1ddfae8802 | ||
|
|
d742fa9ea0 | ||
|
|
4b613f1787 | ||
|
|
afe81094ba | ||
|
|
358125dfa6 | ||
|
|
61d62c91ca | ||
|
|
033051066b | ||
|
|
9074914f39 | ||
|
|
e55779d257 | ||
|
|
11f3546ba6 | ||
|
|
e0286fdc42 | ||
|
|
60ebaf73a9 | ||
|
|
e6266dcaf2 | ||
|
|
443bcbf6c6 | ||
|
|
99dd528e55 | ||
|
|
f516505287 | ||
|
|
4203a9c9c0 | ||
|
|
5e09348c3d | ||
|
|
54eac3ddc1 | ||
|
|
d3360cdbf8 | ||
|
|
5911cce057 | ||
|
|
2361f49c1d | ||
|
|
969995283a | ||
|
|
be531b5e9f | ||
|
|
836d0b1da3 | ||
|
|
8566f830d1 | ||
|
|
d42af52ba1 | ||
|
|
80d6a75377 | ||
|
|
c24cfe135a | ||
|
|
90ad29c78e | ||
|
|
93418cb7c9 | ||
|
|
c9e61d1e22 | ||
|
|
9e67c86bc5 | ||
|
|
4b1cb237bf | ||
|
|
a1e2c253bf | ||
|
|
38a47b4680 | ||
|
|
1c820c4041 | ||
|
|
2b328583ec | ||
|
|
a638a7d852 | ||
|
|
206d084bbd | ||
|
|
3a7ec59336 | ||
|
|
e0cb654746 | ||
|
|
da2ea7d114 | ||
|
|
a7f3476568 | ||
|
|
6ccbdf6ef8 | ||
|
|
55ce938d0b | ||
|
|
3f3f29b2ec | ||
|
|
d17a61e6d0 | ||
|
|
ac4b5e60f7 | ||
|
|
8774f63e50 | ||
|
|
785e15d9fd | ||
|
|
98ad40e750 | ||
|
|
4e37fb70f1 | ||
|
|
521240cfd6 | ||
|
|
e90869c781 | ||
|
|
4f494696d9 | ||
|
|
c3d2bd8293 | ||
|
|
24c2c94633 | ||
|
|
aae8e7b63e | ||
|
|
2e0d963bd2 | ||
|
|
3546287649 | ||
|
|
41618bbdaa | ||
|
|
b1005eac5d | ||
|
|
c0f879bd19 | ||
|
|
8eae18512c | ||
|
|
c076a37540 | ||
|
|
46f19d5d64 | ||
|
|
ecf79d530a | ||
|
|
8ac30ed393 | ||
|
|
023b07743a | ||
|
|
5c7741abe3 | ||
|
|
34106a8008 | ||
|
|
98d9179419 | ||
|
|
dce74eb958 | ||
|
|
86bbb41623 | ||
|
|
e8feb1c0dc | ||
|
|
4c08c28bd5 | ||
|
|
a6f228c263 | ||
|
|
1e5ec027bb | ||
|
|
b1ab4224ef | ||
|
|
430e6916bd | ||
|
|
a5851855d9 | ||
|
|
c015375ae9 | ||
|
|
0655672c32 | ||
|
|
e0d864a2d3 | ||
|
|
47e7250b4e | ||
|
|
5c3ce75c02 | ||
|
|
754c116ff1 | ||
|
|
de6d62021a | ||
|
|
a7e39ab276 | ||
|
|
61fecd8c93 | ||
|
|
3edc55800a | ||
|
|
54a3501ddf | ||
|
|
4a1f1a895b | ||
|
|
f82a4db826 | ||
|
|
a02a58ecc9 | ||
|
|
1d7010c381 | ||
|
|
7484962441 | ||
|
|
0470988c32 | ||
|
|
279ac22463 | ||
|
|
4a042392ac | ||
|
|
cbe29c66ee | ||
|
|
f82931d6c6 | ||
|
|
6d4506c5ae | ||
|
|
6aadc2f292 | ||
|
|
6ff3cc0ae7 | ||
|
|
f4e3db8a07 | ||
|
|
ff78a5abf1 | ||
|
|
4ef65219ad | ||
|
|
c56db0f429 | ||
|
|
8e08eb0127 | ||
|
|
f69c5c3703 | ||
|
|
9d5935fb53 | ||
|
|
af2350cd16 | ||
|
|
7617996dc4 | ||
|
|
460c0334c4 | ||
|
|
672ae55ea6 | ||
|
|
0614520e5a | ||
|
|
7abfe4508c | ||
|
|
9daaf523e3 | ||
|
|
30538cf464 | ||
|
|
c766fb0237 | ||
|
|
481f9500c6 | ||
|
|
01a1fdd04b | ||
|
|
9e04968b20 | ||
|
|
f8276ac834 | ||
|
|
ac7164fb0b | ||
|
|
75e71be14d | ||
|
|
5808368b9a | ||
|
|
f3a1b5de62 | ||
|
|
4bb1e5da43 | ||
|
|
e178bd43cf | ||
|
|
096501ee20 | ||
|
|
32e5d5bc1d | ||
|
|
9b67794752 | ||
|
|
c0dbc4b0e5 | ||
|
|
dad3cb084f | ||
|
|
187c6753bd | ||
|
|
478c90b7bb | ||
|
|
09a76935c0 | ||
|
|
03576d48e8 | ||
|
|
73b83771be | ||
|
|
ecd9373df9 | ||
|
|
bdff2bddcd | ||
|
|
179be1bb15 | ||
|
|
305973038d | ||
|
|
3396d6d848 | ||
|
|
d24cef5e17 | ||
|
|
b79b324027 | ||
|
|
8516a3febf | ||
|
|
0fde929da3 | ||
|
|
8fb42c333c | ||
|
|
9b9994b358 | ||
|
|
0f435d32af |
3872 changed files with 172262 additions and 11169 deletions
9
.gitignore
vendored
9
.gitignore
vendored
|
|
@ -52,3 +52,12 @@ linphone.spec
|
|||
# VSCode
|
||||
linphone60*.log
|
||||
linphone61*.log
|
||||
linphone62*.log
|
||||
|
||||
# QT Creator
|
||||
*.cflags
|
||||
*.config
|
||||
*.creator
|
||||
*.cxxflags
|
||||
*.files
|
||||
*.includes
|
||||
|
|
@ -1,171 +0,0 @@
|
|||
.factorize_ubuntu2004: &docker_image_platform_and_runner_tag
|
||||
tags: [ "docker" ]
|
||||
image: gitlab.linphone.org:4567/bc/public/linphone-desktop/bc-dev-ubuntu-20-04-lts:$UBUNTU_2004_IMAGE_VERSION
|
||||
|
||||
ubuntu2004-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
|
||||
#################################################
|
||||
|
||||
ubuntu2004-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
|
||||
|
||||
ubuntu2004-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=ON
|
||||
CMAKE_GENERATOR: Ninja
|
||||
CC: clang
|
||||
CXX: clang++
|
||||
extends: .linux-desktop
|
||||
allow_failure: true
|
||||
<<: *docker_image_platform_and_runner_tag
|
||||
|
||||
ubuntu2004-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: ubuntu2004-ninja-clang
|
||||
|
||||
ubuntu2004-makefile-gcc:
|
||||
rules:
|
||||
- !reference [.rules-merge-request-manual, rules]
|
||||
- if: $NIGHTLY_MASTER
|
||||
- if: $DEPLOY_PLUGINS
|
||||
variables:
|
||||
CMAKE_OPTIONS: -DLINPHONE_BUILDER_SIGNING_IDENTITY=$GPG_SIGNING_KEYID -DENABLE_G729=ON -DENABLE_PQCRYPTO=ON -DENABLE_GPL_THIRD_PARTIES=ON
|
||||
CMAKE_GENERATOR: Unix Makefiles
|
||||
CC: gcc
|
||||
CXX: g++
|
||||
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
|
||||
- eval "$(qtchooser -qt=$QT_LINUX_VER -print-env)"
|
||||
- export PATH=${QTTOOLDIR}:$PATH
|
||||
- export Qt6_DIR=${QTLIBDIR}/cmake/Qt6
|
||||
- echo "Using Qt $QT_LINUX_VER at ${QTLIBDIR}"
|
||||
- cd build
|
||||
- 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
|
||||
extends: .linux-desktop
|
||||
<<: *docker_image_platform_and_runner_tag
|
||||
|
||||
#################################################
|
||||
# Package - Nightly
|
||||
#################################################
|
||||
|
||||
ubuntu2004-makefile-gcc-package:
|
||||
stage: package
|
||||
tags: [ "docker" ]
|
||||
image: gitlab.linphone.org:4567/bc/public/linphone-desktop/bc-dev-ubuntu-20-04-lts:$UBUNTU_2004_IMAGE_VERSION
|
||||
dependencies: []
|
||||
rules:
|
||||
- !reference [.rules-merge-request-manual, rules]
|
||||
- if: $NIGHTLY_MASTER
|
||||
- if: $PACKAGE_LINUX
|
||||
- if: $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 -DENABLE_GPL_THIRD_PARTIES=ON
|
||||
CMAKE_GENERATOR: Unix Makefiles
|
||||
CC: gcc
|
||||
CXX: g++
|
||||
APPIMAGETOOL_SIGN_PASSPHRASE: $GPG_SIGNING_PASS
|
||||
extends: .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
|
||||
- eval "$(qtchooser -qt=$QT_LINUX_VER -print-env)"
|
||||
- export PATH=${QTTOOLDIR}:$PATH
|
||||
- cd build
|
||||
- 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
|
||||
artifacts:
|
||||
paths:
|
||||
- build/OUTPUT/*
|
||||
expire_in: 1 week
|
||||
|
||||
#################################################
|
||||
# Deploy - Nightly
|
||||
#################################################
|
||||
|
||||
ubuntu2004-makefile-gcc-deploy:
|
||||
stage: deploy
|
||||
tags: [ "deploy" ]
|
||||
needs:
|
||||
- ubuntu2004-makefile-gcc-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
|
||||
- |-
|
||||
if [[ $MAKE_RELEASE_FILE_URL != "" ]]; then
|
||||
rsync -rlv build/OUTPUT/Packages/RELEASE $DEPLOY_SERVER:$UPLOAD_ROOT_PATH/$LINUX_PLATFORM
|
||||
rsync -rlv build/OUTPUT/Packages/RELEASE $MAIN_DEPLOY_SERVER:$UPLOAD_ROOT_PATH/$LINUX_PLATFORM
|
||||
fi
|
||||
|
||||
|
||||
ubuntu2004-makefile-gcc-plugins-deploy:
|
||||
stage: deploy
|
||||
tags: [ "deploy" ]
|
||||
needs:
|
||||
- ubuntu2004-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/
|
||||
|
||||
111
.gitlab-ci-files/linux-desktop-ubuntu-2204.yml
Normal file
111
.gitlab-ci-files/linux-desktop-ubuntu-2204.yml
Normal 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/
|
||||
|
||||
|
|
@ -2,7 +2,7 @@
|
|||
# BUILD
|
||||
#################################################
|
||||
|
||||
.build_all_linux_script: &build_all_linux_script |
|
||||
.common_linux: &common_linux |
|
||||
cmake --version
|
||||
export CC=$CC
|
||||
export CXX=$CXX
|
||||
|
|
@ -11,20 +11,68 @@
|
|||
echo $CMAKE_GENERATOR
|
||||
echo $DEFAULT_LINUX_CMAKE_OPTIONS
|
||||
echo $CMAKE_SANITIZER_OPTIONS
|
||||
cd build
|
||||
eval "$(qtchooser -qt=$QT_LINUX_VER -print-env)"
|
||||
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
|
||||
|
|
@ -26,7 +26,7 @@
|
|||
|
||||
.macosx-desktop:
|
||||
stage: build
|
||||
tags: [ "macos-min-xcode12.2" ]
|
||||
tags: [ "macmini-m1-xcode15-flat" ]
|
||||
rules:
|
||||
- if: $CI_PIPELINE_SOURCE == "merge_request_event" && $DOCKER_UPDATE == null && $SKIP_MACOSX == null
|
||||
- if: $CI_PIPELINE_SOURCE == "schedule" && $DOCKER_UPDATE == null && $SKIP_MACOSX == null
|
||||
|
|
@ -93,8 +93,8 @@ macosx-ninja-novideo:
|
|||
# WAIT for QT6 for arm64
|
||||
macosx-ninja-package:
|
||||
stage: package
|
||||
tags: [ "macos-min-xcode12.2" ]
|
||||
dependencies: []
|
||||
tags: [ "macmini-m1-xcode15-flat" ]
|
||||
needs: []
|
||||
rules:
|
||||
- !reference [.rules-merge-request-manual, rules]
|
||||
- if: $CI_PIPELINE_SOURCE == "schedule" && $DOCKER_UPDATE == null && $SKIP_MACOSX == null
|
||||
|
|
@ -102,7 +102,7 @@ macosx-ninja-package:
|
|||
- if: $PACKAGE_MACOSX
|
||||
- if: $DEPLOY_MACOSX
|
||||
variables:
|
||||
CMAKE_OPTIONS: -DPython3_ROOT_DIR=/opt/bc/pip-packages/ -DENABLE_APP_PACKAGING=ON -DENABLE_GPL_THIRD_PARTIES=ON -DENABLE_G729=ON
|
||||
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:
|
||||
|
|
@ -117,7 +117,7 @@ macosx-ninja-package:
|
|||
|
||||
macosx-codesigning:
|
||||
stage: signing
|
||||
tags: [ "macos-min-xcode12.2" ]
|
||||
tags: [ "macmini-m1-xcode15-flat" ]
|
||||
needs:
|
||||
- macosx-ninja-package
|
||||
rules:
|
||||
|
|
@ -142,7 +142,7 @@ macosx-codesigning:
|
|||
|
||||
macosx-deploy:
|
||||
stage: deploy
|
||||
tags: [ "macos-min-xcode12.2" ]
|
||||
tags: [ "macmini-m1-xcode15-flat" ]
|
||||
needs:
|
||||
- macosx-codesigning
|
||||
only:
|
||||
|
|
@ -160,7 +160,7 @@ macosx-deploy:
|
|||
|
||||
macosx-makefile-plugins-deploy:
|
||||
stage: deploy
|
||||
tags: [ "macos-min-xcode12.2" ]
|
||||
tags: [ "macmini-m1-xcode15-flat" ]
|
||||
needs:
|
||||
- macosx-makefile
|
||||
only:
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@
|
|||
- 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=ON
|
||||
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
|
||||
|
|
@ -92,11 +92,11 @@
|
|||
|
||||
.windows-vs2022:
|
||||
extends: .windows-vs
|
||||
tags: [ "windows-powershell-vs-17-2022" ]
|
||||
tags: [ "windows-powershell-vs-17-2022-flat" ]
|
||||
|
||||
.windows-codesigning:
|
||||
extends: .prepare
|
||||
tags: [ "windows-powershell-vs-17-2022-apps" ]
|
||||
tags: [ "windows-powershell-vs-17-2022-apps-flat" ]
|
||||
|
||||
.windows-msbuild-variables:
|
||||
variables:
|
||||
|
|
@ -177,7 +177,7 @@ win64-ninja-vs2022-scheduled-windows:
|
|||
|
||||
.vs-win64-package:
|
||||
stage: package
|
||||
dependencies: []
|
||||
needs: []
|
||||
rules:
|
||||
- !reference [.rules-merge-request-manual, rules]
|
||||
- if: $NIGHTLY_MASTER
|
||||
|
|
@ -185,7 +185,7 @@ win64-ninja-vs2022-scheduled-windows:
|
|||
- if: $PACKAGE_WINDOWS
|
||||
- if: $DEPLOY_WINDOWS
|
||||
variables:
|
||||
CMAKE_OPTIONS: -DENABLE_APP_PACKAGING=YES -DENABLE_G729=ON -DENABLE_PQCRYPTO=ON -DENABLE_GPL_THIRD_PARTIES=ON
|
||||
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:
|
||||
|
|
@ -204,6 +204,7 @@ win64-ninja-vs2022-package-windows:
|
|||
|
||||
win64-codesigning:
|
||||
stage: signing
|
||||
allow_failure: true
|
||||
extends:
|
||||
- .windows-codesigning
|
||||
needs:
|
||||
|
|
@ -218,6 +219,7 @@ win64-codesigning:
|
|||
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\*
|
||||
|
|
@ -230,15 +232,15 @@ win64-codesigning:
|
|||
|
||||
.win64-upload:
|
||||
stage: deploy
|
||||
tags: [ "windows-powershell" ]
|
||||
tags: [ "windows-powershell-flat" ]
|
||||
rules:
|
||||
- if: $NIGHTLY_MASTER
|
||||
- if: $DEPLOY_WINDOWS
|
||||
|
||||
script:
|
||||
- scp -pr build-desktop/OUTPUT/Packages/*.exe ${DEPLOY_SERVER}:${UPLOAD_ROOT_PATH}/${WINDOWS_PLATFORM}/${APP_FOLDER}
|
||||
- if ($MAKE_RELEASE_FILE_URL) { scp -pr build-desktop/OUTPUT/Packages/RELEASE ${DEPLOY_SERVER}:${UPLOAD_ROOT_PATH}/${WINDOWS_PLATFORM}/ }
|
||||
- if ($MAKE_RELEASE_FILE_URL) { scp -pr build-desktop/OUTPUT/Packages/RELEASE ${MAIN_DEPLOY_SERVER}:${UPLOAD_ROOT_PATH}/${WINDOWS_PLATFORM}/ }
|
||||
- rsync --perms --chmod=Fu=rw,Fg=r,Fo=r build-desktop/OUTPUT/Packages/*.exe ${DEPLOY_SERVER}:${UPLOAD_ROOT_PATH}/${WINDOWS_PLATFORM}/${APP_FOLDER}
|
||||
- if ($MAKE_RELEASE_FILE_URL) { rsync --perms --chmod=Fu=rw,Fg=r,Fo=r build-desktop/OUTPUT/Packages/RELEASE ${DEPLOY_SERVER}:${UPLOAD_ROOT_PATH}/${WINDOWS_PLATFORM}/ }
|
||||
- if ($MAKE_RELEASE_FILE_URL) { rsync --perms --chmod=Fu=rw,Fg=r,Fo=r build-desktop/OUTPUT/Packages/RELEASE ${MAIN_DEPLOY_SERVER}:${UPLOAD_ROOT_PATH}/${WINDOWS_PLATFORM}/ }
|
||||
|
||||
win64-ninja-vs2022-upload:
|
||||
extends:
|
||||
|
|
@ -248,7 +250,7 @@ win64-ninja-vs2022-upload:
|
|||
|
||||
.win64-plugins-upload:
|
||||
stage: deploy
|
||||
tags: [ "windows" ]
|
||||
tags: [ "windows-flat" ]
|
||||
rules:
|
||||
- if: $DEPLOY_PLUGINS
|
||||
script:
|
||||
|
|
|
|||
|
|
@ -21,12 +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: 20230417_qtopen_gstreamer
|
||||
DEBIAN_10_IMAGE_VERSION: 20210217_python3
|
||||
UBUNTU_ROLLING_IMAGE_VERSION: 20211012_add_qtwebview
|
||||
UBUNTU_2004_IMAGE_VERSION: 20250226_qt6-8-0
|
||||
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:
|
||||
|
|
@ -50,7 +45,7 @@ include:
|
|||
- '.gitlab-ci-files/rules.yml'
|
||||
- '.gitlab-ci-files/linux-prepare.yml'
|
||||
- '.gitlab-ci-files/linux-desktop.yml'
|
||||
- '.gitlab-ci-files/linux-desktop-ubuntu-2004.yml'
|
||||
- '.gitlab-ci-files/linux-desktop-ubuntu-2204.yml'
|
||||
- '.gitlab-ci-files/windows-desktop.yml'
|
||||
- '.gitlab-ci-files/macosx-desktop.yml'
|
||||
|
||||
|
|
|
|||
2
.gitmodules
vendored
2
.gitmodules
vendored
|
|
@ -1,3 +1,3 @@
|
|||
[submodule "linphone-sdk"]
|
||||
path = external/linphone-sdk
|
||||
url = https://gitlab.linphone.org/BC/public/linphone-sdk.git
|
||||
url = https://gitlab.linphone.org/BC/public/linphone-sdk.git
|
||||
|
|
|
|||
39
CHANGELOG.md
Normal file
39
CHANGELOG.md
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
# Change Log
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
Group changes to describe their impact on the project, as follows:
|
||||
|
||||
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.
|
||||
|
||||
## [6.0.0] - 2025-04-17
|
||||
|
||||
6.0.0 release is a complete rework of Linphone Desktop, with only the call and contact list features availables
|
||||
|
||||
### Added
|
||||
- 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.
|
||||
|
||||
|
||||
## [6.1.0] - XXXX-XX-XX
|
||||
|
||||
### Changed
|
||||
- Minimum supported Qt version is now 6.10.0
|
||||
|
|
@ -60,7 +60,6 @@ project(linphoneqt)
|
|||
|
||||
include(GNUInstallDirs)
|
||||
include(CheckCXXCompilerFlag)
|
||||
include(Linphone/application_info.cmake)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
if(LINPHONEAPP_INSTALL_PREFIX)
|
||||
|
|
@ -72,8 +71,11 @@ endif()
|
|||
|
||||
set(CMAKE_INSTALL_PREFIX "${APPLICATION_OUTPUT_DIR}")
|
||||
|
||||
set(LINPHONEAPP_APPLICATION_NAME "Linphone6" CACHE STRING "Application name" )
|
||||
set(LINPHONEAPP_EXECUTABLE_NAME "linphone6" CACHE STRING "Executable name" )
|
||||
set(LINPHONEAPP_APPLICATION_NAME "Linphone" CACHE STRING "Application name" )
|
||||
set(LINPHONEAPP_EXECUTABLE_NAME "linphone" CACHE STRING "Executable name" )
|
||||
|
||||
# Include application_info.cmake here as the LINPHONEAPP_APPLICATION_NAME variable must be known as it is set to the APPLICATION_NAME variable.
|
||||
include(Linphone/application_info.cmake)
|
||||
|
||||
if( APPLE )
|
||||
set(LINPHONEAPP_MACOS_ARCHS "arm64" CACHE STRING "MacOS architectures to build: comma-separated list of values in [arm64, x86_64]")
|
||||
|
|
@ -163,7 +165,7 @@ add_option(OPTION_LIST ENABLE_FFMPEG "Build mediastreamer2 with ffmpeg video sup
|
|||
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." ON)
|
||||
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)
|
||||
|
|
@ -185,9 +187,9 @@ add_option(OPTION_LIST ENABLE_SCREENSHARING "Enable screen sharing." ${ENABLE_VI
|
|||
# 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 Qt5Keychain library name for a workaround with windeployqt" "EQt5Keychain")
|
||||
add_cache(OPTION_LIST QTKEYCHAIN_TARGET_NAME "Override Qt6Keychain library name for a workaround with windeployqt" "EQt6Keychain")
|
||||
else()
|
||||
add_cache(OPTION_LIST QTKEYCHAIN_TARGET_NAME "Override Qt5Keychain library name" "Qt5Keychain")
|
||||
add_cache(OPTION_LIST QTKEYCHAIN_TARGET_NAME "Override Qt6Keychain library name" "Qt6Keychain")
|
||||
endif()
|
||||
if(WIN32)
|
||||
add_option(OPTION_LIST ENABLE_OPENSSL_EXPORT "Enable OpenSSL deployment" YES)
|
||||
|
|
@ -206,7 +208,7 @@ set(ENABLE_CSHARP_WRAPPER OFF CACHE BOOL "Build the CSharp wrapper for Liblinpho
|
|||
set(ENABLE_THEORA OFF)
|
||||
set(ENABLE_QT_GL ${ENABLE_VIDEO})
|
||||
|
||||
find_package(Qt6 REQUIRED COMPONENTS Core)
|
||||
find_package(Qt6 REQUIRED COMPONENTS Core Quick Widgets)
|
||||
|
||||
if(NOT Qt6_FOUND)
|
||||
message(FATAL_ERROR "Minimum supported Qt6!")
|
||||
|
|
@ -227,18 +229,11 @@ 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()
|
||||
|
||||
if(ENABLE_QT_KEYCHAIN)
|
||||
function(add_linphone_keychain)
|
||||
#add_subdirectory("external/qtkeychain")
|
||||
endfunction()
|
||||
add_linphone_keychain()
|
||||
endif()
|
||||
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
|
||||
|
|
@ -270,4 +265,16 @@ 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/")
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
cmake_minimum_required(VERSION 3.16)
|
||||
|
||||
project(Linphone VERSION 6.0.0 LANGUAGES CXX)
|
||||
project(Linphone VERSION 6.2.0 LANGUAGES CXX)
|
||||
|
||||
################################################################
|
||||
# PACKAGES
|
||||
|
|
@ -19,14 +19,14 @@ endforeach()
|
|||
set(TARGET_NAME Linphone)
|
||||
set(APP_TARGETS ${LinphoneCxx_TARGET}
|
||||
${Mediastreamer2_TARGET}#MediastreamerUtils
|
||||
${LibLinphone_TARGET})#MediastreamerUtils
|
||||
${LibLinphone_TARGET})#Liblinphone
|
||||
|
||||
set(QT_DEFAULT_MAJOR_VERSION 6)
|
||||
set(QT_PACKAGES Core Quick Qml Widgets Svg Multimedia Test NetworkAuth Concurrent)# Search Core at first for initialize Qt scripts for next find_packages.
|
||||
set(QT_PACKAGES Quick Qml Widgets Svg Multimedia Test NetworkAuth Concurrent)# Search Core at first for initialize Qt scripts for next find_packages.
|
||||
if (UNIX AND NOT APPLE)
|
||||
list(APPEND QT_PACKAGES DBus)
|
||||
endif()
|
||||
find_package(Qt6 REQUIRED COMPONENTS Core)
|
||||
find_package(Qt6 REQUIRED COMPONENTS Core)
|
||||
find_package(Qt6 REQUIRED COMPONENTS ${QT_PACKAGES})
|
||||
find_package(Qt6 REQUIRED COMPONENTS LinguistTools)
|
||||
|
||||
|
|
@ -98,15 +98,11 @@ endif()
|
|||
|
||||
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/config.h.cmake" "${CMAKE_CURRENT_BINARY_DIR}/config.h")
|
||||
|
||||
if(${Qt6_VERSION} VERSION_LESS "6.3.0")
|
||||
set(CMAKE_AUTOMOC ON)
|
||||
set(CMAKE_AUTORCC ON)
|
||||
set(CMAKE_AUTOUIC ON)
|
||||
else()
|
||||
qt6_standard_project_setup()
|
||||
if(${Qt6_VERSION} VERSION_LESS "6.10.0")
|
||||
message( FATAL_ERROR "Linphone requires Qt 6.10.0 or newer. Exiting CMake." )
|
||||
endif()
|
||||
|
||||
|
||||
qt6_standard_project_setup()
|
||||
|
||||
|
||||
################################################################
|
||||
|
|
@ -127,7 +123,7 @@ add_subdirectory(core)
|
|||
|
||||
set(LANGUAGES_DIRECTORY "data/languages")
|
||||
set(I18N_FILENAME i18n.qrc)
|
||||
set(LANGUAGES en fr_FR de)
|
||||
set(LANGUAGES en fr de)
|
||||
|
||||
# Add languages support.
|
||||
add_subdirectory("${LANGUAGES_DIRECTORY}" "data/languages")
|
||||
|
|
@ -174,6 +170,12 @@ qt6_add_qml_module(Linphone
|
|||
RESOURCES data/fonts.qrc
|
||||
)
|
||||
|
||||
if (WIN32)
|
||||
if(MSVC AND (CMAKE_BUILD_TYPE STREQUAL "Debug" OR CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo"))
|
||||
install(FILES "$<TARGET_PDB_FILE:${TARGET_NAME}>" DESTINATION ${CMAKE_INSTALL_BINDIR})
|
||||
endif()
|
||||
endif()
|
||||
|
||||
qt6_add_resources(Linphone "resources" PREFIX "/" FILES ${_LINPHONEAPP_RC_FILES})
|
||||
set_property(TARGET Linphone PROPERTY POSITION_INDEPENDENT_CODE ON) #Need by Qt
|
||||
|
||||
|
|
@ -199,6 +201,7 @@ set_target_properties(${TARGET_NAME} PROPERTIES
|
|||
if(MSVC)
|
||||
set_target_properties(${TARGET_NAME} PROPERTIES PDB_NAME "${EXECUTABLE_NAME}_app")
|
||||
endif()
|
||||
set_target_properties(${TARGET_NAME} PROPERTIES WINDOWS_EXPORT_ALL_SYMBOLS ON)
|
||||
|
||||
if(WIN32)
|
||||
if(ENABLE_SCREENSHARING)
|
||||
|
|
@ -214,6 +217,7 @@ foreach(T ${APP_TARGETS})
|
|||
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()
|
||||
|
|
|
|||
|
|
@ -22,7 +22,6 @@
|
|||
|
||||
#include "App.hpp"
|
||||
|
||||
#include <QAction>
|
||||
#include <QCoreApplication>
|
||||
#include <QDirIterator>
|
||||
#include <QFileSelector>
|
||||
|
|
@ -53,9 +52,19 @@
|
|||
#include "core/call/CallList.hpp"
|
||||
#include "core/call/CallProxy.hpp"
|
||||
#include "core/camera/CameraGui.hpp"
|
||||
#include "core/chat/ChatProxy.hpp"
|
||||
#include "core/chat/files/ChatMessageFileProxy.hpp"
|
||||
#include "core/chat/message/ChatMessageGui.hpp"
|
||||
#include "core/chat/message/EventLogGui.hpp"
|
||||
#include "core/chat/message/EventLogList.hpp"
|
||||
#include "core/chat/message/EventLogProxy.hpp"
|
||||
#include "core/chat/message/content/ChatMessageContentGui.hpp"
|
||||
#include "core/chat/message/content/ChatMessageContentProxy.hpp"
|
||||
#include "core/chat/message/imdn/ImdnStatusProxy.hpp"
|
||||
#include "core/conference/ConferenceGui.hpp"
|
||||
#include "core/conference/ConferenceInfoGui.hpp"
|
||||
#include "core/conference/ConferenceInfoProxy.hpp"
|
||||
#include "core/emoji/EmojiProxy.hpp"
|
||||
#include "core/fps-counter/FPSCounter.hpp"
|
||||
#include "core/friend/FriendCore.hpp"
|
||||
#include "core/friend/FriendGui.hpp"
|
||||
|
|
@ -64,34 +73,50 @@
|
|||
#include "core/notifier/Notifier.hpp"
|
||||
#include "core/participant/ParticipantDeviceProxy.hpp"
|
||||
#include "core/participant/ParticipantGui.hpp"
|
||||
#include "core/participant/ParticipantInfoProxy.hpp"
|
||||
#include "core/participant/ParticipantProxy.hpp"
|
||||
#include "core/payload-type/PayloadTypeCore.hpp"
|
||||
#include "core/payload-type/PayloadTypeGui.hpp"
|
||||
#include "core/payload-type/PayloadTypeProxy.hpp"
|
||||
#include "core/phone-number/PhoneNumber.hpp"
|
||||
#include "core/phone-number/PhoneNumberProxy.hpp"
|
||||
#include "core/recorder/RecorderGui.hpp"
|
||||
#include "core/register/RegisterPage.hpp"
|
||||
#include "core/screen/ScreenList.hpp"
|
||||
#include "core/screen/ScreenProxy.hpp"
|
||||
#include "core/search/MagicSearchProxy.hpp"
|
||||
#include "core/setting/SettingsCore.hpp"
|
||||
#include "core/singleapplication/singleapplication.h"
|
||||
#include "core/sound-player/SoundPlayerGui.hpp"
|
||||
#include "core/timezone/TimeZoneProxy.hpp"
|
||||
#include "core/translator/DefaultTranslatorCore.hpp"
|
||||
#include "core/variant/VariantList.hpp"
|
||||
#include "core/videoSource/VideoSourceDescriptorGui.hpp"
|
||||
#include "model/friend/FriendsManager.hpp"
|
||||
#include "model/object/VariantObject.hpp"
|
||||
#include "model/tool/ToolModel.hpp"
|
||||
#include "tool/Constants.hpp"
|
||||
#include "tool/EnumsToString.hpp"
|
||||
#include "tool/Utils.hpp"
|
||||
#include "tool/accessibility/AccessibilityHelper.hpp"
|
||||
#include "tool/accessibility/FocusHelper.hpp"
|
||||
#include "tool/accessibility/KeyboardShortcuts.hpp"
|
||||
#include "tool/native/DesktopTools.hpp"
|
||||
#include "tool/providers/AvatarProvider.hpp"
|
||||
#include "tool/providers/EmojiProvider.hpp"
|
||||
#include "tool/providers/ImageProvider.hpp"
|
||||
#include "tool/providers/ScreenProvider.hpp"
|
||||
#include "tool/providers/ThumbnailProvider.hpp"
|
||||
#include "tool/request/CallbackHelper.hpp"
|
||||
#include "tool/request/RequestDialog.hpp"
|
||||
#include "tool/thread/Thread.hpp"
|
||||
#include "tool/ui/DashRectangle.hpp"
|
||||
|
||||
#if defined(Q_OS_MACOS)
|
||||
#include "core/event-count-notifier/EventCountNotifierMacOs.hpp"
|
||||
#else
|
||||
#include "core/event-count-notifier/EventCountNotifierSystemTrayIcon.hpp"
|
||||
#endif // if defined(Q_OS_MACOS)
|
||||
|
||||
DEFINE_ABSTRACT_OBJECT(App)
|
||||
|
||||
|
|
@ -242,9 +267,7 @@ void App::setAutoStart(bool enabled) {
|
|||
|
||||
void App::setAutoStart(bool enabled) {
|
||||
QSettings settings(AutoStartSettingsFilePath, QSettings::NativeFormat);
|
||||
QString parameters;
|
||||
if (!mSettings->getExitOnClose()) parameters = " --minimized";
|
||||
if (enabled) settings.setValue(EXECUTABLE_NAME, QDir::toNativeSeparators(applicationFilePath()) + parameters);
|
||||
if (enabled) settings.setValue(EXECUTABLE_NAME, QDir::toNativeSeparators(applicationFilePath()));
|
||||
else settings.remove(EXECUTABLE_NAME);
|
||||
|
||||
mAutoStart = enabled;
|
||||
|
|
@ -259,10 +282,14 @@ void App::setAutoStart(bool enabled) {
|
|||
// -----------------------------------------------------------------------------
|
||||
|
||||
App::App(int &argc, char *argv[])
|
||||
: SingleApplication(argc, argv, true, Mode::User | Mode::ExcludeAppPath | Mode::ExcludeAppVersion) {
|
||||
: SingleApplication(argc, argv, true, Mode::User | Mode::ExcludeAppPath | Mode::ExcludeAppVersion) {
|
||||
// Do not use APPLICATION_NAME here.
|
||||
// The EXECUTABLE_NAME will be used in qt standard paths. It's our goal.
|
||||
QDir::setCurrent(
|
||||
QCoreApplication::applicationDirPath()); // Set working directory as the executable to allow relative paths.
|
||||
QThread::currentThread()->setPriority(QThread::HighPriority);
|
||||
qDebug() << "app thread is" << QThread::currentThread();
|
||||
lDebug() << "Starting app with Qt version" << qVersion();
|
||||
QCoreApplication::setApplicationName(EXECUTABLE_NAME);
|
||||
QApplication::setOrganizationDomain(EXECUTABLE_NAME);
|
||||
QCoreApplication::setApplicationVersion(APPLICATION_SEMVER);
|
||||
|
|
@ -279,7 +306,7 @@ App::App(int &argc, char *argv[])
|
|||
.arg(applicationVersion())
|
||||
.arg(Utils::getOsProduct())
|
||||
.arg(qVersion());
|
||||
|
||||
lInfo() << "at " << QDir().absolutePath();
|
||||
mCurrentDate = QDate::currentDate();
|
||||
mAutoStart = autoStartEnabled();
|
||||
mDateUpdateTimer.setInterval(60000);
|
||||
|
|
@ -291,7 +318,12 @@ App::App(int &argc, char *argv[])
|
|||
emit currentDateChanged();
|
||||
}
|
||||
});
|
||||
mEventCountNotifier = new EventCountNotifier(this);
|
||||
mDateUpdateTimer.start();
|
||||
|
||||
#ifdef Q_OS_LINUX
|
||||
exportDesktopFile();
|
||||
#endif
|
||||
}
|
||||
|
||||
App::~App() {
|
||||
|
|
@ -305,7 +337,7 @@ void App::setSelf(QSharedPointer<App>(me)) {
|
|||
auto callCore = CallCore::create(call);
|
||||
mCoreModelConnection->invokeToCore([this, callCore] {
|
||||
auto callGui = new CallGui(callCore);
|
||||
auto win = getCallsWindow(QVariant::fromValue(callGui));
|
||||
auto win = getOrCreateCallsWindow(QVariant::fromValue(callGui));
|
||||
Utils::smartShowWindow(win);
|
||||
auto mainwin = getMainWindow();
|
||||
QMetaObject::invokeMethod(mainwin, "callCreated");
|
||||
|
|
@ -349,21 +381,106 @@ void App::setSelf(QSharedPointer<App>(me)) {
|
|||
}
|
||||
});
|
||||
mCoreModelConnection->makeConnectToModel(&CoreModel::authenticationRequested, &App::onAuthenticationRequested);
|
||||
// Config error message
|
||||
mCoreModelConnection->makeConnectToModel(
|
||||
&CoreModel::configuringStatus, [this](const std::shared_ptr<linphone::Core> &core,
|
||||
linphone::ConfiguringState status, const std::string &message) {
|
||||
mustBeInLinphoneThread(log().arg(Q_FUNC_INFO));
|
||||
if (status == linphone::ConfiguringState::Failed) {
|
||||
mCoreModelConnection->invokeToCore([this, message]() {
|
||||
mustBeInMainThread(log().arg(Q_FUNC_INFO));
|
||||
//: Error
|
||||
Utils::showInformationPopup(
|
||||
tr("info_popup_error_title"),
|
||||
tr("info_popup_configuration_failed_message").arg(Utils::coreStringToAppString(message)),
|
||||
false);
|
||||
});
|
||||
}
|
||||
});
|
||||
mCoreModelConnection->makeConnectToModel(
|
||||
&CoreModel::accountAdded,
|
||||
[this](const std::shared_ptr<linphone::Core> &core, const std::shared_ptr<linphone::Account> &account) {
|
||||
mustBeInLinphoneThread(log().arg(Q_FUNC_INFO));
|
||||
if (CoreModel::getInstance()->mConfigStatus == linphone::ConfiguringState::Successful) {
|
||||
bool accountConnected = account && account->getState() == linphone::RegistrationState::Ok;
|
||||
mCoreModelConnection->invokeToCore([this, accountConnected]() {
|
||||
mustBeInMainThread(log().arg(Q_FUNC_INFO));
|
||||
// There is an account added by a remote provisioning, force switching to main page
|
||||
// because the account may not be connected already
|
||||
// if (accountConnected)
|
||||
if (mPossiblyLookForAddedAccount) {
|
||||
QMetaObject::invokeMethod(mMainWindow, "openMainPage", Qt::DirectConnection,
|
||||
Q_ARG(QVariant, accountConnected));
|
||||
}
|
||||
mPossiblyLookForAddedAccount = false;
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Synchronize state for because linphoneCore was ran before any connections.
|
||||
mCoreModelConnection->invokeToModel([this]() {
|
||||
auto state = CoreModel::getInstance()->getCore()->getGlobalState();
|
||||
mCoreModelConnection->invokeToCore([this, state] { setCoreStarted(state == linphone::GlobalState::On); });
|
||||
});
|
||||
|
||||
mCoreModelConnection->makeConnectToModel(&CoreModel::unreadNotificationsChanged, [this] {
|
||||
int n = mEventCountNotifier->getCurrentEventCount();
|
||||
mCoreModelConnection->invokeToCore([this, n] { mEventCountNotifier->notifyEventCount(n); });
|
||||
});
|
||||
mCoreModelConnection->makeConnectToModel(&CoreModel::defaultAccountChanged, [this] {
|
||||
int n = mEventCountNotifier->getCurrentEventCount();
|
||||
mCoreModelConnection->invokeToCore([this, n] {
|
||||
mEventCountNotifier->notifyEventCount(n);
|
||||
emit defaultAccountChanged();
|
||||
});
|
||||
});
|
||||
|
||||
// Check update
|
||||
mCoreModelConnection->makeConnectToModel(
|
||||
&CoreModel::versionUpdateCheckResultReceived,
|
||||
[this](const std::shared_ptr<linphone::Core> &core, linphone::VersionUpdateCheckResult result,
|
||||
const std::string &version, const std::string &url, bool checkRequestedByUser) {
|
||||
mustBeInLinphoneThread(log().arg(Q_FUNC_INFO));
|
||||
mCoreModelConnection->invokeToCore([this, result, version, url, checkRequestedByUser] {
|
||||
switch (result) {
|
||||
case linphone::VersionUpdateCheckResult::Error:
|
||||
Utils::showInformationPopup(tr("info_popup_error_title"),
|
||||
//: An error occured while trying to check update. Please
|
||||
//: try again later or contact support team.
|
||||
tr("info_popup_error_checking_update"), false);
|
||||
break;
|
||||
case linphone::VersionUpdateCheckResult::NewVersionAvailable: {
|
||||
QString downloadLink =
|
||||
QStringLiteral("<a href='%1'><font color='DefaultStyle.main2_600'>%2</a>")
|
||||
.arg(url)
|
||||
//: Download it !
|
||||
.arg(tr("info_popup_new_version_download_label"));
|
||||
Utils::showInformationPopup(
|
||||
//: New version available !
|
||||
tr("info_popup_new_version_available_title"),
|
||||
//: A new version of Linphone (%1) is available. %2
|
||||
tr("info_popup_new_version_available_message").arg(version).arg(downloadLink));
|
||||
break;
|
||||
}
|
||||
case linphone::VersionUpdateCheckResult::UpToDate:
|
||||
if (checkRequestedByUser)
|
||||
//: Up to date
|
||||
Utils::showInformationPopup(tr("info_popup_version_up_to_date_title"),
|
||||
//: Your version is up to date
|
||||
tr("info_popup_version_up_to_date_message"));
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
mCliModelConnection = SafeConnection<App, CliModel>::create(me, CliModel::getInstance());
|
||||
mCliModelConnection->makeConnectToCore(&App::receivedMessage, [this](int, const QByteArray &byteArray) {
|
||||
QString command(byteArray);
|
||||
if (command.isEmpty()) {
|
||||
lDebug() << log().arg("Check with CliModel for commands");
|
||||
lInfo() << log().arg("Check with CliModel for commands");
|
||||
mCliModelConnection->invokeToModel([]() { CliModel::getInstance()->runProcess(); });
|
||||
} else {
|
||||
qInfo() << QStringLiteral("Received command from other application: `%1`.").arg(command);
|
||||
lInfo() << log().arg("Received command from other application: `%1`.").arg(command);
|
||||
mCliModelConnection->invokeToModel([command]() { CliModel::getInstance()->executeCommand(command); });
|
||||
}
|
||||
});
|
||||
|
|
@ -376,13 +493,22 @@ App *App::getInstance() {
|
|||
return dynamic_cast<App *>(QApplication::instance());
|
||||
}
|
||||
|
||||
QThread *App::getLinphoneThread() {
|
||||
Thread *App::getLinphoneThread() {
|
||||
return App::getInstance()->mLinphoneThread;
|
||||
}
|
||||
|
||||
Notifier *App::getNotifier() const {
|
||||
return mNotifier;
|
||||
}
|
||||
|
||||
EventCountNotifier *App::getEventCountNotifier() {
|
||||
return mEventCountNotifier;
|
||||
}
|
||||
|
||||
int App::getEventCount() const {
|
||||
return mEventCountNotifier ? mEventCountNotifier->getEventCount() : 0;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------
|
||||
// Initializations
|
||||
//-----------------------------------------------------------
|
||||
|
|
@ -422,10 +548,12 @@ void App::init() {
|
|||
}
|
||||
|
||||
void App::initCore() {
|
||||
mustBeInMainThread(log().arg(Q_FUNC_INFO));
|
||||
// Core. Manage the logger so it must be instantiate at first.
|
||||
CoreModel::create("", mLinphoneThread);
|
||||
if (mParser->isSet("verbose")) QtLogger::enableVerbose(true);
|
||||
if (mParser->isSet("qt-logs-only")) QtLogger::enableQtOnly(true);
|
||||
qDebug() << "linphone thread is" << mLinphoneThread;
|
||||
QMetaObject::invokeMethod(
|
||||
mLinphoneThread->getThreadId(),
|
||||
[this, settings = mSettings]() mutable {
|
||||
|
|
@ -483,10 +611,12 @@ void App::initCore() {
|
|||
|
||||
initCppInterfaces();
|
||||
mEngine->addImageProvider(ImageProvider::ProviderId, new ImageProvider());
|
||||
mEngine->addImageProvider(EmojiProvider::ProviderId, new EmojiProvider());
|
||||
mEngine->addImageProvider(AvatarProvider::ProviderId, new AvatarProvider());
|
||||
mEngine->addImageProvider(ScreenProvider::ProviderId, new ScreenProvider());
|
||||
mEngine->addImageProvider(WindowProvider::ProviderId, new WindowProvider());
|
||||
mEngine->addImageProvider(WindowIconProvider::ProviderId, new WindowIconProvider());
|
||||
mEngine->addImageProvider(ThumbnailProvider::ProviderId, new ThumbnailProvider());
|
||||
|
||||
// Enable notifications.
|
||||
mNotifier = new Notifier(mEngine);
|
||||
|
|
@ -497,6 +627,14 @@ void App::initCore() {
|
|||
mAccountList->setInitialized(false);
|
||||
mAccountList->lUpdate(true);
|
||||
}
|
||||
// Update global unread Notifications when an account updates his unread Notifications
|
||||
connect(mAccountList.get(), &AccountList::unreadNotificationsChanged, this, [this]() {
|
||||
lDebug() << "unreadNotificationsChanged of AccountList";
|
||||
mCoreModelConnection->invokeToModel([this] {
|
||||
int n = mEventCountNotifier->getCurrentEventCount();
|
||||
mCoreModelConnection->invokeToCore([this, n] { mEventCountNotifier->notifyEventCount(n); });
|
||||
});
|
||||
});
|
||||
if (!mCallList) setCallList(CallList::create());
|
||||
else mCallList->lUpdate();
|
||||
if (!mSettings) {
|
||||
|
|
@ -522,7 +660,7 @@ void App::initCore() {
|
|||
setLocale(settings->getConfigLocale());
|
||||
setAutoStart(settings->getAutoStart());
|
||||
setQuitOnLastWindowClosed(settings->getExitOnClose());
|
||||
}
|
||||
}
|
||||
const QUrl url("qrc:/qt/qml/Linphone/view/Page/Window/Main/MainWindow.qml");
|
||||
QObject::connect(
|
||||
mEngine, &QQmlApplicationEngine::objectCreated, this,
|
||||
|
|
@ -534,18 +672,45 @@ void App::initCore() {
|
|||
}
|
||||
auto window = qobject_cast<QQuickWindow *>(obj);
|
||||
setMainWindow(window);
|
||||
#ifndef __APPLE__
|
||||
#if defined(__APPLE__)
|
||||
setMacOSDockActions();
|
||||
#else
|
||||
// Enable TrayIconSystem.
|
||||
if (!QSystemTrayIcon::isSystemTrayAvailable())
|
||||
qWarning("System tray not found on this system.");
|
||||
else setSysTrayIcon();
|
||||
#endif // ifndef __APPLE__
|
||||
#endif // if defined(__APPLE__)
|
||||
|
||||
static bool firstOpen = true;
|
||||
if (!firstOpen || !mParser->isSet("minimized")) {
|
||||
lDebug() << log().arg("Openning window");
|
||||
window->show();
|
||||
if (window) window->show();
|
||||
} else lInfo() << log().arg("Stay minimized");
|
||||
firstOpen = false;
|
||||
lInfo() << log().arg("Checking remote provisioning");
|
||||
if (mIsRestarting) {
|
||||
if (CoreModel::getInstance()->mConfigStatus == linphone::ConfiguringState::Failed) {
|
||||
QMetaObject::invokeMethod(thread(), [this]() {
|
||||
auto message = CoreModel::getInstance()->mConfigMessage;
|
||||
//: not reachable
|
||||
if (message.isEmpty()) message = tr("configuration_error_detail");
|
||||
mustBeInMainThread(log().arg(Q_FUNC_INFO));
|
||||
//: Error
|
||||
Utils::showInformationPopup(
|
||||
tr("info_popup_error_title"),
|
||||
//: Remote provisioning failed : %1
|
||||
tr("info_popup_configuration_failed_message").arg(message), false);
|
||||
});
|
||||
} else {
|
||||
mPossiblyLookForAddedAccount = true;
|
||||
}
|
||||
}
|
||||
checkForUpdate();
|
||||
mIsRestarting = false;
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
lDebug() << log().arg("Creating KeyboardShortcuts");
|
||||
KeyboardShortcuts::create(getMainWindow());
|
||||
}
|
||||
},
|
||||
Qt::QueuedConnection);
|
||||
|
|
@ -557,7 +722,6 @@ void App::initCore() {
|
|||
}
|
||||
|
||||
static inline bool installLocale(App &app, QTranslator &translator, const QLocale &locale) {
|
||||
auto appPath = QStandardPaths::ApplicationsLocation;
|
||||
bool ok = translator.load(locale.name(), Constants::LanguagePath);
|
||||
ok = ok && app.installTranslator(&translator);
|
||||
if (ok) QLocale::setDefault(locale);
|
||||
|
|
@ -565,7 +729,6 @@ static inline bool installLocale(App &app, QTranslator &translator, const QLocal
|
|||
}
|
||||
|
||||
void App::initLocale() {
|
||||
|
||||
// Try to use preferred locale.
|
||||
QString locale;
|
||||
|
||||
|
|
@ -573,28 +736,28 @@ void App::initLocale() {
|
|||
mLocale = QLocale(QLocale::English);
|
||||
if (!installLocale(*this, *mDefaultTranslatorCore, mLocale)) qFatal("Unable to install default translator.");
|
||||
|
||||
// if (installLocale(*this, *mTranslatorCore, getLocale())) {
|
||||
// qDebug() << "installed locale" << getLocale().name();
|
||||
// return;
|
||||
// }
|
||||
// if (installLocale(*this, *mTranslatorCore, getLocale())) {
|
||||
// qDebug() << "installed locale" << getLocale().name();
|
||||
// return;
|
||||
// }
|
||||
|
||||
// Try to use system locale.
|
||||
// #ifdef Q_OS_MACOS
|
||||
// Use this workaround if there is still an issue about detecting wrong language from system on Mac. Qt doesn't use
|
||||
// the current system language on QLocale::system(). So we need to get it from user settings and overwrite its
|
||||
// Locale.
|
||||
// QSettings settings;
|
||||
// QString preferredLanguage = settings.value("AppleLanguages").toStringList().first();
|
||||
// QStringList qtLocale = QLocale::system().name().split('_');
|
||||
// if(qtLocale[0] != preferredLanguage){
|
||||
// qInfo() << "Override Qt language from " << qtLocale[0] << " to the preferred language : " <<
|
||||
// preferredLanguage; qtLocale[0] = preferredLanguage;
|
||||
// }
|
||||
// QLocale sysLocale = QLocale(qtLocale.join('_'));
|
||||
// #else
|
||||
QLocale sysLocale(QLocale::system().name()); // Use Locale from name because Qt has a bug where it didn't use the
|
||||
// QLocale::language (aka : translator.language != locale.language) on
|
||||
// Mac. #endif
|
||||
// Try to use system locale.
|
||||
// #ifdef Q_OS_MACOS
|
||||
// Use this workaround if there is still an issue about detecting wrong language from system on Mac. Qt doesn't
|
||||
// use the current system language on QLocale::system(). So we need to get it from user settings and overwrite
|
||||
// its Locale.
|
||||
// QSettings settings;
|
||||
// QString preferredLanguage = settings.value("AppleLanguages").toStringList().first();
|
||||
// QStringList qtLocale = QLocale::system().name().split('_');
|
||||
// if(qtLocale[0] != preferredLanguage){
|
||||
// qInfo() << "Override Qt language from " << qtLocale[0] << " to the preferred language : " <<
|
||||
// preferredLanguage; qtLocale[0] = preferredLanguage;
|
||||
// }
|
||||
// QLocale sysLocale = QLocale(qtLocale.join('_'));
|
||||
// #else
|
||||
QLocale sysLocale(QLocale::system().name()); // Use Locale from name because Qt has a bug where it didn't use
|
||||
// the QLocale::language (aka : translator.language !=
|
||||
// locale.language) on Mac. #endif
|
||||
if (installLocale(*this, *mTranslatorCore, sysLocale)) {
|
||||
qDebug() << "installed sys locale" << sysLocale.name();
|
||||
setLocale(sysLocale.name());
|
||||
|
|
@ -632,10 +795,18 @@ void App::initCppInterfaces() {
|
|||
"SettingsCpp", 1, 0, "SettingsCpp",
|
||||
[this](QQmlEngine *engine, QJSEngine *) -> QObject * { return mSettings.get(); });
|
||||
|
||||
qmlRegisterSingletonType<AccessibilityHelper>(
|
||||
"AccessibilityHelperCpp", 1, 0, "AccessibilityHelperCpp",
|
||||
[](QQmlEngine *engine, QJSEngine *) -> QObject * { return new AccessibilityHelper(engine); });
|
||||
|
||||
qmlRegisterType<FocusHelper>("CustomControls", 1, 0, "FocusHelper");
|
||||
|
||||
qmlRegisterType<DashRectangle>(Constants::MainQmlUri, 1, 0, "DashRectangle");
|
||||
qmlRegisterType<PhoneNumberProxy>(Constants::MainQmlUri, 1, 0, "PhoneNumberProxy");
|
||||
qmlRegisterType<VariantObject>(Constants::MainQmlUri, 1, 0, "VariantObject");
|
||||
qmlRegisterType<VariantList>(Constants::MainQmlUri, 1, 0, "VariantList");
|
||||
|
||||
qmlRegisterType<ParticipantInfoProxy>(Constants::MainQmlUri, 1, 0, "ParticipantInfoProxy");
|
||||
qmlRegisterType<ParticipantProxy>(Constants::MainQmlUri, 1, 0, "ParticipantProxy");
|
||||
qmlRegisterType<ParticipantGui>(Constants::MainQmlUri, 1, 0, "ParticipantGui");
|
||||
qmlRegisterType<ConferenceInfoProxy>(Constants::MainQmlUri, 1, 0, "ConferenceInfoProxy");
|
||||
|
|
@ -652,6 +823,16 @@ void App::initCppInterfaces() {
|
|||
qmlRegisterType<CallHistoryProxy>(Constants::MainQmlUri, 1, 0, "CallHistoryProxy");
|
||||
qmlRegisterType<CallGui>(Constants::MainQmlUri, 1, 0, "CallGui");
|
||||
qmlRegisterType<CallProxy>(Constants::MainQmlUri, 1, 0, "CallProxy");
|
||||
qmlRegisterType<ChatList>(Constants::MainQmlUri, 1, 0, "ChatList");
|
||||
qmlRegisterType<ChatProxy>(Constants::MainQmlUri, 1, 0, "ChatProxy");
|
||||
qmlRegisterType<ChatGui>(Constants::MainQmlUri, 1, 0, "ChatGui");
|
||||
qmlRegisterType<EventLogGui>(Constants::MainQmlUri, 1, 0, "EventLogGui");
|
||||
qmlRegisterType<ChatMessageGui>(Constants::MainQmlUri, 1, 0, "ChatMessageGui");
|
||||
qmlRegisterType<EventLogList>(Constants::MainQmlUri, 1, 0, "EventLogList");
|
||||
qmlRegisterType<EventLogProxy>(Constants::MainQmlUri, 1, 0, "EventLogProxy");
|
||||
qmlRegisterType<ChatMessageContentProxy>(Constants::MainQmlUri, 1, 0, "ChatMessageContentProxy");
|
||||
qmlRegisterType<ChatMessageFileProxy>(Constants::MainQmlUri, 1, 0, "ChatMessageFileProxy");
|
||||
qmlRegisterType<ChatMessageContentGui>(Constants::MainQmlUri, 1, 0, "ChatMessageContentGui");
|
||||
qmlRegisterUncreatableType<ConferenceCore>(Constants::MainQmlUri, 1, 0, "ConferenceCore",
|
||||
QLatin1String("Uncreatable"));
|
||||
qmlRegisterType<ConferenceGui>(Constants::MainQmlUri, 1, 0, "ConferenceGui");
|
||||
|
|
@ -661,6 +842,11 @@ void App::initCppInterfaces() {
|
|||
qmlRegisterType<MagicSearchList>(Constants::MainQmlUri, 1, 0, "MagicSearchList");
|
||||
qmlRegisterType<CameraGui>(Constants::MainQmlUri, 1, 0, "CameraGui");
|
||||
qmlRegisterType<FPSCounter>(Constants::MainQmlUri, 1, 0, "FPSCounter");
|
||||
qmlRegisterType<EmojiModel>(Constants::MainQmlUri, 1, 0, "EmojiModel");
|
||||
qmlRegisterType<EmojiProxy>(Constants::MainQmlUri, 1, 0, "EmojiProxy");
|
||||
qmlRegisterType<ImdnStatusProxy>(Constants::MainQmlUri, 1, 0, "ImdnStatusProxy");
|
||||
qmlRegisterType<SoundPlayerGui>(Constants::MainQmlUri, 1, 0, "SoundPlayerGui");
|
||||
qmlRegisterType<RecorderGui>(Constants::MainQmlUri, 1, 0, "RecorderGui");
|
||||
|
||||
qmlRegisterType<TimeZoneProxy>(Constants::MainQmlUri, 1, 0, "TimeZoneProxy");
|
||||
|
||||
|
|
@ -701,6 +887,14 @@ void App::initFonts() {
|
|||
allFamilies << QFontDatabase::applicationFontFamilies(id);
|
||||
}
|
||||
}
|
||||
QDirIterator itEmojis(":/emoji/font/", QDirIterator::Subdirectories);
|
||||
while (itEmojis.hasNext()) {
|
||||
QString ttf = itEmojis.next();
|
||||
if (itEmojis.fileInfo().isFile()) {
|
||||
auto id = QFontDatabase::addApplicationFont(ttf);
|
||||
allFamilies << QFontDatabase::applicationFontFamilies(id);
|
||||
}
|
||||
}
|
||||
#ifdef Q_OS_LINUX
|
||||
QDirIterator itFonts(":/linux/font/", QDirIterator::Subdirectories);
|
||||
while (itFonts.hasNext()) {
|
||||
|
|
@ -748,8 +942,10 @@ void App::clean() {
|
|||
}
|
||||
void App::restart() {
|
||||
mCoreModelConnection->invokeToModel([this]() {
|
||||
FriendsManager::getInstance()->clearMaps();
|
||||
CoreModel::getInstance()->getCore()->stop();
|
||||
mCoreModelConnection->invokeToCore([this]() {
|
||||
mIsRestarting = true;
|
||||
closeCallsWindow();
|
||||
setMainWindow(nullptr);
|
||||
mEngine->clearComponentCache();
|
||||
|
|
@ -775,34 +971,37 @@ void App::createCommandParser() {
|
|||
//: "A free and open source SIP video-phone."
|
||||
mParser->setApplicationDescription(tr("application_description"));
|
||||
//: "Send an order to the application towards a command line"
|
||||
mParser->addPositionalArgument("command", tr("command_line_arg_order").replace("%1", APPLICATION_NAME), "[command]");
|
||||
mParser->addPositionalArgument("command", tr("command_line_arg_order").replace("%1", APPLICATION_NAME),
|
||||
"[command]");
|
||||
mParser->addOptions({
|
||||
//: "Show this help"
|
||||
{{"h", "help"}, tr("command_line_option_show_help")},
|
||||
//: "Show this help"
|
||||
{{"h", "help"}, tr("command_line_option_show_help")},
|
||||
|
||||
//{"cli-help", tr("commandLineOptionCliHelp").replace("%1", APPLICATION_NAME)},
|
||||
|
||||
//:"Show app version"
|
||||
{{"v", "version"}, tr("command_line_option_show_app_version")},
|
||||
//:"Show app version"
|
||||
{{"v", "version"}, tr("command_line_option_show_app_version")},
|
||||
|
||||
//{"config", tr("command_line_option_config").replace("%1", EXECUTABLE_NAME), tr("command_line_option_config_arg")},
|
||||
//{"config", tr("command_line_option_config").replace("%1", EXECUTABLE_NAME),
|
||||
// tr("command_line_option_config_arg")},
|
||||
|
||||
{"fetch-config",
|
||||
//: "Specify the linphone configuration file to be fetched. It will be merged with the current configuration."
|
||||
tr("command_line_option_config_to_fetch")
|
||||
.replace("%1", EXECUTABLE_NAME),
|
||||
//: "URL, path or file"
|
||||
tr("command_line_option_config_to_fetch_arg")},
|
||||
//: "Specify the linphone configuration file to be fetched. It will be merged with the current
|
||||
//: configuration."
|
||||
tr("command_line_option_config_to_fetch").replace("%1", EXECUTABLE_NAME),
|
||||
//: "URL, path or file"
|
||||
tr("command_line_option_config_to_fetch_arg")},
|
||||
|
||||
//{{"c", "call"}, tr("command_line_option_call").replace("%1", EXECUTABLE_NAME), tr("command_line_option_call_arg")},
|
||||
//{{"c", "call"}, tr("command_line_option_call").replace("%1", EXECUTABLE_NAME),
|
||||
// tr("command_line_option_call_arg")},
|
||||
|
||||
{"minimized", tr("command_line_option_minimized")},
|
||||
{"minimized", tr("command_line_option_minimized")},
|
||||
|
||||
//: "Log to stdout some debug information while running"
|
||||
{{"V", "verbose"}, tr("command_line_option_log_to_stdout")},
|
||||
//: "Log to stdout some debug information while running"
|
||||
{{"V", "verbose"}, tr("command_line_option_log_to_stdout")},
|
||||
|
||||
//: "Print only logs from the application"
|
||||
{"qt-logs-only", tr("command_line_option_print_app_logs_only")},
|
||||
//: "Print only logs from the application"
|
||||
{"qt-logs-only", tr("command_line_option_print_app_logs_only")},
|
||||
});
|
||||
}
|
||||
// Should be call only at first start
|
||||
|
|
@ -855,9 +1054,9 @@ bool App::notify(QObject *receiver, QEvent *event) {
|
|||
try {
|
||||
done = QApplication::notify(receiver, event);
|
||||
} catch (const std::exception &ex) {
|
||||
lCritical() << log().arg("Exception has been catch in notify: %1").arg(ex.what());
|
||||
lFatal() << log().arg("Exception has been catch in notify: %1").arg(ex.what());
|
||||
} catch (...) {
|
||||
lCritical() << log().arg("Generic exeption has been catch in notify");
|
||||
lFatal() << log().arg("Generic exeption has been catch in notify");
|
||||
}
|
||||
if (event->type() == QEvent::MouseButtonPress) {
|
||||
auto window = findParentWindow(receiver);
|
||||
|
|
@ -871,7 +1070,38 @@ bool App::notify(QObject *receiver, QEvent *event) {
|
|||
return done;
|
||||
}
|
||||
|
||||
QQuickWindow *App::getCallsWindow(QVariant callGui) {
|
||||
void App::handleAppActivity() {
|
||||
auto handle = [this](QSharedPointer<AccountCore> accountCore) {
|
||||
if (!accountCore) return;
|
||||
auto accountPresence = accountCore->getPresence();
|
||||
if ((mMainWindow && mMainWindow->isActive() || (mCallsWindow && mCallsWindow->isActive())) &&
|
||||
accountPresence == LinphoneEnums::Presence::Away)
|
||||
accountCore->lSetPresence(LinphoneEnums::Presence::Online);
|
||||
if (((!mMainWindow || !mMainWindow->isActive()) && (!mCallsWindow || !mCallsWindow->isActive())) &&
|
||||
accountPresence == LinphoneEnums::Presence::Online)
|
||||
accountCore->lSetPresence(LinphoneEnums::Presence::Away);
|
||||
};
|
||||
if (mAccountList) {
|
||||
for (auto &account : mAccountList->getSharedList<AccountCore>())
|
||||
handle(account);
|
||||
} else {
|
||||
connect(
|
||||
this, &App::accountsChanged, this,
|
||||
[this, &handle] {
|
||||
if (mAccountList) {
|
||||
for (auto &account : mAccountList->getSharedList<AccountCore>())
|
||||
handle(account);
|
||||
}
|
||||
},
|
||||
Qt::SingleShotConnection);
|
||||
}
|
||||
}
|
||||
|
||||
QQuickWindow *App::getCallsWindow() {
|
||||
return mCallsWindow;
|
||||
}
|
||||
|
||||
QQuickWindow *App::getOrCreateCallsWindow(QVariant callGui) {
|
||||
mustBeInMainThread(getClassName());
|
||||
if (!mCallsWindow) {
|
||||
const QUrl callUrl("qrc:/qt/qml/Linphone/view/Page/Window/Call/CallsWindow.qml");
|
||||
|
|
@ -906,6 +1136,7 @@ QQuickWindow *App::getCallsWindow(QVariant callGui) {
|
|||
}
|
||||
// window->setParent(mMainWindow);
|
||||
mCallsWindow = window;
|
||||
connect(mCallsWindow, &QQuickWindow::activeChanged, this, &App::handleAppActivity);
|
||||
}
|
||||
if (!callGui.isNull() && callGui.isValid()) mCallsWindow->setProperty("call", callGui);
|
||||
return mCallsWindow;
|
||||
|
|
@ -916,7 +1147,7 @@ void App::setCallsWindowProperty(const char *id, QVariant property) {
|
|||
}
|
||||
|
||||
void App::closeCallsWindow() {
|
||||
if (mCallsWindow) {
|
||||
if (mCallsWindow && mCallList && !mCallList->getHaveCall()) {
|
||||
mCallsWindow->close();
|
||||
mCallsWindow->deleteLater();
|
||||
mCallsWindow = nullptr;
|
||||
|
|
@ -928,8 +1159,11 @@ QQuickWindow *App::getMainWindow() const {
|
|||
}
|
||||
|
||||
void App::setMainWindow(QQuickWindow *data) {
|
||||
if (mMainWindow) disconnect(mMainWindow, &QQuickWindow::activeChanged, this, nullptr);
|
||||
if (mMainWindow != data) {
|
||||
mMainWindow = data;
|
||||
connect(mMainWindow, &QQuickWindow::activeChanged, this, &App::handleAppActivity);
|
||||
handleAppActivity();
|
||||
emit mainWindowChanged();
|
||||
}
|
||||
}
|
||||
|
|
@ -1137,6 +1371,16 @@ bool App::event(QEvent *event) {
|
|||
} else if (event->type() == QEvent::ApplicationStateChange) {
|
||||
auto state = static_cast<QApplicationStateChangeEvent *>(event);
|
||||
if (state->applicationState() == Qt::ApplicationActive) Utils::smartShowWindow(getLastActiveWindow());
|
||||
} else if (event->type() == QEvent::ApplicationActivate) {
|
||||
for (int i = 0; i < getAccountList()->rowCount(); ++i) {
|
||||
auto accountCore = getAccountList()->getAt<AccountCore>(i);
|
||||
emit accountCore->lSetPresence(LinphoneEnums::Presence::Online, false, false);
|
||||
}
|
||||
} else if (event->type() == QEvent::ApplicationDeactivate) {
|
||||
for (int i = 0; i < getAccountList()->rowCount(); ++i) {
|
||||
auto accountCore = getAccountList()->getAt<AccountCore>(i);
|
||||
emit accountCore->lSetPresence(LinphoneEnums::Presence::Away, false, false);
|
||||
}
|
||||
}
|
||||
|
||||
return SingleApplication::event(event);
|
||||
|
|
@ -1149,6 +1393,7 @@ bool App::event(QEvent *event) {
|
|||
//-----------------------------------------------------------
|
||||
|
||||
void App::setSysTrayIcon() {
|
||||
qDebug() << "setSysTrayIcon";
|
||||
QQuickWindow *root = getMainWindow();
|
||||
QSystemTrayIcon *systemTrayIcon =
|
||||
(mSystemTrayIcon ? mSystemTrayIcon
|
||||
|
|
@ -1164,7 +1409,7 @@ void App::setSysTrayIcon() {
|
|||
//: "Afficher"
|
||||
restoreAction->setText(visible ? tr("hide_action") : tr("show_action"));
|
||||
};
|
||||
setRestoreActionText(root->isVisible());
|
||||
setRestoreActionText(root ? root->isVisible() : false);
|
||||
connect(root, &QWindow::visibleChanged, restoreAction, setRestoreActionText);
|
||||
|
||||
root->connect(restoreAction, &QAction::triggered, this, [this, restoreAction](bool checked) {
|
||||
|
|
@ -1180,6 +1425,9 @@ void App::setSysTrayIcon() {
|
|||
QAction *quitAction = new QAction(tr("quit_action"), root);
|
||||
root->connect(quitAction, &QAction::triggered, this, &App::quit);
|
||||
|
||||
//: "Mark all as read"
|
||||
QAction *markAllReadAction = createMarkAsReadAction(root);
|
||||
|
||||
// trayIcon: Left click actions.
|
||||
QMenu *menu = mSystemTrayIcon ? mSystemTrayIcon->contextMenu() : new QMenu();
|
||||
menu->clear();
|
||||
|
|
@ -1189,6 +1437,13 @@ void App::setSysTrayIcon() {
|
|||
menu->addAction(restoreAction);
|
||||
menu->addSeparator();
|
||||
}
|
||||
menu->addAction(markAllReadAction);
|
||||
//: Check for update
|
||||
if (mSettings->isCheckForUpdateAvailable()) {
|
||||
QAction *checkForUpdateAction = new QAction(tr("check_for_update"), root);
|
||||
root->connect(checkForUpdateAction, &QAction::triggered, this, [this] { checkForUpdate(true); });
|
||||
menu->addAction(checkForUpdateAction);
|
||||
}
|
||||
menu->addAction(quitAction);
|
||||
if (!mSystemTrayIcon) {
|
||||
systemTrayIcon->setContextMenu(menu); // This is a Qt bug. We cannot call setContextMenu more than once. So
|
||||
|
|
@ -1208,6 +1463,24 @@ void App::setSysTrayIcon() {
|
|||
if (!QSystemTrayIcon::isSystemTrayAvailable()) qInfo() << "System tray is not available";
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------
|
||||
// MacOS dock menu actions
|
||||
//-----------------------------------------------------------
|
||||
|
||||
#ifdef __APPLE__
|
||||
/**
|
||||
* Set more actions to the MacOS Dock actions
|
||||
* WARNING: call this function only on macOS
|
||||
*/
|
||||
void App::setMacOSDockActions() {
|
||||
QMenu *menu = new QMenu();
|
||||
QQuickWindow *root = getMainWindow();
|
||||
QAction *markAllReadAction = createMarkAsReadAction(root);
|
||||
menu->addAction(markAllReadAction);
|
||||
menu->setAsDockMenu();
|
||||
}
|
||||
#endif
|
||||
|
||||
//-----------------------------------------------------------
|
||||
// Locale TODO - App only in French now.
|
||||
//-----------------------------------------------------------
|
||||
|
|
@ -1249,3 +1522,48 @@ QString App::getSdkVersion() {
|
|||
return tr('unknown');
|
||||
#endif
|
||||
}
|
||||
|
||||
QString App::getQtVersion() const {
|
||||
return qVersion();
|
||||
}
|
||||
|
||||
void App::checkForUpdate(bool requestedByUser) {
|
||||
mustBeInMainThread(log().arg(Q_FUNC_INFO));
|
||||
if (CoreModel::getInstance() && mCoreModelConnection) {
|
||||
mCoreModelConnection->invokeToModel([this, requestedByUser] {
|
||||
mustBeInLinphoneThread(log().arg(Q_FUNC_INFO));
|
||||
CoreModel::getInstance()->checkForUpdate(Utils::appStringToCoreString(applicationVersion()),
|
||||
requestedByUser);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
ChatGui *App::getCurrentChat() const {
|
||||
return mCurrentChat;
|
||||
}
|
||||
|
||||
void App::setCurrentChat(ChatGui *chat) {
|
||||
if (chat != mCurrentChat) {
|
||||
mCurrentChat = chat;
|
||||
emit currentChatChanged();
|
||||
}
|
||||
}
|
||||
|
||||
float App::getScreenRatio() const {
|
||||
return mScreenRatio;
|
||||
}
|
||||
|
||||
void App::setScreenRatio(float ratio) {
|
||||
mScreenRatio = ratio;
|
||||
}
|
||||
|
||||
QAction *App::createMarkAsReadAction(QQuickWindow *window) {
|
||||
QAction *markAllReadAction = new QAction(tr("mark_all_read_action"), window);
|
||||
window->connect(markAllReadAction, &QAction::triggered, this, [this] {
|
||||
lDebug() << "Mark all as read";
|
||||
emit mAccountList->lResetMissedCalls();
|
||||
emit mAccountList->lResetUnreadMessages();
|
||||
mCoreModelConnection->invokeToModel([this]() { CoreModel::getInstance()->unreadNotificationsChanged(); });
|
||||
});
|
||||
return markAllReadAction;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,12 +18,14 @@
|
|||
* 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"
|
||||
|
|
@ -31,6 +33,7 @@
|
|||
#include "tool/AbstractObject.hpp"
|
||||
|
||||
class CallGui;
|
||||
class ChatGui;
|
||||
class Thread;
|
||||
class Notifier;
|
||||
class QQuickWindow;
|
||||
|
|
@ -43,17 +46,22 @@ class App : public SingleApplication, public AbstractObject {
|
|||
Q_PROPERTY(AccountList *accounts READ getAccounts NOTIFY accountsChanged)
|
||||
Q_PROPERTY(CallList *calls READ getCalls NOTIFY callsChanged)
|
||||
Q_PROPERTY(QString shortApplicationVersion READ getShortApplicationVersion CONSTANT)
|
||||
Q_PROPERTY(QString qtVersion READ getQtVersion CONSTANT)
|
||||
Q_PROPERTY(QString gitBranchName READ getGitBranchName CONSTANT)
|
||||
Q_PROPERTY(QString sdkVersion READ getSdkVersion CONSTANT)
|
||||
Q_PROPERTY(ChatGui *currentChat READ getCurrentChat WRITE setCurrentChat NOTIFY currentChatChanged)
|
||||
|
||||
public:
|
||||
App(int &argc, char *argv[]);
|
||||
~App();
|
||||
void setSelf(QSharedPointer<App>(me));
|
||||
static App *getInstance();
|
||||
static QThread *getLinphoneThread();
|
||||
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>
|
||||
|
|
@ -119,6 +127,10 @@ public:
|
|||
void restart();
|
||||
bool autoStartEnabled();
|
||||
void setSysTrayIcon();
|
||||
QSystemTrayIcon *getSystemTrayIcon() const {
|
||||
return mSystemTrayIcon;
|
||||
}
|
||||
void updateSysTrayCount(int n);
|
||||
QLocale getLocale();
|
||||
|
||||
void onLoggerInitialized();
|
||||
|
|
@ -127,7 +139,9 @@ public:
|
|||
bool getCoreStarted() const;
|
||||
void setCoreStarted(bool started);
|
||||
|
||||
QQuickWindow *getCallsWindow(QVariant callGui = QVariant());
|
||||
QQuickWindow *getCallsWindow();
|
||||
Q_INVOKABLE void handleAppActivity();
|
||||
QQuickWindow *getOrCreateCallsWindow(QVariant callGui = QVariant());
|
||||
void setCallsWindowProperty(const char *id, QVariant property);
|
||||
void closeCallsWindow();
|
||||
|
||||
|
|
@ -153,6 +167,14 @@ public:
|
|||
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();
|
||||
|
|
@ -172,22 +194,30 @@ 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;
|
||||
|
|
@ -195,11 +225,14 @@ private:
|
|||
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
|
||||
};
|
||||
|
|
|
|||
|
|
@ -19,6 +19,28 @@ list(APPEND _LINPHONEAPP_SOURCES
|
|||
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
|
||||
|
|
@ -64,10 +86,18 @@ list(APPEND _LINPHONEAPP_SOURCES
|
|||
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
|
||||
|
||||
|
|
@ -99,5 +129,9 @@ else() # Use QDBus for Linux
|
|||
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)
|
||||
|
|
|
|||
|
|
@ -43,18 +43,18 @@ AccountCore::AccountCore(const std::shared_ptr<linphone::Account> &account) : QO
|
|||
auto address = account->getContactAddress();
|
||||
mContactAddress = address ? Utils::coreStringToAppString(account->getContactAddress()->asStringUriOnly()) : "";
|
||||
auto params = account->getParams()->clone();
|
||||
auto identityAddress = params->getIdentityAddress()->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->getUnreadChatMessageCount() + account->getMissedCallsCount(); // TODO
|
||||
mUnreadNotifications = account->getMissedCallsCount();
|
||||
mUnreadNotifications = account->getMissedCallsCount() + account->getUnreadChatMessageCount();
|
||||
mDisplayName = Utils::coreStringToAppString(identityAddress->getDisplayName());
|
||||
if (mDisplayName.isEmpty()) {
|
||||
mDisplayName = ToolModel::getDisplayName(identityAddress);
|
||||
identityAddress->setDisplayName(Utils::appStringToCoreString(mDisplayName));
|
||||
params->setIdentityAddress(identityAddress);
|
||||
auto copyAddress = identityAddress->clone();
|
||||
copyAddress->setDisplayName(Utils::appStringToCoreString(mDisplayName));
|
||||
params->setIdentityAddress(copyAddress);
|
||||
account->setParams(params);
|
||||
}
|
||||
mRegisterEnabled = params->registerEnabled();
|
||||
|
|
@ -65,9 +65,11 @@ AccountCore::AccountCore(const std::shared_ptr<linphone::Account> &account) : QO
|
|||
<< "TLS"
|
||||
<< "DTLS";
|
||||
mTransport = LinphoneEnums::toString(LinphoneEnums::fromLinphone(params->getTransport()));
|
||||
mServerAddress =
|
||||
mRegistrarUri =
|
||||
params->getServerAddress() ? Utils::coreStringToAppString(params->getServerAddress()->asString()) : "";
|
||||
mOutboundProxyEnabled = params->outboundProxyEnabled();
|
||||
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();
|
||||
|
|
@ -82,10 +84,19 @@ AccountCore::AccountCore(const std::shared_ptr<linphone::Account> &account) : QO
|
|||
? Utils::coreStringToAppString(params->getAudioVideoConferenceFactoryAddress()->asString())
|
||||
: "";
|
||||
mLimeServerUrl = Utils::coreStringToAppString(params->getLimeServerUrl());
|
||||
mCcmpServerUrl = Utils::coreStringToAppString(params->getCcmpServerUrl());
|
||||
|
||||
// Add listener
|
||||
mAccountModel = Utils::makeQObject_ptr<AccountModel>(account); // OK
|
||||
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;
|
||||
|
|
@ -128,8 +139,8 @@ AccountCore::AccountCore(const AccountCore &accountCore) {
|
|||
mVoicemailAddress = accountCore.mVoicemailAddress;
|
||||
mTransport = accountCore.mTransport;
|
||||
mTransports = accountCore.mTransports;
|
||||
mServerAddress = accountCore.mServerAddress;
|
||||
mOutboundProxyEnabled = accountCore.mOutboundProxyEnabled;
|
||||
mRegistrarUri = accountCore.mRegistrarUri;
|
||||
mOutboundProxyUri = accountCore.mOutboundProxyUri;
|
||||
mStunServer = accountCore.mStunServer;
|
||||
mIceEnabled = accountCore.mIceEnabled;
|
||||
mAvpfEnabled = accountCore.mAvpfEnabled;
|
||||
|
|
@ -138,6 +149,7 @@ AccountCore::AccountCore(const AccountCore &accountCore) {
|
|||
mConferenceFactoryAddress = accountCore.mConferenceFactoryAddress;
|
||||
mAudioVideoConferenceFactoryAddress = accountCore.mAudioVideoConferenceFactoryAddress;
|
||||
mLimeServerUrl = accountCore.mLimeServerUrl;
|
||||
mCcmpServerUrl = accountCore.mCcmpServerUrl;
|
||||
}
|
||||
|
||||
void AccountCore::setSelf(QSharedPointer<AccountCore> me) {
|
||||
|
|
@ -148,6 +160,12 @@ void AccountCore::setSelf(QSharedPointer<AccountCore> me) {
|
|||
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); });
|
||||
|
|
@ -187,11 +205,11 @@ void AccountCore::setSelf(QSharedPointer<AccountCore> me) {
|
|||
mAccountModelConnection->invokeToCore(
|
||||
[this, value]() { onTransportChanged(LinphoneEnums::toString(LinphoneEnums::fromLinphone(value))); });
|
||||
});
|
||||
mAccountModelConnection->makeConnectToModel(&AccountModel::serverAddressChanged, [this](QString value) {
|
||||
mAccountModelConnection->invokeToCore([this, value]() { onServerAddressChanged(value); });
|
||||
mAccountModelConnection->makeConnectToModel(&AccountModel::registrarUriChanged, [this](QString value) {
|
||||
mAccountModelConnection->invokeToCore([this, value]() { onRegistrarUriChanged(value); });
|
||||
});
|
||||
mAccountModelConnection->makeConnectToModel(&AccountModel::outboundProxyEnabledChanged, [this](bool value) {
|
||||
mAccountModelConnection->invokeToCore([this, value]() { onOutboundProxyEnabledChanged(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); });
|
||||
|
|
@ -220,9 +238,23 @@ void AccountCore::setSelf(QSharedPointer<AccountCore> me) {
|
|||
mAccountModelConnection->makeConnectToModel(&AccountModel::limeServerUrlChanged, [this](QString value) {
|
||||
mAccountModelConnection->invokeToCore([this, value]() { onLimeServerUrlChanged(value); });
|
||||
});
|
||||
mAccountModelConnection->makeConnectToModel(&AccountModel::ccmpServerUrlChanged, [this](QString value) {
|
||||
mAccountModelConnection->invokeToCore([this, value]() { onCcmpServerUrlChanged(value); });
|
||||
});
|
||||
|
||||
mAccountModelConnection->makeConnectToModel(
|
||||
&AccountModel::removed, [this]() { mAccountModelConnection->invokeToCore([this]() { emit removed(); }); });
|
||||
|
||||
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); });
|
||||
|
|
@ -233,19 +265,17 @@ void AccountCore::setSelf(QSharedPointer<AccountCore> me) {
|
|||
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(); });
|
||||
});
|
||||
mCoreModelConnection = SafeConnection<AccountCore, CoreModel>::create(me, CoreModel::getInstance());
|
||||
mAccountModelConnection->makeConnectToCore(&AccountCore::unreadCallNotificationsChanged, [this]() {
|
||||
mAccountModelConnection->invokeToModel([this]() { CoreModel::getInstance()->unreadNotificationsChanged(); });
|
||||
});
|
||||
mAccountModelConnection->makeConnectToCore(&AccountCore::unreadMessageNotificationsChanged, [this]() {
|
||||
mAccountModelConnection->invokeToModel([this]() { CoreModel::getInstance()->unreadNotificationsChanged(); });
|
||||
});
|
||||
mAccountModelConnection->makeConnectToCore(&AccountCore::unreadNotificationsChanged, [this]() {
|
||||
mAccountModelConnection->invokeToModel([this]() { CoreModel::getInstance()->unreadNotificationsChanged(); });
|
||||
});
|
||||
mAccountModelConnection->makeConnectToCore(&AccountCore::lSetDisplayName, [this](QString displayName) {
|
||||
mAccountModelConnection->invokeToModel([this, displayName]() { mAccountModel->setDisplayName(displayName); });
|
||||
});
|
||||
|
|
@ -260,6 +290,13 @@ void AccountCore::setSelf(QSharedPointer<AccountCore> me) {
|
|||
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)
|
||||
|
|
@ -269,6 +306,14 @@ void AccountCore::setSelf(QSharedPointer<AccountCore> me) {
|
|||
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) {
|
||||
|
|
@ -278,8 +323,8 @@ void AccountCore::reset(const AccountCore &accountCore) {
|
|||
setMwiServerAddress(accountCore.mMwiServerAddress);
|
||||
setVoicemailAddress(accountCore.mVoicemailAddress);
|
||||
setTransport(accountCore.mTransport);
|
||||
setServerAddress(accountCore.mServerAddress);
|
||||
setOutboundProxyEnabled(accountCore.mOutboundProxyEnabled);
|
||||
setRegistrarUri(accountCore.mRegistrarUri);
|
||||
setOutboundProxyUri(accountCore.mOutboundProxyUri);
|
||||
setStunServer(accountCore.mStunServer);
|
||||
setIceEnabled(accountCore.mIceEnabled);
|
||||
setAvpfEnabled(accountCore.mAvpfEnabled);
|
||||
|
|
@ -288,6 +333,7 @@ void AccountCore::reset(const AccountCore &accountCore) {
|
|||
setConferenceFactoryAddress(accountCore.mConferenceFactoryAddress);
|
||||
setAudioVideoConferenceFactoryAddress(accountCore.mAudioVideoConferenceFactoryAddress);
|
||||
setLimeServerUrl(accountCore.mLimeServerUrl);
|
||||
setCcmpServerUrl(accountCore.mCcmpServerUrl);
|
||||
}
|
||||
|
||||
const std::shared_ptr<AccountModel> &AccountCore::getModel() const {
|
||||
|
|
@ -347,8 +393,8 @@ void AccountCore::setUnreadMessageNotifications(int unread) {
|
|||
void AccountCore::onRegistrationStateChanged(const std::shared_ptr<linphone::Account> &account,
|
||||
linphone::RegistrationState state,
|
||||
const std::string &message) {
|
||||
lDebug() << log().arg(Q_FUNC_INFO) << (int)state;
|
||||
mRegistrationState = LinphoneEnums::fromLinphone(state);
|
||||
qDebug() << log().arg(Q_FUNC_INFO) << mRegistrationState;
|
||||
emit registrationStateChanged(Utils::coreStringToAppString(message));
|
||||
}
|
||||
|
||||
|
|
@ -423,6 +469,30 @@ QString AccountCore::getHumanReadableRegistrationState() const {
|
|||
}
|
||||
}
|
||||
|
||||
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:
|
||||
|
|
@ -471,12 +541,12 @@ QString AccountCore::getTransport() {
|
|||
return mTransport;
|
||||
}
|
||||
|
||||
QString AccountCore::getServerAddress() {
|
||||
return mServerAddress;
|
||||
QString AccountCore::getRegistrarUri() {
|
||||
return mRegistrarUri;
|
||||
}
|
||||
|
||||
bool AccountCore::getOutboundProxyEnabled() {
|
||||
return mOutboundProxyEnabled;
|
||||
QString AccountCore::getOutboundProxyUri() {
|
||||
return mOutboundProxyUri;
|
||||
}
|
||||
|
||||
QString AccountCore::getStunServer() {
|
||||
|
|
@ -511,6 +581,10 @@ QString AccountCore::getLimeServerUrl() {
|
|||
return mLimeServerUrl;
|
||||
}
|
||||
|
||||
QString AccountCore::getCcmpServerUrl() {
|
||||
return mCcmpServerUrl;
|
||||
}
|
||||
|
||||
void AccountCore::setMwiServerAddress(QString value) {
|
||||
if (mMwiServerAddress != value) {
|
||||
mMwiServerAddress = value;
|
||||
|
|
@ -529,32 +603,24 @@ void AccountCore::setVoicemailAddress(QString value) {
|
|||
|
||||
void AccountCore::setTransport(QString value) {
|
||||
if (mTransport != value) {
|
||||
mAccountModelConnection->invokeToModel([this, value] {
|
||||
mustBeInLinphoneThread(getClassName() + Q_FUNC_INFO);
|
||||
LinphoneEnums::TransportType transport;
|
||||
LinphoneEnums::fromString(value, &transport);
|
||||
mAccountModel->setTransport(LinphoneEnums::toLinphone(transport), false);
|
||||
});
|
||||
mTransport = value;
|
||||
emit transportChanged();
|
||||
setIsSaved(false);
|
||||
}
|
||||
}
|
||||
|
||||
void AccountCore::setServerAddress(QString value) {
|
||||
if (mServerAddress != value) {
|
||||
mAccountModelConnection->invokeToModel([this, value, transportString = mTransport] {
|
||||
LinphoneEnums::TransportType transport;
|
||||
LinphoneEnums::fromString(transportString, &transport);
|
||||
mustBeInLinphoneThread(getClassName() + Q_FUNC_INFO);
|
||||
mAccountModel->setServerAddress(value, LinphoneEnums::toLinphone(transport), false);
|
||||
});
|
||||
void AccountCore::setRegistrarUri(QString value) {
|
||||
if (mRegistrarUri != value) {
|
||||
mRegistrarUri = value;
|
||||
emit registrarUriChanged();
|
||||
setIsSaved(false);
|
||||
}
|
||||
}
|
||||
|
||||
void AccountCore::setOutboundProxyEnabled(bool value) {
|
||||
if (mOutboundProxyEnabled != value) {
|
||||
mOutboundProxyEnabled = value;
|
||||
emit outboundProxyEnabledChanged();
|
||||
void AccountCore::setOutboundProxyUri(QString value) {
|
||||
if (mOutboundProxyUri != value) {
|
||||
mOutboundProxyUri = value;
|
||||
emit outboundProxyUriChanged();
|
||||
setIsSaved(false);
|
||||
}
|
||||
}
|
||||
|
|
@ -623,6 +689,14 @@ void AccountCore::setLimeServerUrl(QString value) {
|
|||
}
|
||||
}
|
||||
|
||||
void AccountCore::setCcmpServerUrl(QString value) {
|
||||
if (mCcmpServerUrl != value) {
|
||||
mCcmpServerUrl = value;
|
||||
emit ccmpServerUrlChanged();
|
||||
setIsSaved(false);
|
||||
}
|
||||
}
|
||||
|
||||
bool AccountCore::isSaved() const {
|
||||
return mIsSaved;
|
||||
}
|
||||
|
|
@ -662,19 +736,20 @@ void AccountCore::onTransportChanged(QString value) {
|
|||
}
|
||||
}
|
||||
|
||||
void AccountCore::onServerAddressChanged(QString value) {
|
||||
if (value != mServerAddress) {
|
||||
mServerAddress = value;
|
||||
emit serverAddressChanged();
|
||||
void AccountCore::onRegistrarUriChanged(QString value) {
|
||||
if (value != mRegistrarUri) {
|
||||
mRegistrarUri = value;
|
||||
emit registrarUriChanged();
|
||||
}
|
||||
}
|
||||
|
||||
void AccountCore::onOutboundProxyEnabledChanged(bool value) {
|
||||
if (value != mOutboundProxyEnabled) {
|
||||
mOutboundProxyEnabled = value;
|
||||
emit outboundProxyEnabledChanged();
|
||||
void AccountCore::onOutboundProxyUriChanged(QString value) {
|
||||
if (value != mOutboundProxyUri) {
|
||||
mOutboundProxyUri = value;
|
||||
emit outboundProxyUriChanged();
|
||||
}
|
||||
}
|
||||
|
||||
void AccountCore::onStunServerChanged(QString value) {
|
||||
if (value != mStunServer) {
|
||||
mStunServer = value;
|
||||
|
|
@ -734,14 +809,21 @@ void AccountCore::onLimeServerUrlChanged(QString value) {
|
|||
}
|
||||
}
|
||||
|
||||
void AccountCore::onCcmpServerUrlChanged(QString value) {
|
||||
if (value != mCcmpServerUrl) {
|
||||
mCcmpServerUrl = value;
|
||||
emit ccmpServerUrlChanged();
|
||||
}
|
||||
}
|
||||
|
||||
void AccountCore::writeIntoModel(std::shared_ptr<AccountModel> model) const {
|
||||
mustBeInLinphoneThread(getClassName() + Q_FUNC_INFO);
|
||||
model->setMwiServerAddress(mMwiServerAddress);
|
||||
LinphoneEnums::TransportType transport;
|
||||
LinphoneEnums::fromString(mTransport, &transport);
|
||||
model->setTransport(LinphoneEnums::toLinphone(transport), true);
|
||||
model->setServerAddress(mServerAddress, LinphoneEnums::toLinphone(transport), true);
|
||||
model->setOutboundProxyEnabled(mOutboundProxyEnabled);
|
||||
model->setRegistrarUri(mRegistrarUri);
|
||||
model->setOutboundProxyUri(mOutboundProxyUri);
|
||||
model->setStunServer(mStunServer);
|
||||
model->setIceEnabled(mIceEnabled);
|
||||
model->setAvpfEnabled(mAvpfEnabled);
|
||||
|
|
@ -750,27 +832,28 @@ void AccountCore::writeIntoModel(std::shared_ptr<AccountModel> model) const {
|
|||
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);
|
||||
|
||||
mUnreadCallNotifications = model->getMissedCallsCount();
|
||||
mUnreadMessageNotifications = model->getUnreadMessagesCount();
|
||||
mMwiServerAddress = model->getMwiServerAddress();
|
||||
mTransport = LinphoneEnums::toString(LinphoneEnums::fromLinphone(model->getTransport()));
|
||||
mServerAddress = model->getServerAddress();
|
||||
mOutboundProxyEnabled = model->getOutboundProxyEnabled();
|
||||
mStunServer = model->getStunServer();
|
||||
mIceEnabled = model->getIceEnabled();
|
||||
mAvpfEnabled = model->getAvpfEnabled();
|
||||
mBundleModeEnabled = model->getBundleModeEnabled();
|
||||
mExpire = model->getExpire();
|
||||
mConferenceFactoryAddress = model->getConferenceFactoryAddress();
|
||||
mAudioVideoConferenceFactoryAddress = model->getAudioVideoConferenceFactoryAddress();
|
||||
mLimeServerUrl = model->getLimeServerUrl();
|
||||
mVoicemailAddress = model->getVoicemailAddress();
|
||||
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() {
|
||||
|
|
@ -802,3 +885,38 @@ void AccountCore::undo() {
|
|||
});
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -58,9 +58,9 @@ public:
|
|||
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 serverAddress READ getServerAddress WRITE setServerAddress NOTIFY serverAddressChanged)
|
||||
Q_PROPERTY(bool outboundProxyEnabled READ getOutboundProxyEnabled WRITE setOutboundProxyEnabled NOTIFY
|
||||
outboundProxyEnabledChanged)
|
||||
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)
|
||||
|
|
@ -75,6 +75,16 @@ public:
|
|||
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);
|
||||
|
|
@ -114,6 +124,8 @@ public:
|
|||
void onDialPlanChanged(QVariantMap internationalPrefix);
|
||||
QString getHumanReadableRegistrationState() const;
|
||||
QString getHumanReadableRegistrationStateExplained() const;
|
||||
QColor getRegistrationColor() const;
|
||||
QUrl getRegistrationIcon() const;
|
||||
bool getRegisterEnabled() const;
|
||||
void onRegisterEnabledChanged(bool enabled);
|
||||
|
||||
|
|
@ -121,8 +133,8 @@ public:
|
|||
QString getMwiServerAddress();
|
||||
QString getTransport();
|
||||
QStringList getTransports();
|
||||
QString getServerAddress();
|
||||
bool getOutboundProxyEnabled();
|
||||
QString getRegistrarUri();
|
||||
QString getOutboundProxyUri();
|
||||
QString getStunServer();
|
||||
bool getIceEnabled();
|
||||
bool getAvpfEnabled();
|
||||
|
|
@ -132,11 +144,12 @@ public:
|
|||
QString getAudioVideoConferenceFactoryAddress();
|
||||
QString getLimeServerUrl();
|
||||
QString getVoicemailAddress();
|
||||
QString getCcmpServerUrl();
|
||||
|
||||
void setMwiServerAddress(QString value);
|
||||
void setTransport(QString value);
|
||||
void setServerAddress(QString value);
|
||||
void setOutboundProxyEnabled(bool value);
|
||||
void setRegistrarUri(QString value);
|
||||
void setOutboundProxyUri(QString value);
|
||||
void setStunServer(QString value);
|
||||
void setIceEnabled(bool value);
|
||||
void setAvpfEnabled(bool value);
|
||||
|
|
@ -146,6 +159,7 @@ public:
|
|||
void setAudioVideoConferenceFactoryAddress(QString value);
|
||||
void setLimeServerUrl(QString value);
|
||||
void setVoicemailAddress(QString value);
|
||||
void setCcmpServerUrl(QString value);
|
||||
|
||||
bool isSaved() const;
|
||||
void setIsSaved(bool saved);
|
||||
|
|
@ -154,8 +168,8 @@ public:
|
|||
void onMwiServerAddressChanged(QString value);
|
||||
void onVoicemailAddressChanged(QString value);
|
||||
void onTransportChanged(QString value);
|
||||
void onServerAddressChanged(QString value);
|
||||
void onOutboundProxyEnabledChanged(bool value);
|
||||
void onRegistrarUriChanged(QString value);
|
||||
void onOutboundProxyUriChanged(QString value);
|
||||
void onStunServerChanged(QString value);
|
||||
void onIceEnabledChanged(bool value);
|
||||
void onAvpfEnabledChanged(bool value);
|
||||
|
|
@ -164,15 +178,26 @@ public:
|
|||
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);
|
||||
|
|
@ -185,8 +210,8 @@ signals:
|
|||
void notificationsAllowedChanged();
|
||||
void mwiServerAddressChanged();
|
||||
void transportChanged();
|
||||
void serverAddressChanged();
|
||||
void outboundProxyEnabledChanged();
|
||||
void registrarUriChanged();
|
||||
void outboundProxyUriChanged();
|
||||
void stunServerChanged();
|
||||
void iceEnabledChanged();
|
||||
void avpfEnabledChanged();
|
||||
|
|
@ -198,16 +223,22 @@ signals:
|
|||
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;
|
||||
|
|
@ -231,8 +262,8 @@ private:
|
|||
QString mMwiServerAddress;
|
||||
QString mTransport;
|
||||
QStringList mTransports;
|
||||
QString mServerAddress;
|
||||
bool mOutboundProxyEnabled;
|
||||
QString mRegistrarUri;
|
||||
QString mOutboundProxyUri;
|
||||
QString mStunServer;
|
||||
bool mIceEnabled;
|
||||
bool mAvpfEnabled;
|
||||
|
|
@ -242,8 +273,14 @@ private:
|
|||
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;
|
||||
|
|
|
|||
|
|
@ -69,16 +69,14 @@ void AccountDeviceList::setAccount(const QSharedPointer<AccountCore> &accountCor
|
|||
mAccountCore = accountCore;
|
||||
lDebug() << log().arg("Set account model") << mAccountCore.get();
|
||||
// oldConnect.unlock();
|
||||
refreshDevices();
|
||||
if (mAccountCore) refreshDevices();
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
void AccountDeviceList::refreshDevices() {
|
||||
mustBeInMainThread(log().arg(Q_FUNC_INFO));
|
||||
beginResetModel();
|
||||
clearData();
|
||||
endResetModel();
|
||||
resetData();
|
||||
if (mAccountCore) {
|
||||
auto requestDeviceList = [this] {
|
||||
if (!mAccountManagerServicesModelConnection) return;
|
||||
|
|
@ -150,14 +148,14 @@ void AccountDeviceList::setSelf(QSharedPointer<AccountDeviceList> me) {
|
|||
&AccountManagerServicesModel::requestError,
|
||||
[this](const std::shared_ptr<const linphone::AccountManagerServicesRequest> &request, int statusCode,
|
||||
const std::string &errorMessage,
|
||||
const std::shared_ptr<const linphone::Dictionary> ¶meterErrors) {
|
||||
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);
|
||||
const std::shared_ptr<const linphone::Dictionary> ¶meterErrors) {
|
||||
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,
|
||||
|
|
|
|||
|
|
@ -21,9 +21,9 @@
|
|||
#ifndef ACCOUNT_DEVICE_LIST_H_
|
||||
#define ACCOUNT_DEVICE_LIST_H_
|
||||
|
||||
#include "../proxy/ListProxy.hpp"
|
||||
#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"
|
||||
|
|
|
|||
|
|
@ -61,6 +61,11 @@ void AccountList::setSelf(QSharedPointer<AccountList> me) {
|
|||
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());
|
||||
|
|
@ -68,8 +73,14 @@ void AccountList::setSelf(QSharedPointer<AccountList> me) {
|
|||
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(
|
||||
|
|
@ -88,7 +99,8 @@ void AccountList::setSelf(QSharedPointer<AccountList> me) {
|
|||
// with the open id account
|
||||
mModelConnection->makeConnectToModel(&CoreModel::bearerAccountAdded, [this] {
|
||||
setInitialized(false);
|
||||
emit lUpdate(true); });
|
||||
emit lUpdate(true);
|
||||
});
|
||||
|
||||
mModelConnection->makeConnectToModel(
|
||||
&CoreModel::globalStateChanged,
|
||||
|
|
@ -158,6 +170,18 @@ void AccountList::setInitialized(bool init) {
|
|||
}
|
||||
}
|
||||
|
||||
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();
|
||||
|
|
|
|||
|
|
@ -51,6 +51,8 @@ public:
|
|||
|
||||
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:
|
||||
|
|
@ -58,6 +60,7 @@ signals:
|
|||
void haveAccountChanged();
|
||||
void defaultAccountChanged();
|
||||
void initializedChanged(bool init);
|
||||
void unreadNotificationsChanged();
|
||||
|
||||
private:
|
||||
bool mHaveAccount = false;
|
||||
|
|
|
|||
|
|
@ -73,8 +73,11 @@ void CarddavCore::remove() {
|
|||
|
||||
void CarddavCore::setSelf(QSharedPointer<CarddavCore> me) {
|
||||
mCarddavModelConnection = SafeConnection<CarddavCore, CarddavModel>::create(me, mCarddavModel);
|
||||
mCarddavModelConnection->makeConnectToModel(&CarddavModel::saved, [this](bool success) {
|
||||
mCarddavModelConnection->invokeToCore([this, success]() { emit saved(success); });
|
||||
mCarddavModelConnection->makeConnectToModel(&CarddavModel::saved, [this](bool success, QString message) {
|
||||
mCarddavModelConnection->invokeToCore([this, success, message]() {
|
||||
if (success) emit App::getInstance() -> getSettings()->cardDAVAddressBookSynchronized();
|
||||
emit saved(success, message);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -50,7 +50,7 @@ public:
|
|||
DECLARE_CORE_MEMBER(bool, storeNewFriendsInIt, StoreNewFriendsInIt)
|
||||
|
||||
signals:
|
||||
void saved(bool success);
|
||||
void saved(bool success, QString message);
|
||||
|
||||
private:
|
||||
std::shared_ptr<CarddavModel> mCarddavModel;
|
||||
|
|
|
|||
|
|
@ -46,8 +46,7 @@ CallHistoryCore::CallHistoryCore(const std::shared_ptr<linphone::CallLog> &callL
|
|||
mustBeInLinphoneThread(getClassName());
|
||||
mCallHistoryModel = std::make_shared<CallHistoryModel>(callLog);
|
||||
|
||||
auto addr = callLog->getRemoteAddress()->clone();
|
||||
addr->clean();
|
||||
auto addr = callLog->getRemoteAddress();
|
||||
mStatus = LinphoneEnums::fromLinphone(callLog->getStatus());
|
||||
mDate = QDateTime::fromMSecsSinceEpoch(callLog->getStartDate() * 1000);
|
||||
mIsOutgoing = callLog->getDir() == linphone::Call::Dir::Outgoing;
|
||||
|
|
@ -129,14 +128,16 @@ void CallHistoryCore::setSelf(QSharedPointer<CallHistoryCore> me) {
|
|||
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)
|
||||
mCoreModelConnection->makeConnectToModel(&CoreModel::magicSearchResultReceived, [this, remoteAddress = mRemoteAddress] {
|
||||
auto displayName = ToolModel::getDisplayName(remoteAddress);
|
||||
mCoreModelConnection->invokeToCore([this, displayName]() {
|
||||
mDisplayName = displayName;
|
||||
emit displayNameChanged();
|
||||
});
|
||||
});
|
||||
|
||||
// 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 {
|
||||
|
|
|
|||
|
|
@ -57,10 +57,12 @@ 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>>();
|
||||
mustBeInLinphoneThread(getClassName());
|
||||
std::list<std::shared_ptr<linphone::CallLog>> linphoneCallLogs;
|
||||
if (auto account = CoreModel::getInstance()->getCore()->getDefaultAccount()) {
|
||||
linphoneCallLogs = account->getCallLogs();
|
||||
|
|
|
|||
|
|
@ -62,6 +62,7 @@ signals:
|
|||
void lUpdate();
|
||||
void lRemoveEntriesForAddress(QString address);
|
||||
void lRemoveAllEntries();
|
||||
void listAboutToBeReset();
|
||||
|
||||
private:
|
||||
// Check the state from CallHistoryCore: sender() must be a CallHistoryCore.
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ 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(); });
|
||||
}
|
||||
|
|
@ -56,7 +57,7 @@ bool CallHistoryProxy::SortFilterList::filterAcceptsRow(int sourceRow, const QMo
|
|||
QRegularExpression::CaseInsensitiveOption |
|
||||
QRegularExpression::UseUnicodePropertiesOption);
|
||||
auto callLog = getItemAtSource<CallHistoryList, CallHistoryCore>(sourceRow);
|
||||
show = callLog->mDisplayName.contains(search) || callLog->mRemoteAddress.contains(search);
|
||||
show = callLog && (callLog->mDisplayName.contains(search) || callLog->mRemoteAddress.contains(search));
|
||||
}
|
||||
return show;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -41,6 +41,9 @@ public:
|
|||
Q_INVOKABLE void removeEntriesWithFilter(QString filter);
|
||||
Q_INVOKABLE void reload();
|
||||
|
||||
signals:
|
||||
void listAboutToBeReset();
|
||||
|
||||
protected:
|
||||
QSharedPointer<CallHistoryList> mHistoryList;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2010-2024 Belledonne Communications SARL.
|
||||
* Copyright (c) 2010-2026 Belledonne Communications SARL.
|
||||
*
|
||||
* This file is part of linphone-desktop
|
||||
* (see https://www.linphone.org).
|
||||
|
|
@ -28,6 +28,8 @@
|
|||
#include "tool/Utils.hpp"
|
||||
#include "tool/thread/SafeConnection.hpp"
|
||||
|
||||
#include <QQuickWindow>
|
||||
|
||||
DEFINE_ABSTRACT_OBJECT(CallCore)
|
||||
|
||||
/***********************************************************************/
|
||||
|
|
@ -107,18 +109,17 @@ CallCore::CallCore(const std::shared_ptr<linphone::Call> &call) : QObject(nullpt
|
|||
mCallModel->setSelf(mCallModel);
|
||||
mDuration = call->getDuration();
|
||||
mIsStarted = mDuration > 0;
|
||||
mMicrophoneMuted = call->getMicrophoneMuted();
|
||||
mSpeakerMuted = call->getSpeakerMuted();
|
||||
auto videoDirection = call->getParams()->getVideoDirection();
|
||||
auto callParams = call->getParams();
|
||||
auto videoDirection = callParams->getVideoDirection();
|
||||
mLocalVideoEnabled =
|
||||
videoDirection == linphone::MediaDirection::SendOnly || videoDirection == linphone::MediaDirection::SendRecv;
|
||||
mCameraEnabled = callParams->cameraEnabled();
|
||||
auto remoteParams = call->getRemoteParams();
|
||||
videoDirection = remoteParams ? remoteParams->getVideoDirection() : linphone::MediaDirection::Inactive;
|
||||
mRemoteVideoEnabled =
|
||||
videoDirection == linphone::MediaDirection::SendOnly || videoDirection == linphone::MediaDirection::SendRecv;
|
||||
mState = LinphoneEnums::fromLinphone(call->getState());
|
||||
auto remoteAddress = call->getCallLog()->getRemoteAddress()->clone();
|
||||
remoteAddress->clean();
|
||||
auto remoteAddress = call->getCallLog()->getRemoteAddress();
|
||||
mRemoteAddress = Utils::coreStringToAppString(remoteAddress->asStringUriOnly());
|
||||
mRemoteUsername = Utils::coreStringToAppString(remoteAddress->getUsername());
|
||||
auto linphoneFriend = ToolModel::findFriendByAddress(remoteAddress);
|
||||
|
|
@ -136,7 +137,7 @@ CallCore::CallCore(const std::shared_ptr<linphone::Call> &call) : QObject(nullpt
|
|||
mTransferState = LinphoneEnums::fromLinphone(call->getTransferState());
|
||||
mLocalToken = Utils::coreStringToAppString(mCallModel->getLocalAtuhenticationToken());
|
||||
mRemoteTokens = mCallModel->getRemoteAtuhenticationTokens();
|
||||
mEncryption = LinphoneEnums::fromLinphone(call->getParams()->getMediaEncryption());
|
||||
mEncryption = LinphoneEnums::fromLinphone(callParams->getMediaEncryption());
|
||||
auto tokenVerified = call->getAuthenticationTokenVerified();
|
||||
mIsMismatch = call->getZrtpCacheMismatchFlag();
|
||||
mIsSecured = (mEncryption == LinphoneEnums::MediaEncryption::Zrtp && tokenVerified) ||
|
||||
|
|
@ -158,10 +159,12 @@ CallCore::CallCore(const std::shared_ptr<linphone::Call> &call) : QObject(nullpt
|
|||
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 = call->getParams() && call->getParams()->isRecording();
|
||||
mRecording = callParams && callParams->isRecording();
|
||||
mRemoteRecording = call->getRemoteParams() && call->getRemoteParams()->isRecording();
|
||||
auto settingsModel = SettingsModel::getInstance();
|
||||
mMicrophoneVolume = call->getRecordVolume();
|
||||
|
|
@ -194,8 +197,8 @@ void CallCore::setSelf(QSharedPointer<CallCore> me) {
|
|||
mCallModelConnection->makeConnectToModel(&CallModel::speakerMutedChanged, [this](bool isMuted) {
|
||||
mCallModelConnection->invokeToCore([this, isMuted]() { setSpeakerMuted(isMuted); });
|
||||
});
|
||||
mCallModelConnection->makeConnectToCore(&CallCore::lSetLocalVideoEnabled, [this](bool enabled) {
|
||||
mCallModelConnection->invokeToModel([this, enabled]() { mCallModel->setLocalVideoEnabled(enabled); });
|
||||
mCallModelConnection->makeConnectToCore(&CallCore::lSetCameraEnabled, [this](bool enabled) {
|
||||
mCallModelConnection->invokeToModel([this, enabled]() { mCallModel->setCameraEnabled(enabled); });
|
||||
});
|
||||
mCallModelConnection->makeConnectToCore(&CallCore::lStartRecording, [this]() {
|
||||
mCallModelConnection->invokeToModel([this]() { mCallModel->startRecording(); });
|
||||
|
|
@ -205,14 +208,15 @@ void CallCore::setSelf(QSharedPointer<CallCore> me) {
|
|||
});
|
||||
mCallModelConnection->makeConnectToModel(
|
||||
&CallModel::recordingChanged, [this](const std::shared_ptr<linphone::Call> &call, bool recording) {
|
||||
mCallModelConnection->invokeToCore([this, recording]() {
|
||||
auto recordFile = QString::fromStdString(mCallModel->getRecordFile());
|
||||
mCallModelConnection->invokeToCore([this, recording, recordFile]() {
|
||||
setRecording(recording);
|
||||
if (recording == false) {
|
||||
//: "Enregistrement terminé"
|
||||
Utils::showInformationPopup(tr("call_record_end_message"),
|
||||
//: "L'appel a été enregistré dans le fichier : %1"
|
||||
tr("call_record_saved_in_file_message").arg(QString::fromStdString(mCallModel->getRecordFile())),
|
||||
true, App::getInstance()->getCallsWindow());
|
||||
//: "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());
|
||||
}
|
||||
});
|
||||
});
|
||||
|
|
@ -244,6 +248,9 @@ void CallCore::setSelf(QSharedPointer<CallCore> me) {
|
|||
mCallModelConnection->makeConnectToModel(&CallModel::localVideoEnabledChanged, [this](bool enabled) {
|
||||
mCallModelConnection->invokeToCore([this, enabled]() { setLocalVideoEnabled(enabled); });
|
||||
});
|
||||
mCallModelConnection->makeConnectToModel(&CallModel::cameraEnabledChanged, [this](bool enabled) {
|
||||
mCallModelConnection->invokeToCore([this, enabled]() { setCameraEnabled(enabled); });
|
||||
});
|
||||
mCallModelConnection->makeConnectToModel(&CallModel::durationChanged, [this](int duration) {
|
||||
mCallModelConnection->invokeToCore([this, duration]() { setDuration(duration); });
|
||||
});
|
||||
|
|
@ -261,6 +268,7 @@ void CallCore::setSelf(QSharedPointer<CallCore> me) {
|
|||
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);
|
||||
|
|
@ -308,8 +316,8 @@ void CallCore::setSelf(QSharedPointer<CallCore> me) {
|
|||
[this, call, encryption, tokenVerified, localToken, remoteTokens, isCaseMismatch]() {
|
||||
setLocalToken(localToken);
|
||||
setRemoteTokens(remoteTokens);
|
||||
setIsMismatch(isCaseMismatch);
|
||||
setTokenVerified(tokenVerified);
|
||||
setIsMismatch(isCaseMismatch);
|
||||
setTokenVerified(tokenVerified);
|
||||
setEncryption(encryption);
|
||||
});
|
||||
auto mediaEncryption = call->getParams()->getMediaEncryption();
|
||||
|
|
@ -321,7 +329,7 @@ void CallCore::setSelf(QSharedPointer<CallCore> me) {
|
|||
zrtpStats.mHashAlgorithm = Utils::coreStringToAppString(stats->getZrtpHashAlgo());
|
||||
zrtpStats.mAuthenticationAlgorithm = Utils::coreStringToAppString(stats->getZrtpAuthTagAlgo());
|
||||
zrtpStats.mSasAlgorithm = Utils::coreStringToAppString(stats->getZrtpSasAlgo());
|
||||
zrtpStats.mIsPostQuantum = stats->isZrtpKeyAgreementAlgoPostQuantum();
|
||||
zrtpStats.mIsPostQuantum = stats->isZrtpKeyAgreementAlgoPostQuantum();
|
||||
mCallModelConnection->invokeToCore([this, zrtpStats]() { setZrtpStats(zrtpStats); });
|
||||
}
|
||||
});
|
||||
|
|
@ -343,6 +351,8 @@ void CallCore::setSelf(QSharedPointer<CallCore> me) {
|
|||
});
|
||||
mCallModelConnection->makeConnectToModel(&CallModel::conferenceChanged, [this]() {
|
||||
auto conference = mCallModel->getMonitor()->getConference();
|
||||
// Force enable video if in conference to handle screen sharing
|
||||
if (conference && !mCallModel->videoEnabled()) mCallModel->enableVideo(true);
|
||||
QSharedPointer<ConferenceCore> core = conference ? ConferenceCore::create(conference) : nullptr;
|
||||
mCallModelConnection->invokeToCore([this, core]() { setConference(core); });
|
||||
});
|
||||
|
|
@ -388,23 +398,23 @@ void CallCore::setSelf(QSharedPointer<CallCore> me) {
|
|||
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);
|
||||
//: "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")
|
||||
//: "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")
|
||||
//: "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());
|
||||
//: "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) {
|
||||
|
|
@ -414,15 +424,15 @@ void CallCore::setSelf(QSharedPointer<CallCore> me) {
|
|||
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);
|
||||
tr("call_stats_codec_label").arg(Utils::coreStringToAppString(codecType)).arg(codecRate);
|
||||
auto linVideoStats = call->getVideoStats();
|
||||
if (stats) {
|
||||
videoStats.mBandwidth = tr("call_stats_bandwidth_label")
|
||||
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")
|
||||
videoStats.mLossRate = tr("call_stats_loss_rate_label")
|
||||
.arg(linVideoStats->getSenderLossRate())
|
||||
.arg(linVideoStats->getReceiverLossRate());
|
||||
}
|
||||
|
|
@ -430,17 +440,55 @@ void CallCore::setSelf(QSharedPointer<CallCore> me) {
|
|||
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")
|
||||
//: "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);
|
||||
//: "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);
|
||||
}
|
||||
|
||||
|
|
@ -557,6 +605,18 @@ void CallCore::setLocalVideoEnabled(bool enabled) {
|
|||
}
|
||||
}
|
||||
|
||||
bool CallCore::getCameraEnabled() const {
|
||||
return mCameraEnabled;
|
||||
}
|
||||
|
||||
void CallCore::setCameraEnabled(bool enabled) {
|
||||
if (mCameraEnabled != enabled) {
|
||||
mCameraEnabled = enabled;
|
||||
lDebug() << "CameraEnabled: " << mCameraEnabled;
|
||||
emit cameraEnabledChanged();
|
||||
}
|
||||
}
|
||||
|
||||
bool CallCore::getPaused() const {
|
||||
return mPaused;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -117,8 +117,8 @@ public:
|
|||
Q_PROPERTY(QStringList remoteTokens WRITE setRemoteTokens MEMBER mRemoteTokens NOTIFY remoteTokensChanged)
|
||||
Q_PROPERTY(
|
||||
bool remoteVideoEnabled READ getRemoteVideoEnabled WRITE setRemoteVideoEnabled NOTIFY remoteVideoEnabledChanged)
|
||||
Q_PROPERTY(
|
||||
bool localVideoEnabled READ getLocalVideoEnabled WRITE lSetLocalVideoEnabled NOTIFY localVideoEnabledChanged)
|
||||
Q_PROPERTY(bool localVideoEnabled READ getLocalVideoEnabled NOTIFY localVideoEnabledChanged)
|
||||
Q_PROPERTY(bool cameraEnabled READ getCameraEnabled WRITE lSetCameraEnabled NOTIFY cameraEnabledChanged)
|
||||
Q_PROPERTY(bool recording READ getRecording WRITE setRecording NOTIFY recordingChanged)
|
||||
Q_PROPERTY(bool remoteRecording READ getRemoteRecording WRITE setRemoteRecording NOTIFY remoteRecordingChanged)
|
||||
Q_PROPERTY(bool recordable READ getRecordable WRITE setRecordable NOTIFY recordableChanged)
|
||||
|
|
@ -201,6 +201,9 @@ public:
|
|||
bool getLocalVideoEnabled() const;
|
||||
void setLocalVideoEnabled(bool enabled);
|
||||
|
||||
bool getCameraEnabled() const;
|
||||
void setCameraEnabled(bool enabled);
|
||||
|
||||
bool getRecording() const;
|
||||
void setRecording(bool recording);
|
||||
|
||||
|
|
@ -255,6 +258,7 @@ signals:
|
|||
void remoteTokensChanged();
|
||||
void remoteVideoEnabledChanged(bool remoteVideoEnabled);
|
||||
void localVideoEnabledChanged();
|
||||
void cameraEnabledChanged();
|
||||
void recordingChanged();
|
||||
void remoteRecordingChanged();
|
||||
void recordableChanged();
|
||||
|
|
@ -275,7 +279,7 @@ signals:
|
|||
void lTerminateAllCalls(); // Hangup all calls
|
||||
void lSetSpeakerMuted(bool muted);
|
||||
void lSetMicrophoneMuted(bool isMuted);
|
||||
void lSetLocalVideoEnabled(bool enabled);
|
||||
void lSetCameraEnabled(bool enabled);
|
||||
void lSetVideoEnabled(bool enabled);
|
||||
void lSetPaused(bool paused);
|
||||
void lTransferCall(QString address);
|
||||
|
|
@ -331,6 +335,7 @@ private:
|
|||
bool mSpeakerMuted = false;
|
||||
bool mMicrophoneMuted = false;
|
||||
bool mLocalVideoEnabled = false;
|
||||
bool mCameraEnabled = false;
|
||||
bool mVideoEnabled = false;
|
||||
bool mPaused = false;
|
||||
bool mRemoteVideoEnabled = false;
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@
|
|||
#include "CallCore.hpp"
|
||||
#include "CallGui.hpp"
|
||||
#include "core/App.hpp"
|
||||
#include "model/tool/ToolModel.hpp"
|
||||
#include <QSharedPointer>
|
||||
#include <linphone++/linphone.hh>
|
||||
|
||||
|
|
@ -96,40 +97,32 @@ void CallList::setSelf(QSharedPointer<CallList> me) {
|
|||
bool enablingVideo = false;
|
||||
if (currentCall) enablingVideo = currentCall->getCurrentParams()->videoEnabled();
|
||||
if (!conference) {
|
||||
auto parameters = core->createConferenceParams(conference);
|
||||
auto audioVideoConfFactoryUri =
|
||||
core->getDefaultAccount()->getParams()->getAudioVideoConferenceFactoryAddress();
|
||||
if (audioVideoConfFactoryUri) {
|
||||
parameters->setConferenceFactoryAddress(audioVideoConfFactoryUri);
|
||||
parameters->setSubject("Meeting");
|
||||
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 {
|
||||
parameters->setSubject("Local meeting");
|
||||
}
|
||||
parameters->enableVideo(enablingVideo);
|
||||
conference = core->createConferenceWithParams(parameters);
|
||||
}
|
||||
|
||||
std::list<std::shared_ptr<linphone::Address>> allLinphoneAddresses;
|
||||
std::list<std::shared_ptr<linphone::Address>> newCalls;
|
||||
std::list<std::shared_ptr<linphone::Call>> runningCallsToAdd;
|
||||
|
||||
for (auto call : currentCalls) {
|
||||
if (!call->getConference()) {
|
||||
runningCallsToAdd.push_back(call);
|
||||
conference->addParticipants(currentCalls);
|
||||
}
|
||||
}
|
||||
|
||||
// 1) Add running calls
|
||||
if (runningCallsToAdd.size() > 0) {
|
||||
conference->addParticipants(runningCallsToAdd);
|
||||
}
|
||||
|
||||
// emit lUpdate();
|
||||
});
|
||||
});
|
||||
|
||||
mModelConnection->makeConnectToModel(&CoreModel::firstCallStarted,
|
||||
[this]() { mModelConnection->invokeToCore([this]() { lUpdate(); }); });
|
||||
[this]() { mModelConnection->invokeToCore([this]() { lUpdate(); }); });
|
||||
mModelConnection->makeConnectToModel(&CoreModel::lastCallEnded, [this]() {
|
||||
mModelConnection->invokeToCore([this]() {
|
||||
setHaveCall(false);
|
||||
|
|
@ -158,12 +151,8 @@ CallGui *CallList::getCurrentCall() const {
|
|||
else return nullptr;
|
||||
}
|
||||
|
||||
void CallList::setCurrentCall(CallGui* callGui) {
|
||||
auto callCore = callGui ? callGui->mCore : nullptr;
|
||||
if (mCurrentCall != callCore) {
|
||||
mCurrentCall = callCore;
|
||||
emit currentCallChanged();
|
||||
}
|
||||
void CallList::setCurrentCall(CallGui *callGui) {
|
||||
setCurrentCallCore(callGui ? callGui->mCore : nullptr);
|
||||
}
|
||||
|
||||
void CallList::setCurrentCallCore(QSharedPointer<CallCore> call) {
|
||||
|
|
@ -184,16 +173,21 @@ void CallList::setHaveCall(bool haveCall) {
|
|||
}
|
||||
}
|
||||
|
||||
QSharedPointer<CallCore> CallList::getNextCall() const {
|
||||
QSharedPointer<CallCore> call;
|
||||
QSharedPointer<CallCore> CallList::getNextCall() {
|
||||
auto currentCall = getCurrentCallCore();
|
||||
for (auto it = mList.rbegin(); !call && it != mList.rend(); ++it) {
|
||||
if (*it != currentCall) {
|
||||
call = it->objectCast<CallCore>();
|
||||
}
|
||||
for (auto &item : getSharedList<CallCore>()) {
|
||||
if (item != currentCall) return item;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return call;
|
||||
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() {
|
||||
|
|
@ -202,21 +196,26 @@ void CallList::onStateChanged() {
|
|||
case LinphoneEnums::CallState::StreamsRunning:
|
||||
case LinphoneEnums::CallState::Resuming: {
|
||||
auto sharedCall = get(call);
|
||||
setCurrentCallCore(sharedCall.objectCast<CallCore>());
|
||||
setCurrentCallCore(sharedCall ? sharedCall.objectCast<CallCore>() : nullptr);
|
||||
break;
|
||||
}
|
||||
case LinphoneEnums::CallState::Released: {
|
||||
auto sharedCall = get(call);
|
||||
auto currentCall = getCurrentCallCore();
|
||||
// Update current call
|
||||
if (sharedCall == currentCall) {
|
||||
// Unpause the next call. The current call will change on resume.
|
||||
// Assumption: All calls that are not the current are paused.
|
||||
auto nextCall = getNextCall();
|
||||
if (nextCall) nextCall->lSetPaused(false);
|
||||
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);
|
||||
}
|
||||
sharedCall->disconnect(this);
|
||||
remove(sharedCall);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ class CoreModel;
|
|||
|
||||
class CallList : public ListProxy, public AbstractObject {
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(CallGui* currentCall READ getCurrentCall WRITE setCurrentCall NOTIFY currentCallChanged)
|
||||
Q_PROPERTY(CallGui *currentCall READ getCurrentCall WRITE setCurrentCall NOTIFY currentCallChanged)
|
||||
public:
|
||||
static QSharedPointer<CallList> create();
|
||||
// Create a CallCore and make connections to List.
|
||||
|
|
@ -45,7 +45,7 @@ public:
|
|||
|
||||
CallGui *getCurrentCall() const; // Used for Ui
|
||||
QSharedPointer<CallCore> getCurrentCallCore() const;
|
||||
void setCurrentCall(CallGui* callGui);
|
||||
void setCurrentCall(CallGui *callGui);
|
||||
void setCurrentCallCore(QSharedPointer<CallCore> call);
|
||||
|
||||
bool getHaveCall() const;
|
||||
|
|
@ -53,7 +53,9 @@ public:
|
|||
|
||||
// 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() const;
|
||||
QSharedPointer<CallCore> getNextCall();
|
||||
|
||||
QSharedPointer<CallCore> getFirstIncommingPendingCall();
|
||||
|
||||
virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
|
||||
signals:
|
||||
|
|
|
|||
|
|
@ -25,20 +25,32 @@
|
|||
|
||||
DEFINE_ABSTRACT_OBJECT(CallProxy)
|
||||
|
||||
CallProxy::CallProxy(QObject *parent) : LimitProxy(parent) {
|
||||
CallProxy::CallProxy() : SortFilterProxy() {
|
||||
mShowCurrentCall = true;
|
||||
}
|
||||
|
||||
CallProxy::~CallProxy() {
|
||||
}
|
||||
|
||||
CallGui *CallProxy::getCurrentCall() {
|
||||
auto model = getListModel<CallList>();
|
||||
auto model = qobject_cast<CallList *>(sourceModel());
|
||||
if (!mCurrentCall && model) mCurrentCall = model->getCurrentCall();
|
||||
return mCurrentCall;
|
||||
}
|
||||
|
||||
void CallProxy::setShowCurrentCall(bool show) {
|
||||
if (mShowCurrentCall != show) {
|
||||
mShowCurrentCall = show;
|
||||
emit showCurrentCallChanged();
|
||||
}
|
||||
}
|
||||
|
||||
bool CallProxy::showCurrentCall() const {
|
||||
return mShowCurrentCall;
|
||||
}
|
||||
|
||||
void CallProxy::setCurrentCall(CallGui *call) {
|
||||
getListModel<CallList>()->setCurrentCall(call);
|
||||
qobject_cast<CallList *>(sourceModel())->setCurrentCall(call);
|
||||
}
|
||||
|
||||
// Reset the default account to let UI build its new object if needed.
|
||||
|
|
@ -48,12 +60,12 @@ void CallProxy::resetCurrentCall() {
|
|||
}
|
||||
|
||||
bool CallProxy::getHaveCall() const {
|
||||
auto model = getListModel<CallList>();
|
||||
auto model = qobject_cast<CallList *>(sourceModel());
|
||||
return model ? model->getHaveCall() : false;
|
||||
}
|
||||
|
||||
void CallProxy::setSourceModel(QAbstractItemModel *model) {
|
||||
auto oldCallList = getListModel<CallList>();
|
||||
auto oldCallList = qobject_cast<CallList *>(sourceModel());
|
||||
if (oldCallList) {
|
||||
disconnect(oldCallList);
|
||||
}
|
||||
|
|
@ -63,24 +75,24 @@ void CallProxy::setSourceModel(QAbstractItemModel *model) {
|
|||
connect(newCallList, &CallList::haveCallChanged, this, &CallProxy::haveCallChanged, Qt::QueuedConnection);
|
||||
connect(this, &CallProxy::lMergeAll, newCallList, &CallList::lMergeAll);
|
||||
}
|
||||
setSourceModels(new SortFilterList(model, Qt::AscendingOrder));
|
||||
QSortFilterProxyModel::setSourceModel(model);
|
||||
}
|
||||
|
||||
bool CallProxy::SortFilterList::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const {
|
||||
bool CallProxy::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const {
|
||||
bool show = (mFilterText.isEmpty() || mFilterText == "*");
|
||||
auto callList = qobject_cast<CallList *>(sourceModel());
|
||||
auto call = callList->getAt<CallCore>(sourceRow);
|
||||
if (!mShowCurrentCall && call == callList->getCurrentCallCore()) return false;
|
||||
if (!show) {
|
||||
QRegularExpression search(QRegularExpression::escape(mFilterText),
|
||||
QRegularExpression::CaseInsensitiveOption |
|
||||
QRegularExpression::UseUnicodePropertiesOption);
|
||||
auto call = qobject_cast<CallList *>(sourceModel())->getAt<CallCore>(sourceRow);
|
||||
|
||||
show = call->getRemoteAddress().contains(search);
|
||||
}
|
||||
|
||||
return show;
|
||||
}
|
||||
|
||||
bool CallProxy::SortFilterList::lessThan(const QModelIndex &sourceLeft, const QModelIndex &sourceRight) const {
|
||||
bool CallProxy::lessThan(const QModelIndex &sourceLeft, const QModelIndex &sourceRight) const {
|
||||
auto l = getItemAtSource<CallList, CallCore>(sourceLeft.row());
|
||||
auto r = getItemAtSource<CallList, CallCore>(sourceRight.row());
|
||||
|
||||
|
|
|
|||
|
|
@ -28,15 +28,14 @@
|
|||
|
||||
// =============================================================================
|
||||
|
||||
class CallProxy : public LimitProxy, public AbstractObject {
|
||||
class CallProxy : public SortFilterProxy, public AbstractObject {
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(CallGui *currentCall READ getCurrentCall WRITE setCurrentCall NOTIFY currentCallChanged)
|
||||
Q_PROPERTY(bool haveCall READ getHaveCall NOTIFY haveCallChanged)
|
||||
Q_PROPERTY(bool showCurrentCall READ showCurrentCall WRITE setShowCurrentCall NOTIFY showCurrentCallChanged)
|
||||
|
||||
public:
|
||||
DECLARE_SORTFILTER_CLASS()
|
||||
|
||||
CallProxy(QObject *parent = Q_NULLPTR);
|
||||
CallProxy();
|
||||
~CallProxy();
|
||||
|
||||
// Get a new object from List or give the stored one.
|
||||
|
|
@ -48,15 +47,23 @@ public:
|
|||
|
||||
bool getHaveCall() const;
|
||||
|
||||
void setShowCurrentCall(bool show);
|
||||
bool showCurrentCall() const;
|
||||
|
||||
void setSourceModel(QAbstractItemModel *sourceModel) override;
|
||||
|
||||
virtual bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override;
|
||||
virtual bool lessThan(const QModelIndex &sourceLeft, const QModelIndex &sourceRight) const override;
|
||||
|
||||
signals:
|
||||
void lMergeAll();
|
||||
void currentCallChanged();
|
||||
void haveCallChanged();
|
||||
void showCurrentCallChanged();
|
||||
|
||||
protected:
|
||||
CallGui *mCurrentCall = nullptr; // When null, a new UI object is build from List
|
||||
bool mShowCurrentCall = false;
|
||||
|
||||
DECLARE_ABSTRACT_OBJECT
|
||||
};
|
||||
|
|
|
|||
|
|
@ -71,10 +71,12 @@ QQuickFramebufferObject::Renderer *PreviewManager::subscribe(const CameraGui *ca
|
|||
App::postModelBlock([&renderer, isFirst = (itCandidate == mCandidates.begin()),
|
||||
name = itCandidate->first->getQmlName()]() {
|
||||
renderer =
|
||||
(QQuickFramebufferObject::Renderer *)CoreModel::getInstance()->getCore()->createNativePreviewWindowId();
|
||||
(QQuickFramebufferObject::Renderer *)CoreModel::getInstance()->getCore()->createNativePreviewWindowId(
|
||||
nullptr);
|
||||
if (!renderer) { // TODO debug
|
||||
renderer =
|
||||
(QQuickFramebufferObject::Renderer *)CoreModel::getInstance()->getCore()->createNativePreviewWindowId();
|
||||
(QQuickFramebufferObject::Renderer *)CoreModel::getInstance()->getCore()->createNativePreviewWindowId(
|
||||
nullptr);
|
||||
}
|
||||
if (isFirst) {
|
||||
lDebug() << "[PreviewManager] " << name << " Set Native Preview Id with " << renderer;
|
||||
|
|
|
|||
686
Linphone/core/chat/ChatCore.cpp
Normal file
686
Linphone/core/chat/ChatCore.cpp
Normal 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);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
236
Linphone/core/chat/ChatCore.hpp
Normal file
236
Linphone/core/chat/ChatCore.hpp
Normal 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
|
||||
38
Linphone/core/chat/ChatGui.cpp
Normal file
38
Linphone/core/chat/ChatGui.cpp
Normal 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();
|
||||
}
|
||||
41
Linphone/core/chat/ChatGui.hpp
Normal file
41
Linphone/core/chat/ChatGui.hpp
Normal 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
|
||||
209
Linphone/core/chat/ChatList.cpp
Normal file
209
Linphone/core/chat/ChatList.cpp
Normal 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();
|
||||
}
|
||||
60
Linphone/core/chat/ChatList.hpp
Normal file
60
Linphone/core/chat/ChatList.hpp
Normal 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
|
||||
81
Linphone/core/chat/ChatProxy.cpp
Normal file
81
Linphone/core/chat/ChatProxy.cpp
Normal 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;
|
||||
}
|
||||
50
Linphone/core/chat/ChatProxy.hpp
Normal file
50
Linphone/core/chat/ChatProxy.hpp
Normal 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
|
||||
127
Linphone/core/chat/files/ChatMessageFileList.cpp
Normal file
127
Linphone/core/chat/files/ChatMessageFileList.cpp
Normal 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();
|
||||
}
|
||||
61
Linphone/core/chat/files/ChatMessageFileList.hpp
Normal file
61
Linphone/core/chat/files/ChatMessageFileList.hpp
Normal 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
|
||||
65
Linphone/core/chat/files/ChatMessageFileProxy.cpp
Normal file
65
Linphone/core/chat/files/ChatMessageFileProxy.cpp
Normal 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;
|
||||
}
|
||||
56
Linphone/core/chat/files/ChatMessageFileProxy.hpp
Normal file
56
Linphone/core/chat/files/ChatMessageFileProxy.hpp
Normal 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
|
||||
694
Linphone/core/chat/message/ChatMessageCore.cpp
Normal file
694
Linphone/core/chat/message/ChatMessageCore.cpp
Normal 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);
|
||||
}
|
||||
246
Linphone/core/chat/message/ChatMessageCore.hpp
Normal file
246
Linphone/core/chat/message/ChatMessageCore.hpp
Normal 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_
|
||||
39
Linphone/core/chat/message/ChatMessageGui.cpp
Normal file
39
Linphone/core/chat/message/ChatMessageGui.cpp
Normal 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();
|
||||
}
|
||||
41
Linphone/core/chat/message/ChatMessageGui.hpp
Normal file
41
Linphone/core/chat/message/ChatMessageGui.hpp
Normal 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
|
||||
173
Linphone/core/chat/message/EventLogCore.cpp
Normal file
173
Linphone/core/chat/message/EventLogCore.cpp
Normal file
|
|
@ -0,0 +1,173 @@
|
|||
/*
|
||||
* 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 "EventLogCore.hpp"
|
||||
#include "core/App.hpp"
|
||||
#include "core/chat/ChatCore.hpp"
|
||||
#include "model/chat/message/EventLogModel.hpp"
|
||||
#include "model/tool/ToolModel.hpp"
|
||||
|
||||
DEFINE_ABSTRACT_OBJECT(EventLogCore)
|
||||
|
||||
QSharedPointer<EventLogCore> EventLogCore::create(const std::shared_ptr<const linphone::EventLog> &eventLog,
|
||||
const std::shared_ptr<linphone::ChatRoom> &chatRoom) {
|
||||
auto sharedPointer = QSharedPointer<EventLogCore>(new EventLogCore(eventLog, chatRoom), &QObject::deleteLater);
|
||||
sharedPointer->setSelf(sharedPointer);
|
||||
sharedPointer->moveToThread(App::getInstance()->thread());
|
||||
return sharedPointer;
|
||||
}
|
||||
|
||||
EventLogCore::EventLogCore(const std::shared_ptr<const linphone::EventLog> &eventLog,
|
||||
const std::shared_ptr<linphone::ChatRoom> &chatRoom) {
|
||||
mustBeInLinphoneThread(getClassName());
|
||||
App::getInstance()->mEngine->setObjectOwnership(this, QQmlEngine::CppOwnership);
|
||||
mEventLogType = LinphoneEnums::fromLinphone(eventLog->getType());
|
||||
mEventLogModel = Utils::makeQObject_ptr<EventLogModel>(eventLog);
|
||||
mTimestamp = QDateTime::fromMSecsSinceEpoch(eventLog->getCreationTime() * 1000);
|
||||
auto chatmessage = eventLog->getChatMessage();
|
||||
if (chatmessage) {
|
||||
mChatMessageCore = ChatMessageCore::create(chatmessage);
|
||||
mEventId = Utils::coreStringToAppString(chatmessage->getMessageId());
|
||||
mTimestamp = QDateTime::fromSecsSinceEpoch(chatmessage->getTime());
|
||||
} else if (eventLog->getCallLog()) {
|
||||
mCallHistoryCore = CallHistoryCore::create(eventLog->getCallLog());
|
||||
mEventId = Utils::coreStringToAppString(eventLog->getCallLog()->getCallId());
|
||||
}
|
||||
if (mEventId.isEmpty()) { // getNotifyId
|
||||
QString type = QString::fromLatin1(
|
||||
QMetaEnum::fromType<LinphoneEnums::EventLogType>().valueToKey(static_cast<int>(mEventLogType)));
|
||||
mEventId = type + QString::number(static_cast<qint64>(eventLog->getCreationTime()));
|
||||
computeEvent(eventLog, chatRoom);
|
||||
}
|
||||
}
|
||||
|
||||
EventLogCore::~EventLogCore() {
|
||||
}
|
||||
|
||||
void EventLogCore::setSelf(QSharedPointer<EventLogCore> me) {
|
||||
}
|
||||
|
||||
QString EventLogCore::getEventLogId() {
|
||||
return mEventId;
|
||||
}
|
||||
|
||||
QSharedPointer<ChatMessageCore> EventLogCore::getChatMessageCore() {
|
||||
return mChatMessageCore;
|
||||
}
|
||||
ChatMessageGui *EventLogCore::getChatMessageGui() {
|
||||
return mChatMessageCore ? new ChatMessageGui(mChatMessageCore) : nullptr;
|
||||
}
|
||||
|
||||
QSharedPointer<CallHistoryCore> EventLogCore::getCallHistoryCore() {
|
||||
return mCallHistoryCore;
|
||||
}
|
||||
|
||||
ChatMessageCore *EventLogCore::getChatMessageCorePointer() {
|
||||
return mChatMessageCore.get();
|
||||
}
|
||||
CallHistoryCore *EventLogCore::getCallHistoryCorePointer() {
|
||||
return mCallHistoryCore.get();
|
||||
}
|
||||
|
||||
QDateTime EventLogCore::getTimestamp() const {
|
||||
return mTimestamp;
|
||||
}
|
||||
|
||||
std::shared_ptr<EventLogModel> EventLogCore::getModel() const {
|
||||
return mEventLogModel;
|
||||
}
|
||||
|
||||
// Events (other than ChatMessage and CallLog which are handled in their respective Core)
|
||||
|
||||
void EventLogCore::computeEvent(const std::shared_ptr<const linphone::EventLog> &eventLog,
|
||||
const std::shared_ptr<linphone::ChatRoom> &chatRoom) {
|
||||
mustBeInLinphoneThread(getClassName());
|
||||
mHandled = true;
|
||||
mImportant = false;
|
||||
mEphemeralRelated = false;
|
||||
|
||||
auto participantAddress = eventLog->getParticipantAddress() ? eventLog->getParticipantAddress() : nullptr;
|
||||
|
||||
switch (eventLog->getType()) {
|
||||
case linphone::EventLog::Type::ConferenceCreated:
|
||||
if (chatRoom->hasCapability((int)linphone::ChatRoom::Capabilities::OneToOne) &&
|
||||
!chatRoom->hasCapability((int)linphone::ChatRoom::Capabilities::Conference))
|
||||
mHandled = false;
|
||||
mEventDetails = tr("conference_created_event");
|
||||
break;
|
||||
case linphone::EventLog::Type::ConferenceTerminated:
|
||||
if (chatRoom->hasCapability((int)linphone::ChatRoom::Capabilities::OneToOne) &&
|
||||
!chatRoom->hasCapability((int)linphone::ChatRoom::Capabilities::Conference))
|
||||
mHandled = false;
|
||||
mEventDetails = tr("conference_created_terminated");
|
||||
mImportant = true;
|
||||
break;
|
||||
case linphone::EventLog::Type::ConferenceParticipantAdded:
|
||||
mEventDetails = tr("conference_participant_added_event").arg(ToolModel::getDisplayName(participantAddress));
|
||||
break;
|
||||
case linphone::EventLog::Type::ConferenceParticipantRemoved:
|
||||
mEventDetails =
|
||||
tr("conference_participant_removed_event").arg(ToolModel::getDisplayName(participantAddress));
|
||||
mImportant = true;
|
||||
break;
|
||||
case linphone::EventLog::Type::ConferenceSecurityEvent: {
|
||||
if (eventLog->getSecurityEventType() == linphone::EventLog::SecurityEventType::SecurityLevelDowngraded) {
|
||||
auto faultyParticipant = eventLog->getSecurityEventFaultyDeviceAddress()
|
||||
? eventLog->getSecurityEventFaultyDeviceAddress()
|
||||
: nullptr;
|
||||
if (faultyParticipant)
|
||||
mEventDetails = tr("conference_security_event").arg(ToolModel::getDisplayName(faultyParticipant));
|
||||
else if (participantAddress)
|
||||
mEventDetails = tr("conference_security_event").arg(ToolModel::getDisplayName(participantAddress));
|
||||
mImportant = true;
|
||||
} else mHandled = false;
|
||||
break;
|
||||
}
|
||||
case linphone::EventLog::Type::ConferenceEphemeralMessageEnabled:
|
||||
mEphemeralRelated = true;
|
||||
mEventDetails = tr("conference_ephemeral_message_enabled_event")
|
||||
.arg(Utils::getEphemeralFormatedTime(eventLog->getEphemeralMessageLifetime()));
|
||||
break;
|
||||
case linphone::EventLog::Type::ConferenceEphemeralMessageLifetimeChanged:
|
||||
mEphemeralRelated = true;
|
||||
mHandled = eventLog->getEphemeralMessageLifetime() != 0; // Disabled is sent in case of 0.
|
||||
mEventDetails = tr("conference_ephemeral_message_lifetime_changed_event")
|
||||
.arg(Utils::getEphemeralFormatedTime(eventLog->getEphemeralMessageLifetime()));
|
||||
break;
|
||||
case linphone::EventLog::Type::ConferenceEphemeralMessageDisabled:
|
||||
mEphemeralRelated = true;
|
||||
mEventDetails = tr("conference_ephemeral_message_disabled_event");
|
||||
mImportant = true;
|
||||
break;
|
||||
case linphone::EventLog::Type::ConferenceSubjectChanged:
|
||||
mEventDetails = tr("conference_subject_changed_event").arg(QString::fromStdString(eventLog->getSubject()));
|
||||
break;
|
||||
case linphone::EventLog::Type::ConferenceParticipantSetAdmin:
|
||||
mEventDetails =
|
||||
tr("conference_participant_set_admin_event").arg(ToolModel::getDisplayName(participantAddress));
|
||||
break;
|
||||
case linphone::EventLog::Type::ConferenceParticipantUnsetAdmin:
|
||||
mEventDetails =
|
||||
tr("conference_participant_unset_admin_event").arg(ToolModel::getDisplayName(participantAddress));
|
||||
break;
|
||||
default:
|
||||
mHandled = false;
|
||||
}
|
||||
}
|
||||
96
Linphone/core/chat/message/EventLogCore.hpp
Normal file
96
Linphone/core/chat/message/EventLogCore.hpp
Normal file
|
|
@ -0,0 +1,96 @@
|
|||
/*
|
||||
* 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 EVENT_LOG_CORE_H_
|
||||
#define EVENT_LOG_CORE_H_
|
||||
|
||||
#include "ChatMessageCore.hpp"
|
||||
#include "core/call-history/CallHistoryCore.hpp"
|
||||
#include "core/conference/ConferenceInfoCore.hpp"
|
||||
#include "core/conference/ConferenceInfoGui.hpp"
|
||||
#include "model/chat/message/ChatMessageModel.hpp"
|
||||
#include "tool/AbstractObject.hpp"
|
||||
#include "tool/LinphoneEnums.hpp"
|
||||
#include "tool/thread/SafeConnection.hpp"
|
||||
#include <QObject>
|
||||
#include <QSharedPointer>
|
||||
|
||||
#include <linphone++/linphone.hh>
|
||||
|
||||
class ChatMessageCore;
|
||||
class ChatMessageGui;
|
||||
class EventLogModel;
|
||||
|
||||
class EventLogCore : public QObject, public AbstractObject {
|
||||
Q_OBJECT
|
||||
|
||||
Q_PROPERTY(LinphoneEnums::EventLogType type MEMBER mEventLogType CONSTANT)
|
||||
Q_PROPERTY(ChatMessageGui *chatMessageGui READ getChatMessageGui CONSTANT)
|
||||
// Q_PROPERTY(NotifyCore *notification MEMBER mNotifyCore CONSTANT)
|
||||
Q_PROPERTY(CallHistoryCore *callLog READ getCallHistoryCorePointer CONSTANT)
|
||||
Q_PROPERTY(bool important MEMBER mImportant CONSTANT)
|
||||
Q_PROPERTY(bool handled MEMBER mHandled CONSTANT)
|
||||
Q_PROPERTY(QString eventDetails MEMBER mEventDetails CONSTANT)
|
||||
Q_PROPERTY(QDateTime timestamp READ getTimestamp CONSTANT)
|
||||
|
||||
public:
|
||||
static QSharedPointer<EventLogCore> create(const std::shared_ptr<const linphone::EventLog> &eventLog,
|
||||
const std::shared_ptr<linphone::ChatRoom> &chatRoom);
|
||||
EventLogCore(const std::shared_ptr<const linphone::EventLog> &eventLog,
|
||||
const std::shared_ptr<linphone::ChatRoom> &chatRoom);
|
||||
~EventLogCore();
|
||||
void setSelf(QSharedPointer<EventLogCore> me);
|
||||
QString getEventLogId();
|
||||
QSharedPointer<ChatMessageCore> getChatMessageCore();
|
||||
ChatMessageGui *getChatMessageGui();
|
||||
QSharedPointer<CallHistoryCore> getCallHistoryCore();
|
||||
|
||||
QDateTime getTimestamp() const;
|
||||
|
||||
bool isHandled() const {
|
||||
return mHandled;
|
||||
}
|
||||
bool isEphemeralRelated() const {
|
||||
return mEphemeralRelated;
|
||||
}
|
||||
|
||||
std::shared_ptr<EventLogModel> getModel() const;
|
||||
|
||||
private:
|
||||
DECLARE_ABSTRACT_OBJECT
|
||||
QString mEventId;
|
||||
|
||||
QSharedPointer<ChatMessageCore> mChatMessageCore = nullptr;
|
||||
QSharedPointer<CallHistoryCore> mCallHistoryCore = nullptr;
|
||||
LinphoneEnums::EventLogType mEventLogType;
|
||||
bool mHandled = false;
|
||||
bool mImportant;
|
||||
bool mEphemeralRelated;
|
||||
QString mEventDetails;
|
||||
QDateTime mTimestamp;
|
||||
|
||||
ChatMessageCore *getChatMessageCorePointer();
|
||||
CallHistoryCore *getCallHistoryCorePointer();
|
||||
std::shared_ptr<EventLogModel> mEventLogModel;
|
||||
void computeEvent(const std::shared_ptr<const linphone::EventLog> &eventLog,
|
||||
const std::shared_ptr<linphone::ChatRoom> &chatRoom);
|
||||
};
|
||||
|
||||
#endif // EventLogCore_H_
|
||||
38
Linphone/core/chat/message/EventLogGui.cpp
Normal file
38
Linphone/core/chat/message/EventLogGui.cpp
Normal 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 "EventLogGui.hpp"
|
||||
#include "core/App.hpp"
|
||||
|
||||
DEFINE_ABSTRACT_OBJECT(EventLogGui)
|
||||
|
||||
EventLogGui::EventLogGui(QSharedPointer<EventLogCore> core) {
|
||||
App::getInstance()->mEngine->setObjectOwnership(this, QQmlEngine::JavaScriptOwnership);
|
||||
mCore = core;
|
||||
if (isInLinphoneThread()) moveToThread(App::getInstance()->thread());
|
||||
}
|
||||
|
||||
EventLogGui::~EventLogGui() {
|
||||
mustBeInMainThread("~" + getClassName());
|
||||
}
|
||||
|
||||
EventLogCore *EventLogGui::getCore() const {
|
||||
return mCore.get();
|
||||
}
|
||||
41
Linphone/core/chat/message/EventLogGui.hpp
Normal file
41
Linphone/core/chat/message/EventLogGui.hpp
Normal 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 EVENT_LOG_GUI_H_
|
||||
#define EVENT_LOG_GUI_H_
|
||||
|
||||
#include "EventLogCore.hpp"
|
||||
#include <QObject>
|
||||
#include <QSharedPointer>
|
||||
|
||||
class EventLogGui : public QObject, public AbstractObject {
|
||||
Q_OBJECT
|
||||
|
||||
Q_PROPERTY(EventLogCore *core READ getCore CONSTANT)
|
||||
|
||||
public:
|
||||
EventLogGui(QSharedPointer<EventLogCore> core);
|
||||
~EventLogGui();
|
||||
EventLogCore *getCore() const;
|
||||
QSharedPointer<EventLogCore> mCore;
|
||||
DECLARE_ABSTRACT_OBJECT
|
||||
};
|
||||
|
||||
#endif
|
||||
391
Linphone/core/chat/message/EventLogList.cpp
Normal file
391
Linphone/core/chat/message/EventLogList.cpp
Normal file
|
|
@ -0,0 +1,391 @@
|
|||
/*
|
||||
* 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 "EventLogList.hpp"
|
||||
#include "ChatMessageCore.hpp"
|
||||
#include "ChatMessageGui.hpp"
|
||||
#include "EventLogGui.hpp"
|
||||
#include "core/App.hpp"
|
||||
#include "core/call-history/CallHistoryGui.hpp"
|
||||
#include "core/chat/ChatCore.hpp"
|
||||
#include "core/chat/ChatGui.hpp"
|
||||
#include "model/chat/message/EventLogModel.hpp"
|
||||
#include <QSharedPointer>
|
||||
#include <linphone++/linphone.hh>
|
||||
|
||||
// =============================================================================
|
||||
|
||||
DEFINE_ABSTRACT_OBJECT(EventLogList)
|
||||
|
||||
QSharedPointer<EventLogList> EventLogList::create() {
|
||||
auto model = QSharedPointer<EventLogList>(new EventLogList(), &QObject::deleteLater);
|
||||
model->moveToThread(App::getInstance()->thread());
|
||||
model->setSelf(model);
|
||||
return model;
|
||||
}
|
||||
|
||||
EventLogList::EventLogList(QObject *parent) : ListProxy(parent) {
|
||||
mustBeInMainThread(getClassName());
|
||||
App::getInstance()->mEngine->setObjectOwnership(this, QQmlEngine::CppOwnership);
|
||||
}
|
||||
|
||||
EventLogList::~EventLogList() {
|
||||
mustBeInMainThread("~" + getClassName());
|
||||
}
|
||||
|
||||
ChatGui *EventLogList::getChat() const {
|
||||
if (mChatCore) return new ChatGui(mChatCore);
|
||||
else return nullptr;
|
||||
}
|
||||
|
||||
QSharedPointer<ChatCore> EventLogList::getChatCore() const {
|
||||
return mChatCore;
|
||||
}
|
||||
|
||||
void EventLogList::disconnectItem(const QSharedPointer<EventLogCore> &item) {
|
||||
auto message = item->getChatMessageCore();
|
||||
if (message) {
|
||||
disconnect(message.get(), &ChatMessageCore::isReadChanged, this, nullptr);
|
||||
disconnect(message.get(), &ChatMessageCore::deleted, this, nullptr);
|
||||
disconnect(message.get(), &ChatMessageCore::edited, this, nullptr);
|
||||
disconnect(message.get(), &ChatMessageCore::isRetractedChanged, this, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
void EventLogList::connectItem(const QSharedPointer<EventLogCore> &item) {
|
||||
auto message = item->getChatMessageCore();
|
||||
if (message) {
|
||||
connect(message.get(), &ChatMessageCore::isReadChanged, this, [this] {
|
||||
if (mChatCore) emit mChatCore->lUpdateUnreadCount();
|
||||
});
|
||||
connect(message.get(), &ChatMessageCore::deleted, this, [this, item] {
|
||||
if (mChatCore) emit mChatCore->lUpdateLastMessage();
|
||||
remove(item);
|
||||
});
|
||||
connect(message.get(), &ChatMessageCore::isRetractedChanged, this, [this, item] {
|
||||
if (mChatCore) emit mChatCore->lUpdateUnreadCount();
|
||||
});
|
||||
connect(message.get(), &ChatMessageCore::edited, this, [this, item] {
|
||||
auto eventLogModel = item->getModel();
|
||||
mCoreModelConnection->invokeToModel([this, eventLogModel, item]() {
|
||||
auto chatRoom = mChatCore->getModel()->getMonitor();
|
||||
auto newEventLog = EventLogCore::create(eventLogModel->getEventLog(), chatRoom);
|
||||
bool wasLastMessage =
|
||||
mChatCore->getModel()->getLastChatMessage() == eventLogModel->getEventLog()->getChatMessage();
|
||||
mCoreModelConnection->invokeToCore([this, newEventLog, wasLastMessage, item] {
|
||||
connectItem(newEventLog);
|
||||
replace(item, newEventLog);
|
||||
if (wasLastMessage) mChatCore->setLastMessage(newEventLog->getChatMessageCore());
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void EventLogList::setChatCore(QSharedPointer<ChatCore> core) {
|
||||
if (mChatCore != core) {
|
||||
if (mChatCore) {
|
||||
disconnect(mChatCore.get(), &ChatCore::eventsInserted, this, nullptr);
|
||||
disconnect(mChatCore.get(), &ChatCore::eventListCleared, this, nullptr);
|
||||
}
|
||||
mChatCore = core;
|
||||
if (mChatCore) {
|
||||
connect(mChatCore.get(), &ChatCore::eventListCleared, this, [this] { resetData(); });
|
||||
connect(mChatCore.get(), &ChatCore::eventsInserted, this, [this](QList<QSharedPointer<EventLogCore>> list) {
|
||||
auto eventsList = getSharedList<EventLogCore>();
|
||||
for (auto &event : list) {
|
||||
auto it = std::find_if(eventsList.begin(), eventsList.end(),
|
||||
[event](const QSharedPointer<EventLogCore> item) { return item == event; });
|
||||
if (it == eventsList.end()) {
|
||||
connectItem(event);
|
||||
prepend(event);
|
||||
int index;
|
||||
get(event.get(), &index);
|
||||
if (event->getChatMessageCore() && !event->getChatMessageCore()->isRemoteMessage()) {
|
||||
emit eventInsertedByUser(index);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
lUpdate();
|
||||
// setIsUpdating(false);
|
||||
emit chatGuiChanged();
|
||||
}
|
||||
}
|
||||
|
||||
void EventLogList::setChatGui(ChatGui *chat) {
|
||||
auto chatCore = chat ? chat->mCore : nullptr;
|
||||
setChatCore(chatCore);
|
||||
}
|
||||
|
||||
void EventLogList::setDisplayItemsStep(int displayItemsStep) {
|
||||
if (mDisplayItemsStep != displayItemsStep) {
|
||||
mDisplayItemsStep = displayItemsStep;
|
||||
emit displayItemsStepChanged();
|
||||
}
|
||||
}
|
||||
|
||||
void EventLogList::markIndexAsRead(int index) {
|
||||
if (index < mList.count()) {
|
||||
auto eventLog = mList[index].objectCast<EventLogCore>();
|
||||
if (eventLog && eventLog->getChatMessageCore()) eventLog->getChatMessageCore()->lMarkAsRead();
|
||||
}
|
||||
}
|
||||
|
||||
void EventLogList::displayMore() {
|
||||
auto loadMoreItems = [this] {
|
||||
if (!mChatCore) return;
|
||||
auto chatModel = mChatCore->getModel();
|
||||
if (!chatModel) return;
|
||||
mCoreModelConnection->invokeToModel([this, chatModel]() {
|
||||
mustBeInLinphoneThread(log().arg(Q_FUNC_INFO));
|
||||
int maxSize = chatModel->getHistorySizeEvents();
|
||||
int totalItemsCount = mList.count();
|
||||
auto newCount = std::min(totalItemsCount + mDisplayItemsStep, maxSize);
|
||||
if (newCount <= totalItemsCount) {
|
||||
return;
|
||||
}
|
||||
auto linphoneLogs = chatModel->getHistoryRange(totalItemsCount, newCount);
|
||||
QList<QSharedPointer<EventLogCore>> *events = new QList<QSharedPointer<EventLogCore>>();
|
||||
for (auto it : linphoneLogs) {
|
||||
auto model = EventLogCore::create(it, chatModel->getMonitor());
|
||||
if (it->getChatMessage() || model->isHandled()) events->push_front(model);
|
||||
}
|
||||
mCoreModelConnection->invokeToCore([this, events] {
|
||||
int currentCount = mList.count();
|
||||
if (!events->isEmpty()) {
|
||||
for (int i = events->size() - 1; i >= 0; --i) {
|
||||
const auto &ev = events->at(i);
|
||||
connectItem(ev);
|
||||
}
|
||||
add(*events);
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
if (mIsUpdating) {
|
||||
connect(this, &EventLogList::isUpdatingChanged, this, [this, loadMoreItems] {
|
||||
if (!mIsUpdating) {
|
||||
disconnect(this, &EventLogList::isUpdatingChanged, this, nullptr);
|
||||
loadMoreItems();
|
||||
}
|
||||
});
|
||||
return;
|
||||
} else loadMoreItems();
|
||||
}
|
||||
|
||||
void EventLogList::loadMessagesUpTo(std::shared_ptr<linphone::EventLog> event) {
|
||||
mustBeInLinphoneThread(log().arg(Q_FUNC_INFO));
|
||||
auto oldestEventLoaded = mList.count() > 0 ? getAt<EventLogCore>(mList.count() - 1) : nullptr;
|
||||
auto linOldest = oldestEventLoaded
|
||||
? std::const_pointer_cast<linphone::EventLog>(oldestEventLoaded->getModel()->getEventLog())
|
||||
: nullptr;
|
||||
auto chatModel = mChatCore->getModel();
|
||||
assert(chatModel);
|
||||
if (!chatModel) return;
|
||||
int filters = static_cast<int>(linphone::ChatRoom::HistoryFilter::ChatMessage) |
|
||||
static_cast<int>(linphone::ChatRoom::HistoryFilter::InfoNoDevice);
|
||||
auto beforeEvents = chatModel->getHistoryRangeNear(mItemsToLoadBeforeSearchResult, 0, event, filters);
|
||||
auto linphoneLogs = chatModel->getHistoryRangeBetween(event, linOldest, filters);
|
||||
QList<QSharedPointer<EventLogCore>> *events = new QList<QSharedPointer<EventLogCore>>();
|
||||
const auto &linChatRoom = chatModel->getMonitor();
|
||||
for (const auto &it : beforeEvents) {
|
||||
auto model = EventLogCore::create(it, linChatRoom);
|
||||
if (it->getChatMessage() || model->isHandled()) events->push_front(model);
|
||||
}
|
||||
for (const auto &it : linphoneLogs) {
|
||||
auto model = EventLogCore::create(it, linChatRoom);
|
||||
if (it->getChatMessage() || model->isHandled()) events->push_front(model);
|
||||
}
|
||||
mCoreModelConnection->invokeToCore([this, events, event] {
|
||||
for (const auto &e : *events) {
|
||||
connectItem(e);
|
||||
}
|
||||
add(*events);
|
||||
emit messagesLoadedUpTo(event);
|
||||
});
|
||||
}
|
||||
|
||||
int EventLogList::findFirstUnreadIndex() {
|
||||
auto eventList = getSharedList<EventLogCore>();
|
||||
auto it = std::find_if(eventList.rbegin(), eventList.rend(), [](const QSharedPointer<EventLogCore> item) {
|
||||
auto chatmessage = item->getChatMessageCore();
|
||||
return chatmessage && !chatmessage->isRead();
|
||||
});
|
||||
return it == eventList.rend() ? -1 : std::distance(it, eventList.rend()) - 1;
|
||||
}
|
||||
|
||||
void EventLogList::findChatMessageWithFilter(QString filter, int startIndex, bool forward, bool isFirstResearch) {
|
||||
if (mChatCore) {
|
||||
if (isFirstResearch) mLastFoundResult.reset();
|
||||
auto chatModel = mChatCore->getModel();
|
||||
auto startEvent =
|
||||
startIndex >= 0 && startIndex < mList.count() ? mList[startIndex].objectCast<EventLogCore>() : nullptr;
|
||||
lInfo() << log().arg("searching event starting from index") << startIndex << "| event :"
|
||||
<< (startEvent && startEvent->getChatMessageCore() ? startEvent->getChatMessageCore()->getText()
|
||||
: "null")
|
||||
<< "| filter :" << filter;
|
||||
auto startEventModel = startEvent ? startEvent->getModel() : nullptr;
|
||||
mCoreModelConnection->invokeToModel([this, chatModel, startEventModel, filter, forward, isFirstResearch] {
|
||||
auto linStartEvent = startEventModel ? startEventModel->getEventLog() : nullptr;
|
||||
auto eventLog = chatModel->searchMessageByText(filter, linStartEvent, forward);
|
||||
if (!eventLog) {
|
||||
// event not found, search in the entire history
|
||||
lInfo() << log().arg("not found, search in entire history");
|
||||
auto eventLog = chatModel->searchMessageByText(filter, nullptr, forward);
|
||||
}
|
||||
int index = -1;
|
||||
if (eventLog) {
|
||||
lInfo() << log().arg("event with filter found") << eventLog.get();
|
||||
auto eventList = getSharedList<EventLogCore>();
|
||||
auto it = std::find_if(eventList.begin(), eventList.end(),
|
||||
[eventLog](const QSharedPointer<EventLogCore> item) {
|
||||
return item->getModel()->getEventLog() == eventLog;
|
||||
});
|
||||
if (it != eventList.end()) {
|
||||
int index = std::distance(eventList.begin(), it);
|
||||
if (mLastFoundResult && mLastFoundResult == *it) index = -1;
|
||||
mLastFoundResult = *it;
|
||||
mCoreModelConnection->invokeToCore([this, index] { emit messageWithFilterFound(index); });
|
||||
} else {
|
||||
connect(this, &EventLogList::messagesLoadedUpTo, this,
|
||||
[this](std::shared_ptr<linphone::EventLog> event) {
|
||||
auto eventList = getSharedList<EventLogCore>();
|
||||
auto it = std::find_if(eventList.begin(), eventList.end(),
|
||||
[event](const QSharedPointer<EventLogCore> item) {
|
||||
return item->getModel()->getEventLog() == event;
|
||||
});
|
||||
int index = it != eventList.end() ? std::distance(eventList.begin(), it) : -1;
|
||||
if (mLastFoundResult && mLastFoundResult == *it) index = -1;
|
||||
mLastFoundResult = *it;
|
||||
mCoreModelConnection->invokeToCore(
|
||||
[this, index] { emit messageWithFilterFound(index); });
|
||||
});
|
||||
loadMessagesUpTo(eventLog);
|
||||
}
|
||||
} else {
|
||||
lInfo() << log().arg("event not found at all in history");
|
||||
mCoreModelConnection->invokeToCore([this, index] { emit messageWithFilterFound(index); });
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void EventLogList::setSelf(QSharedPointer<EventLogList> me) {
|
||||
mCoreModelConnection = SafeConnection<EventLogList, CoreModel>::create(me, CoreModel::getInstance());
|
||||
|
||||
mCoreModelConnection->makeConnectToCore(&EventLogList::lUpdate, [this]() {
|
||||
mustBeInMainThread(log().arg(Q_FUNC_INFO));
|
||||
if (mIsUpdating) {
|
||||
connect(this, &EventLogList::isUpdatingChanged, this, [this] {
|
||||
if (!mIsUpdating) {
|
||||
disconnect(this, &EventLogList::isUpdatingChanged, this, nullptr);
|
||||
lUpdate();
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
setIsUpdating(true);
|
||||
beginResetModel();
|
||||
for (auto &event : getSharedList<EventLogCore>()) {
|
||||
disconnectItem(event);
|
||||
}
|
||||
mList.clear();
|
||||
if (!mChatCore) {
|
||||
endResetModel();
|
||||
setIsUpdating(false);
|
||||
return;
|
||||
}
|
||||
auto chatModel = mChatCore->getModel();
|
||||
if (!chatModel) {
|
||||
endResetModel();
|
||||
setIsUpdating(false);
|
||||
return;
|
||||
}
|
||||
mCoreModelConnection->invokeToModel([this, chatModel]() {
|
||||
mustBeInLinphoneThread(log().arg(Q_FUNC_INFO));
|
||||
auto linphoneLogs = chatModel->getHistoryRange(0, mDisplayItemsStep);
|
||||
QList<QSharedPointer<EventLogCore>> *events = new QList<QSharedPointer<EventLogCore>>();
|
||||
for (auto it : linphoneLogs) {
|
||||
auto model = EventLogCore::create(it, chatModel->getMonitor());
|
||||
if (it->getChatMessage() || model->isHandled()) events->push_front(model);
|
||||
}
|
||||
mCoreModelConnection->invokeToCore([this, events] {
|
||||
for (auto &event : *events) {
|
||||
connectItem(event);
|
||||
mList.append(event);
|
||||
}
|
||||
endResetModel();
|
||||
setIsUpdating(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
connect(this, &EventLogList::filterChanged, [this](QString filter) {
|
||||
mFilter = filter;
|
||||
lUpdate();
|
||||
});
|
||||
lUpdate();
|
||||
}
|
||||
|
||||
QVariant EventLogList::data(const QModelIndex &index, int role) const {
|
||||
int row = index.row();
|
||||
if (!index.isValid() || row < 0 || row >= mList.count()) return QVariant();
|
||||
|
||||
auto core = mList[row].objectCast<EventLogCore>();
|
||||
if (core->getChatMessageCore()) {
|
||||
switch (role) {
|
||||
case Qt::DisplayRole:
|
||||
return QVariant::fromValue(new EventLogGui(core));
|
||||
case Qt::DisplayRole + 1:
|
||||
return "chatMessage";
|
||||
}
|
||||
} else if (core->getCallHistoryCore()) {
|
||||
switch (role) {
|
||||
case Qt::DisplayRole:
|
||||
return QVariant::fromValue(new EventLogGui(core));
|
||||
case Qt::DisplayRole + 1:
|
||||
return "callLog";
|
||||
}
|
||||
} else if (core->isEphemeralRelated()) {
|
||||
switch (role) {
|
||||
case Qt::DisplayRole:
|
||||
return QVariant::fromValue(new EventLogGui(core));
|
||||
case Qt::DisplayRole + 1:
|
||||
return "ephemeralEvent";
|
||||
}
|
||||
} else {
|
||||
switch (role) {
|
||||
case Qt::DisplayRole:
|
||||
return QVariant::fromValue(new EventLogGui(core));
|
||||
case Qt::DisplayRole + 1:
|
||||
return "event";
|
||||
}
|
||||
}
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
QHash<int, QByteArray> EventLogList::roleNames() const {
|
||||
QHash<int, QByteArray> roles;
|
||||
roles[Qt::DisplayRole] = "modelData";
|
||||
roles[Qt::DisplayRole + 1] = "eventType";
|
||||
return roles;
|
||||
}
|
||||
88
Linphone/core/chat/message/EventLogList.hpp
Normal file
88
Linphone/core/chat/message/EventLogList.hpp
Normal 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 EVENT_LOG_LIST_H_
|
||||
#define EVENT_LOG_LIST_H_
|
||||
|
||||
#include "core/proxy/ListProxy.hpp"
|
||||
#include "tool/AbstractObject.hpp"
|
||||
#include "tool/thread/SafeConnection.hpp"
|
||||
#include <QLocale>
|
||||
|
||||
class EventLogGui;
|
||||
class EventLogCore;
|
||||
class ChatCore;
|
||||
class ChatGui;
|
||||
class ChatModel;
|
||||
// =============================================================================
|
||||
|
||||
class EventLogList : public ListProxy, public AbstractObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
static QSharedPointer<EventLogList> create();
|
||||
EventLogList(QObject *parent = Q_NULLPTR);
|
||||
~EventLogList();
|
||||
|
||||
QSharedPointer<ChatCore> getChatCore() const;
|
||||
ChatGui *getChat() const;
|
||||
void setChatCore(QSharedPointer<ChatCore> core);
|
||||
void setChatGui(ChatGui *chat);
|
||||
|
||||
void connectItem(const QSharedPointer<EventLogCore> &item);
|
||||
void disconnectItem(const QSharedPointer<EventLogCore> &item);
|
||||
|
||||
void loadMessagesUpTo(std::shared_ptr<linphone::EventLog> event);
|
||||
|
||||
void setLastFoundResult(const QSharedPointer<EventLogCore> &eventLog);
|
||||
|
||||
int findFirstUnreadIndex();
|
||||
|
||||
void markIndexAsRead(int index);
|
||||
|
||||
void displayMore();
|
||||
void setDisplayItemsStep(int displayItemsStep);
|
||||
|
||||
void findChatMessageWithFilter(QString filter, int startIndex, bool forward = true, bool isFirstResearch = true);
|
||||
|
||||
void setSelf(QSharedPointer<EventLogList> me);
|
||||
virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
|
||||
QHash<int, QByteArray> roleNames() const override;
|
||||
|
||||
signals:
|
||||
void lUpdate();
|
||||
void filterChanged(QString filter);
|
||||
void eventInsertedByUser(int index);
|
||||
void messageWithFilterFound(int index);
|
||||
void listAboutToBeReset();
|
||||
void chatGuiChanged();
|
||||
void displayItemsStepChanged();
|
||||
void messagesLoadedUpTo(std::shared_ptr<linphone::EventLog> event);
|
||||
|
||||
private:
|
||||
QString mFilter;
|
||||
QSharedPointer<ChatCore> mChatCore;
|
||||
QSharedPointer<SafeConnection<EventLogList, CoreModel>> mCoreModelConnection;
|
||||
int mDisplayItemsStep = 0;
|
||||
int mItemsToLoadBeforeSearchResult = 3;
|
||||
QSharedPointer<EventLogCore> mLastFoundResult;
|
||||
DECLARE_ABSTRACT_OBJECT
|
||||
};
|
||||
|
||||
#endif
|
||||
208
Linphone/core/chat/message/EventLogProxy.cpp
Normal file
208
Linphone/core/chat/message/EventLogProxy.cpp
Normal file
|
|
@ -0,0 +1,208 @@
|
|||
/*
|
||||
* 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 "EventLogProxy.hpp"
|
||||
#include "EventLogGui.hpp"
|
||||
#include "EventLogList.hpp"
|
||||
// #include "core/chat/ChatGui.hpp"
|
||||
#include "core/App.hpp"
|
||||
|
||||
DEFINE_ABSTRACT_OBJECT(EventLogProxy)
|
||||
|
||||
EventLogProxy::EventLogProxy(QObject *parent) : QSortFilterProxyModel(parent) {
|
||||
mList = EventLogList::create();
|
||||
setSourceModel(mList.get());
|
||||
}
|
||||
|
||||
EventLogProxy::~EventLogProxy() {
|
||||
}
|
||||
|
||||
void EventLogProxy::setSourceModel(QAbstractItemModel *model) {
|
||||
auto oldEventLogList = dynamic_cast<EventLogList *>(sourceModel());
|
||||
if (oldEventLogList) {
|
||||
disconnect(oldEventLogList, &EventLogList::displayItemsStepChanged, this, nullptr);
|
||||
disconnect(oldEventLogList, &EventLogList::messageWithFilterFound, this, nullptr);
|
||||
disconnect(oldEventLogList, &EventLogList::eventInsertedByUser, this, nullptr);
|
||||
}
|
||||
auto newEventLogList = dynamic_cast<EventLogList *>(model);
|
||||
if (newEventLogList) {
|
||||
connect(this, &EventLogProxy::displayItemsStepChanged, newEventLogList,
|
||||
[this, newEventLogList] { newEventLogList->setDisplayItemsStep(mDisplayItemsStep); });
|
||||
connect(newEventLogList, &EventLogList::messageWithFilterFound, this, [this, newEventLogList](int i) {
|
||||
auto model = dynamic_cast<EventLogList *>(sourceModel());
|
||||
int proxyIndex = mapFromSource(newEventLogList->index(i, 0)).row();
|
||||
if (i != -1) {
|
||||
loadUntil(proxyIndex);
|
||||
}
|
||||
emit indexWithFilterFound(proxyIndex);
|
||||
});
|
||||
connect(newEventLogList, &EventLogList::eventInsertedByUser, this, [this, newEventLogList](int i) {
|
||||
int proxyIndex = mapFromSource(newEventLogList->index(i, 0)).row();
|
||||
emit eventInsertedByUser(proxyIndex);
|
||||
});
|
||||
}
|
||||
QSortFilterProxyModel::setSourceModel(model);
|
||||
}
|
||||
|
||||
ChatGui *EventLogProxy::getChatGui() {
|
||||
auto model = dynamic_cast<EventLogList *>(sourceModel());
|
||||
if (!mChatGui && model) mChatGui = model->getChat();
|
||||
return mChatGui;
|
||||
}
|
||||
|
||||
void EventLogProxy::setChatGui(ChatGui *chat) {
|
||||
auto model = dynamic_cast<EventLogList *>(sourceModel());
|
||||
if (model) model->setChatGui(chat);
|
||||
}
|
||||
|
||||
EventLogGui *EventLogProxy::getEventAtIndex(int i) {
|
||||
auto eventCore = getEventCoreAtIndex(i);
|
||||
return eventCore == nullptr ? nullptr : new EventLogGui(eventCore);
|
||||
}
|
||||
|
||||
int EventLogProxy::getCount() const {
|
||||
return rowCount();
|
||||
}
|
||||
|
||||
int EventLogProxy::getInitialDisplayItems() const {
|
||||
return mInitialDisplayItems;
|
||||
}
|
||||
|
||||
void EventLogProxy::setInitialDisplayItems(int initialItems) {
|
||||
if (mInitialDisplayItems != initialItems) {
|
||||
mInitialDisplayItems = initialItems;
|
||||
if (getMaxDisplayItems() <= mInitialDisplayItems) setMaxDisplayItems(initialItems);
|
||||
if (getDisplayItemsStep() <= 0) setDisplayItemsStep(initialItems);
|
||||
emit initialDisplayItemsChanged();
|
||||
}
|
||||
}
|
||||
|
||||
int EventLogProxy::getDisplayCount(int listCount, int maxCount) {
|
||||
return maxCount >= 0 ? qMin(listCount, maxCount) : listCount;
|
||||
}
|
||||
|
||||
int EventLogProxy::getDisplayCount(int listCount) const {
|
||||
return getDisplayCount(listCount, mMaxDisplayItems);
|
||||
}
|
||||
|
||||
QSharedPointer<EventLogCore> EventLogProxy::getEventCoreAtIndex(int i) {
|
||||
auto model = dynamic_cast<EventLogList *>(sourceModel());
|
||||
if (model) {
|
||||
return model->getAt<EventLogCore>(mapToSource(index(i, 0)).row());
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void EventLogProxy::displayMore() {
|
||||
auto model = dynamic_cast<EventLogList *>(sourceModel());
|
||||
if (model) {
|
||||
model->displayMore();
|
||||
}
|
||||
}
|
||||
int EventLogProxy::getMaxDisplayItems() const {
|
||||
return mMaxDisplayItems;
|
||||
}
|
||||
|
||||
void EventLogProxy::setMaxDisplayItems(int maxItems) {
|
||||
if (mMaxDisplayItems != maxItems) {
|
||||
auto model = sourceModel();
|
||||
int modelCount = model ? model->rowCount() : 0;
|
||||
int oldCount = getDisplayCount(modelCount);
|
||||
mMaxDisplayItems = maxItems;
|
||||
if (getInitialDisplayItems() > mMaxDisplayItems) setInitialDisplayItems(maxItems);
|
||||
if (getDisplayItemsStep() <= 0) setDisplayItemsStep(maxItems);
|
||||
emit maxDisplayItemsChanged();
|
||||
|
||||
if (model && getDisplayCount(modelCount) != oldCount) {
|
||||
invalidate();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int EventLogProxy::getDisplayItemsStep() const {
|
||||
return mDisplayItemsStep;
|
||||
}
|
||||
|
||||
void EventLogProxy::setDisplayItemsStep(int step) {
|
||||
if (step > 0 && mDisplayItemsStep != step) {
|
||||
mDisplayItemsStep = step;
|
||||
emit displayItemsStepChanged();
|
||||
}
|
||||
}
|
||||
|
||||
void EventLogProxy::loadUntil(int index) {
|
||||
if (mMaxDisplayItems < index) setMaxDisplayItems(index + mDisplayItemsStep);
|
||||
}
|
||||
|
||||
int EventLogProxy::findFirstUnreadIndex() {
|
||||
auto eventLogList = dynamic_cast<EventLogList *>(sourceModel());
|
||||
if (eventLogList) {
|
||||
auto listIndex = eventLogList->findFirstUnreadIndex();
|
||||
if (listIndex != -1) {
|
||||
listIndex = mapFromSource(eventLogList->index(listIndex, 0)).row();
|
||||
if (mMaxDisplayItems <= listIndex) setMaxDisplayItems(listIndex + mDisplayItemsStep);
|
||||
return listIndex;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
QString EventLogProxy::getFilterText() const {
|
||||
return mFilterText;
|
||||
}
|
||||
|
||||
void EventLogProxy::setFilterText(const QString &filter) {
|
||||
if (mFilterText != filter) {
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(6, 10, 0)
|
||||
beginFilterChange();
|
||||
mFilterText = filter;
|
||||
endFilterChange();
|
||||
#else
|
||||
mFilterText = filter;
|
||||
invalidateFilter();
|
||||
#endif
|
||||
emit filterTextChanged();
|
||||
}
|
||||
}
|
||||
|
||||
QSharedPointer<EventLogCore> EventLogProxy::getAt(int atIndex) const {
|
||||
auto model = dynamic_cast<EventLogList *>(sourceModel());
|
||||
if (model) {
|
||||
return model->getAt<EventLogCore>(mapToSource(index(atIndex, 0)).row());
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void EventLogProxy::markIndexAsRead(int proxyIndex) {
|
||||
auto event = getAt(proxyIndex);
|
||||
if (event && event->getChatMessageCore()) event->getChatMessageCore()->lMarkAsRead();
|
||||
}
|
||||
|
||||
void EventLogProxy::findIndexCorrespondingToFilter(int startIndex, bool forward, bool isFirstResearch) {
|
||||
auto filter = getFilterText();
|
||||
if (filter.isEmpty()) return;
|
||||
auto eventLogList = dynamic_cast<EventLogList *>(sourceModel());
|
||||
if (eventLogList) {
|
||||
auto listIndex = mapToSource(index(startIndex, 0)).row();
|
||||
eventLogList->findChatMessageWithFilter(filter, listIndex, forward, isFirstResearch);
|
||||
}
|
||||
}
|
||||
102
Linphone/core/chat/message/EventLogProxy.hpp
Normal file
102
Linphone/core/chat/message/EventLogProxy.hpp
Normal file
|
|
@ -0,0 +1,102 @@
|
|||
/*
|
||||
* 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 EVENT_LIST_PROXY_H_
|
||||
#define EVENT_LIST_PROXY_H_
|
||||
|
||||
#include "EventLogList.hpp"
|
||||
// #include "core/proxy/LimitProxy.hpp"
|
||||
#include "tool/AbstractObject.hpp"
|
||||
#include <QSortFilterProxyModel>
|
||||
|
||||
// =============================================================================
|
||||
|
||||
class ChatGui;
|
||||
|
||||
class EventLogProxy : public QSortFilterProxyModel, public AbstractObject {
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(int count READ getCount NOTIFY countChanged)
|
||||
Q_PROPERTY(ChatGui *chatGui READ getChatGui WRITE setChatGui NOTIFY chatGuiChanged)
|
||||
Q_PROPERTY(int initialDisplayItems READ getInitialDisplayItems WRITE setInitialDisplayItems NOTIFY
|
||||
initialDisplayItemsChanged)
|
||||
Q_PROPERTY(int maxDisplayItems READ getMaxDisplayItems WRITE setMaxDisplayItems NOTIFY maxDisplayItemsChanged)
|
||||
Q_PROPERTY(int displayItemsStep READ getDisplayItemsStep WRITE setDisplayItemsStep NOTIFY displayItemsStepChanged)
|
||||
Q_PROPERTY(QString filterText READ getFilterText WRITE setFilterText NOTIFY filterTextChanged)
|
||||
|
||||
public:
|
||||
// DECLARE_SORTFILTER_CLASS()
|
||||
|
||||
EventLogProxy(QObject *parent = Q_NULLPTR);
|
||||
~EventLogProxy();
|
||||
|
||||
ChatGui *getChatGui();
|
||||
void setChatGui(ChatGui *chat);
|
||||
|
||||
void setSourceModel(QAbstractItemModel *sourceModel) override;
|
||||
virtual int getCount() const;
|
||||
static int getDisplayCount(int listCount, int maxCount);
|
||||
int getDisplayCount(int listCount) const;
|
||||
int getInitialDisplayItems() const;
|
||||
void setInitialDisplayItems(int initialItems);
|
||||
|
||||
int getMaxDisplayItems() const;
|
||||
void setMaxDisplayItems(int maxItems);
|
||||
|
||||
int getDisplayItemsStep() const;
|
||||
void setDisplayItemsStep(int step);
|
||||
|
||||
QString getFilterText() const;
|
||||
void setFilterText(const QString &filter);
|
||||
|
||||
// bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override;
|
||||
// bool lessThan(const QModelIndex &sourceLeft, const QModelIndex &sourceRight) const override;
|
||||
|
||||
QSharedPointer<EventLogCore> getAt(int atIndex) const;
|
||||
|
||||
Q_INVOKABLE void displayMore();
|
||||
Q_INVOKABLE void loadUntil(int index);
|
||||
Q_INVOKABLE EventLogGui *getEventAtIndex(int i);
|
||||
QSharedPointer<EventLogCore> getEventCoreAtIndex(int i);
|
||||
Q_INVOKABLE int findFirstUnreadIndex();
|
||||
Q_INVOKABLE void markIndexAsRead(int proxyIndex);
|
||||
Q_INVOKABLE void findIndexCorrespondingToFilter(int startIndex, bool forward = true, bool isFirstResearch = true);
|
||||
|
||||
signals:
|
||||
void eventInsertedByUser(int index);
|
||||
void indexWithFilterFound(int index);
|
||||
void chatGuiChanged();
|
||||
void countChanged();
|
||||
void initialDisplayItemsChanged();
|
||||
void maxDisplayItemsChanged();
|
||||
void displayItemsStepChanged();
|
||||
void filterTextChanged();
|
||||
|
||||
protected:
|
||||
QSharedPointer<EventLogList> mList;
|
||||
QSharedPointer<EventLogCore> mLastSearchStart;
|
||||
ChatGui *mChatGui = nullptr;
|
||||
int mInitialDisplayItems = -1;
|
||||
int mMaxDisplayItems = -1;
|
||||
int mDisplayItemsStep = 5;
|
||||
QString mFilterText;
|
||||
DECLARE_ABSTRACT_OBJECT
|
||||
};
|
||||
|
||||
#endif
|
||||
272
Linphone/core/chat/message/content/ChatMessageContentCore.cpp
Normal file
272
Linphone/core/chat/message/content/ChatMessageContentCore.cpp
Normal file
|
|
@ -0,0 +1,272 @@
|
|||
/*
|
||||
* 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 "ChatMessageContentCore.hpp"
|
||||
#include "core/App.hpp"
|
||||
#include "core/chat/ChatCore.hpp"
|
||||
#include "model/tool/ToolModel.hpp"
|
||||
#include "tool/providers/ThumbnailProvider.hpp"
|
||||
|
||||
DEFINE_ABSTRACT_OBJECT(ChatMessageContentCore)
|
||||
|
||||
QSharedPointer<ChatMessageContentCore>
|
||||
ChatMessageContentCore::create(const std::shared_ptr<linphone::Content> &content,
|
||||
std::shared_ptr<ChatMessageModel> chatMessageModel) {
|
||||
auto sharedPointer = QSharedPointer<ChatMessageContentCore>(new ChatMessageContentCore(content, chatMessageModel),
|
||||
&QObject::deleteLater);
|
||||
sharedPointer->setSelf(sharedPointer);
|
||||
sharedPointer->moveToThread(App::getInstance()->thread());
|
||||
return sharedPointer;
|
||||
}
|
||||
|
||||
ChatMessageContentCore::ChatMessageContentCore(const std::shared_ptr<linphone::Content> &content,
|
||||
std::shared_ptr<ChatMessageModel> chatMessageModel) {
|
||||
if (content) {
|
||||
mName = Utils::coreStringToAppString(content->getName());
|
||||
if (mName.isEmpty()) { // Try to find the name from file Path
|
||||
QString fileName = Utils::coreStringToAppString(content->getFilePath());
|
||||
if (!fileName.isEmpty()) {
|
||||
mName = QFileInfo(fileName).baseName();
|
||||
}
|
||||
}
|
||||
mFilePath = QDir::fromNativeSeparators(Utils::coreStringToAppString(content->getFilePath()));
|
||||
mIsFile = content->isFile();
|
||||
mIsFileEncrypted = content->isFileEncrypted();
|
||||
mIsFileTransfer = content->isFileTransfer();
|
||||
mIsCalendar = content->isIcalendar();
|
||||
if (content->isIcalendar()) {
|
||||
auto conferenceInfo = linphone::Factory::get()->createConferenceInfoFromIcalendarContent(content);
|
||||
mConferenceInfo = ConferenceInfoCore::create(conferenceInfo);
|
||||
}
|
||||
mIsMultipart = content->isMultipart();
|
||||
mIsText = content->isText();
|
||||
mIsVoiceRecording = content->isVoiceRecording();
|
||||
mIsVideo = Utils::isVideo(mFilePath);
|
||||
mFileSize = (quint64)content->getFileSize();
|
||||
mFileDuration = content->getFileDuration();
|
||||
mFileOffset = 0;
|
||||
mUtf8Text = Utils::coreStringToAppString(content->getUtf8Text());
|
||||
auto chatRoom = chatMessageModel ? chatMessageModel->getMonitor()->getChatRoom() : nullptr;
|
||||
mRichFormatText = ToolModel::encodeTextToQmlRichFormat(mUtf8Text, {}, chatRoom);
|
||||
mWasDownloaded = !mFilePath.isEmpty() && QFileInfo(mFilePath).isFile();
|
||||
mThumbnail = mFilePath.isEmpty()
|
||||
? QUrl()
|
||||
: QUrl(QString("image://%1/%2").arg(ThumbnailProvider::ProviderId).arg(mFilePath));
|
||||
mChatMessageContentModel = Utils::makeQObject_ptr<ChatMessageContentModel>(content, chatMessageModel);
|
||||
}
|
||||
}
|
||||
|
||||
ChatMessageContentCore ::~ChatMessageContentCore() {
|
||||
}
|
||||
|
||||
void ChatMessageContentCore::setSelf(QSharedPointer<ChatMessageContentCore> me) {
|
||||
mChatMessageContentModelConnection =
|
||||
SafeConnection<ChatMessageContentCore, ChatMessageContentModel>::create(me, mChatMessageContentModel);
|
||||
|
||||
auto updateThumbnailType = [this] {
|
||||
if (Utils::isVideo(mFilePath)) mIsVideo = true;
|
||||
emit isVideoChanged();
|
||||
};
|
||||
|
||||
mChatMessageContentModelConnection->makeConnectToCore(
|
||||
&ChatMessageContentCore::lCreateThumbnail, [this](const bool &force = false) {
|
||||
mChatMessageContentModelConnection->invokeToModel(
|
||||
[this, force] { mChatMessageContentModel->createThumbnail(); });
|
||||
});
|
||||
mChatMessageContentModelConnection->makeConnectToModel(
|
||||
&ChatMessageContentModel::thumbnailChanged, [this, updateThumbnailType](QString thumbnail) {
|
||||
mChatMessageContentModelConnection->invokeToCore([this, thumbnail] { setThumbnail(QUrl(thumbnail)); });
|
||||
});
|
||||
|
||||
mChatMessageContentModelConnection->makeConnectToCore(&ChatMessageContentCore::lDownloadFile, [this]() {
|
||||
mChatMessageContentModelConnection->invokeToModel([this] {
|
||||
QString *error = new QString();
|
||||
bool downloaded = mChatMessageContentModel->downloadFile(mName, error);
|
||||
if (!downloaded) {
|
||||
mChatMessageContentModelConnection->invokeToCore([this, error] {
|
||||
//: Error downloading file %1
|
||||
if (error->isEmpty()) *error = tr("download_file_default_error").arg(mName);
|
||||
Utils::showInformationPopup(tr("info_popup_error_titile"), *error, false);
|
||||
delete error;
|
||||
});
|
||||
} else delete error;
|
||||
});
|
||||
});
|
||||
mChatMessageContentModelConnection->makeConnectToModel(
|
||||
&ChatMessageContentModel::wasDownloadedChanged,
|
||||
[this](const std::shared_ptr<linphone::Content> &content, bool downloaded) {
|
||||
mChatMessageContentModelConnection->invokeToCore([this, downloaded] { setWasDownloaded(downloaded); });
|
||||
});
|
||||
mChatMessageContentModelConnection->makeConnectToModel(
|
||||
&ChatMessageContentModel::filePathChanged,
|
||||
[this](const std::shared_ptr<linphone::Content> &content, QString filePath) {
|
||||
auto isFile = content->isFile();
|
||||
auto isFileTransfer = content->isFileTransfer();
|
||||
auto isFileEncrypted = content->isFileEncrypted();
|
||||
mChatMessageContentModelConnection->invokeToCore([this, filePath, isFile, isFileTransfer, isFileEncrypted] {
|
||||
setIsFile(isFile || QFileInfo(filePath).isFile());
|
||||
setIsFileTransfer(isFileTransfer);
|
||||
setIsFileEncrypted(isFileEncrypted);
|
||||
setFilePath(filePath);
|
||||
});
|
||||
});
|
||||
|
||||
mChatMessageContentModelConnection->makeConnectToCore(&ChatMessageContentCore::lCancelDownloadFile, [this]() {
|
||||
mChatMessageContentModelConnection->invokeToModel([this] { mChatMessageContentModel->cancelDownloadFile(); });
|
||||
});
|
||||
mChatMessageContentModelConnection->makeConnectToCore(
|
||||
&ChatMessageContentCore::lOpenFile, [this](bool showDirectory = false) {
|
||||
if (!QFileInfo(mFilePath).exists()) {
|
||||
//: Error
|
||||
Utils::showInformationPopup(tr("popup_error_title"),
|
||||
//: Could not open file : unknown path %1
|
||||
tr("popup_open_file_error_does_not_exist_message").arg(mFilePath), false);
|
||||
} else {
|
||||
mChatMessageContentModelConnection->invokeToModel([this, showDirectory] {
|
||||
mChatMessageContentModel->openFile(mName, mWasDownloaded, showDirectory);
|
||||
});
|
||||
}
|
||||
});
|
||||
mChatMessageContentModelConnection->makeConnectToModel(
|
||||
&ChatMessageContentModel::messageStateChanged, [this](linphone::ChatMessage::State state) {
|
||||
mChatMessageContentModelConnection->invokeToCore(
|
||||
[this, msgState = LinphoneEnums::fromLinphone(state)] { emit msgStateChanged(msgState); });
|
||||
});
|
||||
}
|
||||
|
||||
bool ChatMessageContentCore::isFile() const {
|
||||
return mIsFile;
|
||||
}
|
||||
|
||||
void ChatMessageContentCore::setIsFile(bool isFile) {
|
||||
if (mIsFile != isFile) {
|
||||
mIsFile = isFile;
|
||||
emit isFileChanged();
|
||||
}
|
||||
}
|
||||
|
||||
bool ChatMessageContentCore::isVideo() const {
|
||||
return mIsVideo;
|
||||
}
|
||||
|
||||
bool ChatMessageContentCore::isFileEncrypted() const {
|
||||
return mIsFileEncrypted;
|
||||
}
|
||||
|
||||
void ChatMessageContentCore::setIsFileEncrypted(bool isFileEncrypted) {
|
||||
if (mIsFileEncrypted != isFileEncrypted) {
|
||||
mIsFileEncrypted = isFileEncrypted;
|
||||
emit isFileEncryptedChanged();
|
||||
}
|
||||
}
|
||||
|
||||
bool ChatMessageContentCore::isFileTransfer() const {
|
||||
return mIsFileTransfer;
|
||||
}
|
||||
|
||||
void ChatMessageContentCore::setIsFileTransfer(bool isFileTransfer) {
|
||||
if (mIsFileTransfer != isFileTransfer) {
|
||||
mIsFileTransfer = isFileTransfer;
|
||||
emit isFileTransferChanged();
|
||||
}
|
||||
}
|
||||
|
||||
bool ChatMessageContentCore::isCalendar() const {
|
||||
return mIsCalendar;
|
||||
}
|
||||
|
||||
bool ChatMessageContentCore::isMultipart() const {
|
||||
return mIsMultipart;
|
||||
}
|
||||
|
||||
bool ChatMessageContentCore::isText() const {
|
||||
return mIsText;
|
||||
}
|
||||
|
||||
bool ChatMessageContentCore::isVoiceRecording() const {
|
||||
return mIsVoiceRecording;
|
||||
}
|
||||
|
||||
QString ChatMessageContentCore::getFilePath() const {
|
||||
return mFilePath;
|
||||
}
|
||||
|
||||
void ChatMessageContentCore::setFilePath(QString path) {
|
||||
if (mFilePath != path) {
|
||||
mFilePath = path;
|
||||
emit filePathChanged();
|
||||
}
|
||||
}
|
||||
|
||||
QString ChatMessageContentCore::getUtf8Text() const {
|
||||
return mUtf8Text;
|
||||
}
|
||||
|
||||
QString ChatMessageContentCore::getName() const {
|
||||
return mName;
|
||||
}
|
||||
|
||||
quint64 ChatMessageContentCore::getFileSize() const {
|
||||
return mFileSize;
|
||||
}
|
||||
|
||||
quint64 ChatMessageContentCore::getFileOffset() const {
|
||||
return mFileOffset;
|
||||
}
|
||||
|
||||
void ChatMessageContentCore::setFileOffset(quint64 fileOffset) {
|
||||
if (mFileOffset != fileOffset) {
|
||||
mFileOffset = fileOffset;
|
||||
emit fileOffsetChanged();
|
||||
}
|
||||
}
|
||||
|
||||
int ChatMessageContentCore::getFileDuration() const {
|
||||
return mFileDuration;
|
||||
}
|
||||
|
||||
ConferenceInfoGui *ChatMessageContentCore::getConferenceInfoGui() const {
|
||||
return mConferenceInfo ? new ConferenceInfoGui(mConferenceInfo) : nullptr;
|
||||
}
|
||||
|
||||
bool ChatMessageContentCore::wasDownloaded() const {
|
||||
return mWasDownloaded;
|
||||
}
|
||||
|
||||
QUrl ChatMessageContentCore::getThumbnail() const {
|
||||
return mThumbnail;
|
||||
}
|
||||
|
||||
void ChatMessageContentCore::setThumbnail(const QUrl &data) {
|
||||
if (mThumbnail != data) {
|
||||
mThumbnail = data;
|
||||
emit thumbnailChanged();
|
||||
}
|
||||
}
|
||||
void ChatMessageContentCore::setWasDownloaded(bool wasDownloaded) {
|
||||
if (mWasDownloaded != wasDownloaded) {
|
||||
mWasDownloaded = wasDownloaded;
|
||||
emit wasDownloadedChanged(wasDownloaded);
|
||||
}
|
||||
}
|
||||
|
||||
const std::shared_ptr<ChatMessageContentModel> &ChatMessageContentCore::getContentModel() const {
|
||||
return mChatMessageContentModel;
|
||||
}
|
||||
138
Linphone/core/chat/message/content/ChatMessageContentCore.hpp
Normal file
138
Linphone/core/chat/message/content/ChatMessageContentCore.hpp
Normal file
|
|
@ -0,0 +1,138 @@
|
|||
/*
|
||||
* 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_CONTENT_CORE_H_
|
||||
#define CHAT_MESSAGE_CONTENT_CORE_H_
|
||||
|
||||
#include "core/conference/ConferenceInfoCore.hpp"
|
||||
#include "core/conference/ConferenceInfoGui.hpp"
|
||||
#include "model/chat/message/content/ChatMessageContentModel.hpp"
|
||||
#include "tool/AbstractObject.hpp"
|
||||
#include "tool/thread/SafeConnection.hpp"
|
||||
#include <QObject>
|
||||
#include <QSharedPointer>
|
||||
|
||||
#include <linphone++/linphone.hh>
|
||||
|
||||
class ChatMessageContentCore : public QObject, public AbstractObject {
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(QString name READ getName CONSTANT)
|
||||
Q_PROPERTY(quint64 fileOffset READ getFileOffset WRITE setFileOffset NOTIFY fileOffsetChanged)
|
||||
|
||||
Q_PROPERTY(QUrl thumbnail READ getThumbnail WRITE setThumbnail NOTIFY thumbnailChanged)
|
||||
Q_PROPERTY(bool wasDownloaded READ wasDownloaded WRITE setWasDownloaded NOTIFY wasDownloadedChanged)
|
||||
Q_PROPERTY(QString filePath READ getFilePath WRITE setFilePath NOTIFY filePathChanged)
|
||||
Q_PROPERTY(QString utf8Text READ getUtf8Text CONSTANT)
|
||||
Q_PROPERTY(QString richFormatText MEMBER mRichFormatText CONSTANT)
|
||||
Q_PROPERTY(bool isFile READ isFile WRITE setIsFile NOTIFY isFileChanged)
|
||||
Q_PROPERTY(bool isFileEncrypted READ isFileEncrypted WRITE setIsFileEncrypted NOTIFY isFileEncryptedChanged)
|
||||
Q_PROPERTY(bool isFileTransfer READ isFileTransfer WRITE setIsFileTransfer NOTIFY isFileTransferChanged)
|
||||
Q_PROPERTY(bool isCalendar READ isCalendar CONSTANT)
|
||||
Q_PROPERTY(ConferenceInfoGui *conferenceInfo READ getConferenceInfoGui CONSTANT)
|
||||
Q_PROPERTY(bool isMultipart READ isMultipart CONSTANT)
|
||||
Q_PROPERTY(bool isText READ isText CONSTANT)
|
||||
Q_PROPERTY(bool isVideo READ isVideo NOTIFY isVideoChanged)
|
||||
Q_PROPERTY(bool isVoiceRecording READ isVoiceRecording CONSTANT)
|
||||
Q_PROPERTY(int fileDuration READ getFileDuration CONSTANT)
|
||||
Q_PROPERTY(quint64 fileSize READ getFileSize CONSTANT)
|
||||
|
||||
public:
|
||||
static QSharedPointer<ChatMessageContentCore> create(const std::shared_ptr<linphone::Content> &content,
|
||||
std::shared_ptr<ChatMessageModel> chatMessageModel);
|
||||
ChatMessageContentCore(const std::shared_ptr<linphone::Content> &content,
|
||||
std::shared_ptr<ChatMessageModel> chatMessageModel);
|
||||
~ChatMessageContentCore();
|
||||
void setSelf(QSharedPointer<ChatMessageContentCore> me);
|
||||
|
||||
bool isFile() const;
|
||||
void setIsFile(bool isFile);
|
||||
bool isFileEncrypted() const;
|
||||
void setIsFileEncrypted(bool isFileEncrypted);
|
||||
bool isFileTransfer() const;
|
||||
void setIsFileTransfer(bool isFileTransfer);
|
||||
|
||||
bool isVideo() const;
|
||||
bool isCalendar() const;
|
||||
bool isMultipart() const;
|
||||
bool isText() const;
|
||||
bool isVoiceRecording() const;
|
||||
|
||||
QString getUtf8Text() const;
|
||||
QString getName() const;
|
||||
quint64 getFileSize() const;
|
||||
quint64 getFileOffset() const;
|
||||
void setFileOffset(quint64 fileOffset);
|
||||
QString getFilePath() const;
|
||||
void setFilePath(QString path);
|
||||
int getFileDuration() const;
|
||||
ConferenceInfoGui *getConferenceInfoGui() const;
|
||||
|
||||
void setThumbnail(const QUrl &data);
|
||||
QUrl getThumbnail() const;
|
||||
|
||||
bool wasDownloaded() const;
|
||||
void setWasDownloaded(bool downloaded);
|
||||
|
||||
const std::shared_ptr<ChatMessageContentModel> &getContentModel() const;
|
||||
|
||||
signals:
|
||||
void msgStateChanged(LinphoneEnums::ChatMessageState state);
|
||||
void thumbnailChanged();
|
||||
void fileOffsetChanged();
|
||||
void filePathChanged();
|
||||
void isFileChanged();
|
||||
void isFileTransferChanged();
|
||||
void isFileEncryptedChanged();
|
||||
void wasDownloadedChanged(bool downloaded);
|
||||
void isVideoChanged();
|
||||
|
||||
void lCreateThumbnail(const bool &force = false);
|
||||
void lRemoveDownloadedFile();
|
||||
void lDownloadFile();
|
||||
void lCancelDownloadFile();
|
||||
void lOpenFile(bool showDirectory = false);
|
||||
bool lSaveAs(const QString &path);
|
||||
|
||||
private:
|
||||
DECLARE_ABSTRACT_OBJECT
|
||||
bool mIsFile;
|
||||
bool mIsVideo;
|
||||
bool mIsFileEncrypted;
|
||||
bool mIsFileTransfer;
|
||||
bool mIsCalendar;
|
||||
bool mIsMultipart;
|
||||
bool mIsText;
|
||||
bool mIsVoiceRecording;
|
||||
int mFileDuration;
|
||||
QUrl mThumbnail;
|
||||
QString mUtf8Text;
|
||||
QString mRichFormatText;
|
||||
QString mFilePath;
|
||||
QString mName;
|
||||
quint64 mFileSize;
|
||||
quint64 mFileOffset;
|
||||
bool mWasDownloaded;
|
||||
QSharedPointer<ConferenceInfoCore> mConferenceInfo = nullptr;
|
||||
|
||||
std::shared_ptr<ChatMessageContentModel> mChatMessageContentModel;
|
||||
QSharedPointer<SafeConnection<ChatMessageContentCore, ChatMessageContentModel>> mChatMessageContentModelConnection;
|
||||
};
|
||||
|
||||
#endif // CHAT_MESSAGE_CONTENT_CORE_H_
|
||||
38
Linphone/core/chat/message/content/ChatMessageContentGui.cpp
Normal file
38
Linphone/core/chat/message/content/ChatMessageContentGui.cpp
Normal 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 "ChatMessageContentGui.hpp"
|
||||
#include "core/App.hpp"
|
||||
|
||||
DEFINE_ABSTRACT_OBJECT(ChatMessageContentGui)
|
||||
|
||||
ChatMessageContentGui::ChatMessageContentGui(QSharedPointer<ChatMessageContentCore> core) {
|
||||
App::getInstance()->mEngine->setObjectOwnership(this, QQmlEngine::JavaScriptOwnership);
|
||||
mCore = core;
|
||||
if (isInLinphoneThread()) moveToThread(App::getInstance()->thread());
|
||||
}
|
||||
|
||||
ChatMessageContentGui::~ChatMessageContentGui() {
|
||||
mustBeInMainThread("~" + getClassName());
|
||||
}
|
||||
|
||||
ChatMessageContentCore *ChatMessageContentGui::getCore() const {
|
||||
return mCore.get();
|
||||
}
|
||||
41
Linphone/core/chat/message/content/ChatMessageContentGui.hpp
Normal file
41
Linphone/core/chat/message/content/ChatMessageContentGui.hpp
Normal 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_CONTENT_GUI_H_
|
||||
#define CHAT_MESSAGE_CONTENT_GUI_H_
|
||||
|
||||
#include "core/chat/message/content/ChatMessageContentCore.hpp"
|
||||
#include <QObject>
|
||||
#include <QSharedPointer>
|
||||
|
||||
class ChatMessageContentGui : public QObject, public AbstractObject {
|
||||
Q_OBJECT
|
||||
|
||||
Q_PROPERTY(ChatMessageContentCore *core READ getCore CONSTANT)
|
||||
|
||||
public:
|
||||
ChatMessageContentGui(QSharedPointer<ChatMessageContentCore> core);
|
||||
~ChatMessageContentGui();
|
||||
ChatMessageContentCore *getCore() const;
|
||||
QSharedPointer<ChatMessageContentCore> mCore;
|
||||
DECLARE_ABSTRACT_OBJECT
|
||||
};
|
||||
|
||||
#endif
|
||||
227
Linphone/core/chat/message/content/ChatMessageContentList.cpp
Normal file
227
Linphone/core/chat/message/content/ChatMessageContentList.cpp
Normal file
|
|
@ -0,0 +1,227 @@
|
|||
/*
|
||||
* 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 "ChatMessageContentList.hpp"
|
||||
#include "core/App.hpp"
|
||||
#include "core/chat/ChatCore.hpp"
|
||||
#include "core/chat/message/content/ChatMessageContentGui.hpp"
|
||||
|
||||
#include <QMimeDatabase>
|
||||
#include <QSharedPointer>
|
||||
|
||||
#include <linphone++/linphone.hh>
|
||||
|
||||
// =============================================================================
|
||||
|
||||
DEFINE_ABSTRACT_OBJECT(ChatMessageContentList)
|
||||
|
||||
QSharedPointer<ChatMessageContentList> ChatMessageContentList::create() {
|
||||
auto model = QSharedPointer<ChatMessageContentList>(new ChatMessageContentList(), &QObject::deleteLater);
|
||||
model->moveToThread(App::getInstance()->thread());
|
||||
model->setSelf(model);
|
||||
return model;
|
||||
}
|
||||
|
||||
ChatMessageContentList::ChatMessageContentList(QObject *parent) : ListProxy(parent) {
|
||||
mustBeInMainThread(getClassName());
|
||||
App::getInstance()->mEngine->setObjectOwnership(this, QQmlEngine::CppOwnership);
|
||||
}
|
||||
|
||||
ChatMessageContentList::~ChatMessageContentList() {
|
||||
mustBeInMainThread("~" + getClassName());
|
||||
mModelConnection = nullptr;
|
||||
}
|
||||
|
||||
ChatMessageGui *ChatMessageContentList::getChatMessage() const {
|
||||
if (mChatMessageCore) return new ChatMessageGui(mChatMessageCore);
|
||||
else return nullptr;
|
||||
}
|
||||
|
||||
QSharedPointer<ChatMessageCore> ChatMessageContentList::getChatMessageCore() const {
|
||||
return mChatMessageCore;
|
||||
}
|
||||
|
||||
void ChatMessageContentList::setChatMessageCore(QSharedPointer<ChatMessageCore> core) {
|
||||
if (mChatMessageCore != core) {
|
||||
// if (mChatMessageCore) disconnect(mChatMessageCore.get(), &ChatCore::, this, nullptr);
|
||||
mChatMessageCore = core;
|
||||
// if (mChatMessageCore)
|
||||
// connect(mChatMessageCore.get(), &ChatCore::messageListChanged, this, &ChatMessageContentList::lUpdate);
|
||||
emit chatMessageChanged();
|
||||
lUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
void ChatMessageContentList::setChatMessageGui(ChatMessageGui *chat) {
|
||||
auto chatCore = chat ? chat->mCore : nullptr;
|
||||
setChatMessageCore(chatCore);
|
||||
}
|
||||
|
||||
int ChatMessageContentList::findFirstUnreadIndex() {
|
||||
auto chatList = getSharedList<ChatMessageCore>();
|
||||
auto it = std::find_if(chatList.begin(), chatList.end(),
|
||||
[](const QSharedPointer<ChatMessageCore> item) { return !item->isRead(); });
|
||||
return it == chatList.end() ? -1 : std::distance(chatList.begin(), it);
|
||||
}
|
||||
|
||||
void ChatMessageContentList::addFiles(const QStringList &paths) {
|
||||
mustBeInMainThread(log().arg(Q_FUNC_INFO));
|
||||
|
||||
QStringList finalList;
|
||||
QList<QFileInfo> fileList;
|
||||
|
||||
int nbNotFound = 0;
|
||||
QString lastNotFound;
|
||||
int nbExcess = 0;
|
||||
int count = rowCount();
|
||||
|
||||
for (auto &path : paths) {
|
||||
QFileInfo file(path.toUtf8());
|
||||
// #ifdef _WIN32
|
||||
// // A bug from FileDialog suppose that the file is local and overwrite the uri by removing "\\".
|
||||
// if (!file.exists()) {
|
||||
// path.prepend("\\\\");
|
||||
// file.setFileName(path);
|
||||
// }
|
||||
// #endif
|
||||
if (!file.exists()) {
|
||||
++nbNotFound;
|
||||
lastNotFound = path;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (count + finalList.count() >= 12) {
|
||||
++nbExcess;
|
||||
continue;
|
||||
}
|
||||
finalList.append(path);
|
||||
fileList.append(file);
|
||||
}
|
||||
if (nbNotFound > 0) {
|
||||
//: Error adding file
|
||||
Utils::showInformationPopup(tr("popup_error_title"),
|
||||
//: File was not found: %1
|
||||
(nbNotFound == 1 ? tr("popup_error_path_does_not_exist_message").arg(lastNotFound)
|
||||
//: %n files were not found
|
||||
: tr("popup_error_nb_files_not_found_message").arg(nbNotFound)),
|
||||
false);
|
||||
}
|
||||
if (nbExcess > 0) {
|
||||
//: Error
|
||||
Utils::showInformationPopup(tr("popup_error_title"),
|
||||
//: You can send 12 files maximum at a time. %n files were ignored
|
||||
tr("popup_error_max_files_count_message", "", nbExcess), false);
|
||||
}
|
||||
|
||||
mModelConnection->invokeToModel([this, finalList, fileList] {
|
||||
int nbTooBig = 0;
|
||||
int nbMimeError = 0;
|
||||
QString lastMimeError;
|
||||
QList<QSharedPointer<ChatMessageContentCore>> contentList;
|
||||
for (auto &file : fileList) {
|
||||
qint64 fileSize = file.size();
|
||||
if (fileSize > Constants::FileSizeLimit) {
|
||||
++nbTooBig;
|
||||
lWarning() << log().arg("Unable to send file. (Size limit=%1)").arg(Constants::FileSizeLimit);
|
||||
continue;
|
||||
}
|
||||
auto name = file.fileName().toStdString();
|
||||
auto path = file.filePath();
|
||||
std::shared_ptr<linphone::Content> content = CoreModel::getInstance()->getCore()->createContent();
|
||||
QStringList mimeType = QMimeDatabase().mimeTypeForFile(path).name().split('/');
|
||||
if (mimeType.length() != 2) {
|
||||
++nbMimeError;
|
||||
lastMimeError = path;
|
||||
lWarning() << log().arg("Unable to get supported mime type for: `%1`.").arg(path);
|
||||
continue;
|
||||
}
|
||||
content->setType(Utils::appStringToCoreString(mimeType[0]));
|
||||
content->setSubtype(Utils::appStringToCoreString(mimeType[1]));
|
||||
content->setSize(size_t(fileSize));
|
||||
content->setName(name);
|
||||
content->setFilePath(Utils::appStringToCoreString(path));
|
||||
contentList.append(ChatMessageContentCore::create(content, nullptr));
|
||||
}
|
||||
if (nbTooBig > 0) {
|
||||
//: Error adding file
|
||||
Utils::showInformationPopup(
|
||||
tr("popup_error_title"),
|
||||
//: %n files were ignored cause they exceed the maximum size. (Size limit=%2)
|
||||
tr("popup_error_file_too_big_message").arg(nbTooBig).arg(Constants::FileSizeLimit), false);
|
||||
}
|
||||
if (nbMimeError > 0) {
|
||||
//: Error adding file
|
||||
Utils::showInformationPopup(tr("popup_error_title"),
|
||||
//: Unable to get supported mime type for: `%1`.
|
||||
(nbMimeError == 1
|
||||
? tr("popup_error_unsupported_file_message").arg(lastMimeError)
|
||||
//: Unable to get supported mime type for %1 files.
|
||||
: tr("popup_error_unsupported_files_message").arg(nbMimeError)),
|
||||
false);
|
||||
}
|
||||
|
||||
mModelConnection->invokeToCore([this, contentList] {
|
||||
for (auto &contentCore : contentList) {
|
||||
connect(contentCore.get(), &ChatMessageContentCore::isFileChanged, this, [this, contentCore] {
|
||||
int i = -1;
|
||||
get(contentCore.get(), &i);
|
||||
emit dataChanged(index(i), index(i));
|
||||
});
|
||||
add(contentCore);
|
||||
contentCore->lCreateThumbnail(
|
||||
true); // Was not created because linphone::Content is not considered as a file (yet)
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
void ChatMessageContentList::setSelf(QSharedPointer<ChatMessageContentList> me) {
|
||||
mModelConnection = SafeConnection<ChatMessageContentList, CoreModel>::create(me, CoreModel::getInstance());
|
||||
|
||||
mModelConnection->makeConnectToCore(&ChatMessageContentList::lUpdate, [this]() {
|
||||
for (auto &content : getSharedList<ChatMessageContentCore>()) {
|
||||
if (content) disconnect(content.get(), &ChatMessageContentCore::wasDownloadedChanged, this, nullptr);
|
||||
if (content) disconnect(content.get(), &ChatMessageContentCore::thumbnailChanged, this, nullptr);
|
||||
}
|
||||
if (!mChatMessageCore) return;
|
||||
auto contents = mChatMessageCore->getChatMessageContentList();
|
||||
for (auto &content : contents) {
|
||||
connect(content.get(), &ChatMessageContentCore::wasDownloadedChanged, this,
|
||||
[this, content](bool wasDownloaded) {
|
||||
if (wasDownloaded) {
|
||||
content->lCreateThumbnail();
|
||||
}
|
||||
});
|
||||
connect(content.get(), &ChatMessageContentCore::thumbnailChanged, this, [this] { emit lUpdate(); });
|
||||
}
|
||||
resetData<ChatMessageContentCore>(contents);
|
||||
});
|
||||
mModelConnection->makeConnectToCore(&ChatMessageContentList::lAddFiles,
|
||||
[this](const QStringList &paths) { addFiles(paths); });
|
||||
emit lUpdate();
|
||||
}
|
||||
|
||||
QVariant ChatMessageContentList::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();
|
||||
}
|
||||
|
|
@ -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/>.
|
||||
*/
|
||||
|
||||
#ifndef CHAT_MESSAGE_CONTENT_LIST_H_
|
||||
#define CHAT_MESSAGE_CONTENT_LIST_H_
|
||||
|
||||
#include "core/proxy/ListProxy.hpp"
|
||||
#include "tool/AbstractObject.hpp"
|
||||
#include "tool/thread/SafeConnection.hpp"
|
||||
#include <QLocale>
|
||||
|
||||
class ChatMessageGui;
|
||||
class ChatMessageCore;
|
||||
// =============================================================================
|
||||
|
||||
class ChatMessageContentList : public ListProxy, public AbstractObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
static QSharedPointer<ChatMessageContentList> create();
|
||||
ChatMessageContentList(QObject *parent = Q_NULLPTR);
|
||||
~ChatMessageContentList();
|
||||
|
||||
QSharedPointer<ChatMessageCore> getChatMessageCore() const;
|
||||
ChatMessageGui *getChatMessage() const;
|
||||
void setChatMessageCore(QSharedPointer<ChatMessageCore> core);
|
||||
void setChatMessageGui(ChatMessageGui *chat);
|
||||
|
||||
int findFirstUnreadIndex();
|
||||
|
||||
void addFiles(const QStringList &paths);
|
||||
|
||||
void setSelf(QSharedPointer<ChatMessageContentList> me);
|
||||
virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
|
||||
|
||||
signals:
|
||||
void lAddFiles(QStringList paths);
|
||||
void isFileChanged();
|
||||
void lUpdate();
|
||||
void chatMessageChanged();
|
||||
|
||||
private:
|
||||
QSharedPointer<ChatMessageCore> mChatMessageCore;
|
||||
QSharedPointer<SafeConnection<ChatMessageContentList, CoreModel>> mModelConnection;
|
||||
DECLARE_ABSTRACT_OBJECT
|
||||
};
|
||||
|
||||
#endif
|
||||
103
Linphone/core/chat/message/content/ChatMessageContentProxy.cpp
Normal file
103
Linphone/core/chat/message/content/ChatMessageContentProxy.cpp
Normal file
|
|
@ -0,0 +1,103 @@
|
|||
/*
|
||||
* 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 "ChatMessageContentProxy.hpp"
|
||||
#include "ChatMessageContentGui.hpp"
|
||||
#include "core/App.hpp"
|
||||
#include "core/chat/message/ChatMessageGui.hpp"
|
||||
|
||||
DEFINE_ABSTRACT_OBJECT(ChatMessageContentProxy)
|
||||
|
||||
ChatMessageContentProxy::ChatMessageContentProxy(QObject *parent) : LimitProxy(parent) {
|
||||
mList = ChatMessageContentList::create();
|
||||
setSourceModel(mList.get());
|
||||
}
|
||||
|
||||
ChatMessageContentProxy::~ChatMessageContentProxy() {
|
||||
}
|
||||
|
||||
void ChatMessageContentProxy::setSourceModel(QAbstractItemModel *model) {
|
||||
auto oldChatMessageContentList = getListModel<ChatMessageContentList>();
|
||||
if (oldChatMessageContentList) {
|
||||
// disconnect(oldChatMessageContentList);
|
||||
}
|
||||
auto newChatMessageContentList = dynamic_cast<ChatMessageContentList *>(model);
|
||||
if (newChatMessageContentList) {
|
||||
// connect(newChatMessageContentList, &ChatMessageContentList::chatChanged, this,
|
||||
// &ChatMessageContentProxy::chatChanged);
|
||||
}
|
||||
setSourceModels(new SortFilterList(model));
|
||||
sort(0);
|
||||
}
|
||||
|
||||
ChatMessageGui *ChatMessageContentProxy::getChatMessageGui() {
|
||||
auto model = getListModel<ChatMessageContentList>();
|
||||
if (!mChatMessageGui && model) mChatMessageGui = model->getChatMessage();
|
||||
return mChatMessageGui;
|
||||
}
|
||||
|
||||
void ChatMessageContentProxy::setChatMessageGui(ChatMessageGui *chat) {
|
||||
getListModel<ChatMessageContentList>()->setChatMessageGui(chat);
|
||||
}
|
||||
|
||||
ChatMessageContentGui *ChatMessageContentProxy::getChatMessageContentAtIndex(int i) {
|
||||
auto model = getListModel<ChatMessageContentList>();
|
||||
auto sourceIndex = mapToSource(index(i, 0)).row();
|
||||
if (model) {
|
||||
auto chat = model->getAt<ChatMessageContentCore>(sourceIndex);
|
||||
if (chat) return new ChatMessageContentGui(chat);
|
||||
else return nullptr;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void ChatMessageContentProxy::addFiles(const QStringList &paths) {
|
||||
auto model = getListModel<ChatMessageContentList>();
|
||||
if (model) emit model->lAddFiles(paths);
|
||||
}
|
||||
|
||||
void ChatMessageContentProxy::removeContent(ChatMessageContentGui *contentGui) {
|
||||
auto model = getListModel<ChatMessageContentList>();
|
||||
if (model && contentGui) model->remove(contentGui->mCore);
|
||||
}
|
||||
|
||||
void ChatMessageContentProxy::clear() {
|
||||
auto model = getListModel<ChatMessageContentList>();
|
||||
if (model) model->clearData();
|
||||
}
|
||||
|
||||
bool ChatMessageContentProxy::SortFilterList::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const {
|
||||
auto contentCore = getItemAtSource<ChatMessageContentList, ChatMessageContentCore>(sourceRow);
|
||||
if (contentCore) {
|
||||
if (mFilterType == (int)FilterContentType::Unknown) return false;
|
||||
else if (mFilterType == (int)FilterContentType::File) {
|
||||
return !contentCore->isVoiceRecording() && (contentCore->isFile() || contentCore->isFileTransfer());
|
||||
} else if (mFilterType == (int)FilterContentType::Text) return contentCore->isText();
|
||||
else if (mFilterType == (int)FilterContentType::Voice) return contentCore->isVoiceRecording();
|
||||
else if (mFilterType == (int)FilterContentType::Conference) return contentCore->isCalendar();
|
||||
else if (mFilterType == (int)FilterContentType::All) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ChatMessageContentProxy::SortFilterList::lessThan(const QModelIndex &sourceLeft,
|
||||
const QModelIndex &sourceRight) const {
|
||||
return true;
|
||||
}
|
||||
|
|
@ -0,0 +1,66 @@
|
|||
/*
|
||||
* 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_CONENT_PROXY_H_
|
||||
#define CHAT_MESSAGE_CONENT_PROXY_H_
|
||||
|
||||
#include "ChatMessageContentList.hpp"
|
||||
#include "core/proxy/LimitProxy.hpp"
|
||||
#include "tool/AbstractObject.hpp"
|
||||
|
||||
// =============================================================================
|
||||
|
||||
class ChatMessageGui;
|
||||
class ChatMessageContentGui;
|
||||
|
||||
class ChatMessageContentProxy : public LimitProxy, public AbstractObject {
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(ChatMessageGui *chatMessageGui READ getChatMessageGui WRITE setChatMessageGui NOTIFY chatChanged)
|
||||
|
||||
public:
|
||||
enum class FilterContentType { Unknown = 0, File = 1, Text = 2, Voice = 3, Conference = 4, All = 5 };
|
||||
Q_ENUM(FilterContentType)
|
||||
|
||||
DECLARE_SORTFILTER_CLASS(ChatMessageContentProxy *mHideListProxy = nullptr;)
|
||||
ChatMessageContentProxy(QObject *parent = Q_NULLPTR);
|
||||
~ChatMessageContentProxy();
|
||||
|
||||
ChatMessageGui *getChatMessageGui();
|
||||
void setChatMessageGui(ChatMessageGui *chat);
|
||||
|
||||
Q_INVOKABLE ChatMessageContentGui *getChatMessageContentAtIndex(int i);
|
||||
|
||||
void setSourceModel(QAbstractItemModel *sourceModel) override;
|
||||
|
||||
Q_INVOKABLE void addFiles(const QStringList &paths);
|
||||
Q_INVOKABLE void removeContent(ChatMessageContentGui *contentGui);
|
||||
Q_INVOKABLE void clear();
|
||||
|
||||
signals:
|
||||
void chatChanged();
|
||||
|
||||
protected:
|
||||
QSharedPointer<ChatMessageContentList> mList;
|
||||
ChatMessageGui *mChatMessageGui = nullptr;
|
||||
|
||||
DECLARE_ABSTRACT_OBJECT
|
||||
};
|
||||
|
||||
#endif
|
||||
61
Linphone/core/chat/message/imdn/ImdnStatusList.cpp
Normal file
61
Linphone/core/chat/message/imdn/ImdnStatusList.cpp
Normal 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/>.
|
||||
*/
|
||||
|
||||
#include "ImdnStatusList.hpp"
|
||||
#include "core/App.hpp"
|
||||
|
||||
#include <QSharedPointer>
|
||||
|
||||
#include <linphone++/linphone.hh>
|
||||
|
||||
// =============================================================================
|
||||
|
||||
DEFINE_ABSTRACT_OBJECT(ImdnStatusList)
|
||||
|
||||
QSharedPointer<ImdnStatusList> ImdnStatusList::create() {
|
||||
auto model = QSharedPointer<ImdnStatusList>(new ImdnStatusList(), &QObject::deleteLater);
|
||||
model->moveToThread(App::getInstance()->thread());
|
||||
return model;
|
||||
}
|
||||
|
||||
ImdnStatusList::ImdnStatusList(QObject *parent) : AbstractListProxy<ImdnStatus>(parent) {
|
||||
mustBeInMainThread(getClassName());
|
||||
App::getInstance()->mEngine->setObjectOwnership(this, QQmlEngine::CppOwnership);
|
||||
}
|
||||
|
||||
ImdnStatusList::~ImdnStatusList() {
|
||||
mustBeInMainThread("~" + getClassName());
|
||||
mList.clear();
|
||||
}
|
||||
|
||||
QList<ImdnStatus> ImdnStatusList::getImdnStatusList() {
|
||||
return mList;
|
||||
}
|
||||
|
||||
void ImdnStatusList::setImdnStatusList(QList<ImdnStatus> imdnStatusList) {
|
||||
resetData(imdnStatusList);
|
||||
}
|
||||
|
||||
QVariant ImdnStatusList::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(mList.at(row));
|
||||
return QVariant();
|
||||
}
|
||||
51
Linphone/core/chat/message/imdn/ImdnStatusList.hpp
Normal file
51
Linphone/core/chat/message/imdn/ImdnStatusList.hpp
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
/*
|
||||
* 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 IMDN_STATUS_LIST_H_
|
||||
#define IMDN_STATUS_LIST_H_
|
||||
|
||||
#include "core/chat/message/ChatMessageCore.hpp"
|
||||
#include "core/proxy/AbstractListProxy.hpp"
|
||||
#include "tool/AbstractObject.hpp"
|
||||
#include "tool/thread/SafeConnection.hpp"
|
||||
#include <QLocale>
|
||||
|
||||
// =============================================================================
|
||||
|
||||
class ImdnStatusList : public AbstractListProxy<ImdnStatus>, public AbstractObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
static QSharedPointer<ImdnStatusList> create();
|
||||
ImdnStatusList(QObject *parent = Q_NULLPTR);
|
||||
~ImdnStatusList();
|
||||
|
||||
QList<ImdnStatus> getImdnStatusList();
|
||||
void setImdnStatusList(QList<ImdnStatus> imdnStatusList);
|
||||
|
||||
virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
|
||||
|
||||
signals:
|
||||
void imdnStatusListChanged();
|
||||
|
||||
private:
|
||||
DECLARE_ABSTRACT_OBJECT
|
||||
};
|
||||
|
||||
#endif
|
||||
65
Linphone/core/chat/message/imdn/ImdnStatusProxy.cpp
Normal file
65
Linphone/core/chat/message/imdn/ImdnStatusProxy.cpp
Normal 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 "ImdnStatusProxy.hpp"
|
||||
#include "ImdnStatusList.hpp"
|
||||
#include "core/App.hpp"
|
||||
// #include "core/chat/message/ChatMessageGui.hpp"
|
||||
|
||||
DEFINE_ABSTRACT_OBJECT(ImdnStatusProxy)
|
||||
|
||||
ImdnStatusProxy::ImdnStatusProxy(QObject *parent) : LimitProxy(parent) {
|
||||
mList = ImdnStatusList::create();
|
||||
setSourceModel(mList.get());
|
||||
connect(mList.get(), &ImdnStatusList::modelReset, this, &ImdnStatusProxy::imdnStatusListChanged);
|
||||
connect(this, &ImdnStatusProxy::filterChanged, this, [this] { invalidate(); });
|
||||
}
|
||||
|
||||
ImdnStatusProxy::~ImdnStatusProxy() {
|
||||
}
|
||||
|
||||
QList<ImdnStatus> ImdnStatusProxy::getImdnStatusList() {
|
||||
return mList->getImdnStatusList();
|
||||
}
|
||||
|
||||
void ImdnStatusProxy::setImdnStatusList(QList<ImdnStatus> statusList) {
|
||||
mList->setImdnStatusList(statusList);
|
||||
}
|
||||
|
||||
LinphoneEnums::ChatMessageState ImdnStatusProxy::getFilter() const {
|
||||
return mFilter;
|
||||
}
|
||||
|
||||
void ImdnStatusProxy::setFilter(LinphoneEnums::ChatMessageState filter) {
|
||||
if (mFilter != filter) {
|
||||
mFilter = filter;
|
||||
emit filterChanged();
|
||||
}
|
||||
}
|
||||
|
||||
bool ImdnStatusProxy::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const {
|
||||
auto imdn = mList->getAt(sourceRow);
|
||||
return imdn.mState == mFilter;
|
||||
}
|
||||
|
||||
bool ImdnStatusProxy::lessThan(const QModelIndex &sourceLeft, const QModelIndex &sourceRight) const {
|
||||
return true;
|
||||
}
|
||||
62
Linphone/core/chat/message/imdn/ImdnStatusProxy.hpp
Normal file
62
Linphone/core/chat/message/imdn/ImdnStatusProxy.hpp
Normal 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 IMDN_STATUS_PROXY_H_
|
||||
#define IMDN_STATUS_PROXY_H_
|
||||
|
||||
#include "core/chat/message/ChatMessageCore.hpp"
|
||||
#include "core/proxy/LimitProxy.hpp"
|
||||
#include "tool/AbstractObject.hpp"
|
||||
|
||||
// =============================================================================
|
||||
|
||||
class ImdnStatusList;
|
||||
|
||||
class ImdnStatusProxy : public LimitProxy, public AbstractObject {
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(
|
||||
QList<ImdnStatus> imdnStatusList READ getImdnStatusList WRITE setImdnStatusList NOTIFY imdnStatusListChanged)
|
||||
Q_PROPERTY(LinphoneEnums::ChatMessageState filter READ getFilter WRITE setFilter NOTIFY filterChanged)
|
||||
|
||||
public:
|
||||
ImdnStatusProxy(QObject *parent = Q_NULLPTR);
|
||||
~ImdnStatusProxy();
|
||||
|
||||
QList<ImdnStatus> getImdnStatusList();
|
||||
void setImdnStatusList(QList<ImdnStatus> imdnStatusList);
|
||||
|
||||
LinphoneEnums::ChatMessageState getFilter() const;
|
||||
void setFilter(LinphoneEnums::ChatMessageState filter);
|
||||
|
||||
bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override;
|
||||
bool lessThan(const QModelIndex &sourceLeft, const QModelIndex &sourceRight) const override;
|
||||
|
||||
signals:
|
||||
void imdnStatusListChanged();
|
||||
void filterChanged();
|
||||
|
||||
protected:
|
||||
LinphoneEnums::ChatMessageState mFilter;
|
||||
QSharedPointer<ImdnStatusList> mList;
|
||||
DECLARE_ABSTRACT_OBJECT
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
@ -47,7 +47,12 @@ ConferenceCore::ConferenceCore(const std::shared_ptr<linphone::Conference> &conf
|
|||
mIsLocalScreenSharing = mConferenceModel->isLocalScreenSharing();
|
||||
mIsScreenSharingEnabled = mConferenceModel->isScreenSharingEnabled();
|
||||
mIsRecording = conference->isRecording();
|
||||
if (conference->getCurrentParams()) mIsChatEnabled = conference->getCurrentParams()->chatEnabled();
|
||||
auto me = conference->getMe();
|
||||
auto confAddress = conference->getConferenceAddress();
|
||||
if (confAddress) {
|
||||
mConfUri = Utils::coreStringToAppString(confAddress->asStringUriOnly());
|
||||
}
|
||||
if (me) {
|
||||
mMe = ParticipantCore::create(me);
|
||||
}
|
||||
|
|
@ -75,7 +80,14 @@ void ConferenceCore::setSelf(QSharedPointer<ConferenceCore> me) {
|
|||
if (newState == linphone::Conference::State::Created) {
|
||||
if (auto participantDevice = conference->getActiveSpeakerParticipantDevice()) {
|
||||
auto device = ParticipantDeviceCore::create(participantDevice);
|
||||
mConferenceModelConnection->invokeToCore([this, device]() { setActiveSpeakerDevice(device); });
|
||||
QString address;
|
||||
auto confAddress = conference->getConferenceAddress();
|
||||
if (confAddress) address = Utils::coreStringToAppString(confAddress->asStringUriOnly());
|
||||
mConferenceModelConnection->invokeToCore([this, device, address]() {
|
||||
setActiveSpeakerDevice(device);
|
||||
mConfUri = address;
|
||||
emit conferenceUriChanged();
|
||||
});
|
||||
} else if (conference->getParticipantDeviceList().size() > 1) {
|
||||
for (auto &device : conference->getParticipantDeviceList()) {
|
||||
if (!ToolModel::isMe(device->getAddress())) {
|
||||
|
|
@ -194,6 +206,10 @@ void ConferenceCore::setIsScreenSharingEnabled(bool state) {
|
|||
}
|
||||
}
|
||||
|
||||
bool ConferenceCore::isChatEnabled() const {
|
||||
return mIsChatEnabled;
|
||||
}
|
||||
|
||||
std::shared_ptr<ConferenceModel> ConferenceCore::getModel() const {
|
||||
return mConferenceModel;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -37,12 +37,14 @@ class ConferenceCore : public QObject, public AbstractObject {
|
|||
Q_OBJECT
|
||||
public:
|
||||
Q_PROPERTY(QDateTime startDate READ getStartDate CONSTANT)
|
||||
Q_PROPERTY(bool isChatEnabled READ isChatEnabled CONSTANT)
|
||||
// Q_PROPERTY(ParticipantDeviceList *participantDevices READ getParticipantDeviceList CONSTANT)
|
||||
// Q_PROPERTY(ParticipantModel* localParticipant READ getLocalParticipant NOTIFY localParticipantChanged)
|
||||
Q_PROPERTY(bool isReady MEMBER mIsReady WRITE setIsReady NOTIFY isReadyChanged)
|
||||
Q_PROPERTY(bool isRecording READ isRecording WRITE setRecording NOTIFY isRecordingChanged)
|
||||
|
||||
Q_PROPERTY(QString subject READ getSubject WRITE setSubject NOTIFY subjectChanged)
|
||||
Q_PROPERTY(QString uri MEMBER mConfUri NOTIFY conferenceUriChanged)
|
||||
Q_PROPERTY(bool isLocalScreenSharing MEMBER mIsLocalScreenSharing WRITE setIsLocalScreenSharing NOTIFY
|
||||
isLocalScreenSharingChanged)
|
||||
Q_PROPERTY(bool isScreenSharingEnabled MEMBER mIsScreenSharingEnabled WRITE setIsScreenSharingEnabled NOTIFY
|
||||
|
|
@ -80,6 +82,8 @@ public:
|
|||
void setIsLocalScreenSharing(bool state);
|
||||
void setIsScreenSharingEnabled(bool state);
|
||||
|
||||
bool isChatEnabled() const;
|
||||
|
||||
std::shared_ptr<ConferenceModel> getModel() const;
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
|
@ -92,6 +96,7 @@ signals:
|
|||
void activeSpeakerDeviceChanged();
|
||||
void subjectChanged();
|
||||
void isRecordingChanged();
|
||||
void conferenceUriChanged();
|
||||
|
||||
void lToggleScreenSharing();
|
||||
|
||||
|
|
@ -106,7 +111,9 @@ private:
|
|||
bool mIsRecording = false;
|
||||
bool mIsLocalScreenSharing = false;
|
||||
bool mIsScreenSharingEnabled = false;
|
||||
bool mIsChatEnabled = false;
|
||||
QString mSubject;
|
||||
QString mConfUri;
|
||||
QDateTime mStartDate = QDateTime::currentDateTime();
|
||||
|
||||
DECLARE_ABSTRACT_OBJECT
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
/*
|
||||
/*
|
||||
* Copyright (c) 2021 Belledonne Communications SARL.
|
||||
*
|
||||
* This file is part of linphone-desktop
|
||||
|
|
@ -21,12 +21,15 @@
|
|||
#include "ConferenceInfoCore.hpp"
|
||||
|
||||
#include "core/App.hpp"
|
||||
#include "core/path/Paths.hpp"
|
||||
#include "core/proxy/ListProxy.hpp"
|
||||
#include "model/object/VariantObject.hpp"
|
||||
#include "model/tool/ToolModel.hpp"
|
||||
#include "tool/Utils.hpp"
|
||||
#include "tool/thread/SafeConnection.hpp"
|
||||
|
||||
#include <QDesktopServices>
|
||||
|
||||
DEFINE_ABSTRACT_OBJECT(ConferenceInfoCore)
|
||||
|
||||
QSharedPointer<ConferenceInfoCore>
|
||||
|
|
@ -69,16 +72,13 @@ ConferenceInfoCore::ConferenceInfoCore(std::shared_ptr<linphone::ConferenceInfo>
|
|||
mUri = address && address->isValid() && !address->getDomain().empty()
|
||||
? Utils::coreStringToAppString(address->asStringUriOnly())
|
||||
: "";
|
||||
mIcalendarString = Utils::coreStringToAppString(conferenceInfo->getIcalendarString());
|
||||
mDateTime = QDateTime::fromMSecsSinceEpoch(conferenceInfo->getDateTime() * 1000);
|
||||
mDuration = conferenceInfo->getDuration();
|
||||
mEndDateTime = mDateTime.addSecs(mDuration * 60);
|
||||
mIsScheduled = mDateTime.isValid();
|
||||
mOrganizerAddress = Utils::coreStringToAppString(conferenceInfo->getOrganizer()->asStringUriOnly());
|
||||
mOrganizerName = Utils::coreStringToAppString(conferenceInfo->getOrganizer()->getDisplayName());
|
||||
if (mOrganizerName.isEmpty()) {
|
||||
mOrganizerName = Utils::coreStringToAppString(conferenceInfo->getOrganizer()->getUsername());
|
||||
mOrganizerName.replace(".", " ");
|
||||
}
|
||||
mOrganizerName = mConferenceInfoModel->getOrganizerName();
|
||||
mSubject = Utils::coreStringToAppString(conferenceInfo->getSubject());
|
||||
mDescription = Utils::coreStringToAppString(conferenceInfo->getDescription());
|
||||
mIsEnded = getDateTimeUtc().addSecs(mDuration * 60) < QDateTime::currentDateTimeUtc();
|
||||
|
|
@ -127,6 +127,7 @@ ConferenceInfoCore::ConferenceInfoCore(const ConferenceInfoCore &conferenceInfoC
|
|||
mIsEnded = conferenceInfoCore.mIsEnded;
|
||||
mInviteEnabled = conferenceInfoCore.mInviteEnabled;
|
||||
mConferenceInfoState = conferenceInfoCore.mConferenceInfoState;
|
||||
mIcalendarString = conferenceInfoCore.mIcalendarString;
|
||||
}
|
||||
|
||||
ConferenceInfoCore::~ConferenceInfoCore() {
|
||||
|
|
@ -179,6 +180,13 @@ void ConferenceInfoCore::setSelf(QSharedPointer<ConferenceInfoCore> me) {
|
|||
}
|
||||
});
|
||||
});
|
||||
mConfInfoModelConnection->makeConnectToCore(&ConferenceInfoCore::lCancelCreation, [this]() {
|
||||
mConfInfoModelConnection->invokeToModel([this] {
|
||||
if (mConferenceInfoModel) {
|
||||
mConferenceInfoModel->setConferenceScheduler(nullptr);
|
||||
}
|
||||
});
|
||||
});
|
||||
mConfInfoModelConnection->makeConnectToCore(&ConferenceInfoCore::lDeleteConferenceInfo, [this]() {
|
||||
mConfInfoModelConnection->invokeToModel([this] { mConferenceInfoModel->deleteConferenceInfo(); });
|
||||
});
|
||||
|
|
@ -192,21 +200,15 @@ void ConferenceInfoCore::setSelf(QSharedPointer<ConferenceInfoCore> me) {
|
|||
QString uri;
|
||||
if (state == linphone::ConferenceScheduler::State::Ready) {
|
||||
uri = mConferenceInfoModel->getConferenceScheduler()->getUri();
|
||||
emit CoreModel::getInstance() -> conferenceInfoReceived(
|
||||
CoreModel::getInstance()->getCore(),
|
||||
mConferenceInfoModel->getConferenceInfo());
|
||||
}
|
||||
mConfInfoModelConnection->invokeToCore([this, state = LinphoneEnums::fromLinphone(state),
|
||||
infoState = LinphoneEnums::fromLinphone(confInfoState),
|
||||
uri] {
|
||||
setConferenceSchedulerState(state);
|
||||
setConferenceInfoState(infoState);
|
||||
if (state == LinphoneEnums::ConferenceSchedulerState::Ready) {
|
||||
setUri(uri);
|
||||
mConfInfoModelConnection->invokeToModel([this, uri, infoState] {
|
||||
if (infoState == LinphoneEnums::ConferenceInfoState::New)
|
||||
emit CoreModel::getInstance()->conferenceInfoCreated(
|
||||
mConferenceInfoModel->getConferenceInfo());
|
||||
});
|
||||
}
|
||||
setConferenceSchedulerState(state);
|
||||
});
|
||||
});
|
||||
mConfInfoModelConnection->makeConnectToModel(
|
||||
|
|
@ -328,7 +330,8 @@ void ConferenceInfoCore::setTimeZoneModel(TimeZoneModel *model) {
|
|||
mTimeZoneModel->getStandardTimeOffset() != model->getStandardTimeOffset() ||
|
||||
mTimeZoneModel->getTimeZone() != model->getTimeZone()) {
|
||||
|
||||
mTimeZoneModel = QSharedPointer<TimeZoneModel>(model);
|
||||
mTimeZoneModel = QSharedPointer<TimeZoneModel>(new TimeZoneModel(model->getTimeZone()));
|
||||
|
||||
emit timeZoneModelChanged();
|
||||
}
|
||||
}
|
||||
|
|
@ -555,6 +558,10 @@ void ConferenceInfoCore::writeIntoModel(std::shared_ptr<ConferenceInfoModel> mod
|
|||
model->setParticipantInfos(participantInfos);
|
||||
}
|
||||
|
||||
std::shared_ptr<ConferenceInfoModel> ConferenceInfoCore::getModel() const {
|
||||
return mConferenceInfoModel;
|
||||
}
|
||||
|
||||
void ConferenceInfoCore::save() {
|
||||
mustBeInMainThread(getClassName() + "::save()");
|
||||
ConferenceInfoCore *thisCopy = new ConferenceInfoCore(*this); // Pointer to avoid multiple copies in lambdas
|
||||
|
|
@ -575,8 +582,8 @@ void ConferenceInfoCore::save() {
|
|||
linphone::RegistrationState::Ok) {
|
||||
//: "Erreur"
|
||||
Utils::showInformationPopup(tr("information_popup_error_title"),
|
||||
//: "Votre compte est déconnecté"
|
||||
tr("information_popup_disconnected_account_message"), false);
|
||||
//: "Votre compte est déconnecté"
|
||||
tr("information_popup_disconnected_account_message"), false);
|
||||
emit saveFailed();
|
||||
return;
|
||||
}
|
||||
|
|
@ -599,6 +606,9 @@ void ConferenceInfoCore::save() {
|
|||
} else lCritical() << "No default account";
|
||||
// Add text capability for chat in conf
|
||||
linphoneConf->setCapability(linphone::StreamType::Text, true);
|
||||
if (SettingsModel::getInstance()->getCreateEndToEndEncryptedMeetingsAndGroupCalls())
|
||||
linphoneConf->setSecurityLevel(linphone::Conference::SecurityLevel::EndToEnd);
|
||||
else linphoneConf->setSecurityLevel(linphone::Conference::SecurityLevel::PointToPoint);
|
||||
auto confInfoModel = Utils::makeQObject_ptr<ConferenceInfoModel>(linphoneConf);
|
||||
auto confSchedulerModel = confInfoModel->getConferenceScheduler();
|
||||
if (!confSchedulerModel) {
|
||||
|
|
@ -637,12 +647,6 @@ void ConferenceInfoCore::undo() {
|
|||
}
|
||||
}
|
||||
|
||||
void ConferenceInfoCore::cancelCreation() {
|
||||
if (mConferenceInfoModel) {
|
||||
mConferenceInfoModel->setConferenceScheduler(nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
|
||||
void ConferenceInfoCore::onInvitationsSent(const std::list<std::shared_ptr<linphone::Address>> &failedInvitations) {
|
||||
|
|
@ -654,3 +658,14 @@ bool ConferenceInfoCore::isAllDayConf() const {
|
|||
return mDateTime.time().hour() == 0 && mDateTime.time().minute() == 0 && mEndDateTime.time().hour() == 23 &&
|
||||
mEndDateTime.time().minute() == 59;
|
||||
}
|
||||
|
||||
void ConferenceInfoCore::exportConferenceToICS() const {
|
||||
QString filePath(Paths::getAppLocalDirPath() + "conference.ics");
|
||||
QFile file(filePath);
|
||||
if (file.open(QIODevice::WriteOnly | QIODevice::Text)) {
|
||||
QTextStream out(&file);
|
||||
out << mIcalendarString;
|
||||
file.close();
|
||||
}
|
||||
QDesktopServices::openUrl(QUrl::fromLocalFile(filePath));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
/*
|
||||
/*
|
||||
* Copyright (c) 2022 Belledonne Communications SARL.
|
||||
*
|
||||
* This file is part of linphone-desktop
|
||||
|
|
@ -126,14 +126,17 @@ public:
|
|||
void writeFromModel(const std::shared_ptr<ConferenceInfoModel> &model);
|
||||
void writeIntoModel(std::shared_ptr<ConferenceInfoModel> model);
|
||||
|
||||
std::shared_ptr<ConferenceInfoModel> getModel() const;
|
||||
|
||||
Q_INVOKABLE void save();
|
||||
Q_INVOKABLE void undo();
|
||||
Q_INVOKABLE void cancelCreation();
|
||||
|
||||
virtual void onInvitationsSent(const std::list<std::shared_ptr<linphone::Address>> &failedInvitations);
|
||||
|
||||
Q_INVOKABLE bool isAllDayConf() const;
|
||||
|
||||
Q_INVOKABLE void exportConferenceToICS() const;
|
||||
|
||||
signals:
|
||||
void dateTimeChanged();
|
||||
void endDateTimeChanged();
|
||||
|
|
@ -158,6 +161,7 @@ signals:
|
|||
void invitationsSent();
|
||||
void removed(ConferenceInfoCore *confInfo);
|
||||
|
||||
void lCancelCreation();
|
||||
void lCancelConferenceInfo();
|
||||
void lDeleteConferenceInfo(); // Remove completly this conference info from DB
|
||||
|
||||
|
|
@ -175,6 +179,7 @@ private:
|
|||
QString mSubject;
|
||||
QString mDescription;
|
||||
QString mUri;
|
||||
QString mIcalendarString;
|
||||
QVariantList mParticipants;
|
||||
QSharedPointer<TimeZoneModel> mTimeZoneModel;
|
||||
LinphoneEnums::ConferenceSchedulerState mConferenceSchedulerState;
|
||||
|
|
|
|||
|
|
@ -58,58 +58,119 @@ ConferenceInfoList::~ConferenceInfoList() {
|
|||
void ConferenceInfoList::setSelf(QSharedPointer<ConferenceInfoList> me) {
|
||||
mCoreModelConnection = SafeConnection<ConferenceInfoList, CoreModel>::create(me, CoreModel::getInstance());
|
||||
|
||||
mCoreModelConnection->makeConnectToCore(&ConferenceInfoList::lUpdate, [this](bool isInitialization) {
|
||||
mCoreModelConnection->invokeToModel([this, isInitialization]() {
|
||||
QList<QSharedPointer<ConferenceInfoCore>> *items = new QList<QSharedPointer<ConferenceInfoCore>>();
|
||||
mCoreModelConnection->makeConnectToCore(&ConferenceInfoList::lUpdate, [this]() {
|
||||
if (mIsUpdating) {
|
||||
connect(this, &ConferenceInfoList::isUpdatingChanged, this, [this] {
|
||||
if (!mIsUpdating) {
|
||||
disconnect(this, &ConferenceInfoList::isUpdatingChanged, this, nullptr);
|
||||
lUpdate();
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
setIsUpdating(true);
|
||||
mCoreModelConnection->invokeToModel([this]() {
|
||||
mustBeInLinphoneThread(getClassName());
|
||||
beginResetModel();
|
||||
mList.clear();
|
||||
QList<QSharedPointer<ConferenceInfoCore>> *items = new QList<QSharedPointer<ConferenceInfoCore>>();
|
||||
auto defaultAccount = CoreModel::getInstance()->getCore()->getDefaultAccount();
|
||||
if (!defaultAccount) return;
|
||||
setAccountConnected(defaultAccount && defaultAccount->getState() == linphone::RegistrationState::Ok);
|
||||
if (!defaultAccount || !mAccountConnected) {
|
||||
endResetModel();
|
||||
setIsUpdating(false);
|
||||
return;
|
||||
}
|
||||
std::list<std::shared_ptr<linphone::ConferenceInfo>> conferenceInfos =
|
||||
defaultAccount->getConferenceInformationList();
|
||||
if (conferenceInfos.empty()) {
|
||||
endResetModel();
|
||||
setIsUpdating(false);
|
||||
return;
|
||||
}
|
||||
items->push_back(nullptr); // Add Dummy conference for today
|
||||
for (auto conferenceInfo : conferenceInfos) {
|
||||
if (conferenceInfo->getState() == linphone::ConferenceInfo::State::Cancelled) {
|
||||
auto myAddress = defaultAccount->getParams()->getIdentityAddress();
|
||||
if (!myAddress || myAddress->weakEqual(conferenceInfo->getOrganizer())) continue;
|
||||
}
|
||||
// if (conferenceInfo->getState() == linphone::ConferenceInfo::State::Cancelled) {
|
||||
// auto myAddress = defaultAccount->getParams()->getIdentityAddress();
|
||||
// if (!myAddress || myAddress->weakEqual(conferenceInfo->getOrganizer())) continue;
|
||||
// }
|
||||
auto confInfoCore = build(conferenceInfo);
|
||||
// Cancelled conference organized ourself me must be hidden
|
||||
if (confInfoCore) {
|
||||
// qDebug() << log().arg("Add conf") << confInfoCore->getSubject() << "with state"
|
||||
// << confInfoCore->getConferenceInfoState();
|
||||
items->push_back(confInfoCore);
|
||||
}
|
||||
}
|
||||
mCoreModelConnection->invokeToCore([this, items, isInitialization]() {
|
||||
mCoreModelConnection->invokeToCore([this, items]() {
|
||||
mustBeInMainThread(getClassName());
|
||||
for (auto &item : *items) {
|
||||
connectItem(item);
|
||||
mList << item.template objectCast<QObject>();
|
||||
}
|
||||
resetData(*items);
|
||||
updateHaveCurrentDate();
|
||||
endResetModel();
|
||||
setIsUpdating(false);
|
||||
delete items;
|
||||
if (isInitialization) {
|
||||
emit initialized();
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// This is needed because account does not have a contact address until
|
||||
// it is connected, so we can't verify if it is the organizer of a deleted
|
||||
// conference (which must hidden)
|
||||
mCoreModelConnection->makeConnectToModel(&CoreModel::defaultAccountChanged, [this]() { emit lUpdate(true); });
|
||||
|
||||
mCoreModelConnection->makeConnectToModel(
|
||||
&CoreModel::conferenceInfoCreated,
|
||||
[this](const std::shared_ptr<linphone::ConferenceInfo> &confInfo) { addConference(confInfo); });
|
||||
mCoreModelConnection->makeConnectToModel(
|
||||
&CoreModel::conferenceInfoReceived,
|
||||
[this](const std::shared_ptr<linphone::Core> &core,
|
||||
const std::shared_ptr<const linphone::ConferenceInfo> &conferenceInfo) {
|
||||
lDebug() << log().arg("conference info received") << conferenceInfo->getSubject();
|
||||
addConference(conferenceInfo->clone());
|
||||
lInfo() << log().arg("conference info received") << conferenceInfo->getSubject();
|
||||
// We must refresh all the conference infos cause we are not able to determine
|
||||
// which account is concerned by the signal if multiple accounts are connected
|
||||
emit lUpdate();
|
||||
});
|
||||
emit lUpdate(true);
|
||||
|
||||
// This is needed because account does not have a contact address until
|
||||
// it is connected, so we can't verify if it is the organizer of a deleted
|
||||
// conference (which must hidden)
|
||||
mCoreModelConnection->makeConnectToModel(
|
||||
&CoreModel::defaultAccountChanged,
|
||||
[this](const std::shared_ptr<linphone::Core> &core, const std::shared_ptr<linphone::Account> &account) {
|
||||
auto accountCore = account ? AccountCore::create(account) : nullptr;
|
||||
mCoreModelConnection->invokeToCore([this, accountCore] {
|
||||
if (mCurrentAccountCore) {
|
||||
disconnect(mCurrentAccountCore.get(), &AccountCore::registrationStateChanged, this, nullptr);
|
||||
disconnect(mCurrentAccountCore.get(), &AccountCore::conferenceInformationUpdated, this, nullptr);
|
||||
}
|
||||
mCurrentAccountCore = accountCore;
|
||||
if (mCurrentAccountCore) {
|
||||
connect(mCurrentAccountCore.get(), &AccountCore::registrationStateChanged, this,
|
||||
[this] { emit lUpdate(); });
|
||||
connect(mCurrentAccountCore.get(), &AccountCore::conferenceInformationUpdated, this,
|
||||
[this] { emit lUpdate(); });
|
||||
}
|
||||
emit lUpdate();
|
||||
});
|
||||
});
|
||||
mCoreModelConnection->invokeToModel([this] {
|
||||
auto defaultAccount = CoreModel::getInstance()->getCore()->getDefaultAccount();
|
||||
auto accountCore = defaultAccount ? AccountCore::create(defaultAccount) : nullptr;
|
||||
mCoreModelConnection->invokeToCore([this, accountCore] {
|
||||
mCurrentAccountCore = accountCore;
|
||||
if (mCurrentAccountCore) {
|
||||
connect(mCurrentAccountCore.get(), &AccountCore::registrationStateChanged, this,
|
||||
[this] { emit lUpdate(); });
|
||||
connect(mCurrentAccountCore.get(), &AccountCore::conferenceInformationUpdated, this,
|
||||
[this] { emit lUpdate(); });
|
||||
}
|
||||
});
|
||||
});
|
||||
emit lUpdate();
|
||||
}
|
||||
|
||||
void ConferenceInfoList::setAccountConnected(bool connected) {
|
||||
if (mAccountConnected != connected) {
|
||||
mAccountConnected = connected;
|
||||
emit accountConnectedChanged(mAccountConnected);
|
||||
}
|
||||
}
|
||||
|
||||
bool ConferenceInfoList::getAccountConnected() const {
|
||||
return mAccountConnected;
|
||||
}
|
||||
|
||||
void ConferenceInfoList::resetData(QList<QSharedPointer<ConferenceInfoCore>> data) {
|
||||
|
|
@ -129,6 +190,7 @@ void ConferenceInfoList::addConference(const std::shared_ptr<linphone::Conferenc
|
|||
return confInfo->getUri()->weakEqual(confAddr);
|
||||
});
|
||||
if (haveConf == list.end()) {
|
||||
if (confInfo->getState() == linphone::ConferenceInfo::State::Cancelled) return;
|
||||
auto confInfoCore = build(confInfo);
|
||||
mCoreModelConnection->invokeToCore([this, confInfoCore] {
|
||||
connectItem(confInfoCore);
|
||||
|
|
@ -173,13 +235,17 @@ int ConferenceInfoList::getCurrentDateIndex() {
|
|||
return it == confInfoList.end() ? -1 : std::distance(confInfoList.begin(), it);
|
||||
}
|
||||
|
||||
QSharedPointer<ConferenceInfoCore> ConferenceInfoList::getCurrentDateConfInfo() {
|
||||
QSharedPointer<ConferenceInfoCore> ConferenceInfoList::getCurrentDateConfInfo(bool enableCancelledConference) {
|
||||
auto today = QDate::currentDate();
|
||||
auto confInfoList = getSharedList<ConferenceInfoCore>();
|
||||
QList<QSharedPointer<ConferenceInfoCore>>::iterator it;
|
||||
if (mHaveCurrentDate) {
|
||||
it = std::find_if(confInfoList.begin(), confInfoList.end(),
|
||||
[today](const QSharedPointer<ConferenceInfoCore> &item) {
|
||||
[today, enableCancelledConference](const QSharedPointer<ConferenceInfoCore> &item) {
|
||||
if (!item) return false;
|
||||
if (!enableCancelledConference &&
|
||||
item->getConferenceInfoState() == LinphoneEnums::ConferenceInfoState::Cancelled)
|
||||
return false;
|
||||
return item && item->getDateTimeUtc().date() == today;
|
||||
});
|
||||
} else it = std::find(confInfoList.begin(), confInfoList.end(), nullptr);
|
||||
|
|
@ -236,7 +302,9 @@ QVariant ConferenceInfoList::data(const QModelIndex &index, int role) const {
|
|||
if (role == Qt::DisplayRole) {
|
||||
return QVariant::fromValue(new ConferenceInfoGui(mList[row].objectCast<ConferenceInfoCore>()));
|
||||
} else if (role == Qt::DisplayRole + 1) {
|
||||
return Utils::toDateMonthString(mList[row].objectCast<ConferenceInfoCore>()->getDateTimeUtc());
|
||||
auto date = mList[row].objectCast<ConferenceInfoCore>()->getDateTimeUtc();
|
||||
if (date.date().year() != QDate::currentDate().year()) return Utils::toDateMonthAndYearString(date);
|
||||
else return Utils::toDateMonthString(date);
|
||||
}
|
||||
} else { // Dummy date
|
||||
if (role == Qt::DisplayRole) {
|
||||
|
|
|
|||
|
|
@ -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).
|
||||
|
|
@ -48,28 +48,32 @@ public:
|
|||
void updateHaveCurrentDate();
|
||||
|
||||
int getCurrentDateIndex();
|
||||
QSharedPointer<ConferenceInfoCore> getCurrentDateConfInfo();
|
||||
QSharedPointer<ConferenceInfoCore> getCurrentDateConfInfo(bool enableCancelledConference = false);
|
||||
|
||||
QSharedPointer<ConferenceInfoCore> build(const std::shared_ptr<linphone::ConferenceInfo> &conferenceInfo);
|
||||
void connectItem(QSharedPointer<ConferenceInfoCore> confInfoCore);
|
||||
|
||||
QHash<int, QByteArray> roleNames() const override;
|
||||
|
||||
void setAccountConnected(bool connected);
|
||||
bool getAccountConnected() const;
|
||||
|
||||
virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
|
||||
|
||||
signals:
|
||||
void lUpdate(bool isInitialization = false);
|
||||
void initialized();
|
||||
void lUpdate();
|
||||
void addCurrentDateChanged();
|
||||
void haveCurrentDateChanged();
|
||||
void currentDateIndexChanged(int index);
|
||||
void confInfoInserted(QSharedPointer<ConferenceInfoCore> data);
|
||||
void confInfoUpdated(QSharedPointer<ConferenceInfoCore> data);
|
||||
void accountConnectedChanged(bool connected);
|
||||
|
||||
private:
|
||||
QSharedPointer<SafeConnection<ConferenceInfoList, CoreModel>> mCoreModelConnection;
|
||||
QSharedPointer<AccountCore> mCurrentAccountCore;
|
||||
bool mHaveCurrentDate = false;
|
||||
bool mAccountConnected = false;
|
||||
DECLARE_ABSTRACT_OBJECT
|
||||
};
|
||||
#endif // CONFERENCE_INFO_LIST_H_
|
||||
|
|
|
|||
|
|
@ -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).
|
||||
|
|
@ -57,20 +57,26 @@ ConferenceInfoProxy::ConferenceInfoProxy(QObject *parent) : LimitProxy(parent) {
|
|||
if (isSignalConnected(conferenceInfoUpdatedSignal)) emit conferenceInfoUpdated(new ConferenceInfoGui(data));
|
||||
},
|
||||
Qt::QueuedConnection);
|
||||
connect(mList.get(), &ConferenceInfoList::initialized, this, &ConferenceInfoProxy::initialized);
|
||||
connect(mList.get(), &ConferenceInfoList::accountConnectedChanged, this,
|
||||
&ConferenceInfoProxy::accountConnectedChanged);
|
||||
}
|
||||
|
||||
ConferenceInfoProxy::~ConferenceInfoProxy() {
|
||||
}
|
||||
|
||||
bool ConferenceInfoProxy::haveCurrentDate() const {
|
||||
return mList->haveCurrentDate();
|
||||
return mList && mList->haveCurrentDate();
|
||||
}
|
||||
|
||||
bool ConferenceInfoProxy::getAccountConnected() const {
|
||||
return mList && mList->getAccountConnected();
|
||||
}
|
||||
|
||||
bool ConferenceInfoProxy::SortFilterList::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const {
|
||||
auto list = qobject_cast<ConferenceInfoList *>(sourceModel());
|
||||
auto ciCore = list->getAt<ConferenceInfoCore>(sourceRow);
|
||||
if (ciCore) {
|
||||
if (ciCore->getDuration() == 0) return false;
|
||||
bool searchTextInSubject = false;
|
||||
bool searchTextInParticipant = false;
|
||||
if (ciCore->getSubject().contains(mFilterText, Qt::CaseInsensitive)) searchTextInSubject = true;
|
||||
|
|
@ -99,6 +105,14 @@ void ConferenceInfoProxy::clear() {
|
|||
mList->clearData();
|
||||
}
|
||||
|
||||
ConferenceInfoGui *ConferenceInfoProxy::getCurrentDateConfInfo(bool enableCancelledConference) {
|
||||
if (mList) {
|
||||
auto confInfo = mList->getCurrentDateConfInfo();
|
||||
return confInfo ? new ConferenceInfoGui(confInfo) : nullptr;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
int ConferenceInfoProxy::loadUntil(ConferenceInfoGui *confInfo) {
|
||||
return loadUntil(confInfo ? confInfo->mCore : nullptr);
|
||||
}
|
||||
|
|
@ -136,7 +150,7 @@ bool ConferenceInfoProxy::SortFilterList::lessThan(const QModelIndex &sourceLeft
|
|||
auto nowDate = QDate::currentDate();
|
||||
if (!l || !r) { // sort on date
|
||||
auto rdate = r ? r->getDateTimeUtc().date() : QDate::currentDate();
|
||||
return !l ? nowDate <= r->getDateTimeUtc().date() : l->getDateTimeUtc().date() < nowDate;
|
||||
return !l ? nowDate < r->getDateTimeUtc().date() : l->getDateTimeUtc().date() < nowDate;
|
||||
} else {
|
||||
return l->getDateTimeUtc() < r->getDateTimeUtc();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@ class ConferenceInfoProxy : public LimitProxy, public AbstractObject {
|
|||
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(bool haveCurrentDate READ haveCurrentDate NOTIFY haveCurrentDateChanged)
|
||||
Q_PROPERTY(bool accountConnected READ getAccountConnected NOTIFY accountConnectedChanged)
|
||||
|
||||
public:
|
||||
enum ConferenceInfoFiltering { None = 0, Future = 1 };
|
||||
|
|
@ -43,15 +44,17 @@ public:
|
|||
~ConferenceInfoProxy();
|
||||
|
||||
bool haveCurrentDate() const;
|
||||
bool getAccountConnected() const;
|
||||
|
||||
Q_INVOKABLE void clear();
|
||||
Q_INVOKABLE ConferenceInfoGui *getCurrentDateConfInfo(bool enableCancelledConference = false);
|
||||
Q_INVOKABLE int loadUntil(ConferenceInfoGui *confInfo);
|
||||
int loadUntil(QSharedPointer<ConferenceInfoCore> data);
|
||||
signals:
|
||||
void initialized();
|
||||
void haveCurrentDateChanged();
|
||||
void conferenceInfoCreated(ConferenceInfoGui *confInfo);
|
||||
void conferenceInfoUpdated(ConferenceInfoGui *confInfo);
|
||||
void accountConnectedChanged(bool connected);
|
||||
|
||||
private:
|
||||
QSharedPointer<ConferenceInfoList> mList;
|
||||
|
|
|
|||
63
Linphone/core/emoji/EmojiList.cpp
Normal file
63
Linphone/core/emoji/EmojiList.cpp
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
/*
|
||||
* 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 "EmojiList.hpp"
|
||||
#include "core/App.hpp"
|
||||
#include "core/chat/ChatCore.hpp"
|
||||
#include "core/chat/message/content/ChatMessageContentGui.hpp"
|
||||
|
||||
#include <QMimeDatabase>
|
||||
#include <QSharedPointer>
|
||||
|
||||
#include <linphone++/linphone.hh>
|
||||
|
||||
// =============================================================================
|
||||
|
||||
DEFINE_ABSTRACT_OBJECT(EmojiList)
|
||||
|
||||
QSharedPointer<EmojiList> EmojiList::create() {
|
||||
auto model = QSharedPointer<EmojiList>(new EmojiList(), &QObject::deleteLater);
|
||||
model->moveToThread(App::getInstance()->thread());
|
||||
return model;
|
||||
}
|
||||
|
||||
EmojiList::EmojiList(QObject *parent) : AbstractListProxy<Reaction>(parent) {
|
||||
mustBeInMainThread(getClassName());
|
||||
App::getInstance()->mEngine->setObjectOwnership(this, QQmlEngine::CppOwnership);
|
||||
}
|
||||
|
||||
EmojiList::~EmojiList() {
|
||||
mustBeInMainThread("~" + getClassName());
|
||||
}
|
||||
|
||||
QList<Reaction> EmojiList::getReactions() {
|
||||
return mList;
|
||||
}
|
||||
|
||||
void EmojiList::setReactions(QList<Reaction> reactions) {
|
||||
resetData(reactions);
|
||||
}
|
||||
|
||||
QVariant EmojiList::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(mList.at(row));
|
||||
return QVariant();
|
||||
}
|
||||
49
Linphone/core/emoji/EmojiList.hpp
Normal file
49
Linphone/core/emoji/EmojiList.hpp
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
/*
|
||||
* 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 EMOJI_LIST_H_
|
||||
#define EMOJI_LIST_H_
|
||||
|
||||
#include "core/chat/message/ChatMessageCore.hpp"
|
||||
#include "core/proxy/AbstractListProxy.hpp"
|
||||
#include "tool/AbstractObject.hpp"
|
||||
#include "tool/thread/SafeConnection.hpp"
|
||||
#include <QLocale>
|
||||
|
||||
// =============================================================================
|
||||
|
||||
class EmojiList : public AbstractListProxy<Reaction>, public AbstractObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
static QSharedPointer<EmojiList> create();
|
||||
EmojiList(QObject *parent = Q_NULLPTR);
|
||||
~EmojiList();
|
||||
|
||||
QList<Reaction> getReactions();
|
||||
void setReactions(QList<Reaction> reactions);
|
||||
|
||||
virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
|
||||
|
||||
private:
|
||||
QList<Reaction> mReactions;
|
||||
DECLARE_ABSTRACT_OBJECT
|
||||
};
|
||||
|
||||
#endif
|
||||
91
Linphone/core/emoji/EmojiModel.cpp
Normal file
91
Linphone/core/emoji/EmojiModel.cpp
Normal file
|
|
@ -0,0 +1,91 @@
|
|||
/*
|
||||
* MIT License
|
||||
|
||||
Copyright (c) 2023 AmirHosseinCH
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "EmojiModel.hpp"
|
||||
#include "core/path/Paths.hpp"
|
||||
#include "tool/Constants.hpp"
|
||||
|
||||
EmojiModel::EmojiModel() {
|
||||
QFile file(QString(":/data/emoji/emoji.json"));
|
||||
auto open = file.open(QIODevice::ReadOnly);
|
||||
QByteArray data = file.readAll();
|
||||
file.close();
|
||||
QJsonDocument doc = QJsonDocument::fromJson(data);
|
||||
QJsonObject rootObj = doc.object();
|
||||
for (auto category{rootObj.begin()}; category != rootObj.end(); ++category) {
|
||||
emojies[category.key()] = category.value().toArray();
|
||||
QJsonArray &emojiesData = emojies[category.key()];
|
||||
for (auto it{emojiesData.begin()}; it != emojiesData.end(); ++it) {
|
||||
QJsonObject emoji = it->toObject();
|
||||
QJsonArray allKeywords = emoji.value("keywords").toArray();
|
||||
for (auto k{allKeywords.begin()}; k != allKeywords.end(); ++k) {
|
||||
keywords[k->toString()].append(emoji);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int EmojiModel::count(QString category) {
|
||||
return emojies[category].size();
|
||||
}
|
||||
|
||||
QString EmojiModel::path(QString category, int index, int skinColor) {
|
||||
QJsonObject emoji = emojies[category][index].toObject();
|
||||
if (emoji.contains("types") && skinColor != -1) {
|
||||
QJsonArray types = emoji.value("types").toArray();
|
||||
return mIconsPath + types[skinColor].toString() + mIconsType;
|
||||
} else return mIconsPath + emoji.value("code").toString() + mIconsType;
|
||||
}
|
||||
|
||||
QVector<QString> EmojiModel::search(QString searchKey, int skinColor) {
|
||||
bool foundFirstItem{false};
|
||||
QVector<QString> searchResult;
|
||||
for (auto it{keywords.begin()}; it != keywords.end(); ++it) {
|
||||
if (it.key().startsWith(searchKey)) {
|
||||
QVector<QJsonObject> &emojiesData{it.value()};
|
||||
for (auto emoji{emojiesData.begin()}; emoji != emojiesData.end(); ++emoji) {
|
||||
if (emoji->contains("types") && skinColor != -1) {
|
||||
QJsonArray types = emoji->value("types").toArray();
|
||||
QString path = mIconsPath + types[skinColor].toString() + mIconsType;
|
||||
if (!searchResult.contains(path)) searchResult.append(path);
|
||||
} else {
|
||||
QString path = mIconsPath + emoji->value("code").toString() + mIconsType;
|
||||
if (!searchResult.contains(path)) searchResult.append(path);
|
||||
}
|
||||
}
|
||||
foundFirstItem = true;
|
||||
} else if (foundFirstItem) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return searchResult;
|
||||
}
|
||||
|
||||
void EmojiModel::setIconsPath(QString path) {
|
||||
mIconsPath = path;
|
||||
}
|
||||
|
||||
void EmojiModel::setIconsType(QString type) {
|
||||
mIconsType = type;
|
||||
}
|
||||
54
Linphone/core/emoji/EmojiModel.hpp
Normal file
54
Linphone/core/emoji/EmojiModel.hpp
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
/*
|
||||
* MIT License
|
||||
|
||||
Copyright (c) 2023 AmirHosseinCH
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef EMOJIMODEL_H
|
||||
#define EMOJIMODEL_H
|
||||
|
||||
#include <QFile>
|
||||
#include <QJsonArray>
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonObject>
|
||||
#include <QObject>
|
||||
|
||||
class EmojiModel : public QObject {
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(QString iconsPath WRITE setIconsPath MEMBER mIconsPath)
|
||||
Q_PROPERTY(QString iconsType WRITE setIconsType MEMBER mIconsType)
|
||||
public:
|
||||
EmojiModel();
|
||||
void setIconsPath(QString);
|
||||
void setIconsType(QString);
|
||||
public slots:
|
||||
int count(QString);
|
||||
QString path(QString, int, int = -1);
|
||||
QVector<QString> search(QString, int = -1);
|
||||
|
||||
private:
|
||||
QString mIconsPath;
|
||||
QString mIconsType;
|
||||
QMap<QString, QJsonArray> emojies;
|
||||
QMap<QString, QVector<QJsonObject>> keywords;
|
||||
};
|
||||
|
||||
#endif
|
||||
65
Linphone/core/emoji/EmojiProxy.cpp
Normal file
65
Linphone/core/emoji/EmojiProxy.cpp
Normal 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 "EmojiProxy.hpp"
|
||||
#include "EmojiList.hpp"
|
||||
#include "core/App.hpp"
|
||||
// #include "core/chat/message/ChatMessageGui.hpp"
|
||||
|
||||
DEFINE_ABSTRACT_OBJECT(EmojiProxy)
|
||||
|
||||
EmojiProxy::EmojiProxy(QObject *parent) : LimitProxy(parent) {
|
||||
mList = EmojiList::create();
|
||||
setSourceModel(mList.get());
|
||||
connect(mList.get(), &EmojiList::modelReset, this, &EmojiProxy::reactionsChanged);
|
||||
connect(this, &EmojiProxy::filterChanged, this, [this] { invalidate(); });
|
||||
}
|
||||
|
||||
EmojiProxy::~EmojiProxy() {
|
||||
}
|
||||
|
||||
QList<Reaction> EmojiProxy::getReactions() {
|
||||
return mList->getReactions();
|
||||
}
|
||||
|
||||
void EmojiProxy::setReactions(QList<Reaction> reactions) {
|
||||
mList->setReactions(reactions);
|
||||
}
|
||||
|
||||
QString EmojiProxy::getFilter() const {
|
||||
return mFilter;
|
||||
}
|
||||
|
||||
void EmojiProxy::setFilter(QString filter) {
|
||||
if (mFilter != filter) {
|
||||
mFilter = filter;
|
||||
emit filterChanged();
|
||||
}
|
||||
}
|
||||
|
||||
bool EmojiProxy::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const {
|
||||
auto emoji = mList->getAt(sourceRow);
|
||||
return emoji.mBody.contains(mFilter);
|
||||
}
|
||||
|
||||
bool EmojiProxy::lessThan(const QModelIndex &sourceLeft, const QModelIndex &sourceRight) const {
|
||||
return true;
|
||||
}
|
||||
62
Linphone/core/emoji/EmojiProxy.hpp
Normal file
62
Linphone/core/emoji/EmojiProxy.hpp
Normal 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 EMOJI_PROXY_H_
|
||||
#define EMOJI_PROXY_H_
|
||||
|
||||
#include "core/chat/message/ChatMessageCore.hpp"
|
||||
#include "core/emoji/EmojiModel.hpp"
|
||||
#include "core/proxy/LimitProxy.hpp"
|
||||
#include "tool/AbstractObject.hpp"
|
||||
|
||||
// =============================================================================
|
||||
|
||||
class EmojiList;
|
||||
|
||||
class EmojiProxy : public LimitProxy, public AbstractObject {
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(QList<Reaction> reactions READ getReactions WRITE setReactions NOTIFY reactionsChanged)
|
||||
Q_PROPERTY(QString filter READ getFilter WRITE setFilter NOTIFY filterChanged)
|
||||
|
||||
public:
|
||||
EmojiProxy(QObject *parent = Q_NULLPTR);
|
||||
~EmojiProxy();
|
||||
|
||||
QList<Reaction> getReactions();
|
||||
void setReactions(QList<Reaction> reactions);
|
||||
|
||||
QString getFilter() const;
|
||||
void setFilter(QString filter);
|
||||
|
||||
bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override;
|
||||
bool lessThan(const QModelIndex &sourceLeft, const QModelIndex &sourceRight) const override;
|
||||
|
||||
signals:
|
||||
void reactionsChanged();
|
||||
void filterChanged();
|
||||
|
||||
protected:
|
||||
QString mFilter;
|
||||
QSharedPointer<EmojiList> mList;
|
||||
DECLARE_ABSTRACT_OBJECT
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
@ -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/>.
|
||||
*/
|
||||
|
||||
#include <QtDebug>
|
||||
|
||||
#include "core/App.hpp"
|
||||
#include "model/core/CoreModel.hpp"
|
||||
#include "model/setting/SettingsModel.hpp"
|
||||
|
||||
#include "AbstractEventCountNotifier.hpp"
|
||||
|
||||
// =============================================================================
|
||||
|
||||
using namespace std;
|
||||
|
||||
DEFINE_ABSTRACT_OBJECT(AbstractEventCountNotifier)
|
||||
|
||||
AbstractEventCountNotifier::AbstractEventCountNotifier(QObject *parent) : QObject(parent) {
|
||||
}
|
||||
|
||||
int AbstractEventCountNotifier::getEventCount() const {
|
||||
mustBeInLinphoneThread(log().arg(Q_FUNC_INFO));
|
||||
auto coreModel = CoreModel::getInstance();
|
||||
int count = coreModel->getCore()->getMissedCallsCount();
|
||||
if (SettingsModel::getInstance()->getStandardChatEnabled() || SettingsModel::getInstance()->getSecureChatEnabled())
|
||||
count += coreModel->getCore()->getUnreadChatMessageCountFromActiveLocals();
|
||||
return count;
|
||||
}
|
||||
|
||||
int AbstractEventCountNotifier::getCurrentEventCount() const {
|
||||
mustBeInLinphoneThread(log().arg(Q_FUNC_INFO));
|
||||
auto coreModel = CoreModel::getInstance();
|
||||
int count = coreModel->getCore()->getMissedCallsCount();
|
||||
bool filtered = SettingsModel::getInstance()->isSystrayNotificationFiltered();
|
||||
bool global = SettingsModel::getInstance()->isSystrayNotificationGlobal();
|
||||
// if (global && !filtered)
|
||||
if (true) return getEventCount();
|
||||
else {
|
||||
auto currentAccount = CoreModel::getInstance()->getCore()->getDefaultAccount();
|
||||
if (currentAccount) {
|
||||
count += currentAccount->getUnreadChatMessageCount();
|
||||
}
|
||||
return count;
|
||||
}
|
||||
}
|
||||
|
|
@ -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 ABSTRACT_EVENT_COUNT_NOTIFIER_H_
|
||||
#define ABSTRACT_EVENT_COUNT_NOTIFIER_H_
|
||||
|
||||
#include <QHash>
|
||||
#include <QObject>
|
||||
#include <QPair>
|
||||
#include <QSystemTrayIcon>
|
||||
|
||||
#include "tool/AbstractObject.hpp"
|
||||
#include "tool/thread/SafeConnection.hpp"
|
||||
|
||||
// =============================================================================
|
||||
|
||||
namespace linphone {
|
||||
class ChatMessage;
|
||||
}
|
||||
|
||||
class CallModel;
|
||||
class ChatRoomModel;
|
||||
class HistoryModel;
|
||||
|
||||
class AbstractEventCountNotifier : public QObject, public AbstractObject {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
AbstractEventCountNotifier(QObject *parent = Q_NULLPTR);
|
||||
|
||||
int getEventCount() const; // global
|
||||
int getCurrentEventCount() const; // Current account
|
||||
|
||||
protected:
|
||||
virtual void notifyEventCount(int n) = 0;
|
||||
|
||||
private:
|
||||
DECLARE_ABSTRACT_OBJECT
|
||||
};
|
||||
|
||||
#endif // ABSTRACT_EVENT_COUNT_NOTIFIER_H_
|
||||
|
|
@ -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/>.
|
||||
*/
|
||||
|
||||
#ifndef EVENT_COUNT_NOTIFIER_MAC_OS_H_
|
||||
#define EVENT_COUNT_NOTIFIER_MAC_OS_H_
|
||||
|
||||
#include "AbstractEventCountNotifier.hpp"
|
||||
|
||||
// =============================================================================
|
||||
|
||||
extern "C" void notifyEventCountMacOs(int n);
|
||||
|
||||
class EventCountNotifier : public AbstractEventCountNotifier {
|
||||
public:
|
||||
EventCountNotifier(QObject *parent = Q_NULLPTR) : AbstractEventCountNotifier(parent) {
|
||||
}
|
||||
|
||||
void notifyEventCount(int n) override {
|
||||
notifyEventCountMacOs(n);
|
||||
}
|
||||
};
|
||||
|
||||
#endif // EVENT_COUNT_NOTIFIER_MAC_OS_H_
|
||||
30
Linphone/core/event-count-notifier/EventCountNotifierMacOs.m
Normal file
30
Linphone/core/event-count-notifier/EventCountNotifierMacOs.m
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
* EventCountNotifierMacOs.m
|
||||
* Copyright (C) 2017-2018 Belledonne Communications, Grenoble, France
|
||||
*
|
||||
* 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 2
|
||||
* 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, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* Created on: June 30, 2017
|
||||
* Author: Ghislain MARY
|
||||
*/
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
// =============================================================================
|
||||
|
||||
void notifyEventCountMacOs (int n) {
|
||||
NSString *badgeStr = (n > 0) ? [NSString stringWithFormat:@"%d", n] : @"";
|
||||
[[NSApp dockTile] setBadgeLabel:badgeStr];
|
||||
}
|
||||
|
|
@ -0,0 +1,134 @@
|
|||
/*
|
||||
* 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 <QIcon>
|
||||
#include <QPainter>
|
||||
#include <QSvgRenderer>
|
||||
#include <QSystemTrayIcon>
|
||||
#include <QTimer>
|
||||
#include <QWindow>
|
||||
|
||||
#include "core/App.hpp"
|
||||
#include "model/core/CoreModel.hpp"
|
||||
#include "model/setting/SettingsModel.hpp"
|
||||
#include "tool/Constants.hpp"
|
||||
#include "tool/Utils.hpp"
|
||||
|
||||
#include "EventCountNotifierSystemTrayIcon.hpp"
|
||||
|
||||
// =============================================================================
|
||||
|
||||
namespace {
|
||||
constexpr int IconWidth = 256;
|
||||
constexpr int IconHeight = 256;
|
||||
|
||||
constexpr int IconCounterBackgroundRadius = 100;
|
||||
constexpr int IconCounterBlinkInterval = 1000;
|
||||
constexpr int IconCounterTextPixelSize = 144;
|
||||
} // namespace
|
||||
|
||||
DEFINE_ABSTRACT_OBJECT(EventCountNotifier)
|
||||
|
||||
QSharedPointer<EventCountNotifier> EventCountNotifier::create(QObject *parent) {
|
||||
auto sharedPointer = QSharedPointer<EventCountNotifier>(new EventCountNotifier(parent), &QObject::deleteLater);
|
||||
sharedPointer->setSelf(sharedPointer);
|
||||
sharedPointer->moveToThread(App::getInstance()->thread());
|
||||
return sharedPointer;
|
||||
}
|
||||
|
||||
EventCountNotifier::EventCountNotifier(QObject *parent) : AbstractEventCountNotifier(parent) {
|
||||
QSvgRenderer renderer((QString(Constants::WindowIconPath)));
|
||||
if (!renderer.isValid()) qFatal("Invalid SVG Image.");
|
||||
|
||||
QPixmap buf(IconWidth, IconHeight);
|
||||
buf.fill(QColor(Qt::transparent));
|
||||
|
||||
QPainter painter(&buf);
|
||||
renderer.render(&painter);
|
||||
|
||||
mBuf = new QPixmap(buf);
|
||||
mBufWithCounter = new QPixmap();
|
||||
|
||||
mBlinkTimer = new QTimer(this);
|
||||
mBlinkTimer->setInterval(IconCounterBlinkInterval);
|
||||
connect(mBlinkTimer, &QTimer::timeout, this, &EventCountNotifier::update);
|
||||
}
|
||||
|
||||
void EventCountNotifier::setSelf(QSharedPointer<EventCountNotifier> me) {
|
||||
}
|
||||
|
||||
EventCountNotifier::~EventCountNotifier() {
|
||||
delete mBuf;
|
||||
delete mBufWithCounter;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
void EventCountNotifier::notifyEventCount(int n) {
|
||||
mustBeInMainThread(log().arg(Q_FUNC_INFO));
|
||||
n = n > 99 ? 99 : n;
|
||||
QSystemTrayIcon *sysTrayIcon = App::getInstance()->getSystemTrayIcon();
|
||||
if (!sysTrayIcon) return;
|
||||
|
||||
if (!n) {
|
||||
mBlinkTimer->stop();
|
||||
sysTrayIcon->setIcon(QIcon(*mBuf));
|
||||
return;
|
||||
}
|
||||
|
||||
*mBufWithCounter = *mBuf;
|
||||
QPainter p(mBufWithCounter);
|
||||
|
||||
const int width = mBufWithCounter->width();
|
||||
const int height = mBufWithCounter->height();
|
||||
|
||||
// Draw background.
|
||||
{
|
||||
p.setBrush(QColor(Utils::getDefaultStyleColor("main1_100")));
|
||||
p.drawEllipse(QPointF(width / 2, height / 2), IconCounterBackgroundRadius, IconCounterBackgroundRadius);
|
||||
}
|
||||
|
||||
// Draw text.
|
||||
{
|
||||
QFont font = p.font();
|
||||
font.setPixelSize(IconCounterTextPixelSize);
|
||||
|
||||
p.setFont(font);
|
||||
p.setPen(QPen(QColor(Utils::getDefaultStyleColor("main1_500_main"))));
|
||||
p.drawText(QRect(0, 0, width, height), Qt::AlignCenter, QString::number(n));
|
||||
}
|
||||
|
||||
// Change counter.
|
||||
mBlinkTimer->stop();
|
||||
auto coreModel = CoreModel::getInstance();
|
||||
if (!coreModel->isInitialized() || SettingsModel::getInstance()->isSystrayNotificationBlinkEnabled())
|
||||
mBlinkTimer->start();
|
||||
mDisplayCounter = true;
|
||||
update();
|
||||
}
|
||||
|
||||
void EventCountNotifier::update() {
|
||||
mustBeInMainThread(log().arg(Q_FUNC_INFO));
|
||||
QSystemTrayIcon *sysTrayIcon = App::getInstance()->getSystemTrayIcon();
|
||||
if (sysTrayIcon) {
|
||||
sysTrayIcon->setIcon(QIcon(mDisplayCounter ? *mBufWithCounter : *mBuf));
|
||||
}
|
||||
mDisplayCounter = !mDisplayCounter;
|
||||
}
|
||||
|
|
@ -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 EVENT_COUNT_NOTIFIER_SYSTEM_TRAY_ICON_H_
|
||||
#define EVENT_COUNT_NOTIFIER_SYSTEM_TRAY_ICON_H_
|
||||
|
||||
#include "AbstractEventCountNotifier.hpp"
|
||||
|
||||
// =============================================================================
|
||||
|
||||
class QTimer;
|
||||
|
||||
class EventCountNotifier : public AbstractEventCountNotifier {
|
||||
public:
|
||||
static QSharedPointer<EventCountNotifier> create(QObject *parent = Q_NULLPTR);
|
||||
EventCountNotifier(QObject *parent = Q_NULLPTR);
|
||||
void setSelf(QSharedPointer<EventCountNotifier> me);
|
||||
~EventCountNotifier();
|
||||
|
||||
void notifyEventCount(int n) override;
|
||||
|
||||
private:
|
||||
const QPixmap *mBuf = nullptr;
|
||||
QPixmap *mBufWithCounter = nullptr;
|
||||
QTimer *mBlinkTimer = nullptr;
|
||||
bool mDisplayCounter = false;
|
||||
QSharedPointer<SafeConnection<EventCountNotifier, CoreModel>> mCoreModelConnection;
|
||||
void update();
|
||||
|
||||
DECLARE_ABSTRACT_OBJECT
|
||||
};
|
||||
|
||||
#endif // EVENT_COUNT_NOTIFIER_SYSTEM_TRAY_ICON_H_
|
||||
|
|
@ -48,8 +48,9 @@ FriendCore::FriendCore(const std::shared_ptr<linphone::Friend> &contact, bool is
|
|||
mustBeInLinphoneThread(getClassName());
|
||||
mFriendModel = Utils::makeQObject_ptr<FriendModel>(contact);
|
||||
mFriendModel->setSelf(mFriendModel);
|
||||
mConsolidatedPresence = LinphoneEnums::fromLinphone(contact->getConsolidatedPresence());
|
||||
mPresenceTimestamp = mFriendModel->getPresenceTimestamp();
|
||||
auto presence = mFriendModel->getPresence(contact);
|
||||
auto note = mFriendModel->getPresenceNote(contact);
|
||||
App::postCoreAsync([this, presence, note]() { setPresence(presence, note); });
|
||||
mPictureUri = Utils::coreStringToAppString(contact->getPhoto());
|
||||
mFullName = mFriendModel->getFullName();
|
||||
auto defaultAddress = contact->getAddress();
|
||||
|
|
@ -65,7 +66,7 @@ FriendCore::FriendCore(const std::shared_ptr<linphone::Friend> &contact, bool is
|
|||
auto addresses = contact->getAddresses();
|
||||
for (auto &address : addresses) {
|
||||
mAddressList.append(Utils::createFriendAddressVariant(
|
||||
tr("sip_address"), Utils::coreStringToAppString(address->asStringUriOnly())));
|
||||
tr("sip_address"), Utils::coreStringToAppString(address->asStringUriOnly())));
|
||||
}
|
||||
mDefaultAddress = defaultAddress ? Utils::coreStringToAppString(defaultAddress->asStringUriOnly()) : QString();
|
||||
mDefaultFullAddress = defaultAddress ? Utils::coreStringToAppString(defaultAddress->asString()) : QString();
|
||||
|
|
@ -90,7 +91,8 @@ FriendCore::FriendCore(const std::shared_ptr<linphone::Friend> &contact, bool is
|
|||
mStarred = contact->getStarred();
|
||||
mIsSaved = true;
|
||||
mIsStored = isStored;
|
||||
mIsLdap = ToolModel::friendIsInFriendList(ToolModel::getLdapFriendList(), contact);
|
||||
mIsLdap = ToolModel::friendIsInFriendList(ToolModel::getLdapFriendList(), contact) ||
|
||||
(sourceFlags & (int)linphone::MagicSearch::Source::LdapServers) != 0;
|
||||
mIsCardDAV = (sourceFlags & (int)linphone::MagicSearch::Source::RemoteCardDAV) != 0;
|
||||
mIsAppFriend = ToolModel::friendIsInFriendList(ToolModel::getAppFriendList(), contact);
|
||||
} else {
|
||||
|
|
@ -142,8 +144,7 @@ void FriendCore::setSelf(QSharedPointer<FriendCore> me) {
|
|||
mFriendModelConnection->makeConnectToModel(
|
||||
&FriendModel::removed, [this]() { mFriendModelConnection->invokeToCore([this]() { removed(this); }); });
|
||||
mFriendModelConnection->makeConnectToModel(
|
||||
&FriendModel::presenceReceived,
|
||||
[this](LinphoneEnums::ConsolidatedPresence consolidatedPresence, QDateTime presenceTimestamp) {
|
||||
&FriendModel::presenceReceived, [this](LinphoneEnums::Presence presence, QString presenceNote) {
|
||||
auto devices = mFriendModel->getDevices();
|
||||
QVariantList devicesList;
|
||||
for (auto &device : devices) {
|
||||
|
|
@ -153,14 +154,11 @@ void FriendCore::setSelf(QSharedPointer<FriendCore> me) {
|
|||
Utils::coreStringToAppString(device->getAddress()->asString()),
|
||||
LinphoneEnums::fromLinphone(device->getSecurityLevel())));
|
||||
}
|
||||
mFriendModelConnection->invokeToCore(
|
||||
[this, consolidatedPresence, presenceTimestamp, devicesList]() {
|
||||
setConsolidatedPresence(consolidatedPresence);
|
||||
setPresenceTimestamp(presenceTimestamp);
|
||||
|
||||
setDevices(devicesList);
|
||||
updateVerifiedDevicesCount();
|
||||
});
|
||||
mFriendModelConnection->invokeToCore([this, presence, devicesList, presenceNote]() {
|
||||
setPresence(presence, presenceNote);
|
||||
setDevices(devicesList);
|
||||
updateVerifiedDevicesCount();
|
||||
});
|
||||
});
|
||||
mFriendModelConnection->makeConnectToModel(&FriendModel::pictureUriChanged, [this](const QString &uri) {
|
||||
mFriendModelConnection->invokeToCore([this, uri]() { this->onPictureUriChanged(uri); });
|
||||
|
|
@ -188,7 +186,7 @@ void FriendCore::setSelf(QSharedPointer<FriendCore> me) {
|
|||
QList<QVariant> addr;
|
||||
for (auto &num : numbers) {
|
||||
addr.append(Utils::createFriendAddressVariant(
|
||||
tr("sip_address"), Utils::coreStringToAppString(num->asStringUriOnly())));
|
||||
tr("sip_address"), Utils::coreStringToAppString(num->asStringUriOnly())));
|
||||
}
|
||||
mFriendModelConnection->invokeToCore([this, addr]() { resetPhoneNumbers(addr); });
|
||||
});
|
||||
|
|
@ -419,9 +417,10 @@ void FriendCore::appendAddress(const QString &addr) {
|
|||
QString interpretedFullAddress = linphoneAddr ? Utils::coreStringToAppString(linphoneAddr->asString()) : "";
|
||||
QString interpretedAddress = linphoneAddr ? Utils::coreStringToAppString(linphoneAddr->asStringUriOnly()) : "";
|
||||
mCoreModelConnection->invokeToCore([this, interpretedAddress, interpretedFullAddress]() {
|
||||
if (interpretedAddress.isEmpty()) Utils::showInformationPopup(tr("information_popup_error_title"),
|
||||
//: "Adresse invalide"
|
||||
tr("information_popup_invalid_address_message"), false);
|
||||
if (interpretedAddress.isEmpty())
|
||||
Utils::showInformationPopup(tr("information_popup_error_title"),
|
||||
//: "Adresse invalide"
|
||||
tr("information_popup_invalid_address_message"), false);
|
||||
else {
|
||||
mAddressList.append(Utils::createFriendAddressVariant(tr("sip_address"), interpretedAddress));
|
||||
if (mDefaultFullAddress.isEmpty()) mDefaultFullAddress = interpretedFullAddress;
|
||||
|
|
@ -507,30 +506,6 @@ void FriendCore::setDefaultAddress(const QString &address) {
|
|||
}
|
||||
}
|
||||
|
||||
LinphoneEnums::ConsolidatedPresence FriendCore::getConsolidatedPresence() const {
|
||||
return mConsolidatedPresence;
|
||||
}
|
||||
|
||||
void FriendCore::setConsolidatedPresence(LinphoneEnums::ConsolidatedPresence presence) {
|
||||
mustBeInMainThread(log().arg(Q_FUNC_INFO));
|
||||
if (mConsolidatedPresence != presence) {
|
||||
mConsolidatedPresence = presence;
|
||||
emit consolidatedPresenceChanged(mConsolidatedPresence);
|
||||
}
|
||||
}
|
||||
|
||||
QDateTime FriendCore::getPresenceTimestamp() const {
|
||||
return mPresenceTimestamp;
|
||||
}
|
||||
|
||||
void FriendCore::setPresenceTimestamp(QDateTime presenceTimestamp) {
|
||||
mustBeInMainThread(log().arg(Q_FUNC_INFO));
|
||||
if (mPresenceTimestamp != presenceTimestamp) {
|
||||
mPresenceTimestamp = presenceTimestamp;
|
||||
emit presenceTimestampChanged(mPresenceTimestamp);
|
||||
}
|
||||
}
|
||||
|
||||
QString FriendCore::getPictureUri() const {
|
||||
return mPictureUri;
|
||||
}
|
||||
|
|
@ -612,8 +587,8 @@ void FriendCore::writeFromModel(const std::shared_ptr<FriendModel> &model) {
|
|||
|
||||
QList<QVariant> addresses;
|
||||
for (auto &addr : model->getAddresses()) {
|
||||
addresses.append(
|
||||
Utils::createFriendAddressVariant(tr("sip_address"), Utils::coreStringToAppString(addr->asStringUriOnly())));
|
||||
addresses.append(Utils::createFriendAddressVariant(tr("sip_address"),
|
||||
Utils::coreStringToAppString(addr->asStringUriOnly())));
|
||||
}
|
||||
mAddressList = addresses;
|
||||
mDefaultAddress = model->getDefaultAddress();
|
||||
|
|
@ -759,3 +734,29 @@ bool FriendCore::getReadOnly() const {
|
|||
std::shared_ptr<FriendModel> FriendCore::getFriendModel() {
|
||||
return mFriendModel;
|
||||
}
|
||||
|
||||
void FriendCore::setPresence(LinphoneEnums::Presence presence, QString presenceNote) {
|
||||
mustBeInMainThread(log().arg(Q_FUNC_INFO));
|
||||
bool notify = false;
|
||||
if (presence != mPresence) {
|
||||
mPresence = presence;
|
||||
notify = true;
|
||||
}
|
||||
if (presenceNote != mPresenceNote) {
|
||||
mPresenceNote = presenceNote;
|
||||
notify = true;
|
||||
}
|
||||
if (notify) emit presenceChanged();
|
||||
}
|
||||
|
||||
QUrl FriendCore::getPresenceIcon() {
|
||||
return Utils::getPresenceIcon(mPresence);
|
||||
}
|
||||
|
||||
QColor FriendCore::getPresenceColor() {
|
||||
return Utils::getPresenceColor(mPresence);
|
||||
}
|
||||
|
||||
QString FriendCore::getPresenceStatus() {
|
||||
return Utils::getPresenceStatus(mPresence);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,8 +29,8 @@
|
|||
#include "tool/thread/SafeSharedPointer.hpp"
|
||||
#include <linphone++/linphone.hh>
|
||||
|
||||
#include <QColor>
|
||||
#include <QDateTime>
|
||||
#include <QMap>
|
||||
#include <QObject>
|
||||
#include <QSharedPointer>
|
||||
|
||||
|
|
@ -63,9 +63,11 @@ class FriendCore : public QObject, public AbstractObject {
|
|||
Q_PROPERTY(QString defaultAddress READ getDefaultAddress WRITE setDefaultAddress NOTIFY defaultAddressChanged)
|
||||
Q_PROPERTY(QString defaultFullAddress READ getDefaultFullAddress WRITE setDefaultFullAddress NOTIFY
|
||||
defaultFullAddressChanged)
|
||||
Q_PROPERTY(QDateTime presenceTimestamp READ getPresenceTimestamp NOTIFY presenceTimestampChanged)
|
||||
Q_PROPERTY(LinphoneEnums::ConsolidatedPresence consolidatedPresence READ getConsolidatedPresence NOTIFY
|
||||
consolidatedPresenceChanged)
|
||||
Q_PROPERTY(LinphoneEnums::Presence presence MEMBER mPresence NOTIFY presenceChanged)
|
||||
Q_PROPERTY(QUrl presenceIcon READ getPresenceIcon NOTIFY presenceChanged)
|
||||
Q_PROPERTY(QColor presenceColor READ getPresenceColor NOTIFY presenceChanged)
|
||||
Q_PROPERTY(QString presenceStatus READ getPresenceStatus NOTIFY presenceChanged)
|
||||
Q_PROPERTY(QString presenceNote MEMBER mPresenceNote NOTIFY presenceChanged)
|
||||
Q_PROPERTY(bool isSaved READ getIsSaved NOTIFY isSavedChanged)
|
||||
Q_PROPERTY(bool isStored READ getIsStored NOTIFY isStoredChanged)
|
||||
Q_PROPERTY(QString pictureUri READ getPictureUri WRITE setPictureUri NOTIFY pictureUriChanged)
|
||||
|
|
@ -130,12 +132,6 @@ public:
|
|||
void setDevices(QVariantList devices);
|
||||
Q_INVOKABLE LinphoneEnums::SecurityLevel getSecurityLevelForAddress(const QString &address) const;
|
||||
|
||||
LinphoneEnums::ConsolidatedPresence getConsolidatedPresence() const;
|
||||
void setConsolidatedPresence(LinphoneEnums::ConsolidatedPresence presence);
|
||||
|
||||
QDateTime getPresenceTimestamp() const;
|
||||
void setPresenceTimestamp(QDateTime presenceTimestamp);
|
||||
|
||||
bool getIsSaved() const;
|
||||
void setIsSaved(bool isSaved);
|
||||
|
||||
|
|
@ -146,8 +142,6 @@ public:
|
|||
void setPictureUri(const QString &uri);
|
||||
void onPictureUriChanged(QString uri);
|
||||
|
||||
void onPresenceReceived(LinphoneEnums::ConsolidatedPresence consolidatedPresence, QDateTime presenceTimestamp);
|
||||
|
||||
bool isLdap() const;
|
||||
bool isAppFriend() const;
|
||||
bool isCardDAV() const;
|
||||
|
|
@ -159,6 +153,10 @@ public:
|
|||
Q_INVOKABLE void save();
|
||||
Q_INVOKABLE void undo();
|
||||
|
||||
QColor getPresenceColor();
|
||||
QString getPresenceStatus();
|
||||
QUrl getPresenceIcon();
|
||||
|
||||
protected:
|
||||
void resetPhoneNumbers(QList<QVariant> newList);
|
||||
void resetAddresses(QList<QVariant> newList);
|
||||
|
|
@ -173,8 +171,6 @@ signals:
|
|||
void addressChanged();
|
||||
void organizationChanged();
|
||||
void jobChanged();
|
||||
void consolidatedPresenceChanged(LinphoneEnums::ConsolidatedPresence level);
|
||||
void presenceTimestampChanged(QDateTime presenceTimestamp);
|
||||
void pictureUriChanged();
|
||||
void saved();
|
||||
void isSavedChanged(bool isSaved);
|
||||
|
|
@ -186,13 +182,16 @@ signals:
|
|||
void devicesChanged();
|
||||
void verifiedDevicesChanged();
|
||||
void lSetStarred(bool starred);
|
||||
void presenceChanged();
|
||||
|
||||
protected:
|
||||
void writeIntoModel(std::shared_ptr<FriendModel> model) const;
|
||||
void writeFromModel(const std::shared_ptr<FriendModel> &model);
|
||||
|
||||
LinphoneEnums::ConsolidatedPresence mConsolidatedPresence = LinphoneEnums::ConsolidatedPresence::Offline;
|
||||
QDateTime mPresenceTimestamp;
|
||||
LinphoneEnums::Presence mPresence = LinphoneEnums::Presence::Undefined;
|
||||
QColor mPresenceColor;
|
||||
QString mPresenceStatus;
|
||||
QString mPresenceNote = "";
|
||||
QString mGivenName;
|
||||
QString mFamilyName;
|
||||
QString mFullName;
|
||||
|
|
@ -214,6 +213,9 @@ protected:
|
|||
QSharedPointer<SafeConnection<FriendCore, FriendModel>> mFriendModelConnection;
|
||||
QSharedPointer<SafeConnection<FriendCore, CoreModel>> mCoreModelConnection;
|
||||
|
||||
private:
|
||||
void setPresence(LinphoneEnums::Presence presence, QString presenceNote);
|
||||
|
||||
DECLARE_ABSTRACT_OBJECT
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -64,22 +64,27 @@ void LoginPage::login(const QString &username,
|
|||
const QString &password,
|
||||
QString displayName,
|
||||
QString domain,
|
||||
LinphoneEnums::TransportType transportType) {
|
||||
LinphoneEnums::TransportType transportType,
|
||||
QString registrarUri,
|
||||
QString outboundProxyAddress,
|
||||
QString connectionId) {
|
||||
setErrorMessage("");
|
||||
App::postModelAsync([=]() {
|
||||
// Create on Model thread.
|
||||
AccountManager *accountManager = new AccountManager();
|
||||
connect(accountManager, &AccountManager::registrationStateChanged, this,
|
||||
[accountManager, this](linphone::RegistrationState state, QString message) mutable {
|
||||
[accountManager, this](linphone::RegistrationState state, linphone::Reason reason,
|
||||
QString message) mutable {
|
||||
// View thread
|
||||
setRegistrationState(state);
|
||||
mBadIds = reason == linphone::Reason::Forbidden;
|
||||
emit reasonChanged();
|
||||
switch (state) {
|
||||
case linphone::RegistrationState::Failed: {
|
||||
if (message.isEmpty())
|
||||
//: Erreur durant la connexion
|
||||
setErrorMessage(tr("default_account_connection_state_error_toast"));
|
||||
else
|
||||
setErrorMessage(message);
|
||||
if (message.isEmpty())
|
||||
//: Erreur durant la connexion, veuillez vérifier vos paramètres
|
||||
setErrorMessage(tr("default_account_connection_state_error_toast"));
|
||||
else setErrorMessage(message);
|
||||
if (accountManager) {
|
||||
accountManager->deleteLater();
|
||||
accountManager = nullptr;
|
||||
|
|
@ -110,9 +115,9 @@ void LoginPage::login(const QString &username,
|
|||
|
||||
QString error;
|
||||
if (!accountManager->login(username, password, displayName, domain, LinphoneEnums::toLinphone(transportType),
|
||||
&error)) {
|
||||
&error, registrarUri, outboundProxyAddress, connectionId)) {
|
||||
setErrorMessage(error);
|
||||
emit accountManager->registrationStateChanged(linphone::RegistrationState::None);
|
||||
emit accountManager->registrationStateChanged(linphone::RegistrationState::None, linphone::Reason::None);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -35,12 +35,16 @@ public:
|
|||
|
||||
Q_PROPERTY(linphone::RegistrationState registrationState READ getRegistrationState NOTIFY registrationStateChanged)
|
||||
Q_PROPERTY(QString errorMessage READ getErrorMessage NOTIFY errorMessageChanged)
|
||||
Q_PROPERTY(bool badIds MEMBER mBadIds NOTIFY reasonChanged)
|
||||
|
||||
Q_INVOKABLE void login(const QString &username,
|
||||
const QString &password,
|
||||
QString displayName = QString(),
|
||||
QString domain = QString(),
|
||||
LinphoneEnums::TransportType transportType = LinphoneEnums::TransportType::Tls);
|
||||
LinphoneEnums::TransportType transportType = LinphoneEnums::TransportType::Tls,
|
||||
QString registrarUri = QString(),
|
||||
QString outboundProxyAddress = QString(),
|
||||
QString connectionId = QString());
|
||||
|
||||
linphone::RegistrationState getRegistrationState() const;
|
||||
void setRegistrationState(linphone::RegistrationState status);
|
||||
|
|
@ -51,10 +55,12 @@ public:
|
|||
signals:
|
||||
void registrationStateChanged();
|
||||
void errorMessageChanged(QString error);
|
||||
void reasonChanged();
|
||||
|
||||
private:
|
||||
linphone::RegistrationState mRegistrationState = linphone::RegistrationState::None;
|
||||
QString mErrorMessage;
|
||||
bool mBadIds = false;
|
||||
|
||||
DECLARE_ABSTRACT_OBJECT
|
||||
};
|
||||
|
|
|
|||
|
|
@ -33,8 +33,10 @@
|
|||
|
||||
#include "core/App.hpp"
|
||||
#include "core/call/CallGui.hpp"
|
||||
#include "core/chat/ChatGui.hpp"
|
||||
#include "model/tool/ToolModel.hpp"
|
||||
#include "tool/LinphoneEnums.hpp"
|
||||
#include "tool/accessibility/AccessibilityHelper.hpp"
|
||||
#include "tool/providers/AvatarProvider.hpp"
|
||||
#include "tool/providers/ImageProvider.hpp"
|
||||
|
||||
|
|
@ -85,9 +87,9 @@ void setProperty(QObject &object, const char *property, const T &value) {
|
|||
// =============================================================================
|
||||
|
||||
const QHash<int, Notifier::Notification> Notifier::Notifications = {
|
||||
//{Notifier::ReceivedMessage, {Notifier::ReceivedMessage, "NotificationReceivedMessage.qml", 10}},
|
||||
{Notifier::ReceivedMessage, {Notifier::ReceivedMessage, "NotificationReceivedMessage.qml", 10}},
|
||||
//{Notifier::ReceivedFileMessage, {Notifier::ReceivedFileMessage, "NotificationReceivedFileMessage.qml", 10}},
|
||||
{Notifier::ReceivedCall, {Notifier::ReceivedCall, "NotificationReceivedCall.qml", 30}},
|
||||
{Notifier::ReceivedCall, {Notifier::ReceivedCall, "NotificationReceivedCall.qml", 30}}
|
||||
//{Notifier::NewVersionAvailable, {Notifier::NewVersionAvailable, "NotificationNewVersionAvailable.qml", 30}},
|
||||
//{Notifier::SnapshotWasTaken, {Notifier::SnapshotWasTaken, "NotificationSnapshotWasTaken.qml", 10}},
|
||||
//{Notifier::RecordingCompleted, {Notifier::RecordingCompleted, "NotificationRecordingCompleted.qml", 10}}
|
||||
|
|
@ -127,8 +129,8 @@ Notifier::~Notifier() {
|
|||
|
||||
bool Notifier::createNotification(Notifier::NotificationType type, QVariantMap data) {
|
||||
mMutex->lock();
|
||||
Q_ASSERT(mInstancesNumber <= MaxNotificationsNumber);
|
||||
if (mInstancesNumber == MaxNotificationsNumber) { // Check existing instances.
|
||||
// Q_ASSERT(mInstancesNumber <= MaxNotificationsNumber);
|
||||
if (mInstancesNumber >= MaxNotificationsNumber) { // Check existing instances.
|
||||
qWarning() << QStringLiteral("Unable to create another notification.");
|
||||
mMutex->unlock();
|
||||
return false;
|
||||
|
|
@ -163,7 +165,6 @@ bool Notifier::createNotification(Notifier::NotificationType type, QVariantMap d
|
|||
engine->deleteLater();
|
||||
exit(-1);
|
||||
} else {
|
||||
lDebug() << engine->rootObjects()[0];
|
||||
auto window = qobject_cast<QQuickWindow *>(obj);
|
||||
if (window) {
|
||||
window->setProperty(NotificationPropertyData, data);
|
||||
|
|
@ -171,28 +172,24 @@ bool Notifier::createNotification(Notifier::NotificationType type, QVariantMap d
|
|||
// window->setProperty(it.key().toLatin1(), it.value());
|
||||
const int timeout = Notifications[type].getTimeout() * 1000;
|
||||
auto updateNotificationCoordinates = [this, window, screen](int width, int height) {
|
||||
int *screenHeightOffset = &mScreenHeightOffset[screen->name()]; // Access optimization
|
||||
auto screenHeightOffset =
|
||||
screen ? mScreenHeightOffset.value(screen->name()) : 0; // Access optimization
|
||||
QRect availableGeometry = screen->availableGeometry();
|
||||
int heightOffset = availableGeometry.y() +
|
||||
(availableGeometry.height() -
|
||||
height); //*screen->devicePixelRatio(); when using manual scaler
|
||||
|
||||
window->setX(availableGeometry.x() +
|
||||
(availableGeometry.width() -
|
||||
width)); //*screen->devicePixelRatio()); when using manual scaler
|
||||
window->setY(heightOffset - (*screenHeightOffset % heightOffset));
|
||||
window->setY(availableGeometry.y() + availableGeometry.height() - screenHeightOffset -
|
||||
height);
|
||||
};
|
||||
QObject::connect(window, &QQuickWindow::widthChanged,
|
||||
[window, updateNotificationCoordinates](int w) {
|
||||
updateNotificationCoordinates(w, window->height());
|
||||
});
|
||||
QObject::connect(window, &QQuickWindow::heightChanged,
|
||||
[window, updateNotificationCoordinates](int h) {
|
||||
updateNotificationCoordinates(window->width(), h);
|
||||
});
|
||||
updateNotificationCoordinates(window->width(), window->height());
|
||||
QObject::connect(window, &QQuickWindow::closing, window,
|
||||
[this, window] { deleteNotification(QVariant::fromValue(window)); });
|
||||
auto screenHeightOffset =
|
||||
screen ? mScreenHeightOffset.take(screen->name()) : 0; // Access optimization
|
||||
mScreenHeightOffset.insert(screen->name(), screenHeightOffset + window->height());
|
||||
QObject::connect(window, &QQuickWindow::closing, window, [this, window] {
|
||||
qDebug() << "closing notification";
|
||||
deleteNotification(QVariant::fromValue(window));
|
||||
});
|
||||
showNotification(window, timeout);
|
||||
lInfo() << QStringLiteral("Create notification:") << QVariant::fromValue(window);
|
||||
}
|
||||
|
|
@ -212,8 +209,6 @@ bool Notifier::createNotification(Notifier::NotificationType type, QVariantMap d
|
|||
|
||||
void Notifier::showNotification(QObject *notification, int timeout) {
|
||||
// Display notification.
|
||||
QMetaObject::invokeMethod(notification, NotificationShowMethodName, Qt::DirectConnection);
|
||||
|
||||
QTimer *timer = new QTimer(notification);
|
||||
timer->setInterval(timeout);
|
||||
timer->setSingleShot(true);
|
||||
|
|
@ -285,63 +280,153 @@ void Notifier::notifyReceivedCall(const shared_ptr<linphone::Call> &call) {
|
|||
auto account = ToolModel::findAccount(call->getToAddress());
|
||||
if (account) {
|
||||
auto accountModel = Utils::makeQObject_ptr<AccountModel>(account);
|
||||
accountModel->setSelf(accountModel);
|
||||
if (!accountModel->getNotificationsAllowed()) {
|
||||
qInfo()
|
||||
<< "Notifications have been disabled for this account - not creating a notification for incoming call";
|
||||
lInfo() << log().arg(
|
||||
"Notifications have been disabled for this account - not creating a notification for incoming call");
|
||||
if (accountModel->forwardToVoiceMailInDndPresence()) {
|
||||
lInfo() << log().arg("Transferring call to voicemail");
|
||||
auto voicemailAddress = linphone::Factory::get()->createAddress(
|
||||
Utils::appStringToCoreString(accountModel->getVoicemailAddress()));
|
||||
if (voicemailAddress) call->transferTo(voicemailAddress);
|
||||
}
|
||||
return;
|
||||
}
|
||||
accountModel->deleteLater();
|
||||
}
|
||||
|
||||
auto model = CallCore::create(call);
|
||||
auto gui = new CallGui(model);
|
||||
gui->moveToThread(App::getInstance()->thread());
|
||||
App::postCoreAsync([this, gui]() {
|
||||
auto callLog = call->getCallLog();
|
||||
auto displayName = callLog && callLog->getConferenceInfo()
|
||||
? Utils::coreStringToAppString(callLog->getConferenceInfo()->getSubject())
|
||||
: ToolModel::getDisplayName(call->getRemoteAddress());
|
||||
|
||||
// Accessibility alert
|
||||
//: New call from %1
|
||||
AccessibilityHelper::announceMessage(tr("new_call_alert_accessible_name").arg(displayName));
|
||||
|
||||
App::postCoreAsync([this, gui, displayName]() {
|
||||
mustBeInMainThread(getClassName());
|
||||
QVariantMap map;
|
||||
|
||||
map["displayName"].setValue(displayName);
|
||||
map["call"].setValue(gui);
|
||||
CREATE_NOTIFICATION(Notifier::ReceivedCall, map)
|
||||
});
|
||||
}
|
||||
|
||||
/*
|
||||
void Notifier::notifyReceivedMessages(const list<shared_ptr<linphone::ChatMessage>> &messages) {
|
||||
QVariantMap map;
|
||||
QString txt;
|
||||
if (messages.size() > 0) {
|
||||
shared_ptr<linphone::ChatMessage> message = messages.front();
|
||||
void Notifier::notifyReceivedMessages(const std::shared_ptr<linphone::ChatRoom> &room,
|
||||
const list<shared_ptr<linphone::ChatMessage>> &messages) {
|
||||
mustBeInLinphoneThread(log().arg(Q_FUNC_INFO));
|
||||
|
||||
if (messages.size() == 1) {
|
||||
auto fileContent = message->getFileTransferInformation();
|
||||
if (!fileContent) {
|
||||
foreach (auto content, message->getContents()) {
|
||||
if (content->isText()) txt += content->getUtf8Text().c_str();
|
||||
}
|
||||
} else if (fileContent->isVoiceRecording())
|
||||
//: 'Voice message received!' : message to warn the user in a notofication for voice messages.
|
||||
txt = tr("new_voice_message");
|
||||
else txt = tr("new_file_message");
|
||||
if (txt.isEmpty() && message->hasConferenceInvitationContent())
|
||||
//: 'Conference invitation received!' : Notification about receiving an invitation to a conference.
|
||||
txt = tr("new_conference_invitation");
|
||||
} else
|
||||
//: 'New messages received!' Notification that warn the user of new messages.
|
||||
txt = tr("new_chat_room_messages");
|
||||
map["message"] = txt;
|
||||
shared_ptr<linphone::ChatRoom> chatRoom(message->getChatRoom());
|
||||
map["timelineModel"].setValue(
|
||||
CoreManager::getInstance()->getTimelineListModel()->getTimeline(chatRoom, true).get());
|
||||
if (messages.size() == 1) { // Display only sender on mono message.
|
||||
map["remoteAddress"] = Utils::coreStringToAppString(message->getFromAddress()->asStringUriOnly());
|
||||
map["fullremoteAddress"] = Utils::coreStringToAppString(message->getFromAddress()->asString());
|
||||
}
|
||||
map["localAddress"] = Utils::coreStringToAppString(message->getToAddress()->asStringUriOnly());
|
||||
map["fullLocalAddress"] = Utils::coreStringToAppString(message->getToAddress()->asString());
|
||||
map["window"].setValue(App::getInstance()->getMainWindow());
|
||||
CREATE_NOTIFICATION(Notifier::ReceivedMessage, map)
|
||||
}
|
||||
if (room->getMuted()) return;
|
||||
|
||||
QString txt;
|
||||
QString remoteAddress;
|
||||
|
||||
if (messages.size() > 0) {
|
||||
|
||||
shared_ptr<linphone::ChatMessage> message = messages.front();
|
||||
auto receiverAccount = ToolModel::findAccount(message->getToAddress());
|
||||
if (receiverAccount) {
|
||||
auto senderAccount = ToolModel::findAccount(message->getFromAddress());
|
||||
auto currentAccount = CoreModel::getInstance()->getCore()->getDefaultAccount();
|
||||
if (senderAccount && senderAccount->getContactAddress() && currentAccount->getContactAddress() &&
|
||||
senderAccount->getContactAddress()->weakEqual(currentAccount->getContactAddress())) {
|
||||
qDebug() << "sender is current account, return";
|
||||
return;
|
||||
}
|
||||
auto accountModel = Utils::makeQObject_ptr<AccountModel>(receiverAccount);
|
||||
if (!accountModel->getNotificationsAllowed()) {
|
||||
lInfo() << log().arg(
|
||||
"Notifications have been disabled for this account - not creating a notification for "
|
||||
"incoming message");
|
||||
return;
|
||||
}
|
||||
accountModel->deleteLater();
|
||||
}
|
||||
|
||||
auto getMessage = [this, &remoteAddress, &txt](const shared_ptr<linphone::ChatMessage> &message) {
|
||||
if (message->isRead()) return;
|
||||
auto remoteAddr = message->getFromAddress();
|
||||
// remoteAddr->clean();
|
||||
remoteAddress = Utils::coreStringToAppString(remoteAddr->asStringUriOnly());
|
||||
auto fileContent = message->getFileTransferInformation();
|
||||
if (!fileContent) {
|
||||
for (auto content : message->getContents()) {
|
||||
if (content->isText()) txt += content->getUtf8Text().c_str();
|
||||
}
|
||||
} else if (fileContent->isVoiceRecording())
|
||||
//: 'Voice message received!' : message to warn the user in a notofication for voice messages.
|
||||
txt = tr("new_voice_message");
|
||||
else txt = tr("new_file_message");
|
||||
if (txt.isEmpty() && message->hasConferenceInvitationContent())
|
||||
//: 'Conference invitation received!' : Notification about receiving an invitation to a conference.
|
||||
txt = tr("new_conference_invitation");
|
||||
};
|
||||
|
||||
if (messages.size() == 1) { // Display only sender on mono message.
|
||||
if (message->isRead()) return;
|
||||
getMessage(message);
|
||||
if (txt.isEmpty()) { // Do not notify message without content
|
||||
qDebug() << "empty notif, return";
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
int unreadCount = 0;
|
||||
for (auto &message : messages) {
|
||||
if (!message->isRead()) {
|
||||
++unreadCount;
|
||||
if (unreadCount == 1) getMessage(message);
|
||||
}
|
||||
}
|
||||
if (unreadCount == 0) return;
|
||||
if (unreadCount > 1)
|
||||
//: 'New messages received!' Notification that warn the user of new messages.
|
||||
txt = tr("new_chat_room_messages");
|
||||
}
|
||||
|
||||
// Play noitification sound
|
||||
auto settings = SettingsModel::getInstance();
|
||||
if (settings && !settings->dndEnabled()) {
|
||||
CoreModel::getInstance()->getCore()->playLocal(
|
||||
Utils::appStringToCoreString(settings->getChatNotificationSoundPath()));
|
||||
}
|
||||
|
||||
// If chat currently displayed, do not display notification
|
||||
auto currentlyDisplayedChat = App::getInstance()->getCurrentChat();
|
||||
auto mainWin = App::getInstance()->getMainWindow();
|
||||
if (currentlyDisplayedChat && mainWin->isActive()) {
|
||||
auto linphoneCurrent = currentlyDisplayedChat->mCore->getModel()->getMonitor();
|
||||
if (linphoneCurrent->getIdentifier() == room->getIdentifier()) {
|
||||
lInfo() << log().arg("Chat is currently displayed in the view, do not show notification");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
auto chatCore = ChatCore::create(room);
|
||||
|
||||
App::postCoreAsync([this, txt, chatCore, remoteAddress]() {
|
||||
mustBeInMainThread(getClassName());
|
||||
|
||||
// Accessibility alert
|
||||
//: New message on chatroom %1
|
||||
AccessibilityHelper::announceMessage(tr("new_message_alert_accessible_name").arg(chatCore->getTitle()));
|
||||
|
||||
QVariantMap map;
|
||||
map["message"] = txt;
|
||||
map["remoteAddress"] = remoteAddress;
|
||||
map["chatRoomName"] = chatCore->getTitle();
|
||||
map["chatRoomAddress"] = chatCore->getChatRoomAddress();
|
||||
map["avatarUri"] = chatCore->getAvatarUri();
|
||||
map["isGroupChat"] = chatCore->isGroupChat();
|
||||
map["chat"] = QVariant::fromValue(chatCore ? new ChatGui(chatCore) : nullptr);
|
||||
CREATE_NOTIFICATION(Notifier::ReceivedMessage, map)
|
||||
});
|
||||
}
|
||||
}
|
||||
/*
|
||||
|
||||
void Notifier::notifyReceivedReactions(
|
||||
const QList<QPair<std::shared_ptr<linphone::ChatMessage>, std::shared_ptr<const linphone::ChatMessageReaction>>>
|
||||
|
|
|
|||
|
|
@ -41,9 +41,9 @@ public:
|
|||
~Notifier();
|
||||
|
||||
enum NotificationType {
|
||||
// ReceivedMessage,
|
||||
ReceivedMessage,
|
||||
// ReceivedFileMessage,
|
||||
ReceivedCall,
|
||||
ReceivedCall
|
||||
// NewVersionAvailable,
|
||||
// SnapshotWasTaken,
|
||||
// RecordingCompleted
|
||||
|
|
@ -52,8 +52,9 @@ public:
|
|||
// void notifyReceivedCall(Call *call);
|
||||
void notifyReceivedCall(const std::shared_ptr<linphone::Call> &call); // Call from Linphone
|
||||
|
||||
void notifyReceivedMessages(const std::shared_ptr<linphone::ChatRoom> &room,
|
||||
const std::list<std::shared_ptr<linphone::ChatMessage>> &messages);
|
||||
/*
|
||||
void notifyReceivedMessages(const std::list<std::shared_ptr<linphone::ChatMessage>> &messages);
|
||||
void notifyReceivedReactions(
|
||||
const QList<QPair<std::shared_ptr<linphone::ChatMessage>, std::shared_ptr<const
|
||||
linphone::ChatMessageReaction>>> &reactions); void notifyReceivedFileMessage(const
|
||||
|
|
|
|||
|
|
@ -46,12 +46,16 @@ ParticipantCore::ParticipantCore(const std::shared_ptr<linphone::Participant> &p
|
|||
mParticipantModel->moveToThread(CoreModel::getInstance()->thread());
|
||||
if (participant) {
|
||||
mAdminStatus = participant->isAdmin();
|
||||
mSipAddress = Utils::coreStringToAppString(participant->getAddress()->asStringUriOnly());
|
||||
auto participantAddress = participant->getAddress();
|
||||
mUsername = Utils::coreStringToAppString(participantAddress->getUsername());
|
||||
mSipAddress = Utils::coreStringToAppString(participantAddress->asStringUriOnly());
|
||||
mIsMe = ToolModel::isMe(mSipAddress);
|
||||
mCreationTime = QDateTime::fromSecsSinceEpoch(participant->getCreationTime());
|
||||
mDisplayName = Utils::coreStringToAppString(participant->getAddress()->getDisplayName());
|
||||
if (mDisplayName.isEmpty())
|
||||
mDisplayName = Utils::coreStringToAppString(participant->getAddress()->getUsername());
|
||||
mDisplayName = Utils::coreStringToAppString(participantAddress->getDisplayName());
|
||||
if (mDisplayName.isEmpty()) mDisplayName = ToolModel::getDisplayName(participantAddress);
|
||||
auto isFriend = ToolModel::findFriendByAddress(participantAddress);
|
||||
if (isFriend && isFriend->getCore()) mSecurityLevel = LinphoneEnums::fromLinphone(isFriend->getSecurityLevel());
|
||||
else mSecurityLevel = LinphoneEnums::SecurityLevel::None;
|
||||
for (auto &device : participant->getDevices()) {
|
||||
auto name = Utils::coreStringToAppString(device->getName());
|
||||
auto address = Utils::coreStringToAppString(device->getAddress()->asStringUriOnly());
|
||||
|
|
@ -75,7 +79,7 @@ void ParticipantCore::setSelf(QSharedPointer<ParticipantCore> me) {
|
|||
connect(this, &ParticipantCore::sipAddressChanged, this, &ParticipantCore::updateIsMe);
|
||||
}
|
||||
|
||||
int ParticipantCore::getSecurityLevel() const {
|
||||
LinphoneEnums::SecurityLevel ParticipantCore::getSecurityLevel() const {
|
||||
return mSecurityLevel;
|
||||
}
|
||||
|
||||
|
|
@ -121,6 +125,17 @@ QString ParticipantCore::getDisplayName() const {
|
|||
return mDisplayName;
|
||||
}
|
||||
|
||||
void ParticipantCore::setUsername(const QString &name) {
|
||||
if (mUsername != name) {
|
||||
mUsername = name;
|
||||
emit usernameChanged();
|
||||
}
|
||||
}
|
||||
|
||||
QString ParticipantCore::getUsername() const {
|
||||
return mUsername;
|
||||
}
|
||||
|
||||
QDateTime ParticipantCore::getCreationTime() const {
|
||||
return mCreationTime;
|
||||
}
|
||||
|
|
@ -153,7 +168,7 @@ void ParticipantCore::setIsFocus(const bool &focus) {
|
|||
}
|
||||
}
|
||||
|
||||
void ParticipantCore::setSecurityLevel(int level) {
|
||||
void ParticipantCore::setSecurityLevel(LinphoneEnums::SecurityLevel level) {
|
||||
if (level != mSecurityLevel) {
|
||||
mSecurityLevel = level;
|
||||
emit securityLevelChanged();
|
||||
|
|
|
|||
|
|
@ -40,11 +40,12 @@ class ParticipantCore : public QObject, public AbstractObject {
|
|||
|
||||
Q_PROPERTY(QString sipAddress READ getSipAddress WRITE setSipAddress NOTIFY sipAddressChanged)
|
||||
Q_PROPERTY(QString displayName READ getDisplayName WRITE setDisplayName NOTIFY displayNameChanged)
|
||||
Q_PROPERTY(QString username READ getUsername WRITE setUsername NOTIFY usernameChanged)
|
||||
Q_PROPERTY(bool isAdmin READ isAdmin WRITE setIsAdmin NOTIFY isAdminChanged)
|
||||
Q_PROPERTY(bool isMe READ isMe NOTIFY isMeChanged)
|
||||
Q_PROPERTY(QDateTime creationTime READ getCreationTime CONSTANT)
|
||||
Q_PROPERTY(bool focus READ isFocus CONSTANT)
|
||||
Q_PROPERTY(int securityLevel READ getSecurityLevel NOTIFY securityLevelChanged)
|
||||
Q_PROPERTY(LinphoneEnums::SecurityLevel securityLevel READ getSecurityLevel NOTIFY securityLevelChanged)
|
||||
Q_PROPERTY(int deviceCount READ getDeviceCount NOTIFY deviceCountChanged)
|
||||
Q_PROPERTY(QList<QVariant> devices READ getParticipantDevices NOTIFY deviceChanged)
|
||||
|
||||
|
|
@ -56,11 +57,12 @@ public:
|
|||
void setSelf(QSharedPointer<ParticipantCore> me);
|
||||
|
||||
QString getDisplayName() const;
|
||||
QString getUsername() const;
|
||||
QString getSipAddress() const;
|
||||
QDateTime getCreationTime() const;
|
||||
bool isAdmin() const;
|
||||
bool isFocus() const;
|
||||
int getSecurityLevel() const;
|
||||
LinphoneEnums::SecurityLevel getSecurityLevel() const;
|
||||
int getDeviceCount() const;
|
||||
|
||||
bool isMe() const;
|
||||
|
|
@ -69,10 +71,11 @@ public:
|
|||
|
||||
void setSipAddress(const QString &address);
|
||||
void setDisplayName(const QString &name);
|
||||
void setUsername(const QString &name);
|
||||
void setCreationTime(const QDateTime &date);
|
||||
void setIsAdmin(const bool &status);
|
||||
void setIsFocus(const bool &focus);
|
||||
void setSecurityLevel(int level);
|
||||
void setSecurityLevel(LinphoneEnums::SecurityLevel level);
|
||||
|
||||
QList<QVariant> getParticipantDevices();
|
||||
|
||||
|
|
@ -92,6 +95,7 @@ signals:
|
|||
void invitingChanged();
|
||||
void creationTimeChanged();
|
||||
void displayNameChanged();
|
||||
void usernameChanged();
|
||||
|
||||
void lStartInvitation(const int &secs = 30);
|
||||
void lSetIsAdmin(bool status);
|
||||
|
|
@ -107,11 +111,12 @@ private:
|
|||
QList<QVariant> mParticipantDevices;
|
||||
|
||||
QString mDisplayName;
|
||||
QString mUsername;
|
||||
QString mSipAddress;
|
||||
QDateTime mCreationTime;
|
||||
bool mAdminStatus;
|
||||
bool mIsFocus;
|
||||
int mSecurityLevel;
|
||||
LinphoneEnums::SecurityLevel mSecurityLevel;
|
||||
bool mIsMe;
|
||||
|
||||
DECLARE_ABSTRACT_OBJECT
|
||||
|
|
|
|||
|
|
@ -44,12 +44,13 @@ ParticipantDeviceCore::ParticipantDeviceCore(const std::shared_ptr<linphone::Par
|
|||
mustBeInLinphoneThread(getClassName());
|
||||
if (device) {
|
||||
mName = Utils::coreStringToAppString(device->getName());
|
||||
auto deviceAddress = device->getAddress()->clone();
|
||||
auto deviceAddress = device->getAddress();
|
||||
mUniqueAddress = Utils::coreStringToAppString(deviceAddress->asString());
|
||||
mAddress = Utils::coreStringToAppString(deviceAddress->asStringUriOnly());
|
||||
// the display name of the device himself may be the uncleaned sip uri
|
||||
// Use the participant name instead
|
||||
mDisplayName = Utils::coreStringToAppString(device->getParticipant()->getAddress()->getDisplayName());
|
||||
auto participant = device->getParticipant();
|
||||
mDisplayName = Utils::coreStringToAppString(participant ? participant->getAddress()->getDisplayName() : "");
|
||||
if (mDisplayName.isEmpty()) {
|
||||
mDisplayName = ToolModel::getDisplayName(deviceAddress);
|
||||
}
|
||||
|
|
|
|||
76
Linphone/core/participant/ParticipantInfoList.cpp
Normal file
76
Linphone/core/participant/ParticipantInfoList.cpp
Normal 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/>.
|
||||
*/
|
||||
|
||||
#include "ParticipantInfoList.hpp"
|
||||
#include "core/App.hpp"
|
||||
#include "core/chat/ChatCore.hpp"
|
||||
#include "core/participant/ParticipantGui.hpp"
|
||||
#include "tool/Utils.hpp"
|
||||
|
||||
DEFINE_ABSTRACT_OBJECT(ParticipantInfoList)
|
||||
|
||||
QSharedPointer<ParticipantInfoList> ParticipantInfoList::create() {
|
||||
auto model = QSharedPointer<ParticipantInfoList>(new ParticipantInfoList(), &QObject::deleteLater);
|
||||
model->moveToThread(App::getInstance()->thread());
|
||||
return model;
|
||||
}
|
||||
|
||||
QSharedPointer<ParticipantInfoList> ParticipantInfoList::create(const QSharedPointer<ChatCore> &chatCore) {
|
||||
auto model = create();
|
||||
model->setChatCore(chatCore);
|
||||
return model;
|
||||
}
|
||||
|
||||
ParticipantInfoList::ParticipantInfoList(QObject *parent) : ListProxy(parent) {
|
||||
App::getInstance()->mEngine->setObjectOwnership(this, QQmlEngine::CppOwnership);
|
||||
}
|
||||
|
||||
ParticipantInfoList::~ParticipantInfoList() {
|
||||
mList.clear();
|
||||
}
|
||||
|
||||
void ParticipantInfoList::setChatCore(const QSharedPointer<ChatCore> &chatCore) {
|
||||
mustBeInMainThread(log().arg(Q_FUNC_INFO));
|
||||
if (mChatCore) disconnect(mChatCore.get(), &ChatCore::participantsChanged, this, nullptr);
|
||||
mChatCore = chatCore;
|
||||
lDebug() << "[ParticipantInfoList] : set Chat " << mChatCore.get();
|
||||
clearData();
|
||||
if (mChatCore) {
|
||||
auto buildList = [this] {
|
||||
QStringList participantAddresses;
|
||||
QList<QSharedPointer<ParticipantGui>> participantList;
|
||||
auto participants = mChatCore->getParticipants();
|
||||
resetData();
|
||||
for (auto p : participants)
|
||||
add(p);
|
||||
};
|
||||
connect(mChatCore.get(), &ChatCore::participantsChanged, this, buildList);
|
||||
buildList();
|
||||
}
|
||||
}
|
||||
|
||||
QVariant ParticipantInfoList::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 ParticipantGui(mList[row].objectCast<ParticipantCore>()));
|
||||
}
|
||||
return QVariant();
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue