diff --git a/linphone-app/CMakeLists.txt b/linphone-app/CMakeLists.txt
index 1e13d963f..9924517dd 100644
--- a/linphone-app/CMakeLists.txt
+++ b/linphone-app/CMakeLists.txt
@@ -122,7 +122,10 @@ set(SOURCES
src/components/calls/CallsListProxyModel.cpp
src/components/camera/Camera.cpp
src/components/camera/CameraPreview.cpp
- src/components/chat-message/ChatMessageModel.cpp
+ src/components/chat-events/ChatCallModel.cpp
+ src/components/chat-events/ChatEvent.cpp
+ src/components/chat-events/ChatMessageModel.cpp
+ src/components/chat-events/ChatNoticeModel.cpp
src/components/chat-room/ChatRoomModel.cpp
src/components/chat-room/ChatRoomListModel.cpp
src/components/chat-room/ChatRoomProxyModel.cpp
@@ -161,17 +164,21 @@ set(SOURCES
src/components/participant/ParticipantDeviceModel.cpp
src/components/participant/ParticipantDeviceListModel.cpp
src/components/participant/ParticipantDeviceProxyModel.cpp
+ src/components/participant-imdn/ParticipantImdnStateModel.cpp
+ src/components/participant-imdn/ParticipantImdnStateListModel.cpp
+ src/components/participant-imdn/ParticipantImdnStateProxyModel.cpp
src/components/presence/OwnPresenceModel.cpp
src/components/presence/Presence.cpp
src/components/search/SearchHandler.cpp
+ src/components/search/SearchResultModel.cpp
+ src/components/search/SearchSipAddressesModel.cpp
+ src/components/search/SearchSipAddressesProxyModel.cpp
src/components/settings/AccountSettingsModel.cpp
src/components/settings/SettingsModel.cpp
src/components/sip-addresses/SipAddressesModel.cpp
src/components/sip-addresses/SipAddressesProxyModel.cpp
src/components/sip-addresses/SipAddressesSorter.cpp
src/components/sip-addresses/SipAddressObserver.cpp
- src/components/sip-addresses/SearchSipAddressesModel.cpp
- src/components/sip-addresses/SearchSipAddressesProxyModel.cpp
src/components/sound-player/SoundPlayer.cpp
src/components/telephone-numbers/TelephoneNumbersModel.cpp
src/components/timeline/TimelineModel.cpp
@@ -182,7 +189,7 @@ set(SOURCES
src/utils/MediastreamerUtils.cpp
src/utils/QExifImageHeader.cpp
src/utils/Utils.cpp
- src/utils/Tools.cpp
+ src/utils/hacks/ChatRoomInitializer.cpp
src/utils/plugins/PluginsManager.cpp
)
set(PLUGIN_SOURCES src/utils/plugins/PluginDataAPI.cpp
@@ -209,7 +216,10 @@ set(HEADERS
src/components/calls/CallsListProxyModel.hpp
src/components/camera/Camera.hpp
src/components/camera/CameraPreview.hpp
- src/components/chat-message/ChatMessageModel.hpp
+ src/components/chat-events/ChatCallModel.hpp
+ src/components/chat-events/ChatEvent.hpp
+ src/components/chat-events/ChatMessageModel.hpp
+ src/components/chat-events/ChatNoticeModel.hpp
src/components/chat-room/ChatRoomModel.hpp
src/components/chat-room/ChatRoomListModel.hpp
src/components/chat-room/ChatRoomProxyModel.hpp
@@ -250,17 +260,21 @@ set(HEADERS
src/components/participant/ParticipantDeviceModel.hpp
src/components/participant/ParticipantDeviceListModel.hpp
src/components/participant/ParticipantDeviceProxyModel.hpp
+ src/components/participant-imdn/ParticipantImdnStateModel.hpp
+ src/components/participant-imdn/ParticipantImdnStateListModel.cpp
+ src/components/participant-imdn/ParticipantImdnStateProxyModel.cpp
src/components/presence/OwnPresenceModel.hpp
src/components/presence/Presence.hpp
src/components/search/SearchHandler.hpp
+ src/components/search/SearchResultModel.hpp
+ src/components/search/SearchSipAddressesModel.hpp
+ src/components/search/SearchSipAddressesProxyModel.hpp
src/components/settings/AccountSettingsModel.hpp
src/components/settings/SettingsModel.hpp
src/components/sip-addresses/SipAddressesModel.hpp
src/components/sip-addresses/SipAddressesProxyModel.hpp
src/components/sip-addresses/SipAddressesSorter.hpp
src/components/sip-addresses/SipAddressObserver.hpp
- src/components/sip-addresses/SearchSipAddressesModel.hpp
- src/components/sip-addresses/SearchSipAddressesProxyModel.hpp
src/components/sound-player/SoundPlayer.hpp
src/components/telephone-numbers/TelephoneNumbersModel.hpp
src/components/timeline/TimelineModel.hpp
@@ -271,7 +285,7 @@ set(HEADERS
src/utils/MediastreamerUtils.hpp
src/utils/QExifImageHeader.hpp
src/utils/Utils.hpp
- src/utils/Tools.hpp
+ src/utils/hacks/ChatRoomInitializer.hpp
src/utils/plugins/PluginsManager.hpp
)
set(PLUGIN_HEADERS
diff --git a/linphone-app/assets/images/chat_micro.svg b/linphone-app/assets/images/chat_micro.svg
new file mode 100644
index 000000000..7bd118b07
--- /dev/null
+++ b/linphone-app/assets/images/chat_micro.svg
@@ -0,0 +1,54 @@
+
+
diff --git a/linphone-app/assets/images/chat_room.svg b/linphone-app/assets/images/chat_room.svg
index 4dae42a49..1973fa81c 100644
--- a/linphone-app/assets/images/chat_room.svg
+++ b/linphone-app/assets/images/chat_room.svg
@@ -1,23 +1,18 @@
-
-
diff --git a/linphone-app/assets/images/contact_disabled.svg b/linphone-app/assets/images/contact_disabled.svg
new file mode 100644
index 000000000..f7197ac04
--- /dev/null
+++ b/linphone-app/assets/images/contact_disabled.svg
@@ -0,0 +1,12 @@
+
+
\ No newline at end of file
diff --git a/linphone-app/assets/images/contact_normal.svg b/linphone-app/assets/images/contact_normal.svg
index f7197ac04..f0a637c0b 100644
--- a/linphone-app/assets/images/contact_normal.svg
+++ b/linphone-app/assets/images/contact_normal.svg
@@ -1,11 +1,11 @@
diff --git a/linphone-app/assets/images/menu_infos2.svg b/linphone-app/assets/images/menu_infos2.svg
index fbc563352..8cc59adc3 100644
--- a/linphone-app/assets/images/menu_infos2.svg
+++ b/linphone-app/assets/images/menu_infos2.svg
@@ -1,11 +1,11 @@
+ inkscape:current-layer="svg12"
+ width="15px" />
+ id="g10"
+ transform="matrix(0.17647011,0,0,0.17647155,-0.08823505,-0.08823577)">
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
diff --git a/linphone-app/assets/images/menu_vdots_normal.svg b/linphone-app/assets/images/menu_vdots_normal.svg
index f9cb5f9c0..7aed15fa0 100644
--- a/linphone-app/assets/images/menu_vdots_normal.svg
+++ b/linphone-app/assets/images/menu_vdots_normal.svg
@@ -1,13 +1,54 @@
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
diff --git a/linphone-app/assets/images/menu_vdots_pressed.svg b/linphone-app/assets/images/menu_vdots_pressed.svg
index f9cb5f9c0..7aed15fa0 100644
--- a/linphone-app/assets/images/menu_vdots_pressed.svg
+++ b/linphone-app/assets/images/menu_vdots_pressed.svg
@@ -1,13 +1,54 @@
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
diff --git a/linphone-app/assets/images/new_chat_group_hovered.svg b/linphone-app/assets/images/new_chat_group_hovered.svg
index 6c2a25926..578548f11 100644
--- a/linphone-app/assets/images/new_chat_group_hovered.svg
+++ b/linphone-app/assets/images/new_chat_group_hovered.svg
@@ -1,23 +1,18 @@
-
-
-
+ inkscape:current-layer="svg21" />
+
+
+
+
+
-
+ fill="none"
+ fill-rule="evenodd"
+ id="g19">
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/linphone-app/assets/images/new_chat_group_normal.svg b/linphone-app/assets/images/new_chat_group_normal.svg
index 1ec88f1b0..33edd0b0d 100644
--- a/linphone-app/assets/images/new_chat_group_normal.svg
+++ b/linphone-app/assets/images/new_chat_group_normal.svg
@@ -1,23 +1,18 @@
-
-
-
+ inkscape:current-layer="svg21" />
+
+
+
+
+
-
+ fill="none"
+ fill-rule="evenodd"
+ id="g19">
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/linphone-app/assets/images/new_chat_group_pressed.svg b/linphone-app/assets/images/new_chat_group_pressed.svg
index 85680b043..c4fce2792 100644
--- a/linphone-app/assets/images/new_chat_group_pressed.svg
+++ b/linphone-app/assets/images/new_chat_group_pressed.svg
@@ -1,23 +1,18 @@
-
-
-
+ inkscape:current-layer="svg23" />
+
+
+
+
+
-
+ fill="none"
+ fill-rule="evenodd"
+ id="g21">
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/linphone-app/assets/images/new_conference_disabled.svg b/linphone-app/assets/images/new_conference_disabled.svg
index 9ad2da828..5076f48fe 100644
--- a/linphone-app/assets/images/new_conference_disabled.svg
+++ b/linphone-app/assets/images/new_conference_disabled.svg
@@ -1,23 +1,18 @@
-
-
-
+ inkscape:current-layer="svg29" />
+
+
+
+
+
-
+ fill="none"
+ fill-rule="evenodd"
+ id="g27">
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/linphone-app/assets/images/new_conference_hovered.svg b/linphone-app/assets/images/new_conference_hovered.svg
index 83b653b07..eb909735e 100644
--- a/linphone-app/assets/images/new_conference_hovered.svg
+++ b/linphone-app/assets/images/new_conference_hovered.svg
@@ -1,23 +1,18 @@
-
-
-
+ inkscape:current-layer="svg29" />
+
+
+
+
+
-
+ fill="none"
+ fill-rule="evenodd"
+ id="g27">
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/linphone-app/assets/images/new_conference_normal.svg b/linphone-app/assets/images/new_conference_normal.svg
index 757af6e8f..b2f19248e 100644
--- a/linphone-app/assets/images/new_conference_normal.svg
+++ b/linphone-app/assets/images/new_conference_normal.svg
@@ -1,23 +1,18 @@
-
-
-
+ inkscape:current-layer="svg27" />
+
+
+
+
+
-
+ fill="none"
+ fill-rule="evenodd"
+ id="g25">
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/linphone-app/assets/images/new_conference_pressed.svg b/linphone-app/assets/images/new_conference_pressed.svg
index dc9f2cf75..0274bed8e 100644
--- a/linphone-app/assets/images/new_conference_pressed.svg
+++ b/linphone-app/assets/images/new_conference_pressed.svg
@@ -1,23 +1,18 @@
-
-
-
+ inkscape:current-layer="svg29" />
+
+
+
+
+
-
+ fill="none"
+ fill-rule="evenodd"
+ id="g27">
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/linphone-app/assets/images/panel_arrow.svg b/linphone-app/assets/images/panel_arrow.svg
new file mode 100644
index 000000000..9c3c6d21e
--- /dev/null
+++ b/linphone-app/assets/images/panel_arrow.svg
@@ -0,0 +1,55 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/linphone-app/assets/images/remove_normal.svg b/linphone-app/assets/images/remove_normal.svg
deleted file mode 100644
index 97cc19996..000000000
--- a/linphone-app/assets/images/remove_normal.svg
+++ /dev/null
@@ -1,50 +0,0 @@
-
-
-
-
-
-
-
-
-
-
diff --git a/linphone-app/assets/images/secure_level_1.svg b/linphone-app/assets/images/secure_level_1.svg
index 7e9adcd94..1dae93a42 100644
--- a/linphone-app/assets/images/secure_level_1.svg
+++ b/linphone-app/assets/images/secure_level_1.svg
@@ -1,8 +1,8 @@
+ inkscape:current-layer="svg16"
+ width="15px" />
+ id="g14"
+ transform="matrix(1.0283369,0,0,1.0153836,0.84203685,-0.00127697)">
diff --git a/linphone-app/assets/images/secure_level_2.svg b/linphone-app/assets/images/secure_level_2.svg
index 3518579a5..72e807ac6 100644
--- a/linphone-app/assets/images/secure_level_2.svg
+++ b/linphone-app/assets/images/secure_level_2.svg
@@ -4,9 +4,9 @@
+ xlink:href="data:image/png;base64,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 "
+ id="image10"
+ x="1"
+ y="0" />
diff --git a/linphone-app/assets/images/secure_level_unsafe.svg b/linphone-app/assets/images/secure_level_unsafe.svg
index 4b6767918..e7fde50aa 100644
--- a/linphone-app/assets/images/secure_level_unsafe.svg
+++ b/linphone-app/assets/images/secure_level_unsafe.svg
@@ -4,9 +4,9 @@
+ inkscape:current-layer="g8"
+ width="15px" />
+ xlink:href="data:image/png;base64,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 "
+ id="image10"
+ x="1"
+ y="0" />
diff --git a/linphone-app/assets/images/secure_off.svg b/linphone-app/assets/images/secure_off.svg
index 326ea4b46..510a937c0 100644
--- a/linphone-app/assets/images/secure_off.svg
+++ b/linphone-app/assets/images/secure_off.svg
@@ -1,8 +1,8 @@
+ inkscape:current-layer="svg18"
+ width="18px" />
+ id="g16"
+ transform="matrix(0.72345975,0,0,0.71432646,0.99978048,-8.5563664e-4)">
diff --git a/linphone-app/assets/images/secure_on.svg b/linphone-app/assets/images/secure_on.svg
index a38470fff..022721fbe 100644
--- a/linphone-app/assets/images/secure_on.svg
+++ b/linphone-app/assets/images/secure_on.svg
@@ -1,8 +1,8 @@
+ inkscape:current-layer="svg14"
+ width="13px" />
+ id="g12"
+ transform="matrix(0.72345975,0,0,0.71432646,0.99978048,-8.5563367e-4)">
@@ -45,8 +47,7 @@
diff --git a/linphone-app/assets/images/timeline_search (copy).svg b/linphone-app/assets/images/timeline_search (copy).svg
deleted file mode 100644
index db2c58d33..000000000
--- a/linphone-app/assets/images/timeline_search (copy).svg
+++ /dev/null
@@ -1,65 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/linphone-app/assets/images/timeline_search.svg b/linphone-app/assets/images/timeline_search.svg
index 61b2cf396..32e842dc7 100644
--- a/linphone-app/assets/images/timeline_search.svg
+++ b/linphone-app/assets/images/timeline_search.svg
@@ -1,18 +1,23 @@
+
+
+
-
-
-
-
-
+ inkscape:current-layer="g8" />
-
-
-
-
-
+ inkscape:groupmode="layer"
+ inkscape:label="Image"
+ id="g8">
+
diff --git a/linphone-app/assets/images/timer.svg b/linphone-app/assets/images/timer.svg
index 6c7f18da1..5d55601c2 100644
--- a/linphone-app/assets/images/timer.svg
+++ b/linphone-app/assets/images/timer.svg
@@ -1,23 +1,20 @@
-
-
+ id="defs16" />
+ inkscape:current-layer="svg12" />
-
+ fill="none"
+ fill-rule="evenodd"
+ id="g10"
+ transform="matrix(0.15097165,0,0,0.15318627,2.0162868,0)">
+
+
+
+
+
diff --git a/linphone-app/resources.qrc b/linphone-app/resources.qrc
index 2fd5c0420..6351569cb 100644
--- a/linphone-app/resources.qrc
+++ b/linphone-app/resources.qrc
@@ -68,6 +68,7 @@
assets/images/chat_is_composing_1.svg
assets/images/chat_is_composing_2.svg
assets/images/chat_is_composing_3.svg
+ assets/images/chat_micro.svg
assets/images/chat_normal.svg
assets/images/chat_pressed.svg
assets/images/chat_read.svg
@@ -88,9 +89,13 @@
assets/images/contact_edit_hovered.svg
assets/images/contact_edit_normal.svg
assets/images/contact_edit_pressed.svg
+ assets/images/contact_disabled.svg
assets/images/contact_normal.svg
assets/images/contact_selected.svg
- assets/images/current_account_status_available.svg
+ assets/images/current_account_status_online.svg
+ assets/images/current_account_status_offline.svg
+ assets/images/current_account_status_dnd.svg
+ assets/images/current_account_status_busy.svg
assets/images/declined_incoming_call.svg
assets/images/declined_outgoing_call.svg
assets/images/delete_hovered.svg
@@ -118,6 +123,9 @@
assets/images/generic_error_normal.svg
assets/images/generic_error_pressed.svg
assets/images/generic_error.svg
+ assets/images/group_chat_hovered.svg
+ assets/images/group_chat_normal.svg
+ assets/images/group_chat_pressed.svg
assets/images/hangup_hovered.svg
assets/images/hangup_normal.svg
assets/images/hangup_pressed.svg
@@ -169,6 +177,7 @@
assets/images/options_normal.svg
assets/images/options_pressed.svg
assets/images/outgoing_call.svg
+ assets/images/panel_arrow.svg
assets/images/panel_hidden_normal.svg
assets/images/panel_hidden_hovered.svg
assets/images/panel_hidden_pressed.svg
@@ -482,6 +491,7 @@
ui/views/App/Main/Dialogs/AuthenticationRequest.qml
ui/views/App/Main/Dialogs/EphemeralChatRoom.qml
ui/views/App/Main/Dialogs/InfoChatRoom.qml
+ ui/views/App/Main/Dialogs/InfoEncryption.qml
ui/views/App/Main/Dialogs/ManageAccount.js
ui/views/App/Main/Dialogs/ManageAccounts.qml
ui/views/App/Main/Dialogs/ManageChatRoom.qml
@@ -531,6 +541,7 @@
ui/views/App/Styles/Main/ConversationStyle.qml
ui/views/App/Styles/Main/Dialogs/AboutStyle.qml
ui/views/App/Styles/Main/Dialogs/AuthenticationRequestStyle.qml
+ ui/views/App/Styles/Main/Dialogs/InfoEncryptionStyle.qml
ui/views/App/Styles/Main/Dialogs/ManageAccountsStyle.qml
ui/views/App/Styles/Main/HomeStyle.qml
ui/views/App/Styles/Main/InviteFriendsStyle.qml
diff --git a/linphone-app/src/app/App.cpp b/linphone-app/src/app/App.cpp
index 71a52fc22..ac6e7ad91 100644
--- a/linphone-app/src/app/App.cpp
+++ b/linphone-app/src/app/App.cpp
@@ -215,6 +215,8 @@ bool App::setFetchConfig (QCommandLineParser *parser) {
}
// -----------------------------------------------------------------------------
+
+
App::App (int &argc, char *argv[]) : SingleApplication(argc, argv, true, Mode::User | Mode::ExcludeAppPath | Mode::ExcludeAppVersion) {
connect(this, SIGNAL(applicationStateChanged(Qt::ApplicationState)), this, SLOT(stateChanged(Qt::ApplicationState)));
@@ -592,8 +594,12 @@ void App::registerTypes () {
qRegisterMetaType>();
qRegisterMetaType>();
qRegisterMetaType>();
+ qRegisterMetaType>();
+ qRegisterMetaType>();
+ qRegisterMetaType>();
+ //qRegisterMetaType>();
LinphoneEnums::registerMetaTypes();
-
+
registerType("AssistantModel");
registerType("AuthenticationNotifier");
registerType("CallsListProxyModel");
@@ -626,13 +632,17 @@ void App::registerTypes () {
registerSingletonType("VideoCodecsModel");
registerUncreatableType("CallModel");
+ registerUncreatableType("ChatCallModel");
registerUncreatableType("ChatMessageModel");
+ registerUncreatableType("ChatNoticeModel");
registerUncreatableType("ChatRoomModel");
registerUncreatableType("ConferenceAddModel");
registerUncreatableType("ContactModel");
registerUncreatableType("ContactsImporterModel");
+ registerUncreatableType("ContentModel");
registerUncreatableType("HistoryModel");
registerUncreatableType("LdapModel");
+ registerUncreatableType("SearchResultModel");
registerUncreatableType("SipAddressObserver");
registerUncreatableType("VcardModel");
registerUncreatableType("TimelineModel");
@@ -641,6 +651,11 @@ void App::registerTypes () {
registerUncreatableType("ParticipantDeviceModel");
registerUncreatableType("ParticipantDeviceListModel");
registerUncreatableType("ParticipantDeviceProxyModel");
+ registerUncreatableType("ParticipantImdnStateModel");
+ registerUncreatableType("ParticipantImdnStateListModel");
+ registerUncreatableType("ParticipantImdnStateProxyModel");
+
+
qmlRegisterUncreatableMetaObject(LinphoneEnums::staticMetaObject, "LinphoneEnums", 1, 0, "LinphoneEnums", "Only enums");
}
@@ -668,7 +683,7 @@ void App::registerToolTypes () {
registerToolType("TextToSpeech");
registerToolType("Units");
registerToolType("ContactsImporterPluginsManager");
- registerToolType("Utils");
+ registerToolType("UtilsCpp");
}
void App::registerSharedToolTypes () {
diff --git a/linphone-app/src/components/Components.hpp b/linphone-app/src/components/Components.hpp
index f4ce82e37..8d626a259 100644
--- a/linphone-app/src/components/Components.hpp
+++ b/linphone-app/src/components/Components.hpp
@@ -28,7 +28,9 @@
#include "calls/CallsListProxyModel.hpp"
#include "camera/Camera.hpp"
#include "camera/CameraPreview.hpp"
-#include "components/chat-message/ChatMessageModel.hpp"
+#include "components/chat-events/ChatCallModel.hpp"
+#include "components/chat-events/ChatMessageModel.hpp"
+#include "components/chat-events/ChatNoticeModel.hpp"
#include "chat-room/ChatRoomProxyModel.hpp"
#include "codecs/AudioCodecsModel.hpp"
#include "codecs/VideoCodecsModel.hpp"
@@ -57,13 +59,17 @@
#include "participant/ParticipantDeviceListModel.hpp"
#include "participant/ParticipantDeviceModel.hpp"
#include "participant/ParticipantDeviceProxyModel.hpp"
+#include "participant-imdn/ParticipantImdnStateModel.hpp"
+#include "participant-imdn/ParticipantImdnStateListModel.hpp"
+#include "participant-imdn/ParticipantImdnStateProxyModel.hpp"
#include "presence/OwnPresenceModel.hpp"
#include "settings/AccountSettingsModel.hpp"
#include "settings/SettingsModel.hpp"
+#include "search/SearchResultModel.hpp"
#include "sip-addresses/SipAddressesModel.hpp"
#include "sip-addresses/SipAddressesProxyModel.hpp"
-#include "sip-addresses/SearchSipAddressesModel.hpp"
-#include "sip-addresses/SearchSipAddressesProxyModel.hpp"
+#include "search/SearchSipAddressesModel.hpp"
+#include "search/SearchSipAddressesProxyModel.hpp"
#include "sound-player/SoundPlayer.hpp"
#include "telephone-numbers/TelephoneNumbersModel.hpp"
#include "timeline/TimelineModel.hpp"
diff --git a/linphone-app/src/components/call/CallModel.cpp b/linphone-app/src/components/call/CallModel.cpp
index daf0bd23e..309286b5b 100644
--- a/linphone-app/src/components/call/CallModel.cpp
+++ b/linphone-app/src/components/call/CallModel.cpp
@@ -26,6 +26,7 @@
#include "app/App.hpp"
#include "components/calls/CallsListModel.hpp"
+#include "components/chat-room/ChatRoomModel.hpp"
#include "components/contact/ContactModel.hpp"
#include "components/contacts/ContactsListModel.hpp"
#include "components/core/CoreHandlers.hpp"
@@ -33,6 +34,7 @@
#include "components/notifier/Notifier.hpp"
#include "components/settings/AccountSettingsModel.hpp"
#include "components/settings/SettingsModel.hpp"
+#include "components/timeline/TimelineListModel.hpp"
#include "utils/MediastreamerUtils.hpp"
#include "utils/Utils.hpp"
@@ -88,6 +90,7 @@ CallModel::CallModel (shared_ptr call){
mRemoteAddress = mCall->getRemoteAddress()->clone();
mMagicSearch->getContactListFromFilterAsync(mRemoteAddress->getUsername(),mRemoteAddress->getDomain());
+ qWarning() << getFullPeerAddress();
}
CallModel::~CallModel () {
@@ -118,6 +121,12 @@ ContactModel *CallModel::getContactModel() const{
return contact;
}
+ChatRoomModel * CallModel::getChatRoomModel() const{
+ if(mCall->getCallLog()->getCallId() != "")
+ return CoreManager::getInstance()->getTimelineListModel()->getChatRoomModel(mCall->getChatRoom(), true).get();
+ else
+ return nullptr;
+}
// -----------------------------------------------------------------------------
void CallModel::setRecordFile (const shared_ptr &callParams) {
diff --git a/linphone-app/src/components/call/CallModel.hpp b/linphone-app/src/components/call/CallModel.hpp
index 92b2ead41..3cf628edb 100644
--- a/linphone-app/src/components/call/CallModel.hpp
+++ b/linphone-app/src/components/call/CallModel.hpp
@@ -27,6 +27,7 @@
// =============================================================================
class ContactModel;
+class ChatRoomModel;
class CallModel : public QObject {
Q_OBJECT;
@@ -36,7 +37,10 @@ class CallModel : public QObject {
Q_PROPERTY(QString fullPeerAddress READ getFullPeerAddress NOTIFY fullPeerAddressChanged);
Q_PROPERTY(QString fullLocalAddress READ getFullLocalAddress CONSTANT);
- Q_PROPERTY(ContactModel *contact READ getContactModel CONSTANT )/*
+ Q_PROPERTY(ContactModel *contactModel READ getContactModel CONSTANT )
+ Q_PROPERTY(ChatRoomModel * chatRoomModel READ getChatRoomModel CONSTANT)
+
+ /*
Q_PROPERTY(QString sipAddress READ getFullPeerAddress NOTIFY fullPeerAddressChanged)
Q_PROPERTY(QString username READ getUsername NOTIFY usernameChanged)
Q_PROPERTY(QString avatar READ getAvatar NOTIFY avatarChanged)
@@ -74,6 +78,8 @@ class CallModel : public QObject {
Q_PROPERTY(float speakerVolumeGain READ getSpeakerVolumeGain WRITE setSpeakerVolumeGain NOTIFY speakerVolumeGainChanged);
Q_PROPERTY(float microVolumeGain READ getMicroVolumeGain WRITE setMicroVolumeGain NOTIFY microVolumeGainChanged);
+
+
public:
enum CallStatus {
@@ -107,6 +113,8 @@ public:
QString getFullLocalAddress () const;
ContactModel *getContactModel() const;
+
+ ChatRoomModel * getChatRoomModel() const;
bool isInConference () const {
return mIsInConference;
diff --git a/linphone-app/src/components/calls/CallsListModel.cpp b/linphone-app/src/components/calls/CallsListModel.cpp
index 51ddae494..3c45a4ef8 100644
--- a/linphone-app/src/components/calls/CallsListModel.cpp
+++ b/linphone-app/src/components/calls/CallsListModel.cpp
@@ -36,209 +36,205 @@
#include "CallsListModel.hpp"
+#include "utils/hacks/ChatRoomInitializer.hpp"
+
// =============================================================================
using namespace std;
namespace {
- // Delay before removing call in ms.
- constexpr int DelayBeforeRemoveCall = 3000;
+// Delay before removing call in ms.
+constexpr int DelayBeforeRemoveCall = 3000;
}
static inline int findCallIndex (QList &list, const shared_ptr &call) {
- auto it = find_if(list.begin(), list.end(), [call](CallModel *callModel) {
- return call == callModel->getCall();
- });
-
- Q_ASSERT(it != list.end());
-
- return int(distance(list.begin(), it));
+ auto it = find_if(list.begin(), list.end(), [call](CallModel *callModel) {
+ return call == callModel->getCall();
+});
+
+ Q_ASSERT(it != list.end());
+
+ return int(distance(list.begin(), it));
}
static inline int findCallIndex (QList &list, const CallModel &callModel) {
- return ::findCallIndex(list, callModel.getCall());
+ return ::findCallIndex(list, callModel.getCall());
}
// -----------------------------------------------------------------------------
CallsListModel::CallsListModel (QObject *parent) : QAbstractListModel(parent) {
- mCoreHandlers = CoreManager::getInstance()->getHandlers();
- QObject::connect(
- mCoreHandlers.get(), &CoreHandlers::callStateChanged,
- this, &CallsListModel::handleCallStateChanged
- );
+ mCoreHandlers = CoreManager::getInstance()->getHandlers();
+ QObject::connect(
+ mCoreHandlers.get(), &CoreHandlers::callStateChanged,
+ this, &CallsListModel::handleCallStateChanged
+ );
}
int CallsListModel::rowCount (const QModelIndex &) const {
- return mList.count();
+ return mList.count();
}
QHash CallsListModel::roleNames () const {
- QHash roles;
- roles[Qt::DisplayRole] = "$call";
- return roles;
+ QHash roles;
+ roles[Qt::DisplayRole] = "$call";
+ return roles;
}
QVariant CallsListModel::data (const QModelIndex &index, int role) const {
- int row = index.row();
-
- if (!index.isValid() || row < 0 || row >= mList.count())
- return QVariant();
-
- if (role == Qt::DisplayRole)
- return QVariant::fromValue(mList[row]);
-
- return QVariant();
+ int row = index.row();
+
+ if (!index.isValid() || row < 0 || row >= mList.count())
+ return QVariant();
+
+ if (role == Qt::DisplayRole)
+ return QVariant::fromValue(mList[row]);
+
+ return QVariant();
}
// -----------------------------------------------------------------------------
void CallsListModel::askForTransfer (CallModel *callModel) {
- emit callTransferAsked(callModel);
+ emit callTransferAsked(callModel);
}
// -----------------------------------------------------------------------------
void CallsListModel::launchAudioCall (const QString &sipAddress, const QHash &headers) const {
- shared_ptr core = CoreManager::getInstance()->getCore();
-
- shared_ptr address = core->interpretUrl(Utils::appStringToCoreString(sipAddress));
- if (!address)
- return;
-
- shared_ptr params = core->createCallParams(nullptr);
- params->enableVideo(false);
-
- QHashIterator iterator(headers);
- while (iterator.hasNext()) {
- iterator.next();
- params->addCustomHeader(Utils::appStringToCoreString(iterator.key()), Utils::appStringToCoreString(iterator.value()));
- }
- params->setProxyConfig(core->getDefaultProxyConfig());
- CallModel::setRecordFile(params, QString::fromStdString(address->getUsername()));
- shared_ptr currentProxyConfig = core->getDefaultProxyConfig();
- if(currentProxyConfig){
- if(currentProxyConfig->getState() == linphone::RegistrationState::Ok)
- core->inviteAddressWithParams(address, params);
- else{
- QObject * context = new QObject();
- QObject::connect(CoreManager::getInstance()->getHandlers().get(), &CoreHandlers::registrationStateChanged,context,
- [address,core,params,currentProxyConfig, context](const std::shared_ptr &proxyConfig, linphone::RegistrationState state) mutable {
- if(context && proxyConfig==currentProxyConfig && state==linphone::RegistrationState::Ok){
- delete context;
- context = nullptr;
- core->inviteAddressWithParams(address, params);
- }
- });
- }
- }else
- core->inviteAddressWithParams(address, params);
+ shared_ptr core = CoreManager::getInstance()->getCore();
+
+ shared_ptr address = core->interpretUrl(Utils::appStringToCoreString(sipAddress));
+ if (!address)
+ return;
+
+ shared_ptr params = core->createCallParams(nullptr);
+ params->enableVideo(false);
+
+ QHashIterator iterator(headers);
+ while (iterator.hasNext()) {
+ iterator.next();
+ params->addCustomHeader(Utils::appStringToCoreString(iterator.key()), Utils::appStringToCoreString(iterator.value()));
+ }
+ params->setProxyConfig(core->getDefaultProxyConfig());
+ CallModel::setRecordFile(params, QString::fromStdString(address->getUsername()));
+ shared_ptr currentProxyConfig = core->getDefaultProxyConfig();
+ if(currentProxyConfig){
+ if(currentProxyConfig->getState() == linphone::RegistrationState::Ok)
+ core->inviteAddressWithParams(address, params);
+ else{
+ QObject * context = new QObject();
+ QObject::connect(CoreManager::getInstance()->getHandlers().get(), &CoreHandlers::registrationStateChanged,context,
+ [address,core,params,currentProxyConfig, context](const std::shared_ptr &proxyConfig, linphone::RegistrationState state) mutable {
+ if(context && proxyConfig==currentProxyConfig && state==linphone::RegistrationState::Ok){
+ delete context;
+ context = nullptr;
+ core->inviteAddressWithParams(address, params);
+ }
+ });
+ }
+ }else
+ core->inviteAddressWithParams(address, params);
}
void CallsListModel::launchSecureAudioCall (const QString &sipAddress, LinphoneEnums::MediaEncryption encryption, const QHash &headers) const {
- shared_ptr core = CoreManager::getInstance()->getCore();
-
- shared_ptr address = core->interpretUrl(Utils::appStringToCoreString(sipAddress));
- if (!address)
- return;
-
- shared_ptr params = core->createCallParams(nullptr);
- params->enableVideo(false);
-
- QHashIterator iterator(headers);
- while (iterator.hasNext()) {
- iterator.next();
- params->addCustomHeader(Utils::appStringToCoreString(iterator.key()), Utils::appStringToCoreString(iterator.value()));
- }
- params->setProxyConfig(core->getDefaultProxyConfig());
- CallModel::setRecordFile(params, QString::fromStdString(address->getUsername()));
- shared_ptr currentProxyConfig = core->getDefaultProxyConfig();
- params->setMediaEncryption(LinphoneEnums::toLinphone(encryption));
- if(currentProxyConfig){
- if(currentProxyConfig->getState() == linphone::RegistrationState::Ok)
- core->inviteAddressWithParams(address, params);
- else{
- QObject * context = new QObject();
- QObject::connect(CoreManager::getInstance()->getHandlers().get(), &CoreHandlers::registrationStateChanged,context,
- [address,core,params,currentProxyConfig, context](const std::shared_ptr &proxyConfig, linphone::RegistrationState state) mutable {
- if(context && proxyConfig==currentProxyConfig && state==linphone::RegistrationState::Ok){
- delete context;
- context = nullptr;
- core->inviteAddressWithParams(address, params);
- }
- });
- }
- }else
- core->inviteAddressWithParams(address, params);
+ shared_ptr core = CoreManager::getInstance()->getCore();
+
+ shared_ptr address = core->interpretUrl(Utils::appStringToCoreString(sipAddress));
+ if (!address)
+ return;
+
+ shared_ptr params = core->createCallParams(nullptr);
+ params->enableVideo(false);
+
+ QHashIterator iterator(headers);
+ while (iterator.hasNext()) {
+ iterator.next();
+ params->addCustomHeader(Utils::appStringToCoreString(iterator.key()), Utils::appStringToCoreString(iterator.value()));
+ }
+ params->setProxyConfig(core->getDefaultProxyConfig());
+ CallModel::setRecordFile(params, QString::fromStdString(address->getUsername()));
+ shared_ptr currentProxyConfig = core->getDefaultProxyConfig();
+ params->setMediaEncryption(LinphoneEnums::toLinphone(encryption));
+ if(currentProxyConfig){
+ if(currentProxyConfig->getState() == linphone::RegistrationState::Ok)
+ core->inviteAddressWithParams(address, params);
+ else{
+ QObject * context = new QObject();
+ QObject::connect(CoreManager::getInstance()->getHandlers().get(), &CoreHandlers::registrationStateChanged,context,
+ [address,core,params,currentProxyConfig, context](const std::shared_ptr &proxyConfig, linphone::RegistrationState state) mutable {
+ if(context && proxyConfig==currentProxyConfig && state==linphone::RegistrationState::Ok){
+ delete context;
+ context = nullptr;
+ core->inviteAddressWithParams(address, params);
+ }
+ });
+ }
+ }else
+ core->inviteAddressWithParams(address, params);
}
void CallsListModel::launchVideoCall (const QString &sipAddress) const {
- shared_ptr core = CoreManager::getInstance()->getCore();
- if (!core->videoSupported()) {
- qWarning() << QStringLiteral("Unable to launch video call. (Video not supported.) Launching audio call...");
- launchAudioCall(sipAddress);
- return;
- }
-
- shared_ptr address = core->interpretUrl(Utils::appStringToCoreString(sipAddress));
- if (!address)
- return;
-
- shared_ptr params = core->createCallParams(nullptr);
- params->enableVideo(true);
- params->setProxyConfig(core->getDefaultProxyConfig());
- CallModel::setRecordFile(params, QString::fromStdString(address->getUsername()));
- core->inviteAddressWithParams(address, params);
+ shared_ptr core = CoreManager::getInstance()->getCore();
+ if (!core->videoSupported()) {
+ qWarning() << QStringLiteral("Unable to launch video call. (Video not supported.) Launching audio call...");
+ launchAudioCall(sipAddress);
+ return;
+ }
+
+ shared_ptr address = core->interpretUrl(Utils::appStringToCoreString(sipAddress));
+ if (!address)
+ return;
+
+ shared_ptr params = core->createCallParams(nullptr);
+ params->enableVideo(true);
+ params->setProxyConfig(core->getDefaultProxyConfig());
+ CallModel::setRecordFile(params, QString::fromStdString(address->getUsername()));
+ core->inviteAddressWithParams(address, params);
}
ChatRoomModel* CallsListModel::launchSecureChat (const QString &sipAddress) const {
- shared_ptr core = CoreManager::getInstance()->getCore();
- shared_ptr address = core->interpretUrl(Utils::appStringToCoreString(sipAddress));
- if (!address)
- return nullptr;
-
- std::shared_ptr params = core->createDefaultChatRoomParams();
- std::list > participants;
- std::shared_ptr localAddress;
- participants.push_back(address);
- auto proxy = core->getDefaultProxyConfig();
- params->enableEncryption(true);
-
- params->setSubject("Dummy Subject");
- params->setBackend(linphone::ChatRoomBackend::FlexisipChat);
- params->setEncryptionBackend(linphone::ChatRoomEncryptionBackend::Lime);
+ shared_ptr core = CoreManager::getInstance()->getCore();
+ shared_ptr address = core->interpretUrl(Utils::appStringToCoreString(sipAddress));
+ if (!address)
+ return nullptr;
+
+ std::shared_ptr params = core->createDefaultChatRoomParams();
+ std::list > participants;
+ std::shared_ptr localAddress;
+ participants.push_back(address);
+ auto proxy = core->getDefaultProxyConfig();
+ params->enableEncryption(true);
+
+ params->setSubject("Dummy Subject");
+ params->setBackend(linphone::ChatRoomBackend::FlexisipChat);
+ params->setEncryptionBackend(linphone::ChatRoomEncryptionBackend::Lime);
+
+ std::shared_ptr chatRoom = core->createChatRoom(params, localAddress, participants);
+ if( chatRoom != nullptr){
+ auto timelineList = CoreManager::getInstance()->getTimelineListModel();
+ timelineList->update();
+ auto timeline = timelineList->getTimeline(chatRoom, false);
+ if(!timeline){
+ timeline = timelineList->getTimeline(chatRoom, true);
+ timelineList->add(timeline);
+ }
+ return timeline->getChatRoomModel();
+ }
+ return nullptr;
+}
- std::shared_ptr chatRoom = core->createChatRoom(params, localAddress, participants);
- /*
- if( chatRoom!=nullptr){
- auto search = core->searchChatRoom(params, localAddress
- , address
- , participants);
- if(search != chatRoom)
- qWarning("toto");
- }
-
-
- return chatRoom!=nullptr;
- */
- if( chatRoom != nullptr){
- auto timelineList = CoreManager::getInstance()->getTimelineListModel();
- timelineList->update();
- auto timeline = timelineList->getTimeline(chatRoom, false);
- if(!timeline){
- timeline = timelineList->getTimeline(chatRoom, true);
- timelineList->add(timeline);
- }
- return timeline->getChatRoomModel();
- }
- return nullptr;
+QVariantMap CallsListModel::launchChat(const QString &sipAddress, const int& securityLevel) const{
+ QVariantList participants;
+ participants << sipAddress;
+ return createChatRoom("", securityLevel, participants);
}
ChatRoomModel* CallsListModel::createChat (const QString &participantAddress) const{
shared_ptr core = CoreManager::getInstance()->getCore();
shared_ptr address = core->interpretUrl(Utils::appStringToCoreString(participantAddress));
if (!address)
- return nullptr;
+ return nullptr;
std::shared_ptr params = core->createDefaultChatRoomParams();
std::list > participants;
@@ -247,7 +243,7 @@ ChatRoomModel* CallsListModel::createChat (const QString &participantAddress) co
auto proxy = core->getDefaultProxyConfig();
params->setBackend(linphone::ChatRoomBackend::Basic);
-
+
std::shared_ptr chatRoom = core->createChatRoom(params, localAddress, participants);
/*
if( chatRoom!=nullptr){
@@ -269,11 +265,19 @@ ChatRoomModel* CallsListModel::createChat (const QString &participantAddress) co
return nullptr;
}
+ChatRoomModel* CallsListModel::createChat (const CallModel * model) const{
+ if(model){
+ return model->getChatRoomModel();
+ }
+
+ return nullptr;
+}
+
bool CallsListModel::createSecureChat (const QString& subject, const QString &participantAddress) const{
shared_ptr core = CoreManager::getInstance()->getCore();
shared_ptr address = core->interpretUrl(Utils::appStringToCoreString(participantAddress));
if (!address)
- return false;
+ return false;
std::shared_ptr params = core->createDefaultChatRoomParams();
std::list > participants;
@@ -286,30 +290,35 @@ bool CallsListModel::createSecureChat (const QString& subject, const QString &pa
params->setBackend(linphone::ChatRoomBackend::FlexisipChat);
params->setEncryptionBackend(linphone::ChatRoomEncryptionBackend::Lime);
params->enableGroup(true);
-
+
std::shared_ptr chatRoom = core->createChatRoom(params, localAddress, participants);
return chatRoom != nullptr;
}
-
-bool CallsListModel::createChatRoom(const QString& subject, const int& securityLevel, const QVariantList& participants) const{
+// Created, timeline that can be used
+QVariantMap CallsListModel::createChatRoom(const QString& subject, const int& securityLevel, const QVariantList& participants) const{
+ QVariantMap result;
shared_ptr core = CoreManager::getInstance()->getCore();
std::shared_ptr chatRoom;
- qWarning() << "Creation of " << subject << " " << securityLevel << " " << participants;
- for(auto p : participants){
- ParticipantModel* pp = p.value();
- qWarning() << pp->getSipAddress() << "=>" << pp->getAdminStatus();
- }
-
-
+ QList< std::shared_ptr> admins;
+ qWarning() << "ChatRoom creation of " << subject << " at " << securityLevel << " security and with " << participants;
std::shared_ptr params = core->createDefaultChatRoomParams();
std::list > chatRoomParticipants;
std::shared_ptr localAddress;
for(auto p : participants){
ParticipantModel* participant = p.value();
- auto address = Utils::interpretUrl(participant->getSipAddress());
+ std::shared_ptr address;
+ if(participant) {
+ address = Utils::interpretUrl(participant->getSipAddress());
+ if(participant->getAdminStatus())
+ admins << address;
+ }else{
+ QString participant = p.toString();
+ if( participant != "")
+ address = Utils::interpretUrl(participant);
+ }
if( address)
- chatRoomParticipants.push_back( address );
+ chatRoomParticipants.push_back( address );
}
auto proxy = core->getDefaultProxyConfig();
params->enableEncryption(securityLevel>0);
@@ -320,34 +329,48 @@ bool CallsListModel::createChatRoom(const QString& subject, const int& securityL
}else
params->setBackend(linphone::ChatRoomBackend::Basic);
params->enableGroup(subject != "");
-
+
if(chatRoomParticipants.size() > 0) {
if(!params->groupEnabled()) {// Chat room is one-one : check if it is already exist with empty or dummy subject
chatRoom = core->searchChatRoom(params, localAddress
- , localAddress
- , chatRoomParticipants);
+ , localAddress
+ , chatRoomParticipants);
params->setSubject(subject != ""?subject.toStdString():"Dummy Subject");
if(!chatRoom)
chatRoom = core->searchChatRoom(params, localAddress
- , localAddress
- , chatRoomParticipants);
+ , localAddress
+ , chatRoomParticipants);
}else
params->setSubject(subject != ""?subject.toStdString():"Dummy Subject");
- if( !chatRoom)
+ if( !chatRoom) {
chatRoom = core->createChatRoom(params, localAddress, chatRoomParticipants);
+ if(chatRoom != nullptr && admins.size() > 0)
+ ChatRoomInitializer::setAdminsAsync(params->getSubject(), params->getBackend(), params->groupEnabled(), admins );
+ }else{
+ if(admins.size() > 0){
+ ChatRoomInitializer::setAdminsSync(chatRoom, admins);
+ }
+ auto timelineList = CoreManager::getInstance()->getTimelineListModel();
+ auto timeline = timelineList->getTimeline(chatRoom, true);
+ QTimer::singleShot(200, [timeline](){// Delay process in order to let GUI time for Timeline building/linking before doing actions
+ timeline->setSelected(true);
+ });
+ result["chatRoomModel"] = QVariant::fromValue(timeline->getChatRoomModel());
+ }
}
- return chatRoom != nullptr;
+ result["created"] = (chatRoom != nullptr);
+ return result;
}
// -----------------------------------------------------------------------------
int CallsListModel::getRunningCallsNumber () const {
- return CoreManager::getInstance()->getCore()->getCallsNb();
+ return CoreManager::getInstance()->getCore()->getCallsNb();
}
void CallsListModel::terminateAllCalls () const {
- CoreManager::getInstance()->getCore()->terminateAllCalls();
+ CoreManager::getInstance()->getCore()->terminateAllCalls();
}
void CallsListModel::terminateCall (const QString& sipAddress) const{
auto coreManager = CoreManager::getInstance();
@@ -368,134 +391,134 @@ void CallsListModel::terminateCall (const QString& sipAddress) const{
// -----------------------------------------------------------------------------
static void joinConference (const shared_ptr &call) {
- if (call->getToHeader("method") != "join-conference")
- return;
-
- shared_ptr core = CoreManager::getInstance()->getCore();
- if (!core->getConference()) {
- qWarning() << QStringLiteral("Not in a conference. => Responding to `join-conference` as a simple call...");
- return;
- }
-
- shared_ptr conference = core->getConference();
- const QString conferenceId = Utils::coreStringToAppString(call->getToHeader("conference-id"));
-
- if (conference->getId() != Utils::appStringToCoreString(conferenceId)) {
- qWarning() << QStringLiteral("Trying to join conference with an invalid conference id: `%1`. Responding as a simple call...")
- .arg(conferenceId);
- return;
- }
- qInfo() << QStringLiteral("Join conference: `%1`.").arg(conferenceId);
-
- ConferenceHelperModel helperModel;
- ConferenceHelperModel::ConferenceAddModel *addModel = helperModel.getConferenceAddModel();
-
- CallModel *callModel = &call->getData("call-model");
- callModel->accept();
- addModel->addToConference(call->getRemoteAddress());
- addModel->update();
+ if (call->getToHeader("method") != "join-conference")
+ return;
+
+ shared_ptr core = CoreManager::getInstance()->getCore();
+ if (!core->getConference()) {
+ qWarning() << QStringLiteral("Not in a conference. => Responding to `join-conference` as a simple call...");
+ return;
+ }
+
+ shared_ptr conference = core->getConference();
+ const QString conferenceId = Utils::coreStringToAppString(call->getToHeader("conference-id"));
+
+ if (conference->getId() != Utils::appStringToCoreString(conferenceId)) {
+ qWarning() << QStringLiteral("Trying to join conference with an invalid conference id: `%1`. Responding as a simple call...")
+ .arg(conferenceId);
+ return;
+ }
+ qInfo() << QStringLiteral("Join conference: `%1`.").arg(conferenceId);
+
+ ConferenceHelperModel helperModel;
+ ConferenceHelperModel::ConferenceAddModel *addModel = helperModel.getConferenceAddModel();
+
+ CallModel *callModel = &call->getData("call-model");
+ callModel->accept();
+ addModel->addToConference(call->getRemoteAddress());
+ addModel->update();
}
void CallsListModel::handleCallStateChanged (const shared_ptr &call, linphone::Call::State state) {
- switch (state) {
- case linphone::Call::State::IncomingReceived:
- addCall(call);
- joinConference(call);
- break;
-
- case linphone::Call::State::OutgoingInit:
- addCall(call);
- break;
-
- case linphone::Call::State::End:
- case linphone::Call::State::Error:
- if (call->getCallLog()->getStatus() == linphone::Call::Status::Missed)
- emit callMissed(&call->getData("call-model"));
- removeCall(call);
- break;
-
- case linphone::Call::State::StreamsRunning: {
- int index = findCallIndex(mList, call);
- emit callRunning(index, &call->getData("call-model"));
- } break;
-
- default:
- break;
- }
+ switch (state) {
+ case linphone::Call::State::IncomingReceived:
+ addCall(call);
+ joinConference(call);
+ break;
+
+ case linphone::Call::State::OutgoingInit:
+ addCall(call);
+ break;
+
+ case linphone::Call::State::End:
+ case linphone::Call::State::Error:
+ if (call->getCallLog()->getStatus() == linphone::Call::Status::Missed)
+ emit callMissed(&call->getData("call-model"));
+ removeCall(call);
+ break;
+
+ case linphone::Call::State::StreamsRunning: {
+ int index = findCallIndex(mList, call);
+ emit callRunning(index, &call->getData("call-model"));
+ } break;
+
+ default:
+ break;
+ }
}
bool CallsListModel::removeRow (int row, const QModelIndex &parent) {
- return removeRows(row, 1, parent);
+ return removeRows(row, 1, parent);
}
bool CallsListModel::removeRows (int row, int count, const QModelIndex &parent) {
- int limit = row + count - 1;
-
- if (row < 0 || count < 0 || limit >= mList.count())
- return false;
-
- beginRemoveRows(parent, row, limit);
-
- for (int i = 0; i < count; ++i)
- mList.takeAt(row)->deleteLater();
-
- endRemoveRows();
-
- return true;
+ int limit = row + count - 1;
+
+ if (row < 0 || count < 0 || limit >= mList.count())
+ return false;
+
+ beginRemoveRows(parent, row, limit);
+
+ for (int i = 0; i < count; ++i)
+ mList.takeAt(row)->deleteLater();
+
+ endRemoveRows();
+
+ return true;
}
// -----------------------------------------------------------------------------
void CallsListModel::addCall (const shared_ptr &call) {
- if (call->getDir() == linphone::Call::Dir::Outgoing) {
- QQuickWindow *callsWindow = App::getInstance()->getCallsWindow();
- if (callsWindow) {
- if (CoreManager::getInstance()->getSettingsModel()->getKeepCallsWindowInBackground()) {
- if (!callsWindow->isVisible())
- callsWindow->showMinimized();
- } else
- App::smartShowWindow(callsWindow);
- }
- }
-
- CallModel *callModel = new CallModel(call);
- qInfo() << QStringLiteral("Add call:") << callModel->getFullLocalAddress() << callModel->getFullPeerAddress();
- App::getInstance()->getEngine()->setObjectOwnership(callModel, QQmlEngine::CppOwnership);
-
- // This connection is (only) useful for `CallsListProxyModel`.
- QObject::connect(callModel, &CallModel::isInConferenceChanged, this, [this, callModel](bool) {
- int id = findCallIndex(mList, *callModel);
- emit dataChanged(index(id, 0), index(id, 0));
- });
-
- int row = mList.count();
-
- beginInsertRows(QModelIndex(), row, row);
- mList << callModel;
- endInsertRows();
+ if (call->getDir() == linphone::Call::Dir::Outgoing) {
+ QQuickWindow *callsWindow = App::getInstance()->getCallsWindow();
+ if (callsWindow) {
+ if (CoreManager::getInstance()->getSettingsModel()->getKeepCallsWindowInBackground()) {
+ if (!callsWindow->isVisible())
+ callsWindow->showMinimized();
+ } else
+ App::smartShowWindow(callsWindow);
+ }
+ }
+
+ CallModel *callModel = new CallModel(call);
+ qInfo() << QStringLiteral("Add call:") << callModel->getFullLocalAddress() << callModel->getFullPeerAddress();
+ App::getInstance()->getEngine()->setObjectOwnership(callModel, QQmlEngine::CppOwnership);
+
+ // This connection is (only) useful for `CallsListProxyModel`.
+ QObject::connect(callModel, &CallModel::isInConferenceChanged, this, [this, callModel](bool) {
+ int id = findCallIndex(mList, *callModel);
+ emit dataChanged(index(id, 0), index(id, 0));
+ });
+
+ int row = mList.count();
+
+ beginInsertRows(QModelIndex(), row, row);
+ mList << callModel;
+ endInsertRows();
}
void CallsListModel::removeCall (const shared_ptr &call) {
- CallModel *callModel;
-
- try {
- callModel = &call->getData("call-model");
- } catch (const out_of_range &) {
- // The call model not exists because the linphone call state
- // `CallStateIncomingReceived`/`CallStateOutgoingInit` was not notified.
- qWarning() << QStringLiteral("Unable to find call:") << call.get();
- return;
- }
-
- QTimer::singleShot(DelayBeforeRemoveCall, this, [this, callModel] {
- removeCallCb(callModel);
- });
+ CallModel *callModel;
+
+ try {
+ callModel = &call->getData("call-model");
+ } catch (const out_of_range &) {
+ // The call model not exists because the linphone call state
+ // `CallStateIncomingReceived`/`CallStateOutgoingInit` was not notified.
+ qWarning() << QStringLiteral("Unable to find call:") << call.get();
+ return;
+ }
+
+ QTimer::singleShot(DelayBeforeRemoveCall, this, [this, callModel] {
+ removeCallCb(callModel);
+ });
}
void CallsListModel::removeCallCb (CallModel *callModel) {
- qInfo() << QStringLiteral("Removing call:") << callModel;
-
- int index = mList.indexOf(callModel);
- if (index == -1 || !removeRow(index))
- qWarning() << QStringLiteral("Unable to remove call:") << callModel;
+ qInfo() << QStringLiteral("Removing call:") << callModel;
+
+ int index = mList.indexOf(callModel);
+ if (index == -1 || !removeRow(index))
+ qWarning() << QStringLiteral("Unable to remove call:") << callModel;
}
diff --git a/linphone-app/src/components/calls/CallsListModel.hpp b/linphone-app/src/components/calls/CallsListModel.hpp
index 441f7c1bd..8feaaf013 100644
--- a/linphone-app/src/components/calls/CallsListModel.hpp
+++ b/linphone-app/src/components/calls/CallsListModel.hpp
@@ -49,10 +49,12 @@ public:
Q_INVOKABLE void launchSecureAudioCall (const QString &sipAddress, LinphoneEnums::MediaEncryption encryption, const QHash &headers = {}) const;
Q_INVOKABLE void launchVideoCall (const QString &sipAddress) const;
Q_INVOKABLE ChatRoomModel* launchSecureChat (const QString &sipAddress) const;
+ Q_INVOKABLE QVariantMap launchChat(const QString &sipAddress, const int& securityLevel) const;
Q_INVOKABLE ChatRoomModel* createChat (const QString &participantAddress) const;
+ Q_INVOKABLE ChatRoomModel* createChat (const CallModel * ) const;
Q_INVOKABLE bool createSecureChat (const QString& subject, const QString &participantAddress) const;
- Q_INVOKABLE bool createChatRoom(const QString& subject, const int& securityLevel, const QVariantList& participants) const;
+ Q_INVOKABLE QVariantMap createChatRoom(const QString& subject, const int& securityLevel, const QVariantList& participants) const;
Q_INVOKABLE int getRunningCallsNumber () const;
diff --git a/linphone-app/src/components/chat-events/ChatCallModel.cpp b/linphone-app/src/components/chat-events/ChatCallModel.cpp
new file mode 100644
index 000000000..6ea663675
--- /dev/null
+++ b/linphone-app/src/components/chat-events/ChatCallModel.cpp
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2021 Belledonne Communications SARL.
+ *
+ * This file is part of linphone-desktop
+ * (see https://www.linphone.org).
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#include
+
+#include "app/App.hpp"
+
+#include "ChatCallModel.hpp"
+
+// =============================================================================
+
+ChatCallModel::ChatCallModel ( std::shared_ptr callLog, const bool& isStart, QObject * parent) : QObject(parent), ChatEvent(ChatRoomModel::EntryType::CallEntry) {
+ App::getInstance()->getEngine()->setObjectOwnership(this, QQmlEngine::CppOwnership);// Avoid QML to destroy it when passing by Q_INVOKABLE
+ mCallLog = callLog;
+ if(isStart){
+ mTimestamp = QDateTime::fromMSecsSinceEpoch(callLog->getStartDate() * 1000);
+ setIsStart(true);
+ }else{
+ mTimestamp = QDateTime::fromMSecsSinceEpoch((callLog->getStartDate() + callLog->getDuration()) * 1000);
+ setIsStart(false);
+ }
+}
+
+ChatCallModel::~ChatCallModel(){
+}
+
+std::shared_ptr ChatCallModel::create(std::shared_ptr callLog, const bool& isStart, QObject * parent){
+ auto model = std::make_shared(callLog, isStart, parent);
+ if(model && model->update()){
+ model->mSelf = model;
+ return model;
+ }else
+ return nullptr;
+}
+
+
+std::shared_ptr ChatCallModel::getCallLog(){
+ return mCallLog;
+}
+//--------------------------------------------------------------------------------------------------------------------------
+void ChatCallModel::setIsStart(const bool& data){
+ if(data != mIsStart) {
+ mIsStart = data;
+ emit isStartChanged();
+ }
+}
+void ChatCallModel::setStatus(const LinphoneEnums::CallStatus& data){
+ if(data != mStatus) {
+ mStatus = data;
+ emit statusChanged();
+ }
+}
+void ChatCallModel::setIsOutgoing(const bool& data){
+ if(data != mIsOutgoing) {
+ mIsOutgoing = data;
+ emit isOutgoingChanged();
+ }
+}
+
+
+bool ChatCallModel::update(){
+ setIsOutgoing(mCallLog->getDir() == linphone::Call::Dir::Outgoing);
+ setStatus(LinphoneEnums::fromLinphone(mCallLog->getStatus()));
+}
\ No newline at end of file
diff --git a/linphone-app/src/components/chat-events/ChatCallModel.hpp b/linphone-app/src/components/chat-events/ChatCallModel.hpp
new file mode 100644
index 000000000..d3b648900
--- /dev/null
+++ b/linphone-app/src/components/chat-events/ChatCallModel.hpp
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 2021 Belledonne Communications SARL.
+ *
+ * This file is part of linphone-desktop
+ * (see https://www.linphone.org).
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#ifndef CHAT_CALL_MODEL_H
+#define CHAT_CALL_MODEL_H
+
+#include "utils/LinphoneEnums.hpp"
+#include "ChatEvent.hpp"
+
+// =============================================================================
+
+
+class ChatCallModel : public QObject, public ChatEvent {
+ Q_OBJECT
+
+public:
+ static std::shared_ptr create(std::shared_ptr chatLog, const bool& isStart, QObject * parent = nullptr);// Call it instead constructor
+ ChatCallModel (std::shared_ptr eventLog, const bool& isStart, QObject * parent = nullptr);
+ virtual ~ChatCallModel();
+
+ Q_PROPERTY(ChatRoomModel::EntryType type MEMBER mType CONSTANT)
+ Q_PROPERTY(QDateTime timestamp MEMBER mTimestamp CONSTANT)
+
+ Q_PROPERTY(bool isStart MEMBER mIsStart WRITE setIsStart NOTIFY isStartChanged)
+ Q_PROPERTY(LinphoneEnums::CallStatus status MEMBER mStatus WRITE setStatus NOTIFY statusChanged)
+ Q_PROPERTY(bool isOutgoing MEMBER mIsOutgoing WRITE setIsOutgoing NOTIFY isOutgoingChanged)
+
+ std::shared_ptr getCallLog();
+
+ void setIsStart(const bool& isStart);
+ void setStatus(const LinphoneEnums::CallStatus& status);
+ void setIsOutgoing(const bool& isOutgoing);
+
+ bool update();
+
+ bool mIsStart;
+ LinphoneEnums::CallStatus mStatus;
+ bool mIsOutgoing;
+signals:
+ void isStartChanged();
+ void statusChanged();
+ void isOutgoingChanged();
+
+private:
+ std::shared_ptr mCallLog;
+ std::weak_ptr mSelf; // Used to pass to functions that need a shared_ptr
+};
+
+Q_DECLARE_METATYPE(std::shared_ptr)
+Q_DECLARE_METATYPE(ChatCallModel*)
+#endif
diff --git a/linphone-app/src/components/chat-events/ChatEvent.cpp b/linphone-app/src/components/chat-events/ChatEvent.cpp
new file mode 100644
index 000000000..c03571fb3
--- /dev/null
+++ b/linphone-app/src/components/chat-events/ChatEvent.cpp
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2021 Belledonne Communications SARL.
+ *
+ * This file is part of linphone-desktop
+ * (see https://www.linphone.org).
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#include
+
+#include "app/App.hpp"
+
+#include "ChatEvent.hpp"
+
+// =============================================================================
+
+ChatEvent::ChatEvent (ChatRoomModel::EntryType type){
+ mType = type;
+}
+ChatEvent::~ChatEvent(){
+}
+
+void ChatEvent::deleteEvent(){
+}
\ No newline at end of file
diff --git a/linphone-app/src/components/chat-events/ChatEvent.hpp b/linphone-app/src/components/chat-events/ChatEvent.hpp
new file mode 100644
index 000000000..50677676d
--- /dev/null
+++ b/linphone-app/src/components/chat-events/ChatEvent.hpp
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2021 Belledonne Communications SARL.
+ *
+ * This file is part of linphone-desktop
+ * (see https://www.linphone.org).
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#ifndef CHAT_EVENT_H
+#define CHAT_EVENT_H
+
+#include "components/chat-room/ChatRoomModel.hpp"
+
+// =============================================================================
+
+
+class ChatEvent{
+public:
+ ChatEvent (ChatRoomModel::EntryType type);
+ virtual ~ChatEvent();
+ ChatRoomModel::EntryType mType;
+ QDateTime mTimestamp;
+
+ virtual void deleteEvent();
+};
+Q_DECLARE_METATYPE(ChatEvent*)
+#endif
diff --git a/linphone-app/src/components/chat-events/ChatMessageModel.cpp b/linphone-app/src/components/chat-events/ChatMessageModel.cpp
new file mode 100644
index 000000000..0d2b18a83
--- /dev/null
+++ b/linphone-app/src/components/chat-events/ChatMessageModel.cpp
@@ -0,0 +1,534 @@
+/*
+ * Copyright (c) 2021 Belledonne Communications SARL.
+ *
+ * This file is part of linphone-desktop
+ * (see https://www.linphone.org).
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+#include "ChatMessageModel.hpp"
+
+
+#include
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "app/App.hpp"
+#include "app/paths/Paths.hpp"
+#include "components/contact/ContactModel.hpp"
+#include "components/contacts/ContactsListModel.hpp"
+#include "components/core/CoreManager.hpp"
+#include "app/providers/ThumbnailProvider.hpp"
+#include "components/notifier/Notifier.hpp"
+#include "components/participant-imdn/ParticipantImdnStateListModel.hpp"
+#include "components/participant-imdn/ParticipantImdnStateProxyModel.hpp"
+#include "components/settings/SettingsModel.hpp"
+#include "utils/QExifImageHeader.hpp"
+#include "utils/Utils.hpp"
+
+// =============================================================================
+namespace {
+constexpr int ThumbnailImageFileWidth = 100;
+constexpr int ThumbnailImageFileHeight = 100;
+
+// In Bytes.
+constexpr qint64 FileSizeLimit = 524288000;
+}
+/*
+std::shared_ptr ChatMessageModel::ChatMessageListener::create(ChatMessageModel * model, std::shared_ptr chatMessage, QObject * parent){// Call it instead constructor
+ auto listener = std::shared_ptr(new ChatMessageModel::ChatMessageListener::ChatMessageListener(model,chatMessage, parent), [model](ChatMessageModel::ChatMessageListener::ChatMessageListener * listener){
+ chatMessage->removeListener(model->getHandler());
+ });
+ chatMessage->addListener(listener);
+ return model;
+}
+
+ChatMessageModel::ChatMessageListener::ChatMessageListener(ChatMessageModel * model, std::shared_ptr chatMessage, QObject * parent){
+ connect(this, &ChatMessageModel::ChatMessageListener::onFileTransferSend, model, ChatMessageModel::onFileTransferSend);
+ connect(this, &ChatMessageModel::ChatMessageListener::onFileTransferProgressIndication, model, ChatMessageModel::onFileTransferProgressIndication);
+ connect(this, &ChatMessageModel::ChatMessageListener::onMsgStateChanged, model, ChatMessageModel::onMsgStateChanged);
+}
+ChatMessageModel::ChatMessageListener::~ChatMessageListener(){
+
+}
+*/
+
+// Warning : isFileTransfer/isFile/getpath cannot be used for Content that comes from linphone::ChatMessage::getContents(). That lead to a crash.
+// in SDK there is this note : return c->isFile(); // TODO FIXME this doesn't work when Content is from linphone_chat_message_get_contents() list
+ContentModel::ContentModel(ChatMessageModel* chatModel){
+ App::getInstance()->getEngine()->setObjectOwnership(this, QQmlEngine::CppOwnership);// Avoid QML to destroy it when passing by Q_INVOKABLE
+ mChatMessageModel = chatModel;
+ mWasDownloaded = false;
+ mFileOffset = 0;
+}
+ContentModel::ContentModel(std::shared_ptr content, ChatMessageModel* chatModel){
+ App::getInstance()->getEngine()->setObjectOwnership(this, QQmlEngine::CppOwnership);// Avoid QML to destroy it when passing by Q_INVOKABLE
+ mChatMessageModel = chatModel;
+ mWasDownloaded = false;
+ mFileOffset = 0;
+ setContent(content);
+}
+std::shared_ptr ContentModel::getContent()const{
+ return mContent;
+}
+
+quint64 ContentModel::getFileSize() const{
+ auto s = mContent->getFileSize();
+ return (quint64)s;
+}
+
+QString ContentModel::getName() const{
+ return QString::fromStdString(mContent->getName());
+}
+
+QString ContentModel::getThumbnail() const{
+ return mThumbnail;
+}
+
+
+void ContentModel::setFileOffset(quint64 fileOffset){
+ if( mFileOffset != fileOffset) {
+ mFileOffset = fileOffset;
+ emit fileOffsetChanged();
+ }
+}
+void ContentModel::setThumbnail(const QString& data){
+ if( mThumbnail != data) {
+ mThumbnail = data;
+ emit thumbnailChanged();
+ }
+}
+void ContentModel::setWasDownloaded(bool wasDownloaded){
+ if( mWasDownloaded != wasDownloaded) {
+ mWasDownloaded = wasDownloaded;
+ emit wasDownloadedChanged();
+ }
+}
+
+void ContentModel::setContent(std::shared_ptr content){
+ mContent = content;
+ auto chatMessageFileContentModel = mChatMessageModel->getFileContentModel();
+ if(chatMessageFileContentModel && chatMessageFileContentModel->getContent() == content){
+ QString path = Utils::coreStringToAppString(mContent->getFilePath());
+ if (!path.isEmpty() && (mChatMessageModel->isOutgoing() ||
+ mChatMessageModel->getState() == LinphoneEnums::ChatMessageStateDisplayed))
+ createThumbnail();
+ }
+}
+
+// Create a thumbnail from the first content that have a file and store it in Appdata
+void ContentModel::createThumbnail () {
+ //if (!getChatMessageModel()->getChatMessage()->getAppdata().empty())
+ // return;// Already exist : no need to create one
+ //std::list > contents = message->getContents();
+ //if( contents.size() > 0)
+ //{
+ auto chatMessageFileContentModel = mChatMessageModel->getFileContentModel();
+ if( chatMessageFileContentModel && chatMessageFileContentModel->getContent() == mContent){
+ QString id;
+ auto a = chatMessageFileContentModel->getContent();
+ auto b = mChatMessageModel->getChatMessage()->getFileTransferInformation();
+ if( a == b)
+ qWarning() << "OK";
+ else
+ qWarning() << "NOOOOOOOOOO";
+ QString path = Utils::coreStringToAppString(b->getFilePath());
+
+ auto appdata = ChatMessageModel::AppDataManager(Utils::coreStringToAppString(mChatMessageModel->getChatMessage()->getAppdata()));
+
+ if(!appdata.mData.contains(path)
+ || !QFileInfo(Utils::coreStringToAppString(Paths::getThumbnailsDirPath())+appdata.mData[path]).isFile()){
+ // File don't exist. Create the thumbnail
+
+ QImage image(path);
+ if( image.isNull()){// Try to determine format from headers
+ QImageReader reader(path);
+ reader.setDecideFormatFromContent(true);
+ QByteArray format = reader.format();
+ if(!format.isEmpty())
+ image = QImage(path, format);
+ }
+ if (!image.isNull()){
+ int rotation = 0;
+ QExifImageHeader exifImageHeader;
+ if (exifImageHeader.loadFromJpeg(path))
+ rotation = int(exifImageHeader.value(QExifImageHeader::ImageTag::Orientation).toShort());
+ QImage thumbnail = image.scaled(
+ ThumbnailImageFileWidth, ThumbnailImageFileHeight,
+ Qt::KeepAspectRatio, Qt::SmoothTransformation
+ );
+
+ if (rotation != 0) {
+ QTransform transform;
+ if (rotation == 3 || rotation == 4)
+ transform.rotate(180);
+ else if (rotation == 5 || rotation == 6)
+ transform.rotate(90);
+ else if (rotation == 7 || rotation == 8)
+ transform.rotate(-90);
+ thumbnail = thumbnail.transformed(transform);
+ if (rotation == 2 || rotation == 4 || rotation == 5 || rotation == 7)
+ thumbnail = thumbnail.mirrored(true, false);
+ }
+ QString uuid = QUuid::createUuid().toString();
+ id = QStringLiteral("%1.jpg").arg(uuid.mid(1, uuid.length() - 2));
+
+ if (!thumbnail.save(Utils::coreStringToAppString(Paths::getThumbnailsDirPath()) + id , "jpg", 100)) {
+ qWarning() << QStringLiteral("Unable to create thumbnail of: `%1`.").arg(path);
+ }else{
+ appdata.mData[path] = id;
+ mChatMessageModel->getChatMessage()->setAppdata(Utils::appStringToCoreString(appdata.toString()));
+ }
+ }
+ }
+
+ if( path != ""){
+ setWasDownloaded( !path.isEmpty() && QFileInfo(path).isFile());
+ if(appdata.mData.contains(path))
+ setThumbnail(QStringLiteral("image://%1/%2").arg(ThumbnailProvider::ProviderId).arg(appdata.mData[path]));
+ }
+ }
+ //message->setAppdata(Utils::appStringToCoreString(id+':'+path));
+ //}
+}
+
+void ContentModel::downloadFile(){
+ switch (mChatMessageModel->getState()) {
+ case LinphoneEnums::ChatMessageStateDelivered:
+ case LinphoneEnums::ChatMessageStateDeliveredToUser:
+ case LinphoneEnums::ChatMessageStateDisplayed:
+ case LinphoneEnums::ChatMessageStateFileTransferDone:
+ break;
+
+ default:
+ qWarning() << QStringLiteral("Unable to download file of entry %1. It was not uploaded.").arg(mChatMessageModel->getState());
+ return;
+ }
+ bool soFarSoGood;
+ QString filename = getName();//mFileTransfertContent->getName();
+ const QString safeFilePath = Utils::getSafeFilePath(
+ QStringLiteral("%1%2")
+ .arg(CoreManager::getInstance()->getSettingsModel()->getDownloadFolder())
+ .arg(filename),
+ &soFarSoGood
+ );
+
+ if (!soFarSoGood) {
+ qWarning() << QStringLiteral("Unable to create safe file path for: %1.").arg(filename);
+ return;
+ }
+ mContent->setFilePath(Utils::appStringToCoreString(safeFilePath));
+ //mChatMessage->getContents().front()->setFilePath(Utils::appStringToCoreString(safeFilePath));
+
+ if( !mContent->isFileTransfer()){
+ QMessageBox::warning(nullptr, "Download File", "This file was already downloaded and is no more on the server. Your peer have to resend it if you want to get it");
+ }else
+ {
+ if (!mChatMessageModel->getChatMessage()->downloadContent(mContent))
+ qWarning() << QStringLiteral("Unable to download file of entry %1.").arg(filename);
+ }
+}
+
+void ContentModel::openFile (bool showDirectory) {
+ if (!mWasDownloaded && !mChatMessageModel->isOutgoing()) {
+ downloadFile();
+ }else{
+ QFileInfo info( Utils::coreStringToAppString(mContent->getFilePath()));
+ QDesktopServices::openUrl(
+ QUrl(QStringLiteral("file:///%1").arg(showDirectory ? info.absolutePath() : info.absoluteFilePath()))
+ );
+ }
+}
+
+
+// =============================================================================
+ChatMessageListener::ChatMessageListener(ChatMessageModel * model, QObject* parent) : QObject(parent){
+ connect(this, &ChatMessageListener::fileTransferRecv, model, &ChatMessageModel::onFileTransferRecv);
+ connect(this, &ChatMessageListener::fileTransferSendChunk, model, &ChatMessageModel::onFileTransferSendChunk);
+ connect(this, &ChatMessageListener::fileTransferSend, model, &ChatMessageModel::onFileTransferSend);
+ connect(this, &ChatMessageListener::fileTransferProgressIndication, model, &ChatMessageModel::onFileTransferProgressIndication);
+ connect(this, &ChatMessageListener::msgStateChanged, model, &ChatMessageModel::onMsgStateChanged);
+ connect(this, &ChatMessageListener::participantImdnStateChanged, model, &ChatMessageModel::onParticipantImdnStateChanged);
+ connect(this, &ChatMessageListener::ephemeralMessageTimerStarted, model, &ChatMessageModel::onEphemeralMessageTimerStarted);
+ connect(this, &ChatMessageListener::ephemeralMessageDeleted, model, &ChatMessageModel::onEphemeralMessageDeleted);
+ connect(this, &ChatMessageListener::participantImdnStateChanged, model->getParticipantImdnStates().get(), &ParticipantImdnStateListModel::onParticipantImdnStateChanged);
+}
+
+
+
+void ChatMessageListener::onFileTransferRecv(const std::shared_ptr & message, const std::shared_ptr & content, const std::shared_ptr & buffer){
+ emit fileTransferRecv(message, content, buffer);
+}
+void ChatMessageListener::onFileTransferSendChunk(const std::shared_ptr & message, const std::shared_ptr & content, size_t offset, size_t size, const std::shared_ptr & buffer){
+ emit fileTransferSendChunk(message, content, offset, size, buffer);
+}
+std::shared_ptr ChatMessageListener::onFileTransferSend(const std::shared_ptr & message, const std::shared_ptr & content, size_t offset, size_t size) {
+ emit fileTransferSend(message, content, offset, size);
+ return nullptr;
+}
+void ChatMessageListener::onFileTransferProgressIndication (const std::shared_ptr &message, const std::shared_ptr & content, size_t offset, size_t i){
+ emit fileTransferProgressIndication(message, content, offset, i);
+}
+void ChatMessageListener::onMsgStateChanged (const std::shared_ptr &message, linphone::ChatMessage::State state){
+ emit msgStateChanged(message, state);
+}
+void ChatMessageListener::onParticipantImdnStateChanged(const std::shared_ptr & message, const std::shared_ptr & state){
+ emit participantImdnStateChanged(message, state);
+}
+void ChatMessageListener::onEphemeralMessageTimerStarted(const std::shared_ptr & message){
+ emit ephemeralMessageTimerStarted(message);
+}
+void ChatMessageListener::onEphemeralMessageDeleted(const std::shared_ptr & message){
+ emit ephemeralMessageDeleted(message);
+}
+
+
+// =============================================================================
+ChatMessageModel::AppDataManager::AppDataManager(const QString& appdata){
+ if(!appdata.isEmpty()){
+ for(QString pair : appdata.split(';')){
+ QStringList fields = pair.split(':');
+ mData[fields[1]] = fields[0];
+ }
+ }
+}
+
+QString ChatMessageModel::AppDataManager::toString(){
+ QStringList pairs;
+ for(QMap::iterator it = mData.begin() ; it != mData.end() ; ++it){
+ pairs << it.value() + ":" + it.key();
+ }
+ return pairs.join(';');
+}
+ChatMessageModel::ChatMessageModel ( std::shared_ptr chatMessage, QObject * parent) : QObject(parent), ChatEvent(ChatRoomModel::EntryType::MessageEntry) {
+ App::getInstance()->getEngine()->setObjectOwnership(this, QQmlEngine::CppOwnership);// Avoid QML to destroy it
+ mParticipantImdnStateListModel = std::make_shared(chatMessage);
+ mChatMessageListener = std::make_shared(this, parent);
+ mChatMessage = chatMessage;
+ mWasDownloaded = false;
+ mChatMessage->addListener(mChatMessageListener);
+ mTimestamp = QDateTime::fromMSecsSinceEpoch(chatMessage->getTime() * 1000);
+ connect(this, &ChatMessageModel::remove, dynamic_cast(parent), &ChatRoomModel::removeEntry);
+
+ std::list> contents = chatMessage->getContents();
+ QString txt;
+ for(auto content : contents){
+ if(content->isText())
+ txt += QString::fromStdString(content->getUtf8Text());
+ }
+ mContent = txt;
+ //mIsOutgoing = chatMessage->isOutgoing() || chatMessage->getState() == linphone::ChatMessage::State::Idle;
+
+ // Old workaround.
+ // It can exist messages with a not delivered status. It's a linphone core bug.
+ /*
+ linphone::ChatMessage::State state = chatMessage->getState();
+ if (state == linphone::ChatMessage::State::InProgress)
+ dest["status"] = ChatRoomModel::MessageStatusNotDelivered;
+ else
+ dest["status"] = static_cast(chatMessage->getState());
+ */
+
+ auto content = chatMessage->getFileTransferInformation();
+ if (content) {
+ mFileTransfertContent = std::make_shared(this);
+ mFileTransfertContent->setContent(content);
+
+ }
+ for(auto content : chatMessage->getContents()){
+ mContents << std::make_shared(content, this);
+ }
+
+}
+
+ChatMessageModel::~ChatMessageModel(){
+ mChatMessage->removeListener(mChatMessageListener);
+}
+std::shared_ptr ChatMessageModel::create(std::shared_ptr chatMessage, QObject * parent){
+ auto model = std::make_shared(chatMessage, parent);
+ return model;
+}
+
+std::shared_ptr ChatMessageModel::getChatMessage(){
+ return mChatMessage;
+}
+std::shared_ptr ChatMessageModel::getContentModel(std::shared_ptr content){
+ if(content == mFileTransfertContent->getContent())
+ return mFileTransfertContent;
+ for(auto c : mContents)
+ if(c->getContent() == content)
+ return c;
+ return nullptr;
+}
+
+ContentModel * ChatMessageModel::getContent(int i){
+ return mContents[i].get();
+}
+
+//-----------------------------------------------------------------------------------------------------------------------
+
+QString ChatMessageModel::getFromDisplayName() const{
+ return Utils::getDisplayName(mChatMessage->getFromAddress());
+}
+
+QString ChatMessageModel::getToDisplayName() const{
+ return Utils::getDisplayName(mChatMessage->getToAddress());
+}
+
+ContactModel * ChatMessageModel::getContactModel() const{
+ return CoreManager::getInstance()->getContactsListModel()->findContactModelFromSipAddress(Utils::coreStringToAppString(mChatMessage->getFromAddress()->asString()));
+}
+
+bool ChatMessageModel::isEphemeral() const{
+ return mChatMessage->isEphemeral();
+}
+
+qint64 ChatMessageModel::getEphemeralExpireTime() const{
+ time_t t = mChatMessage->getEphemeralExpireTime();
+ return t >0 ? t - QDateTime::currentSecsSinceEpoch() : 0;
+ //return QDateTime::fromMSecsSinceEpoch(mChatMessage->getEphemeralExpireTime() * 1000)
+}
+
+LinphoneEnums::ChatMessageState ChatMessageModel::getState() const{
+ return LinphoneEnums::fromLinphone(mChatMessage->getState());
+}
+
+bool ChatMessageModel::isOutgoing() const{
+ return mChatMessage->isOutgoing();
+}
+
+ContentModel * ChatMessageModel::getFileContentModel() const{
+ return mFileTransfertContent.get();
+}
+
+QList ChatMessageModel::getContents() const{
+ QList models;
+ for(auto content : mContents)
+ models << content.get();
+ return models;
+}
+
+ParticipantImdnStateProxyModel * ChatMessageModel::getProxyImdnStates(){
+ ParticipantImdnStateProxyModel * proxy = new ParticipantImdnStateProxyModel();
+ proxy->setChatMessageModel(this);
+ return proxy;
+}
+
+std::shared_ptr ChatMessageModel::getParticipantImdnStates() const{
+ return mParticipantImdnStateListModel;
+}
+
+
+
+//-----------------------------------------------------------------------------------------------------------------------
+
+
+
+void ChatMessageModel::setWasDownloaded(bool wasDownloaded){
+ if( mWasDownloaded != wasDownloaded) {
+ mWasDownloaded = wasDownloaded;
+ emit wasDownloadedChanged();
+ }
+}
+
+//-----------------------------------------------------------------------------------------------------------------------
+
+void ChatMessageModel::resendMessage (){
+ switch (getState()) {
+ case LinphoneEnums::ChatMessageStateFileTransferError:
+ case LinphoneEnums::ChatMessageStateNotDelivered: {
+ mChatMessage->send();
+ break;
+ }
+
+ default:
+ qWarning() << QStringLiteral("Unable to resend message: %1. Bad state.").arg(getState());
+ }
+}
+
+
+void ChatMessageModel::deleteEvent(){
+ if (mChatMessage && mChatMessage->getFileTransferInformation()) {// Remove thumbnail
+ mChatMessage->cancelFileTransfer();
+ QString appdata = Utils::coreStringToAppString(mChatMessage->getAppdata());
+ QStringList fields = appdata.split(':');
+
+ if(fields[0].size() > 0) {
+ QString thumbnailPath = Utils::coreStringToAppString(Paths::getThumbnailsDirPath()) + fields[0];
+ if (!QFile::remove(thumbnailPath))
+ qWarning() << QStringLiteral("Unable to remove `%1`.").arg(thumbnailPath);
+ }
+ mChatMessage->setAppdata("");// Remove completely Thumbnail from the message
+ }
+ mChatMessage->getChatRoom()->deleteMessage(mChatMessage);
+}
+void ChatMessageModel::updateFileTransferInformation(){
+ if( mFileTransfertContent && mFileTransfertContent->getContent() != getChatMessage()->getFileTransferInformation()){
+ mFileTransfertContent->setContent(getChatMessage()->getFileTransferInformation());
+ }
+}
+
+void ChatMessageModel::onFileTransferRecv(const std::shared_ptr & message, const std::shared_ptr & content, const std::shared_ptr & buffer){
+}
+void ChatMessageModel::onFileTransferSendChunk(const std::shared_ptr & message, const std::shared_ptr & content, size_t offset, size_t size, const std::shared_ptr & buffer) {
+
+}
+std::shared_ptr ChatMessageModel::onFileTransferSend (const std::shared_ptr &,const std::shared_ptr &content,size_t,size_t) {
+ return nullptr;
+}
+
+void ChatMessageModel::onFileTransferProgressIndication (const std::shared_ptr &message,const std::shared_ptr &content,size_t offset,size_t) {
+ // content parameter is not in getContents() and getFileTransferInformation(). Question? What is it? Workaround : use the current file transfert.
+ // Note here : mFileTransfertContent->getContent() == getChatMessage()->getFileTransferInformation()
+ // Idea :
+ // auto model = getContentModel(content);
+ // if(model)
+ // model->setFileOffset(offset);
+ mFileTransfertContent->setFileOffset(offset);
+}
+
+void ChatMessageModel::onMsgStateChanged (const std::shared_ptr &message, linphone::ChatMessage::State state) {
+ updateFileTransferInformation();// On message state, file transfert information Content can be changed
+ // File message downloaded.
+ if (state == linphone::ChatMessage::State::FileTransferDone && !mChatMessage->isOutgoing()) {
+ if(mFileTransfertContent)
+ mFileTransfertContent->createThumbnail();
+ setWasDownloaded(true);
+ App::getInstance()->getNotifier()->notifyReceivedFileMessage(message);
+ }
+ emit stateChanged();
+}
+void ChatMessageModel::onParticipantImdnStateChanged(const std::shared_ptr & message, const std::shared_ptr & state){
+
+}
+void ChatMessageModel::onEphemeralMessageTimerStarted(const std::shared_ptr & message) {
+ emit ephemeralExpireTimeChanged();
+}
+void ChatMessageModel::onEphemeralMessageDeleted(const std::shared_ptr & message) {
+ //emit remove(mSelf.lock());
+ emit remove(this);
+}
+//-------------------------------------------------------------------------------------------------------
+
+
diff --git a/linphone-app/src/components/chat-events/ChatMessageModel.hpp b/linphone-app/src/components/chat-events/ChatMessageModel.hpp
new file mode 100644
index 000000000..bf02c8ed7
--- /dev/null
+++ b/linphone-app/src/components/chat-events/ChatMessageModel.hpp
@@ -0,0 +1,220 @@
+/*
+ * Copyright (c) 2021 Belledonne Communications SARL.
+ *
+ * This file is part of linphone-desktop
+ * (see https://www.linphone.org).
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#ifndef CHAT_MESSAGE_MODEL_H
+#define CHAT_MESSAGE_MODEL_H
+
+#include "utils/LinphoneEnums.hpp"
+
+#include
+
+// =============================================================================
+/*
+class Thumbnail{
+public:
+ Thumbnail();
+ QString mId;
+ QString mPath;
+
+ QString toString()const;
+ void fromString(const QString& );
+ static QString toString(const QVector& );
+ static QVector fromListString(const QString& );
+};
+*/
+#include "components/chat-room/ChatRoomModel.hpp"
+#include "ChatEvent.hpp"
+#include "components/participant-imdn/ParticipantImdnStateListModel.hpp"
+
+class ChatMessageModel;
+class ParticipantImdnStateProxyModel;
+class ParticipantImdnStateListModel;
+
+class ContentModel : public QObject{
+ Q_OBJECT
+public:
+ ContentModel(ChatMessageModel* chatModel);
+ ContentModel(std::shared_ptr content, ChatMessageModel* chatModel);
+
+ Q_PROPERTY(quint64 fileSize READ getFileSize NOTIFY fileSizeChanged)
+ Q_PROPERTY(QString name READ getName NOTIFY nameChanged)
+ Q_PROPERTY(quint64 fileOffset MEMBER mFileOffset WRITE setFileOffset NOTIFY fileOffsetChanged)
+
+ Q_PROPERTY(QString thumbnail READ getThumbnail WRITE setThumbnail NOTIFY thumbnailChanged)
+ Q_PROPERTY(bool wasDownloaded MEMBER mWasDownloaded WRITE setWasDownloaded NOTIFY wasDownloadedChanged)
+
+ std::shared_ptr getContent()const;
+
+ quint64 getFileSize() const;
+ QString getName() const;
+ QString getThumbnail() const;
+
+ void setFileOffset(quint64 fileOffset);
+ void setThumbnail(const QString& data);
+ void setWasDownloaded(bool wasDownloaded);
+ void setContent(std::shared_ptr content);
+
+ void createThumbnail ();
+ Q_INVOKABLE void downloadFile();
+ Q_INVOKABLE void openFile (bool showDirectory = false);
+
+
+ QString mThumbnail;
+ bool mWasDownloaded;
+ quint64 mFileOffset;
+
+signals:
+ void fileSizeChanged();
+ void nameChanged();
+ void thumbnailChanged();
+ void fileOffsetChanged();
+ void wasDownloadedChanged();
+
+private:
+ std::shared_ptr mContent;
+ ChatMessageModel* mChatMessageModel;
+};
+Q_DECLARE_METATYPE(std::shared_ptr)
+
+class ChatMessageListener : public QObject, public linphone::ChatMessageListener {
+Q_OBJECT
+public:
+ ChatMessageListener(ChatMessageModel * model, QObject * parent = nullptr);
+ virtual ~ChatMessageListener(){};
+
+ virtual void onFileTransferRecv(const std::shared_ptr & message, const std::shared_ptr & content, const std::shared_ptr & buffer) override;
+ virtual void onFileTransferSendChunk(const std::shared_ptr & message, const std::shared_ptr & content, size_t offset, size_t size, const std::shared_ptr & buffer) override;
+ virtual std::shared_ptr onFileTransferSend(const std::shared_ptr & message, const std::shared_ptr & content, size_t offset, size_t size) override;
+ virtual void onFileTransferProgressIndication (const std::shared_ptr &message, const std::shared_ptr &, size_t offset, size_t) override;
+ virtual void onMsgStateChanged (const std::shared_ptr &message, linphone::ChatMessage::State state) override;
+ virtual void onParticipantImdnStateChanged(const std::shared_ptr & message, const std::shared_ptr & state) override;
+ virtual void onEphemeralMessageTimerStarted(const std::shared_ptr & message) override;
+ virtual void onEphemeralMessageDeleted(const std::shared_ptr & message) override;
+signals:
+ void fileTransferRecv(const std::shared_ptr & message, const std::shared_ptr & content, const std::shared_ptr & buffer);
+ void fileTransferSendChunk(const std::shared_ptr & message, const std::shared_ptr & content, size_t offset, size_t size, const std::shared_ptr & buffer);
+ std::shared_ptr fileTransferSend (const std::shared_ptr &,const std::shared_ptr &,size_t,size_t);
+ void fileTransferProgressIndication (const std::shared_ptr &message, const std::shared_ptr &, size_t offset, size_t);
+ void msgStateChanged (const std::shared_ptr &message, linphone::ChatMessage::State state);
+ void participantImdnStateChanged(const std::shared_ptr & message, const std::shared_ptr & state);
+ void ephemeralMessageTimerStarted(const std::shared_ptr