Merge branch 'release/5.0'

This commit is contained in:
Julien Wadel 2022-12-07 15:32:21 +01:00
commit 63127c633b
118 changed files with 1482 additions and 797 deletions

View file

@ -5,7 +5,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## 5.0.0 - [undefined]
## 5.0.0 - 2022-12-07
### Added
- Video conference and iCalendars.
@ -13,8 +13,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- New call layouts.
- Display a waiting room before going into a conference.
- Log viewer.
- Read contacts from all friends lists.
- Option to set the display name in "using an account" tab of assistant.
- Long pressed buttons.
- Date and Time pickers.
- Phone dialpad on main window.
- Animated file in chats/notifications.
- Round progress bar for transferring a file and allow to cancel it.
@ -25,6 +27,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Mark as Read synchronized between devices.
- Merge messages into one notification to avoid spam.
- Design overhaul on calls.
- Audio devices can be changed while being in call.
- Use a cryptographic checksum when downloading openH264 from CISCO (Update to 2.2.0)
### Fixed

View file

@ -204,6 +204,7 @@ set(SOURCES
src/components/core/event-count-notifier/AbstractEventCountNotifier.cpp
src/components/file/FileDownloader.cpp
src/components/file/FileExtractor.cpp
src/components/friend/FriendListListener.cpp
src/components/history/HistoryModel.cpp
src/components/history/HistoryProxyModel.cpp
src/components/ldap/LdapModel.cpp
@ -338,6 +339,7 @@ set(HEADERS
src/components/core/event-count-notifier/AbstractEventCountNotifier.hpp
src/components/file/FileDownloader.hpp
src/components/file/FileExtractor.hpp
src/components/friend/FriendListListener.hpp
src/components/history/HistoryModel.hpp
src/components/history/HistoryProxyModel.hpp
src/components/ldap/LdapModel.hpp

View file

@ -25,3 +25,4 @@ do
inkscape -z -e hicolor/${i}x${i}/apps/icon.png -w $i -h $i ../images/linphone_logo.svg
done
convert hicolor/16x16/apps/icon.png hicolor/22x22/apps/icon.png hicolor/24x24/apps/icon.png hicolor/32x32/apps/icon.png hicolor/64x64/apps/icon.png hicolor/128x128/apps/icon.png hicolor/256x256/apps/icon.png -colors 256 ../icon.ico
png2icns ../../cmake_builder/linphone_package/macos/linphone.icns hicolor/16x16/apps/icon.png hicolor/32x32/apps/icon.png hicolor/128x128/apps/icon.png hicolor/256x256/apps/icon.png

View file

@ -25,3 +25,4 @@ do
inkscape -z --export-type=png --export-file=hicolor/${i}x${i}/apps/icon.png -w $i -h $i ../images/linphone_logo.svg
done
convert hicolor/16x16/apps/icon.png hicolor/22x22/apps/icon.png hicolor/24x24/apps/icon.png hicolor/32x32/apps/icon.png hicolor/64x64/apps/icon.png hicolor/128x128/apps/icon.png hicolor/256x256/apps/icon.png -colors 256 ../icon.ico
png2icns ../../cmake_builder/linphone_package/macos/linphone.icns hicolor/16x16/apps/icon.png hicolor/32x32/apps/icon.png hicolor/128x128/apps/icon.png hicolor/256x256/apps/icon.png

View file

@ -25,3 +25,4 @@ do
inkscape -z --export-type=png --export-filename=hicolor/${i}x${i}/apps/icon.png -w $i -h $i ../images/linphone_logo.svg
done
convert hicolor/16x16/apps/icon.png hicolor/22x22/apps/icon.png hicolor/24x24/apps/icon.png hicolor/32x32/apps/icon.png hicolor/64x64/apps/icon.png hicolor/128x128/apps/icon.png hicolor/256x256/apps/icon.png -colors 256 ../icon.ico
png2icns ../../cmake_builder/linphone_package/macos/linphone.icns hicolor/16x16/apps/icon.png hicolor/32x32/apps/icon.png hicolor/128x128/apps/icon.png hicolor/256x256/apps/icon.png

View file

@ -3,16 +3,19 @@
width="80"
height="80"
viewBox="0 0 80 80"
fill="none"
version="1.1"
id="svg21"
id="svg8"
sodipodi:docname="chat_room_custom.svg"
inkscape:version="1.1 (c68e22c387, 2021-05-23)"
inkscape:version="1.1.2 (0a00cf5339, 2022-02-04)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs12" />
<sodipodi:namedview
id="namedview23"
id="namedview10"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
@ -20,57 +23,31 @@
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
showgrid="false"
width="30px"
inkscape:zoom="5.659091"
inkscape:cx="-9.6305219"
inkscape:cy="24.738955"
inkscape:zoom="6.9166667"
inkscape:cx="15.180723"
inkscape:cy="22.987952"
inkscape:window-width="1920"
inkscape:window-height="1043"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="svg21" />
<defs
id="defs5">
<filter
id="d1wmealema"
color-interpolation-filters="auto"
x="0"
y="0"
width="1"
height="1">
<feColorMatrix
in="SourceGraphic"
values="0 0 0 0 0.349020 0 0 0 0 0.341176 0 0 0 0 0.349020 0 0 0 1.000000 0"
id="feColorMatrix2" />
</filter>
</defs>
inkscape:current-layer="svg8" />
<g
fill="none"
fill-rule="evenodd"
id="g19"
transform="matrix(0.91808004,0,0,0.97218937,14.299365,21.746553)">
<g
id="g17">
<g
id="g15">
<g
filter="url(#d1wmealema)"
transform="translate(0.763635,0.775)"
id="g13">
<g
fill="#000000"
fill-rule="nonzero"
id="g11">
<path
d="m 45.911,13.38 c 1.019,0 2,-0.213 2.882,-0.616 0.049,-0.022 0.098,-0.04 0.149,-0.055 0.06,-0.018 0.21,-0.075 0.417,-0.178 0.362,-0.18 0.727,-0.419 1.066,-0.723 0.294,-0.264 0.555,-0.565 0.775,-0.906 0.432,-0.67 0.709,-1.305 0.864,-1.88 0.052,-0.19 0.085,-0.358 0.104,-0.498 L 52.18,8.419 52.185,8.326 c 0.018,-0.169 0.026,-0.34 0.026,-0.51 0,-3.046 -2.796,-5.566 -6.3,-5.566 -3.503,0 -6.3,2.52 -6.3,5.565 0,1.452 0.633,2.82 1.758,3.855 0.181,0.166 0.302,0.388 0.346,0.63 l 0.013,0.075 c 0.157,0.635 0.156,1.22 0.026,1.752 0.291,-0.182 0.532,-0.363 0.74,-0.542 0.071,-0.06 0.132,-0.116 0.184,-0.166 l 0.043,-0.041 0.028,-0.028 0.032,-0.03 C 42.8,13.303 42.8,13.303 42.887,13.235 l 0.1,-0.063 0.849,-0.1 c 0.678,0.206 1.367,0.308 2.075,0.308 z m 0,2.25 c -0.707,0 -1.404,-0.078 -2.077,-0.232 -0.258,0.213 -0.545,0.422 -0.86,0.62 -1.44,0.905 -3.045,1.324 -4.733,0.947 -1.135,-0.253 -1.186,-1.852 -0.07,-2.178 0.027,-0.008 0.105,-0.035 0.216,-0.083 0.193,-0.084 0.386,-0.19 0.56,-0.315 0.5,-0.359 0.72,-0.756 0.628,-1.324 -1.41,-1.421 -2.214,-3.275 -2.214,-5.25 0,-4.343 3.853,-7.815 8.55,-7.815 4.697,0 8.55,3.472 8.55,7.815 0,0.235 -0.01,0.468 -0.033,0.7 -0.011,0.253 -0.065,0.625 -0.192,1.095 -0.214,0.794 -0.585,1.641 -1.145,2.51 -0.332,0.516 -0.725,0.969 -1.164,1.363 -0.5,0.448 -1.032,0.798 -1.568,1.064 -0.283,0.14 -0.521,0.236 -0.696,0.293 -1.159,0.518 -2.435,0.79 -3.752,0.79 z M 42.454,5.506 h 6.895 c 1.5,0 1.5,2.25 0,2.25 h -6.895 c -1.5,0 -1.5,-2.25 0,-2.25 z m 0,2.818 h 6.895 c 1.5,0 1.5,2.25 0,2.25 h -6.895 c -1.5,0 -1.5,-2.25 0,-2.25 z M 8.671,32.229 c 2.257,0.675 4.576,1.106 6.87,1.336 1.674,0.167 3.097,0.207 4.157,0.177 0.104,-0.003 0.173,-0.006 0.203,-0.008 l 0.209,-0.002 0.265,0.01 c 1.06,0.03 2.483,-0.01 4.156,-0.177 2.295,-0.23 4.614,-0.66 6.87,-1.336 0.34,-1.838 0.01,-3.916 -1.507,-5.904 -1.151,-1.51 -2.924,-2.845 -5.428,-3.946 -0.064,-0.028 -0.124,-0.061 -0.181,-0.1 l -0.274,-0.187 c -0.607,-0.413 -0.662,-1.287 -0.112,-1.773 0.087,-0.103 0.105,-0.125 0.185,-0.21 0.944,-1.019 1.478,-2.356 1.478,-3.782 0,-3.055 -2.448,-5.527 -5.463,-5.527 h -0.126 c -3.014,0 -5.462,2.472 -5.462,5.527 0,1.426 0.534,2.763 1.48,3.783 0.079,0.086 0.092,0.102 0.163,0.193 0.57,0.5 0.516,1.377 -0.093,1.79 l -0.276,0.187 c -0.056,0.038 -0.116,0.071 -0.178,0.099 -2.504,1.1 -4.277,2.437 -5.428,3.946 -1.517,1.988 -1.848,4.066 -1.508,5.904 z m 11.32,3.753 -0.229,0.009 c -1.151,0.033 -2.67,-0.01 -4.444,-0.187 C 12.671,35.539 9.992,35.021 7.388,34.187 7.05,34.079 6.784,33.818 6.668,33.484 6.615,33.33 6.545,33.081 6.48,32.747 5.999,30.307 6.414,27.55 8.39,24.96 c 1.217,-1.596 2.96,-2.995 5.276,-4.155 -0.905,-1.294 -1.405,-2.848 -1.405,-4.478 0,-4.293 3.45,-7.777 7.712,-7.777 h 0.126 c 4.263,0 7.713,3.484 7.713,7.777 0,1.63 -0.5,3.183 -1.406,4.478 2.316,1.16 4.06,2.56 5.277,4.155 1.976,2.59 2.39,5.347 1.91,7.787 -0.066,0.334 -0.135,0.583 -0.188,0.737 -0.116,0.334 -0.383,0.595 -0.72,0.703 -2.604,0.834 -5.284,1.352 -7.93,1.617 -1.775,0.177 -3.294,0.22 -4.445,0.187 l -0.228,-0.009 z"
id="path7" />
<path
d="m 27.704,16.327 c 0,1.63 -0.5,3.183 -1.405,4.478 3.468,1.736 5.623,3.985 6.655,6.56 1.97,-0.151 4.02,-0.496 6.038,-1.09 0.223,-1.387 -0.052,-2.942 -1.193,-4.437 -0.901,-1.181 -2.296,-2.232 -4.275,-3.102 -0.063,-0.028 -0.123,-0.061 -0.18,-0.1 l -0.221,-0.15 c -0.607,-0.414 -0.662,-1.288 -0.111,-1.774 0.049,-0.062 0.069,-0.085 0.132,-0.153 0.721,-0.778 1.129,-1.799 1.129,-2.889 0,-2.333 -1.868,-4.22 -4.168,-4.22 h -0.102 c -1.611,0 -3.048,0.935 -3.739,2.35 0.928,1.306 1.44,2.878 1.44,4.527 z m 5.278,0.423 0.002,-0.001 z m -7.719,3.57 -0.452,1.03 0.633,-0.93 -0.085,-0.058 c -0.031,-0.015 -0.063,-0.029 -0.096,-0.043 z m -1.287,-0.211 c 0.945,-1.019 1.478,-2.356 1.478,-3.782 0,-1.348 -0.476,-2.617 -1.33,-3.615 -0.255,-0.299 -0.336,-0.71 -0.213,-1.083 C 24.775,9.007 27.216,7.2 30.003,7.2 h 0.102 c 3.547,0 6.418,2.9 6.418,6.47 0,1.269 -0.364,2.482 -1.028,3.515 1.78,0.925 3.133,2.03 4.093,3.289 1.631,2.137 1.974,4.416 1.576,6.434 -0.054,0.279 -0.113,0.489 -0.159,0.622 -0.116,0.334 -0.382,0.594 -0.72,0.702 -2.702,0.866 -5.457,1.303 -8.056,1.432 -0.517,0.026 -0.985,-0.306 -1.133,-0.802 -0.755,-2.53 -2.838,-4.77 -6.738,-6.483 -0.063,-0.028 -0.124,-0.061 -0.18,-0.1 l -0.275,-0.187 c -0.607,-0.413 -0.661,-1.287 -0.111,-1.773 0.086,-0.103 0.105,-0.125 0.184,-0.21 z m -10.31,0.696 c -0.905,-1.294 -1.405,-2.848 -1.405,-4.478 0,-2.066 0.805,-4 2.192,-5.432 C 13.675,9.992 12.54,9.45 11.312,9.45 H 11.21 c -2.3,0 -4.168,1.887 -4.168,4.22 0,1.09 0.408,2.111 1.13,2.89 0.064,0.069 0.08,0.088 0.13,0.15 0.553,0.486 0.498,1.363 -0.111,1.777 l -0.221,0.15 c -0.057,0.038 -0.117,0.071 -0.18,0.099 -1.978,0.87 -3.373,1.92 -4.274,3.102 -1.14,1.495 -1.416,3.05 -1.193,4.438 1.561,0.458 3.157,0.771 4.741,0.96 1.053,-2.522 3.194,-4.725 6.602,-6.431 z M 8.333,16.75 8.332,16.748 v 0.002 z m 6.19,3.667 0.63,0.931 -0.452,-1.03 -0.095,0.042 -0.084,0.057 z m 1.538,1.675 -0.276,0.187 C 15.729,22.317 15.669,22.35 15.607,22.378 11.752,24.072 9.671,26.281 8.894,28.78 8.737,29.287 8.246,29.615 7.717,29.566 5.495,29.362 3.232,28.936 1.03,28.231 0.693,28.123 0.426,27.863 0.31,27.529 0.264,27.396 0.206,27.186 0.15,26.907 -0.247,24.888 0.096,22.61 1.727,20.473 2.687,19.214 4.04,18.108 5.82,17.183 5.157,16.151 4.792,14.938 4.792,13.669 c 0,-3.57 2.871,-6.47 6.418,-6.47 h 0.102 c 2.356,0 4.486,1.29 5.61,3.325 0.267,0.483 0.144,1.089 -0.29,1.43 -1.329,1.04 -2.121,2.638 -2.121,4.372 0,1.426 0.534,2.763 1.48,3.783 0.079,0.086 0.092,0.102 0.163,0.193 0.57,0.5 0.516,1.377 -0.093,1.79 z"
id="path9" />
</g>
</g>
</g>
</g>
id="g832"
transform="matrix(0.67345025,0,0,0.67345025,9.9999997,20)">
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="m 33.7385,59.5539 c -5.4936,-0.5499 -11.046,-1.5836 -16.4472,-3.2018 -0.8149,-4.4048 -0.0225,-9.3836 3.6084,-14.1483 2.756,-3.6166 7.0003,-6.8186 12.9943,-9.456 0.1497,-0.0658 0.2931,-0.1452 0.4285,-0.2371 l 0.6594,-0.4479 c 1.4585,-0.9906 1.5891,-3.0935 0.2234,-4.291 C 35.0358,27.5543 35.0033,27.5167 34.813,27.3106 32.5497,24.8668 31.2713,21.6615 31.2713,18.2449 31.2713,10.924 37.1314,5 44.3466,5 h 0.3033 c 7.2171,0 13.0769,5.9235 13.0769,13.2449 0,3.4172 -1.278,6.6224 -3.5383,9.063 -0.1905,0.2048 -0.2352,0.2573 -0.4418,0.5031 -1.3173,1.1642 -1.186,3.2589 0.2664,4.2491 L 54.67,32.508 c 0.1365,0.0931 0.2813,0.1734 0.4325,0.2399 5.9945,2.6373 10.2391,5.8394 12.9953,9.456 3.6313,4.7647 4.4238,9.7434 3.609,14.1482 -5.4018,1.6182 -10.9545,2.6519 -16.4482,3.2018 -4.0055,0.4008 -7.4119,0.4967 -9.9495,0.4236 -0.1865,-0.0054 -0.3252,-0.0106 -0.4752,-0.0162 -0.0509,-0.002 -0.1031,-0.0039 -0.1589,-0.006 l -0.4995,0.0041 c -0.0734,0.004 -0.2375,0.0109 -0.4874,0.0181 -2.5377,0.0731 -5.9441,-0.0228 -9.9496,-0.4236 z"
fill="#444444"
id="path2" />
<path
d="m 62.1606,17.3664 c 0,4.0408 -1.3225,8.2219 -3.5628,11.4314 8.5813,4.3036 11.0776,8.8212 13.6299,15.2022 4.874,-0.3728 11.6211,-1.122 16.5902,-2.8452 0.8014,-4.0073 -0.0642,-8.6958 -3.6707,-12.3023 -2.6641,-2.6641 -4.1035,-4.1096 -8.7036,-6.1351 -0.1563,-0.0689 -0.3481,-0.1803 -0.4891,-0.2766 -1.7232,-0.8015 -2.5246,-1.6029 -2.1239,-3.2058 0,-0.4008 1.021,-1.3364 1.2022,-1.6029 1.4108,-2.0755 2.5647,-3.3516 2.5647,-7.2132 C 77.5975,4.80874 73.2857,0 66.6175,0 61.408,0 57.8015,4.00729 57,7.21311 c 2.2963,3.23579 5.1606,6.06519 5.1606,10.15329 z"
fill="#444444"
id="path4" />
<path
d="m 26.8544,17.3664 c 0,4.0408 1.3187,8.2219 3.5525,11.4314 C 21.8505,33.1014 19.3615,37.619 16.8166,44 11.9569,43.6272 5.22934,42.878 0.274772,41.1548 -0.524352,37.1475 0.338703,32.459 3.93476,28.8525 6.59109,26.1884 8.02628,24.7429 12.613,22.7174 c 0.1559,-0.0689 0.347,-0.1803 0.4877,-0.2766 1.7181,-0.8015 2.5173,-1.6029 2.1177,-3.2058 0,-0.4008 -1.018,-1.3364 -1.1987,-1.6029 C 12.613,15.5566 11.4625,14.2805 11.4625,10.4189 11.4625,4.80874 15.7617,0 22.4105,0 27.6048,0 31.2009,4.00729 32,7.21311 29.7104,10.4489 26.8544,13.2783 26.8544,17.3664 Z"
fill="#444444"
id="path6" />
</g>
</svg>

Before

Width:  |  Height:  |  Size: 7.2 KiB

After

Width:  |  Height:  |  Size: 3.4 KiB

View file

@ -0,0 +1,67 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
width="80"
height="80"
viewBox="0 0 80 80"
fill="none"
version="1.1"
id="svg7"
sodipodi:docname="conference_merge_custom.svg"
inkscape:version="1.1.2 (0a00cf5339, 2022-02-04)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs11" />
<sodipodi:namedview
id="namedview9"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
showgrid="false"
inkscape:zoom="5.6283784"
inkscape:cx="72.045618"
inkscape:cy="45.661464"
inkscape:window-width="1920"
inkscape:window-height="1043"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="svg7" />
<g
id="rassemblement_conf_over_alpha"
transform="matrix(0.67228336,0,0,0.66946229,-9.7483237,-10.083869)">
<path
id="new_conference_default"
fill-rule="evenodd"
clip-rule="evenodd"
d="m 57.542,109.575 c 0.2349,-0.253 0.6303,-0.268 0.8833,-0.033 6.9396,6.444 18.2098,6.444 25.1494,0 0.253,-0.235 0.6484,-0.22 0.8833,0.033 0.2349,0.253 0.2202,0.648 -0.0327,0.883 -7.4192,6.889 -19.4314,6.889 -26.8506,0 -0.2529,-0.235 -0.2676,-0.63 -0.0327,-0.883 z m 8.9776,-1.922 c 0.1435,0.214 0.1402,0.495 -0.0083,0.706 l -6.3268,9 c -0.1399,0.199 -0.3824,0.299 -0.6217,0.256 -0.2393,-0.043 -0.432,-0.221 -0.494,-0.456 l -1.8876,-7.162 c -0.0433,-0.164 -0.0179,-0.339 0.0704,-0.484 0.0883,-0.145 0.2318,-0.248 0.3975,-0.285 l 8.2144,-1.838 c 0.252,-0.056 0.5127,0.048 0.6561,0.263 z m -7.9643,2.653 1.3796,5.235 4.6243,-6.578 z M 50,46.625 c -9.5977,0 -17.375,7.7777 -17.375,17.375 0,0.3452 -0.2798,0.625 -0.625,0.625 -0.3452,0 -0.625,-0.2798 -0.625,-0.625 0,-10.2877 8.3369,-18.625 18.625,-18.625 0.3452,0 0.625,0.2798 0.625,0.625 0,0.3452 -0.2798,0.625 -0.625,0.625 z m -6.3179,-6.1631 c 0.2267,-0.1339 0.5129,-0.1114 0.7158,0.0562 l 6.0312,4.9797 c 0.2459,0.2031 0.2995,0.5587 0.1244,0.8252 l -3.9555,6.0202 c -0.1399,0.2129 -0.3951,0.3196 -0.6449,0.2697 -0.2499,-0.05 -0.4444,-0.2466 -0.4916,-0.497 l -2.0757,-11 c -0.0488,-0.2587 0.0697,-0.5202 0.2963,-0.654 z m 1.2525,2.1203 1.4683,7.7815 2.7982,-4.2588 z M 93.375,46 c 0,-0.3452 0.2798,-0.625 0.625,-0.625 10.286,0 18.625,8.3394 18.625,18.625 0,0.3452 -0.28,0.625 -0.625,0.625 -0.345,0 -0.625,-0.2798 -0.625,-0.625 0,-9.5953 -7.779,-17.375 -17.375,-17.375 -0.3452,0 -0.625,-0.2798 -0.625,-0.625 z m 23.138,11.6424 c 0.14,0.2018 0.15,0.4674 0.023,0.6785 l -3.853,6.4407 c -0.086,0.1431 -0.225,0.246 -0.387,0.2859 -0.162,0.0399 -0.333,0.0135 -0.475,-0.0735 l -7.147,-4.3647 c -0.22,-0.1348 -0.335,-0.3915 -0.289,-0.6459 0.047,-0.2543 0.245,-0.4537 0.499,-0.5017 l 11,-2.0759 c 0.242,-0.0456 0.488,0.0549 0.629,0.2566 z m -9.801,2.7465 5.222,3.1896 2.816,-4.7066 z"
fill="#000000"
stroke="#000000"
stroke-width="4"
stroke-linecap="round"
stroke-linejoin="round" />
<path
id="chat_group_new"
fill-rule="evenodd"
clip-rule="evenodd"
d="m 47.171,101.988 c 0.1201,-0.004 0.212,-0.007 0.2743,-0.01 h 0.109 c 0.0623,0.003 0.1542,0.006 0.2743,0.01 1.3829,0.039 3.2061,-0.012 5.3379,-0.225 3.178,-0.318 6.3965,-0.942 9.5244,-1.9443 0.4048,-0.1297 0.725,-0.4426 0.8643,-0.8446 0.0641,-0.1846 0.147,-0.4846 0.2259,-0.8858 0.5774,-2.9339 0.0789,-6.2473 -2.2942,-9.361 -1.4622,-1.9186 -3.5553,-3.6001 -6.3368,-4.995 C 56.2374,82.1758 56.838,80.3087 56.838,78.349 56.838,73.1884 52.6946,69 47.5757,69 h -0.1522 c -5.1179,0 -9.2615,4.1887 -9.2615,9.349 0,1.9599 0.601,3.8273 1.6879,5.3829 -2.7817,1.395 -4.8749,3.0766 -6.3372,4.9955 -2.3728,3.1136 -2.8711,6.427 -2.2938,9.3609 0.0789,0.4012 0.1619,0.7012 0.2259,0.8858 0.1393,0.402 0.4595,0.7148 0.8643,0.8446 3.1276,1.0023 6.3459,1.6263 9.5239,1.9443 2.1318,0.213 3.955,0.264 5.338,0.225 z m -4.5622,-4.1908 c -2.497,-0.2499 -5.0209,-0.7198 -7.476,-1.4553 -0.3703,-2.0022 -0.0102,-4.2653 1.6402,-6.4311 1.2528,-1.6439 3.182,-3.0994 5.9065,-4.2981 0.0681,-0.03 0.1333,-0.0661 0.1948,-0.1078 l 0.2997,-0.2036 c 0.663,-0.4503 0.7223,-1.4062 0.1016,-1.9505 -0.0771,-0.0989 -0.0918,-0.1159 -0.1783,-0.2096 -1.0288,-1.1108 -1.6099,-2.5678 -1.6099,-4.1208 0,-3.3277 2.6637,-6.0204 5.9433,-6.0204 h 0.1379 c 3.2805,0 5.944,2.6925 5.944,6.0204 0,1.5533 -0.5809,3.0102 -1.6083,4.1196 -0.0866,0.093 -0.1069,0.1169 -0.2008,0.2286 -0.5988,0.5292 -0.5391,1.4814 0.1211,1.9315 l 0.2986,0.2035 c 0.062,0.0423 0.1278,0.0788 0.1965,0.1091 2.7248,1.1988 4.6542,2.6543 5.907,4.2981 1.6506,2.1658 2.0108,4.4289 1.6405,6.4311 -2.4554,0.7355 -4.9794,1.2054 -7.4765,1.4553 -1.8207,0.1822 -3.369,0.2258 -4.5225,0.1926 -0.0846,-0.0025 -0.1476,-0.0048 -0.2157,-0.0074 -0.0232,-9e-4 -0.047,-0.0018 -0.0725,-0.0027 l -0.2271,0.0018 c -0.0333,0.0018 -0.1079,0.005 -0.2215,0.0083 -1.1535,0.0332 -2.7019,-0.0104 -4.5226,-0.1926 z"
fill="#000000" />
<path
id="chat_group_new_2"
fill-rule="evenodd"
clip-rule="evenodd"
d="m 100.171,101.988 c 0.12,-0.004 0.212,-0.007 0.274,-0.01 h 0.109 c 0.063,0.003 0.154,0.006 0.275,0.01 1.383,0.039 3.206,-0.012 5.337,-0.225 3.179,-0.318 6.397,-0.942 9.525,-1.9443 0.405,-0.1297 0.725,-0.4426 0.864,-0.8446 0.064,-0.1846 0.147,-0.4846 0.226,-0.8858 0.577,-2.9339 0.079,-6.2473 -2.294,-9.361 -1.462,-1.9186 -3.555,-3.6001 -6.337,-4.995 1.087,-1.5565 1.688,-3.4236 1.688,-5.3833 0,-5.1606 -4.143,-9.349 -9.262,-9.349 h -0.152 c -5.1184,0 -9.262,4.1887 -9.262,9.349 0,1.9599 0.601,3.8273 1.6879,5.3829 -2.7817,1.395 -4.8749,3.0766 -6.3372,4.9955 -2.3728,3.1136 -2.8711,6.427 -2.2938,9.3609 0.0789,0.4012 0.1619,0.7012 0.2259,0.8858 0.1393,0.402 0.4595,0.7148 0.8643,0.8446 3.1276,1.0023 6.3459,1.6263 9.5239,1.9443 2.1318,0.213 3.955,0.264 5.338,0.225 z m -4.5622,-4.1908 c -2.497,-0.2499 -5.0209,-0.7198 -7.476,-1.4553 -0.3703,-2.0022 -0.0102,-4.2653 1.6402,-6.4311 1.2528,-1.6439 3.182,-3.0994 5.9065,-4.2981 0.0681,-0.03 0.1333,-0.0661 0.1948,-0.1078 l 0.2997,-0.2036 c 0.663,-0.4503 0.7223,-1.4062 0.1016,-1.9505 -0.0771,-0.0989 -0.0918,-0.1159 -0.1783,-0.2096 -1.0288,-1.1108 -1.6099,-2.5678 -1.6099,-4.1208 0,-3.3277 2.6637,-6.0204 5.9436,-6.0204 h 0.138 c 3.28,0 5.944,2.6925 5.944,6.0204 0,1.5533 -0.581,3.0102 -1.609,4.1196 -0.086,0.093 -0.107,0.1169 -0.201,0.2286 -0.598,0.5292 -0.539,1.4814 0.122,1.9315 l 0.298,0.2035 c 0.062,0.0423 0.128,0.0788 0.197,0.1091 2.725,1.1988 4.654,2.6543 5.907,4.2981 1.65,2.1658 2.011,4.4289 1.64,6.4311 -2.455,0.7355 -4.979,1.2054 -7.476,1.4553 -1.821,0.1822 -3.369,0.2258 -4.523,0.1926 -0.084,-0.0025 -0.147,-0.0048 -0.215,-0.0074 -0.024,-9e-4 -0.048,-0.0018 -0.073,-0.0027 l -0.227,0.0018 c -0.033,0.0018 -0.108,0.005 -0.222,0.0083 -1.1531,0.0332 -2.7015,-0.0104 -4.5222,-0.1926 z"
fill="#000000" />
<path
id="chat_group_new_3"
fill-rule="evenodd"
clip-rule="evenodd"
d="m 73.171,62.9876 c 0.1201,-0.0035 0.212,-0.007 0.2743,-0.01 h 0.109 c 0.0623,0.003 0.1542,0.0065 0.2743,0.01 1.3829,0.0399 3.2061,-0.0115 5.3379,-0.2248 3.178,-0.3181 6.3965,-0.9415 9.5244,-1.9441 0.4048,-0.1297 0.725,-0.4426 0.8643,-0.8446 0.0641,-0.1846 0.147,-0.4846 0.2259,-0.8858 0.5774,-2.9339 0.0789,-6.2473 -2.2942,-9.361 -1.4622,-1.9186 -3.5553,-3.6001 -6.3368,-4.995 C 82.2374,43.1758 82.838,41.3087 82.838,39.349 82.838,34.1884 78.6946,30 73.5757,30 h -0.1522 c -5.1179,0 -9.2615,4.1887 -9.2615,9.349 0,1.9599 0.601,3.8273 1.6879,5.3829 -2.7817,1.395 -4.8749,3.0766 -6.3372,4.9955 -2.3728,3.1136 -2.8711,6.427 -2.2938,9.3609 0.0789,0.4012 0.1619,0.7012 0.2259,0.8858 0.1393,0.402 0.4595,0.7148 0.8643,0.8446 3.1276,1.0026 6.3459,1.626 9.5239,1.9441 2.1318,0.2133 3.955,0.2647 5.338,0.2248 z m -4.5622,-4.1904 c -2.497,-0.2499 -5.0209,-0.7198 -7.476,-1.4553 -0.3703,-2.0022 -0.0102,-4.2653 1.6402,-6.4311 1.2528,-1.6439 3.182,-3.0994 5.9065,-4.2981 0.0681,-0.03 0.1333,-0.0661 0.1948,-0.1078 l 0.2997,-0.2036 c 0.663,-0.4503 0.7223,-1.4062 0.1016,-1.9505 -0.0771,-0.0989 -0.0918,-0.1159 -0.1783,-0.2096 -1.0288,-1.1108 -1.6099,-2.5678 -1.6099,-4.1208 0,-3.3277 2.6637,-6.0204 5.9433,-6.0204 h 0.1379 c 3.2805,0 5.944,2.6925 5.944,6.0204 0,1.5533 -0.5809,3.0102 -1.6083,4.1196 -0.0866,0.093 -0.1069,0.1169 -0.2008,0.2286 -0.5988,0.5292 -0.5391,1.4814 0.1211,1.9315 l 0.2986,0.2035 c 0.062,0.0423 0.1278,0.0788 0.1965,0.1091 2.7248,1.1988 4.6542,2.6543 5.907,4.2981 1.6506,2.1658 2.0108,4.4289 1.6405,6.4311 -2.4554,0.7355 -4.9794,1.2054 -7.4765,1.4553 -1.8207,0.1822 -3.369,0.2258 -4.5225,0.1926 -0.0846,-0.0025 -0.1476,-0.0048 -0.2157,-0.0074 -0.0232,-9e-4 -0.047,-0.0018 -0.0725,-0.0027 l -0.2271,0.0018 c -0.0333,0.0018 -0.1079,0.005 -0.2215,0.0083 -1.1535,0.0332 -2.7019,-0.0104 -4.5226,-0.1926 z"
fill="#000000" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 8.8 KiB

View file

@ -1,49 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
width="80"
height="80"
viewBox="0 0 80 80"
version="1.1"
id="svg8"
sodipodi:docname="menu_forward_custom.svg"
inkscape:version="1.1 (c68e22c387, 2021-05-23)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs12" />
<sodipodi:namedview
id="namedview10"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
showgrid="false"
inkscape:zoom="4.85"
inkscape:cx="-16.597938"
inkscape:cy="43.917526"
inkscape:window-width="1920"
inkscape:window-height="1043"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="svg8" />
<g
fill="none"
fill-rule="evenodd"
id="g6"
transform="matrix(0.94339623,0,0,0.90909091,15,19.609091)">
<g
fill="#444444"
fill-rule="nonzero"
id="g4">
<path
d="M 24.071,28.481 V 26.72 h 1.625 c 1.553,0 2.931,0.043 4.13,0.126 1.15,0.08 2.294,0.223 3.428,0.429 1.003,0.182 1.83,0.418 2.48,0.697 0.632,0.271 1.22,0.618 1.772,1.046 0.425,0.33 0.759,0.708 1.02,1.15 0.308,0.521 0.571,1.195 0.775,2.03 0.213,0.872 0.325,1.914 0.325,3.123 l -0.003,0.362 c -0.008,0.496 -0.033,1.038 -0.074,1.628 l -0.043,0.548 -0.018,0.125 c -0.073,0.46 -0.113,0.841 -0.113,1.229 0,1.225 0.446,2.401 1.308,3.312 0.95,0.999 2.203,1.475 3.496,1.475 1.48,0 2.772,-0.657 3.687,-1.716 l 0.04,-0.047 c 0.335,-0.388 0.616,-0.814 0.855,-1.277 l 0.245,-0.483 0.418,-0.853 C 51.77,34.91 53,30.994 53,27.76 53,24.254 52.46,21.216 51.328,18.652 l -0.15,-0.324 C 47.572,10.724 38.85,7.2 25.697,7.2 H 24.072 V 5.44 C 24.072,3.882 23.409,2.474 22.265,1.449 21.208,0.503 19.88,0 18.464,0 17.048,0 15.72,0.503 14.664,1.45 L 1.808,12.968 C 0.663,13.996 0,15.403 0,16.96 c 0,1.557 0.664,2.965 1.807,3.99 l 12.858,11.522 c 1.058,0.946 2.385,1.448 3.8,1.448 1.414,0 2.742,-0.502 3.797,-1.446 1.146,-1.027 1.81,-2.436 1.81,-3.994 z M 18.464,4 c 0.436,0 0.812,0.143 1.13,0.428 0.278,0.249 0.435,0.538 0.47,0.868 l 0.007,0.144 v 5.76 h 5.625 c 11.936,0 19.26,3.023 21.973,9.068 0.887,2.01 1.331,4.508 1.331,7.493 0,2.401 -0.989,5.632 -2.965,9.692 l -0.666,1.355 -0.16,0.314 c -0.102,0.196 -0.21,0.36 -0.327,0.496 C 44.681,39.873 44.447,40 44.179,40 c -0.251,0 -0.448,-0.075 -0.59,-0.225 -0.143,-0.15 -0.214,-0.337 -0.214,-0.562 l 0.003,-0.089 0.02,-0.223 0.087,-0.613 0.014,-0.148 0.002,-0.052 c 0.083,-1.02 0.125,-1.942 0.125,-2.767 0,-1.515 -0.147,-2.873 -0.44,-4.073 -0.292,-1.2 -0.698,-2.238 -1.217,-3.116 -0.52,-0.878 -1.189,-1.635 -2.01,-2.273 -0.82,-0.637 -1.703,-1.158 -2.649,-1.563 -0.946,-0.406 -2.06,-0.724 -3.34,-0.957 -1.28,-0.232 -2.57,-0.394 -3.867,-0.484 -1.135,-0.078 -2.402,-0.123 -3.8,-0.132 l -0.607,-0.002 h -5.625 v 5.76 c 0,0.39 -0.159,0.728 -0.477,1.013 -0.318,0.284 -0.695,0.427 -1.13,0.427 -0.38,0 -0.716,-0.11 -1.008,-0.327 L 17.334,29.494 4.477,17.973 C 4.16,17.688 4,17.351 4,16.961 4,16.619 4.122,16.318 4.365,16.057 L 4.477,15.947 17.334,4.427 C 17.652,4.143 18.029,4 18.464,4 Z"
transform="matrix(-1,0,0,1,53,0.43)"
id="path2" />
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 3.3 KiB

View file

@ -569,6 +569,11 @@ Server url ikke konfigureret.</translation>
<extracomment>&apos;Do you really want do cancel this meeting?&apos; : Warning message to confirm the cancellation of a meeting.</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<source>icsCancelledMeetingInvite</source>
<extracomment>&apos;Meeting has been cancelled&apos; : ICS Title for cancelled meetings</extracomment>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>ChatConferenceInvitationMessage</name>

View file

@ -569,6 +569,11 @@ Server URL ist nicht konfiguriert.</translation>
<extracomment>&apos;Do you really want do cancel this meeting?&apos; : Warning message to confirm the cancellation of a meeting.</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<source>icsCancelledMeetingInvite</source>
<extracomment>&apos;Meeting has been cancelled&apos; : ICS Title for cancelled meetings</extracomment>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>ChatConferenceInvitationMessage</name>

View file

@ -569,6 +569,11 @@ Server URL not configured.</translation>
<extracomment>&apos;Do you really want do cancel this meeting?&apos; : Warning message to confirm the cancellation of a meeting.</extracomment>
<translation>Do you really want do cancel this meeting?</translation>
</message>
<message>
<source>icsCancelledMeetingInvite</source>
<extracomment>&apos;Meeting has been cancelled&apos; : ICS Title for cancelled meetings</extracomment>
<translation>Meeting has been cancelled</translation>
</message>
</context>
<context>
<name>ChatConferenceInvitationMessage</name>

View file

@ -569,6 +569,11 @@ URL del servidor no configurada.</translation>
<extracomment>&apos;Do you really want do cancel this meeting?&apos; : Warning message to confirm the cancellation of a meeting.</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<source>icsCancelledMeetingInvite</source>
<extracomment>&apos;Meeting has been cancelled&apos; : ICS Title for cancelled meetings</extracomment>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>ChatConferenceInvitationMessage</name>

View file

@ -569,6 +569,11 @@ URL du serveur non configurée.</translation>
<extracomment>&apos;Do you really want do cancel this meeting?&apos; : Warning message to confirm the cancellation of a meeting.</extracomment>
<translation>Voulez-vous supprimer cette réunion ?</translation>
</message>
<message>
<source>icsCancelledMeetingInvite</source>
<extracomment>&apos;Meeting has been cancelled&apos; : ICS Title for cancelled meetings</extracomment>
<translation type="unfinished">La réunion a é annulée</translation>
</message>
</context>
<context>
<name>ChatConferenceInvitationMessage</name>

View file

@ -568,6 +568,11 @@ A kiszolgáló URL-je nincs konfigurálva.</translation>
<extracomment>&apos;Do you really want do cancel this meeting?&apos; : Warning message to confirm the cancellation of a meeting.</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<source>icsCancelledMeetingInvite</source>
<extracomment>&apos;Meeting has been cancelled&apos; : ICS Title for cancelled meetings</extracomment>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>ChatConferenceInvitationMessage</name>

View file

@ -569,6 +569,11 @@ URL del server non configurato.</translation>
<extracomment>&apos;Do you really want do cancel this meeting?&apos; : Warning message to confirm the cancellation of a meeting.</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<source>icsCancelledMeetingInvite</source>
<extracomment>&apos;Meeting has been cancelled&apos; : ICS Title for cancelled meetings</extracomment>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>ChatConferenceInvitationMessage</name>

View file

@ -568,6 +568,11 @@
<extracomment>&apos;Do you really want do cancel this meeting?&apos; : Warning message to confirm the cancellation of a meeting.</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<source>icsCancelledMeetingInvite</source>
<extracomment>&apos;Meeting has been cancelled&apos; : ICS Title for cancelled meetings</extracomment>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>ChatConferenceInvitationMessage</name>

View file

@ -570,6 +570,11 @@ Nesukonfigūruotas serverio url.</translation>
<extracomment>&apos;Do you really want do cancel this meeting?&apos; : Warning message to confirm the cancellation of a meeting.</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<source>icsCancelledMeetingInvite</source>
<extracomment>&apos;Meeting has been cancelled&apos; : ICS Title for cancelled meetings</extracomment>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>ChatConferenceInvitationMessage</name>

View file

@ -569,6 +569,11 @@ URL do servidor não configurado.</translation>
<extracomment>&apos;Do you really want do cancel this meeting?&apos; : Warning message to confirm the cancellation of a meeting.</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<source>icsCancelledMeetingInvite</source>
<extracomment>&apos;Meeting has been cancelled&apos; : ICS Title for cancelled meetings</extracomment>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>ChatConferenceInvitationMessage</name>

View file

@ -570,6 +570,11 @@
<extracomment>&apos;Do you really want do cancel this meeting?&apos; : Warning message to confirm the cancellation of a meeting.</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<source>icsCancelledMeetingInvite</source>
<extracomment>&apos;Meeting has been cancelled&apos; : ICS Title for cancelled meetings</extracomment>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>ChatConferenceInvitationMessage</name>

View file

@ -569,6 +569,11 @@ Serverwebbadressen är inte konfigurerad.</translation>
<extracomment>&apos;Do you really want do cancel this meeting?&apos; : Warning message to confirm the cancellation of a meeting.</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<source>icsCancelledMeetingInvite</source>
<extracomment>&apos;Meeting has been cancelled&apos; : ICS Title for cancelled meetings</extracomment>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>ChatConferenceInvitationMessage</name>

View file

@ -568,6 +568,11 @@ Sunucu url&apos;si yapılandırılmadı.</translation>
<extracomment>&apos;Do you really want do cancel this meeting?&apos; : Warning message to confirm the cancellation of a meeting.</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<source>icsCancelledMeetingInvite</source>
<extracomment>&apos;Meeting has been cancelled&apos; : ICS Title for cancelled meetings</extracomment>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>ChatConferenceInvitationMessage</name>

View file

@ -570,6 +570,11 @@
<extracomment>&apos;Do you really want do cancel this meeting?&apos; : Warning message to confirm the cancellation of a meeting.</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<source>icsCancelledMeetingInvite</source>
<extracomment>&apos;Meeting has been cancelled&apos; : ICS Title for cancelled meetings</extracomment>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>ChatConferenceInvitationMessage</name>

View file

@ -568,6 +568,11 @@
<extracomment>&apos;Do you really want do cancel this meeting?&apos; : Warning message to confirm the cancellation of a meeting.</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<source>icsCancelledMeetingInvite</source>
<extracomment>&apos;Meeting has been cancelled&apos; : ICS Title for cancelled meetings</extracomment>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>ChatConferenceInvitationMessage</name>

View file

@ -62,6 +62,7 @@
<file>assets/images/conference_audio_only_custom.svg</file>
<file>assets/images/conference_layout_grid_custom.svg</file>
<file>assets/images/conference_layout_active_speaker_custom.svg</file>
<file>assets/images/conference_merge_custom.svg</file>
<file>assets/images/contact_add_custom.svg</file>
<file>assets/images/contact_card_photo_custom.svg</file>
<file>assets/images/contact_custom.svg</file>
@ -77,7 +78,6 @@
<file>assets/images/declined_outgoing_call_custom.svg</file>
<file>assets/images/delete_custom.svg</file>
<file>assets/images/download_custom.svg</file>
<file>assets/images/draft_custom.svg</file>
<file>assets/images/drop_down_custom.svg</file>
<file>assets/images/edit_custom.svg</file>
<file>assets/images/ended_call_custom.svg</file>

View file

@ -153,7 +153,7 @@ static inline bool installLocale (App &app, QTranslator &translator, const QLoca
}
static inline string getConfigPathIfExists (const QCommandLineParser &parser) {
QString filePath = parser.value("config");
QString filePath = parser.isSet("config") ? parser.value("config") : "";
string configPath;
if(!QUrl(filePath).isRelative()){
configPath = Utils::appStringToCoreString(FileDownloader::synchronousDownload(filePath, Utils::coreStringToAppString(Paths::getConfigDirPath(false)), true));
@ -217,12 +217,21 @@ App::App (int &argc, char *argv[]) : SingleApplication(argc, argv, true, Mode::U
}
bctbx_set_default_encoding(Constants::LinphoneLocaleEncoding);// Use UTF-8 for internals. Linphone uses UTF-8 so there will be no loss on data with less precise encodings. Qt will do the rest.
createParser();
mParser->parse(this->arguments());
// Get configuration for translators
shared_ptr<linphone::Config> config = Utils::getConfigIfExists (QString::fromStdString(getConfigPathIfExists(*mParser)));
// Init locale.
mTranslator = new DefaultTranslator(this);
mDefaultTranslator = new DefaultTranslator(this);
initLocale(config);
Logger::init(config);
createParser();// Recreate parser in order to use translations from config.
mParser->process(*this);
// Initialize logger.
shared_ptr<linphone::Config> config = Utils::getConfigIfExists (QString::fromStdString(getConfigPathIfExists(*mParser)));
Logger::init(config);
if (mParser->isSet("verbose"))
Logger::getInstance()->setVerbose(true);
@ -230,11 +239,6 @@ App::App (int &argc, char *argv[]) : SingleApplication(argc, argv, true, Mode::U
for (const auto &locale : QDir(Constants::LanguagePath).entryList())
mAvailableLocales << QLocale(locale);
// Init locale.
mTranslator = new DefaultTranslator(this);
mDefaultTranslator = new DefaultTranslator(this);
initLocale(config);
if (mParser->isSet("help")) {
mParser->showHelp();
}

View file

@ -35,6 +35,19 @@ ProxyModel::ProxyModel (QAbstractItemModel * model, const int& defaultFilterMode
sort(0, Qt::DescendingOrder);
}
ProxyModel::~ProxyModel(){
if(mDeleteSourceModel)
deleteSourceModel();
}
void ProxyModel::deleteSourceModel(){
auto oldSourceModel = sourceModel();
if(oldSourceModel) {
oldSourceModel->deleteLater();
setSourceModel(nullptr);
}
}
int ProxyModel::getFilterMode () const {
return mFilterMode;
}

View file

@ -35,6 +35,9 @@ public:
ProxyModel (QObject *parent = Q_NULLPTR);
ProxyModel (QAbstractItemModel * list, const int& defaultFilterMode, QObject *parent = Q_NULLPTR);
virtual ~ProxyModel();
virtual void deleteSourceModel();
int getFilterMode () const;
void setFilterMode (int filterMode);
@ -56,6 +59,8 @@ protected:
bool filterAcceptsRow (int sourceRow, const QModelIndex &sourceParent) const override;
bool lessThan (const QModelIndex &left, const QModelIndex &right) const override;
bool mDeleteSourceModel = false;
private:
int mFilterMode;
};

View file

@ -26,6 +26,19 @@ SortFilterProxyModel::SortFilterProxyModel(QObject * parent) : QSortFilterProxyM
connect(this, &SortFilterProxyModel::rowsRemoved, this, &SortFilterProxyModel::countChanged);
}
SortFilterProxyModel::~SortFilterProxyModel(){
if(mDeleteSourceModel)
deleteSourceModel();
}
void SortFilterProxyModel::deleteSourceModel(){
auto oldSourceModel = sourceModel();
if(oldSourceModel) {
oldSourceModel->deleteLater();
setSourceModel(nullptr);
}
}
int SortFilterProxyModel::getCount() const{
return rowCount();
}

View file

@ -30,6 +30,8 @@ public:
Q_PROPERTY(int filterType READ getFilterType WRITE setFilterType NOTIFY filterTypeChanged)
SortFilterProxyModel(QObject * parent = nullptr);
virtual ~SortFilterProxyModel();
virtual void deleteSourceModel();
virtual int getCount() const;
virtual int getFilterType () const;
@ -47,6 +49,7 @@ signals:
protected:
int mFilterType;
bool mDeleteSourceModel = false;
};
#endif

View file

@ -116,8 +116,10 @@ CallModel::CallModel (shared_ptr<linphone::Call> call){
if(mCall) {
mRemoteAddress = mCall->getRemoteAddress()->clone();
if(mCall->getConference())
if(mCall->getConference()) {
mConferenceModel = ConferenceModel::create(mCall->getConference());
connect(mConferenceModel.get(), &ConferenceModel::participantAdminStatusChanged, this, &CallModel::onParticipantAdminStatusChanged);
}
auto conferenceInfo = CoreManager::getInstance()->getCore()->findConferenceInformationFromUri(getConferenceAddress());
if( conferenceInfo ){
mConferenceInfoModel = ConferenceInfoModel::create(conferenceInfo);
@ -239,7 +241,8 @@ ConferenceInfoModel * CallModel::getConferenceInfoModel(){
QSharedPointer<ConferenceModel> CallModel::getConferenceSharedModel(){
if(mCall->getConference() && !mConferenceModel){
mConferenceModel = ConferenceModel::create(mCall->getConference());
mConferenceModel = ConferenceModel::create(mCall->getConference());
connect(mConferenceModel.get(), &ConferenceModel::participantAdminStatusChanged, this, &CallModel::onParticipantAdminStatusChanged);
emit conferenceModelChanged();
}
return mConferenceModel;
@ -247,7 +250,24 @@ QSharedPointer<ConferenceModel> CallModel::getConferenceSharedModel(){
bool CallModel::isConference () const{
// Check status to avoid crash when requesting a conference on an ended call.
return mCall && (Utils::coreStringToAppString(mCall->getRemoteAddress()->asString()).toLower().contains("conf-id") || (getStatus() != CallStatusEnded && mCall->getConference() != nullptr));
bool isConf = false;
if(mCall){
// Do not call getConference on Ended status.
isConf = (getStatus() != CallStatusEnded && mCall->getConference() != nullptr) || mConferenceInfoModel != nullptr;
if(!isConf){// Check special cases for Linphone. Having conf-id for a conference URI is not standard.
auto remoteAddress = mCall->getRemoteAddress();
if( remoteAddress->getDomain() == Constants::LinphoneDomain){
isConf = remoteAddress->hasUriParam("conf-id");
}
}
}
return isConf;
}
bool CallModel::isOneToOne() const{
return !isConference();
}
// -----------------------------------------------------------------------------
@ -972,6 +992,12 @@ void CallModel::onChatRoomInitialized(int state){
emit chatRoomModelChanged();
}
void CallModel::onParticipantAdminStatusChanged(const std::shared_ptr<const linphone::Participant> & participant){
if(mConferenceModel && participant == mConferenceModel->getConference()->getMe()) {
emit meAdminChanged();
}
}
void CallModel::setRemoteDisplayName(const std::string& name){
mRemoteAddress->setDisplayName(name);
if(mCall) {

View file

@ -55,7 +55,9 @@ class CallModel : public QObject {
Q_PROPERTY(bool isOutgoing READ isOutgoing CONSTANT)
Q_PROPERTY(bool isInConference READ isInConference NOTIFY isInConferenceChanged)
Q_PROPERTY(bool isConference READ isConference CONSTANT)
Q_PROPERTY(bool isConference READ isConference NOTIFY conferenceInfoModelChanged)
Q_PROPERTY(bool isOneToOne READ isOneToOne NOTIFY conferenceInfoModelChanged)
Q_PROPERTY(int duration READ getDuration CONSTANT) // Constants but called with a timer in qml.
Q_PROPERTY(float quality READ getQuality CONSTANT)
@ -145,6 +147,7 @@ public:
return mIsInConference;
}
bool isConference () const;
bool isOneToOne() const;
void setRecordFile (const std::shared_ptr<linphone::CallParams> &callParams);
static void setRecordFile (const std::shared_ptr<linphone::CallParams> &callParams, const QString &to);
@ -205,8 +208,10 @@ public slots:
void endCall();
void onRemoteRecording(const std::shared_ptr<linphone::Call> & call, bool recording);
void onChatRoomInitialized(int state);
void onParticipantAdminStatusChanged(const std::shared_ptr<const linphone::Participant> & participant);
signals:
void meAdminChanged();
void callErrorChanged (const QString &callError);
void callIdChanged();
void isInConferenceChanged (bool status);

View file

@ -69,6 +69,7 @@ CallsListModel::CallsListModel (QObject *parent) : ProxyListModel(parent) {
mCoreHandlers.get(), &CoreHandlers::callStateChanged,
this, &CallsListModel::handleCallStateChanged
);
connect(this, &CallsListModel::countChanged, this, &CallsListModel::canMergeCallsChanged);
}
CallModel *CallsListModel::findCallModelFromPeerAddress (const QString &peerAddress) const {
@ -343,7 +344,7 @@ QVariantMap CallsListModel::createChatRoom(const QString& subject, const int& se
initializer->setAdminsData(admins);
ChatRoomInitializer::start(initializer);
}
timeline = timelineList->getTimeline(chatRoom, ChatRoomModel::isTerminated(chatRoom));
timeline = timelineList->getTimeline(chatRoom, true);
}else{
if(admins.size() > 0){
ChatRoomInitializer::create(chatRoom)->setAdmins(admins);
@ -354,9 +355,7 @@ QVariantMap CallsListModel::createChatRoom(const QString& subject, const int& se
CoreManager::getInstance()->getTimelineListModel()->mAutoSelectAfterCreation = false;
result["chatRoomModel"] = QVariant::fromValue(timeline->getChatRoomModel());
if(selectAfterCreation) {// The timeline here will not receive the first creation event. Set Selected if needed
QTimer::singleShot(200, [timeline](){// Delay process in order to let GUI time for Timeline building/linking before doing actions
timeline->setSelected(true);
});
timeline->delaySelected();
}
}
}
@ -368,19 +367,106 @@ QVariantMap CallsListModel::createChatRoom(const QString& subject, const int& se
}
void CallsListModel::prepareConferenceCall(ConferenceInfoModel * model){
auto app = App::getInstance();
app->smartShowWindow(app->getCallsWindow());
emit callConferenceAsked(model);
if(model->getConferenceInfoState() != LinphoneEnums::ConferenceInfoStateCancelled) {
auto app = App::getInstance();
app->smartShowWindow(app->getCallsWindow());
emit callConferenceAsked(model);
}
}
int CallsListModel::addAllToConference(){
return CoreManager::getInstance()->getCore()->addAllToConference();
}
void CallsListModel::mergeAll(){
auto core = CoreManager::getInstance()->getCore();
auto currentCalls = CoreManager::getInstance()->getCore()->getCalls();
shared_ptr<linphone::Conference> conference = core->getConference();
// Search a managable conference from calls
if(!conference){
for(auto call : currentCalls){
auto dbConference = call->getConference();
if(dbConference && dbConference->getMe()->isAdmin()){
conference = dbConference;
break;
}
}
}
auto currentCall = CoreManager::getInstance()->getCore()->getCurrentCall();
bool enablingVideo = false;
if( currentCall )
enablingVideo = currentCall->getCurrentParams()->videoEnabled();
if(!conference){
auto parameters = core->createConferenceParams(conference);
if(!CoreManager::getInstance()->getSettingsModel()->getVideoConferenceEnabled()) {
parameters->enableVideo(false);
parameters->setConferenceFactoryAddress(nullptr);// Do a local conference
parameters->setSubject("Local meeting");
}else{
parameters->enableVideo(enablingVideo);
parameters->setSubject("Meeting");
}
conference = core->createConferenceWithParams(parameters);
}
list<shared_ptr<linphone::Address>> allLinphoneAddresses;
list<shared_ptr<linphone::Address>> newCalls;
list<shared_ptr<linphone::Call>> runningCallsToAdd;
for(auto call : currentCalls){
if(!call->getConference()){
runningCallsToAdd.push_back(call);
}
}
// 1) Add running calls
if( runningCallsToAdd.size() > 0){
conference->addParticipants(runningCallsToAdd);
}
/*
// 2) Put in pause and remove all calls that are not in the conference list
for(const auto &call : CoreManager::getInstance()->getCore()->getCalls()){
const std::string callAddress = call->getRemoteAddress()->asStringUriOnly();
auto address = allLinphoneAddresses.begin();
while(address != allLinphoneAddresses.end() && (*address)->asStringUriOnly() != callAddress)
++address;
if(address == allLinphoneAddresses.end()){// Not in conference list : put in pause and remove it from conference if it's the case
if( call->getParams()->getLocalConferenceMode() ){// Remove conference if it is not yet requested
CoreManager::getInstance()->getCore()->removeFromConference(call);
}else
call->pause();
}
}*/
}
// -----------------------------------------------------------------------------
int CallsListModel::getRunningCallsNumber () const {
return CoreManager::getInstance()->getCore()->getCallsNb();
}
bool CallsListModel::canMergeCalls()const{
auto calls = CoreManager::getInstance()->getCore()->getCalls();
bool mergableConference = false;
int mergableCalls = 0;
bool mergable = false;
for(auto itCall = calls.begin(); !mergable && itCall != calls.end() ; ++itCall ) {
auto conference = (*itCall)->getConference();
if(conference){
if( !mergableConference )
mergableConference = (conference && conference->getMe()->isAdmin());
}else{
++mergableCalls;
}
mergable = (mergableConference && mergableCalls>0) // A call can be merged into the conference
|| mergableCalls>1;// 2 calls can be merged
}
return mergable;
}
void CallsListModel::terminateAllCalls () const {
CoreManager::getInstance()->getCore()->terminateAllCalls();
}
@ -479,6 +565,8 @@ void CallsListModel::addCall (const shared_ptr<linphone::Call> &call) {
qInfo() << QStringLiteral("Add call:") << callModel->getFullLocalAddress() << callModel->getFullPeerAddress();
App::getInstance()->getEngine()->setObjectOwnership(callModel.get(), QQmlEngine::CppOwnership);
connect(callModel.get(), &CallModel::meAdminChanged, this, &CallsListModel::canMergeCallsChanged);
add(callModel);
emit layoutChanged();
@ -511,7 +599,7 @@ void CallsListModel::addDummyCall () {
int id = findCallIndex(mList, *callModel);
emit dataChanged(index(id, 0), index(id, 0));
});
connect(callModel.get(), &CallModel::meAdminChanged, this, &CallsListModel::canMergeCallsChanged);
add(callModel);
emit layoutChanged();

View file

@ -37,6 +37,9 @@ class CallsListModel : public ProxyListModel {
Q_OBJECT
public:
Q_PROPERTY(bool canMergeCalls READ canMergeCalls NOTIFY canMergeCallsChanged)
CallsListModel (QObject *parent = Q_NULLPTR);
CallModel *findCallModelFromPeerAddress (const QString &peerAddress) const;
@ -57,9 +60,11 @@ public:
Q_INVOKABLE void prepareConferenceCall(ConferenceInfoModel * model);
Q_INVOKABLE int addAllToConference();
Q_INVOKABLE void mergeAll();
Q_INVOKABLE int getRunningCallsNumber () const;
bool canMergeCalls()const;
Q_INVOKABLE void terminateAllCalls () const;
Q_INVOKABLE void terminateCall (const QString& sipAddress) const;
@ -73,6 +78,7 @@ signals:
void callConferenceAsked(ConferenceInfoModel * conferenceInfoModel);
void callMissed (CallModel *callModel);
void canMergeCallsChanged();
private:

View file

@ -44,6 +44,7 @@ int Camera::mPreviewCounter;
// =============================================================================
Camera::Camera (QQuickItem *parent) : QQuickFramebufferObject(parent) {
qDebug() << "[Camera] Camera constructor" << this;
updateWindowIdLocation();
setTextureFollowsItemSize(true);
// The fbo content must be y-mirrored because the ms rendering is y-inverted.
@ -66,9 +67,11 @@ Camera::Camera (QQuickItem *parent) : QQuickFramebufferObject(parent) {
Camera::~Camera(){
qDebug() << "[Camera] Camera destructor" << this;
mRefreshTimer->stop();
if(mIsPreview)
deactivatePreview();
setWindowIdLocation(None);
setWindowIdLocation(None);// We need to remove the Qt Buffer from SDK ot avoid to reuse it.
}
void Camera::resetWindowId() const{
@ -157,8 +160,6 @@ void Camera::removeParticipantDeviceModel(){
}
QQuickFramebufferObject::Renderer *Camera::createRenderer () const {
resetWindowId();
QQuickFramebufferObject::Renderer * renderer = NULL;
if(mWindowIdLocation == CorePreview){
qDebug() << "[Camera] Setting Camera to Preview";

View file

@ -83,7 +83,7 @@ void ChatRoomInitializer::setAdmins(QList< std::shared_ptr<linphone::Address>> a
void ChatRoomInitializer::start(QSharedPointer<ChatRoomInitializer> initializer){
QObject * context = new QObject();
QObject::connect(initializer.get(), &ChatRoomInitializer::finished, context, [context, initializer](int state){
QObject::connect(initializer.get(), &ChatRoomInitializer::finished, context, [context, initializer](LinphoneEnums::ChatRoomState state){
qDebug() << "[ChatRoomInitializer] initialized";
context->deleteLater();// This will destroy context and initializer
});
@ -93,7 +93,7 @@ void ChatRoomInitializer::checkInitialization(){
if( mAdmins.size() > 0 && !mAdminsSet)
return;
emit finished((int)mChatRoom->getState());
emit finished(LinphoneEnums::fromLinphone(mChatRoom->getState()));
}
void ChatRoomInitializer::onConferenceJoined(const std::shared_ptr<linphone::ChatRoom> & chatRoom, const std::shared_ptr<const linphone::EventLog> & eventLog) {

View file

@ -24,6 +24,7 @@
#include <linphone++/linphone.hh>
#include "ChatRoomInitializer.hpp"
#include "utils/LinphoneEnums.hpp"
#include <QList>
@ -54,7 +55,7 @@ public:
virtual void onStateChanged(const std::shared_ptr<linphone::ChatRoom> & chatRoom, linphone::ChatRoom::State newState);
signals:
void finished(int state); // this signal is emit before deletion and give the current linphone::ChatRoom:State of the chat room.
void finished(LinphoneEnums::ChatRoomState state); // this signal is emit before deletion and give the current linphone::ChatRoom:State of the chat room.
private:
void connectTo(ChatRoomListener * listener);

View file

@ -107,7 +107,7 @@ void ChatRoomModel::connectTo(ChatRoomListener * listener){
}
// -----------------------------------------------------------------------------
QSharedPointer<ChatRoomModel> ChatRoomModel::create(std::shared_ptr<linphone::ChatRoom> chatRoom, const std::list<std::shared_ptr<linphone::CallLog>>& callLogs){
QSharedPointer<ChatRoomModel> ChatRoomModel::create(const std::shared_ptr<linphone::ChatRoom>& chatRoom, const std::list<std::shared_ptr<linphone::CallLog>>& callLogs){
QSharedPointer<ChatRoomModel> model = QSharedPointer<ChatRoomModel>::create(chatRoom, callLogs);
if(model){
model->mSelf = model;
@ -117,7 +117,7 @@ QSharedPointer<ChatRoomModel> ChatRoomModel::create(std::shared_ptr<linphone::Ch
return nullptr;
}
ChatRoomModel::ChatRoomModel (std::shared_ptr<linphone::ChatRoom> chatRoom, const std::list<std::shared_ptr<linphone::CallLog>>& callLogs, QObject * parent) : ProxyListModel(parent){
ChatRoomModel::ChatRoomModel (const std::shared_ptr<linphone::ChatRoom>& chatRoom, const std::list<std::shared_ptr<linphone::CallLog>>& callLogs, QObject * parent) : ProxyListModel(parent){
App::getInstance()->getEngine()->setObjectOwnership(this, QQmlEngine::CppOwnership);// Avoid QML to destroy it when passing by Q_INVOKABLE
CoreManager *coreManager = CoreManager::getInstance();
mCoreHandlers = coreManager->getHandlers();
@ -149,6 +149,7 @@ ChatRoomModel::ChatRoomModel (std::shared_ptr<linphone::ChatRoom> chatRoom, cons
QObject::connect(coreManager->getContactsListModel(), &ContactsListModel::contactUpdated, this, &ChatRoomModel::avatarChanged);
connect(this, &ChatRoomModel::fullPeerAddressChanged, this, &ChatRoomModel::usernameChanged);
connect(this, &ChatRoomModel::stateChanged, this, &ChatRoomModel::updatingChanged);
if(mChatRoom){
mParticipantListModel = QSharedPointer<ParticipantListModel>::create(this);
@ -311,11 +312,11 @@ QString ChatRoomModel::getLocalAddress () const {
}
QString ChatRoomModel::getFullPeerAddress () const {
return mChatRoom ? Utils::coreStringToAppString(mChatRoom->getPeerAddress()->asString()) : "";
return mChatRoom && mChatRoom->getPeerAddress() ? Utils::coreStringToAppString(mChatRoom->getPeerAddress()->asString()) : "";
}
QString ChatRoomModel::getFullLocalAddress () const {
return mChatRoom ? Utils::coreStringToAppString(mChatRoom->getLocalAddress()->asString()) : "";
return mChatRoom && mChatRoom->getLocalAddress()? Utils::coreStringToAppString(mChatRoom->getLocalAddress()->asString()) : "";
}
QString ChatRoomModel::getConferenceAddress () const {
@ -404,8 +405,8 @@ std::list<std::shared_ptr<linphone::Participant>> ChatRoomModel::getParticipants
return participantList;
}
int ChatRoomModel::getState() const {
return mChatRoom ? (int)mChatRoom->getState() : 0;
LinphoneEnums::ChatRoomState ChatRoomModel::getState() const {
return mChatRoom ? LinphoneEnums::fromLinphone(mChatRoom->getState()) : LinphoneEnums::ChatRoomStateNone;
}
bool ChatRoomModel::isReadOnly() const{
@ -481,6 +482,10 @@ bool ChatRoomModel::isBasic() const{
return mChatRoom && mChatRoom->hasCapability((int)linphone::ChatRoomCapabilities::Basic);
}
bool ChatRoomModel::isUpdating() const{
return getState() == LinphoneEnums::ChatRoomStateCreationPending || getState() == LinphoneEnums::ChatRoomStateTerminationPending;
}
std::shared_ptr<linphone::ChatRoom> ChatRoomModel::getChatRoom(){
return mChatRoom;
}
@ -523,14 +528,6 @@ int ChatRoomModel::getAllUnreadCount(){
return mUnreadMessagesCount + mMissedCallsCount;
}
QString ChatRoomModel::getCachedText()const{
return mCachedText;
}
bool ChatRoomModel::hasDraft() const{
return mHasDraft;
}
//------------------------------------------------------------------------------------------------
void ChatRoomModel::setSubject(QString& subject){
@ -605,23 +602,6 @@ void ChatRoomModel::enableMarkAsRead(const bool& enable){
}
}
bool ChatRoomModel::setCachedText(const QString& text){
if(mCachedText != text){
mCachedText = text;
emit cachedTextChanged();
setHasDraft(!mCachedText.isEmpty());
return true;
}else
return false;
}
void ChatRoomModel::setHasDraft(const bool& cached){
if(mHasDraft != cached){
mHasDraft = cached;
emit hasDraftChanged();
}
}
void ChatRoomModel::setReply(ChatMessageModel * model){
if(model != mReplyModel.get()){
if( model && model->getChatMessage() )
@ -645,15 +625,14 @@ void ChatRoomModel::markAsToDelete(){
void ChatRoomModel::deleteChatRoom(){
qInfo() << "Deleting ChatRoom : " << getSubject() << ", address=" << getFullPeerAddress();
if(mChatRoom){
mChatRoom->removeListener(mChatRoomListener);
CoreManager::getInstance()->getCore()->deleteChatRoom(mChatRoom);
}
emit chatRoomDeleted();
}
void ChatRoomModel::leaveChatRoom (){
if(mChatRoom){
mChatRoom->leave();
if(!isReadOnly())
mChatRoom->leave();
if( mChatRoom->getHistorySize() == 0 && mChatRoom->getHistoryEventsSize() == 0)
deleteChatRoom();
}
@ -724,7 +703,6 @@ void ChatRoomModel::sendMessage (const QString &message) {
if(recorder->haveVocalRecorder())
recorder->clearVocalRecorder();
CoreManager::getInstance()->getChatModel()->clear();
setCachedText("");
}
}
@ -744,8 +722,8 @@ void ChatRoomModel::forwardMessage(ChatMessageModel * model){
}
// -----------------------------------------------------------------------------
void ChatRoomModel::compose (const QString& text) {
if( setCachedText(text) && mChatRoom)// only send a compose if text has changed
void ChatRoomModel::compose () {
if( mChatRoom)
mChatRoom->compose();
}
@ -880,23 +858,31 @@ int ChatRoomModel::loadTillMessage(ChatMessageModel * message){
});
// if not find, load more entries and find it in new entries.
if( entry == mList.end()){
mPostModelChangedEvents = false;
beginResetModel();
int newEntries = loadMoreEntries();
while( newEntries > 0){// no more new entries
int entryCount = 0;
entry = mList.begin();
auto chatEventEntry = entry->objectCast<ChatEvent>();
auto chatEventEntry = entry->objectCast<ChatEvent>();
while(entryCount < newEntries &&
(chatEventEntry->mType != ChatRoomModel::EntryType::MessageEntry || chatEventEntry.objectCast<ChatMessageModel>()->getChatMessage() != linphoneMessage)
){
++entryCount;
++entry;
if( entry != mList.end())
chatEventEntry = entry->objectCast<ChatEvent>();
}
if( entryCount < newEntries){// We got it
qDebug() << "Find message at " << entryCount << " after loading new entries";
mPostModelChangedEvents = true;
endResetModel();
return entryCount;
}else
newEntries = loadMoreEntries();// continue
}
mPostModelChangedEvents = true;
endResetModel();
}else{
int entryCount = entry - mList.begin();
qDebug() << "Find message at " << entryCount;
@ -1051,18 +1037,21 @@ int ChatRoomModel::loadMoreEntries(){
EntrySorterHelper::getLimitedSelection(&entries, prepareEntries, mLastEntriesStep, this);
if(entries.size() >0){
beginInsertRows(QModelIndex(), 0, entries.size()-1);
if(mPostModelChangedEvents)
beginInsertRows(QModelIndex(), 0, entries.size()-1);
for(auto entry : entries)
mList.prepend(entry);
endInsertRows();
if(mPostModelChangedEvents)
endInsertRows();
//emit layoutChanged();
updateLastUpdateTime();
}
newEntries = entries.size();
}while( newEntries>0 && currentRowCount == rowCount());
currentRowCount = rowCount() - currentRowCount + 1;
currentRowCount = rowCount() - currentRowCount;
setEntriesLoading(false);
emit moreEntriesLoaded(currentRowCount);
if(mPostModelChangedEvents)
emit moreEntriesLoaded(currentRowCount);
return currentRowCount;
}
@ -1337,6 +1326,11 @@ void ChatRoomModel::onParticipantAdminStatusChanged(const std::shared_ptr<linpho
void ChatRoomModel::onStateChanged(const std::shared_ptr<linphone::ChatRoom> & chatRoom, linphone::ChatRoom::State newState){
updateLastUpdateTime();
emit stateChanged(getState());
if(newState == linphone::ChatRoom::State::Deleted){
mChatRoom->removeListener(mChatRoomListener);
mChatRoom = nullptr;
emit chatRoomDeleted();
}
}
void ChatRoomModel::onSecurityEvent(const std::shared_ptr<linphone::ChatRoom> & chatRoom, const std::shared_ptr<const linphone::EventLog> & eventLog){

View file

@ -25,6 +25,8 @@
#include "app/proxyModel/ProxyListModel.hpp"
#include <QDateTime>
#include "utils/LinphoneEnums.hpp"
// =============================================================================
// Fetch all N messages of a ChatRoom.
// =============================================================================
@ -73,13 +75,14 @@ public:
Q_PROPERTY(bool isComposing READ getIsRemoteComposing NOTIFY isRemoteComposingChanged)
Q_PROPERTY(QList<QString> composers READ getComposers NOTIFY isRemoteComposingChanged)
Q_PROPERTY(bool isReadOnly READ isReadOnly NOTIFY isReadOnlyChanged)
Q_PROPERTY(bool updating READ isUpdating NOTIFY updatingChanged)
Q_PROPERTY(QString sipAddress READ getFullPeerAddress NOTIFY fullPeerAddressChanged)
Q_PROPERTY(QString sipAddressUriOnly READ getPeerAddress NOTIFY fullPeerAddressChanged)
Q_PROPERTY(QString username READ getUsername NOTIFY usernameChanged)
Q_PROPERTY(QString avatar READ getAvatar NOTIFY avatarChanged)
Q_PROPERTY(int presenceStatus READ getPresenceStatus NOTIFY presenceStatusChanged)
Q_PROPERTY(int state READ getState NOTIFY stateChanged)
Q_PROPERTY(LinphoneEnums::ChatRoomState state READ getState NOTIFY stateChanged)
Q_PROPERTY(long ephemeralLifetime READ getEphemeralLifetime WRITE setEphemeralLifetime NOTIFY ephemeralLifetimeChanged)
Q_PROPERTY(bool ephemeralEnabled READ isEphemeralEnabled WRITE setEphemeralEnabled NOTIFY ephemeralEnabledChanged)
@ -92,12 +95,9 @@ public:
Q_PROPERTY(bool entriesLoading READ isEntriesLoading WRITE setEntriesLoading NOTIFY entriesLoadingChanged)
Q_PROPERTY(QString cachedText READ getCachedText WRITE setCachedText NOTIFY cachedTextChanged)
Q_PROPERTY(bool hasDraft READ hasDraft WRITE setHasDraft NOTIFY hasDraftChanged)
static QSharedPointer<ChatRoomModel> create(std::shared_ptr<linphone::ChatRoom> chatRoom, const std::list<std::shared_ptr<linphone::CallLog>>& callLogs = std::list<std::shared_ptr<linphone::CallLog>>());
ChatRoomModel (std::shared_ptr<linphone::ChatRoom> chatRoom, const std::list<std::shared_ptr<linphone::CallLog>>& callLogs = std::list<std::shared_ptr<linphone::CallLog>>(), QObject * parent = nullptr);
static QSharedPointer<ChatRoomModel> create(const std::shared_ptr<linphone::ChatRoom>& chatRoom, const std::list<std::shared_ptr<linphone::CallLog>>& callLogs = std::list<std::shared_ptr<linphone::CallLog>>());
ChatRoomModel (const std::shared_ptr<linphone::ChatRoom>& chatRoom, const std::list<std::shared_ptr<linphone::CallLog>>& callLogs = std::list<std::shared_ptr<linphone::CallLog>>(), QObject * parent = nullptr);
~ChatRoomModel ();
@ -119,7 +119,7 @@ public:
QString getUsername () const;
QString getAvatar () const;
int getPresenceStatus() const;
int getState() const;
LinphoneEnums::ChatRoomState getState() const;
bool isReadOnly() const;
bool isEphemeralEnabled() const;
long getEphemeralLifetime() const;
@ -138,14 +138,14 @@ public:
bool getIsRemoteComposing () const;
bool isEntriesLoading() const;
bool isBasic() const;
bool isUpdating() const;
ParticipantListModel* getParticipantListModel() const;
std::list<std::shared_ptr<linphone::Participant>> getParticipants(const bool& withMe = true) const;
std::shared_ptr<linphone::ChatRoom> getChatRoom();
QList<QString> getComposers();
QString getParticipantAddress(); // return peerAddress if not secure else return the first participant SIP address.
int getAllUnreadCount(); // Return unread messages and missed call.
QString getCachedText() const;
bool hasDraft() const;
//---- Setters
void setSubject(QString& subject);
@ -159,8 +159,6 @@ public:
void setEphemeralEnabled(bool enabled);
void setEphemeralLifetime(long lifetime);
void enableMarkAsRead(const bool& enable);
bool setCachedText(const QString& text); // return true if cache changed
void setHasDraft(const bool& draft);
void setReply(ChatMessageModel * model);
ChatMessageModel * getReply()const;
@ -175,7 +173,7 @@ public:
Q_INVOKABLE void updateParticipants(const QVariantList& participants);
void sendMessage (const QString &message);
Q_INVOKABLE void forwardMessage(ChatMessageModel * model);
void compose (const QString& text);
void compose ();
Q_INVOKABLE void resetMessageCount ();
void initEntries();
Q_INVOKABLE int loadMoreEntries(); // return new entries count
@ -280,8 +278,7 @@ signals:
void markAsReadEnabledChanged();
void chatRoomDeleted();// Must be connected with DirectConnection mode
void replyChanged();
void cachedTextChanged();
void hasDraftChanged();
void updatingChanged();
// Chat Room listener callbacks
@ -314,8 +311,7 @@ private:
QSharedPointer<ChatMessageModel> mReplyModel;
QSharedPointer<ChatNoticeModel> mUnreadMessageNotice;
int mBindingCalls = 0;
QString mCachedText; // TODO : replace it by content to manage files/audio etc.
bool mHasDraft = false;
bool mPostModelChangedEvents = true;
QWeakPointer<ChatRoomModel> mSelf;
};

View file

@ -36,6 +36,8 @@
using namespace std;
QString ChatRoomProxyModel::gCachedText;
// =============================================================================
ChatRoomProxyModel::ChatRoomProxyModel (QObject *parent) : QSortFilterProxyModel(parent) {
@ -55,6 +57,7 @@ ChatRoomProxyModel::ChatRoomProxyModel (QObject *parent) : QSortFilterProxyModel
}
ChatRoomProxyModel::~ChatRoomProxyModel(){
setSourceModel(nullptr);
setChatRoomModel(nullptr); // Do remove process like setting haveCall if is Call.
}
@ -99,7 +102,8 @@ CREATE_PARENT_MODEL_FUNCTION(deleteChatRoom)
void ChatRoomProxyModel::compose (const QString& text) {
if (mChatRoomModel)
mChatRoomModel->compose(text);
mChatRoomModel->compose();
gCachedText = text;
}
int ChatRoomProxyModel::getEntryTypeFilter () {
@ -239,6 +243,10 @@ QVariant ChatRoomProxyModel::getAt(int row){
return sourceModel()->data(sourceIndex);
}
QString ChatRoomProxyModel::getCachedText() const{
return gCachedText;
}
void ChatRoomProxyModel::setIsCall(const bool& isCall){
if(mIsCall != isCall) {
if(mChatRoomModel){
@ -328,7 +336,7 @@ void ChatRoomProxyModel::setChatRoomModel (ChatRoomModel *chatRoomModel){
}else{
if(mIsCall && mChatRoomModel)
mChatRoomModel->removeBindingCall();
mChatRoomModel = nullptr;
mChatRoomModel = nullptr;
}
}
// -----------------------------------------------------------------------------

View file

@ -40,6 +40,7 @@ class ChatRoomProxyModel : public QSortFilterProxyModel {
Q_PROPERTY(QString fullLocalAddress READ getFullLocalAddress WRITE setFullLocalAddress NOTIFY fullLocalAddressChanged)
Q_PROPERTY(ChatRoomModel *chatRoomModel READ getChatRoomModel WRITE setChatRoomModel NOTIFY chatRoomModelChanged)
Q_PROPERTY(QList<QString> composers READ getComposers NOTIFY isRemoteComposingChanged)
Q_PROPERTY(QString cachedText READ getCachedText)
Q_PROPERTY(QString filterText MEMBER mFilterText WRITE setFilterText NOTIFY filterTextChanged)
Q_PROPERTY(bool markAsReadEnabled READ markAsReadEnabled WRITE enableMarkAsRead NOTIFY markAsReadEnabledChanged)// Focus is at end of the list. Used to reset message count if not at end
@ -119,6 +120,9 @@ private:
void setChatRoomModel (ChatRoomModel *chatRoomModel);
QList<QString> getComposers () const;
QString getCachedText() const;
void reload (ChatRoomModel *chatRoomModel);
void handleIsActiveChanged (QWindow *window);
@ -134,6 +138,7 @@ private:
QString mLocalAddress;
QString mFullPeerAddress;
QString mFullLocalAddress;
static QString gCachedText;
bool mMarkAsReadEnabled;
bool mIsCall = false;

View file

@ -41,6 +41,11 @@ ConferenceListener::~ConferenceListener(){
//-----------------------------------------------------------------------------------------------------------------------
// LINPHONE LISTENERS
//-----------------------------------------------------------------------------------------------------------------------
void ConferenceListener::onActiveSpeakerParticipantDevice(const std::shared_ptr<linphone::Conference> & conference, const std::shared_ptr<const linphone::ParticipantDevice> & participantDevice) {
qDebug() << "onActiveSpeakerParticipantDevice: " << participantDevice->getAddress()->asString().c_str();
emit activeSpeakerParticipantDevice(participantDevice);
}
void ConferenceListener::onParticipantAdded(const std::shared_ptr<linphone::Conference> & conference, const std::shared_ptr<const linphone::Participant> & participant){
qDebug() << "onParticipantAdded: " << participant->getAddress()->asString().c_str();
emit participantAdded(participant);

View file

@ -35,6 +35,7 @@ public:
virtual ~ConferenceListener();
// LINPHONE LISTENERS
virtual void onActiveSpeakerParticipantDevice(const std::shared_ptr<linphone::Conference> & conference, const std::shared_ptr<const linphone::ParticipantDevice> & participantDevice) override;
virtual void onParticipantAdded(const std::shared_ptr<linphone::Conference> & conference, const std::shared_ptr<const linphone::Participant> & participant) override;
virtual void onParticipantRemoved(const std::shared_ptr<linphone::Conference> & conference, const std::shared_ptr<const linphone::Participant> & participant) override;
virtual void onParticipantAdminStatusChanged(const std::shared_ptr<linphone::Conference> & conference, const std::shared_ptr<const linphone::Participant> & participant) override;
@ -50,6 +51,7 @@ public:
//---------------------------------------------------------------------------
signals:
void activeSpeakerParticipantDevice(const std::shared_ptr<const linphone::ParticipantDevice> & participantDevice);
void participantAdded(const std::shared_ptr<const linphone::Participant> & participant);
void participantRemoved(const std::shared_ptr<const linphone::Participant> & participant);
void participantAdminStatusChanged(const std::shared_ptr<const linphone::Participant> & participant);
@ -61,6 +63,7 @@ signals:
void participantDeviceIsSpeakingChanged(const std::shared_ptr<const linphone::ParticipantDevice> & participantDevice, bool isSpeaking);
void conferenceStateChanged(linphone::Conference::State newState);
void subjectChanged(const std::string & subject);
};

View file

@ -37,6 +37,7 @@
#include "components/Components.hpp"
void ConferenceModel::connectTo(ConferenceListener * listener){
connect(listener, &ConferenceListener::activeSpeakerParticipantDevice, this, &ConferenceModel::onActiveSpeakerParticipantDevice);
connect(listener, &ConferenceListener::participantAdded, this, &ConferenceModel::onParticipantAdded);
connect(listener, &ConferenceListener::participantRemoved, this, &ConferenceModel::onParticipantRemoved);
connect(listener, &ConferenceListener::participantAdminStatusChanged, this, &ConferenceModel::onParticipantAdminStatusChanged);
@ -140,6 +141,9 @@ void ConferenceModel::setIsReady(bool state){
//-----------------------------------------------------------------------------------------------------------------------
// LINPHONE LISTENERS
//-----------------------------------------------------------------------------------------------------------------------
void ConferenceModel::onActiveSpeakerParticipantDevice(const std::shared_ptr<const linphone::ParticipantDevice> & participantDevice){
emit activeSpeakerParticipantDevice(participantDevice);
}
void ConferenceModel::onParticipantAdded(const std::shared_ptr<const linphone::Participant> & participant){
qDebug() << "Added call, participant count: " << getParticipantList().size() << ". Me devices : " << mConference->getMe()->getDevices().size();
updateLocalParticipant();

View file

@ -62,7 +62,7 @@ public:
void setIsReady(bool state);
virtual void onActiveSpeakerParticipantDevice(const std::shared_ptr<const linphone::ParticipantDevice> & participantDevice);
virtual void onParticipantAdded(const std::shared_ptr<const linphone::Participant> & participant);
virtual void onParticipantRemoved(const std::shared_ptr<const linphone::Participant> & participant);
virtual void onParticipantAdminStatusChanged(const std::shared_ptr<const linphone::Participant> & participant);
@ -77,6 +77,7 @@ public:
//---------------------------------------------------------------------------
signals:
void activeSpeakerParticipantDevice(const std::shared_ptr<const linphone::ParticipantDevice> & participantDevice);
void localParticipantChanged();
void participantAdded(const std::shared_ptr<const linphone::Participant> & participant);
void participantRemoved(const std::shared_ptr<const linphone::Participant> & participant);

View file

@ -38,6 +38,7 @@
using namespace std;
ConferenceProxyModel::ConferenceProxyModel (QObject *parent) : SortFilterProxyModel(parent) {
mDeleteSourceModel = false;
setSourceModel(CoreManager::getInstance()->getCallsListModel());
emit conferenceChanged();

View file

@ -43,7 +43,7 @@ ConferenceInfoListModel::ConferenceInfoListModel (QObject *parent) : ProxyListMo
auto conferenceInfos = coreManager->getCore()->getConferenceInformationList();
QList<QSharedPointer<ConferenceInfoModel> > items;
for(auto conferenceInfo : conferenceInfos){
auto item = build(conferenceInfo);
auto item = build(conferenceInfo, mBuildAll);
if(item)
items << item;
}
@ -53,10 +53,10 @@ ConferenceInfoListModel::ConferenceInfoListModel (QObject *parent) : ProxyListMo
// -----------------------------------------------------------------------------
QSharedPointer<ConferenceInfoModel> ConferenceInfoListModel::build(const std::shared_ptr<linphone::ConferenceInfo> & conferenceInfo) const{
QSharedPointer<ConferenceInfoModel> ConferenceInfoListModel::build(const std::shared_ptr<linphone::ConferenceInfo> & conferenceInfo, const bool& buildAll) const{
auto me = CoreManager::getInstance()->getCore()->getDefaultAccount()->getParams()->getIdentityAddress();
std::list<std::shared_ptr<linphone::Address>> participants = conferenceInfo->getParticipants();
bool haveMe = conferenceInfo->getOrganizer()->weakEqual(me);
bool haveMe = buildAll || conferenceInfo->getOrganizer()->weakEqual(me);
if(!haveMe)
haveMe = (std::find_if(participants.begin(), participants.end(), [me](const std::shared_ptr<linphone::Address>& address){
return me->weakEqual(address);
@ -70,7 +70,7 @@ QSharedPointer<ConferenceInfoModel> ConferenceInfoListModel::build(const std::sh
}
void ConferenceInfoListModel::add(const std::shared_ptr<linphone::ConferenceInfo> & conferenceInfo, const bool& sendEvents){
auto item = build(conferenceInfo);
auto item = build(conferenceInfo, mBuildAll);
if( item)
ProxyListModel::add(item);
}
@ -89,7 +89,7 @@ QVariant ConferenceInfoListModel::data (const QModelIndex &index, int role ) con
if (role == Qt::DisplayRole)
return QVariant::fromValue(mList[row].get());
else if (role == Qt::DisplayRole +1 )
return QVariant::fromValue(mList[row].objectCast<ConferenceInfoModel>()->getDateTimeUtc().date());
return QVariant::fromValue(mList[row].objectCast<ConferenceInfoModel>()->getDateTimeSystem().date());
return QVariant();
}

View file

@ -37,7 +37,7 @@ class ConferenceInfoListModel : public ProxyListModel {
public:
ConferenceInfoListModel (QObject *parent = Q_NULLPTR);
QSharedPointer<ConferenceInfoModel> build(const std::shared_ptr<linphone::ConferenceInfo> & conferenceInfo) const;
QSharedPointer<ConferenceInfoModel> build(const std::shared_ptr<linphone::ConferenceInfo> & conferenceInfo, const bool& buildAll) const;
void add(const std::shared_ptr<linphone::ConferenceInfo> & conferenceInfo, const bool& sendEvents = true);
@ -50,7 +50,8 @@ public slots:
void onRemoved(bool byUser);
signals:
void filterTypeChanged(int filterType);
private:
bool mBuildAll = true; // Short term design choice : display all. As of 5.2.0 SDK and on cancel, there are no more more links between conference info and current account.
};
Q_DECLARE_METATYPE(ConferenceInfoListModel*)
#endif

View file

@ -104,6 +104,7 @@ ConferenceInfoModel::ConferenceInfoModel (QObject * parent) : QObject(parent){
connect(this, &ConferenceInfoModel::conferenceInfoChanged, this, &ConferenceInfoModel::isScheduledChanged);
connect(this, &ConferenceInfoModel::conferenceInfoChanged, this, &ConferenceInfoModel::inviteModeChanged);
connect(this, &ConferenceInfoModel::conferenceInfoChanged, this, &ConferenceInfoModel::conferenceInfoStateChanged);
connect(this, &ConferenceInfoModel::conferenceInfoChanged, this, &ConferenceInfoModel::conferenceSchedulerStateChanged);
}
// Callable from C++
@ -124,6 +125,7 @@ ConferenceInfoModel::ConferenceInfoModel (std::shared_ptr<linphone::ConferenceIn
connect(this, &ConferenceInfoModel::conferenceInfoChanged, this, &ConferenceInfoModel::isScheduledChanged);
connect(this, &ConferenceInfoModel::conferenceInfoChanged, this, &ConferenceInfoModel::inviteModeChanged);
connect(this, &ConferenceInfoModel::conferenceInfoChanged, this, &ConferenceInfoModel::conferenceInfoStateChanged);
connect(this, &ConferenceInfoModel::conferenceInfoChanged, this, &ConferenceInfoModel::conferenceSchedulerStateChanged);
}
ConferenceInfoModel::~ConferenceInfoModel () {
@ -139,14 +141,15 @@ std::shared_ptr<linphone::ConferenceInfo> ConferenceInfoModel::findConferenceInf
//------------------------------------------------------------------------------------------------
QDateTime ConferenceInfoModel::getDateTimeUtc() const{
return QDateTime::fromMSecsSinceEpoch(mConferenceInfo->getDateTime() * 1000).toUTC();
//Note conferenceInfo->getDateTime uses system timezone and fromMSecsSinceEpoch need a UTC
QDateTime ConferenceInfoModel::getDateTimeSystem() const{
QDateTime reference(QDateTime::fromMSecsSinceEpoch(mConferenceInfo->getDateTime() * 1000));// Get a reference for timezone offset computing
qint64 utcMs = (mConferenceInfo->getDateTime() - QTimeZone::systemTimeZone().offsetFromUtc(reference)) * 1000;// Remove system timezone offset to get UTC
return QDateTime::fromMSecsSinceEpoch(utcMs, QTimeZone::systemTimeZone()); // Return a System Timezone datetime based
}
QDateTime ConferenceInfoModel::getDateTimeSystem() const{
QDateTime utc = getDateTimeUtc();
return utc.addSecs(QTimeZone::systemTimeZone().offsetFromUtc(utc));
QDateTime ConferenceInfoModel::getDateTimeUtc() const{
return getDateTimeSystem().toUTC();
}
int ConferenceInfoModel::getDuration() const{
@ -204,11 +207,29 @@ QVariantList ConferenceInfoModel::getParticipants() const{
}
return addresses;
}
QVariantList ConferenceInfoModel::getAllParticipants() const{
QVariantList addresses = getParticipants();
QString organizerAddress = QString::fromStdString(mConferenceInfo->getOrganizer()->asStringUriOnly());
for(auto item : addresses){
if( item.toMap()["address"] == organizerAddress)
return addresses;
}
QVariantMap participant;
participant["displayName"] = Utils::getDisplayName(mConferenceInfo->getOrganizer());
participant["address"] = organizerAddress;
addresses << participant;
return addresses;
}
int ConferenceInfoModel::getParticipantCount()const{
return mConferenceInfo->getParticipants().size();
}
int ConferenceInfoModel::getAllParticipantCount()const{
return getAllParticipants().size();
}
TimeZoneModel* ConferenceInfoModel::getTimeZoneModel() const{
TimeZoneModel * model = new TimeZoneModel(mTimeZone);
App::getInstance()->getEngine()->setObjectOwnership(model, QQmlEngine::JavaScriptOwnership);
@ -223,12 +244,18 @@ LinphoneEnums::ConferenceInfoState ConferenceInfoModel::getConferenceInfoState()
return LinphoneEnums::fromLinphone(mConferenceInfo->getState());
}
LinphoneEnums::ConferenceSchedulerState ConferenceInfoModel::getConferenceSchedulerState() const{
return LinphoneEnums::fromLinphone(mLastConferenceSchedulerState);
}
//------------------------------------------------------------------------------------------------
// Convert into UTC with TimeZone and pass system timezone to conference info
// Datetime is in Custom (Locale/UTC/System). Convert into system timezone for conference info
void ConferenceInfoModel::setDateTime(const QDateTime& dateTime){
QDateTime utc = dateTime.addSecs( -mTimeZone.offsetFromUtc(dateTime));
QDateTime system = utc.addSecs(QTimeZone::systemTimeZone().offsetFromUtc(utc));
mConferenceInfo->setDateTime(system.toMSecsSinceEpoch() / 1000);
QDateTime system = dateTime.toTimeZone(QTimeZone::systemTimeZone());//System
int offset = QTimeZone::systemTimeZone().offsetFromUtc(system);//Get UTC offset in system coordinate
system = system.addSecs( offset - mTimeZone.offsetFromUtc(dateTime));// Delta on offsets
mConferenceInfo->setDateTime(system.toMSecsSinceEpoch() / 1000 + offset);// toMSecsSinceEpoch() is UTC, add system reference.
emit dateTimeChanged();
}
@ -254,6 +281,7 @@ void ConferenceInfoModel::setDescription(const QString& description){
void ConferenceInfoModel::setParticipants(ParticipantListModel * participants){
mConferenceInfo->setParticipants(participants->getParticipants());
emit participantsChanged();
}
void ConferenceInfoModel::setTimeZoneModel(TimeZoneModel * model){
@ -314,23 +342,22 @@ void ConferenceInfoModel::createConference(const int& securityLevel) {
CoreManager::getInstance()->getTimelineListModel()->mAutoSelectAfterCreation = false;
shared_ptr<linphone::Core> core = CoreManager::getInstance()->getCore();
static std::shared_ptr<linphone::Conference> conference;
qInfo() << "Conference creation of " << getSubject() << " at " << securityLevel << " security, organized by " << getOrganizer();
qInfo() << "Conference creation of " << getSubject() << " at " << securityLevel << " security, organized by " << getOrganizer() << " for " << getDateTimeSystem().toString();
qInfo() << "Participants:";
for(auto p : mConferenceInfo->getParticipants())
qInfo() << "\t" << p->asString().c_str();
mConferenceScheduler = ConferenceScheduler::create();
mConferenceScheduler->mSendInvite = mInviteMode;
connect(mConferenceScheduler.get(), &ConferenceScheduler::invitationsSent, this, &ConferenceInfoModel::onInvitationsSent);
connect(mConferenceScheduler.get(), &ConferenceScheduler::stateChanged, this, &ConferenceInfoModel::onStateChanged);
connect(mConferenceScheduler.get(), &ConferenceScheduler::stateChanged, this, &ConferenceInfoModel::onConferenceSchedulerStateChanged);
mConferenceScheduler->getConferenceScheduler()->setInfo(mConferenceInfo);
}
void ConferenceInfoModel::cancelConference(){
mConferenceScheduler = ConferenceScheduler::create();
connect(mConferenceScheduler.get(), &ConferenceScheduler::invitationsSent, this, &ConferenceInfoModel::onInvitationsSent);
connect(mConferenceScheduler.get(), &ConferenceScheduler::stateChanged, this, &ConferenceInfoModel::onStateChanged);
connect(mConferenceScheduler.get(), &ConferenceScheduler::stateChanged, this, &ConferenceInfoModel::onConferenceSchedulerStateChanged);
mConferenceScheduler->getConferenceScheduler()->cancelConference(mConferenceInfo);
}
@ -343,12 +370,14 @@ void ConferenceInfoModel::deleteConferenceInfo(){
//-------------------------------------------------------------------------------------------------
void ConferenceInfoModel::onStateChanged(linphone::ConferenceScheduler::State state){
qDebug() << "ConferenceInfoModel::onStateChanged: " << (int) state;
void ConferenceInfoModel::onConferenceSchedulerStateChanged(linphone::ConferenceScheduler::State state){
qDebug() << "ConferenceInfoModel::onConferenceSchedulerStateChanged: " << (int) state;
mLastConferenceSchedulerState = state;
if( state == linphone::ConferenceScheduler::State::Ready)
emit conferenceCreated();
else if( state == linphone::ConferenceScheduler::State::Error)
emit conferenceCreationFailed();
emit conferenceInfoChanged();
}
void ConferenceInfoModel::onInvitationsSent(const std::list<std::shared_ptr<linphone::Address>> & failedInvitations) {
qDebug() << "ConferenceInfoModel::onInvitationsSent";

View file

@ -49,7 +49,10 @@ public:
Q_PROPERTY(QString uri READ getUri NOTIFY uriChanged)
Q_PROPERTY(bool isScheduled READ isScheduled WRITE setIsScheduled NOTIFY isScheduledChanged)
Q_PROPERTY(int inviteMode READ getInviteMode WRITE setInviteMode NOTIFY inviteModeChanged)
Q_PROPERTY(int participantCount READ getParticipantCount NOTIFY participantsChanged)
Q_PROPERTY(int allParticipantCount READ getAllParticipantCount NOTIFY participantsChanged)
Q_PROPERTY(LinphoneEnums::ConferenceInfoState state READ getConferenceInfoState NOTIFY conferenceInfoStateChanged)
Q_PROPERTY(LinphoneEnums::ConferenceSchedulerState conferenceSchedulerState READ getConferenceSchedulerState NOTIFY conferenceSchedulerStateChanged)
static QSharedPointer<ConferenceInfoModel> create(std::shared_ptr<linphone::ConferenceInfo> conferenceInfo);
ConferenceInfoModel (QObject * parent = nullptr);
@ -72,10 +75,13 @@ public:
bool isScheduled() const;
int getInviteMode() const;
Q_INVOKABLE QVariantList getParticipants() const;
Q_INVOKABLE QVariantList getAllParticipants() const;
Q_INVOKABLE int getParticipantCount()const;
Q_INVOKABLE int getAllParticipantCount()const;
Q_INVOKABLE TimeZoneModel* getTimeZoneModel() const;
Q_INVOKABLE QString getIcalendarString() const;
LinphoneEnums::ConferenceInfoState getConferenceInfoState() const;
LinphoneEnums::ConferenceSchedulerState getConferenceSchedulerState() const;
void setDateTime(const QDateTime& dateTime);
void setDuration(const int& duration);
@ -97,10 +103,9 @@ public:
// SCHEDULER
virtual void onStateChanged(linphone::ConferenceScheduler::State state);
virtual void onConferenceSchedulerStateChanged(linphone::ConferenceScheduler::State state);
virtual void onInvitationsSent(const std::list<std::shared_ptr<linphone::Address>> & failedInvitations);
signals:
void timeZoneModelChanged();
void dateTimeChanged();
@ -113,6 +118,7 @@ signals:
void isScheduledChanged();
void inviteModeChanged();
void conferenceInfoStateChanged();
void conferenceSchedulerStateChanged();
void conferenceCreated();
void conferenceCreationFailed();
@ -128,6 +134,7 @@ private:
bool mIsScheduled = true;
int mInviteMode = 0;
bool mRemoveRequested = false;// true if user has request its deletion from DB
linphone::ConferenceScheduler::State mLastConferenceSchedulerState = linphone::ConferenceScheduler::State::Idle;// Workaround for missing getter in scheduler.
};
Q_DECLARE_METATYPE(QSharedPointer<ConferenceInfoModel>)

View file

@ -54,6 +54,8 @@ bool ConferenceInfoProxyModel::filterAcceptsRow (int sourceRow, const QModelInde
QModelIndex index = listModel->index(sourceRow, 0, QModelIndex());
const ConferenceInfoModel* ics = sourceModel()->data(index).value<ConferenceInfoModel*>();
if(ics){
if(ics->getDuration() == 0)
return false;
QDateTime currentDateTime = QDateTime::currentDateTime();
if( mFilterType == 0){
return ics->getEndDateTime() < currentDateTime;
@ -65,7 +67,7 @@ bool ConferenceInfoProxyModel::filterAcceptsRow (int sourceRow, const QModelInde
return mFilterType == -1;
}
}
return true;
return false;
}
bool ConferenceInfoProxyModel::lessThan (const QModelIndex &left, const QModelIndex &right) const {

View file

@ -215,3 +215,7 @@ Presence::PresenceLevel ContactModel::getPresenceLevel () const {
bool ContactModel::hasCapability(const LinphoneEnums::FriendCapability& capability){
return mLinphoneFriend->hasCapability(LinphoneEnums::toLinphone(capability));
}
std::shared_ptr<linphone::Friend> ContactModel::getFriend() const{
return mLinphoneFriend;
}

View file

@ -57,6 +57,8 @@ public:
Q_INVOKABLE VcardModel *cloneVcardModel () const;
Presence::PresenceLevel getPresenceLevel () const;
Q_INVOKABLE bool hasCapability(const LinphoneEnums::FriendCapability& capability);
std::shared_ptr<linphone::Friend> getFriend() const;
signals:
void contactUpdated ();

View file

@ -460,9 +460,12 @@ QVariantList VcardModel::getUrls () const {
return list;
}
bool VcardModel::addUrl (const QString &url) {
bool VcardModel::addUrl (QString url) {
CHECK_VCARD_IS_WRITABLE(this);
QUrl urlParser(url);
if( urlParser.scheme() == ""){
url = "https://"+url;
}
shared_ptr<belcard::BelCard> belcard = mVcard->getVcard();
if (findBelCardValue(belcard->getURLs(), url))
return false;

View file

@ -88,7 +88,7 @@ public:
Q_INVOKABLE void removeEmail (const QString &email);
Q_INVOKABLE bool updateEmail (const QString &oldEmail, const QString &email);
Q_INVOKABLE bool addUrl (const QString &url);
Q_INVOKABLE bool addUrl (QString url);
Q_INVOKABLE void removeUrl (const QString &url);
Q_INVOKABLE bool updateUrl (const QString &oldUrl, const QString &url);

View file

@ -24,40 +24,26 @@
#include "components/contact/ContactModel.hpp"
#include "components/contact/VcardModel.hpp"
#include "components/core/CoreManager.hpp"
#include "components/friend/FriendListListener.hpp"
#include "ContactsListModel.hpp"
// =============================================================================
void ContactsListModel::connectTo(FriendListListener * listener){
connect(listener, &FriendListListener::contactCreated, this, &ContactsListModel::onContactCreated);
connect(listener, &FriendListListener::contactDeleted, this, &ContactsListModel::onContactDeleted);
connect(listener, &FriendListListener::contactUpdated, this, &ContactsListModel::onContactUpdated);
connect(listener, &FriendListListener::syncStatusChanged, this, &ContactsListModel::onSyncStatusChanged);
connect(listener, &FriendListListener::presenceReceived, this, &ContactsListModel::onPresenceReceived);
}
// =============================================================================
using namespace std;
ContactsListModel::ContactsListModel (QObject *parent) : ProxyListModel(parent) {
mLinphoneFriends = CoreManager::getInstance()->getCore()->getFriendsLists().front();
// Clean friends.
{
list<shared_ptr<linphone::Friend>> toRemove;
for (const auto &linphoneFriend : mLinphoneFriends->getFriends()) {
if (!linphoneFriend->getVcard())
toRemove.push_back(linphoneFriend);
}
for (const auto &linphoneFriend : toRemove) {
qWarning() << QStringLiteral("Remove one friend without vcard.");
mLinphoneFriends->removeFriend(linphoneFriend);
}
}
// Init contacts with linphone friends list.
QQmlEngine *engine = App::getInstance()->getEngine();
for (const auto &linphoneFriend : mLinphoneFriends->getFriends()) {
auto contact = QSharedPointer<ContactModel>::create(linphoneFriend);
// See: http://doc.qt.io/qt-5/qtqml-cppintegration-data.html#data-ownership
// The returned value must have a explicit parent or a QQmlEngine::CppOwnership.
engine->setObjectOwnership(contact.get(), QQmlEngine::CppOwnership);
addContact(contact);
}
mFriendListListener = std::make_shared<FriendListListener>();
connectTo(mFriendListListener.get());
update();
}
ContactsListModel::~ContactsListModel(){
@ -65,7 +51,7 @@ ContactsListModel::~ContactsListModel(){
beginResetModel();
mOptimizedSearch.clear();
mList.clear();
mLinphoneFriends = nullptr;
mLinphoneFriends.clear();
endResetModel();
}
}
@ -75,7 +61,9 @@ bool ContactsListModel::removeRows (int row, int count, const QModelIndex &paren
if (row < 0 || count < 0 || limit >= mList.count())
return false;
auto friendsList = CoreManager::getInstance()->getCore()->getFriendsLists();
beginRemoveRows(parent, row, limit);
for (int i = 0; i < count; ++i) {
@ -84,7 +72,8 @@ bool ContactsListModel::removeRows (int row, int count, const QModelIndex &paren
mOptimizedSearch.remove(address.toString());
}
mLinphoneFriends->removeFriend(contact->mLinphoneFriend);
for(auto l : friendsList)
l->removeFriend(contact->mLinphoneFriend);
emit contactRemoved(contact);
}
@ -111,6 +100,10 @@ QSharedPointer<ContactModel> ContactsListModel::findContactModelFromUsername (co
}
// -----------------------------------------------------------------------------
ContactModel *ContactsListModel::getContactModelFromAddress (const QString& address) const{
auto contact = findContactModelFromSipAddress(address);
return contact.get();
}
ContactModel *ContactsListModel::addContact (VcardModel *vcardModel) {
// Try to merge vcardModel to an existing contact.
@ -123,7 +116,16 @@ ContactModel *ContactsListModel::addContact (VcardModel *vcardModel) {
contact = QSharedPointer<ContactModel>::create(vcardModel);
App::getInstance()->getEngine()->setObjectOwnership(contact.get(), QQmlEngine::CppOwnership);
if (mLinphoneFriends->addFriend(contact->mLinphoneFriend) != linphone::FriendList::Status::OK) {
if( mLinphoneFriends.size() == 0){
update();// Friends were not loaded correctly. Update them.
}
auto friendsList = CoreManager::getInstance()->getCore()->getDefaultFriendList();
if( !friendsList){
qWarning() << "There is no friends list available, cannot add a contact" ;
return nullptr;
}
if (friendsList->addFriend(contact->mLinphoneFriend) != linphone::FriendList::Status::OK) {
qWarning() << QStringLiteral("Unable to add contact from vcard:") << vcardModel;
return nullptr;
}
@ -131,9 +133,8 @@ ContactModel *ContactsListModel::addContact (VcardModel *vcardModel) {
qInfo() << QStringLiteral("Add contact from vcard:") << contact.get() << vcardModel;
// Make sure new subscribe is issued.
mLinphoneFriends->updateSubscriptions();
friendsList->updateSubscriptions();
addContact(contact);
emit layoutChanged();
emit contactAdded(contact);
@ -177,3 +178,47 @@ void ContactsListModel::addContact (QSharedPointer<ContactModel> contact) {
mOptimizedSearch[address.toString()] = contact;
}
}
void ContactsListModel::update(){
beginResetModel();
for(auto l : mLinphoneFriends)
l->removeListener(mFriendListListener);
mLinphoneFriends.clear();
mOptimizedSearch.clear();
mList.clear();
endResetModel();
mLinphoneFriends = CoreManager::getInstance()->getCore()->getFriendsLists();
for(auto l : mLinphoneFriends){
l->addListener(mFriendListListener);
for (const auto &linphoneFriend : l->getFriends()) {
onContactCreated(linphoneFriend);
}
}
}
//------------------------------------------------------------------------------------------------
void ContactsListModel::onContactCreated(const std::shared_ptr<linphone::Friend> & linphoneFriend){
QQmlEngine *engine = App::getInstance()->getEngine();
auto haveContact = std::find_if(mList.begin(), mList.end(), [linphoneFriend] (const QSharedPointer<QObject>& item){
return item.objectCast<ContactModel>()->getFriend() == linphoneFriend;
});
if(haveContact == mList.end()) {
auto contact = QSharedPointer<ContactModel>::create(linphoneFriend);
// See: http://doc.qt.io/qt-5/qtqml-cppintegration-data.html#data-ownership
// The returned value must have a explicit parent or a QQmlEngine::CppOwnership.
engine->setObjectOwnership(contact.get(), QQmlEngine::CppOwnership);
addContact(contact);
}
}
void ContactsListModel::onContactDeleted(const std::shared_ptr<linphone::Friend> & linphoneFriend){
}
void ContactsListModel::onContactUpdated(const std::shared_ptr<linphone::Friend> & newFriend, const std::shared_ptr<linphone::Friend> & oldFriend){
}
void ContactsListModel::onSyncStatusChanged(linphone::FriendList::SyncStatus status, const std::string & message){
}
void ContactsListModel::onPresenceReceived(const std::list<std::shared_ptr<linphone::Friend>> & friends){
}

View file

@ -33,6 +33,7 @@ namespace linphone {
class ContactModel;
class VcardModel;
class FriendListListener;
class ContactsListModel : public ProxyListModel {
friend class SipAddressesModel;
@ -48,10 +49,21 @@ public:
QSharedPointer<ContactModel> findContactModelFromSipAddress (const QString &sipAddress) const;
QSharedPointer<ContactModel> findContactModelFromUsername (const QString &username) const;
Q_INVOKABLE ContactModel *getContactModelFromAddress (const QString& address) const;
Q_INVOKABLE ContactModel *addContact (VcardModel *vcardModel);
Q_INVOKABLE void removeContact (ContactModel *contact);
Q_INVOKABLE void cleanAvatars ();
Q_INVOKABLE void update ();
void connectTo(FriendListListener * listener);
public slots:
void onContactCreated(const std::shared_ptr<linphone::Friend> & linphoneFriend);
void onContactDeleted(const std::shared_ptr<linphone::Friend> & linphoneFriend);
void onContactUpdated(const std::shared_ptr<linphone::Friend> & newFriend, const std::shared_ptr<linphone::Friend> & oldFriend);
void onSyncStatusChanged(linphone::FriendList::SyncStatus status, const std::string & message);
void onPresenceReceived(const std::list<std::shared_ptr<linphone::Friend>> & friends);
signals:
void contactAdded (QSharedPointer<ContactModel>);
@ -65,7 +77,8 @@ private:
void addContact (QSharedPointer<ContactModel> contact);
QMap<QString, QSharedPointer<ContactModel>> mOptimizedSearch;
std::shared_ptr<linphone::FriendList> mLinphoneFriends;
std::list<std::shared_ptr<linphone::FriendList>> mLinphoneFriends;
std::shared_ptr<FriendListListener> mFriendListListener;
};
#endif // CONTACTS_LIST_MODEL_H_

View file

@ -260,6 +260,7 @@ void CoreManager::createLinphoneCore (const QString &configPath) {
Paths::getFactoryConfigFilePath(),
nullptr
);
setDatabasesPaths();
// Enable LIME on your core to use encryption.
mCore->enableLimeX3Dh(mCore->limeX3DhAvailable());
// Now see the CoreService.CreateGroupChatRoom to see how to create a secure chat room
@ -279,12 +280,13 @@ void CoreManager::createLinphoneCore (const QString &configPath) {
QString userAgent = Utils::computeUserAgent(config);
mCore->setUserAgent(Utils::appStringToCoreString(userAgent), mCore->getVersion());
mCore->start();
setDatabasesPaths();
setOtherPaths();
mCore->enableFriendListSubscription(true);
mCore->enableRecordAware(true);
if(mCore->getAccountCreatorUrl() == "")
mCore->setAccountCreatorUrl(Constants::DefaultFlexiAPIURL);
if( mCore->getAccountList().size() == 0)
mCore->setLogCollectionUploadServerUrl(Constants::DefaultUploadLogsServer);
}
void CoreManager::updateUserAgent(){

View file

@ -0,0 +1,51 @@
/*
* Copyright (c) 2022 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 "FriendListListener.hpp"
#include "../../utils/Utils.hpp"
#include <QDebug>
// =============================================================================
FriendListListener::FriendListListener(QObject *parent) : QObject(parent) {
}
//--------------------------------------------------------------------
void FriendListListener::onContactCreated(const std::shared_ptr<linphone::FriendList> & friendList, const std::shared_ptr<linphone::Friend> & linphoneFriend) {
qDebug() << "onContactCreated: " << Utils::coreStringToAppString(linphoneFriend->getName());
emit contactCreated(linphoneFriend);
}
void FriendListListener::onContactDeleted(const std::shared_ptr<linphone::FriendList> & friendList, const std::shared_ptr<linphone::Friend> & linphoneFriend) {
qDebug() << "onContactDeleted: " << Utils::coreStringToAppString(linphoneFriend->getName());
emit contactDeleted(linphoneFriend);
}
void FriendListListener::onContactUpdated(const std::shared_ptr<linphone::FriendList> & friendList, const std::shared_ptr<linphone::Friend> & newFriend, const std::shared_ptr<linphone::Friend> & oldFriend) {
qDebug() << "onContactUpdated: " << Utils::coreStringToAppString(newFriend->getName());
emit contactUpdated(newFriend, oldFriend);
}
void FriendListListener::onSyncStatusChanged(const std::shared_ptr<linphone::FriendList> & friendList, linphone::FriendList::SyncStatus status, const std::string & message) {
qDebug() << "onSyncStatusChanged: [" << (int)status<<"] " << Utils::coreStringToAppString(message);
emit syncStatusChanged(status, message);
}
void FriendListListener::onPresenceReceived(const std::shared_ptr<linphone::FriendList> & friendList, const std::list<std::shared_ptr<linphone::Friend>> & friends) {
qDebug() << "onPresenceReceived: " <<friends.size();
emit presenceReceived(friends);
}

View file

@ -0,0 +1,50 @@
/*
* Copyright (c) 2022 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 FRIEND_LIST_LISTENER_H_
#define FRIEND_LIST_LISTENER_H_
#include <linphone++/linphone.hh>
#include <QObject>
// =============================================================================
class FriendListListener : public QObject, public linphone::FriendListListener {
Q_OBJECT
public:
FriendListListener (QObject *parent = nullptr);
virtual void onContactCreated(const std::shared_ptr<linphone::FriendList> & friendList, const std::shared_ptr<linphone::Friend> & linphoneFriend) override;
virtual void onContactDeleted(const std::shared_ptr<linphone::FriendList> & friendList, const std::shared_ptr<linphone::Friend> & linphoneFriend) override;
virtual void onContactUpdated(const std::shared_ptr<linphone::FriendList> & friendList, const std::shared_ptr<linphone::Friend> & newFriend, const std::shared_ptr<linphone::Friend> & oldFriend) override;
virtual void onSyncStatusChanged(const std::shared_ptr<linphone::FriendList> & friendList, linphone::FriendList::SyncStatus status, const std::string & message) override;
virtual void onPresenceReceived(const std::shared_ptr<linphone::FriendList> & friendList, const std::list<std::shared_ptr<linphone::Friend>> & friends) override;
signals:
void contactCreated(const std::shared_ptr<linphone::Friend> & linphoneFriend);
void contactDeleted(const std::shared_ptr<linphone::Friend> & linphoneFriend);
void contactUpdated(const std::shared_ptr<linphone::Friend> & newFriend, const std::shared_ptr<linphone::Friend> & oldFriend);
void syncStatusChanged(linphone::FriendList::SyncStatus status, const std::string & message);
void presenceReceived(const std::list<std::shared_ptr<linphone::Friend>> & friends);
};
#endif

View file

@ -27,6 +27,7 @@
// -----------------------------------------------------------------------------
TimeZoneProxyModel::TimeZoneProxyModel (QObject *parent) : SortFilterProxyModel(parent) {
mDeleteSourceModel = true;
setSourceModel(new TimeZoneListModel(parent));
sort(0);
}

View file

@ -58,6 +58,7 @@ void ParticipantDeviceListModel::initConferenceModel(){
updateDevices(conferenceModel->getConference()->getParticipantDeviceList(), false);
qDebug() << "Conference have " << mList.size() << " devices";
connect(conferenceModel.get(), &ConferenceModel::activeSpeakerParticipantDevice, this, &ParticipantDeviceListModel::onActiveSpeakerParticipantDevice);
connect(conferenceModel.get(), &ConferenceModel::participantAdded, this, &ParticipantDeviceListModel::onParticipantAdded);
connect(conferenceModel.get(), &ConferenceModel::participantRemoved, this, &ParticipantDeviceListModel::onParticipantRemoved);
connect(conferenceModel.get(), &ConferenceModel::participantDeviceAdded, this, &ParticipantDeviceListModel::onParticipantDeviceAdded);
@ -98,6 +99,8 @@ void ParticipantDeviceListModel::updateDevices(const std::list<std::shared_ptr<l
}
bool ParticipantDeviceListModel::add(std::shared_ptr<linphone::ParticipantDevice> deviceToAdd){
auto deviceToAddAddr = deviceToAdd->getAddress();
int row = 0;
qDebug() << "Adding device " << deviceToAdd->getAddress()->asString().c_str();
for(auto item : mList) {
auto deviceModel = item.objectCast<ParticipantDeviceModel>();
@ -105,7 +108,12 @@ bool ParticipantDeviceListModel::add(std::shared_ptr<linphone::ParticipantDevice
qDebug() << "Device already exist. Send video update event";
deviceModel->updateVideoEnabled();
return false;
}else if(deviceToAddAddr->equal(deviceModel->getDevice()->getAddress())){// Address is the same (same device) but the model is using another linphone object. Replace it.
deviceModel->updateVideoEnabled();
removeRow(row);
break;
}
++row;
}
bool addMe = isMe(deviceToAdd);
auto deviceModel = ParticipantDeviceModel::create(mCallModel, deviceToAdd, addMe);
@ -113,9 +121,18 @@ bool ParticipantDeviceListModel::add(std::shared_ptr<linphone::ParticipantDevice
connect(deviceModel.get(), &ParticipantDeviceModel::isSpeakingChanged, this, &ParticipantDeviceListModel::onParticipantDeviceSpeaking);
ProxyListModel::add<ParticipantDeviceModel>(deviceModel);
qDebug() << "Device added. Count=" << mList.count();
QStringList debugDevices;
for(auto i : mList){
auto item = i.objectCast<ParticipantDeviceModel>();
debugDevices.push_back( item->getAddress());
}
qDebug() << debugDevices.join("\n");
if( addMe){
qDebug() << "Added a me device";
emit meChanged();
}else if(mList.size() == 1 || (mList.size() == 2 && isMe(mList.front().objectCast<ParticipantDeviceModel>()->getDevice()))){
mActiveSpeaker = mList.back().objectCast<ParticipantDeviceModel>();
emit activeSpeakerChanged();
}
return true;
}
@ -126,7 +143,6 @@ bool ParticipantDeviceListModel::remove(std::shared_ptr<const linphone::Particip
auto device = item.objectCast<ParticipantDeviceModel>();
if( device->getDevice() == deviceToRemove){
device->updateVideoEnabled();
mActiveSpeakers.removeAll(device.get());
removeRow(row);
return true;
}else
@ -163,14 +179,8 @@ QSharedPointer<ParticipantDeviceModel> ParticipantDeviceListModel::getMe(int * i
return nullptr;
}
ParticipantDeviceModel* ParticipantDeviceListModel::getLastActiveSpeaking() const{
if( mActiveSpeakers.size() == 0){
if( mList.size() == 0)
return getMe().get();
else
return mList.back().objectCast<ParticipantDeviceModel>().get();
}else
return mActiveSpeakers.first();
ParticipantDeviceModel* ParticipantDeviceListModel::getActiveSpeakerModel() const{
return mActiveSpeaker.get();
}
bool ParticipantDeviceListModel::isMe(std::shared_ptr<linphone::ParticipantDevice> deviceToCheck)const{
@ -280,6 +290,13 @@ void ParticipantDeviceListModel::onParticipantDeviceMediaAvailabilityChanged(con
else
onParticipantDeviceAdded(participantDevice);
}
void ParticipantDeviceListModel::onActiveSpeakerParticipantDevice(const std::shared_ptr<const linphone::ParticipantDevice>& participantDevice){
auto device = get(participantDevice);
if( device){
mActiveSpeaker = device;
emit activeSpeakerChanged();
}
}
void ParticipantDeviceListModel::onParticipantDeviceIsSpeakingChanged(const std::shared_ptr<const linphone::ParticipantDevice> & participantDevice, bool isSpeaking){
auto device = get(participantDevice);
@ -288,14 +305,5 @@ void ParticipantDeviceListModel::onParticipantDeviceIsSpeakingChanged(const std:
}
void ParticipantDeviceListModel::onParticipantDeviceSpeaking(){
auto deviceModel = qobject_cast<ParticipantDeviceModel*>(sender());
bool changed = false;
// Me should not be in the list.
if( !deviceModel->isMe() && (mActiveSpeakers.size() == 0 || deviceModel->getIsSpeaking())) {// Ensure to have at least one last active speaker
changed = mActiveSpeakers.removeAll(deviceModel) > 0;
mActiveSpeakers.push_front(deviceModel);
changed = true;
}
if(changed)
emit participantSpeaking(deviceModel);
}

View file

@ -47,12 +47,13 @@ public:
bool remove(std::shared_ptr<const linphone::ParticipantDevice> deviceToAdd);
QSharedPointer<ParticipantDeviceModel> get(std::shared_ptr<const linphone::ParticipantDevice> deviceToGet, int * index = nullptr);
QSharedPointer<ParticipantDeviceModel> getMe(int * index = nullptr)const;
ParticipantDeviceModel* getLastActiveSpeaking() const;
ParticipantDeviceModel* getActiveSpeakerModel() const;
bool isMe(std::shared_ptr<linphone::ParticipantDevice> device)const;
bool isMeAlone() const;
public slots:
void onActiveSpeakerParticipantDevice(const std::shared_ptr<const linphone::ParticipantDevice>& participantDevice);
void onConferenceModelChanged ();
void onSecurityLevelChanged(std::shared_ptr<const linphone::Address> device);
void onParticipantAdded(const std::shared_ptr<const linphone::Participant> & participant);
@ -66,6 +67,7 @@ public slots:
void onParticipantDeviceSpeaking();
signals:
void activeSpeakerChanged();
void securityLevelChanged(std::shared_ptr<const linphone::Address> device);
void participantSpeaking(ParticipantDeviceModel *speakingDevice);
void conferenceCreated();
@ -73,7 +75,8 @@ signals:
private:
CallModel * mCallModel = nullptr;
QList<ParticipantDeviceModel*> mActiveSpeakers;// First item is last speaker
QSharedPointer<ParticipantDeviceModel> mActiveSpeaker;
//QList<ParticipantDeviceModel*> mActiveSpeakers;// First item is last speaker
bool mInitialized = false;
};

View file

@ -31,6 +31,10 @@
// =============================================================================
ParticipantDeviceProxyModel::ParticipantDeviceProxyModel (QObject *parent) : SortFilterProxyModel(parent){
mDeleteSourceModel = true;
}
ParticipantDeviceProxyModel::~ParticipantDeviceProxyModel(){
}
bool ParticipantDeviceProxyModel::filterAcceptsRow (
@ -57,9 +61,9 @@ ParticipantDeviceModel *ParticipantDeviceProxyModel::getAt(int row){
return sourceModel()->data(sourceIndex).value<ParticipantDeviceModel *>();
}
ParticipantDeviceModel* ParticipantDeviceProxyModel::getLastActiveSpeaking(){
ParticipantDeviceModel* ParticipantDeviceProxyModel::getActiveSpeakerModel(){
auto listModel = qobject_cast<ParticipantDeviceListModel*>(sourceModel());
return listModel ? listModel->getLastActiveSpeaking() : nullptr;
return listModel ? listModel->getActiveSpeakerModel() : nullptr;
}
CallModel * ParticipantDeviceProxyModel::getCallModel() const{
@ -74,28 +78,31 @@ ParticipantDeviceModel * ParticipantDeviceProxyModel::getMe() const{
bool ParticipantDeviceProxyModel::isShowMe() const{
return mShowMe;
}
void ParticipantDeviceProxyModel::connectTo(ParticipantDeviceListModel* model){
connect(model, &ParticipantDeviceListModel::countChanged, this, &ParticipantDeviceProxyModel::onCountChanged);
connect(model, &ParticipantDeviceListModel::participantSpeaking, this, &ParticipantDeviceProxyModel::onParticipantSpeaking);
connect(model, &ParticipantDeviceListModel::conferenceCreated, this, &ParticipantDeviceProxyModel::conferenceCreated);
connect(model, &ParticipantDeviceListModel::meChanged, this, &ParticipantDeviceProxyModel::meChanged);
connect(model, &ParticipantDeviceListModel::activeSpeakerChanged, this, &ParticipantDeviceProxyModel::activeSpeakerChanged);
}
void ParticipantDeviceProxyModel::setCallModel(CallModel * callModel){
setFilterType(1);
mCallModel = callModel;
auto sourceModel = new ParticipantDeviceListModel(mCallModel);
connect(sourceModel, &ParticipantDeviceListModel::countChanged, this, &ParticipantDeviceProxyModel::onCountChanged);
connect(sourceModel, &ParticipantDeviceListModel::participantSpeaking, this, &ParticipantDeviceProxyModel::onParticipantSpeaking);
connect(sourceModel, &ParticipantDeviceListModel::conferenceCreated, this, &ParticipantDeviceProxyModel::conferenceCreated);
connect(sourceModel, &ParticipantDeviceListModel::meChanged, this, &ParticipantDeviceProxyModel::meChanged);
setSourceModel(sourceModel);
deleteSourceModel();
auto newSourceModel = new ParticipantDeviceListModel(mCallModel);
connectTo(newSourceModel);
setSourceModel(newSourceModel);
emit countChanged();
emit meChanged();
}
void ParticipantDeviceProxyModel::setParticipant(ParticipantModel * participant){
setFilterType(0);
auto sourceModel = participant->getParticipantDevices().get();
connect(sourceModel, &ParticipantDeviceListModel::countChanged, this, &ParticipantDeviceProxyModel::countChanged);
connect(sourceModel, &ParticipantDeviceListModel::participantSpeaking, this, &ParticipantDeviceProxyModel::onParticipantSpeaking);
connect(sourceModel, &ParticipantDeviceListModel::conferenceCreated, this, &ParticipantDeviceProxyModel::conferenceCreated);
connect(sourceModel, &ParticipantDeviceListModel::meChanged, this, &ParticipantDeviceProxyModel::meChanged);
setSourceModel(sourceModel);
deleteSourceModel();
auto newSourceModel = participant->getParticipantDevices().get();
connectTo(newSourceModel);
setSourceModel(newSourceModel);
emit countChanged();
emit meChanged();
}

View file

@ -42,12 +42,15 @@ public:
Q_PROPERTY(CallModel * callModel READ getCallModel WRITE setCallModel NOTIFY callModelChanged)
Q_PROPERTY(bool showMe READ isShowMe WRITE setShowMe NOTIFY showMeChanged)
Q_PROPERTY(ParticipantDeviceModel * me READ getMe NOTIFY meChanged)
Q_PROPERTY(ParticipantDeviceModel* activeSpeaker READ getActiveSpeakerModel NOTIFY activeSpeakerChanged)
ParticipantDeviceProxyModel (QObject *parent = nullptr);
~ParticipantDeviceProxyModel();
Q_INVOKABLE ParticipantDeviceModel* getAt(int row);
Q_INVOKABLE ParticipantDeviceModel* getLastActiveSpeaking();
ParticipantDeviceModel * getMe() const;
ParticipantDeviceModel* getActiveSpeakerModel();
ParticipantDeviceModel* getMe() const;
CallModel * getCallModel() const;
bool isShowMe() const;
@ -56,11 +59,14 @@ public:
void setParticipant(ParticipantModel * participant);
void setShowMe(const bool& show);
void connectTo(ParticipantDeviceListModel* model);
public slots:
void onCountChanged();
void onParticipantSpeaking(ParticipantDeviceModel * speakingDevice);
signals:
void activeSpeakerChanged();
void callModelChanged();
void showMeChanged();
void meChanged();
@ -71,7 +77,6 @@ protected:
virtual bool filterAcceptsRow (int sourceRow, const QModelIndex &sourceParent) const override;
virtual bool lessThan (const QModelIndex &left, const QModelIndex &right) const override;
QSharedPointer<ParticipantDeviceListModel> mDevices;
CallModel * mCallModel;
bool mShowMe = true;
};

View file

@ -92,8 +92,11 @@ void ParticipantProxyModel::setChatRoomModel(ChatRoomModel * chatRoomModel){
connect(participants, &ParticipantListModel::countChanged, this, &ParticipantProxyModel::countChanged);
setSourceModel(participants);
emit participantListModelChanged();
for(int i = 0 ; i < participants->getCount() ; ++i)
emit addressAdded(participants->getAt<ParticipantModel>(i)->getSipAddress());
for(int i = 0 ; i < participants->getCount() ; ++i) {
auto participant = participants->getAt<ParticipantModel>(i);
connect(participant.get(), &ParticipantModel::invitationTimeout, this, &ParticipantProxyModel::removeModel);
emit addressAdded(participant->getSipAddress());
}
}else if(!sourceModel()){
auto model = new ParticipantListModel((ChatRoomModel*)nullptr, this);
connect(model, &ParticipantListModel::countChanged, this, &ParticipantProxyModel::countChanged);
@ -113,8 +116,11 @@ void ParticipantProxyModel::setConferenceModel(ConferenceModel * conferenceModel
connect(participants, &ParticipantListModel::countChanged, this, &ParticipantProxyModel::countChanged);
setSourceModel(participants);
emit participantListModelChanged();
for(int i = 0 ; i < participants->getCount() ; ++i)
emit addressAdded(participants->getAt<ParticipantModel>(i)->getSipAddress());
for(int i = 0 ; i < participants->getCount() ; ++i) {
auto participant = participants->getAt<ParticipantModel>(i);
connect(participant.get(), &ParticipantModel::invitationTimeout, this, &ParticipantProxyModel::removeModel);
emit addressAdded(participant->getSipAddress());
}
}else if(!sourceModel()){
auto model = new ParticipantListModel((ConferenceModel*)nullptr, this);
connect(model, &ParticipantListModel::countChanged, this, &ParticipantProxyModel::countChanged);
@ -144,12 +150,12 @@ void ParticipantProxyModel::addAddress(const QString& address){
ParticipantListModel * participantsModel = qobject_cast<ParticipantListModel*>(sourceModel());
if(!participantsModel->contains(address)){
QSharedPointer<ParticipantModel> participant = QSharedPointer<ParticipantModel>::create(nullptr);
connect(participant.get(), &ParticipantModel::invitationTimeout, this, &ParticipantProxyModel::removeModel);
participant->setSipAddress(address);
participantsModel->add(participant);
if(mChatRoomModel && mChatRoomModel->getChatRoom()){// Invite and wait for its creation
mChatRoomModel->getChatRoom()->addParticipant(Utils::interpretUrl(address));
connect(participant.get(), &ParticipantModel::invitationTimeout, this, &ParticipantProxyModel::removeModel);
participant->startInvitation();
mChatRoomModel->getChatRoom()->addParticipant(Utils::interpretUrl(address));
}
if( mConferenceModel && mConferenceModel->getConference()){
auto addressToInvite = Utils::interpretUrl(address);
@ -158,23 +164,18 @@ void ParticipantProxyModel::addAddress(const QString& address){
auto haveCall = std::find_if(currentCalls.begin(), currentCalls.end(), [addressToInvite](const std::shared_ptr<linphone::Call>& call){
return call->getRemoteAddress()->weakEqual(addressToInvite);
});
participant->startInvitation();
if( haveCall == currentCalls.end())
mConferenceModel->getConference()->addParticipant(addressToInvite);
else{
runningCallsToAdd.push_back(*haveCall);
mConferenceModel->getConference()->addParticipants(runningCallsToAdd);
}
/*
std::list<std::shared_ptr<linphone::Address>> addressesToInvite;
addressesToInvite.push_back(addressToInvite);
auto callParameters = CoreManager::getInstance()->getCore()->createCallParams(mConferenceModel->getConference()->getCall());
mConferenceModel->getConference()->inviteParticipants(addressesToInvite, callParameters);*/
connect(participant.get(), &ParticipantModel::invitationTimeout, this, &ParticipantProxyModel::removeModel);
participant->startInvitation();
mConferenceModel->getConference()->inviteParticipants(addressesToInvite, callParameters);*/
}
emit countChanged();
emit addressAdded(address);

View file

@ -507,7 +507,11 @@ QString AccountSettingsModel::getPrimarySipAddress () const {
}
QString AccountSettingsModel::getDefaultAccountDomain() const{
return Utils::coreStringToAppString(CoreManager::getInstance()->getCore()->getDefaultAccount()->getParams()->getDomain());
auto account = CoreManager::getInstance()->getCore()->getDefaultAccount();
if(account)
return Utils::coreStringToAppString(account->getParams()->getDomain());
else
return "";
}
// -----------------------------------------------------------------------------

View file

@ -69,7 +69,6 @@ SettingsModel::SettingsModel (QObject *parent) : QObject(parent) {
connect(coreManager->getAccountSettingsModel(), &AccountSettingsModel::accountSettingsUpdated, this, &SettingsModel::videoConferenceEnabledChanged);
connect(coreManager->getAccountSettingsModel(), &AccountSettingsModel::accountSettingsUpdated, this, &SettingsModel::secureChatEnabledChanged);
configureRlsUri();
}
@ -548,12 +547,9 @@ static inline QVariantMap createMapFromVideoDefinition (const shared_ptr<const l
QVariantMap map;
if (!definition) {
Q_ASSERT(!CoreManager::getInstance()->getCore()->videoSupported());
map["name"] = QStringLiteral("Bad EGG");
map["width"] = QStringLiteral("?????");
map["height"] = QStringLiteral("?????");
return map;
}
@ -987,9 +983,6 @@ void SettingsModel::setMediaEncryption (MediaEncryption encryption) {
if (encryption == getMediaEncryption())
return;
if (encryption != SettingsModel::MediaEncryptionZrtp)
setLimeState(false);
CoreManager::getInstance()->getCore()->setMediaEncryption(
static_cast<linphone::MediaEncryption>(encryption)
);
@ -1032,10 +1025,7 @@ void SettingsModel::setLimeState (const bool& state) {
if (state == getLimeState())
return;
if (state)
setMediaEncryption(SettingsModel::MediaEncryptionZrtp);
CoreManager::getInstance()->getCore()->enableLimeX3Dh(!state);
CoreManager::getInstance()->getCore()->enableLimeX3Dh(state);
emit limeStateChanged(state);
}

View file

@ -455,8 +455,10 @@ void SipAddressesModel::handleMessageCountReset (ChatRoomModel *chatRoomModel) {
}
void SipAddressesModel::handleMessageSent (const shared_ptr<linphone::ChatMessage> &message) {
const QString peerAddress(Utils::coreStringToAppString(message->getChatRoom()->getPeerAddress()->asStringUriOnly()));
addOrUpdateSipAddress(peerAddress, message);
if(message->getChatRoom() && message->getChatRoom()->getPeerAddress()){
const QString peerAddress(Utils::coreStringToAppString(message->getChatRoom()->getPeerAddress()->asStringUriOnly()));
addOrUpdateSipAddress(peerAddress, message);
}
}
void SipAddressesModel::handleIsComposingChanged (const shared_ptr<linphone::ChatRoom> &chatRoom) {

View file

@ -71,7 +71,6 @@ TimelineListModel::TimelineListModel(const TimelineListModel* model){
auto newItem = qobject_cast<TimelineModel*>(item)->clone();
connect(newItem.get(), SIGNAL(selectedChanged(bool)), this, SLOT(onSelectedHasChanged(bool)));
connect(newItem.get(), &TimelineModel::chatRoomDeleted, this, &TimelineListModel::onChatRoomDeleted);
connect(newItem->getChatRoomModel(), &ChatRoomModel::allEntriesRemoved, this, &TimelineListModel::removeChatRoomModel);
mList << newItem;
}
}
@ -141,7 +140,6 @@ QSharedPointer<TimelineModel> TimelineListModel::getTimeline(std::shared_ptr<lin
QSharedPointer<TimelineModel> model = TimelineModel::create(this, chatRoom);
if(model){
connect(model.get(), SIGNAL(selectedChanged(bool)), this, SLOT(onSelectedHasChanged(bool)));
connect(model->getChatRoomModel(), &ChatRoomModel::allEntriesRemoved, this, &TimelineListModel::removeChatRoomModel);
add(model);
return model;
}
@ -189,7 +187,6 @@ QSharedPointer<ChatRoomModel> TimelineListModel::getChatRoomModel(std::shared_pt
QSharedPointer<TimelineModel> model = TimelineModel::create(this, chatRoom);
if(model){
connect(model.get(), SIGNAL(selectedChanged(bool)), this, SLOT(onSelectedHasChanged(bool)));
connect(model->getChatRoomModel(), &ChatRoomModel::allEntriesRemoved, this, &TimelineListModel::removeChatRoomModel);
add(model);
return model->mChatRoomModel;
}
@ -249,7 +246,17 @@ void TimelineListModel::updateTimelines () {
allChatRooms.remove_if([](std::shared_ptr<linphone::ChatRoom> chatRoom){
if( ChatRoomModel::isTerminated(chatRoom) && chatRoom->getUnreadMessagesCount() > 0)
chatRoom->markAsRead();
return !chatRoom->hasCapability((int)linphone::ChatRoomCapabilities::Basic) && chatRoom->getConferenceAddress() && chatRoom->getHistoryEventsSize() == 0;
if(chatRoom->getState() == linphone::ChatRoom::State::Deleted)
return true;
if(!chatRoom->hasCapability((int)linphone::ChatRoomCapabilities::Basic)){
auto conferenceAddress = chatRoom->getConferenceAddress();
if( conferenceAddress && conferenceAddress->getDomain() == Constants::LinphoneDomain) {
QString conferenceAddressStr = Utils::coreStringToAppString(conferenceAddress->asStringUriOnly());
if( conferenceAddressStr.contains("conf-id"))
return true;
}
}
return false;
});
//Remove no more chat rooms
@ -294,7 +301,6 @@ void TimelineListModel::updateTimelines () {
QSharedPointer<TimelineModel> model = TimelineModel::create(this, dbChatRoom, callLogs);
if( model){
connect(model.get(), SIGNAL(selectedChanged(bool)), this, SLOT(onSelectedHasChanged(bool)));
connect(model->getChatRoomModel(), &ChatRoomModel::allEntriesRemoved, this, &TimelineListModel::removeChatRoomModel);
add(model);
}
}
@ -306,12 +312,10 @@ void TimelineListModel::add (QSharedPointer<TimelineModel> timeline){
auto chatRoomModel = timeline->getChatRoomModel();
auto chatRoom = chatRoomModel->getChatRoom();
connect(timeline.get(), &TimelineModel::chatRoomDeleted, this, &TimelineListModel::onChatRoomDeleted);
if( !chatRoomModel->haveConferenceAddress() || chatRoom->getHistoryEventsSize() != 0) {
connect(chatRoomModel, &ChatRoomModel::lastUpdateTimeChanged, this, &TimelineListModel::updated);
ProxyListModel::add(timeline);
emit layoutChanged();
emit countChanged();
}
connect(chatRoomModel, &ChatRoomModel::lastUpdateTimeChanged, this, &TimelineListModel::updated);
ProxyListModel::add(timeline);
emit layoutChanged();
emit countChanged();
}
void TimelineListModel::removeChatRoomModel(QSharedPointer<ChatRoomModel> model){
@ -334,7 +338,10 @@ void TimelineListModel::select(ChatRoomModel * chatRoomModel){
if(chatRoomModel) {
auto timeline = getTimeline(chatRoomModel->getChatRoom(), false);
if(timeline){
timeline->setSelected(true);
if(timeline->isUpdating())
timeline->delaySelected();
else
timeline->setSelected(true);
}
}
}
@ -356,7 +363,6 @@ void TimelineListModel::onChatRoomStateChanged(const std::shared_ptr<linphone::C
QSharedPointer<TimelineModel> model = TimelineModel::create(this, chatRoom);
if(model){
connect(model.get(), SIGNAL(selectedChanged(bool)), this, SLOT(onSelectedHasChanged(bool)));
connect(model->getChatRoomModel(), &ChatRoomModel::allEntriesRemoved, this, &TimelineListModel::removeChatRoomModel);
add(model);
}
}else if(state == linphone::ChatRoom::State::Deleted || state == linphone::ChatRoom::State::Terminated){
@ -368,6 +374,11 @@ void TimelineListModel::onChatRoomStateChanged(const std::shared_ptr<linphone::C
remove(timeline);// This will call removeRows()
}
}
}else if(state == linphone::ChatRoom::State::CreationFailed){
auto timeline = getTimeline(chatRoom, false);
if(timeline) {
remove(timeline);// This will call removeRows()
}
}
}

View file

@ -87,6 +87,8 @@ TimelineModel::TimelineModel (std::shared_ptr<linphone::ChatRoom> chatRoom, cons
QObject::connect(this, &TimelineModel::selectedChanged, this, &TimelineModel::updateUnreadCount);
QObject::connect(CoreManager::getInstance()->getAccountSettingsModel(), &AccountSettingsModel::defaultAccountChanged, this, &TimelineModel::onDefaultAccountChanged);
QObject::connect(mChatRoomModel.get(), &ChatRoomModel::chatRoomDeleted, this, &TimelineModel::onChatRoomDeleted);
QObject::connect(mChatRoomModel.get(), &ChatRoomModel::updatingChanged, this, &TimelineModel::updatingChanged);
QObject::connect(mChatRoomModel.get(), &ChatRoomModel::stateChanged, this, &TimelineModel::onChatRoomStateChanged);
}
if(chatRoom){
mChatRoomListener = std::make_shared<ChatRoomListener>();
@ -103,6 +105,8 @@ TimelineModel::TimelineModel(const TimelineModel * model){
QObject::connect(this, &TimelineModel::selectedChanged, this, &TimelineModel::updateUnreadCount);
QObject::connect(CoreManager::getInstance()->getAccountSettingsModel(), &AccountSettingsModel::defaultAccountChanged, this, &TimelineModel::onDefaultAccountChanged);
QObject::connect(mChatRoomModel.get(), &ChatRoomModel::chatRoomDeleted, this, &TimelineModel::onChatRoomDeleted);
QObject::connect(mChatRoomModel.get(), &ChatRoomModel::updatingChanged, this, &TimelineModel::updatingChanged);
QObject::connect(mChatRoomModel.get(), &ChatRoomModel::stateChanged, this, &TimelineModel::onChatRoomStateChanged);
}
if(mChatRoomModel->getChatRoom()){
mChatRoomListener = model->mChatRoomListener;
@ -141,6 +145,10 @@ int TimelineModel::getPresenceStatus() const{
return 0;
}
bool TimelineModel::isUpdating() const{
return !mChatRoomModel || mChatRoomModel->isUpdating();
}
ChatRoomModel *TimelineModel::getChatRoomModel() const{
return mChatRoomModel.get();
}
@ -165,6 +173,15 @@ void TimelineModel::setSelected(const bool& selected){
}
}
void TimelineModel::delaySelected(){
if( mChatRoomModel->getState() == LinphoneEnums::ChatRoomStateCreated){
QTimer::singleShot(200, [&](){// Delay process in order to let GUI time for Timeline building/linking before doing actions
setSelected(true);
});
}else
mDelaySelection = true;
}
void TimelineModel::updateUnreadCount(){
if(!mSelected){// updateUnreadCount is called when selected has changed;: So if mSelected is false then we are going out of it.
mChatRoomModel->resetMessageCount();// The reset will appear when the chat room has "mark as read enabled", that means that we should have read messages when going out.
@ -230,4 +247,11 @@ void TimelineModel::onChatMessageParticipantImdnStateChanged(const std::shared_p
void TimelineModel::onChatRoomDeleted(){
emit chatRoomDeleted();
}
void TimelineModel::onChatRoomStateChanged(){
if(mDelaySelection && mChatRoomModel->getState() == LinphoneEnums::ChatRoomStateCreated){
mDelaySelection = false;
setSelected(true);
}
}

View file

@ -52,6 +52,7 @@ public:
Q_PROPERTY(ChatRoomModel* chatRoomModel READ getChatRoomModel CONSTANT)
Q_PROPERTY(bool selected MEMBER mSelected WRITE setSelected NOTIFY selectedChanged)
Q_PROPERTY(bool updating READ isUpdating NOTIFY updatingChanged)
QString getFullPeerAddress() const;
@ -61,7 +62,10 @@ public:
QString getAvatar() const;
int getPresenceStatus() const;
bool isUpdating() const;
void setSelected(const bool& selected);
void delaySelected();
Q_INVOKABLE ChatRoomModel* getChatRoomModel() const;
@ -102,6 +106,7 @@ public slots:
void updateUnreadCount();
void onDefaultAccountChanged();
void onChatRoomDeleted();
void onChatRoomStateChanged();
signals:
void fullPeerAddressChanged();
@ -112,9 +117,12 @@ signals:
void selectedChanged(bool selected);
void conferenceLeft();
void chatRoomDeleted();
void updatingChanged();
private:
bool mDelaySelection = false;
void connectTo(ChatRoomListener * listener);
std::shared_ptr<ChatRoomListener> mChatRoomListener;

View file

@ -65,6 +65,7 @@ constexpr int Constants::ThumbnailImageFileHeight;
constexpr qint64 Constants::FileSizeLimit;
constexpr char Constants::DefaultXmlrpcUri[];
constexpr char Constants::DefaultUploadLogsServer[];
constexpr char Constants::DefaultConferenceURI[];
constexpr char Constants::DefaultVideoConferenceURI[];
constexpr char Constants::DefaultLimeServerURL[];

View file

@ -36,21 +36,17 @@ public:
//----------------------------------------------------------------------------------
static constexpr char WindowIconPath[] = ":/assets/images/linphone_logo.svg";
static constexpr char DefaultLocale[] = "en";
static constexpr char ApplicationMinimalQtVersion[] = "5.10.0";
static constexpr char DefaultFont[] = "Noto Sans";
static constexpr size_t MaxLogsCollectionSize = 10485760*5; // 50MB.
#ifdef ENABLE_UPDATE_CHECK
static constexpr int VersionUpdateCheckInterval = 86400000; // 24 hours in milliseconds.
#endif // ifdef ENABLE_UPDATE_CHECK
static constexpr char DefaultXmlrpcUri[] = "https://subscribe.linphone.org:444/wizard.php";
static constexpr char LinphoneDomain[] = "sip.linphone.org";
static constexpr char DefaultUploadLogsServer[] = "https://www.linphone.org:444/lft.php";
static constexpr char DefaultContactParameters[] = "message-expires=604800";
static constexpr char DefaultContactParametersOnRemove[] = "message-expires=0";
static constexpr int DefaultExpires = 3600;
@ -69,9 +65,6 @@ public:
static constexpr char LinphoneBZip2_dll[] = "https://www.linphone.org/releases/windows/tools/bzip2/bzip2.dll";
static constexpr char DefaultRlsUri[] = "sips:rls@sip.linphone.org";
static constexpr char DefaultLogsEmail[] = "linphone-desktop@belledonne-communications.com";
static constexpr char DefaultConferenceURI[] = "sip:conference-factory@sip.linphone.org";
static constexpr char DefaultVideoConferenceURI[] = "sip:videoconference-factory@sip.linphone.org";
static constexpr char DefaultLimeServerURL[] = "https://lime.linphone.org/lime-server/lime-server.php";
static constexpr char DefaultFlexiAPIURL[] = "http://fs-test-sandbox.linphone.org/flexiapi/api/";// Need "/" at the end
static constexpr char RemoteProvisioningURL[] = "http://fs-test-sandbox.linphone.org/flexiapi/provisioning";
@ -94,7 +87,18 @@ public:
static constexpr qint64 FileSizeLimit = 524288000;// In Bytes.
static constexpr int ThumbnailImageFileWidth = 100;
static constexpr int ThumbnailImageFileHeight = 100;
//--------------------------------------------------------------------------------
// LINPHONE
//--------------------------------------------------------------------------------
static constexpr char LinphoneDomain[] = "sip.linphone.org"; // Use for checking if config are a Linphone
static constexpr char WindowIconPath[] = ":/assets/images/linphone_logo.svg";
static constexpr char ApplicationMinimalQtVersion[] = "5.10.0";
static constexpr char DefaultConferenceURI[] = "sip:conference-factory@sip.linphone.org"; // Default for a Linphone account
static constexpr char DefaultVideoConferenceURI[] = "sip:videoconference-factory@sip.linphone.org"; // Default for a Linphone account
static constexpr char DefaultLimeServerURL[] = "https://lime.linphone.org/lime-server/lime-server.php"; // Default for a Linphone account
static constexpr char PathAssistantConfig[] = "/" EXECUTABLE_NAME "/assistant/";
static constexpr char PathAvatars[] = "/avatars/";
static constexpr char PathCaptures[] = "/" EXECUTABLE_NAME "/captures/";
@ -146,8 +150,6 @@ public:
// 3 = CPIM on basic chat rooms
// 4 = RTP bundle mode
// 5 = Video Conference URI
//--------------------------------------------------------------------------------
// CISCO
//--------------------------------------------------------------------------------

View file

@ -25,14 +25,19 @@
// =============================================================================
void LinphoneEnums::registerMetaTypes(){
qRegisterMetaType<LinphoneEnums::MediaEncryption>();
qRegisterMetaType<LinphoneEnums::FriendCapability>();
qRegisterMetaType<LinphoneEnums::EventLogType>();
qRegisterMetaType<LinphoneEnums::ChatMessageState>();
qRegisterMetaType<LinphoneEnums::CallStatus>();
qRegisterMetaType<LinphoneEnums::ChatMessageState>();
qRegisterMetaType<LinphoneEnums::ChatRoomState>();
qRegisterMetaType<LinphoneEnums::ConferenceLayout>();
qRegisterMetaType<LinphoneEnums::TunnelMode>();
qRegisterMetaType<LinphoneEnums::ConferenceInfoState>();
qRegisterMetaType<LinphoneEnums::ConferenceSchedulerState>();
qRegisterMetaType<LinphoneEnums::EventLogType>();
qRegisterMetaType<LinphoneEnums::FriendCapability>();
qRegisterMetaType<LinphoneEnums::MediaEncryption>();
qRegisterMetaType<LinphoneEnums::ParticipantDeviceState>();
qRegisterMetaType<LinphoneEnums::RecorderState>();
qRegisterMetaType<LinphoneEnums::TunnelMode>();
qRegisterMetaType<LinphoneEnums::TransportType>();
}
linphone::MediaEncryption LinphoneEnums::toLinphone(const LinphoneEnums::MediaEncryption& data){
@ -63,6 +68,14 @@ LinphoneEnums::ChatMessageState LinphoneEnums::fromLinphone(const linphone::Chat
return static_cast<LinphoneEnums::ChatMessageState>(data);
}
linphone::ChatRoom::State LinphoneEnums::toLinphone(const LinphoneEnums::ChatRoomState& data){
return static_cast<linphone::ChatRoom::State>(data);
}
LinphoneEnums::ChatRoomState LinphoneEnums::fromLinphone(const linphone::ChatRoom::State& data){
return static_cast<LinphoneEnums::ChatRoomState>(data);
}
linphone::Call::Status LinphoneEnums::toLinphone(const LinphoneEnums::CallStatus& data){
return static_cast<linphone::Call::Status>(data);
}
@ -89,6 +102,14 @@ LinphoneEnums::ConferenceInfoState LinphoneEnums::fromLinphone(const linphone::C
return static_cast<LinphoneEnums::ConferenceInfoState>(state);
}
linphone::ConferenceScheduler::State LinphoneEnums::toLinphone(const LinphoneEnums::ConferenceSchedulerState& state){
return static_cast<linphone::ConferenceScheduler::State>(state);
}
LinphoneEnums::ConferenceSchedulerState LinphoneEnums::fromLinphone(const linphone::ConferenceScheduler::State& state){
return static_cast<LinphoneEnums::ConferenceSchedulerState>(state);
}
linphone::ParticipantDeviceState LinphoneEnums::toLinphone(const LinphoneEnums::ParticipantDeviceState& state){
return static_cast<linphone::ParticipantDeviceState>(state);
}

View file

@ -95,8 +95,25 @@ enum ChatMessageState {
};
Q_ENUM_NS(ChatMessageState)
linphone::ChatMessage::State toLinphone(const LinphoneEnums::ChatMessageState& capability);
LinphoneEnums::ChatMessageState fromLinphone(const linphone::ChatMessage::State& capability);
linphone::ChatMessage::State toLinphone(const LinphoneEnums::ChatMessageState& data);
LinphoneEnums::ChatMessageState fromLinphone(const linphone::ChatMessage::State& data);
enum ChatRoomState {
ChatRoomStateNone = int(linphone::ChatRoom::State::None),
ChatRoomStateInstantiated = int(linphone::ChatRoom::State::Instantiated),
ChatRoomStateCreationPending = int(linphone::ChatRoom::State::CreationPending),
ChatRoomStateCreated = int(linphone::ChatRoom::State::Created),
ChatRoomStateCreationFailed = int(linphone::ChatRoom::State::CreationFailed),
ChatRoomStateTerminationPending = int(linphone::ChatRoom::State::TerminationPending),
ChatRoomStateTerminated = int(linphone::ChatRoom::State::Terminated),
ChatRoomStateTerminationFailed = int(linphone::ChatRoom::State::TerminationFailed),
ChatRoomStateDeleted = int(linphone::ChatRoom::State::Deleted),
};
Q_ENUM_NS(ChatRoomState)
linphone::ChatRoom::State toLinphone(const LinphoneEnums::ChatRoomState& data);
LinphoneEnums::ChatRoomState fromLinphone(const linphone::ChatRoom::State& data);
enum CallStatus {
CallStatusDeclined = int(linphone::Call::Status::Declined),
@ -133,6 +150,18 @@ Q_ENUM_NS(ConferenceInfoState)
linphone::ConferenceInfo::State toLinphone(const LinphoneEnums::ConferenceInfoState& state);
LinphoneEnums::ConferenceInfoState fromLinphone(const linphone::ConferenceInfo::State& state);
enum ConferenceSchedulerState {
ConferenceSchedulerStateAllocationPending = int(linphone::ConferenceScheduler::State::AllocationPending),
ConferenceSchedulerStateError = int(linphone::ConferenceScheduler::State::Error),
ConferenceSchedulerStateIdle = int(linphone::ConferenceScheduler::State::Idle),
ConferenceSchedulerStateReady = int(linphone::ConferenceScheduler::State::Ready),
ConferenceSchedulerStateUpdating = int(linphone::ConferenceScheduler::State::Updating)
};
Q_ENUM_NS(ConferenceSchedulerState)
linphone::ConferenceScheduler::State toLinphone(const LinphoneEnums::ConferenceSchedulerState& state);
LinphoneEnums::ConferenceSchedulerState fromLinphone(const linphone::ConferenceScheduler::State& state);
enum ParticipantDeviceState {
ParticipantDeviceStateJoining = int(linphone::ParticipantDeviceState::Joining),
@ -190,14 +219,16 @@ void fromString(const QString& transportType, LinphoneEnums::TransportType *tran
Q_DECLARE_METATYPE(LinphoneEnums::CallStatus)
Q_DECLARE_METATYPE(LinphoneEnums::ChatMessageState)
Q_DECLARE_METATYPE(LinphoneEnums::ChatRoomState)
Q_DECLARE_METATYPE(LinphoneEnums::ConferenceLayout)
Q_DECLARE_METATYPE(LinphoneEnums::ConferenceInfoState)
Q_DECLARE_METATYPE(LinphoneEnums::ConferenceSchedulerState)
Q_DECLARE_METATYPE(LinphoneEnums::EventLogType)
Q_DECLARE_METATYPE(LinphoneEnums::FriendCapability)
Q_DECLARE_METATYPE(LinphoneEnums::MediaEncryption)
Q_DECLARE_METATYPE(LinphoneEnums::ParticipantDeviceState)
Q_DECLARE_METATYPE(LinphoneEnums::RecorderState)
Q_DECLARE_METATYPE(LinphoneEnums::TunnelMode)
Q_DECLARE_METATYPE(LinphoneEnums::TransportType)
#endif

View file

@ -18,7 +18,7 @@ ColumnLayout{
}
function add(item){
if( !grid.isLayoutWillChanged() || !transitionningTimer.running)
if( !grid.isLayoutWillChanged())
appendItem(item)
else
bufferModels.append(item)
@ -34,7 +34,7 @@ ColumnLayout{
}
function tryToAdd(item){
if( !grid.isLayoutWillChanged() || !transitionningTimer.running) {
if( !grid.isLayoutWillChanged()) {
appendItem(item)
return true
}else
@ -51,21 +51,7 @@ ColumnLayout{
property int transitionCount : 0
property var bufferModels : ListModel{}
property int maxTransitionTime: 250
Timer{
id: transitionningTimer
running: false
interval: maxTransitionTime + 5
onTriggered: updateBuffers()
}
function startTransition(){
transitionningTimer.restart()
}
function updateBuffers(){
while(bufferModels.count > 0 && tryToAdd(bufferModels.get(0))){
bufferModels.remove(0,1)
}
}
onWidthChanged: grid.updateLayout()
onHeightChanged: grid.updateLayout()
GridView{
@ -120,63 +106,6 @@ ColumnLayout{
interactive: false
model: DelegateModel{}
//------------------- ANIMATIONS
property Transition defaultTransition: Transition {
SequentialAnimation {
ScriptAction {
script: {
mainLayout.startTransition()
}
}
ParallelAnimation {
NumberAnimation { properties: "x,y"; duration: mainLayout.maxTransitionTime }
}
}
}
add: Transition {
SequentialAnimation {
ScriptAction {
script: {
mainLayout.startTransition()
}
}
ParallelAnimation {
NumberAnimation { property: "opacity"; from: 0; duration: mainLayout.maxTransitionTime }
NumberAnimation { properties: "x,y"; from: 0; duration: mainLayout.maxTransitionTime; easing.type: Easing.OutBounce }
}
}
}
addDisplaced: defaultTransition
displaced: defaultTransition
move: defaultTransition
moveDisplaced: defaultTransition
remove: Transition {
SequentialAnimation {
PropertyAction { target: grid; property: "GridView.delayRemove"; value: true }
ScriptAction {
script: {
mainLayout.startTransition()
}
}
ParallelAnimation {
NumberAnimation { property: "opacity"; to: 0; duration: mainLayout.maxTransitionTime }
NumberAnimation { properties: "x,y"; to: 0; duration: mainLayout.maxTransitionTime }
}
PropertyAction { target: grid; property: "GridView.delayRemove"; value: false }
}
}
removeDisplaced: defaultTransition
populate:defaultTransition
Timer{ // if cell sizes change while adding/removing an item the animation will not end at the right position.
id: updateLayoutDelay
interval: mainLayout.maxTransitionTime
onTriggered: grid.updateLayout()
}
onItemCountChanged: {
updateLayoutDelay.restart()
}
onCountChanged: grid.updateLayout()
}
}

View file

@ -17,6 +17,14 @@ Control.RadioButton{
font.pointSize: RadioButtonStyle.pointSize
spacing: 10
FontMetrics{id: fontMetrics}
MouseArea{
anchors.fill:parent
hoverEnabled: true
acceptedButtons: Qt.NoButton
cursorShape: containsMouse ? Qt.PointingHandCursor : Qt.ArrowCursor
}
indicator: Rectangle {
height: fontMetrics.height - 5
width: height

View file

@ -5,6 +5,8 @@ import Common 1.0
import Common.Styles 1.0
import Units 1.0
import 'qrc:/ui/scripts/Utils/utils.js' as Utils
Item{
id: mainItem
property bool allYears : false // if false : years from today
@ -33,9 +35,10 @@ Item{
Layout.fillWidth: true
Layout.alignment: Qt.AlignCenter
horizontalAlignment: Qt.AlignCenter
text: new Date(monthList.currentYear, monthList.currentMonth, 1).toLocaleString(Qt.locale(), 'MMMM yyyy')
text: new Date(monthList.currentYear, monthList.currentMonth, 15).toLocaleString(Qt.locale(), 'MMMM yyyy')// 15 because of timezones that can change the date for localeString
color: DatePickerStyle.title.color
font.pointSize: DatePickerStyle.title.pointSize
font.capitalization: Font.Capitalize
}
ActionButton{
isCustom: true
@ -62,7 +65,7 @@ Item{
}
property date selectedDate: new Date()
property int minYear: mainItem.allYears ? new Date(0,0,0).getFullYear() : new Date().getFullYear()
property int minYear: mainItem.allYears ? new Date(0,0,1).getFullYear() : new Date().getFullYear()
snapMode: ListView.SnapOneItem
orientation: Qt.Horizontal
@ -70,7 +73,7 @@ Item{
// One model per month
model: (new Date().getFullYear()- minYear + maxYears) * 12
currentIndex: 0
property int currentYear: Math.floor(currentIndex / 12) + minYear
property int currentMonth: currentIndex % 12
@ -87,7 +90,7 @@ Item{
property int year: Math.floor(index / 12) + monthList.minYear
property int month: index % 12
property int firstDay: new Date(year, month, 1).getDay()
GridLayout { // 1 month calender
id: grid
@ -105,9 +108,12 @@ Item{
delegate: Item{
id: cellItem
property int day: index - 7 // 0 = top left below Sunday (-7 to 41)
property int day: index - 7 // 0 = top left below Sunday (-7 to 41)
property int date: day - firstDay + 1 // 1-31
property bool selected : new Date(year, month, date).toDateString() == monthList.selectedDate.toDateString() && text.text && day >= 0
property date cellDate: new Date(year, month, date)
property bool selected : text.text != '-'
&& Utils.equalDate(cellDate, monthList.selectedDate)
&& text.text && day >= 0
width: grid.cellMinSize
height: width
@ -132,12 +138,12 @@ Item{
: cellItem.day < 0
? DatePickerStyle.cell.dayHeaderPointSize
: DatePickerStyle.cell.dayPointSize
font.bold: cellItem.day < 0 || cellItem.selected || new Date(year, month, cellItem.date).toDateString() == new Date().toDateString() // today
font.bold: cellItem.day < 0 || cellItem.selected || Utils.equalDate(cellItem.cellDate, new Date()) // today
text: {
if(cellItem.day < 0)
if(cellItem.day < 0){
// Magic date to set day names in this order : 'S', 'M', 'T', 'W', 'T', 'F', 'S' in Locale
return new Date(1,3,index).toLocaleString(Qt.locale(), 'ddd')[0]
else if(new Date(year, month, cellItem.date).getMonth() == month && (!hideOldDates || new Date(year, month, cellItem.date+1) >= new Date())) // new Date use time too
return Utils.exactDate(new Date(2000,9,index+1)).toLocaleString(Qt.locale(), 'ddd')[0].toUpperCase()
}else if(cellItem.cellDate.getMonth() == month && (!hideOldDates || new Date(year, month, cellItem.date+1) >= new Date())) // new Date use time too
return cellItem.date
else
return '-'
@ -152,7 +158,7 @@ Item{
enabled: text.text && text.text != '-' && cellItem.day >= 0
onClicked: {
monthList.selectedDate = new Date(year, month, cellItem.date)
monthList.selectedDate = cellItem.cellDate
mainItem.clicked(monthList.selectedDate)
}
}

View file

@ -6,6 +6,7 @@ import ColorsList 1.0
QtObject {
property string sectionName: 'Busy'
property color color: ColorsList.add(sectionName+'_indicator', 'q').color
property color alternateColor: ColorsList.add(sectionName+'_indicator_alt', 'i').color
property int duration: 1250
property int nSpheres: 8
}

View file

@ -13,6 +13,7 @@ Rectangle {
// ---------------------------------------------------------------------------
default property alias _content: content.data
property alias isDarkMode: contact.isDarkMode
property alias signIcon: signIcon.icon
property alias subtitleColor: contact.subtitleColor

View file

@ -144,20 +144,17 @@ ListView {
id: _callControls
// -------------------------------------------------------------------------
function useColorStatus () {
return calls.currentIndex === index && $modelData && $modelData.status !== CallModel.CallStatusEnded
}
isDarkMode: calls.currentIndex === index && $modelData!=undefined && $modelData.status!=undefined && $modelData.status !== CallModel.CallStatusEnded
// -------------------------------------------------------------------------
color: useColorStatus()
color: isDarkMode
? CallsStyle.entry.color.selected
: CallsStyle.entry.color.normal
subtitleColor: useColorStatus()
subtitleColor: isDarkMode
? CallsStyle.entry.subtitleColor.selected
: CallsStyle.entry.subtitleColor.normal
titleColor: useColorStatus()
titleColor: isDarkMode
? CallsStyle.entry.titleColor.selected
: CallsStyle.entry.titleColor.normal

View file

@ -22,10 +22,11 @@ Item {
property bool isFullscreen: false
property bool hideCamera: false
property bool isPaused: false
property bool deactivateCamera: false
property bool deactivateCamera: true
property bool isVideoEnabled: !deactivateCamera && (!callModel || callModel.videoEnabled)
&& (!container.currentDevice || callModel && (container.currentDevice
&& (container.currentDevice.videoEnabled || (container.currentDevice.isMe && callModel.cameraEnabled))))
&& (!container.currentDevice || ( callModel && container.currentDevice &&
( (!container.currentDevice.isMe && container.currentDevice.videoEnabled)
|| (container.currentDevice.isMe && callModel.cameraEnabled))))
property bool a : callModel && callModel.videoEnabled
property bool b: container.currentDevice && container.currentDevice.videoEnabled
@ -51,6 +52,10 @@ Item {
anchors.fill: parent
active: !resetActive && container.isVideoEnabled
onActiveChanged: {
console.log("QML Camera status : " + active)
}
sourceComponent: container.isVideoEnabled && !container.isPaused? camera : null
Timer{

View file

@ -79,6 +79,7 @@ Item{
anchors.fill: parent
visible: false
onVideoDefinitionChanged: mainItem.videoDefinitionChanged()
deactivateCamera: false
}
OpacityMask{
id: renderedCamera

View file

@ -12,6 +12,7 @@ import LinphoneEnums 1.0
import Units 1.0
import 'Chat.js' as Logic
import 'qrc:/ui/scripts/Utils/utils.js' as Utils
// =============================================================================
@ -32,10 +33,17 @@ Rectangle {
color: ChatStyle.color
clip: true
Timer{// Let some time to have a better cell sizes
id: repositionerDelay
property int indexToMove
interval: 100
onTriggered: chat.positionViewAtIndex(indexToMove, ListView.Center)
}
function positionViewAtIndex(index){
chat.bindToEnd = false
chat.positionViewAtIndex(index, ListView.Beginning)
chat.positionViewAtIndex(index, ListView.Center)
repositionerDelay.indexToMove = index
repositionerDelay.restart()
}
function goToMessage(message){
@ -290,10 +298,10 @@ Rectangle {
//: 'Choose where to forward the message' : Dialog title for choosing where to forward the current message.
, {title: qsTr('forwardDialogTitle'),
addressSelectedCallback: function (sipAddress) {
var chat = CallsListModel.createChat(sipAddress)
var chat = CallsListModel.createChatRoom( '', proxyModel.chatRoomModel.haveEncryption, [sipAddress], false )
if(chat){
chat.forwardMessage($chatEntry)
TimelineListModel.select(chat)
chat.chatRoomModel.forwardMessage($chatEntry)
TimelineListModel.select(chat.chatRoomModel)
}
},
chatRoomSelectedCallback: function (chatRoomModel){
@ -379,6 +387,7 @@ Rectangle {
Layout.fillWidth: true
Layout.preferredHeight: textAreaBorders.height + chatMessagePreview.height+messageBlock.height
color: ChatStyle.sendArea.backgroundBorder.color
visible: proxyModel.chatRoomModel && !proxyModel.chatRoomModel.isReadOnly && (!proxyModel.chatRoomModel.haveEncryption && SettingsModel.standardChatEnabled || proxyModel.chatRoomModel.haveEncryption && SettingsModel.secureChatEnabled)
ColumnLayout{
anchors.fill: parent
spacing: 0
@ -411,7 +420,6 @@ Rectangle {
Layout.leftMargin: ChatStyle.sendArea.backgroundBorder.width
borderColor: ChatStyle.sendArea.border.color
topWidth: ChatStyle.sendArea.border.width
visible: proxyModel.chatRoomModel && !proxyModel.chatRoomModel.isReadOnly && (!proxyModel.chatRoomModel.haveEncryption && SettingsModel.standardChatEnabled || proxyModel.chatRoomModel.haveEncryption && SettingsModel.secureChatEnabled)
DroppableTextArea {
id: textArea
@ -423,7 +431,7 @@ Rectangle {
anchors.right: parent.right
anchors.bottom: parent.bottom
height:ChatStyle.sendArea.height + ChatStyle.sendArea.border.width
height: visible ? ChatStyle.sendArea.height + ChatStyle.sendArea.border.width : 0
minimumHeight:ChatStyle.sendArea.height + ChatStyle.sendArea.border.width
maximumHeight:container.height/2
@ -445,10 +453,7 @@ Rectangle {
}
}
onAudioRecordRequest: RecorderManager.resetVocalRecorder()
Component.onCompleted: {
text = proxyModel.chatRoomModel.cachedText
cursorPosition=text.length
}
Component.onCompleted: {text = proxyModel.cachedText; cursorPosition=text.length}
Rectangle{
anchors.fill:parent
color:'white'

View file

@ -111,7 +111,8 @@ Loader{
color: ChatCalendarMessageStyle.schedule.color
elide: Text.ElideRight
font.pointSize: ChatCalendarMessageStyle.schedule.pointSize
text: Qt.formatDateTime(mainItem.conferenceInfoModel.dateTime, 'hh:mm')
// Reminder: QML use locale time (not system). Use UTC from C++ => convert it into QML => pass QML => convert it into UTC and apply our timezone.
text: UtilsCpp.toTimeString(Utils.fromUTC(mainItem.conferenceInfoModel.dateTimeUtc), 'hh:mm')
+ (mainItem.conferenceInfoModel.duration > 0 ? ' (' +Utils.formatDuration(mainItem.conferenceInfoModel.duration * 60) + ')'
: '')
}
@ -143,10 +144,10 @@ Loader{
color: ChatCalendarMessageStyle.type.cancelledColor
font.pointSize: ChatCalendarMessageStyle.type.pointSize
font.weight: Font.Bold
text: 'You have cancelled the conference'
//: 'Meeting has been cancelled' : ICS Title for cancelled meetings
text:qsTr('icsCancelledMeetingInvite')
}
Text{
id: title
Layout.fillWidth: true
@ -178,6 +179,8 @@ Loader{
Layout.preferredHeight: parent.participantLineHeight
Layout.preferredWidth: ChatCalendarMessageStyle.participants.iconSize
Layout.alignment: Qt.AlignTop
visible: mainItem.conferenceInfoModel.participantCount > 0
clip: false
Icon{
anchors.top: parent.top
@ -209,7 +212,11 @@ Loader{
Layout.alignment: Qt.AlignTop
spacing: 0
visible: mainItem.isExpanded
onVisibleChanged: model= mainItem.conferenceInfoModel.getParticipants()
onVisibleChanged: visible ? model= mainItem.conferenceInfoModel.getAllParticipants() : model = []
Connections{
target: mainItem.conferenceInfoModel
onParticipantsChanged: if(expandedParticipantsList.visible) expandedParticipantsList.model = mainItem.conferenceInfoModel.getAllParticipants()
}
delegate: Row{
spacing: 5

View file

@ -109,12 +109,14 @@ Loader{
}
RowLayout {
id: participantsRow
property int participantCount: mainItem.conferenceInfoModel.allParticipantCount
Layout.fillWidth: true
Layout.preferredHeight: ChatCalendarMessageStyle.participants.iconSize
Layout.leftMargin: 5
Layout.rightMargin: 15
spacing: ChatCalendarMessageStyle.participants.spacing
spacing: ChatCalendarMessageStyle.participants.spacing
visible: participantsRow.participantCount > 0
Icon{
icon: ChatCalendarMessageStyle.participants.icon
@ -124,13 +126,12 @@ Loader{
Text {
id: participantsList
property int participantCount: mainItem.conferenceInfoModel.getParticipantCount()
Layout.fillWidth: true
color: ChatCalendarMessageStyle.participants.color
elide: Text.ElideRight
font.pointSize: ChatCalendarMessageStyle.participants.pointSize
//: '%1 participant' : number(=%1) of participant.
text: qsTr('icsParticipants', '', participantCount).arg(participantCount)
text: qsTr('icsParticipants', '', participantsRow.participantCount).arg(participantsRow.participantCount)
}
}
ColumnLayout{
@ -158,7 +159,7 @@ Loader{
color: ChatCalendarMessageStyle.schedule.color
elide: Text.ElideRight
font.pointSize: Units.dp * 8
text: Qt.formatDate(mainItem.conferenceInfoModel.dateTimeUtc, 'yyyy/MM/dd')
text: UtilsCpp.toDateString(Utils.fromUTC(mainItem.conferenceInfoModel.dateTimeUtc), 'yyyy/MM/dd')
}
}
RowLayout {
@ -182,7 +183,8 @@ Loader{
color: ChatCalendarMessageStyle.schedule.color
elide: Text.ElideRight
font.pointSize: Units.dp * 8
text: Qt.formatDateTime(mainItem.conferenceInfoModel.dateTimeUtc, 'hh:mm')
// Reminder: QML use locale time (not system). Use UTC from C++ => convert it into QML => pass QML => convert it into UTC and apply our timezone.
text: UtilsCpp.toTimeString( Utils.fromUTC(mainItem.conferenceInfoModel.dateTimeUtc), 'hh:mm')
+ (mainItem.conferenceInfoModel.duration > 0 ? ' ('+Utils.formatDuration(mainItem.conferenceInfoModel.duration * 60) + ')'
: '')
}
@ -202,7 +204,11 @@ Loader{
Layout.leftMargin: 10
spacing: 0
visible: mainItem.isExpanded
onVisibleChanged: model= mainItem.conferenceInfoModel.getParticipants()
onVisibleChanged: visible ? model= mainItem.conferenceInfoModel.getAllParticipants() : model = []
Connections{
target: mainItem.conferenceInfoModel
onParticipantsChanged: if(expandedParticipantsList.visible) expandedParticipantsList.model = mainItem.conferenceInfoModel.getAllParticipants()
}
delegate: Row{
spacing: 5

View file

@ -10,84 +10,95 @@ import UtilsCpp 1.0
// =============================================================================
Item {
id: avatar
// ---------------------------------------------------------------------------
property alias presenceLevel: presenceLevelIcon.level
property color backgroundColor: AvatarStyle.backgroundColor
property color foregroundColor: 'transparent'
property string username
property var image
property var _initialsRegex: /^\s*([^\s\.]+)(?:[\s\.]+([^\s\.]+))?/
property bool isPhoneNumber: UtilsCpp.isPhoneNumber(username)
// ---------------------------------------------------------------------------
function isLoaded () {
return roundedImage.status === Image.Ready
}
function _computeInitials () {
var result = username.match(_initialsRegex)
if (!result) {
return username.length > 0 ? username.charAt(0).toUpperCase() : ''
}
return result[1].charAt(0).toUpperCase() + (
result[2] != null
? result[2].charAt(0).toUpperCase()
: ''
)
}
// ---------------------------------------------------------------------------
RoundedImage {
id: roundedImage
anchors.fill: parent
backgroundColor: avatar.backgroundColor
foregroundColor: avatar.foregroundColor
source: avatar.image || ''
Icon{
id: avatar
// ---------------------------------------------------------------------------
property alias presenceLevel: presenceLevelIcon.level
property bool isDarkMode: false
property color backgroundColor: isDarkMode ? AvatarStyle.backgroundDarkModeColor : AvatarStyle.backgroundColor
property color foregroundColor: 'transparent'
property string username
property var image
property bool isOneToOne: true
property var _initialsRegex: /^\s*([^\s\.]+)(?:[\s\.]+([^\s\.]+))?/
property bool isPhoneNumber: UtilsCpp.isPhoneNumber(username)
// ---------------------------------------------------------------------------
function isLoaded () {
return roundedImage.status === Image.Ready
}
function _computeInitials () {
var result = username.match(_initialsRegex)
if (!result) {
return username.length > 0 ? username.charAt(0).toUpperCase() : ''
}
return result[1].charAt(0).toUpperCase() + (
result[2] != null
? result[2].charAt(0).toUpperCase()
: ''
)
}
// ---------------------------------------------------------------------------
RoundedImage {
id: roundedImage
anchors.fill: parent
icon: AvatarStyle.personImage
visible: parent.source == '' && avatar.isPhoneNumber
overwriteColor: AvatarStyle.initials.color
}
}
Text {
id: initialsText
anchors.centerIn: parent
color: AvatarStyle.initials.color
font.pointSize: {
var width
if (parent.width > 0) {
width = parent.width / AvatarStyle.initials.ratio
}
return AvatarStyle.initials.pointSize * (width || 1)
}
text: _computeInitials()
visible: roundedImage.status !== Image.Ready && !avatar.isPhoneNumber
}
PresenceLevel {
id: presenceLevelIcon
visible: level >= 0
anchors {
bottom: parent.bottom
right: parent.right
}
height: parent.height / 4
width: parent.width / 4
}
backgroundColor: avatar.backgroundColor
foregroundColor: avatar.foregroundColor
source: avatar.image || ''
Icon{
anchors.fill: parent
icon: AvatarStyle.personImage
visible: parent.source == '' && avatar.isPhoneNumber
overwriteColor: AvatarStyle.initials.color
}
}
Text {
id: initialsText
anchors.centerIn: parent
color: isDarkMode ? AvatarStyle.initials.darkModeColor : AvatarStyle.initials.color
font.pointSize: {
var width
if (parent.width > 0) {
width = parent.width / AvatarStyle.initials.ratio
}
return AvatarStyle.initials.pointSize * (width || 1)
}
text: _computeInitials()
visible: roundedImage.status !== Image.Ready && !avatar.isPhoneNumber && avatar.isOneToOne
}
Icon {
anchors.fill: parent
icon: ContactStyle.groupChat.icon
overwriteColor: isDarkMode ? ContactStyle.groupChat.avatarDarkModeColor : ContactStyle.groupChat.avatarColor
iconSize: avatar.width
//visible: entry!=undefined && entry.isOneToOne!=undefined && !entry.isOneToOne
visible: !avatar.isOneToOne
}
PresenceLevel {
id: presenceLevelIcon
visible: level >= 0
anchors {
bottom: parent.bottom
right: parent.right
}
height: parent.height / 4
width: parent.width / 4
}
}

View file

@ -21,9 +21,11 @@ Rectangle {
property alias subtitleColor: description.subtitleColor
property alias titleColor: description.titleColor
property alias statusText : description.statusText
property alias isDarkMode: avatar.isDarkMode
property bool displayUnreadMessageCount: false
property bool showSubtitle : true
property bool showBusyIndicator: false
property string subtitle: ''
property string subject: (entry && entry.conferenceInfoModel && entry.conferenceInfoModel.subject
@ -75,16 +77,7 @@ Rectangle {
? ''
: item.username
: item.username
visible:!groupChat.visible
Icon {
anchors.fill: parent
icon: ContactStyle.groupChat.icon
overwriteColor: ContactStyle.groupChat.avatarColor
iconSize: ContactStyle.contentHeight
visible: entry!=undefined && entry.isOneToOne!=undefined && !entry.isOneToOne
}
isOneToOne: entry==undefined || entry.isOneToOne==undefined || entry.isOneToOne
Icon{
anchors.top:parent.top
@ -97,25 +90,26 @@ Rectangle {
anchors.fill: parent
onClicked: item.avatarClicked(mouse)
}
}
Icon {
id: groupChat
Layout.preferredHeight: ContactStyle.contentHeight
Layout.preferredWidth: ContactStyle.contentHeight
icon: ContactStyle.groupChat.icon
overwriteColor: ContactStyle.groupChat.color
iconSize: ContactStyle.contentHeight
visible: false //entry!=undefined && entry.isOneToOne!=undefined && !entry.isOneToOne
Icon{
anchors.right: parent.right
anchors.top:parent.top
anchors.topMargin: -5
visible: entry!=undefined && entry.haveEncryption != undefined && entry.haveEncryption
icon: entry?(entry.securityLevel === 2?'secure_level_1': entry.securityLevel===3? 'secure_level_2' : 'secure_level_unsafe'):'secure_level_unsafe'
iconSize:15
Loader{
id: busyLoader
anchors.fill: parent
anchors.margins: 5
active: item.showBusyIndicator
sourceComponent: Component{
BusyIndicator{// Joining spinner
id: joiningSpinner
running: false
Timer{// Delay starting spinner (Qt bug)
id: indicatorDelay
interval: 100
onTriggered: joiningSpinner.running = true
}
Component.onCompleted: indicatorDelay.start()
}
}
}
}
@ -136,7 +130,9 @@ Rectangle {
? item.organizer
? item.organizer
: entry.sipAddress || entry.fullPeerAddress || entry.peerAddress || ''
: entry.participants.addressesToString
: entry.participants
? entry.participants.addressesToString
: ''
: ''
}

View file

@ -50,6 +50,14 @@ Rectangle{
onVisibleChanged: if(!visible && contentsStack.nViews > 1) {
contentsStack.pop()
}
property bool _activateCamera: false
Connections{// Enable camera only when status is ok
target: mainItem.callModel
onStatusChanged: if( mainItem._activateCamera && (status == LinphoneEnums.CallStatusConnected || status == LinphoneEnums.CallStatusIdle)){
camera._activateCamera = false
callModel.cameraEnabled = true
}
}
ButtonGroup{id: modeGroup}
ColumnLayout{
anchors.fill: parent
@ -158,13 +166,16 @@ Rectangle{
font.pointSize: IncallMenuStyle.list.pointSize
color: IncallMenuStyle.list.color
}
ActionButton{
Icon{
Layout.minimumWidth: iconWidth
Layout.rightMargin: 10
Layout.alignment: Qt.AlignVCenter
backgroundRadius: width/2
isCustom: true
colorSet: IncallMenuStyle.buttons.next
//backgroundRadius: width/2
icon: IncallMenuStyle.buttons.next.icon
overwriteColor: IncallMenuStyle.buttons.next.backgroundNormalColor
iconWidth: IncallMenuStyle.buttons.next.iconSize
iconHeight: IncallMenuStyle.buttons.next.iconSize
}
}
MouseArea{
@ -227,6 +238,11 @@ Rectangle{
bottomWidth: IncallMenuStyle.list.border.width
Layout.preferredHeight: Math.max(layoutIcon.height, radio.contentItem.implicitHeight) + 20
Layout.fillWidth: true
enabled: mainItem.callModel && !mainItem.callModel.updating
MouseArea{
anchors.fill: parent
onClicked: radio.clicked()
}
RowLayout{
anchors.fill: parent
@ -238,37 +254,16 @@ Rectangle{
Layout.alignment: Qt.AlignVCenter
ButtonGroup.group: modeGroup
text: modelData.text
property bool isInternallyChecked: mainItem.callModel ? (mainItem.callModel.localVideoEnabled && modelData.value == mainItem.callModel.conferenceVideoLayout)
|| (!mainItem.callModel.localVideoEnabled && modelData.value == LinphoneEnums.ConferenceLayoutAudioOnly)
: false
// break bind. Radiobutton checked itself without taking care of custom binding. This workaound works as long as we don't really need the binding.
onIsInternallyCheckedChanged: checked = isInternallyChecked
Component.onCompleted: checked = isInternallyChecked
Timer{
id: changingLayoutDelay
interval: 100
onTriggered: {if(modelData.value == 2) mainItem.callModel.videoEnabled = false
else {
mainItem.callModel.conferenceVideoLayout = modelData.value
mainItem.callModel.videoEnabled = true
}
mainItem.enabled = true
}
}
onClicked:{
// Do changes only if we choose a different layout.
if(! ( mainItem.callModel ? (mainItem.callModel.localVideoEnabled && modelData.value == mainItem.callModel.conferenceVideoLayout)
|| (!mainItem.callModel.localVideoEnabled && modelData.value == LinphoneEnums.ConferenceLayoutAudioOnly)
: false)){
mainItem.enabled = false
mainItem.layoutChanging(modelData.value)// Let time to clear cameras
changingLayoutDelay.start()
}
}
onClicked: mainItem.layoutChanging(modelData.value)
}
Icon{
id: layoutIcon
id: layoutIcon
Layout.minimumWidth: iconWidth
Layout.rightMargin: 10
Layout.alignment: Qt.AlignVCenter

View file

@ -101,15 +101,20 @@ Item{
color: "#80000000"
source: usernameItem
}
ActionButton{
visible: mainItem._showCloseButton && mainItem._isPreview && mainItem._callModel && mainItem._callModel.videoEnabled
Loader{
id: closeLoader
anchors.right: parent.right
anchors.top: parent.top
anchors.rightMargin: 5
anchors.topMargin: 5
isCustom: true
colorSet: DecorationStickerStyle.closePreview
onClicked: mainItem.closeRequested()
active: mainItem._showCloseButton && mainItem._isPreview && mainItem._callModel && mainItem._callModel.videoEnabled
sourceComponent: Component{
ActionButton{
isCustom: true
colorSet: DecorationStickerStyle.closePreview
onClicked: mainItem.closeRequested()
}
}
}
ColumnLayout{
anchors.top: parent.top
@ -137,17 +142,24 @@ Item{
iconSize: DecorationStickerStyle.isMuted.button.iconSize
}
}
BusyIndicator{// Joining spinner
Loader{
id: busyLoader
Layout.preferredHeight: 20
Layout.preferredWidth: 20
property bool delayed : false
visible: delayed && mainItem._currentDevice && (mainItem._currentDevice.state == LinphoneEnums.ParticipantDeviceStateJoining || mainItem._currentDevice.state == LinphoneEnums.ParticipantDeviceStateScheduledForJoining || mainItem._currentDevice.state == LinphoneEnums.ParticipantDeviceStateAlerting)
Timer{// Delay starting spinner (Qt bug)
id: indicatorDelay
interval: 100
onTriggered: parent.delayed = true
active: mainItem._currentDevice && (mainItem._currentDevice.state == LinphoneEnums.ParticipantDeviceStateJoining || mainItem._currentDevice.state == LinphoneEnums.ParticipantDeviceStateScheduledForJoining || mainItem._currentDevice.state == LinphoneEnums.ParticipantDeviceStateAlerting)
sourceComponent: Component{
BusyIndicator{// Joining spinner
id: joiningSpinner
running: false
Timer{// Delay starting spinner (Qt bug)
id: indicatorDelay
interval: 100
onTriggered: joiningSpinner.running = true
}
Component.onCompleted: indicatorDelay.start()
}
}
Component.onCompleted: indicatorDelay.start()
}
}
}

View file

@ -35,7 +35,7 @@ Item{
property alias showActiveSpeakerOverlay: camera.showActiveSpeakerOverlay
property alias isCameraFromDevice: camera.isCameraFromDevice
property alias deactivateCamera: camera.deactivateCamera
property alias isVideoEnabled: camera.isVideoEnabled
readonly property alias isVideoEnabled: camera.isVideoEnabled
property alias image: avatar.image
property alias avatarBackgroundColor: avatar.avatarBackgroundColor

View file

@ -19,7 +19,7 @@ QtObject {
}
property QtObject burgerMenu: QtObject {
property string name : 'burgerMenu'
property string icon : 'burger_menu_custom'
property string icon : 'menu_vdots_custom'
property int iconSize: 35
property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 'me_n_b_bg').color
property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 'me_h_b_bg').color
@ -30,7 +30,7 @@ QtObject {
}
property QtObject selectedBurgerMenu: QtObject {
property string name : 'selectedBurgerMenu'
property string icon : 'burger_menu_custom'
property string icon : 'menu_vdots_custom'
property int iconSize: 35
property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 'me_n_b_inv_bg').color
property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 'me_h_b_inv_bg').color
@ -39,6 +39,7 @@ QtObject {
property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 'me_h_b_inv_fg').color
property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 'me_p_b_inv_fg').color
}
property QtObject hangup: QtObject {
property int iconSize: 35
property string icon : 'hangup_custom'

View file

@ -9,11 +9,14 @@ import ColorsList 1.0
QtObject {
property string sectionName: 'Avatar'
property color backgroundColor: ColorsList.add(sectionName+'_bg', 'd').color
property color backgroundDarkModeColor: ColorsList.add(sectionName+'_dark_bg', 'q').color
property string personImage : 'contact_custom'
property QtObject initials: QtObject {
property color color: ColorsList.add(sectionName+'_initials', 'q').color
property color darkModeColor: ColorsList.add(sectionName+'_dark_initials', 'd').color
property int pointSize: Units.dp * 10
property int ratio: 30
}

View file

@ -16,5 +16,6 @@ QtObject {
property string icon: 'chat_room_custom'
property color color: ColorsList.addImageColor(sectionName+'_groupChat', icon, 'g').color
property color avatarColor: ColorsList.addImageColor(sectionName+'_groupChat_onAvatar', icon, 'q').color
property color avatarDarkModeColor: ColorsList.addImageColor(sectionName+'_groupChat_dark_onAvatar', icon, 'd').color
}
}

View file

@ -17,13 +17,6 @@ QtObject {
property color selectedTimerColor: ColorsList.addImageColor(sectionName+'_ephemeralTimer_c', icon, 'q').color
}
property QtObject draft: QtObject{
property string icon: 'draft_custom'
property int iconSize : 30
property color color: ColorsList.addImageColor(sectionName+'_draft', icon, 'ad').color
property color selectedColor: ColorsList.addImageColor(sectionName+'_draft_c', icon, 'q').color
}
property QtObject contact: QtObject {
property int height: 60

View file

@ -44,6 +44,7 @@ Item {
NumberAnimation { target: optionsView; property: 'x'; to:optionsView.width; duration: 200;}
}
]
enabled: !contactView.showBusyIndicator
Contact {
@ -68,32 +69,20 @@ Item {
? TimelineStyle.contact.title.color.selected
: TimelineStyle.contact.title.color.normal
showSubtitle: mainItem.timelineModel && (mainItem.timelineModel.chatRoomModel && (mainItem.timelineModel.chatRoomModel.isOneToOne || !mainItem.timelineModel.chatRoomModel.isConference))
TooltipArea {
showBusyIndicator: mainItem.timelineModel && mainItem.timelineModel.updating
TooltipArea {
id: contactTooltip
text: mainItem.timelineModel && UtilsCpp.toDateTimeString(mainItem.timelineModel.chatRoomModel.lastUpdateTime)
isClickable: true
}
Icon{
id: draft
icon: TimelineStyle.draft.icon
iconSize: visible ? TimelineStyle.draft.iconSize : 0
overwriteColor: mainItem.timelineModel && mainItem.timelineModel.selected ? TimelineStyle.draft.selectedColor : TimelineStyle.draft.color
anchors.right:parent.right
anchors.bottom:parent.bottom
anchors.bottomMargin: 3
anchors.rightMargin: 7
visible: mainItem.timelineModel && mainItem.timelineModel.chatRoomModel.hasDraft
}
Icon{
icon: TimelineStyle.ephemeralTimer.icon
iconSize: TimelineStyle.ephemeralTimer.iconSize
overwriteColor: mainItem.timelineModel && mainItem.timelineModel.selected ? TimelineStyle.ephemeralTimer.selectedTimerColor : TimelineStyle.ephemeralTimer.timerColor
anchors.right:draft.left
anchors.right:parent.right
anchors.bottom:parent.bottom
anchors.bottomMargin: 3
anchors.rightMargin: draft.visible ? 0 : 7
anchors.bottomMargin: 7
anchors.rightMargin: 7
visible: mainItem.timelineModel && mainItem.timelineModel.chatRoomModel.ephemeralEnabled
}
MouseArea {
@ -173,5 +162,4 @@ Item {
}
}
}
}

View file

@ -11,7 +11,7 @@ import Linphone.Styles 1.0
import Units 1.0
import UtilsCpp 1.0
import 'qrc:/ui/scripts/Utils/utils.js' as Utils
// =============================================================================
ColumnLayout {
@ -114,6 +114,9 @@ ColumnLayout {
colorSet: ParticipantsListViewStyle.removeParticipant,
secure:0,
visible:true,
visibleHandler: function(entry){
return !UtilsCpp.isMe(entry.sipAddress)
},
//: 'Remove this participant from the list' : Tootltip to explain that the action will lead to remove the participant.
tooltipText: qsTr('participantsListRemoveTooltip'),
handler: function (entry) {

View file

@ -6,6 +6,8 @@ import Linphone 1.0
import Linphone.Styles 1.0
import Common.Styles 1.0
import 'qrc:/ui/scripts/Utils/utils.js' as Utils
// =============================================================================
ScrollableListView {
@ -120,6 +122,9 @@ ScrollableListView {
backgroundRadius: 90
colorSet: modelData.colorSet
visible: sipAddressesView.actions[index].visible
&& (!sipAddressesView.actions[index].visibleHandler || sipAddressesView.actions[index].visibleHandler({
sipAddress: sipAddressesView.interpretableSipAddress
}))
onClicked: sipAddressesView.actions[index].handler({
sipAddress: sipAddressesView.interpretableSipAddress
@ -253,16 +258,7 @@ ScrollableListView {
statusText : showAdminStatus ? getStatus() : ''
entry: $modelData
onAvatarClicked: sipAddressesView.entryClicked(parent.entry, index, contactView)
BusyIndicator{
anchors.verticalCenter: parent.verticalCenter
anchors.horizontalCenter: parent.horizontalCenter
width:15
height:15
running: sipAddressesView.showInvitingIndicator && $modelData.inviting
}
}
@ -289,25 +285,48 @@ ScrollableListView {
}
Repeater {
id: actionsRepeater
model: sipAddressesView.actions
ActionButton {
isCustom: true
backgroundRadius: 90
colorSet: modelData.colorSet
Item{
height: buttonAction.height
width: buttonAction.width
anchors.verticalCenter: parent.verticalCenter
tooltipText: modelData.tooltipText? modelData.tooltipText:''
visible: sipAddressesView.actions[index].visible
onClicked: {
sipAddressesView.actions[index].handler(contactView.entry)
}
Icon{
visible: modelData.secure>0 &&
(sipAddressesView.actions[index].secureIconVisibleHandler ? sipAddressesView.actions[index].secureIconVisibleHandler({sipAddres:$modelData}) : true)
icon: modelData.secure === 2?'secure_level_2':'secure_level_1'
iconSize: parent.height/2
anchors.top:parent.top
anchors.horizontalCenter: parent.right
ActionButton {
id: buttonAction
isCustom: true
backgroundRadius: 90
colorSet: modelData.colorSet
anchors.verticalCenter: parent.verticalCenter
tooltipText: modelData.tooltipText? modelData.tooltipText:''
visible: sipAddressesView.actions[index].visible && (!sipAddressesView.actions[index].visibleHandler || sipAddressesView.actions[index].visibleHandler(contactView.entry))
onClicked: {
sipAddressesView.actions[index].handler(contactView.entry)
}
Icon{
visible: modelData.secure>0 &&
(sipAddressesView.actions[index].secureIconVisibleHandler ? sipAddressesView.actions[index].secureIconVisibleHandler({sipAddres:$modelData}) : true)
icon: modelData.secure === 2?'secure_level_2':'secure_level_1'
iconSize: parent.height/2
anchors.top:parent.top
anchors.horizontalCenter: parent.right
}
Loader{
anchors.verticalCenter: parent.verticalCenter
anchors.horizontalCenter: parent.horizontalCenter
height: parent.height - 2
width: height
active: index == actionsRepeater.count -1 && sipAddressesView.showInvitingIndicator && contactView.entry && contactView.entry.inviting
sourceComponent: Component{
BusyIndicator{
color: BusyIndicatorStyle.alternateColor
running: true
}
}
}
}
}
}

View file

@ -562,6 +562,24 @@ function buildDate(date, time){
dateTime.setSeconds(time.getSeconds())
return dateTime
}
function equalDate(date1, date2){
return date1.getFullYear() == date2.getFullYear() && date1.getMonth() == date2.getMonth() && date1.getDate() == date2.getDate()
}
function fromUTC(date){
return new Date(Date.UTC(date.getUTCFullYear(), date.getUTCMonth(),
date.getUTCDate(), date.getUTCHours(),
date.getUTCMinutes(), date.getUTCSeconds()));
}
// return EXACTLY what date has been set (not take account of Locale timezones, eg. Date(2000,0,1) will print january and not december if timezone lead there.)
// Use this function for toLocaleString/toLocaleDateString or other
function exactDate(date) {
var timeOffset = date.getTimezoneOffset() * 60000
var exactDate = new Date(date.valueOf() - timeOffset)
return exactDate
}
// -----------------------------------------------------------------------------
function formatSize (size) {
@ -788,4 +806,4 @@ function printObject(o) {
out += p + ': ' + o[p] + '\n';
}
return out;
}
}

View file

@ -140,11 +140,12 @@ Window {
ActionButton {
isCustom: true
backgroundRadius: 4
colorSet: CallsWindowStyle.callsList.newConference
colorSet: CallsWindowStyle.callsList.mergeConference
visible: SettingsModel.conferenceEnabled
enabled: CallsListModel.canMergeCalls
onClicked: {
Logic.openConferenceManager()
CallsListModel.mergeAll()
}
}
}

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