diff --git a/submodules/belcard b/submodules/belcard index f5a8603f8..ebd037585 160000 --- a/submodules/belcard +++ b/submodules/belcard @@ -1 +1 @@ -Subproject commit f5a8603f8e379486d3a4bfa4d74861b0a2d880dd +Subproject commit ebd037585965ce80f01be5290abc7e5455009eb8 diff --git a/submodules/belle-sip b/submodules/belle-sip index b8bc7f2a5..b1695fb52 160000 --- a/submodules/belle-sip +++ b/submodules/belle-sip @@ -1 +1 @@ -Subproject commit b8bc7f2a5a899e8acfa59c7b49ebabcbab7d0efd +Subproject commit b1695fb52ede996ed0b50167aab787d1373f49e4 diff --git a/submodules/linphone b/submodules/linphone index 030b1c05d..758d516a3 160000 --- a/submodules/linphone +++ b/submodules/linphone @@ -1 +1 @@ -Subproject commit 030b1c05d5d77dac2d34a1be1c98516a8fb9887a +Subproject commit 758d516a39ce3ee484e185a6e1a2653659f6fd81 diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 524b6d670..36eaa4fa0 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -13,6 +13,7 @@ set(CMAKE_AUTOMOC ON) set(CMAKE_INCLUDE_CURRENT_DIR ON) set(CUSTOM_FLAGS "\ +-Wall \ -Wcast-align \ -Wconversion \ -Werror=suggest-override \ @@ -58,6 +59,8 @@ set(SOURCES src/app/Paths.cpp src/app/ThumbnailProvider.cpp src/components/camera/Camera.cpp + src/components/call/CallModel.cpp + src/components/calls/CallsListModel.cpp src/components/chat/ChatModel.cpp src/components/chat/ChatProxyModel.cpp src/components/contact/ContactObserver.cpp @@ -84,6 +87,8 @@ set(HEADERS src/app/Paths.hpp src/app/ThumbnailProvider.hpp src/components/camera/Camera.hpp + src/components/call/CallModel.hpp + src/components/calls/CallsListModel.hpp src/components/chat/ChatModel.hpp src/components/chat/ChatProxyModel.hpp src/components/contact/ContactObserver.hpp @@ -103,9 +108,7 @@ set(HEADERS src/utils.hpp ) -set(QRC_RESOURCES - resources.qrc -) +set(QRC_RESOURCES resources.qrc) set(LANGUAGES_DIRECTORY assets/languages) set(I18N_FILENAME i18n.qrc) diff --git a/tests/assets/images/burger_menu_hovered.svg b/tests/assets/images/burger_menu_hovered.svg new file mode 100644 index 000000000..20270f076 --- /dev/null +++ b/tests/assets/images/burger_menu_hovered.svg @@ -0,0 +1,14 @@ + + + + burger_menu_over_dark + Created with Sketch. + + + + + + + + + \ No newline at end of file diff --git a/tests/assets/images/burger_menu_light_hovered.svg b/tests/assets/images/burger_menu_light_hovered.svg new file mode 100644 index 000000000..fad7b30a8 --- /dev/null +++ b/tests/assets/images/burger_menu_light_hovered.svg @@ -0,0 +1,14 @@ + + + + burger_menu_over_bright + Created with Sketch. + + + + + + + + + \ No newline at end of file diff --git a/tests/assets/images/burger_menu_light_normal.svg b/tests/assets/images/burger_menu_light_normal.svg new file mode 100644 index 000000000..937b1d880 --- /dev/null +++ b/tests/assets/images/burger_menu_light_normal.svg @@ -0,0 +1,14 @@ + + + + burger_menu_default + Created with Sketch. + + + + + + + + + \ No newline at end of file diff --git a/tests/assets/images/burger_menu_light_pressed.svg b/tests/assets/images/burger_menu_light_pressed.svg new file mode 100644 index 000000000..937b1d880 --- /dev/null +++ b/tests/assets/images/burger_menu_light_pressed.svg @@ -0,0 +1,14 @@ + + + + burger_menu_default + Created with Sketch. + + + + + + + + + \ No newline at end of file diff --git a/tests/assets/images/burger_menu_normal.svg b/tests/assets/images/burger_menu_normal.svg new file mode 100644 index 000000000..937b1d880 --- /dev/null +++ b/tests/assets/images/burger_menu_normal.svg @@ -0,0 +1,14 @@ + + + + burger_menu_default + Created with Sketch. + + + + + + + + + \ No newline at end of file diff --git a/tests/assets/images/burger_menu_pressed.svg b/tests/assets/images/burger_menu_pressed.svg new file mode 100644 index 000000000..937b1d880 --- /dev/null +++ b/tests/assets/images/burger_menu_pressed.svg @@ -0,0 +1,14 @@ + + + + burger_menu_default + Created with Sketch. + + + + + + + + + \ No newline at end of file diff --git a/tests/assets/images/call_sign_connected.svg b/tests/assets/images/call_sign_connected.svg new file mode 100644 index 000000000..fa89999c6 --- /dev/null +++ b/tests/assets/images/call_sign_connected.svg @@ -0,0 +1,13 @@ + + + + call_in_sign + Created with Sketch. + + + + + + + + \ No newline at end of file diff --git a/tests/assets/images/call_sign_incoming.svg b/tests/assets/images/call_sign_incoming.svg new file mode 100644 index 000000000..7c3595e57 --- /dev/null +++ b/tests/assets/images/call_sign_incoming.svg @@ -0,0 +1,13 @@ + + + + call_incoming_sign + Created with Sketch. + + + + + + + + \ No newline at end of file diff --git a/tests/assets/images/call_sign_outgoing.svg b/tests/assets/images/call_sign_outgoing.svg new file mode 100644 index 000000000..e30a239d6 --- /dev/null +++ b/tests/assets/images/call_sign_outgoing.svg @@ -0,0 +1,13 @@ + + + + call_outgoing_sign + Created with Sketch. + + + + + + + + \ No newline at end of file diff --git a/tests/assets/images/call_sign_paused.svg b/tests/assets/images/call_sign_paused.svg new file mode 100644 index 000000000..6173dfcd5 --- /dev/null +++ b/tests/assets/images/call_sign_paused.svg @@ -0,0 +1,13 @@ + + + + call_pausing_sign + Created with Sketch. + + + + + + + + \ No newline at end of file diff --git a/tests/assets/languages/en.ts b/tests/assets/languages/en.ts index 308fad2f1..edc62bce0 100644 --- a/tests/assets/languages/en.ts +++ b/tests/assets/languages/en.ts @@ -11,17 +11,9 @@ contactsEntry Contacts - - acceptAudioCall - - - - acceptVideoCall - - hangup - End call + End call @@ -34,6 +26,30 @@ newConference NEW CONFERENCE + + acceptAudioCall + ACCEPT AUDIO CALL + + + acceptVideoCall + ACCEPT VIDEO CALL + + + terminateCall + HANGUP + + + resumeCall + + + + transferCall + + + + pauseCall + + Chat diff --git a/tests/assets/languages/fr.ts b/tests/assets/languages/fr.ts index dcef4df81..612bdf29b 100644 --- a/tests/assets/languages/fr.ts +++ b/tests/assets/languages/fr.ts @@ -3,17 +3,9 @@ CallControls - - acceptAudioCall - - - - acceptVideoCall - - hangup - Fin d'appel + Fin d'appel @@ -26,6 +18,30 @@ newConference NOUVELLE CONFERENCE + + acceptAudioCall + ACCEPTER APPEL AUDIO + + + acceptVideoCall + ACCEPTER APPEL VIDEO + + + terminateCall + RACCROCHER + + + resumeCall + + + + transferCall + + + + pauseCall + + Chat diff --git a/tests/resources.qrc b/tests/resources.qrc index 715908917..398691972 100644 --- a/tests/resources.qrc +++ b/tests/resources.qrc @@ -9,6 +9,12 @@ assets/images/attachment_normal.svg assets/images/attachment_pressed.svg assets/images/auto_answer.svg + assets/images/burger_menu_hovered.svg + assets/images/burger_menu_light_hovered.svg + assets/images/burger_menu_light_normal.svg + assets/images/burger_menu_light_pressed.svg + assets/images/burger_menu_normal.svg + assets/images/burger_menu_pressed.svg assets/images/call_accept_hovered.svg assets/images/call_accept_normal.svg assets/images/call_accept_pressed.svg @@ -19,6 +25,10 @@ assets/images/call_quality_1.svg assets/images/call_quality_2.svg assets/images/call_quality_3.svg + assets/images/call_sign_connected.svg + assets/images/call_sign_incoming.svg + assets/images/call_sign_outgoing.svg + assets/images/call_sign_paused.svg assets/images/camera_off_hovered.svg assets/images/camera_off_normal.svg assets/images/camera_off_pressed.svg @@ -200,11 +210,8 @@ ui/modules/Common/Tooltip/Tooltip.qml ui/modules/Common/View/ScrollableListView.qml ui/modules/Linphone/Account/AccountStatus.qml - ui/modules/Linphone/Call/CallControls.qml - ui/modules/Linphone/Call/ConnectedCallsControl.qml - ui/modules/Linphone/Call/IncomingCallControls.qml - ui/modules/Linphone/Call/OutgoingCallControls.qml - ui/modules/Linphone/Call/PausedCallControls.qml + ui/modules/Linphone/Calls/CallControls.qml + ui/modules/Linphone/Calls/Calls.qml ui/modules/Linphone/Chat/Chat.qml ui/modules/Linphone/Chat/Event.qml ui/modules/Linphone/Chat/FileMessage.qml @@ -223,6 +230,8 @@ ui/modules/Linphone/qmldir ui/modules/Linphone/SmartSearchBar.qml ui/modules/Linphone/Styles/Account/AccountStatusStyle.qml + ui/modules/Linphone/Styles/Calls/CallControlsStyle.qml + ui/modules/Linphone/Styles/Calls/CallsStyle.qml ui/modules/Linphone/Styles/ChatStyle.qml ui/modules/Linphone/Styles/Contact/AvatarStyle.qml ui/modules/Linphone/Styles/Contact/ContactDescriptionStyle.qml diff --git a/tests/src/app/App.cpp b/tests/src/app/App.cpp index 10e01f9ca..3479084c3 100644 --- a/tests/src/app/App.cpp +++ b/tests/src/app/App.cpp @@ -4,14 +4,15 @@ #include #include +#include "../components/calls/CallsListModel.hpp" #include "../components/camera/Camera.hpp" #include "../components/chat/ChatProxyModel.hpp" #include "../components/contacts/ContactsListProxyModel.hpp" #include "../components/core/CoreManager.hpp" #include "../components/settings/AccountSettingsModel.hpp" #include "../components/settings/SettingsModel.hpp" -#include "../components/timeline/TimelineModel.hpp" #include "../components/smart-search-bar/SmartSearchBarModel.hpp" +#include "../components/timeline/TimelineModel.hpp" #include "App.hpp" @@ -96,17 +97,20 @@ void App::initContentApp () { void App::registerTypes () { qInfo() << "Registering types..."; + qmlRegisterUncreatableType( + "Linphone", 1, 0, "CallModel", "CallModel is uncreatable." + ); qmlRegisterUncreatableType( - "Linphone", 1, 0, "ContactModel", "ContactModel is uncreatable" + "Linphone", 1, 0, "ContactModel", "ContactModel is uncreatable." ); qmlRegisterUncreatableType( - "Linphone", 1, 0, "ContactObserver", "ContactObserver is uncreatable" - ); - qmlRegisterUncreatableType( - "Linphone", 1, 0, "VcardModel", "VcardModel is uncreatable" + "Linphone", 1, 0, "ContactObserver", "ContactObserver is uncreatable." ); qmlRegisterUncreatableType( - "Linphone", 1, 0, "Presence", "Presence is uncreatable" + "Linphone", 1, 0, "Presence", "Presence is uncreatable." + ); + qmlRegisterUncreatableType( + "Linphone", 1, 0, "VcardModel", "VcardModel is uncreatable." ); qmlRegisterSingletonType( @@ -123,20 +127,6 @@ void App::registerTypes () { } ); - qmlRegisterSingletonType( - "Linphone", 1, 0, "ContactsListModel", - [](QQmlEngine *, QJSEngine *) -> QObject *{ - return CoreManager::getInstance()->getContactsListModel(); - } - ); - - qmlRegisterSingletonType( - "Linphone", 1, 0, "SipAddressesModel", - [](QQmlEngine *, QJSEngine *) -> QObject *{ - return CoreManager::getInstance()->getSipAddressesModel(); - } - ); - qmlRegisterSingletonType( "Linphone", 1, 0, "AccountSettingsModel", [](QQmlEngine *, QJSEngine *) -> QObject *{ @@ -144,6 +134,20 @@ void App::registerTypes () { } ); + qmlRegisterSingletonType( + "Linphone", 1, 0, "CallsListModel", + [](QQmlEngine *, QJSEngine *) -> QObject *{ + return new CallsListModel(); + } + ); + + qmlRegisterSingletonType( + "Linphone", 1, 0, "ContactsListModel", + [](QQmlEngine *, QJSEngine *) -> QObject *{ + return CoreManager::getInstance()->getContactsListModel(); + } + ); + qmlRegisterSingletonType( "Linphone", 1, 0, "SettingsModel", [](QQmlEngine *, QJSEngine *) -> QObject *{ @@ -151,6 +155,13 @@ void App::registerTypes () { } ); + qmlRegisterSingletonType( + "Linphone", 1, 0, "SipAddressesModel", + [](QQmlEngine *, QJSEngine *) -> QObject *{ + return CoreManager::getInstance()->getSipAddressesModel(); + } + ); + qmlRegisterSingletonType( "Linphone", 1, 0, "TimelineModel", [](QQmlEngine *, QJSEngine *) -> QObject *{ diff --git a/tests/src/components/call/CallModel.cpp b/tests/src/components/call/CallModel.cpp new file mode 100644 index 000000000..b701e9c7c --- /dev/null +++ b/tests/src/components/call/CallModel.cpp @@ -0,0 +1,71 @@ +#include "../../utils.hpp" +#include "../core/CoreManager.hpp" + +#include "CallModel.hpp" + +// ============================================================================= + +CallModel::CallModel (shared_ptr linphone_call) { + m_linphone_call = linphone_call; +} + +// ----------------------------------------------------------------------------- + +void CallModel::acceptAudioCall () { + CoreManager::getInstance()->getCore()->acceptCall(m_linphone_call); +} + +void CallModel::terminateCall () { + CoreManager::getInstance()->getCore()->terminateCall(m_linphone_call); +} + +// ----------------------------------------------------------------------------- + +QString CallModel::getSipAddress () const { + return ::Utils::linphoneStringToQString(m_linphone_call->getRemoteAddress()->asStringUriOnly()); +} + +CallModel::CallStatus CallModel::getStatus () const { + switch (m_linphone_call->getState()) { + case linphone::CallStateConnected: + case linphone::CallStateStreamsRunning: + return CallStatusConnected; + + case linphone::CallStateEnd: + case linphone::CallStateError: + case linphone::CallStateRefered: + case linphone::CallStateReleased: + return CallStatusEnded; + + case linphone::CallStatePaused: + case linphone::CallStatePausedByRemote: + return CallStatusPaused; + + default: + break; + } + + return m_linphone_call->getDir() == linphone::CallDirIncoming ? CallStatusIncoming : CallStatusOutgoing; +} + +bool CallModel::getPausedByUser () const { + return m_linphone_call->getState() == linphone::CallStatePaused; +} + +void CallModel::setPausedByUser (bool status) { + bool paused = getPausedByUser(); + + if (status) { + if (!paused) { + CoreManager::getInstance()->getCore()->pauseCall(m_linphone_call); + emit pausedByUserChanged(true); + } + + return; + } + + if (paused) { + CoreManager::getInstance()->getCore()->resumeCall(m_linphone_call); + emit pausedByUserChanged(false); + } +} diff --git a/tests/src/components/call/CallModel.hpp b/tests/src/components/call/CallModel.hpp new file mode 100644 index 000000000..3f3f67ce8 --- /dev/null +++ b/tests/src/components/call/CallModel.hpp @@ -0,0 +1,55 @@ +#ifndef CALL_MODEL_H_ +#define CALL_MODEL_H_ + +#include +#include + +// ============================================================================= + +class CallModel : public QObject { + Q_OBJECT; + + Q_PROPERTY(QString sipAddress READ getSipAddress CONSTANT); + + Q_PROPERTY(CallStatus status READ getStatus NOTIFY statusChanged); + Q_PROPERTY(bool isOutgoing READ isOutgoing CONSTANT); + + Q_PROPERTY(bool pausedByUser READ getPausedByUser WRITE setPausedByUser NOTIFY pausedByUserChanged); + +public: + enum CallStatus { + CallStatusConnected, + CallStatusEnded, + CallStatusIncoming, + CallStatusOutgoing, + CallStatusPaused + }; + + Q_ENUM(CallStatus); + + CallModel (std::shared_ptr linphone_call); + ~CallModel () = default; + + Q_INVOKABLE void acceptAudioCall (); + Q_INVOKABLE void terminateCall (); + +signals: + void statusChanged (CallStatus status); + void pausedByUserChanged (bool status); + +private: + QString getSipAddress () const; + + CallStatus getStatus () const; + + bool isOutgoing () const { + return m_linphone_call->getDir() == linphone::CallDirOutgoing; + } + + bool getPausedByUser () const; + void setPausedByUser (bool status); + + std::shared_ptr m_linphone_call; +}; + +#endif // CALL_MODEL_H_ diff --git a/tests/src/components/calls/CallsListModel.cpp b/tests/src/components/calls/CallsListModel.cpp new file mode 100644 index 000000000..782b68138 --- /dev/null +++ b/tests/src/components/calls/CallsListModel.cpp @@ -0,0 +1,103 @@ +#include + +#include "../../app/App.hpp" +#include "../core/CoreManager.hpp" + +#include "CallsListModel.hpp" + +using namespace std; + +// ============================================================================= + +CallsListModel::CallsListModel (QObject *parent) : QAbstractListModel(parent) { + m_core_handlers = CoreManager::getInstance()->getHandlers(); + QObject::connect( + &(*m_core_handlers), &CoreHandlers::callStateChanged, + this, [this](const shared_ptr &linphone_call, linphone::CallState state) { + switch (state) { + case linphone::CallStateIncomingReceived: + addCall(linphone_call); + break; + case linphone::CallStateOutgoingInit: + addCall(linphone_call); + break; + case linphone::CallStateEnd: + case linphone::CallStateError: + removeCall(linphone_call); + break; + default: + + break; + } + } + ); +} + +int CallsListModel::rowCount (const QModelIndex &) const { + return m_list.count(); +} + +QHash CallsListModel::roleNames () const { + 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 >= m_list.count()) + return QVariant(); + + if (role == Qt::DisplayRole) + return QVariant::fromValue(m_list[row]); + + return QVariant(); +} + +// ----------------------------------------------------------------------------- + +bool CallsListModel::removeRow (int row, const QModelIndex &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 >= m_list.count()) + return false; + + beginRemoveRows(parent, row, limit); + + for (int i = 0; i < count; ++i) + m_list.takeAt(row)->deleteLater(); + + endRemoveRows(); + + return true; +} + +// ----------------------------------------------------------------------------- + +void CallsListModel::addCall (const shared_ptr &linphone_call) { + CallModel *call = new CallModel(linphone_call); + App::getInstance()->getEngine()->setObjectOwnership(call, QQmlEngine::CppOwnership); + linphone_call->setData("call-model", *call); + + int row = rowCount(); + + beginInsertRows(QModelIndex(), row, row); + m_list << call; + endInsertRows(); +} + +void CallsListModel::removeCall (const shared_ptr &linphone_call) { + CallModel &call = linphone_call->getData("call-model"); + linphone_call->unsetData("call-model"); + + qInfo() << "Removing call:" << &call; + + int index = m_list.indexOf(&call); + if (index == -1 || !removeRow(index)) + qWarning() << "Unable to remove call:" << &call; +} diff --git a/tests/src/components/calls/CallsListModel.hpp b/tests/src/components/calls/CallsListModel.hpp new file mode 100644 index 000000000..daf9ef5d7 --- /dev/null +++ b/tests/src/components/calls/CallsListModel.hpp @@ -0,0 +1,36 @@ +#ifndef CALLS_LIST_MODEL_H_ +#define CALLS_LIST_MODEL_H_ + +#include + +#include "../call/CallModel.hpp" + +// ============================================================================= + +class CoreHandlers; + +class CallsListModel : public QAbstractListModel { + Q_OBJECT; + +public: + CallsListModel (QObject *parent = Q_NULLPTR); + ~CallsListModel () = default; + + int rowCount (const QModelIndex &index = QModelIndex()) const override; + + QHash roleNames () const override; + QVariant data (const QModelIndex &index, int role = Qt::DisplayRole) const override; + +private: + bool removeRow (int row, const QModelIndex &parent = QModelIndex()); + bool removeRows (int row, int count, const QModelIndex &parent = QModelIndex()) override; + + void addCall (const std::shared_ptr &linphone_call); + void removeCall (const std::shared_ptr &linphone_call); + + QList m_list; + + std::shared_ptr m_core_handlers; +}; + +#endif // CALLS_LIST_MODEL_H_ diff --git a/tests/src/components/contacts/ContactsListModel.cpp b/tests/src/components/contacts/ContactsListModel.cpp index c9360edba..1771ed0bd 100644 --- a/tests/src/components/contacts/ContactsListModel.cpp +++ b/tests/src/components/contacts/ContactsListModel.cpp @@ -111,6 +111,8 @@ void ContactsListModel::removeContact (ContactModel *contact) { qWarning() << "Unable to remove contact:" << contact; } +// ----------------------------------------------------------------------------- + void ContactsListModel::addContact (ContactModel *contact) { QObject::connect( contact, &ContactModel::contactUpdated, diff --git a/tests/src/components/core/CoreHandlers.cpp b/tests/src/components/core/CoreHandlers.cpp index d7e8a13dc..552361f0c 100644 --- a/tests/src/components/core/CoreHandlers.cpp +++ b/tests/src/components/core/CoreHandlers.cpp @@ -10,8 +10,8 @@ using namespace std; // ============================================================================= void CoreHandlers::onAuthenticationRequested ( - const std::shared_ptr &, - const std::shared_ptr &, + const shared_ptr &, + const shared_ptr &, linphone::AuthMethod ) { qDebug() << "Auth request"; @@ -19,11 +19,11 @@ void CoreHandlers::onAuthenticationRequested ( void CoreHandlers::onCallStateChanged ( const shared_ptr &, - const shared_ptr &, - linphone::CallState, + const shared_ptr &call, + linphone::CallState state, const string & ) { - qDebug() << "call"; + emit callStateChanged(call, state); } void CoreHandlers::onMessageReceived ( diff --git a/tests/src/components/core/CoreHandlers.hpp b/tests/src/components/core/CoreHandlers.hpp index d1bb63cde..55b03146b 100644 --- a/tests/src/components/core/CoreHandlers.hpp +++ b/tests/src/components/core/CoreHandlers.hpp @@ -12,6 +12,7 @@ class CoreHandlers : Q_OBJECT; signals: + void callStateChanged (const std::shared_ptr &call, linphone::CallState state); void messageReceived (const std::shared_ptr &message); private: @@ -24,7 +25,7 @@ private: void onCallStateChanged ( const std::shared_ptr &core, const std::shared_ptr &call, - linphone::CallState cstate, + linphone::CallState state, const std::string &message ) override; diff --git a/tests/src/components/notifier/Notifier.cpp b/tests/src/components/notifier/Notifier.cpp index 8f4363006..99ef0b681 100644 --- a/tests/src/components/notifier/Notifier.cpp +++ b/tests/src/components/notifier/Notifier.cpp @@ -152,8 +152,8 @@ void Notifier::showNotification (QObject *notification, int timeout) { void Notifier::notifyReceivedMessage ( int timeout, - const std::shared_ptr &room, - const std::shared_ptr &message + const shared_ptr &room, + const shared_ptr &message ) { QObject *object = createNotification(Notifier::MessageReceived); diff --git a/tests/src/components/sip-addresses/SipAddressesModel.cpp b/tests/src/components/sip-addresses/SipAddressesModel.cpp index 6474adcac..22f2c5b6d 100644 --- a/tests/src/components/sip-addresses/SipAddressesModel.cpp +++ b/tests/src/components/sip-addresses/SipAddressesModel.cpp @@ -19,10 +19,10 @@ SipAddressesModel::SipAddressesModel (QObject *parent) : QAbstractListModel(pare QObject::connect(contacts, &ContactsListModel::contactAdded, this, &SipAddressesModel::handleContactAdded); QObject::connect(contacts, &ContactsListModel::contactRemoved, this, &SipAddressesModel::handleContactRemoved); - m_handlers = CoreManager::getInstance()->getHandlers(); + m_core_handlers = CoreManager::getInstance()->getHandlers(); QObject::connect( - &(*m_handlers), &CoreHandlers::messageReceived, - this, [this](const std::shared_ptr &message) { + &(*m_core_handlers), &CoreHandlers::messageReceived, + this, [this](const shared_ptr &message) { const QString &sip_address = ::Utils::linphoneStringToQString(message->getFromAddress()->asStringUriOnly()); addOrUpdateSipAddress(sip_address, nullptr, message); } @@ -107,7 +107,7 @@ void SipAddressesModel::connectToChatModel (ChatModel *chat_model) { for (auto &signal : { &ChatModel::messageSent, &ChatModel::messageReceived }) { QObject::connect( chat_model, signal, - this, [this](const std::shared_ptr &message) { + this, [this](const shared_ptr &message) { addOrUpdateSipAddress( ::Utils::linphoneStringToQString(message->getToAddress()->asStringUriOnly()), nullptr, message ); diff --git a/tests/src/components/sip-addresses/SipAddressesModel.hpp b/tests/src/components/sip-addresses/SipAddressesModel.hpp index d1f2716aa..eeecd655f 100644 --- a/tests/src/components/sip-addresses/SipAddressesModel.hpp +++ b/tests/src/components/sip-addresses/SipAddressesModel.hpp @@ -60,7 +60,7 @@ private: QMultiHash m_observers; - std::shared_ptr m_handlers; + std::shared_ptr m_core_handlers; }; #endif // SIP_ADDRESSES_MODEL_H_ diff --git a/tests/ui/modules/Common/Menu/ActionMenuEntry.qml b/tests/ui/modules/Common/Menu/ActionMenuEntry.qml index f5b787ee9..1c09ca0c5 100644 --- a/tests/ui/modules/Common/Menu/ActionMenuEntry.qml +++ b/tests/ui/modules/Common/Menu/ActionMenuEntry.qml @@ -33,6 +33,7 @@ Rectangle { color: ActionMenuStyle.entry.text.color elide: Text.ElideRight font.pointSize: ActionMenuStyle.entry.text.fontSize + height: parent.height verticalAlignment: Text.AlignVCenter } @@ -43,6 +44,6 @@ Rectangle { anchors.fill: parent hoverEnabled: true - onClicked: entry.clicked + onClicked: entry.clicked() } } diff --git a/tests/ui/modules/Common/Styles/Menu/ActionMenuStyle.qml b/tests/ui/modules/Common/Styles/Menu/ActionMenuStyle.qml index a2dd626ac..3aa9585cf 100644 --- a/tests/ui/modules/Common/Styles/Menu/ActionMenuStyle.qml +++ b/tests/ui/modules/Common/Styles/Menu/ActionMenuStyle.qml @@ -9,18 +9,18 @@ QtObject { property int spacing: 1 property QtObject entry: QtObject { - property int leftMargin: 4 - property int rightMargin: 4 + property int leftMargin: 18 + property int rightMargin: 8 property QtObject color: QtObject { - property color hovered: Colors.s - property color normal: Colors.i - property color pressed: Colors.t + property color hovered: Colors.j + property color normal: Colors.g + property color pressed: Colors.i } property QtObject text: QtObject { property color color: Colors.k - property int fontSize: 8 + property int fontSize: 9 } } } diff --git a/tests/ui/modules/Linphone/Call/CallControls.qml b/tests/ui/modules/Linphone/Call/CallControls.qml deleted file mode 100644 index b7fe94842..000000000 --- a/tests/ui/modules/Linphone/Call/CallControls.qml +++ /dev/null @@ -1,91 +0,0 @@ -import QtQuick 2.7 -import QtQuick.Layouts 1.3 -import QtQuick.Controls 2.0 - -import Linphone 1.0 -import Common 1.0 - -// =================================================================== - -RowLayout { - property string sipAddress - - // TODO. - property var contact: ContactsListModel.mapSipAddressTocd ( - sipAddress - ) - - implicitHeight: contact.height - spacing: 1 - - Rectangle { - Layout.fillWidth: true - color: '#434343' - implicitHeight: _contact.height - - Contact { - id: contact - - anchors.fill: parent - sipAddressColor: '#FFFFFF' - usernameColor: '#FFFFFF' - } - } - - Rectangle { - id: button - - Layout.preferredHeight: contact.height - Layout.preferredWidth: 42 - color: menu.isOpen() ? '#FE5E00' : '#434343' - - Text { - anchors.centerIn: parent - color: '#FFFFFF' - text: '...' - } - - MouseArea { - anchors.fill: parent - hoverEnabled: true - - onClicked: { - menu.showMenu() - } - } - } - - DropDownMenu { - id: menu - - implicitWidth: actionMenu.width - launcher: button - relativeTo: button - relativeX: button.width + 1 - - ActionMenu { - id: actionMenu - - entryHeight: 22 - entryWidth: 120 - - ActionMenuEntry { - entryName: qsTr('acceptAudioCall') - - onClicked: menu.hideMenu() - } - - ActionMenuEntry { - entryName: qsTr('acceptVideoCall') - - onClicked: menu.hideMenu() - } - - ActionMenuEntry { - entryName: qsTr('hangup') - - onClicked: menu.hideMenu() - } - } - } -} diff --git a/tests/ui/modules/Linphone/Call/ConnectedCallsControl.qml b/tests/ui/modules/Linphone/Call/ConnectedCallsControl.qml deleted file mode 100644 index 1ba758f7f..000000000 --- a/tests/ui/modules/Linphone/Call/ConnectedCallsControl.qml +++ /dev/null @@ -1,3 +0,0 @@ -import QtQuick 2.7 - -Item {} diff --git a/tests/ui/modules/Linphone/Call/IncomingCallControls.qml b/tests/ui/modules/Linphone/Call/IncomingCallControls.qml deleted file mode 100644 index 1ba758f7f..000000000 --- a/tests/ui/modules/Linphone/Call/IncomingCallControls.qml +++ /dev/null @@ -1,3 +0,0 @@ -import QtQuick 2.7 - -Item {} diff --git a/tests/ui/modules/Linphone/Call/OutgoingCallControls.qml b/tests/ui/modules/Linphone/Call/OutgoingCallControls.qml deleted file mode 100644 index 1ba758f7f..000000000 --- a/tests/ui/modules/Linphone/Call/OutgoingCallControls.qml +++ /dev/null @@ -1,3 +0,0 @@ -import QtQuick 2.7 - -Item {} diff --git a/tests/ui/modules/Linphone/Call/PausedCallControls.qml b/tests/ui/modules/Linphone/Call/PausedCallControls.qml deleted file mode 100644 index 1ba758f7f..000000000 --- a/tests/ui/modules/Linphone/Call/PausedCallControls.qml +++ /dev/null @@ -1,3 +0,0 @@ -import QtQuick 2.7 - -Item {} diff --git a/tests/ui/modules/Linphone/Calls/CallControls.qml b/tests/ui/modules/Linphone/Calls/CallControls.qml new file mode 100644 index 000000000..e7474f393 --- /dev/null +++ b/tests/ui/modules/Linphone/Calls/CallControls.qml @@ -0,0 +1,74 @@ +import QtQuick 2.7 +import QtQuick.Layouts 1.3 + +import Common 1.0 +import Linphone 1.0 +import Linphone.Styles 1.0 + +// ============================================================================= + +Rectangle { + id: callControls + + // --------------------------------------------------------------------------- + + default property alias _content: content.data + + property alias signIcon: signIcon.icon + property alias sipAddressColor: contact.sipAddressColor + property alias usernameColor: contact.usernameColor + property string sipAddress + + // --------------------------------------------------------------------------- + + property var _contactObserver: SipAddressesModel.getContactObserver(sipAddress) + + // --------------------------------------------------------------------------- + + color: CallControlsStyle.color + height: CallControlsStyle.height + width: CallControlsStyle.width + + RowLayout { + anchors { + fill: parent + leftMargin: CallControlsStyle.leftMargin + rightMargin: CallControlsStyle.rightMargin + } + + spacing: 0 + + Contact { + id: contact + + Layout.fillHeight: true + Layout.fillWidth: true + anchors.fill: parent + + displayUnreadMessagesCount: true + + entry: ({ + contact: _contactObserver.contact, + sipAddress: callControls.sipAddress + }) + } + + Item { + id: content + Layout.fillHeight: true + } + } + + // --------------------------------------------------------------------------- + + Icon { + id: signIcon + + anchors { + left: parent.left + top: parent.top + } + + iconSize: CallControlsStyle.signSize + } +} diff --git a/tests/ui/modules/Linphone/Calls/Calls.qml b/tests/ui/modules/Linphone/Calls/Calls.qml new file mode 100644 index 000000000..7a2780c03 --- /dev/null +++ b/tests/ui/modules/Linphone/Calls/Calls.qml @@ -0,0 +1,172 @@ +import QtQuick 2.7 + +import Common 1.0 +import Linphone 1.0 +import Linphone.Styles 1.0 + +// ============================================================================= + +ListView { + id: calls + + property var _mapStatusToParams + + // --------------------------------------------------------------------------- + + function _getSignIcon (call) { + if (call) { + var string = _mapStatusToParams[call.status].string + return string ? 'call_sign_' + string : '' + } + + return '' + } + + function _getParams (call) { + if (call) { + return _mapStatusToParams[call.status] + } + } + + // --------------------------------------------------------------------------- + + boundsBehavior: Flickable.StopAtBounds + clip: true + spacing: 0 + + // --------------------------------------------------------------------------- + + Component.onCompleted: { + _mapStatusToParams = {} + + _mapStatusToParams[CallModel.CallStatusConnected] = { + actions: [{ + name: qsTr('resumeCall'), + handler: (function (call) { call.pausedByUser = false }) + }, { + name: qsTr('transferCall'), + handler: (function (call) { call.transferCall() }) + }, { + name: qsTr('terminateCall'), + handler: (function (call) { call.terminateCall() }) + }], + component: callActions, + string: 'connected' + } + + _mapStatusToParams[CallModel.CallStatusEnded] = {} + + _mapStatusToParams[CallModel.CallStatusIncoming] = { + actions: [{ + name: qsTr('acceptAudioCall'), + handler: (function (call) { call.acceptAudioCall() }) + }, { + name: qsTr('acceptVideoCall'), + handler: (function (call) { call.acceptVideoCall() }) + }, { + name: qsTr('terminateCall'), + handler: (function (call) { call.terminateCall() }) + }], + component: callActions, + string: 'incoming' + } + + _mapStatusToParams[CallModel.CallStatusOutgoing] = { + component: callAction, + handler: (function (call) { call.terminateCall() }), + icon: 'hangup', + string: 'outgoing' + } + + _mapStatusToParams[CallModel.CallStatusPaused] = { + actions: [{ + name: qsTr('pauseCall'), + handler: (function (call) { call.pausedByUser = true }) + }, { + name: qsTr('transferCall'), + handler: (function (call) { call.transferCall() }) + }, { + name: qsTr('terminateCall'), + handler: (function (call) { call.terminateCall() }) + }], + component: callActions, + string: 'paused' + } + } + + // --------------------------------------------------------------------------- + + Component { + id: callAction + + ActionButton { + icon: params.icon + iconSize: CallsStyle.entry.iconActionSize + + onClicked: params.handler(call) + } + } + + // --------------------------------------------------------------------------- + + Component { + id: callActions + + ActionButton { + id: button + + icon: 'burger_menu' + iconSize: CallsStyle.entry.iconMenuSize + + onClicked: menu.showMenu() + + DropDownMenu { + id: menu + + implicitWidth: actionMenu.width + launcher: button + relativeTo: callControls + relativeX: callControls.width + + ActionMenu { + id: actionMenu + + entryHeight: CallsStyle.entry.height + entryWidth: CallsStyle.entry.width + + Repeater { + model: params.actions + + ActionMenuEntry { + entryName: modelData.name + + onClicked: { + menu.hideMenu() + params.actions[index].handler(call) + } + } + } + } + } + } + } + + // --------------------------------------------------------------------------- + + delegate: CallControls { + id: _callControls + + signIcon: _getSignIcon($call) + sipAddress: $call.sipAddress + width: parent.width + + Loader { + property var call: $call + property var callControls: _callControls + property var params: _getParams($call) + + anchors.centerIn: parent + sourceComponent: params.component + } + } +} diff --git a/tests/ui/modules/Linphone/SmartSearchBar.qml b/tests/ui/modules/Linphone/SmartSearchBar.qml index 014c00016..9e55bdf11 100644 --- a/tests/ui/modules/Linphone/SmartSearchBar.qml +++ b/tests/ui/modules/Linphone/SmartSearchBar.qml @@ -77,7 +77,7 @@ SearchBox { Layout.fillHeight: true Layout.fillWidth: true - entry: Object ({ + entry: ({ sipAddress: interpretableSipAddress }) } diff --git a/tests/ui/modules/Linphone/Styles/Calls/CallControlsStyle.qml b/tests/ui/modules/Linphone/Styles/Calls/CallControlsStyle.qml new file mode 100644 index 000000000..60516875a --- /dev/null +++ b/tests/ui/modules/Linphone/Styles/Calls/CallControlsStyle.qml @@ -0,0 +1,15 @@ +pragma Singleton +import QtQuick 2.7 + +import Common 1.0 + +// ============================================================================= + +QtObject { + property color color: Colors.e + property int height: 60 + property int leftMargin: 12 + property int rightMargin: 12 + property int signSize: 40 + property int width: 240 +} diff --git a/tests/ui/modules/Linphone/Styles/Calls/CallsStyle.qml b/tests/ui/modules/Linphone/Styles/Calls/CallsStyle.qml new file mode 100644 index 000000000..24b9115f4 --- /dev/null +++ b/tests/ui/modules/Linphone/Styles/Calls/CallsStyle.qml @@ -0,0 +1,15 @@ +pragma Singleton +import QtQuick 2.7 + +import Common 1.0 + +// ============================================================================= + +QtObject { + property QtObject entry: QtObject { + property int iconActionSize: 30 + property int iconMenuSize: 17 + property int height: 30 + property int width: 200 + } +} diff --git a/tests/ui/modules/Linphone/Styles/qmldir b/tests/ui/modules/Linphone/Styles/qmldir index 9b23e21cf..0975f56a2 100644 --- a/tests/ui/modules/Linphone/Styles/qmldir +++ b/tests/ui/modules/Linphone/Styles/qmldir @@ -8,6 +8,9 @@ singleton AccountStatusStyle 1.0 Account/AccountStatusStyle.qml singleton ChatStyle 1.0 ChatStyle.qml +singleton CallsStyle 1.0 Calls/CallsStyle.qml +singleton CallControlsStyle 1.0 Calls/CallControlsStyle.qml + singleton AvatarStyle 1.0 Contact/AvatarStyle.qml singleton ContactDescriptionStyle 1.0 Contact/ContactDescriptionStyle.qml singleton ContactStyle 1.0 Contact/ContactStyle.qml diff --git a/tests/ui/modules/Linphone/qmldir b/tests/ui/modules/Linphone/qmldir index 1c3b0321d..d89a4ba52 100644 --- a/tests/ui/modules/Linphone/qmldir +++ b/tests/ui/modules/Linphone/qmldir @@ -9,8 +9,8 @@ module Linphone # Account AccountStatus 1.0 Account/AccountStatus.qml -# Call -CallControls 1.0 Call/CallControls.qml +# Calls +Calls 1.0 Calls/Calls.qml # Chat Chat 1.0 Chat/Chat.qml diff --git a/tests/ui/views/App/Calls/Calls.qml b/tests/ui/views/App/Calls/Calls.qml index db72c322a..58f31cd1e 100644 --- a/tests/ui/views/App/Calls/Calls.qml +++ b/tests/ui/views/App/Calls/Calls.qml @@ -9,11 +9,25 @@ import Linphone 1.0 import App.Styles 1.0 -// =================================================================== +// ============================================================================= Window { id: window + // --------------------------------------------------------------------------- + + function launchAudioCall (sipAddress) { + window.show() + + } + + function launchVideoCall (sipAddress) { + window.show() + + } + + // --------------------------------------------------------------------------- + minimumHeight: 480 minimumWidth: 960 @@ -33,6 +47,7 @@ Window { ColumnLayout { anchors.fill: parent + spacing: 0 Item { Layout.fillWidth: true @@ -69,52 +84,15 @@ Window { } } - ListView { - Layout.fillWidth: true + Calls { Layout.fillHeight: true - spacing: 0 + Layout.fillWidth: true + + model: CallsListModel } } } - /* childA: ColumnLayout { */ - /* anchors.fill: parent */ - /* spacing: 0 */ - - /* Rectangle { */ - /* Layout.fillWidth: true */ - /* Layout.preferredHeight: 50 */ - /* color: '#FFFFFF' */ - - /* ActionBar { */ - /* anchors.verticalCenter: parent.verticalCenter */ - /* anchors.leftMargin: 10 */ - /* anchors.left: parent.left */ - /* iconSize: 30 */ - /* spacing: 16 */ - - /* ActionButton { */ - /* icon: 'call' */ - /* } */ - - /* ActionButton { */ - /* icon: 'conference' */ - /* } */ - /* } */ - /* } */ - - /* ScrollableListView { */ - /* Layout.fillWidth: true */ - /* Layout.fillHeight: true */ - /* spacing: 1 */ - /* delegate: CallControls { */ - /* width: parent.width */ - /* } */ - - /* model: callsList */ - /* } */ - /* } */ - // --------------------------------------------------------------- // Content. // --------------------------------------------------------------- diff --git a/tests/ui/views/App/MainWindow/Contacts.qml b/tests/ui/views/App/MainWindow/Contacts.qml index cbd91938f..dadfe78d2 100644 --- a/tests/ui/views/App/MainWindow/Contacts.qml +++ b/tests/ui/views/App/MainWindow/Contacts.qml @@ -131,12 +131,12 @@ ColumnLayout { ActionButton { icon: 'video_call' - onClicked: CallsWindow.show() + onClicked: CallsWindow.launchVideoCall($contact.vcard.sipAddresses[0]) // FIXME: Display menu if many addresses. } ActionButton { icon: 'call' - onClicked: CallsWindow.show() + onClicked: CallsWindow.launchAudioCall($contact.vcard.sipAddresses[0]) // FIXME: Display menu if many addresses. } ActionButton { diff --git a/tests/ui/views/App/MainWindow/Conversation.qml b/tests/ui/views/App/MainWindow/Conversation.qml index 193f6a330..878f7c4b6 100644 --- a/tests/ui/views/App/MainWindow/Conversation.qml +++ b/tests/ui/views/App/MainWindow/Conversation.qml @@ -15,9 +15,7 @@ ColumnLayout { property string sipAddress - property var _contact: SipAddressesModel.mapSipAddressToContact( - sipAddress - ) || sipAddress + property var _contact: SipAddressesModel.mapSipAddressToContact(sipAddress) function _removeAllEntries () { Utils.openConfirmDialog(window, { @@ -57,9 +55,9 @@ ColumnLayout { Layout.preferredHeight: ConversationStyle.bar.avatarSize Layout.preferredWidth: ConversationStyle.bar.avatarSize - image: _contact.vcard ? _contact.vcard.avatar : '' - presenceLevel: _contact.presenceLevel || -1 - username: LinphoneUtils.getContactUsername(_contact) + image: _contact ? _contact.vcard.avatar : '' + presenceLevel: _contact ? _contact.presenceLevel : -1 + username: LinphoneUtils.getContactUsername(_contact || conversation.sipAddress) } ContactDescription { @@ -81,12 +79,12 @@ ColumnLayout { ActionButton { icon: 'video_call' - onClicked: CallsWindow.show() + onClicked: CallsWindow.launchVideoCall(conversation.sipAddress) } ActionButton { icon: 'call' - onClicked: CallsWindow.show() + onClicked: CallsWindow.launchAudioCall(conversation.sipAddress) } } @@ -94,7 +92,7 @@ ColumnLayout { anchors.verticalCenter: parent.verticalCenter ActionButton { - icon: Utils.isString(_contact) ? 'contact_add' : 'contact_edit' + icon: _contact ? 'contact_add' : 'contact_edit' iconSize: ConversationStyle.bar.actions.edit.iconSize onClicked: window.setView('ContactEdit', { diff --git a/tests/ui/views/App/MainWindow/MainWindow.qml b/tests/ui/views/App/MainWindow/MainWindow.qml index c6a6a3a78..95a04d84e 100644 --- a/tests/ui/views/App/MainWindow/MainWindow.qml +++ b/tests/ui/views/App/MainWindow/MainWindow.qml @@ -159,14 +159,15 @@ ApplicationWindow { sipAddress: sipAddress }) } - onLaunchCall: CallsWindow.show() + + onLaunchCall: CallsWindow.launchAudioCall(sipAddress) onLaunchChat: { window.ensureCollapsed() window.setView('Conversation', { sipAddress: sipAddress }) } - onLaunchVideoCall: CallsWindow.show() + onLaunchVideoCall: CallsWindow.launchVideoCall(sipAddress) onEntryClicked: { window.ensureCollapsed()