diff --git a/Linphone/CMakeLists.txt b/Linphone/CMakeLists.txt index 18cbda4b6..3dc6b9740 100644 --- a/Linphone/CMakeLists.txt +++ b/Linphone/CMakeLists.txt @@ -35,6 +35,9 @@ if(NOT WIN32) add_compile_options(-Werror=deprecated-declarations) endif() set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -DNDEBUG -DQT_NO_DEBUG") +set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO} -DQT_QML_DEBUG -DQT_DECLARATIVE_DEBUG") +set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -DDEBUG -DQT_QML_DEBUG -DQT_DECLARATIVE_DEBUG" ) set(CMAKE_INCLUDE_CURRENT_DIR ON)#useful for config.h include(application_info.cmake) diff --git a/Linphone/core/App.cpp b/Linphone/core/App.cpp index 3e5a1a029..7f5f36b19 100644 --- a/Linphone/core/App.cpp +++ b/Linphone/core/App.cpp @@ -23,13 +23,18 @@ #include "App.hpp" #include +#include #include +#include #include +#include +#include #include "core/account/Account.hpp" #include "core/account/AccountProxy.hpp" #include "core/logger/QtLogger.hpp" #include "core/login/LoginPage.hpp" +#include "core/notifier/Notifier.hpp" #include "core/phone-number/PhoneNumber.hpp" #include "core/phone-number/PhoneNumberProxy.hpp" #include "core/singleapplication/singleapplication.h" @@ -50,6 +55,9 @@ App *App::getInstance() { return dynamic_cast(QApplication::instance()); } +Notifier *App::getNotifier() const { + return mNotifier; +} //----------------------------------------------------------- // Initializations //----------------------------------------------------------- @@ -76,11 +84,22 @@ void App::init() { // QML mEngine = new QQmlApplicationEngine(this); + // Provide `+custom` folders for custom components and `5.9` for old components. + QStringList selectors("custom"); + const QVersionNumber &version = QLibraryInfo::version(); + if (version.majorVersion() == 5 && version.minorVersion() == 9) selectors.push_back("5.9"); + auto selector = new QQmlFileSelector(mEngine, mEngine); + selector->setExtraSelectors(selectors); + qInfo() << QStringLiteral("[App] Activated selectors:") << selector->selector()->allSelectors(); + mEngine->addImportPath(":/"); mEngine->rootContext()->setContextProperty("applicationDirPath", QGuiApplication::applicationDirPath()); initCppInterfaces(); mEngine->addImageProvider(ImageProvider::ProviderId, new ImageProvider()); + // Enable notifications. + mNotifier = new Notifier(mEngine); + const QUrl url(u"qrc:/Linphone/view/App/Main.qml"_qs); QObject::connect( mEngine, &QQmlApplicationEngine::objectCreated, this, @@ -110,6 +129,7 @@ void App::initCppInterfaces() { qmlRegisterUncreatableType(Constants::MainQmlUri, 1, 0, "PhoneNumber", QLatin1String("Uncreatable")); qmlRegisterType(Constants::MainQmlUri, 1, 0, "AccountProxy"); qmlRegisterUncreatableType(Constants::MainQmlUri, 1, 0, "Account", QLatin1String("Uncreatable")); + qmlRegisterUncreatableType(Constants::MainQmlUri, 1, 0, "Call", QLatin1String("Uncreatable")); LinphoneEnums::registerMetaTypes(); } diff --git a/Linphone/core/App.hpp b/Linphone/core/App.hpp index da2256d63..0e0ced4e0 100644 --- a/Linphone/core/App.hpp +++ b/Linphone/core/App.hpp @@ -26,11 +26,13 @@ #include "model/core/CoreModel.hpp" class Thread; +class Notifier; class App : public SingleApplication { public: App(int &argc, char *argv[]); static App *getInstance(); + Notifier *getNotifier() const; // App::postModelAsync() => run lambda in model thread and continue. // App::postModelSync() => run lambda in current thread and block connection. @@ -43,6 +45,14 @@ public: QMetaObject::invokeMethod(CoreModel::getInstance().get(), callable); } template + static auto postCoreAsync(Func &&callable, Args &&...args) { + QMetaObject::invokeMethod(App::getInstance(), callable, args...); + } + template + static auto postCoreAsync(Func &&callable) { + QMetaObject::invokeMethod(App::getInstance(), callable); + } + template static auto postModelSync(Func &&callable, Args &&...args) { if (QThread::currentThread() != CoreModel::getInstance()->thread()) { bool end = false; @@ -70,4 +80,5 @@ private: QCommandLineParser *mParser = nullptr; Thread *mLinphoneThread = nullptr; + Notifier *mNotifier = nullptr; }; diff --git a/Linphone/core/CMakeLists.txt b/Linphone/core/CMakeLists.txt index 932fe46be..c1bd2d134 100644 --- a/Linphone/core/CMakeLists.txt +++ b/Linphone/core/CMakeLists.txt @@ -3,8 +3,10 @@ list(APPEND _LINPHONEAPP_SOURCES core/account/AccountList.cpp core/account/AccountProxy.cpp core/App.cpp + core/call/Call.cpp core/logger/QtLogger.cpp core/login/LoginPage.cpp + core/notifier/Notifier.cpp core/path/Paths.cpp core/phone-number/PhoneNumber.cpp core/phone-number/PhoneNumberList.cpp diff --git a/Linphone/core/call/Call.cpp b/Linphone/core/call/Call.cpp new file mode 100644 index 000000000..3d281050d --- /dev/null +++ b/Linphone/core/call/Call.cpp @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2010-2024 Belledonne Communications SARL. + * + * This file is part of linphone-desktop + * (see https://www.linphone.org). + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "Call.hpp" +#include "core/App.hpp" +#include "tool/Utils.hpp" + +DEFINE_ABSTRACT_OBJECT(Call) + +Call::Call(const std::shared_ptr &call) : QObject(nullptr) { + // Should be call from model Thread + mustBeInLinphoneThread(getClassName()); + mCallModel = Utils::makeQObject_ptr(call); + connect(mCallModel.get(), &CallModel::stateChanged, this, &Call::onStateChanged); + connect(this, &Call::lAccept, mCallModel.get(), &CallModel::accept); + connect(this, &Call::lDecline, mCallModel.get(), &CallModel::decline); + connect(this, &Call::lTerminate, mCallModel.get(), &CallModel::terminate); + mCallModel->setSelf(mCallModel); + mState = LinphoneEnums::fromLinphone(call->getState()); +} + +Call::~Call() { + mustBeInMainThread("~" + getClassName()); + emit mCallModel->removeListener(); +} + +LinphoneEnums::CallStatus Call::getStatus() const { + return mStatus; +} + +void Call::setStatus(LinphoneEnums::CallStatus status) { + mustBeInMainThread(log().arg(Q_FUNC_INFO)); + if (mStatus != status) { + mStatus = status; + emit statusChanged(mStatus); + } +} + +LinphoneEnums::CallState Call::getState() const { + return mState; +} + +void Call::setState(LinphoneEnums::CallState state, const QString &message) { + mustBeInMainThread(log().arg(Q_FUNC_INFO)); + if (mState != state) { + mState = state; + if (state == LinphoneEnums::CallState::Error) setLastErrorMessage(message); + emit stateChanged(mState); + } +} + +void Call::onStateChanged(linphone::Call::State state, const std::string &message) { + setState(LinphoneEnums::fromLinphone(state), Utils::coreStringToAppString(message)); +} + +QString Call::getLastErrorMessage() const { + return mLastErrorMessage; +} +void Call::setLastErrorMessage(const QString &message) { + if (mLastErrorMessage != message) { + mLastErrorMessage = message; + emit lastErrorMessageChanged(); + } +} diff --git a/Linphone/core/call/Call.hpp b/Linphone/core/call/Call.hpp new file mode 100644 index 000000000..006a17028 --- /dev/null +++ b/Linphone/core/call/Call.hpp @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2010-2024 Belledonne Communications SARL. + * + * This file is part of linphone-desktop + * (see https://www.linphone.org). + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef CALL_H_ +#define CALL_H_ + +#include "model/call/CallModel.hpp" +#include "tool/LinphoneEnums.hpp" +#include +#include +#include + +class Call : public QObject, public AbstractObject { + Q_OBJECT + + Q_PROPERTY(LinphoneEnums::CallStatus status READ getStatus NOTIFY statusChanged) + Q_PROPERTY(LinphoneEnums::CallState state READ getState NOTIFY stateChanged) + Q_PROPERTY(QString lastErrorMessage READ getLastErrorMessage NOTIFY lastErrorMessageChanged) + +public: + // Should be call from model Thread. Will be automatically in App thread after initialization + Call(const std::shared_ptr &call); + ~Call(); + + LinphoneEnums::CallStatus getStatus() const; + void setStatus(LinphoneEnums::CallStatus status); + + LinphoneEnums::CallState getState() const; + void setState(LinphoneEnums::CallState state, const QString &message); + void onStateChanged(linphone::Call::State state, const std::string &message); + + QString getLastErrorMessage() const; + void setLastErrorMessage(const QString &message); + +signals: + void statusChanged(LinphoneEnums::CallStatus status); + void stateChanged(LinphoneEnums::CallState state); + void lastErrorMessageChanged(); + + // Linphone commands + void lAccept(bool withVideo); // Accept an incoming call + void lDecline(); // Decline an incoming call + void lTerminate(); // Hangup a call + /* TODO + Q_INVOKABLE void acceptWithVideo(); + + Q_INVOKABLE void askForTransfer(); + Q_INVOKABLE void askForAttendedTransfer(); + Q_INVOKABLE bool transferTo(const QString &sipAddress); + Q_INVOKABLE bool transferToAnother(const QString &peerAddress); + + Q_INVOKABLE bool getRemoteVideoEnabled() const; + Q_INVOKABLE void acceptVideoRequest(); + Q_INVOKABLE void rejectVideoRequest(); + + Q_INVOKABLE void takeSnapshot(); + Q_INVOKABLE void startRecording(); + Q_INVOKABLE void stopRecording(); + + Q_INVOKABLE void sendDtmf(const QString &dtmf); + Q_INVOKABLE void verifyAuthenticationToken(bool verify); + Q_INVOKABLE void updateStreams(); + Q_INVOKABLE void toggleSpeakerMute(); + */ +private: + std::shared_ptr mCallModel; + LinphoneEnums::CallStatus mStatus; + LinphoneEnums::CallState mState; + QString mLastErrorMessage; + + DECLARE_ABSTRACT_OBJECT +}; +Q_DECLARE_METATYPE(Call *) +#endif diff --git a/Linphone/core/notifier/Notifier.cpp b/Linphone/core/notifier/Notifier.cpp new file mode 100644 index 000000000..f94003969 --- /dev/null +++ b/Linphone/core/notifier/Notifier.cpp @@ -0,0 +1,420 @@ +/* + * Copyright (c) 2010-2024 Belledonne Communications SARL. + * + * This file is part of linphone-desktop + * (see https://www.linphone.org). + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "Notifier.hpp" + +#include "core/App.hpp" +#include "core/call/Call.hpp" +#include "tool/LinphoneEnums.hpp" +#include "tool/providers/ImageProvider.hpp" + +DEFINE_ABSTRACT_OBJECT(Notifier) + +// ============================================================================= + +using namespace std; + +namespace { +constexpr char NotificationsPath[] = "qrc:/Linphone/view/Item/Notification/"; + +// --------------------------------------------------------------------------- +// Notifications QML properties/methods. +// --------------------------------------------------------------------------- + +constexpr char NotificationShowMethodName[] = "open"; + +constexpr char NotificationPropertyData[] = "notificationData"; + +constexpr char NotificationPropertyX[] = "popupX"; +constexpr char NotificationPropertyY[] = "popupY"; + +constexpr char NotificationPropertyWindow[] = "__internalWindow"; + +constexpr char NotificationPropertyTimer[] = "__timer"; + +// --------------------------------------------------------------------------- +// Arbitrary hardcoded values. +// --------------------------------------------------------------------------- + +constexpr int NotificationSpacing = 10; +constexpr int MaxNotificationsNumber = 5; +} // namespace + +// ============================================================================= + +template +void setProperty(QObject &object, const char *property, const T &value) { + if (!object.setProperty(property, QVariant(value))) { + qWarning() << QStringLiteral("Unable to set property: `%1`.").arg(property); + abort(); + } +} + +// ============================================================================= +// Available notifications. +// ============================================================================= + +const QHash Notifier::Notifications = { + //{Notifier::ReceivedMessage, {Notifier::ReceivedMessage, "NotificationReceivedMessage.qml", 10}}, + //{Notifier::ReceivedFileMessage, {Notifier::ReceivedFileMessage, "NotificationReceivedFileMessage.qml", 10}}, + {Notifier::ReceivedCall, {Notifier::ReceivedCall, "NotificationReceivedCall.qml", 30}}, + //{Notifier::NewVersionAvailable, {Notifier::NewVersionAvailable, "NotificationNewVersionAvailable.qml", 30}}, + //{Notifier::SnapshotWasTaken, {Notifier::SnapshotWasTaken, "NotificationSnapshotWasTaken.qml", 10}}, + //{Notifier::RecordingCompleted, {Notifier::RecordingCompleted, "NotificationRecordingCompleted.qml", 10}} +}; + +// ----------------------------------------------------------------------------- + +Notifier::Notifier(QObject *parent) : QObject(parent) { + mustBeInMainThread(getClassName()); + const int nComponents = Notifications.size(); + mComponents = new QQmlComponent *[nComponents]; + + QQmlEngine *engine = App::getInstance()->mEngine; + for (const auto &key : Notifications.keys()) { + QQmlComponent *component = + new QQmlComponent(engine, QUrl(NotificationsPath + Notifier::Notifications[key].filename)); + if (Q_UNLIKELY(component->isError())) { + qWarning() << QStringLiteral("Errors found in `Notification` component %1:").arg(key) + << component->errors(); + abort(); + } + mComponents[key] = component; + } + + mMutex = new QMutex(); +} + +Notifier::~Notifier() { + mustBeInMainThread("~" + getClassName()); + delete mMutex; + + const int nComponents = Notifications.size(); + for (int i = 0; i < nComponents; ++i) + mComponents[i]->deleteLater(); + delete[] mComponents; +} + +// ----------------------------------------------------------------------------- + +QObject *Notifier::createNotification(Notifier::NotificationType type, QVariantMap data) { + QQuickItem *wrapperItem = nullptr; + mMutex->lock(); + Q_ASSERT(mInstancesNumber <= MaxNotificationsNumber); + if (mInstancesNumber == MaxNotificationsNumber) { // Check existing instances. + qWarning() << QStringLiteral("Unable to create another notification."); + mMutex->unlock(); + return nullptr; + } + QList allScreens = QGuiApplication::screens(); + if (allScreens.size() > 0) { // Ensure to have a screen to avoid errors + QQuickItem *previousWrapper = nullptr; + ++mInstancesNumber; + bool showAsTool = false; +#ifdef Q_OS_MACOS + for (auto w : QGuiApplication::topLevelWindows()) { + if ((w->windowState() & Qt::WindowFullScreen) == Qt::WindowFullScreen) { + showAsTool = true; + w->raise(); // Used to get focus on Mac (On Mac, A Tool is hidden if the app has not focus and the only + // way to rid it is to use Widget Attributes(Qt::WA_MacAlwaysShowToolWindow) that is not + // available) + } + } +#endif + for (int i = 0; i < allScreens.size(); ++i) { + + // Use QQuickView to create a visual root object that is + // independant from current application Window + QScreen *screen = allScreens[i]; + // auto engine = App::getInstance()->mEngine; + auto engine = new QQmlApplicationEngine(); + engine->addImageProvider(ImageProvider::ProviderId, new ImageProvider()); + engine->addImportPath(":/"); + // if(showAsTool) window->setProperty("showAsTool",true); + engine->setInitialProperties(data); + // engine->rootContext()->setContextProperty("applicationDirPath",QGuiApplication::applicationDirPath()); + // engine->setInitialProperties({{"screenIndex", i}}); + //, {"x", screen->geometry().x()}, {"y", screen->geometry().y()}}); + const QUrl url(QString(NotificationsPath) + Notifier::Notifications[type].filename); + QObject::connect( + engine, &QQmlApplicationEngine::objectCreated, this, + [this, url, screen, engine](QObject *obj, const QUrl &objUrl) { + if (!obj && url == objUrl) { + qCritical() << "[App] Notifier.qml couldn't be load."; + engine->deleteLater(); + exit(-1); + } else { + qWarning() << engine->rootObjects()[0]; + auto window = qobject_cast(obj); + if (window) { + int *screenHeightOffset = &mScreenHeightOffset[screen->name()]; // Access optimization + QRect availableGeometry = screen->availableGeometry(); + int heightOffset = + availableGeometry.y() + + (availableGeometry.height() - + window->height()); //*screen->devicePixelRatio(); when using manual scaler + + window->setX(availableGeometry.x() + + (availableGeometry.width() - + window->property("width") + .toInt())); //*screen->devicePixelRatio()); when using manual scaler + window->setY(heightOffset - (*screenHeightOffset % heightOffset)); + qWarning() << window->geometry(); + } + } + }, + Qt::QueuedConnection); + engine->load(url); + } + qInfo() << QStringLiteral("Create notifications:") << wrapperItem; + } + + mMutex->unlock(); + return wrapperItem; +} + +// ----------------------------------------------------------------------------- + +void Notifier::showNotification(QObject *notification, int timeout) { + // Display notification. + QMetaObject::invokeMethod(notification, NotificationShowMethodName, Qt::DirectConnection); + + QTimer *timer = new QTimer(notification); + timer->setInterval(timeout); + timer->setSingleShot(true); + notification->setProperty(NotificationPropertyTimer, QVariant::fromValue(timer)); + + // Destroy it after timeout. + QObject::connect(timer, &QTimer::timeout, this, + [this, notification]() { deleteNotificationOnTimeout(QVariant::fromValue(notification)); }); + + // Called explicitly (by a click on notification for example) + QObject::connect(notification, SIGNAL(deleteNotification(QVariant)), this, SLOT(deleteNotification(QVariant))); + + timer->start(); +} + +// ----------------------------------------------------------------------------- +void Notifier::deleteNotificationOnTimeout(QVariant notification) { +#ifdef Q_OS_MACOS + for (auto w : QGuiApplication::topLevelWindows()) { + if ((w->windowState() & Qt::WindowFullScreen) == Qt::WindowFullScreen) { + w->requestActivate(); // Used to get focus on fullscreens on Mac in order to avoid screen switching. + } + } +#endif + deleteNotification(notification); +} + +void Notifier::deleteNotification(QVariant notification) { + mMutex->lock(); + + QObject *instance = notification.value(); + + // Notification marked destroyed. + if (instance->property("__valid").isValid()) { + mMutex->unlock(); + return; + } + + qInfo() << QStringLiteral("Delete notification:") << instance; + + instance->setProperty("__valid", true); + instance->property(NotificationPropertyTimer).value()->stop(); + + mInstancesNumber--; + Q_ASSERT(mInstancesNumber >= 0); + + if (mInstancesNumber == 0) mScreenHeightOffset.clear(); + + mMutex->unlock(); + + instance->deleteLater(); +} + +// ============================================================================= + +#define CREATE_NOTIFICATION(TYPE, DATA) \ + QObject *notification = createNotification(TYPE, DATA); \ + if (!notification) return; \ + const int timeout = Notifications[TYPE].getTimeout() * 1000; \ + showNotification(notification, timeout); + +// ----------------------------------------------------------------------------- +// Notification functions. +// ----------------------------------------------------------------------------- + +void Notifier::notifyReceivedCall(const shared_ptr &call) { + mustBeInLinphoneThread(log().arg(Q_FUNC_INFO)); + auto model = new Call(call); + model->moveToThread(this->thread()); + App::postCoreAsync([this, model]() { + mustBeInMainThread(getClassName()); + QVariantMap map; + map["call"].setValue(model); + CREATE_NOTIFICATION(Notifier::ReceivedCall, map) + + QObject::connect( + model, &Call::statusChanged, notification, [this, notification](LinphoneEnums::CallStatus status) { + qInfo() << log().arg("Delete notification on call status : %1").arg(LinphoneEnums::toString(status)); + deleteNotification(QVariant::fromValue(notification)); + }); + QObject::connect(model, &Call::destroyed, notification, + [this, notification]() { deleteNotification(QVariant::fromValue(notification)); }); + }); +} + +/* +void Notifier::notifyReceivedMessages(const list> &messages) { + QVariantMap map; + QString txt; + if (messages.size() > 0) { + shared_ptr message = messages.front(); + + if (messages.size() == 1) { + auto fileContent = message->getFileTransferInformation(); + if (!fileContent) { + foreach (auto content, message->getContents()) { + if (content->isText()) txt += content->getUtf8Text().c_str(); + } + } else if (fileContent->isVoiceRecording()) + //: 'Voice message received!' : message to warn the user in a notofication for voice messages. + txt = tr("newVoiceMessage"); + else txt = tr("newFileMessage"); + if (txt.isEmpty() && message->hasConferenceInvitationContent()) + //: 'Conference invitation received!' : Notification about receiving an invitation to a conference. + txt = tr("newConferenceInvitation"); + } else + //: 'New messages received!' Notification that warn the user of new messages. + txt = tr("newChatRoomMessages"); + map["message"] = txt; + shared_ptr chatRoom(message->getChatRoom()); + map["timelineModel"].setValue( + CoreManager::getInstance()->getTimelineListModel()->getTimeline(chatRoom, true).get()); + if (messages.size() == 1) { // Display only sender on mono message. + map["peerAddress"] = Utils::coreStringToAppString(message->getFromAddress()->asStringUriOnly()); + map["fullPeerAddress"] = Utils::coreStringToAppString(message->getFromAddress()->asString()); + } + map["localAddress"] = Utils::coreStringToAppString(message->getToAddress()->asStringUriOnly()); + map["fullLocalAddress"] = Utils::coreStringToAppString(message->getToAddress()->asString()); + map["window"].setValue(App::getInstance()->getMainWindow()); + CREATE_NOTIFICATION(Notifier::ReceivedMessage, map) + } +} + +void Notifier::notifyReceivedReactions( + const QList, std::shared_ptr>> + &reactions) { + QVariantMap map; + QString txt; + + if (reactions.size() > 0) { + ChatMessageModel *redirection = nullptr; + QPair, std::shared_ptr> reaction = + reactions.front(); + shared_ptr message = reaction.first; + shared_ptr chatRoom(message->getChatRoom()); + auto timelineModel = CoreManager::getInstance()->getTimelineListModel()->getTimeline(chatRoom, true); + map["messageId"] = Utils::coreStringToAppString(message->getMessageId()); + if (reactions.size() == 1) { + QString messageTxt; + auto fileContent = message->getFileTransferInformation(); + if (!fileContent) { + foreach (auto content, message->getContents()) { + if (content->isText()) messageTxt += content->getUtf8Text().c_str(); + } + } else if (fileContent->isVoiceRecording()) + //: 'Voice message' : Voice message type that has been reacted. + messageTxt += tr("voiceMessageReact"); + else { + QFileInfo file(Utils::coreStringToAppString(fileContent->getFilePath())); + messageTxt += file.fileName(); + } + if (messageTxt.isEmpty() && message->hasConferenceInvitationContent()) + //: 'Conference invitation' : Conference invitation message type that has been reacted. + messageTxt += tr("conferenceInvitationReact"); + //: ''Has reacted by %1 to: %2' : Reaction message. %1=Reaction(emoji), %2=type of message(Voice + //: Message/Conference invitation/ Message text) + txt = tr("reactionMessage").arg(Utils::coreStringToAppString(reaction.second->getBody())).arg(messageTxt); + + } else + //: 'New reactions received!' : Notification that warn the user of new reactions. + txt = tr("newReactionsMessages"); + map["message"] = txt; + + map["timelineModel"].setValue(timelineModel.get()); + if (reactions.size() == 1) { // Display only sender on mono message. + map["peerAddress"] = Utils::coreStringToAppString(reaction.second->getFromAddress()->asStringUriOnly()); + map["fullPeerAddress"] = Utils::coreStringToAppString(reaction.second->getFromAddress()->asString()); + } + map["localAddress"] = Utils::coreStringToAppString(chatRoom->getLocalAddress()->asStringUriOnly()); + map["fullLocalAddress"] = Utils::coreStringToAppString(chatRoom->getLocalAddress()->asString()); + map["window"].setValue(App::getInstance()->getMainWindow()); + CREATE_NOTIFICATION(Notifier::ReceivedMessage, map) + } +} + +void Notifier::notifyReceivedFileMessage(const shared_ptr &message, + const shared_ptr &content) { + QVariantMap map; + shared_ptr chatRoom(message->getChatRoom()); + map["timelineModel"].setValue( + CoreManager::getInstance()->getTimelineListModel()->getTimeline(chatRoom, true).get()); + map["fileUri"] = Utils::coreStringToAppString(content->getFilePath()); + if (Utils::getImage(map["fileUri"].toString()).isNull()) map["imageUri"] = ""; + else map["imageUri"] = map["fileUri"]; + map["fileSize"] = quint64(content->getSize() + content->getFileSize()); + CREATE_NOTIFICATION(Notifier::ReceivedFileMessage, map) +} + + + +void Notifier::notifyNewVersionAvailable(const QString &version, const QString &url) { + QVariantMap map; + map["message"] = tr("newVersionAvailable").arg(version); + map["url"] = url; + CREATE_NOTIFICATION(Notifier::NewVersionAvailable, map) +} + +void Notifier::notifySnapshotWasTaken(const QString &filePath) { + QVariantMap map; + map["filePath"] = filePath; + CREATE_NOTIFICATION(Notifier::SnapshotWasTaken, map) +} + +void Notifier::notifyRecordingCompleted(const QString &filePath) { + QVariantMap map; + map["filePath"] = filePath; + CREATE_NOTIFICATION(Notifier::RecordingCompleted, map) +} +*/ +#undef SHOW_NOTIFICATION +#undef CREATE_NOTIFICATION diff --git a/Linphone/core/notifier/Notifier.hpp b/Linphone/core/notifier/Notifier.hpp new file mode 100644 index 000000000..9f67e2468 --- /dev/null +++ b/Linphone/core/notifier/Notifier.hpp @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2010-2024 Belledonne Communications SARL. + * + * This file is part of linphone-desktop + * (see https://www.linphone.org). + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef NOTIFIER_H_ +#define NOTIFIER_H_ + +#include + +#include "core/call/Call.hpp" +#include "tool/AbstractObject.hpp" +#include +#include +// ============================================================================= + +class QMutex; +class QQmlComponent; + +class Notifier : public QObject, public AbstractObject { + Q_OBJECT + +public: + Notifier(QObject *parent = Q_NULLPTR); + ~Notifier(); + + enum NotificationType { + ReceivedMessage, + ReceivedFileMessage, + ReceivedCall, + NewVersionAvailable, + SnapshotWasTaken, + RecordingCompleted + }; + + // void notifyReceivedCall(Call *call); + void notifyReceivedCall(const std::shared_ptr &call); // Call from Linphone + + /* + void notifyReceivedMessages(const std::list> &messages); + void notifyReceivedReactions( + const QList, std::shared_ptr>> &reactions); void notifyReceivedFileMessage(const + std::shared_ptr &message, const std::shared_ptr &content); + + void notifyNewVersionAvailable(const QString &version, const QString &url); + void notifySnapshotWasTaken(const QString &filePath); + void notifyRecordingCompleted(const QString &filePath); + */ + +public slots: + void deleteNotificationOnTimeout(QVariant notification); + void deleteNotification(QVariant notification); + +private: + struct Notification { + Notification(const int &type = 0, const QString &filename = QString(""), int timeout = 0) { + this->type = type; + this->filename = filename; + this->timeout = timeout; + } + int getTimeout() const { + if (type == Notifier::ReceivedCall) { + // return CoreManager::getInstance()->getSettingsModel()->getIncomingCallTimeout(); + return 30; + } else return timeout; + } + QString filename; + + private: + int timeout; + int type; + }; + + QObject *createNotification(NotificationType type, QVariantMap data); + void showNotification(QObject *notification, int timeout); + + QHash mScreenHeightOffset; + int mInstancesNumber = 0; + + QMutex *mMutex = nullptr; + QQmlComponent **mComponents = nullptr; + + static const QHash Notifications; + + DECLARE_ABSTRACT_OBJECT +}; + +#endif // NOTIFIER_H_ diff --git a/Linphone/main.cpp b/Linphone/main.cpp index 2513a215e..fcd63346a 100644 --- a/Linphone/main.cpp +++ b/Linphone/main.cpp @@ -3,10 +3,31 @@ #include #include +#include #include "core/App.hpp" +#ifdef QT_QML_DEBUG +#include +#endif + +#if QT_VERSION < QT_VERSION_CHECK(5, 15, 10) +// From 5.15.2 to 5.15.10, sometimes, Accessibility freeze the application : Deactivate handlers. +#define ACCESSBILITY_WORKAROUND +#include +#include +void DummyUpdateHandler(QAccessibleEvent *event) { +} +void DummyRootObjectHandler(QObject *) { +} +#endif + int main(int argc, char *argv[]) { + // Useful to share camera on Fullscreen (other context) or multiscreens + QApplication::setAttribute(Qt::AA_ShareOpenGLContexts); + // Disable QML cache. Avoid malformed cache. + qputenv("QML_DISABLE_DISK_CACHE", "true"); + App app(argc, argv); QTranslator translator; @@ -19,6 +40,11 @@ int main(int argc, char *argv[]) { } } +#ifdef ACCESSBILITY_WORKAROUND + QAccessible::installUpdateHandler(DummyUpdateHandler); + QAccessible::installRootObjectHandler(DummyRootObjectHandler); +#endif + int result = 0; while (result >= 0) { result = app.exec(); diff --git a/Linphone/model/CMakeLists.txt b/Linphone/model/CMakeLists.txt index 6cb66ac22..459b17810 100644 --- a/Linphone/model/CMakeLists.txt +++ b/Linphone/model/CMakeLists.txt @@ -1,9 +1,10 @@ list(APPEND _LINPHONEAPP_SOURCES model/account/AccountModel.cpp model/account/AccountManager.cpp + + model/call/CallModel.cpp model/core/CoreModel.cpp - model/core/CoreListener.cpp model/listener/Listener.hpp diff --git a/Linphone/model/account/AccountManager.cpp b/Linphone/model/account/AccountManager.cpp index 34bf64b64..06c5d8d42 100644 --- a/Linphone/model/account/AccountManager.cpp +++ b/Linphone/model/account/AccountManager.cpp @@ -93,6 +93,7 @@ void AccountManager::onRegistrationStateChanged(const std::shared_ptrsetDefaultAccount(account); emit mAccountModel->removeListener(); mAccountModel = nullptr; break; diff --git a/Linphone/model/call/CallModel.cpp b/Linphone/model/call/CallModel.cpp new file mode 100644 index 000000000..7ebd70328 --- /dev/null +++ b/Linphone/model/call/CallModel.cpp @@ -0,0 +1,144 @@ +/* + * Copyright (c) 2010-2024 Belledonne Communications SARL. + * + * This file is part of linphone-desktop + * (see https://www.linphone.org). + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "CallModel.hpp" + +#include + +#include "model/core/CoreModel.hpp" + +DEFINE_ABSTRACT_OBJECT(CallModel) + +CallModel::CallModel(const std::shared_ptr &call, QObject *parent) + : ::Listener(call, parent) { + mustBeInLinphoneThread(getClassName()); +} + +CallModel::~CallModel() { + mustBeInLinphoneThread("~" + getClassName()); +} + +void CallModel::accept(bool withVideo) { + mustBeInLinphoneThread(log().arg(Q_FUNC_INFO)); + + auto core = CoreModel::getInstance()->getCore(); + auto params = core->createCallParams(mMonitor); + params->enableVideo(withVideo); + // Answer with local call address. + auto localAddress = mMonitor->getCallLog()->getLocalAddress(); + for (auto account : core->getAccountList()) { + if (account->getParams()->getIdentityAddress()->weakEqual(localAddress)) { + params->setAccount(account); + break; + } + } + + mMonitor->acceptWithParams(params); +} + +void CallModel::decline() { + mustBeInLinphoneThread(log().arg(Q_FUNC_INFO)); + auto errorInfo = linphone::Factory::get()->createErrorInfo(); + errorInfo->set("SIP", linphone::Reason::Declined, 603, "Decline", ""); + mMonitor->terminateWithErrorInfo(errorInfo); +} + +void CallModel::terminate() { + mustBeInLinphoneThread(log().arg(Q_FUNC_INFO)); + mMonitor->terminate(); +} + +void CallModel::onDtmfReceived(const std::shared_ptr &call, int dtmf) { + emit dtmfReceived(call, dtmf); +} + +void CallModel::onGoclearAckSent(const std::shared_ptr &call) { + emit goclearAckSent(call); +} + +void CallModel::onEncryptionChanged(const std::shared_ptr &call, + bool on, + const std::string &authenticationToken) { + emit encryptionChanged(call, on, authenticationToken); +} + +void CallModel::onSendMasterKeyChanged(const std::shared_ptr &call, const std::string &sendMasterKey) { + emit sendMasterKeyChanged(call, sendMasterKey); +} + +void CallModel::onReceiveMasterKeyChanged(const std::shared_ptr &call, + const std::string &receiveMasterKey) { + emit receiveMasterKeyChanged(call, receiveMasterKey); +} + +void CallModel::onInfoMessageReceived(const std::shared_ptr &call, + const std::shared_ptr &message) { + emit infoMessageReceived(call, message); +} + +void CallModel::onStateChanged(const std::shared_ptr &call, + linphone::Call::State state, + const std::string &message) { + emit stateChanged(state, message); +} + +void CallModel::onStatsUpdated(const std::shared_ptr &call, + const std::shared_ptr &stats) { + emit statsUpdated(call, stats); +} + +void CallModel::onTransferStateChanged(const std::shared_ptr &call, linphone::Call::State state) { + emit transferStateChanged(call, state); +} + +void CallModel::onAckProcessing(const std::shared_ptr &call, + const std::shared_ptr &ack, + bool isReceived) { + emit ackProcessing(call, ack, isReceived); +} + +void CallModel::onTmmbrReceived(const std::shared_ptr &call, int streamIndex, int tmmbr) { + emit tmmbrReceived(call, streamIndex, tmmbr); +} + +void CallModel::onSnapshotTaken(const std::shared_ptr &call, const std::string &filePath) { + emit snapshotTaken(call, filePath); +} + +void CallModel::onNextVideoFrameDecoded(const std::shared_ptr &call) { + emit nextVideoFrameDecoded(call); +} + +void CallModel::onCameraNotWorking(const std::shared_ptr &call, const std::string &cameraName) { + emit cameraNotWorking(call, cameraName); +} + +void CallModel::onVideoDisplayErrorOccurred(const std::shared_ptr &call, int errorCode) { + emit videoDisplayErrorOccurred(call, errorCode); +} + +void CallModel::onAudioDeviceChanged(const std::shared_ptr &call, + const std::shared_ptr &audioDevice) { + emit audioDeviceChanged(call, audioDevice); +} + +void CallModel::onRemoteRecording(const std::shared_ptr &call, bool recording) { + emit remoteRecording(call, recording); +} diff --git a/Linphone/model/call/CallModel.hpp b/Linphone/model/call/CallModel.hpp new file mode 100644 index 000000000..5314a08ee --- /dev/null +++ b/Linphone/model/call/CallModel.hpp @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2010-2024 Belledonne Communications SARL. + * + * This file is part of linphone-desktop + * (see https://www.linphone.org). + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef CALL_MODEL_H_ +#define CALL_MODEL_H_ + +#include "model/listener/Listener.hpp" +#include "tool/AbstractObject.hpp" + +#include +#include + +class CallModel : public ::Listener, + public linphone::CallListener, + public AbstractObject { + Q_OBJECT +public: + CallModel(const std::shared_ptr &account, QObject *parent = nullptr); + ~CallModel(); + + void accept(bool withVideo); + void decline(); + void terminate(); + +private: + DECLARE_ABSTRACT_OBJECT + + //-------------------------------------------------------------------------------- + // LINPHONE + //-------------------------------------------------------------------------------- + virtual void onDtmfReceived(const std::shared_ptr &call, int dtmf) override; + virtual void onGoclearAckSent(const std::shared_ptr &call) override; + virtual void onEncryptionChanged(const std::shared_ptr &call, + bool on, + const std::string &authenticationToken) override; + virtual void onSendMasterKeyChanged(const std::shared_ptr &call, + const std::string &sendMasterKey) override; + virtual void onReceiveMasterKeyChanged(const std::shared_ptr &call, + const std::string &receiveMasterKey) override; + virtual void onInfoMessageReceived(const std::shared_ptr &call, + const std::shared_ptr &message) override; + virtual void onStateChanged(const std::shared_ptr &call, + linphone::Call::State state, + const std::string &message) override; + virtual void onStatsUpdated(const std::shared_ptr &call, + const std::shared_ptr &stats) override; + virtual void onTransferStateChanged(const std::shared_ptr &call, + linphone::Call::State state) override; + virtual void onAckProcessing(const std::shared_ptr &call, + const std::shared_ptr &ack, + bool isReceived) override; + virtual void onTmmbrReceived(const std::shared_ptr &call, int streamIndex, int tmmbr) override; + virtual void onSnapshotTaken(const std::shared_ptr &call, const std::string &filePath) override; + virtual void onNextVideoFrameDecoded(const std::shared_ptr &call) override; + virtual void onCameraNotWorking(const std::shared_ptr &call, + const std::string &cameraName) override; + virtual void onVideoDisplayErrorOccurred(const std::shared_ptr &call, int errorCode) override; + virtual void onAudioDeviceChanged(const std::shared_ptr &call, + const std::shared_ptr &audioDevice) override; + virtual void onRemoteRecording(const std::shared_ptr &call, bool recording) override; + +signals: + void dtmfReceived(const std::shared_ptr &call, int dtmf); + void goclearAckSent(const std::shared_ptr &call); + void + encryptionChanged(const std::shared_ptr &call, bool on, const std::string &authenticationToken); + void sendMasterKeyChanged(const std::shared_ptr &call, const std::string &sendMasterKey); + void receiveMasterKeyChanged(const std::shared_ptr &call, const std::string &receiveMasterKey); + void infoMessageReceived(const std::shared_ptr &call, + const std::shared_ptr &message); + void stateChanged(linphone::Call::State state, const std::string &message); + void statsUpdated(const std::shared_ptr &call, + const std::shared_ptr &stats); + void transferStateChanged(const std::shared_ptr &call, linphone::Call::State state); + void ackProcessing(const std::shared_ptr &call, + const std::shared_ptr &ack, + bool isReceived); + void tmmbrReceived(const std::shared_ptr &call, int streamIndex, int tmmbr); + void snapshotTaken(const std::shared_ptr &call, const std::string &filePath); + void nextVideoFrameDecoded(const std::shared_ptr &call); + void cameraNotWorking(const std::shared_ptr &call, const std::string &cameraName); + void videoDisplayErrorOccurred(const std::shared_ptr &call, int errorCode); + virtual void audioDeviceChanged(const std::shared_ptr &call, + const std::shared_ptr &audioDevice); + void remoteRecording(const std::shared_ptr &call, bool recording); +}; + +#endif diff --git a/Linphone/model/core/CoreListener.cpp b/Linphone/model/core/CoreListener.cpp deleted file mode 100644 index 77997c0e4..000000000 --- a/Linphone/model/core/CoreListener.cpp +++ /dev/null @@ -1,161 +0,0 @@ -/* - * Copyright (c) 2010-2024 Belledonne Communications SARL. - * - * This file is part of linphone-desktop - * (see https://www.linphone.org). - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include "CoreListener.hpp" - -// ============================================================================= - -// ----------------------------------------------------------------------------- - -CoreListener::CoreListener(QObject *parent) : QObject(parent) { -} -CoreListener::~CoreListener() { -} - -void CoreListener::onAccountRegistrationStateChanged(const std::shared_ptr &core, - const std::shared_ptr &account, - linphone::RegistrationState state, - const std::string &message) { - emit accountRegistrationStateChanged(core, account, state, message); -} -void CoreListener::onAuthenticationRequested(const std::shared_ptr &core, - const std::shared_ptr &authInfo, - linphone::AuthMethod method) { - emit authenticationRequested(core, authInfo, method); -} -void CoreListener::onCallEncryptionChanged(const std::shared_ptr &core, - const std::shared_ptr &call, - bool on, - const std::string &authenticationToken) { - emit callEncryptionChanged(core, call, on, authenticationToken); -} -void CoreListener::onCallLogUpdated(const std::shared_ptr &core, - const std::shared_ptr &callLog) { - emit callLogUpdated(core, callLog); -} -void CoreListener::onCallStateChanged(const std::shared_ptr &core, - const std::shared_ptr &call, - linphone::Call::State state, - const std::string &message) { - emit callStateChanged(core, call, state, message); -} -void CoreListener::onCallStatsUpdated(const std::shared_ptr &core, - const std::shared_ptr &call, - const std::shared_ptr &stats) { - emit callStatsUpdated(core, call, stats); -} -void CoreListener::onCallCreated(const std::shared_ptr &lc, - const std::shared_ptr &call) { - emit callCreated(lc, call); -} -void CoreListener::onChatRoomRead(const std::shared_ptr &core, - const std::shared_ptr &chatRoom) { - emit chatRoomRead(core, chatRoom); -} -void CoreListener::onChatRoomStateChanged(const std::shared_ptr &core, - const std::shared_ptr &chatRoom, - linphone::ChatRoom::State state) { - emit chatRoomStateChanged(core, chatRoom, state); -} -void CoreListener::onConferenceInfoReceived(const std::shared_ptr &core, - const std::shared_ptr &conferenceInfo) { - emit conferenceInfoReceived(core, conferenceInfo); -} -void CoreListener::onConfiguringStatus(const std::shared_ptr &core, - linphone::Config::ConfiguringState status, - const std::string &message) { - emit configuringStatus(core, status, message); -} -void CoreListener::onDtmfReceived(const std::shared_ptr &lc, - const std::shared_ptr &call, - int dtmf) { - emit dtmfReceived(lc, call, dtmf); -} -void CoreListener::onEcCalibrationResult(const std::shared_ptr &core, - linphone::EcCalibratorStatus status, - int delayMs) { - emit ecCalibrationResult(core, status, delayMs); -} -void CoreListener::onGlobalStateChanged(const std::shared_ptr &core, - linphone::GlobalState gstate, - const std::string &message) { - emit globalStateChanged(core, gstate, message); -} -void CoreListener::onIsComposingReceived(const std::shared_ptr &core, - const std::shared_ptr &room) { - emit isComposingReceived(core, room); -} -void CoreListener::onLogCollectionUploadStateChanged(const std::shared_ptr &core, - linphone::Core::LogCollectionUploadState state, - const std::string &info) { - emit logCollectionUploadStateChanged(core, state, info); -} -void CoreListener::onLogCollectionUploadProgressIndication(const std::shared_ptr &lc, - size_t offset, - size_t total) { - emit logCollectionUploadProgressIndication(lc, offset, total); -} -void CoreListener::onMessageReceived(const std::shared_ptr &core, - const std::shared_ptr &room, - const std::shared_ptr &message) { - emit messageReceived(core, room, message); -} -void CoreListener::onMessagesReceived(const std::shared_ptr &core, - const std::shared_ptr &room, - const std::list> &messages) { - emit messagesReceived(core, room, messages); -} -void CoreListener::onNewMessageReaction(const std::shared_ptr &core, - const std::shared_ptr &chatRoom, - const std::shared_ptr &message, - const std::shared_ptr &reaction) { - emit newMessageReaction(core, chatRoom, message, reaction); -} -void CoreListener::onNotifyPresenceReceivedForUriOrTel( - const std::shared_ptr &core, - const std::shared_ptr &linphoneFriend, - const std::string &uriOrTel, - const std::shared_ptr &presenceModel) { - emit notifyPresenceReceivedForUriOrTel(core, linphoneFriend, uriOrTel, presenceModel); -} -void CoreListener::onNotifyPresenceReceived(const std::shared_ptr &core, - const std::shared_ptr &linphoneFriend) { - emit notifyPresenceReceived(core, linphoneFriend); -} -void CoreListener::onQrcodeFound(const std::shared_ptr &core, const std::string &result) { - emit qrcodeFound(core, result); -} -void CoreListener::onReactionRemoved(const std::shared_ptr &core, - const std::shared_ptr &chatRoom, - const std::shared_ptr &message, - const std::shared_ptr &address) { - emit reactionRemoved(core, chatRoom, message, address); -} -void CoreListener::onTransferStateChanged(const std::shared_ptr &core, - const std::shared_ptr &call, - linphone::Call::State state) { - emit transferStateChanged(core, call, state); -} -void CoreListener::onVersionUpdateCheckResultReceived(const std::shared_ptr &core, - linphone::VersionUpdateCheckResult result, - const std::string &version, - const std::string &url) { - emit versionUpdateCheckResultReceived(core, result, version, url); -} diff --git a/Linphone/model/core/CoreListener.hpp b/Linphone/model/core/CoreListener.hpp deleted file mode 100644 index 1fabe1cfc..000000000 --- a/Linphone/model/core/CoreListener.hpp +++ /dev/null @@ -1,187 +0,0 @@ -/* - * Copyright (c) 2010-2024 Belledonne Communications SARL. - * - * This file is part of linphone-desktop - * (see https://www.linphone.org). - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#ifndef CORE_LISTENER_H_ -#define CORE_LISTENER_H_ - -#include -#include - -// ============================================================================= - -class CoreListener : public QObject, public linphone::CoreListener { - Q_OBJECT -public: - CoreListener(QObject *parent = nullptr); - virtual ~CoreListener(); - - virtual void onAccountRegistrationStateChanged(const std::shared_ptr &core, - const std::shared_ptr &account, - linphone::RegistrationState state, - const std::string &message) override; - virtual void onAuthenticationRequested(const std::shared_ptr &core, - const std::shared_ptr &authInfo, - linphone::AuthMethod method) override; - virtual void onCallEncryptionChanged(const std::shared_ptr &core, - const std::shared_ptr &call, - bool on, - const std::string &authenticationToken) override; - virtual void onCallLogUpdated(const std::shared_ptr &core, - const std::shared_ptr &callLog) override; - virtual void onCallStateChanged(const std::shared_ptr &core, - const std::shared_ptr &call, - linphone::Call::State state, - const std::string &message) override; - virtual void onCallStatsUpdated(const std::shared_ptr &core, - const std::shared_ptr &call, - const std::shared_ptr &stats) override; - virtual void onCallCreated(const std::shared_ptr &lc, - const std::shared_ptr &call) override; - virtual void onChatRoomRead(const std::shared_ptr &core, - const std::shared_ptr &chatRoom) override; - virtual void onChatRoomStateChanged(const std::shared_ptr &core, - const std::shared_ptr &chatRoom, - linphone::ChatRoom::State state) override; - virtual void - onConferenceInfoReceived(const std::shared_ptr &core, - const std::shared_ptr &conferenceInfo) override; - virtual void onConfiguringStatus(const std::shared_ptr &core, - linphone::Config::ConfiguringState status, - const std::string &message) override; - virtual void onDtmfReceived(const std::shared_ptr &lc, - const std::shared_ptr &call, - int dtmf) override; - virtual void onEcCalibrationResult(const std::shared_ptr &core, - linphone::EcCalibratorStatus status, - int delayMs) override; - virtual void onGlobalStateChanged(const std::shared_ptr &core, - linphone::GlobalState gstate, - const std::string &message) override; - virtual void onIsComposingReceived(const std::shared_ptr &core, - const std::shared_ptr &room) override; - virtual void onLogCollectionUploadStateChanged(const std::shared_ptr &core, - linphone::Core::LogCollectionUploadState state, - const std::string &info) override; - virtual void onLogCollectionUploadProgressIndication(const std::shared_ptr &lc, - size_t offset, - size_t total) override; - virtual void onMessageReceived(const std::shared_ptr &core, - const std::shared_ptr &room, - const std::shared_ptr &message) override; - virtual void onMessagesReceived(const std::shared_ptr &core, - const std::shared_ptr &room, - const std::list> &messages) override; - virtual void onNewMessageReaction(const std::shared_ptr &core, - const std::shared_ptr &chatRoom, - const std::shared_ptr &message, - const std::shared_ptr &reaction) override; - virtual void - onNotifyPresenceReceivedForUriOrTel(const std::shared_ptr &core, - const std::shared_ptr &linphoneFriend, - const std::string &uriOrTel, - const std::shared_ptr &presenceModel) override; - virtual void onNotifyPresenceReceived(const std::shared_ptr &core, - const std::shared_ptr &linphoneFriend) override; - virtual void onQrcodeFound(const std::shared_ptr &core, const std::string &result) override; - virtual void onReactionRemoved(const std::shared_ptr &core, - const std::shared_ptr &chatRoom, - const std::shared_ptr &message, - const std::shared_ptr &address) override; - virtual void onTransferStateChanged(const std::shared_ptr &core, - const std::shared_ptr &call, - linphone::Call::State state) override; - virtual void onVersionUpdateCheckResultReceived(const std::shared_ptr &core, - linphone::VersionUpdateCheckResult result, - const std::string &version, - const std::string &url) override; - -signals: - void accountRegistrationStateChanged(const std::shared_ptr &core, - const std::shared_ptr &account, - linphone::RegistrationState state, - const std::string &message); - void authenticationRequested(const std::shared_ptr &core, - const std::shared_ptr &authInfo, - linphone::AuthMethod method); - void callEncryptionChanged(const std::shared_ptr &core, - const std::shared_ptr &call, - bool on, - const std::string &authenticationToken); - void callLogUpdated(const std::shared_ptr &core, const std::shared_ptr &callLog); - void callStateChanged(const std::shared_ptr &core, - const std::shared_ptr &call, - linphone::Call::State state, - const std::string &message); - void callStatsUpdated(const std::shared_ptr &core, - const std::shared_ptr &call, - const std::shared_ptr &stats); - void callCreated(const std::shared_ptr &lc, const std::shared_ptr &call); - void chatRoomRead(const std::shared_ptr &core, const std::shared_ptr &chatRoom); - void chatRoomStateChanged(const std::shared_ptr &core, - const std::shared_ptr &chatRoom, - linphone::ChatRoom::State state); - void conferenceInfoReceived(const std::shared_ptr &core, - const std::shared_ptr &conferenceInfo); - void configuringStatus(const std::shared_ptr &core, - linphone::Config::ConfiguringState status, - const std::string &message); - void dtmfReceived(const std::shared_ptr &lc, const std::shared_ptr &call, int dtmf); - void - ecCalibrationResult(const std::shared_ptr &core, linphone::EcCalibratorStatus status, int delayMs); - void globalStateChanged(const std::shared_ptr &core, - linphone::GlobalState gstate, - const std::string &message); - void isComposingReceived(const std::shared_ptr &core, - const std::shared_ptr &room); - void logCollectionUploadStateChanged(const std::shared_ptr &core, - linphone::Core::LogCollectionUploadState state, - const std::string &info); - void logCollectionUploadProgressIndication(const std::shared_ptr &lc, size_t offset, size_t total); - void messageReceived(const std::shared_ptr &core, - const std::shared_ptr &room, - const std::shared_ptr &message); - void messagesReceived(const std::shared_ptr &core, - const std::shared_ptr &room, - const std::list> &messages); - void newMessageReaction(const std::shared_ptr &core, - const std::shared_ptr &chatRoom, - const std::shared_ptr &message, - const std::shared_ptr &reaction); - void notifyPresenceReceivedForUriOrTel(const std::shared_ptr &core, - const std::shared_ptr &linphoneFriend, - const std::string &uriOrTel, - const std::shared_ptr &presenceModel); - void notifyPresenceReceived(const std::shared_ptr &core, - const std::shared_ptr &linphoneFriend); - void qrcodeFound(const std::shared_ptr &core, const std::string &result); - void reactionRemoved(const std::shared_ptr &core, - const std::shared_ptr &chatRoom, - const std::shared_ptr &message, - const std::shared_ptr &address); - void transferStateChanged(const std::shared_ptr &core, - const std::shared_ptr &call, - linphone::Call::State state); - void versionUpdateCheckResultReceived(const std::shared_ptr &core, - linphone::VersionUpdateCheckResult result, - const std::string &version, - const std::string &url); -}; - -#endif diff --git a/Linphone/model/core/CoreModel.cpp b/Linphone/model/core/CoreModel.cpp index d2b450869..26541024b 100644 --- a/Linphone/model/core/CoreModel.cpp +++ b/Linphone/model/core/CoreModel.cpp @@ -28,14 +28,17 @@ #include #include "core/App.hpp" +#include "core/notifier/Notifier.hpp" #include "core/path/Paths.hpp" #include "tool/Utils.hpp" // ============================================================================= +DEFINE_ABSTRACT_OBJECT(CoreModel) -QSharedPointer CoreModel::gCoreModel; +std::shared_ptr CoreModel::gCoreModel; -CoreModel::CoreModel(const QString &configPath, QThread *parent) : QObject() { +CoreModel::CoreModel(const QString &configPath, QThread *parent) + : ::Listener(nullptr, parent) { connect(parent, &QThread::finished, this, [this]() { // Model thread if (mCore && mCore->getGlobalState() == linphone::GlobalState::On) mCore->stop(); @@ -50,8 +53,9 @@ CoreModel::CoreModel(const QString &configPath, QThread *parent) : QObject() { CoreModel::~CoreModel() { } -QSharedPointer CoreModel::create(const QString &configPath, QThread *parent) { - auto model = QSharedPointer::create(configPath, parent); +std::shared_ptr CoreModel::create(const QString &configPath, QThread *parent) { + auto model = std::make_shared(configPath, parent); + model->setSelf(model); gCoreModel = model; return model; } @@ -64,6 +68,7 @@ void CoreModel::start() { mCore = linphone::Factory::get()->createCore(Utils::appStringToCoreString(Paths::getConfigFilePath(mConfigPath)), Utils::appStringToCoreString(Paths::getFactoryConfigFilePath()), nullptr); + setMonitor(mCore); setPathsAfterCreation(); mCore->start(); setPathAfterStart(); @@ -71,7 +76,7 @@ void CoreModel::start() { } // ----------------------------------------------------------------------------- -QSharedPointer CoreModel::getInstance() { +std::shared_ptr CoreModel::getInstance() { return gCoreModel; } @@ -131,3 +136,138 @@ void CoreModel::setPathAfterStart() { mCore->setRootCa(Utils::appStringToCoreString(Paths::getRootCaFilePath())); qInfo() << "[CoreModel] Using RootCa path : " << QString::fromStdString(mCore->getRootCa()); } + +//--------------------------------------------------------------------------------------------------------------------------- + +void CoreModel::onAccountRegistrationStateChanged(const std::shared_ptr &core, + const std::shared_ptr &account, + linphone::RegistrationState state, + const std::string &message) { + emit accountRegistrationStateChanged(core, account, state, message); +} +void CoreModel::onAuthenticationRequested(const std::shared_ptr &core, + const std::shared_ptr &authInfo, + linphone::AuthMethod method) { + emit authenticationRequested(core, authInfo, method); +} +void CoreModel::onCallEncryptionChanged(const std::shared_ptr &core, + const std::shared_ptr &call, + bool on, + const std::string &authenticationToken) { + emit callEncryptionChanged(core, call, on, authenticationToken); +} +void CoreModel::onCallLogUpdated(const std::shared_ptr &core, + const std::shared_ptr &callLog) { + emit callLogUpdated(core, callLog); +} +void CoreModel::onCallStateChanged(const std::shared_ptr &core, + const std::shared_ptr &call, + linphone::Call::State state, + const std::string &message) { + if (state == linphone::Call::State::IncomingReceived) { + App::getInstance()->getNotifier()->notifyReceivedCall(call); + } + emit callStateChanged(core, call, state, message); +} +void CoreModel::onCallStatsUpdated(const std::shared_ptr &core, + const std::shared_ptr &call, + const std::shared_ptr &stats) { + emit callStatsUpdated(core, call, stats); +} +void CoreModel::onCallCreated(const std::shared_ptr &lc, const std::shared_ptr &call) { + emit callCreated(lc, call); +} +void CoreModel::onChatRoomRead(const std::shared_ptr &core, + const std::shared_ptr &chatRoom) { + emit chatRoomRead(core, chatRoom); +} +void CoreModel::onChatRoomStateChanged(const std::shared_ptr &core, + const std::shared_ptr &chatRoom, + linphone::ChatRoom::State state) { + emit chatRoomStateChanged(core, chatRoom, state); +} +void CoreModel::onConferenceInfoReceived(const std::shared_ptr &core, + const std::shared_ptr &conferenceInfo) { + emit conferenceInfoReceived(core, conferenceInfo); +} +void CoreModel::onConfiguringStatus(const std::shared_ptr &core, + linphone::Config::ConfiguringState status, + const std::string &message) { + emit configuringStatus(core, status, message); +} +void CoreModel::onDtmfReceived(const std::shared_ptr &lc, + const std::shared_ptr &call, + int dtmf) { + emit dtmfReceived(lc, call, dtmf); +} +void CoreModel::onEcCalibrationResult(const std::shared_ptr &core, + linphone::EcCalibratorStatus status, + int delayMs) { + emit ecCalibrationResult(core, status, delayMs); +} +void CoreModel::onGlobalStateChanged(const std::shared_ptr &core, + linphone::GlobalState gstate, + const std::string &message) { + emit globalStateChanged(core, gstate, message); +} +void CoreModel::onIsComposingReceived(const std::shared_ptr &core, + const std::shared_ptr &room) { + emit isComposingReceived(core, room); +} +void CoreModel::onLogCollectionUploadStateChanged(const std::shared_ptr &core, + linphone::Core::LogCollectionUploadState state, + const std::string &info) { + emit logCollectionUploadStateChanged(core, state, info); +} +void CoreModel::onLogCollectionUploadProgressIndication(const std::shared_ptr &lc, + size_t offset, + size_t total) { + emit logCollectionUploadProgressIndication(lc, offset, total); +} +void CoreModel::onMessageReceived(const std::shared_ptr &core, + const std::shared_ptr &room, + const std::shared_ptr &message) { + emit messageReceived(core, room, message); +} +void CoreModel::onMessagesReceived(const std::shared_ptr &core, + const std::shared_ptr &room, + const std::list> &messages) { + emit messagesReceived(core, room, messages); +} +void CoreModel::onNewMessageReaction(const std::shared_ptr &core, + const std::shared_ptr &chatRoom, + const std::shared_ptr &message, + const std::shared_ptr &reaction) { + emit newMessageReaction(core, chatRoom, message, reaction); +} +void CoreModel::onNotifyPresenceReceivedForUriOrTel( + const std::shared_ptr &core, + const std::shared_ptr &linphoneFriend, + const std::string &uriOrTel, + const std::shared_ptr &presenceModel) { + emit notifyPresenceReceivedForUriOrTel(core, linphoneFriend, uriOrTel, presenceModel); +} +void CoreModel::onNotifyPresenceReceived(const std::shared_ptr &core, + const std::shared_ptr &linphoneFriend) { + emit notifyPresenceReceived(core, linphoneFriend); +} +void CoreModel::onQrcodeFound(const std::shared_ptr &core, const std::string &result) { + emit qrcodeFound(core, result); +} +void CoreModel::onReactionRemoved(const std::shared_ptr &core, + const std::shared_ptr &chatRoom, + const std::shared_ptr &message, + const std::shared_ptr &address) { + emit reactionRemoved(core, chatRoom, message, address); +} +void CoreModel::onTransferStateChanged(const std::shared_ptr &core, + const std::shared_ptr &call, + linphone::Call::State state) { + emit transferStateChanged(core, call, state); +} +void CoreModel::onVersionUpdateCheckResultReceived(const std::shared_ptr &core, + linphone::VersionUpdateCheckResult result, + const std::string &version, + const std::string &url) { + emit versionUpdateCheckResultReceived(core, result, version, url); +} diff --git a/Linphone/model/core/CoreModel.hpp b/Linphone/model/core/CoreModel.hpp index 4200c0909..d4e746d9b 100644 --- a/Linphone/model/core/CoreModel.hpp +++ b/Linphone/model/core/CoreModel.hpp @@ -28,17 +28,21 @@ #include #include +#include "model/listener/Listener.hpp" #include "model/logger/LoggerModel.hpp" +#include "tool/AbstractObject.hpp" // ============================================================================= -class CoreModel : public QObject { +class CoreModel : public ::Listener, + public linphone::CoreListener, + public AbstractObject { Q_OBJECT public: CoreModel(const QString &configPath, QThread *parent); ~CoreModel(); - static QSharedPointer create(const QString &configPath, QThread *parent); - static QSharedPointer getInstance(); + static std::shared_ptr create(const QString &configPath, QThread *parent); + static std::shared_ptr getInstance(); std::shared_ptr getCore(); @@ -61,7 +65,162 @@ private: void setPathsAfterCreation(); void setPathAfterStart(); - static QSharedPointer gCoreModel; + static std::shared_ptr gCoreModel; + + DECLARE_ABSTRACT_OBJECT + //-------------------------------------------------------------------------------- + // LINPHONE + //-------------------------------------------------------------------------------- + virtual void onAccountRegistrationStateChanged(const std::shared_ptr &core, + const std::shared_ptr &account, + linphone::RegistrationState state, + const std::string &message) override; + virtual void onAuthenticationRequested(const std::shared_ptr &core, + const std::shared_ptr &authInfo, + linphone::AuthMethod method) override; + virtual void onCallEncryptionChanged(const std::shared_ptr &core, + const std::shared_ptr &call, + bool on, + const std::string &authenticationToken) override; + virtual void onCallLogUpdated(const std::shared_ptr &core, + const std::shared_ptr &callLog) override; + virtual void onCallStateChanged(const std::shared_ptr &core, + const std::shared_ptr &call, + linphone::Call::State state, + const std::string &message) override; + virtual void onCallStatsUpdated(const std::shared_ptr &core, + const std::shared_ptr &call, + const std::shared_ptr &stats) override; + virtual void onCallCreated(const std::shared_ptr &lc, + const std::shared_ptr &call) override; + virtual void onChatRoomRead(const std::shared_ptr &core, + const std::shared_ptr &chatRoom) override; + virtual void onChatRoomStateChanged(const std::shared_ptr &core, + const std::shared_ptr &chatRoom, + linphone::ChatRoom::State state) override; + virtual void + onConferenceInfoReceived(const std::shared_ptr &core, + const std::shared_ptr &conferenceInfo) override; + virtual void onConfiguringStatus(const std::shared_ptr &core, + linphone::Config::ConfiguringState status, + const std::string &message) override; + virtual void onDtmfReceived(const std::shared_ptr &lc, + const std::shared_ptr &call, + int dtmf) override; + virtual void onEcCalibrationResult(const std::shared_ptr &core, + linphone::EcCalibratorStatus status, + int delayMs) override; + virtual void onGlobalStateChanged(const std::shared_ptr &core, + linphone::GlobalState gstate, + const std::string &message) override; + virtual void onIsComposingReceived(const std::shared_ptr &core, + const std::shared_ptr &room) override; + virtual void onLogCollectionUploadStateChanged(const std::shared_ptr &core, + linphone::Core::LogCollectionUploadState state, + const std::string &info) override; + virtual void onLogCollectionUploadProgressIndication(const std::shared_ptr &lc, + size_t offset, + size_t total) override; + virtual void onMessageReceived(const std::shared_ptr &core, + const std::shared_ptr &room, + const std::shared_ptr &message) override; + virtual void onMessagesReceived(const std::shared_ptr &core, + const std::shared_ptr &room, + const std::list> &messages) override; + virtual void onNewMessageReaction(const std::shared_ptr &core, + const std::shared_ptr &chatRoom, + const std::shared_ptr &message, + const std::shared_ptr &reaction) override; + virtual void + onNotifyPresenceReceivedForUriOrTel(const std::shared_ptr &core, + const std::shared_ptr &linphoneFriend, + const std::string &uriOrTel, + const std::shared_ptr &presenceModel) override; + virtual void onNotifyPresenceReceived(const std::shared_ptr &core, + const std::shared_ptr &linphoneFriend) override; + virtual void onQrcodeFound(const std::shared_ptr &core, const std::string &result) override; + virtual void onReactionRemoved(const std::shared_ptr &core, + const std::shared_ptr &chatRoom, + const std::shared_ptr &message, + const std::shared_ptr &address) override; + virtual void onTransferStateChanged(const std::shared_ptr &core, + const std::shared_ptr &call, + linphone::Call::State state) override; + virtual void onVersionUpdateCheckResultReceived(const std::shared_ptr &core, + linphone::VersionUpdateCheckResult result, + const std::string &version, + const std::string &url) override; + +signals: + void accountRegistrationStateChanged(const std::shared_ptr &core, + const std::shared_ptr &account, + linphone::RegistrationState state, + const std::string &message); + void authenticationRequested(const std::shared_ptr &core, + const std::shared_ptr &authInfo, + linphone::AuthMethod method); + void callEncryptionChanged(const std::shared_ptr &core, + const std::shared_ptr &call, + bool on, + const std::string &authenticationToken); + void callLogUpdated(const std::shared_ptr &core, const std::shared_ptr &callLog); + void callStateChanged(const std::shared_ptr &core, + const std::shared_ptr &call, + linphone::Call::State state, + const std::string &message); + void callStatsUpdated(const std::shared_ptr &core, + const std::shared_ptr &call, + const std::shared_ptr &stats); + void callCreated(const std::shared_ptr &lc, const std::shared_ptr &call); + void chatRoomRead(const std::shared_ptr &core, const std::shared_ptr &chatRoom); + void chatRoomStateChanged(const std::shared_ptr &core, + const std::shared_ptr &chatRoom, + linphone::ChatRoom::State state); + void conferenceInfoReceived(const std::shared_ptr &core, + const std::shared_ptr &conferenceInfo); + void configuringStatus(const std::shared_ptr &core, + linphone::Config::ConfiguringState status, + const std::string &message); + void dtmfReceived(const std::shared_ptr &lc, const std::shared_ptr &call, int dtmf); + void + ecCalibrationResult(const std::shared_ptr &core, linphone::EcCalibratorStatus status, int delayMs); + void globalStateChanged(const std::shared_ptr &core, + linphone::GlobalState gstate, + const std::string &message); + void isComposingReceived(const std::shared_ptr &core, + const std::shared_ptr &room); + void logCollectionUploadStateChanged(const std::shared_ptr &core, + linphone::Core::LogCollectionUploadState state, + const std::string &info); + void logCollectionUploadProgressIndication(const std::shared_ptr &lc, size_t offset, size_t total); + void messageReceived(const std::shared_ptr &core, + const std::shared_ptr &room, + const std::shared_ptr &message); + void messagesReceived(const std::shared_ptr &core, + const std::shared_ptr &room, + const std::list> &messages); + void newMessageReaction(const std::shared_ptr &core, + const std::shared_ptr &chatRoom, + const std::shared_ptr &message, + const std::shared_ptr &reaction); + void notifyPresenceReceivedForUriOrTel(const std::shared_ptr &core, + const std::shared_ptr &linphoneFriend, + const std::string &uriOrTel, + const std::shared_ptr &presenceModel); + void notifyPresenceReceived(const std::shared_ptr &core, + const std::shared_ptr &linphoneFriend); + void qrcodeFound(const std::shared_ptr &core, const std::string &result); + void reactionRemoved(const std::shared_ptr &core, + const std::shared_ptr &chatRoom, + const std::shared_ptr &message, + const std::shared_ptr &address); + void transferStateChanged(const std::shared_ptr &core, + const std::shared_ptr &call, + linphone::Call::State state); + void versionUpdateCheckResultReceived(const std::shared_ptr &core, + linphone::VersionUpdateCheckResult result, + const std::string &version, + const std::string &url); }; #endif diff --git a/Linphone/model/listener/Listener.hpp b/Linphone/model/listener/Listener.hpp index 49e8fd63b..6ebe79608 100644 --- a/Linphone/model/listener/Listener.hpp +++ b/Linphone/model/listener/Listener.hpp @@ -41,7 +41,7 @@ template class Listener : public ListenerPrivate { public: Listener(std::shared_ptr monitor, QObject *parent = nullptr) { - mMonitor = monitor; + setMonitor(monitor); } ~Listener() { qDebug() << "Destroying Listener"; @@ -50,10 +50,15 @@ public: virtual void onRemoveListener() { setSelf(nullptr); } + void setMonitor(std::shared_ptr monitor) { + if (mMonitor && mSelf) mMonitor->removeListener(mSelf); + mMonitor = monitor; + if (mMonitor && mSelf) mMonitor->addListener(mSelf); + } void setSelf(const std::shared_ptr &self) { if (mMonitor && mSelf) mMonitor->removeListener(mSelf); mSelf = self; - if (self) mMonitor->addListener(self); + if (mMonitor && mSelf) mMonitor->addListener(self); } protected: diff --git a/Linphone/model/logger/LoggerModel.cpp b/Linphone/model/logger/LoggerModel.cpp index f06b09b04..d267470a2 100644 --- a/Linphone/model/logger/LoggerModel.cpp +++ b/Linphone/model/logger/LoggerModel.cpp @@ -147,7 +147,7 @@ void LoggerModel::init() { mListener = std::make_shared(); connect(mListener.get(), &LoggerListener::logReceived, this, &LoggerModel::onLinphoneLog); { - std::shared_ptr loggingService = linphone::LoggingService::get(); + std::shared_ptr loggingService = mLoginService = linphone::LoggingService::get(); loggingService->setDomain(Constants::AppDomain); loggingService->setLogLevel(linphone::LogLevel::Debug); loggingService->addListener(mListener); diff --git a/Linphone/model/logger/LoggerModel.hpp b/Linphone/model/logger/LoggerModel.hpp index 6c4be4154..c855b73c5 100644 --- a/Linphone/model/logger/LoggerModel.hpp +++ b/Linphone/model/logger/LoggerModel.hpp @@ -45,15 +45,15 @@ public: void init(); void init(const std::shared_ptr &config); - - void onQtLog(QtMsgType type, QString file, int contextLine, QString msg);// Received from Qt + void onQtLog(QtMsgType type, QString file, int contextLine, QString msg); // Received from Qt void onLinphoneLog(const std::shared_ptr &, - const std::string &domain, - linphone::LogLevel level, - const std::string &message);// Received from SDK + const std::string &domain, + linphone::LogLevel level, + const std::string &message); // Received from SDK signals: - void linphoneLogReceived(const std::string &domain, linphone::LogLevel level, const std::string &message); // Send to Qt + void + linphoneLogReceived(const std::string &domain, linphone::LogLevel level, const std::string &message); // Send to Qt void verboseEnabledChanged(); void qtOnlyEnabledChanged(); @@ -62,6 +62,7 @@ private: bool mVerboseEnabled = false; bool mQtOnlyEnabled = false; std::shared_ptr mListener; + std::shared_ptr mLoginService; // Need to store one instance to avoid unwanted cleanup. }; #endif diff --git a/Linphone/model/object/VariantObject.cpp b/Linphone/model/object/VariantObject.cpp index 220f29610..a5e7335ca 100644 --- a/Linphone/model/object/VariantObject.cpp +++ b/Linphone/model/object/VariantObject.cpp @@ -23,31 +23,40 @@ #include #include +#include "core/App.hpp" + DEFINE_ABSTRACT_OBJECT(VariantObject) VariantObject::VariantObject(QObject *parent) { + mThreadLocation = false; mustBeInMainThread(getClassName()); + mCoreObject = nullptr; } VariantObject::VariantObject(QVariant value, QObject *parent) : mValue(value) { + mThreadLocation = true; mustBeInMainThread(getClassName()); - connect(this, &VariantObject::updateValue, this, &VariantObject::setValue); - mCoreObject = new VariantObject(); + connect(this, &VariantObject::valueUpdated, this, &VariantObject::setValue); + mCoreObject = new VariantObject(nullptr); + mCoreObject->moveToThread(CoreModel::getInstance()->thread()); connect(mCoreObject, &VariantObject::valueChanged, this, &VariantObject::setValue); connect(mCoreObject, &VariantObject::valueChanged, mCoreObject, &QObject::deleteLater); } VariantObject::~VariantObject() { - mustBeInMainThread("~" + getClassName()); + if (mThreadLocation) mustBeInMainThread("~" + getClassName()); + else mustBeInLinphoneThread("~" + getClassName()); } QVariant VariantObject::getValue() const { - mustBeInMainThread(QString(gClassName) + " : " + Q_FUNC_INFO); + if (mThreadLocation) mustBeInMainThread(QString(gClassName) + " : " + Q_FUNC_INFO); + else mustBeInLinphoneThread(QString(gClassName) + " : " + Q_FUNC_INFO); return mValue; } void VariantObject::setValue(QVariant value) { - mustBeInMainThread(QString(gClassName) + " : " + Q_FUNC_INFO); + if (mThreadLocation) mustBeInMainThread(QString(gClassName) + " : " + Q_FUNC_INFO); + else mustBeInLinphoneThread(QString(gClassName) + " : " + Q_FUNC_INFO); if (value != mValue) { mValue = value; emit valueChanged(mValue); diff --git a/Linphone/model/object/VariantObject.hpp b/Linphone/model/object/VariantObject.hpp index f97fad501..a74043999 100644 --- a/Linphone/model/object/VariantObject.hpp +++ b/Linphone/model/object/VariantObject.hpp @@ -42,14 +42,17 @@ public: QVariant getValue() const; void setValue(QVariant value); + // mCoreObject must be used to request update value : this object will be not be deleted by GUI so it is safe to use + // inside model thread. call emit updateValue() from coreObject to set value from model. VariantObject *mCoreObject; // Ensure to use DeleteLater() after updating value signals: void valueChanged(QVariant value); - void updateValue(QVariant value); + void valueUpdated(QVariant value); private: QVariant mValue; + bool mThreadLocation = true; // true=Core, false=Model DECLARE_ABSTRACT_OBJECT }; diff --git a/Linphone/model/tool/ToolModel.cpp b/Linphone/model/tool/ToolModel.cpp index 82bdd1473..9416b1c5b 100644 --- a/Linphone/model/tool/ToolModel.cpp +++ b/Linphone/model/tool/ToolModel.cpp @@ -19,9 +19,9 @@ */ #include "ToolModel.hpp" +#include "core/App.hpp" #include "model/core/CoreModel.hpp" #include "tool/Utils.hpp" - #include #include @@ -67,3 +67,58 @@ QString ToolModel::getDisplayName(QString address) { QString displayName = getDisplayName(interpretUrl(address)); return displayName.isEmpty() ? address : displayName; } + +Call *ToolModel::startAudioCall(const QString &sipAddress, + const QString &prepareTransfertAddress, + const QHash &headers) { + bool waitRegistrationForCall = true; // getSettingsModel()->getWaitRegistrationForCall() + std::shared_ptr core = CoreModel::getInstance()->getCore(); + + std::shared_ptr address = interpretUrl(sipAddress); + if (!address) { + qCritical() << "[" + QString(gClassName) + "] The calling address is not an interpretable SIP address: " + << sipAddress; + return nullptr; + } + + std::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())); + } + if (core->getDefaultAccount()) params->setAccount(core->getDefaultAccount()); + // CallModel::setRecordFile(params, Utils::coreStringToAppString(address->getUsername())); + auto call = core->inviteAddressWithParams(address, params); + return call ? new Call(call) : nullptr; + + /* TODO transfer + + std::shared_ptr currentAccount = core->getDefaultAccount(); + if (currentAccount) { + if (!waitRegistrationForCall || currentAccount->getState() == linphone::RegistrationState::Ok) { + qWarning() << "prepareTransfert not impolemented"; + // CallModel::prepareTransfert(core->inviteAddressWithParams(address, params), prepareTransfertAddress); + } else { + qWarning() << "Waiting registration not implemented"; + + // QObject *context = new QObject(); + // QObject::connect( + // CoreManager::getInstance()->getHandlers().get(), &CoreHandlers::registrationStateChanged, context, + // [address, core, params, currentAccount, prepareTransfertAddress, context]( + // const std::shared_ptr &account, linphone::RegistrationState state) mutable { + // if (context && account == currentAccount && state == linphone::RegistrationState::Ok) { + // CallModel::prepareTransfert(core->inviteAddressWithParams(address, params), + // prepareTransfertAddress); + // context->deleteLater(); + // context = nullptr; + // } + // }); + } + } else qWarning() << "prepareTransfert not impolemented"; + // CallModel::prepareTransfert(core->inviteAddressWithParams(address, params), prepareTransfertAddress); + */ +} diff --git a/Linphone/model/tool/ToolModel.hpp b/Linphone/model/tool/ToolModel.hpp index c6352e360..192c0ab3c 100644 --- a/Linphone/model/tool/ToolModel.hpp +++ b/Linphone/model/tool/ToolModel.hpp @@ -21,8 +21,10 @@ #ifndef TOOL_MODEL_H_ #define TOOL_MODEL_H_ +#include "core/call/Call.hpp" #include "tool/AbstractObject.hpp" +#include #include #include @@ -37,6 +39,10 @@ public: static QString getDisplayName(const std::shared_ptr &address); static QString getDisplayName(QString address); + static Call *startAudioCall(const QString &sipAddress, + const QString &prepareTransfertAddress = "", + const QHash &headers = {}); + private: DECLARE_ABSTRACT_OBJECT }; diff --git a/Linphone/tool/LinphoneEnums.cpp b/Linphone/tool/LinphoneEnums.cpp index 7224aa159..1d9615acb 100644 --- a/Linphone/tool/LinphoneEnums.cpp +++ b/Linphone/tool/LinphoneEnums.cpp @@ -27,6 +27,7 @@ // ============================================================================= void LinphoneEnums::registerMetaTypes() { + qRegisterMetaType(); qRegisterMetaType(); qRegisterMetaType(); qRegisterMetaType(); @@ -81,6 +82,13 @@ LinphoneEnums::ChatRoomState LinphoneEnums::fromLinphone(const linphone::ChatRoo return static_cast(data); } +linphone::Call::State LinphoneEnums::toLinphone(const LinphoneEnums::CallState &data) { + return static_cast(data); +} +LinphoneEnums::CallState LinphoneEnums::fromLinphone(const linphone::Call::State &data) { + return static_cast(data); +} + linphone::Call::Status LinphoneEnums::toLinphone(const LinphoneEnums::CallStatus &data) { return static_cast(data); } @@ -88,6 +96,25 @@ LinphoneEnums::CallStatus LinphoneEnums::fromLinphone(const linphone::Call::Stat return static_cast(data); } +QString LinphoneEnums::toString(const LinphoneEnums::CallStatus &data) { + switch (data) { + case LinphoneEnums::CallStatus::Declined: + return "Declined"; + case LinphoneEnums::CallStatus::Missed: + return "Missed"; + case LinphoneEnums::CallStatus::Success: + return "Success"; + case LinphoneEnums::CallStatus::Aborted: + return "Aborted"; + case LinphoneEnums::CallStatus::EarlyAborted: + return "EarlyAborted"; + case LinphoneEnums::CallStatus::AcceptedElsewhere: + return "AcceptedElsewhere"; + case LinphoneEnums::CallStatus::DeclinedElsewhere: + return "DeclinedElsewhere"; + } +} + linphone::Conference::Layout LinphoneEnums::toLinphone(const LinphoneEnums::ConferenceLayout &layout) { if (layout != LinphoneEnums::ConferenceLayout::AudioOnly) return static_cast(layout); else return linphone::Conference::Layout::Grid; // Audio Only mode diff --git a/Linphone/tool/LinphoneEnums.hpp b/Linphone/tool/LinphoneEnums.hpp index def3a691b..0662b0bb9 100644 --- a/Linphone/tool/LinphoneEnums.hpp +++ b/Linphone/tool/LinphoneEnums.hpp @@ -30,6 +30,7 @@ namespace LinphoneEnums { Q_NAMESPACE +Q_CLASSINFO("RegisterEnumClassesUnscoped", "false") // Avoid name clashes void registerMetaTypes(); @@ -115,6 +116,34 @@ Q_ENUM_NS(ChatRoomState) linphone::ChatRoom::State toLinphone(const LinphoneEnums::ChatRoomState &data); LinphoneEnums::ChatRoomState fromLinphone(const linphone::ChatRoom::State &data); +enum class CallState { + Idle = int(linphone::Call::State::Idle), + IncomingReceived = int(linphone::Call::State::IncomingReceived), + PushIncomingReceived = int(linphone::Call::State::PushIncomingReceived), + OutgoingInit = int(linphone::Call::State::OutgoingInit), + OutgoingProgress = int(linphone::Call::State::OutgoingProgress), + OutgoingRinging = int(linphone::Call::State::OutgoingRinging), + OutgoingEarlyMedia = int(linphone::Call::State::OutgoingEarlyMedia), + Connected = int(linphone::Call::State::Connected), + StreamsRunning = int(linphone::Call::State::StreamsRunning), + Pausing = int(linphone::Call::State::Pausing), + Paused = int(linphone::Call::State::Paused), + Resuming = int(linphone::Call::State::Resuming), + Referred = int(linphone::Call::State::Referred), + Error = int(linphone::Call::State::Error), + End = int(linphone::Call::State::End), + PausedByRemote = int(linphone::Call::State::PausedByRemote), + UpdatedByRemote = int(linphone::Call::State::UpdatedByRemote), + IncomingEarlyMedia = int(linphone::Call::State::IncomingEarlyMedia), + Updating = int(linphone::Call::State::Updating), + Released = int(linphone::Call::State::Released), + EarlyUpdatedByRemote = int(linphone::Call::State::EarlyUpdatedByRemote), + EarlyUpdating = int(linphone::Call::State::EarlyUpdating) +}; +Q_ENUM_NS(CallState) +linphone::Call::State toLinphone(const LinphoneEnums::CallState &data); +LinphoneEnums::CallState fromLinphone(const linphone::Call::State &data); + enum class CallStatus { Declined = int(linphone::Call::Status::Declined), Missed = int(linphone::Call::Status::Missed), @@ -126,8 +155,9 @@ enum class CallStatus { }; Q_ENUM_NS(CallStatus) -linphone::Call::Status toLinphone(const LinphoneEnums::CallStatus &capability); -LinphoneEnums::CallStatus fromLinphone(const linphone::Call::Status &capability); +linphone::Call::Status toLinphone(const LinphoneEnums::CallStatus &data); +LinphoneEnums::CallStatus fromLinphone(const linphone::Call::Status &data); +QString toString(const LinphoneEnums::CallStatus &data); enum class ConferenceLayout { Grid = int(linphone::Conference::Layout::Grid), @@ -227,7 +257,8 @@ LinphoneEnums::TransportType fromLinphone(const linphone::TransportType &type); QString toString(const LinphoneEnums::TransportType &type); void fromString(const QString &transportType, LinphoneEnums::TransportType *transport); } // namespace LinphoneEnums - +/* +Q_DECLARE_METATYPE(LinphoneEnums::CallState) Q_DECLARE_METATYPE(LinphoneEnums::CallStatus) Q_DECLARE_METATYPE(LinphoneEnums::ChatMessageState) Q_DECLARE_METATYPE(LinphoneEnums::ChatRoomState) @@ -242,5 +273,5 @@ Q_DECLARE_METATYPE(LinphoneEnums::RecorderState) Q_DECLARE_METATYPE(LinphoneEnums::RegistrationState) Q_DECLARE_METATYPE(LinphoneEnums::TunnelMode) Q_DECLARE_METATYPE(LinphoneEnums::TransportType) - +*/ #endif diff --git a/Linphone/tool/Utils.cpp b/Linphone/tool/Utils.cpp index bb27bf381..0d73b0917 100644 --- a/Linphone/tool/Utils.cpp +++ b/Linphone/tool/Utils.cpp @@ -21,6 +21,7 @@ #include "Utils.hpp" #include "core/App.hpp" +#include "model/call/CallModel.hpp" #include "model/object/VariantObject.hpp" #include "model/tool/ToolModel.hpp" @@ -43,7 +44,25 @@ VariantObject *Utils::getDisplayName(const QString &address) { VariantObject *data = new VariantObject(address); // Scope : GUI App::postModelAsync([coreObject = data->mCoreObject, address]() mutable { QString displayName = ToolModel::getDisplayName(address); - emit coreObject->valueChanged(displayName); + coreObject->setValue(displayName); }); return data; } + +VariantObject *Utils::startAudioCall(const QString &sipAddress, + const QString &prepareTransfertAddress, + const QHash &headers) { + VariantObject *data = new VariantObject(QVariant()); // Scope : GUI + qDebug() << "Calling " << sipAddress; + App::postModelAsync([coreObject = data->mCoreObject, sipAddress, prepareTransfertAddress, headers]() mutable { + auto call = ToolModel::startAudioCall(sipAddress, prepareTransfertAddress, headers); + if (call && coreObject) { + call->moveToThread(App::getInstance()->thread()); + coreObject->setValue(QVariant::fromValue(call)); + // App::postCoreAsync([data, call]() { data->setValue(QVariant::fromValue(call)); }); + // emit coreObject->valueChanged(call); + } + }); + + return data; +} diff --git a/Linphone/tool/Utils.hpp b/Linphone/tool/Utils.hpp index 3cee921c7..a184c0053 100644 --- a/Linphone/tool/Utils.hpp +++ b/Linphone/tool/Utils.hpp @@ -49,6 +49,9 @@ public: } Q_INVOKABLE static VariantObject *getDisplayName(const QString &address); + Q_INVOKABLE static VariantObject *startAudioCall(const QString &sipAddress, + const QString &prepareTransfertAddress = "", + const QHash &headers = {}); static inline QString coreStringToAppString(const std::string &str) { if (Constants::LinphoneLocaleEncoding == QString("UTF-8")) return QString::fromStdString(str); diff --git a/Linphone/view/CMakeLists.txt b/Linphone/view/CMakeLists.txt index 9aaf623e4..c7ce3b2c5 100644 --- a/Linphone/view/CMakeLists.txt +++ b/Linphone/view/CMakeLists.txt @@ -8,7 +8,12 @@ list(APPEND _LINPHONEAPP_QML_FILES view/Item/Carousel.qml view/Item/CheckBox.qml view/Item/ComboBox.qml + view/Item/DesktopPopup.qml view/Item/DigitInput.qml + + view/Item/Notification/Notification.qml + view/Item/Notification/NotificationReceivedCall.qml + view/Item/PhoneNumberComboBox.qml view/Item/PhoneNumberInput.qml view/Item/RadioButton.qml @@ -35,6 +40,7 @@ list(APPEND _LINPHONEAPP_QML_FILES # Prototypes view/Prototype/PhoneNumberPrototype.qml view/Prototype/AccountsPrototype.qml + view/Prototype/CallPrototype.qml ) list(APPEND _LINPHONEAPP_QML_SINGLETONS diff --git a/Linphone/view/Item/DesktopPopup.qml b/Linphone/view/Item/DesktopPopup.qml new file mode 100644 index 000000000..3a7396537 --- /dev/null +++ b/Linphone/view/Item/DesktopPopup.qml @@ -0,0 +1,187 @@ +import QtQuick 2.7 +import QtQuick.Window 2.2 + +import QtQuick.Controls 2.2 +import QtQuick.Layouts 1.3 +import Qt.labs.platform 1.0 + + +// ============================================================================= + Window { + id: mainItem + + // --------------------------------------------------------------------------- + + + property bool requestActivate: false + //property int flags: Qt.SplashScreen + + + + default property alias _content: content.data + property bool _isOpen: false + signal isOpened() + signal isClosed() + signal dataChanged() + + on_ContentChanged: dataChanged(_content) + // --------------------------------------------------------------------------- + + function open () { + _isOpen = true; + isOpened(); + } +/* + function close () { + _isOpen = false + isClosed() + } +*/ + // --------------------------------------------------------------------------- + + objectName: '__internalWindow' + property bool isFrameLess : false; + property bool showAsTool : false + // Don't use Popup for flags : it could lead to error in geometry. On Mac, Using Tool ensure to have the Window on Top and fullscreen independant + flags: Qt.BypassWindowManagerHint | (showAsTool?Qt.Tool:Qt.WindowStaysOnTopHint) | Qt.Window | Qt.FramelessWindowHint; + opacity: 1.0 + height: _content[0] != null ? _content[0].height : 0 + width: _content[0] != null ? _content[0].width : 0 + visible:true + Item { + id: content + anchors.fill:parent + + property var $parent: mainItem + } + + // --------------------------------------------------------------------------- +/* + states: State { + name: 'opening' + when: _isOpen + + PropertyChanges { + opacity: 1.0 + target: window + } + } + + transitions: [ + Transition { + from: '' + to: 'opening' + ScriptAction { + script: { + if (wrapper.requestActivate) { + window.requestActivate() + } + } + } + }, + Transition { + from: '*' + to: '' + ScriptAction { + script: window.close() + } + } + ] + */ +} + +/* +Item { + id: wrapper + objectName: '__internalWrapper' + + // --------------------------------------------------------------------------- + + property alias popupX: window.x + property alias popupY: window.y + property bool requestActivate: false + property int flags: Qt.SplashScreen + + readonly property alias popupWidth: window.width + readonly property alias popupHeight: window.height + + default property alias _content: content.data + property bool _isOpen: false + signal isOpened() + signal isClosed() + signal dataChanged() + + on_ContentChanged: dataChanged(_content) + // --------------------------------------------------------------------------- + + function open () { + _isOpen = true; + isOpened(); + } + + function close () { + _isOpen = false + isClosed() + } + + // --------------------------------------------------------------------------- + + // No size, no position. + height: 0 + width: 0 + visible:true + + Window { + id: window + objectName: '__internalWindow' + property bool isFrameLess : false; + property bool showAsTool : false + // Don't use Popup for flags : it could lead to error in geometry. On Mac, Using Tool ensure to have the Window on Top and fullscreen independant + flags: Qt.BypassWindowManagerHint | (showAsTool?Qt.Tool:Qt.WindowStaysOnTopHint) | Qt.Window | Qt.FramelessWindowHint; + onXChanged: console.log(x) + opacity: 1.0 + height: _content[0] != null ? _content[0].height : 0 + width: _content[0] != null ? _content[0].width : 0 + visible:true + Item { + id: content + anchors.fill:parent + + property var $parent: wrapper + } + } + + // --------------------------------------------------------------------------- + + states: State { + name: 'opening' + when: _isOpen + + PropertyChanges { + opacity: 1.0 + target: window + } + } + + transitions: [ + Transition { + from: '' + to: 'opening' + ScriptAction { + script: { + if (wrapper.requestActivate) { + window.requestActivate() + } + } + } + }, + Transition { + from: '*' + to: '' + ScriptAction { + script: window.close() + } + } + ] +} +*/ diff --git a/Linphone/view/Item/Notification/Notification.qml b/Linphone/view/Item/Notification/Notification.qml new file mode 100644 index 000000000..cf4fcb708 --- /dev/null +++ b/Linphone/view/Item/Notification/Notification.qml @@ -0,0 +1,57 @@ +import QtQuick 2.7 +import Linphone + +// ============================================================================= + +DesktopPopup { + id: notification + + + property var notificationData: ({ + timelineModel : null + }) + property int overrodeHeight + default property alias _content: content.data + + signal deleteNotification (var notification) + + // Use as an intermediate between signal/slot without propagate the notification var : last signal parameter will be the last notification instance + function deleteNotificationSlot(){ + deleteNotification(notification) + } + + function _close (cb) { + if (cb) { + cb() + } + deleteNotificationSlot(); + } + + Rectangle { + color: "#FFFFFF" + height: overrodeHeight || 120 + width: 300 + + border { + color: "#A1A1A1" + width: 1 + } + + Item { + id: content + + anchors.fill: parent + } + + Image { + id: iconSign + + anchors { + left: parent.left + top: parent.top + } + + + } + } +} diff --git a/Linphone/view/Item/Notification/NotificationReceivedCall.qml b/Linphone/view/Item/Notification/NotificationReceivedCall.qml new file mode 100644 index 000000000..85c04416a --- /dev/null +++ b/Linphone/view/Item/Notification/NotificationReceivedCall.qml @@ -0,0 +1,54 @@ +import QtQuick 2.7 +import QtQuick.Layouts 1.3 +import Linphone + +// ============================================================================= + +Notification { + id: notification + + // --------------------------------------------------------------------------- + + readonly property var call: notificationData && notificationData.call + property var state: call.state + onStateChanged:{ + if(state != LinphoneEnums.CallState.IncomingReceived){ + close() + } + } + // --------------------------------------------------------------------------- + ColumnLayout { + anchors.fill: parent + anchors.leftMargin: 15 + anchors.rightMargin: 15 + anchors.bottomMargin:15 + spacing: 0 + + // --------------------------------------------------------------------- + // Action buttons. + // --------------------------------------------------------------------- + + RowLayout { + Layout.fillHeight: true + Layout.fillWidth: true + Button { + text: 'Accept' + Layout.rightMargin: 20 + onClicked: { + notification.call.lAccept() + } + } + Item{ + Layout.fillWidth: true + Layout.fillHeight: true + } + Button { + text: 'Reject' + Layout.rightMargin: 20 + onClicked: { + notification.call.lDecline() + } + } + } + } +} diff --git a/Linphone/view/Prototype/CallPrototype.qml b/Linphone/view/Prototype/CallPrototype.qml new file mode 100644 index 000000000..7b75659eb --- /dev/null +++ b/Linphone/view/Prototype/CallPrototype.qml @@ -0,0 +1,80 @@ +import QtQuick 2.15 +import QtQuick.Layouts 1.0 +import QtQuick.Controls as Control +import Linphone +import UtilsCpp 1.0 + +// Snippet +Window{ + id: mainItem + height: 400 + width: 800 + onWidthChanged: console.log(width) + property var callVarObject + property var call: callVarObject ? callVarObject.value : null + property var callState: call && call.state + onCallStateChanged: console.log("State:" +callState) + visible: true + onCallChanged: console.log('New Call:' +call) + ColumnLayout{ + anchors.fill: parent + RowLayout { + Layout.fillWidth: true + LoginForm{ + } + Rectangle{ + Layout.preferredWidth: 50 + Layout.preferredHeight: 50 + + color: LoginPageCpp.registrationState === LinphoneEnums.RegistrationState.Ok + ? 'green' + : LoginPageCpp.registrationState === LinphoneEnums.RegistrationState.Failed || LoginPageCpp.registrationState === LinphoneEnums.RegistrationState.None + ? 'red' + : 'orange' + } + TextInput { + id: usernameToCall + label: "Username to call" + textInputWidth: 250 + } + Button{ + text: 'Call' + onClicked: { + mainItem.callVarObject = UtilsCpp.startAudioCall(usernameToCall.inputText + "@sip.linphone.org") + } + } + } + + Rectangle{ + Layout.fillWidth: true + Layout.preferredHeight: 50 + color: call + ? call.state === LinphoneEnums.CallState.StreamsRunning + ? 'green' + : call.state === LinphoneEnums.CallState.Released + ? 'pink' + : 'orange' + : 'red' + Rectangle{ + anchors.centerIn: parent + color: 'white' + width: stateText.contentWidth + height: stateText.contentHeight + Text{ + id: stateText + text: "State:"+(mainItem.callState ? mainItem.callState : 'None') + } + } + } + Text{ + id: errorMessageText + text: mainItem.call ? mainItem.call.lastErrorMessage : '' + color: 'red' + } + Item{ + Layout.fillHeight: true + Layout.fillWidth: true + } + } +} + diff --git a/external/linphone-sdk b/external/linphone-sdk index 8756a37ad..aaeaad94a 160000 --- a/external/linphone-sdk +++ b/external/linphone-sdk @@ -1 +1 @@ -Subproject commit 8756a37ad10399a0c27d32590a0d1cc87ea97c2e +Subproject commit aaeaad94a6e63f182f50318ed1a209c83b152d73