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