Add 02_IncomingCall qt tutorial.
This commit is contained in:
parent
28c66b6398
commit
dfad6c515f
1
qt/02_IncomingCall/.gitignore
vendored
Normal file
1
qt/02_IncomingCall/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
build/
|
52
qt/02_IncomingCall/CMakeLists.txt
Normal file
52
qt/02_IncomingCall/CMakeLists.txt
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
cmake_minimum_required(VERSION 3.22)
|
||||||
|
|
||||||
|
project(02_IncomingCall LANGUAGES CXX)
|
||||||
|
|
||||||
|
set(CMAKE_CXX_STANDARD 17)
|
||||||
|
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||||
|
|
||||||
|
find_package(Qt5 REQUIRED COMPONENTS Core Gui Qml Quick)
|
||||||
|
find_package(LinphoneCxx REQUIRED)
|
||||||
|
|
||||||
|
set(CMAKE_AUTOMOC ON)
|
||||||
|
SET(CMAKE_AUTOUIC ON)
|
||||||
|
|
||||||
|
set(SOURCES
|
||||||
|
"src/main.cpp"
|
||||||
|
"src/App.cpp"
|
||||||
|
"src/CoreHandler.cpp"
|
||||||
|
"src/CoreListener.cpp"
|
||||||
|
"src/CoreManager.cpp"
|
||||||
|
)
|
||||||
|
set(QRC_RESOURCES resources.qrc)
|
||||||
|
set(QML_SOURCES)
|
||||||
|
file(STRINGS ${QRC_RESOURCES} QRC_RESOURCES_CONTENT)
|
||||||
|
foreach(line ${QRC_RESOURCES_CONTENT})
|
||||||
|
set(result)
|
||||||
|
string(REGEX REPLACE
|
||||||
|
"^[ \t]*<[ \t]*file[ \t]*>[ \t]*(.+\\.[a-z]+)[ \t]*<[ \t]*/[ \t]*file[ \t]*>[ \t]*$"
|
||||||
|
"\\1"
|
||||||
|
result
|
||||||
|
"${line}"
|
||||||
|
)
|
||||||
|
string(REGEX MATCH "\\.[a-z]+$" is_ui ${result})
|
||||||
|
if(NOT ${is_ui} STREQUAL "")
|
||||||
|
list(APPEND QML_SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/${result}")
|
||||||
|
endif()
|
||||||
|
endforeach()
|
||||||
|
|
||||||
|
get_filename_component(SDK_PATH "${CMAKE_PREFIX_PATH}" REALPATH)
|
||||||
|
find_path(MSPLUGINS_PATH "plugins" PATH_SUFFIXES "lib/mediastreamer" "lib64/mediastreamer" REQUIRED)
|
||||||
|
set(MSPLUGINS_PATH "${MSPLUGINS_PATH}/plugins")
|
||||||
|
|
||||||
|
add_executable(02_IncomingCall ${SOURCES} ${QML_SOURCES} ${QRC_RESOURCES})
|
||||||
|
|
||||||
|
target_compile_definitions(02_IncomingCall PRIVATE "SDK_PATH=\"${SDK_PATH}\"" "MSPLUGINS_PATH=\"${MSPLUGINS_PATH}\"")
|
||||||
|
target_include_directories(02_IncomingCall PRIVATE ${LINPHONECXX_INCLUDE_DIRS})
|
||||||
|
target_link_libraries(02_IncomingCall PRIVATE Qt5::Core Qt5::Gui Qt5::Qml Qt5::Quick ${LINPHONECXX_LIBRARIES})
|
||||||
|
|
||||||
|
set_target_properties(02_IncomingCall PROPERTIES AUTORCC ON)
|
||||||
|
set_target_properties(02_IncomingCall PROPERTIES
|
||||||
|
WIN32_EXECUTABLE ON
|
||||||
|
MACOSX_BUNDLE ON
|
||||||
|
)
|
15
qt/02_IncomingCall/README.md
Normal file
15
qt/02_IncomingCall/README.md
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
# Incoming call tutorial
|
||||||
|
|
||||||
|
This time we are going to receive our first calls!
|
||||||
|
|
||||||
|
If you don't have SIP friends to test with, you can also install Linphone on your mobile device (Android or iOS) and call yourself with a different account.
|
||||||
|
|
||||||
|
|
||||||
|
## How to build
|
||||||
|
|
||||||
|
In the following instructions, replace **<PATH-TO-SDK>** by the real path where your SDK is located, e.g. *~/projects/linphone-sdk/build-default/linphone-sdk/desktop/*
|
||||||
|
|
||||||
|
mkdir build
|
||||||
|
cd build
|
||||||
|
cmake .. -DCMAKE_PREFIX_PATH=<PATH-TO-SDK>
|
||||||
|
cmake --build .
|
7
qt/02_IncomingCall/resources.qrc
Normal file
7
qt/02_IncomingCall/resources.qrc
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
<RCC>
|
||||||
|
<qresource prefix="/">
|
||||||
|
<file>ui/MainPage.qml</file>
|
||||||
|
<file>ui/CallPage.qml</file>
|
||||||
|
<file>ui/RegistrationPage.qml</file>
|
||||||
|
</qresource>
|
||||||
|
</RCC>
|
49
qt/02_IncomingCall/src/App.cpp
Normal file
49
qt/02_IncomingCall/src/App.cpp
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
#include <QDir>
|
||||||
|
#include <QQmlContext>
|
||||||
|
|
||||||
|
#include "App.hpp"
|
||||||
|
#include "CoreManager.hpp"
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
using namespace linphone;
|
||||||
|
|
||||||
|
App::App(int &argc, char *argv[]) : QGuiApplication(argc, argv)
|
||||||
|
{
|
||||||
|
setOrganizationName("Belledonne Communications");
|
||||||
|
setOrganizationDomain("belledonne-communications.com");
|
||||||
|
setApplicationName(QFileInfo(applicationFilePath()).baseName());
|
||||||
|
}
|
||||||
|
|
||||||
|
App::~App()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void App::init()
|
||||||
|
{
|
||||||
|
registerTypes();
|
||||||
|
|
||||||
|
mEngine = new QQmlApplicationEngine();
|
||||||
|
mEngine->load(QUrl("qrc:/ui/MainPage.qml"));
|
||||||
|
if (mEngine->rootObjects().isEmpty())
|
||||||
|
qFatal("Unable to open main window.");
|
||||||
|
|
||||||
|
// Initialize the CoreManager singleton and add it to the Qml context.
|
||||||
|
CoreManager::init(this);
|
||||||
|
auto coreManager = CoreManager::getInstance();
|
||||||
|
QQmlContext *ctx = mEngine->rootContext();
|
||||||
|
ctx->setContextProperty("coreManager", coreManager);
|
||||||
|
}
|
||||||
|
|
||||||
|
void App::stop()
|
||||||
|
{
|
||||||
|
CoreManager::uninit();
|
||||||
|
}
|
||||||
|
|
||||||
|
void App::registerTypes()
|
||||||
|
{
|
||||||
|
qRegisterMetaType<string>();
|
||||||
|
qRegisterMetaType<RegistrationState>();
|
||||||
|
qRegisterMetaType<shared_ptr<Account>>();
|
||||||
|
qRegisterMetaType<Call::State>();
|
||||||
|
qRegisterMetaType<shared_ptr<Call>>();
|
||||||
|
}
|
27
qt/02_IncomingCall/src/App.hpp
Normal file
27
qt/02_IncomingCall/src/App.hpp
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
#include <QGuiApplication>
|
||||||
|
#include <QQmlApplicationEngine>
|
||||||
|
|
||||||
|
#include <linphone++/linphone.hh>
|
||||||
|
|
||||||
|
class App : public QGuiApplication
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
App(int &argc, char *argv[]);
|
||||||
|
~App();
|
||||||
|
|
||||||
|
void init();
|
||||||
|
void stop();
|
||||||
|
|
||||||
|
private:
|
||||||
|
void registerTypes();
|
||||||
|
|
||||||
|
QQmlApplicationEngine *mEngine = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
Q_DECLARE_METATYPE(std::string);
|
||||||
|
Q_DECLARE_METATYPE(linphone::RegistrationState);
|
||||||
|
Q_DECLARE_METATYPE(std::shared_ptr<linphone::Account>);
|
||||||
|
Q_DECLARE_METATYPE(linphone::Call::State);
|
||||||
|
Q_DECLARE_METATYPE(std::shared_ptr<linphone::Call>);
|
75
qt/02_IncomingCall/src/CoreHandler.cpp
Normal file
75
qt/02_IncomingCall/src/CoreHandler.cpp
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2010-2024 Belledonne Communications SARL.
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <QDebug>
|
||||||
|
|
||||||
|
#include "CoreHandler.hpp"
|
||||||
|
#include "CoreListener.hpp"
|
||||||
|
#include "CoreManager.hpp"
|
||||||
|
|
||||||
|
CoreHandler::CoreHandler()
|
||||||
|
{
|
||||||
|
mCoreListener = std::make_shared<CoreListener>();
|
||||||
|
connectTo(mCoreListener.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
void CoreHandler::setListener(std::shared_ptr<linphone::Core> core)
|
||||||
|
{
|
||||||
|
core->addListener(mCoreListener);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CoreHandler::removeListener(std::shared_ptr<linphone::Core> core)
|
||||||
|
{
|
||||||
|
core->removeListener(mCoreListener);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CoreHandler::onAccountRegistrationStateChanged(const std::shared_ptr<linphone::Core> &, const std::shared_ptr<linphone::Account> &account, linphone::RegistrationState state, const std::string &message)
|
||||||
|
{
|
||||||
|
emit registrationStateChanged(account, state, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CoreHandler::onCallStateStateChanged(const std::shared_ptr<linphone::Core> &, const std::shared_ptr<linphone::Call> &call, linphone::Call::State state, const std::string &message)
|
||||||
|
{
|
||||||
|
emit callStateChanged(call, state, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CoreHandler::onGlobalStateChanged(const std::shared_ptr<linphone::Core> &, linphone::GlobalState state, const std::string &message)
|
||||||
|
{
|
||||||
|
switch (state)
|
||||||
|
{
|
||||||
|
case linphone::GlobalState::On:
|
||||||
|
qInfo() << "Core is running " << QString::fromStdString(message);
|
||||||
|
break;
|
||||||
|
case linphone::GlobalState::Off:
|
||||||
|
qInfo() << "Core is stopped " << QString::fromStdString(message);
|
||||||
|
emit coreStopped();
|
||||||
|
break;
|
||||||
|
case linphone::GlobalState::Startup:
|
||||||
|
qInfo() << "Core is starting" << QString::fromStdString(message);
|
||||||
|
emit coreStarting();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CoreHandler::connectTo(CoreListener *listener)
|
||||||
|
{
|
||||||
|
connect(listener, &CoreListener::accountRegistrationStateChanged, this, &CoreHandler::onAccountRegistrationStateChanged);
|
||||||
|
connect(listener, &CoreListener::callStateChanged, this, &CoreHandler::onCallStateStateChanged);
|
||||||
|
connect(listener, &CoreListener::globalStateChanged, this, &CoreHandler::onGlobalStateChanged);
|
||||||
|
}
|
49
qt/02_IncomingCall/src/CoreHandler.hpp
Normal file
49
qt/02_IncomingCall/src/CoreHandler.hpp
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2010-2024 Belledonne Communications SARL.
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <linphone++/linphone.hh>
|
||||||
|
#include <QObject>
|
||||||
|
|
||||||
|
class CoreListener;
|
||||||
|
|
||||||
|
class CoreHandler : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
CoreHandler();
|
||||||
|
|
||||||
|
void setListener(std::shared_ptr<linphone::Core> core);
|
||||||
|
void removeListener(std::shared_ptr<linphone::Core> core);
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void coreStarting();
|
||||||
|
void coreStopped();
|
||||||
|
void callStateChanged(const std::shared_ptr<linphone::Call>, linphone::Call::State state, const std::string &message);
|
||||||
|
void registrationStateChanged(const std::shared_ptr<linphone::Account> &account, linphone::RegistrationState state, const std::string &message);
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void onAccountRegistrationStateChanged(const std::shared_ptr<linphone::Core> &core, const std::shared_ptr<linphone::Account> &account, linphone::RegistrationState state, const std::string &message);
|
||||||
|
void onCallStateStateChanged(const std::shared_ptr<linphone::Core> &core, const std::shared_ptr<linphone::Call> &call, linphone::Call::State state, const std::string &message);
|
||||||
|
void onGlobalStateChanged(const std::shared_ptr<linphone::Core> &core, linphone::GlobalState gstate, const std::string &message);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void connectTo(CoreListener *listener);
|
||||||
|
std::shared_ptr<CoreListener> mCoreListener;
|
||||||
|
};
|
37
qt/02_IncomingCall/src/CoreListener.cpp
Normal file
37
qt/02_IncomingCall/src/CoreListener.cpp
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2010-2024 Belledonne Communications SARL.
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "CoreListener.hpp"
|
||||||
|
|
||||||
|
CoreListener::CoreListener(QObject *parent) : QObject(parent)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void CoreListener::onAccountRegistrationStateChanged(const std::shared_ptr<linphone::Core> &core, const std::shared_ptr<linphone::Account> &account, linphone::RegistrationState state, const std::string &message)
|
||||||
|
{
|
||||||
|
emit accountRegistrationStateChanged(core, account, state, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CoreListener::onCallStateChanged(const std::shared_ptr<linphone::Core> &core, const std::shared_ptr<linphone::Call> &call, linphone::Call::State state, const std::string &message)
|
||||||
|
{
|
||||||
|
emit callStateChanged(core, call, state, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CoreListener::onGlobalStateChanged(const std::shared_ptr<linphone::Core> &core, linphone::GlobalState gstate, const std::string &message)
|
||||||
|
{
|
||||||
|
emit globalStateChanged(core, gstate, message);
|
||||||
|
}
|
39
qt/02_IncomingCall/src/CoreListener.hpp
Normal file
39
qt/02_IncomingCall/src/CoreListener.hpp
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2010-2024 Belledonne Communications SARL.
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <linphone++/linphone.hh>
|
||||||
|
#include <QObject>
|
||||||
|
|
||||||
|
class CoreListener : public QObject, public linphone::CoreListener
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
CoreListener(QObject *parent = nullptr);
|
||||||
|
virtual ~CoreListener() = default;
|
||||||
|
|
||||||
|
virtual void onAccountRegistrationStateChanged(const std::shared_ptr<linphone::Core> &core, const std::shared_ptr<linphone::Account> &account, linphone::RegistrationState state, const std::string &message) override;
|
||||||
|
virtual void onCallStateChanged(const std::shared_ptr<linphone::Core> &core, const std::shared_ptr<linphone::Call> &call, linphone::Call::State state, const std::string &message) override;
|
||||||
|
virtual void onGlobalStateChanged(const std::shared_ptr<linphone::Core> &core, linphone::GlobalState gstate, const std::string &message) override;
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void accountRegistrationStateChanged(const std::shared_ptr<linphone::Core> &core, const std::shared_ptr<linphone::Account> &account, linphone::RegistrationState state, const std::string &message);
|
||||||
|
void callStateChanged(const std::shared_ptr<linphone::Core> &core, const std::shared_ptr<linphone::Call> &call, linphone::Call::State state, const std::string &message);
|
||||||
|
void globalStateChanged(const std::shared_ptr<linphone::Core> &core, linphone::GlobalState gstate, const std::string &message);
|
||||||
|
};
|
285
qt/02_IncomingCall/src/CoreManager.cpp
Normal file
285
qt/02_IncomingCall/src/CoreManager.cpp
Normal file
@ -0,0 +1,285 @@
|
|||||||
|
#include <QDebug>
|
||||||
|
|
||||||
|
#include "CoreHandler.hpp"
|
||||||
|
#include "CoreManager.hpp"
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
using namespace linphone;
|
||||||
|
|
||||||
|
CoreManager *CoreManager::mInstance = nullptr;
|
||||||
|
|
||||||
|
CoreManager::CoreManager(QObject *parent) : QObject(parent)
|
||||||
|
{
|
||||||
|
mPage = QString("qrc:/ui/RegistrationPage.qml");
|
||||||
|
mHeaderText = QString("Registration Form");
|
||||||
|
mSoundButtonText = QString("Switch off sound");
|
||||||
|
mMicrophoneButtonText = QString("Mute");
|
||||||
|
mHandler = QSharedPointer<CoreHandler>::create();
|
||||||
|
CoreHandler *coreHandler = mHandler.get();
|
||||||
|
QObject::connect(coreHandler, &CoreHandler::coreStarting, this, &CoreManager::startIterate, Qt::QueuedConnection);
|
||||||
|
QObject::connect(coreHandler, &CoreHandler::coreStopped, this, &CoreManager::stopIterate, Qt::QueuedConnection);
|
||||||
|
QObject::connect(coreHandler, &CoreHandler::callStateChanged, this, &CoreManager::onCallStateChanged, Qt::QueuedConnection);
|
||||||
|
QObject::connect(coreHandler, &CoreHandler::registrationStateChanged, this, &CoreManager::onRegistrationStateChanged, Qt::QueuedConnection);
|
||||||
|
|
||||||
|
// Delay the creation of the core so that the CoreManager instance is
|
||||||
|
// already set.
|
||||||
|
QTimer::singleShot(10, [this]()
|
||||||
|
{ createLinphoneCore(); });
|
||||||
|
}
|
||||||
|
|
||||||
|
CoreManager::~CoreManager()
|
||||||
|
{
|
||||||
|
mHandler->removeListener(mCore);
|
||||||
|
mHandler = nullptr;
|
||||||
|
mCore = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CoreManager::init(QObject *parent)
|
||||||
|
{
|
||||||
|
if (mInstance)
|
||||||
|
return;
|
||||||
|
mInstance = new CoreManager(parent);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CoreManager::uninit()
|
||||||
|
{
|
||||||
|
if (mInstance)
|
||||||
|
{
|
||||||
|
mInstance->stopIterate();
|
||||||
|
auto core = mInstance->mCore;
|
||||||
|
delete mInstance;
|
||||||
|
mInstance = nullptr;
|
||||||
|
core->stop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CoreManager *CoreManager::getInstance()
|
||||||
|
{
|
||||||
|
return mInstance;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CoreManager::startIterate()
|
||||||
|
{
|
||||||
|
// Start a timer to call the core iterate every 20 ms.
|
||||||
|
mIterateTimer = new QTimer(this);
|
||||||
|
mIterateTimer->setInterval(20);
|
||||||
|
QObject::connect(mIterateTimer, &QTimer::timeout, this, &CoreManager::iterate);
|
||||||
|
qInfo() << QStringLiteral("Start iterate");
|
||||||
|
mIterateTimer->start();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CoreManager::stopIterate()
|
||||||
|
{
|
||||||
|
qInfo() << QStringLiteral("Stop iterate");
|
||||||
|
mIterateTimer->stop();
|
||||||
|
mIterateTimer->deleteLater(); // Allow the timer to continue its stuff
|
||||||
|
mIterateTimer = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CoreManager::login(QString identity, QString password)
|
||||||
|
{
|
||||||
|
if (mLoginButtonEnabled)
|
||||||
|
{
|
||||||
|
setProperty("loginButtonEnabled", false);
|
||||||
|
|
||||||
|
shared_ptr<Address> address = Factory::get()->createAddress(identity.toStdString());
|
||||||
|
shared_ptr<AuthInfo> authInfo = Factory::get()->createAuthInfo(address->getUsername(), "", password.toStdString(), "", "", address->getDomain());
|
||||||
|
mCore->addAuthInfo(authInfo);
|
||||||
|
|
||||||
|
shared_ptr<AccountParams> accountParams = mCore->createAccountParams();
|
||||||
|
accountParams->setIdentityAddress(address);
|
||||||
|
string serverAddr = "sip:" + address->getDomain() + ";transport=tls";
|
||||||
|
accountParams->setServerAddr(serverAddr);
|
||||||
|
|
||||||
|
accountParams->enableRegister(true);
|
||||||
|
|
||||||
|
shared_ptr<Account> account = mCore->createAccount(accountParams);
|
||||||
|
mCore->addAccount(account);
|
||||||
|
mCore->setDefaultAccount(account);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CoreManager::logout()
|
||||||
|
{
|
||||||
|
shared_ptr<Account> account = mCore->getDefaultAccount();
|
||||||
|
if (account)
|
||||||
|
{
|
||||||
|
shared_ptr<AccountParams> accountParams = account->getParams()->clone();
|
||||||
|
accountParams->enableRegister(false);
|
||||||
|
account->setParams(accountParams);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CoreManager::onCallStateChanged(const std::shared_ptr<linphone::Call> &call, linphone::Call::State state, const std::string &message)
|
||||||
|
{
|
||||||
|
setProperty("callStateText", QString::fromStdString("Your call state is: " + message));
|
||||||
|
switch (state)
|
||||||
|
{
|
||||||
|
case Call::State::IncomingReceived:
|
||||||
|
// When you receive a call the Call::State is incoming receive. By default you can only have one current call,
|
||||||
|
// so if a call is in progress or one is already ringing the second remote call will be decline with the reason
|
||||||
|
// "Busy". If you want to implement a multi call app you can increase Core::setMaxCalls.
|
||||||
|
// Here we store the incoming call reference so we can accept or decline the call on user input.
|
||||||
|
mIncomingCall = call;
|
||||||
|
// And we update the GUI to notify the user of the incoming call.
|
||||||
|
setProperty("incomingCallVisible", true);
|
||||||
|
setProperty("incomingCallText", QString::fromStdString(mIncomingCall->getRemoteAddress()->asString()));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Call::State::StreamsRunning:
|
||||||
|
// The StreamsRunning state is the default one during a call.
|
||||||
|
callInProgressGuiUpdates();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Call::State::Error:
|
||||||
|
case Call::State::End:
|
||||||
|
case Call::State::Released:
|
||||||
|
// By default after 30 seconds of ringing without accept or decline a call is
|
||||||
|
// automatically ended.
|
||||||
|
mIncomingCall = nullptr;
|
||||||
|
endingCallGuiUpdates();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CoreManager::onRegistrationStateChanged(const shared_ptr<Account> &account, RegistrationState state, const string &message)
|
||||||
|
{
|
||||||
|
setProperty("registerText", QString::fromStdString("Your registration state is : " + message));
|
||||||
|
switch (state)
|
||||||
|
{
|
||||||
|
// If the Account was logged out, we clear the Core.
|
||||||
|
case RegistrationState::Cleared:
|
||||||
|
case RegistrationState::None:
|
||||||
|
mCore->clearAllAuthInfo();
|
||||||
|
mCore->clearAccounts();
|
||||||
|
setProperty("loginButtonEnabled", true);
|
||||||
|
break;
|
||||||
|
case RegistrationState::Ok:
|
||||||
|
setProperty("loginButtonEnabled", false);
|
||||||
|
setProperty("headerText", QString::fromStdString("Hello " + mCore->getDefaultProxyConfig()->findAuthInfo()->getUsername()));
|
||||||
|
setProperty("page", QString("qrc:/ui/CallPage.qml"));
|
||||||
|
break;
|
||||||
|
case RegistrationState::Progress:
|
||||||
|
setProperty("loginButtonEnabled", false);
|
||||||
|
break;
|
||||||
|
case RegistrationState::Failed:
|
||||||
|
setProperty("loginButtonEnabled", true);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CoreManager::hangup()
|
||||||
|
{
|
||||||
|
// Simply call terminateAllCalls to hang out.
|
||||||
|
mCore->terminateAllCalls();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CoreManager::soundButtonClicked()
|
||||||
|
{
|
||||||
|
if (toggleSpeaker())
|
||||||
|
{
|
||||||
|
setProperty("soundButtonText", QString("Switch on Sound"));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
setProperty("soundButtonText", QString("Switch off Sound"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CoreManager::microphoneButtonClicked()
|
||||||
|
{
|
||||||
|
if (toggleMicrophone())
|
||||||
|
{
|
||||||
|
setProperty("microphoneButtonText", QString("Mute"));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
setProperty("microphoneButtonText", QString("Unmute"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CoreManager::answer()
|
||||||
|
{
|
||||||
|
if (mIncomingCall)
|
||||||
|
{
|
||||||
|
// To accept a call only use the accept() method on the call object.
|
||||||
|
// If we wanted, we could create a CallParams object and answer using this object to make changes to the call configuration.
|
||||||
|
mIncomingCall->accept();
|
||||||
|
mIncomingCall = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CoreManager::decline()
|
||||||
|
{
|
||||||
|
if (mIncomingCall)
|
||||||
|
{
|
||||||
|
// You have to give a Reason to decline a call. This info is sent to the remote.
|
||||||
|
mIncomingCall->decline(Reason::Declined);
|
||||||
|
mIncomingCall = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CoreManager::createLinphoneCore()
|
||||||
|
{
|
||||||
|
// Setting linphone log level to message.
|
||||||
|
auto loggingService = LoggingService::get();
|
||||||
|
loggingService->setLogLevel(LogLevel::Message);
|
||||||
|
|
||||||
|
// Configure paths.
|
||||||
|
string assetsPath = string(SDK_PATH) + "/share";
|
||||||
|
Factory::get()->setTopResourcesDir(assetsPath);
|
||||||
|
Factory::get()->setDataResourcesDir(assetsPath);
|
||||||
|
Factory::get()->setSoundResourcesDir(assetsPath + "/sounds/linphone");
|
||||||
|
Factory::get()->setRingResourcesDir(Factory::get()->getSoundResourcesDir() + "/rings");
|
||||||
|
Factory::get()->setImageResourcesDir(assetsPath + "/images");
|
||||||
|
Factory::get()->setMspluginsDir(MSPLUGINS_PATH);
|
||||||
|
|
||||||
|
// Create a core from the factory.
|
||||||
|
mCore = Factory::get()->createCore("", "", nullptr);
|
||||||
|
|
||||||
|
mCore->setRootCa(assetsPath + "/linphone/rootca.pem");
|
||||||
|
|
||||||
|
// Listen for core events.
|
||||||
|
mHandler->setListener(mCore);
|
||||||
|
|
||||||
|
// Start the core.
|
||||||
|
mCore->start();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CoreManager::iterate()
|
||||||
|
{
|
||||||
|
if (mCore)
|
||||||
|
mCore->iterate();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CoreManager::toggleSpeaker()
|
||||||
|
{
|
||||||
|
// Calling setSpeakerMuted(true) on a Call object disables the sound output of this call.
|
||||||
|
bool newValue = !mCore->getCurrentCall()->getSpeakerMuted();
|
||||||
|
mCore->getCurrentCall()->setSpeakerMuted(newValue);
|
||||||
|
return newValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CoreManager::toggleMicrophone()
|
||||||
|
{
|
||||||
|
// The following toggles the microphone, disabling completely / enabling the sound capture from the device microphone
|
||||||
|
bool newValue = !mCore->micEnabled();
|
||||||
|
mCore->enableMic(newValue);
|
||||||
|
return newValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CoreManager::callInProgressGuiUpdates()
|
||||||
|
{
|
||||||
|
setProperty("incomingCallVisible", false);
|
||||||
|
setProperty("inCallButtonsEnabled", true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CoreManager::endingCallGuiUpdates()
|
||||||
|
{
|
||||||
|
setProperty("incomingCallVisible", false);
|
||||||
|
setProperty("inCallButtonsEnabled", false);
|
||||||
|
setProperty("soundButtonText", "Switch off Sound");
|
||||||
|
setProperty("microphoneButtonText", "Mute");
|
||||||
|
}
|
84
qt/02_IncomingCall/src/CoreManager.hpp
Normal file
84
qt/02_IncomingCall/src/CoreManager.hpp
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
#include <QSharedPointer>
|
||||||
|
#include <QTimer>
|
||||||
|
|
||||||
|
#include <linphone++/linphone.hh>
|
||||||
|
|
||||||
|
class CoreHandler;
|
||||||
|
|
||||||
|
class CoreManager : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
Q_PROPERTY(QString page MEMBER mPage NOTIFY pageChanged);
|
||||||
|
Q_PROPERTY(QString headerText MEMBER mHeaderText NOTIFY headerTextChanged)
|
||||||
|
Q_PROPERTY(QString registerText MEMBER mRegisterText NOTIFY registerTextChanged)
|
||||||
|
Q_PROPERTY(bool loginButtonEnabled MEMBER mLoginButtonEnabled NOTIFY loginButtonEnabledChanged)
|
||||||
|
Q_PROPERTY(QString callStateText MEMBER mCallStateText NOTIFY callStateTextChanged)
|
||||||
|
Q_PROPERTY(bool incomingCallVisible MEMBER mIncomingCallVisible NOTIFY incomingCallVisibleChanged)
|
||||||
|
Q_PROPERTY(QString incomingCallText MEMBER mIncomingCallText NOTIFY incomingCallTextChanged)
|
||||||
|
Q_PROPERTY(bool inCallButtonsEnabled MEMBER mInCallButtonsEnabled NOTIFY inCallButtonsEnabledChanged)
|
||||||
|
Q_PROPERTY(QString soundButtonText MEMBER mSoundButtonText NOTIFY soundButtonTextChanged)
|
||||||
|
Q_PROPERTY(QString microphoneButtonText MEMBER mMicrophoneButtonText NOTIFY microphoneButtonTextChanged)
|
||||||
|
|
||||||
|
public:
|
||||||
|
static void init(QObject *parent);
|
||||||
|
static void uninit();
|
||||||
|
static CoreManager *getInstance();
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void pageChanged(QString);
|
||||||
|
void headerTextChanged(QString);
|
||||||
|
void registerTextChanged(QString);
|
||||||
|
void loginButtonEnabledChanged(bool);
|
||||||
|
void callStateTextChanged(QString);
|
||||||
|
void incomingCallVisibleChanged(bool);
|
||||||
|
void incomingCallTextChanged(QString);
|
||||||
|
void inCallButtonsEnabledChanged(bool);
|
||||||
|
void soundButtonTextChanged(QString);
|
||||||
|
void microphoneButtonTextChanged(QString);
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void startIterate();
|
||||||
|
void stopIterate();
|
||||||
|
void login(QString identity, QString password);
|
||||||
|
void logout();
|
||||||
|
void onCallStateChanged(const std::shared_ptr<linphone::Call> &call, linphone::Call::State state, const std::string &message);
|
||||||
|
void onRegistrationStateChanged(const std::shared_ptr<linphone::Account> &account, linphone::RegistrationState state, const std::string &message);
|
||||||
|
void hangup();
|
||||||
|
void soundButtonClicked();
|
||||||
|
void microphoneButtonClicked();
|
||||||
|
void answer();
|
||||||
|
void decline();
|
||||||
|
|
||||||
|
private:
|
||||||
|
CoreManager(QObject *parent);
|
||||||
|
~CoreManager();
|
||||||
|
|
||||||
|
void createLinphoneCore();
|
||||||
|
void iterate();
|
||||||
|
bool toggleSpeaker();
|
||||||
|
bool toggleMicrophone();
|
||||||
|
void callInProgressGuiUpdates();
|
||||||
|
void endingCallGuiUpdates();
|
||||||
|
|
||||||
|
std::shared_ptr<linphone::Core> mCore = nullptr;
|
||||||
|
std::shared_ptr<linphone::Call> mIncomingCall = nullptr;
|
||||||
|
QSharedPointer<CoreHandler> mHandler;
|
||||||
|
QTimer *mIterateTimer = nullptr;
|
||||||
|
|
||||||
|
QString mPage;
|
||||||
|
QString mHeaderText;
|
||||||
|
QString mRegisterText;
|
||||||
|
bool mLoginButtonEnabled = true;
|
||||||
|
QString mCallStateText;
|
||||||
|
bool mIncomingCallVisible = false;
|
||||||
|
QString mIncomingCallText;
|
||||||
|
bool mInCallButtonsEnabled = false;
|
||||||
|
QString mSoundButtonText;
|
||||||
|
QString mMicrophoneButtonText;
|
||||||
|
|
||||||
|
static CoreManager *mInstance;
|
||||||
|
};
|
29
qt/02_IncomingCall/src/main.cpp
Normal file
29
qt/02_IncomingCall/src/main.cpp
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2010-2024 Belledonne Communications SARL.
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <QCoreApplication>
|
||||||
|
|
||||||
|
#include "App.hpp"
|
||||||
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
|
||||||
|
App app(argc, argv);
|
||||||
|
app.init();
|
||||||
|
app.exec();
|
||||||
|
app.stop();
|
||||||
|
}
|
77
qt/02_IncomingCall/ui/CallPage.qml
Normal file
77
qt/02_IncomingCall/ui/CallPage.qml
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
import QtQuick 2.9
|
||||||
|
import QtQuick.Controls 2.2
|
||||||
|
import QtQuick.Layouts 1.3
|
||||||
|
|
||||||
|
GridLayout {
|
||||||
|
Layout.alignment: Qt.AlignHCenter
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.fillHeight: true
|
||||||
|
columns: 1
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
Layout.fillHeight: true
|
||||||
|
Layout.alignment: Qt.AlignHCenter
|
||||||
|
|
||||||
|
Text {
|
||||||
|
text: coreManager.callStateText
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.alignment: Qt.AlignHCenter
|
||||||
|
spacing: 20
|
||||||
|
|
||||||
|
Button {
|
||||||
|
text: "Hang up"
|
||||||
|
enabled: coreManager.inCallButtonsEnabled
|
||||||
|
onClicked: {
|
||||||
|
coreManager.hangup()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Button {
|
||||||
|
text: coreManager.soundButtonText
|
||||||
|
enabled: coreManager.inCallButtonsEnabled
|
||||||
|
onClicked: {
|
||||||
|
coreManager.soundButtonClicked()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Button {
|
||||||
|
text: coreManager.microphoneButtonText
|
||||||
|
enabled: coreManager.inCallButtonsEnabled
|
||||||
|
onClicked: {
|
||||||
|
coreManager.microphoneButtonClicked()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
GridLayout {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.alignment: Qt.AlignHCenter
|
||||||
|
Layout.margins: 20
|
||||||
|
columnSpacing: 20
|
||||||
|
columns: 2
|
||||||
|
visible: coreManager.incomingCallVisible
|
||||||
|
|
||||||
|
Text {
|
||||||
|
Layout.alignment: Qt.AlignHCenter
|
||||||
|
Layout.columnSpan: 2
|
||||||
|
text: "You have a call from: " + coreManager.incomingCallText
|
||||||
|
}
|
||||||
|
|
||||||
|
Button {
|
||||||
|
text: "Answer"
|
||||||
|
onClicked: {
|
||||||
|
coreManager.answer()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Button {
|
||||||
|
text: "Decline"
|
||||||
|
onClicked: {
|
||||||
|
coreManager.decline()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
35
qt/02_IncomingCall/ui/MainPage.qml
Normal file
35
qt/02_IncomingCall/ui/MainPage.qml
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
import QtQuick 2.9
|
||||||
|
import QtQuick.Controls 2.2
|
||||||
|
import QtQuick.Layouts 1.3
|
||||||
|
|
||||||
|
ApplicationWindow {
|
||||||
|
id: window
|
||||||
|
visible: true
|
||||||
|
|
||||||
|
title: "Account Login"
|
||||||
|
width: 640
|
||||||
|
height: 480
|
||||||
|
|
||||||
|
header: Rectangle {
|
||||||
|
color: "lightgray";
|
||||||
|
height: 40;
|
||||||
|
width: window.width
|
||||||
|
|
||||||
|
Text {
|
||||||
|
text: coreManager.headerText
|
||||||
|
font.bold: true
|
||||||
|
font.capitalization: Font.AllUppercase
|
||||||
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
width: parent.width
|
||||||
|
height: parent.height
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Main content
|
||||||
|
Loader {
|
||||||
|
id: contentLoader
|
||||||
|
anchors.fill: parent
|
||||||
|
source: coreManager.page
|
||||||
|
}
|
||||||
|
}
|
57
qt/02_IncomingCall/ui/RegistrationPage.qml
Normal file
57
qt/02_IncomingCall/ui/RegistrationPage.qml
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
import QtQuick 2.9
|
||||||
|
import QtQuick.Controls 2.2
|
||||||
|
import QtQuick.Layouts 1.3
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
//Layout.alignment: Qt.AlignHCenter | Qt.AlignTop
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.fillHeight: true
|
||||||
|
|
||||||
|
GridLayout {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.margins: 20
|
||||||
|
columnSpacing: 20
|
||||||
|
columns: 2
|
||||||
|
|
||||||
|
Text {
|
||||||
|
text: "Identity:"
|
||||||
|
}
|
||||||
|
TextField {
|
||||||
|
id: identityTextField
|
||||||
|
Layout.fillWidth: true
|
||||||
|
text: "sip:"
|
||||||
|
}
|
||||||
|
|
||||||
|
Text {
|
||||||
|
text: "Password:"
|
||||||
|
}
|
||||||
|
TextField {
|
||||||
|
id: passwordTextField
|
||||||
|
echoMode: TextInput.Password
|
||||||
|
Layout.fillWidth: true
|
||||||
|
placeholderText: "my password"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.alignment: Qt.AlignHCenter
|
||||||
|
|
||||||
|
Button {
|
||||||
|
text: "Login"
|
||||||
|
enabled: coreManager.loginButtonEnabled && identityTextField.text.length !== 0 && passwordTextField.text.length !== 0
|
||||||
|
onClicked: {
|
||||||
|
coreManager.login(identityTextField.text, passwordTextField.text)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
Layout.fillHeight: true
|
||||||
|
Layout.alignment: Qt.AlignHCenter
|
||||||
|
|
||||||
|
Text {
|
||||||
|
text: coreManager.registerText
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user