Conference, ActiveSpeaker, Camera
This commit is contained in:
parent
2770076a44
commit
10427b5288
33 changed files with 1385 additions and 697 deletions
|
|
@ -50,6 +50,7 @@
|
||||||
#include "core/login/LoginPage.hpp"
|
#include "core/login/LoginPage.hpp"
|
||||||
#include "core/notifier/Notifier.hpp"
|
#include "core/notifier/Notifier.hpp"
|
||||||
#include "core/participant/ParticipantDeviceCore.hpp"
|
#include "core/participant/ParticipantDeviceCore.hpp"
|
||||||
|
#include "core/participant/ParticipantDeviceProxy.hpp"
|
||||||
#include "core/participant/ParticipantGui.hpp"
|
#include "core/participant/ParticipantGui.hpp"
|
||||||
#include "core/participant/ParticipantProxy.hpp"
|
#include "core/participant/ParticipantProxy.hpp"
|
||||||
#include "core/phone-number/PhoneNumber.hpp"
|
#include "core/phone-number/PhoneNumber.hpp"
|
||||||
|
|
@ -217,6 +218,10 @@ void App::initCppInterfaces() {
|
||||||
qmlRegisterType<FPSCounter>(Constants::MainQmlUri, 1, 0, "FPSCounter");
|
qmlRegisterType<FPSCounter>(Constants::MainQmlUri, 1, 0, "FPSCounter");
|
||||||
|
|
||||||
qmlRegisterType<TimeZoneProxy>(Constants::MainQmlUri, 1, 0, "TimeZoneProxy");
|
qmlRegisterType<TimeZoneProxy>(Constants::MainQmlUri, 1, 0, "TimeZoneProxy");
|
||||||
|
|
||||||
|
qmlRegisterType<ParticipantDeviceGui>(Constants::MainQmlUri, 1, 0, "ParticipantDeviceGui");
|
||||||
|
qmlRegisterType<ParticipantDeviceProxy>(Constants::MainQmlUri, 1, 0, "ParticipantDeviceProxy");
|
||||||
|
|
||||||
LinphoneEnums::registerMetaTypes();
|
LinphoneEnums::registerMetaTypes();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -50,8 +50,9 @@ list(APPEND _LINPHONEAPP_SOURCES
|
||||||
core/participant/ParticipantCore.cpp
|
core/participant/ParticipantCore.cpp
|
||||||
core/participant/ParticipantGui.cpp
|
core/participant/ParticipantGui.cpp
|
||||||
core/participant/ParticipantDeviceCore.cpp
|
core/participant/ParticipantDeviceCore.cpp
|
||||||
# core/participant/ParticipantDeviceList.cpp
|
core/participant/ParticipantDeviceGui.cpp
|
||||||
# core/participant/ParticipantDeviceProxy.cpp
|
core/participant/ParticipantDeviceList.cpp
|
||||||
|
core/participant/ParticipantDeviceProxy.cpp
|
||||||
core/participant/ParticipantList.cpp
|
core/participant/ParticipantList.cpp
|
||||||
core/participant/ParticipantProxy.cpp
|
core/participant/ParticipantProxy.cpp
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -380,10 +380,15 @@ ConferenceGui *CallCore::getConferenceGui() const {
|
||||||
return mConference ? new ConferenceGui(mConference) : nullptr;
|
return mConference ? new ConferenceGui(mConference) : nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QSharedPointer<ConferenceCore> CallCore::getConferenceCore() const {
|
||||||
|
return mConference;
|
||||||
|
}
|
||||||
|
|
||||||
void CallCore::setConference(const QSharedPointer<ConferenceCore> &conference) {
|
void CallCore::setConference(const QSharedPointer<ConferenceCore> &conference) {
|
||||||
if (mConference != conference) {
|
if (mConference != conference) {
|
||||||
mConference = conference;
|
mConference = conference;
|
||||||
mIsConference = (mConference != nullptr);
|
mIsConference = (mConference != nullptr);
|
||||||
|
qDebug() << "[CallCore] Set conference : " << mConference;
|
||||||
emit conferenceChanged();
|
emit conferenceChanged();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -103,6 +103,7 @@ public:
|
||||||
|
|
||||||
bool isConference() const;
|
bool isConference() const;
|
||||||
ConferenceGui *getConferenceGui() const;
|
ConferenceGui *getConferenceGui() const;
|
||||||
|
QSharedPointer<ConferenceCore> getConferenceCore() const;
|
||||||
void setConference(const QSharedPointer<ConferenceCore> &conference);
|
void setConference(const QSharedPointer<ConferenceCore> &conference);
|
||||||
|
|
||||||
QString getLocalSas();
|
QString getLocalSas();
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,6 @@
|
||||||
DEFINE_ABSTRACT_OBJECT(CallGui)
|
DEFINE_ABSTRACT_OBJECT(CallGui)
|
||||||
|
|
||||||
CallGui::CallGui(QSharedPointer<CallCore> core) {
|
CallGui::CallGui(QSharedPointer<CallCore> core) {
|
||||||
qDebug() << "[CallGui] new" << this;
|
|
||||||
App::getInstance()->mEngine->setObjectOwnership(this, QQmlEngine::JavaScriptOwnership);
|
App::getInstance()->mEngine->setObjectOwnership(this, QQmlEngine::JavaScriptOwnership);
|
||||||
mCore = core;
|
mCore = core;
|
||||||
if (isInLinphoneThread()) moveToThread(App::getInstance()->thread());
|
if (isInLinphoneThread()) moveToThread(App::getInstance()->thread());
|
||||||
|
|
@ -32,7 +31,6 @@ CallGui::CallGui(QSharedPointer<CallCore> core) {
|
||||||
|
|
||||||
CallGui::~CallGui() {
|
CallGui::~CallGui() {
|
||||||
mustBeInMainThread("~" + getClassName());
|
mustBeInMainThread("~" + getClassName());
|
||||||
qDebug() << "[CallGui] delete" << this;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
CallCore *CallGui::getCore() const {
|
CallCore *CallGui::getCore() const {
|
||||||
|
|
|
||||||
|
|
@ -28,9 +28,14 @@
|
||||||
#include "core/App.hpp"
|
#include "core/App.hpp"
|
||||||
#include "core/call/CallCore.hpp"
|
#include "core/call/CallCore.hpp"
|
||||||
#include "core/call/CallGui.hpp"
|
#include "core/call/CallGui.hpp"
|
||||||
|
#include "core/participant/ParticipantDeviceCore.hpp"
|
||||||
|
#include "core/participant/ParticipantDeviceGui.hpp"
|
||||||
|
|
||||||
DEFINE_ABSTRACT_OBJECT(CameraGui)
|
DEFINE_ABSTRACT_OBJECT(CameraGui)
|
||||||
|
|
||||||
|
QMutex CameraGui::mPreviewCounterMutex;
|
||||||
|
int CameraGui::mPreviewCounter = 0;
|
||||||
|
|
||||||
// =============================================================================
|
// =============================================================================
|
||||||
CameraGui::CameraGui(QQuickItem *parent) : QQuickFramebufferObject(parent) {
|
CameraGui::CameraGui(QQuickItem *parent) : QQuickFramebufferObject(parent) {
|
||||||
mustBeInMainThread(getClassName());
|
mustBeInMainThread(getClassName());
|
||||||
|
|
@ -45,38 +50,14 @@ CameraGui::CameraGui(QQuickItem *parent) : QQuickFramebufferObject(parent) {
|
||||||
// TODO : Deactivate only if there are no previews to display (Could be open in settings and calls)
|
// TODO : Deactivate only if there are no previews to display (Could be open in settings and calls)
|
||||||
CameraGui::~CameraGui() {
|
CameraGui::~CameraGui() {
|
||||||
mustBeInMainThread("~" + getClassName());
|
mustBeInMainThread("~" + getClassName());
|
||||||
|
mRefreshTimer.stop();
|
||||||
App::postModelSync([this]() { CoreModel::getInstance()->getCore()->enableVideoPreview(false); });
|
App::postModelSync([this]() { CoreModel::getInstance()->getCore()->enableVideoPreview(false); });
|
||||||
}
|
}
|
||||||
|
|
||||||
QQuickFramebufferObject::Renderer *CameraGui::createRenderer() const {
|
QQuickFramebufferObject::Renderer *CameraGui::createRenderer() const {
|
||||||
QQuickFramebufferObject::Renderer *renderer = NULL;
|
auto renderer = createRenderer(false);
|
||||||
// A renderer is mandatory, we cannot wait async.
|
|
||||||
switch (getSourceLocation()) {
|
|
||||||
case CorePreview:
|
|
||||||
App::postModelSync([this, &renderer]() {
|
|
||||||
auto coreModel = CoreModel::getInstance();
|
|
||||||
if (coreModel) {
|
|
||||||
auto core = coreModel->getCore();
|
|
||||||
if (!core) return;
|
|
||||||
core->enableVideoPreview(true);
|
|
||||||
renderer = (QQuickFramebufferObject::Renderer *)core->createNativePreviewWindowId();
|
|
||||||
if (renderer) core->setNativePreviewWindowId(renderer);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
case Call:
|
|
||||||
App::postModelSync([this, &renderer]() {
|
|
||||||
auto call = mCallGui->getCore()->getModel()->getMonitor();
|
|
||||||
if (call) {
|
|
||||||
// qInfo() << "[Camera] (" << mQmlName << ") Setting Camera to CallModel";
|
|
||||||
renderer = (QQuickFramebufferObject::Renderer *)call->createNativeVideoWindowId();
|
|
||||||
if (renderer) call->setNativeVideoWindowId(renderer);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
default: {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!renderer) {
|
if (!renderer) {
|
||||||
|
qInfo() << "[Camera] (" << mQmlName << ") Setting Camera to Dummy, " << getSourceLocation();
|
||||||
QTimer::singleShot(1, this, &CameraGui::isNotReady);
|
QTimer::singleShot(1, this, &CameraGui::isNotReady);
|
||||||
renderer = new CameraDummy(); // Used to fill a renderer to avoid pushing a NULL.
|
renderer = new CameraDummy(); // Used to fill a renderer to avoid pushing a NULL.
|
||||||
QTimer::singleShot(1000, this, &CameraGui::requestNewRenderer);
|
QTimer::singleShot(1000, this, &CameraGui::requestNewRenderer);
|
||||||
|
|
@ -84,6 +65,88 @@ QQuickFramebufferObject::Renderer *CameraGui::createRenderer() const {
|
||||||
return renderer;
|
return renderer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QQuickFramebufferObject::Renderer *CameraGui::createRenderer(bool resetWindowId) const {
|
||||||
|
QQuickFramebufferObject::Renderer *renderer = NULL;
|
||||||
|
// A renderer is mandatory, we cannot wait async.
|
||||||
|
switch (getSourceLocation()) {
|
||||||
|
case CorePreview:
|
||||||
|
App::postModelSync([this, &renderer, resetWindowId]() {
|
||||||
|
qInfo() << "[Camera] (" << mQmlName << ") Setting Camera to Preview";
|
||||||
|
auto coreModel = CoreModel::getInstance();
|
||||||
|
if (coreModel) {
|
||||||
|
auto core = coreModel->getCore();
|
||||||
|
if (!core) return;
|
||||||
|
core->enableVideoPreview(true);
|
||||||
|
if (resetWindowId) {
|
||||||
|
renderer = (QQuickFramebufferObject::Renderer *)core->getNativePreviewWindowId();
|
||||||
|
if (renderer) core->setNativePreviewWindowId(NULL);
|
||||||
|
} else {
|
||||||
|
renderer = (QQuickFramebufferObject::Renderer *)core->createNativePreviewWindowId();
|
||||||
|
if (renderer) core->setNativePreviewWindowId(renderer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
case Call:
|
||||||
|
App::postModelSync([this, &renderer, resetWindowId]() {
|
||||||
|
auto call = mCallGui->getCore()->getModel()->getMonitor();
|
||||||
|
if (call) {
|
||||||
|
qInfo() << "[Camera] (" << mQmlName << ") Setting Camera to CallModel";
|
||||||
|
if (resetWindowId) {
|
||||||
|
renderer = (QQuickFramebufferObject::Renderer *)call->getNativeVideoWindowId();
|
||||||
|
if (renderer) call->setNativeVideoWindowId(NULL);
|
||||||
|
} else {
|
||||||
|
renderer = (QQuickFramebufferObject::Renderer *)call->createNativeVideoWindowId();
|
||||||
|
if (renderer) call->setNativeVideoWindowId(renderer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
case Device:
|
||||||
|
App::postModelSync([this, &renderer, resetWindowId]() {
|
||||||
|
auto device = mParticipantDeviceGui->getCore()->getModel()->getMonitor();
|
||||||
|
if (device) {
|
||||||
|
qInfo() << "[Camera] (" << mQmlName << ") Setting Camera to ParticipantDeviceModel";
|
||||||
|
if (resetWindowId) {
|
||||||
|
} else {
|
||||||
|
renderer = (QQuickFramebufferObject::Renderer *)device->createNativeVideoWindowId();
|
||||||
|
if (renderer) device->setNativeVideoWindowId(renderer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
default: {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return renderer;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CameraGui::resetWindowId() const {
|
||||||
|
createRenderer(true);
|
||||||
|
}
|
||||||
|
void CameraGui::checkVideoDefinition() { /*
|
||||||
|
if (mWindowIdLocation == WindowIdLocation::CorePreview) {
|
||||||
|
auto videoDefinition = CoreManager::getInstance()->getSettingsModel()->getCurrentPreviewVideoDefinition();
|
||||||
|
if (videoDefinition["width"] != mLastVideoDefinition["width"] ||
|
||||||
|
videoDefinition["height"] != mLastVideoDefinition["height"]) {
|
||||||
|
mLastVideoDefinition = videoDefinition;
|
||||||
|
emit videoDefinitionChanged();
|
||||||
|
}
|
||||||
|
}*/
|
||||||
|
}
|
||||||
|
|
||||||
|
QString CameraGui::getQmlName() const {
|
||||||
|
return mQmlName;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CameraGui::setQmlName(const QString &name) {
|
||||||
|
if (name != mQmlName) {
|
||||||
|
mQmlName = name;
|
||||||
|
emit qmlNameChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool CameraGui::getIsReady() const {
|
bool CameraGui::getIsReady() const {
|
||||||
return mIsReady;
|
return mIsReady;
|
||||||
}
|
}
|
||||||
|
|
@ -100,6 +163,21 @@ void CameraGui::isNotReady() {
|
||||||
setIsReady(false);
|
setIsReady(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool CameraGui::getIsPreview() const {
|
||||||
|
return mIsPreview;
|
||||||
|
}
|
||||||
|
void CameraGui::setIsPreview(bool status) {
|
||||||
|
if (mIsPreview != status) {
|
||||||
|
mIsPreview = status;
|
||||||
|
if (mIsPreview) activatePreview();
|
||||||
|
else deactivatePreview();
|
||||||
|
// updateWindowIdLocation();
|
||||||
|
update();
|
||||||
|
|
||||||
|
emit isPreviewChanged(status);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
CallGui *CameraGui::getCallGui() const {
|
CallGui *CameraGui::getCallGui() const {
|
||||||
return mCallGui;
|
return mCallGui;
|
||||||
}
|
}
|
||||||
|
|
@ -107,11 +185,70 @@ CallGui *CameraGui::getCallGui() const {
|
||||||
void CameraGui::setCallGui(CallGui *callGui) {
|
void CameraGui::setCallGui(CallGui *callGui) {
|
||||||
if (mCallGui != callGui) {
|
if (mCallGui != callGui) {
|
||||||
mCallGui = callGui;
|
mCallGui = callGui;
|
||||||
|
qDebug() << "Set Call " << mCallGui;
|
||||||
emit callGuiChanged(mCallGui);
|
emit callGuiChanged(mCallGui);
|
||||||
|
updateWindowIdLocation();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ParticipantDeviceGui *CameraGui::getParticipantDeviceGui() const {
|
||||||
|
return mParticipantDeviceGui;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CameraGui::setParticipantDeviceGui(ParticipantDeviceGui *deviceGui) {
|
||||||
|
if (mParticipantDeviceGui != deviceGui) {
|
||||||
|
mParticipantDeviceGui = deviceGui;
|
||||||
|
qDebug() << "Set Device " << mParticipantDeviceGui;
|
||||||
|
// setIsPreview(mParticipantDeviceGui->getCore()->isLocal());
|
||||||
|
emit participantDeviceGuiChanged(mParticipantDeviceGui);
|
||||||
|
updateWindowIdLocation();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
CameraGui::WindowIdLocation CameraGui::getSourceLocation() const {
|
CameraGui::WindowIdLocation CameraGui::getSourceLocation() const {
|
||||||
if (mCallGui != nullptr) return Call;
|
return mWindowIdLocation;
|
||||||
else return CorePreview;
|
}
|
||||||
|
|
||||||
|
void CameraGui::activatePreview() {
|
||||||
|
mPreviewCounterMutex.lock();
|
||||||
|
setWindowIdLocation(WindowIdLocation::CorePreview);
|
||||||
|
if (++mPreviewCounter == 1) {
|
||||||
|
App::postModelSync([this]() {
|
||||||
|
auto coreModel = CoreModel::getInstance();
|
||||||
|
coreModel->getCore()->enableVideoPreview(true);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
mPreviewCounterMutex.unlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CameraGui::deactivatePreview() {
|
||||||
|
mPreviewCounterMutex.lock();
|
||||||
|
setWindowIdLocation(WindowIdLocation::None);
|
||||||
|
if (--mPreviewCounter == 0) {
|
||||||
|
App::postModelSync([this]() {
|
||||||
|
auto coreModel = CoreModel::getInstance();
|
||||||
|
coreModel->getCore()->enableVideoPreview(false);
|
||||||
|
});
|
||||||
|
mPreviewCounterMutex.unlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void CameraGui::setWindowIdLocation(const WindowIdLocation &location) {
|
||||||
|
if (mWindowIdLocation != location) {
|
||||||
|
qDebug() << "Update Window Id location from " << mWindowIdLocation << " to " << location;
|
||||||
|
resetWindowId(); // Location change: Reset old window ID.
|
||||||
|
mWindowIdLocation = location;
|
||||||
|
update();
|
||||||
|
// if (mWindowIdLocation == WindowIdLocation::CorePreview) {
|
||||||
|
// mLastVideoDefinition =
|
||||||
|
// CoreManager::getInstance()->getSettingsModel()->getCurrentPreviewVideoDefinition(); emit
|
||||||
|
// videoDefinitionChanged(); mLastVideoDefinitionChecker.start();
|
||||||
|
// } else mLastVideoDefinitionChecker.stop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void CameraGui::updateWindowIdLocation() {
|
||||||
|
bool useDefaultWindow = true;
|
||||||
|
if (mCallGui) setWindowIdLocation(WindowIdLocation::Call);
|
||||||
|
else if (mParticipantDeviceGui && !mParticipantDeviceGui->getCore()->isLocal())
|
||||||
|
setWindowIdLocation(WindowIdLocation::Device);
|
||||||
|
else setWindowIdLocation(WindowIdLocation::CorePreview);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -28,41 +28,83 @@
|
||||||
#include <QQuickFramebufferObject>
|
#include <QQuickFramebufferObject>
|
||||||
#include <QTimer>
|
#include <QTimer>
|
||||||
|
|
||||||
|
#include "core/participant/ParticipantDeviceGui.hpp"
|
||||||
// =============================================================================
|
// =============================================================================
|
||||||
|
|
||||||
class CallGui;
|
class CallGui;
|
||||||
|
|
||||||
class CameraGui : public QQuickFramebufferObject, public AbstractObject {
|
class CameraGui : public QQuickFramebufferObject, public AbstractObject {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
Q_PROPERTY(bool isReady READ getIsReady NOTIFY isReadyChanged)
|
Q_PROPERTY(CallGui *call READ getCallGui WRITE setCallGui NOTIFY callGuiChanged)
|
||||||
Q_PROPERTY(CallGui *call READ getCallGui WRITE setCallGui NOTIFY callGuiChanged);
|
Q_PROPERTY(ParticipantDeviceGui *participantDevice READ getParticipantDeviceGui WRITE setParticipantDeviceGui NOTIFY
|
||||||
|
participantDeviceGuiChanged)
|
||||||
|
Q_PROPERTY(bool isPreview READ getIsPreview WRITE setIsPreview NOTIFY isPreviewChanged)
|
||||||
|
Q_PROPERTY(bool isReady READ getIsReady WRITE setIsReady NOTIFY isReadyChanged)
|
||||||
|
// Q_PROPERTY(SoundPlayer * linphonePlayer READ getLinphonePlayer WRITE setLinphonePlayer NOTIFY
|
||||||
|
// linphonePlayerChanged)
|
||||||
|
Q_PROPERTY(QString qmlName READ getQmlName WRITE setQmlName NOTIFY qmlNameChanged)
|
||||||
|
|
||||||
|
typedef enum { None = -1, CorePreview = 0, Call, Device, Player, Core } WindowIdLocation;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
CameraGui(QQuickItem *parent = Q_NULLPTR);
|
CameraGui(QQuickItem *parent = Q_NULLPTR);
|
||||||
virtual ~CameraGui();
|
virtual ~CameraGui();
|
||||||
QQuickFramebufferObject::Renderer *createRenderer() const override;
|
QQuickFramebufferObject::Renderer *createRenderer() const override;
|
||||||
|
QQuickFramebufferObject::Renderer *createRenderer(bool resetWindowid) const;
|
||||||
|
|
||||||
|
Q_INVOKABLE void resetWindowId() const; // const to be used from createRenderer()
|
||||||
|
void checkVideoDefinition();
|
||||||
|
|
||||||
|
static QMutex mPreviewCounterMutex;
|
||||||
|
static int mPreviewCounter;
|
||||||
|
|
||||||
bool getIsReady() const;
|
bool getIsReady() const;
|
||||||
void setIsReady(bool isReady);
|
void setIsReady(bool isReady);
|
||||||
void isReady();
|
void isReady();
|
||||||
void isNotReady();
|
void isNotReady();
|
||||||
|
bool getIsPreview() const;
|
||||||
|
void setIsPreview(bool status);
|
||||||
|
|
||||||
CallGui *getCallGui() const;
|
CallGui *getCallGui() const;
|
||||||
void setCallGui(CallGui *callGui);
|
void setCallGui(CallGui *callGui);
|
||||||
|
ParticipantDeviceGui *getParticipantDeviceGui() const;
|
||||||
typedef enum { None = -1, CorePreview = 0, Call, Device, Player, Core } WindowIdLocation;
|
void setParticipantDeviceGui(ParticipantDeviceGui *participantDeviceGui);
|
||||||
|
QString getQmlName() const;
|
||||||
|
void setQmlName(const QString &name);
|
||||||
WindowIdLocation getSourceLocation() const;
|
WindowIdLocation getSourceLocation() const;
|
||||||
|
void setWindowIdLocation(const WindowIdLocation &location);
|
||||||
|
|
||||||
|
void activatePreview();
|
||||||
|
void deactivatePreview();
|
||||||
|
void updateWindowIdLocation();
|
||||||
|
void removeParticipantDeviceModel();
|
||||||
|
void removeCallModel();
|
||||||
|
void removeLinphonePlayer();
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void requestNewRenderer();
|
void requestNewRenderer();
|
||||||
void isReadyChanged(bool isReady);
|
void isReadyChanged(bool isReady);
|
||||||
void callGuiChanged(CallGui *callGui);
|
void callGuiChanged(CallGui *callGui);
|
||||||
|
void isPreviewChanged(bool isPreview);
|
||||||
|
void isReadyChanged();
|
||||||
|
void participantDeviceGuiChanged(ParticipantDeviceGui *participantDeviceGui);
|
||||||
|
void videoDefinitionChanged();
|
||||||
|
// void linphonePlayerChanged(SoundPlayer * linphonePlayer);
|
||||||
|
void qmlNameChanged();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
bool mIsPreview = false;
|
||||||
bool mIsReady = false;
|
bool mIsReady = false;
|
||||||
QTimer mRefreshTimer;
|
QTimer mRefreshTimer;
|
||||||
int mMaxFps = 30;
|
int mMaxFps = 30;
|
||||||
|
QVariantMap mLastVideoDefinition;
|
||||||
|
QTimer mLastVideoDefinitionChecker;
|
||||||
CallGui *mCallGui = nullptr;
|
CallGui *mCallGui = nullptr;
|
||||||
|
ParticipantDeviceGui *mParticipantDeviceGui = nullptr;
|
||||||
|
QString mQmlName;
|
||||||
|
|
||||||
|
WindowIdLocation mWindowIdLocation = None;
|
||||||
|
mutable bool mIsWindowIdSet = false;
|
||||||
|
|
||||||
DECLARE_ABSTRACT_OBJECT
|
DECLARE_ABSTRACT_OBJECT
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@
|
||||||
|
|
||||||
#include "ConferenceCore.hpp"
|
#include "ConferenceCore.hpp"
|
||||||
#include "core/App.hpp"
|
#include "core/App.hpp"
|
||||||
|
#include "model/conference/ConferenceModel.hpp"
|
||||||
#include "tool/Utils.hpp"
|
#include "tool/Utils.hpp"
|
||||||
#include "tool/thread/SafeConnection.hpp"
|
#include "tool/thread/SafeConnection.hpp"
|
||||||
|
|
||||||
|
|
@ -35,6 +36,7 @@ ConferenceCore::ConferenceCore(const std::shared_ptr<linphone::Conference> &conf
|
||||||
App::getInstance()->mEngine->setObjectOwnership(this, QQmlEngine::CppOwnership);
|
App::getInstance()->mEngine->setObjectOwnership(this, QQmlEngine::CppOwnership);
|
||||||
// Should be call from model Thread
|
// Should be call from model Thread
|
||||||
mustBeInLinphoneThread(getClassName());
|
mustBeInLinphoneThread(getClassName());
|
||||||
|
mConferenceModel = ConferenceModel::create(conference);
|
||||||
mSubject = Utils::coreStringToAppString(conference->getSubject());
|
mSubject = Utils::coreStringToAppString(conference->getSubject());
|
||||||
}
|
}
|
||||||
ConferenceCore::~ConferenceCore() {
|
ConferenceCore::~ConferenceCore() {
|
||||||
|
|
@ -45,6 +47,12 @@ ConferenceCore::~ConferenceCore() {
|
||||||
void ConferenceCore::setSelf(QSharedPointer<ConferenceCore> me) {
|
void ConferenceCore::setSelf(QSharedPointer<ConferenceCore> me) {
|
||||||
mConferenceModelConnection = QSharedPointer<SafeConnection<ConferenceCore, ConferenceModel>>(
|
mConferenceModelConnection = QSharedPointer<SafeConnection<ConferenceCore, ConferenceModel>>(
|
||||||
new SafeConnection<ConferenceCore, ConferenceModel>(me, mConferenceModel), &QObject::deleteLater);
|
new SafeConnection<ConferenceCore, ConferenceModel>(me, mConferenceModel), &QObject::deleteLater);
|
||||||
|
mConferenceModelConnection->makeConnectToModel(
|
||||||
|
&ConferenceModel::activeSpeakerParticipantDevice,
|
||||||
|
[this](const std::shared_ptr<linphone::ParticipantDevice> &participantDevice) {
|
||||||
|
auto device = ParticipantDeviceCore::create(participantDevice);
|
||||||
|
mConferenceModelConnection->invokeToCore([this, device]() { setActiveSpeaker(device); });
|
||||||
|
});
|
||||||
// mCallModelConnection->makeConnectToCore(&CallCore::lSetMicrophoneMuted, [this](bool isMuted) {
|
// mCallModelConnection->makeConnectToCore(&CallCore::lSetMicrophoneMuted, [this](bool isMuted) {
|
||||||
// mCallModelConnection->invokeToModel([this, isMuted]() { mCallModel->setMicrophoneMuted(isMuted); });
|
// mCallModelConnection->invokeToModel([this, isMuted]() { mCallModel->setMicrophoneMuted(isMuted); });
|
||||||
// });
|
// });
|
||||||
|
|
@ -79,3 +87,23 @@ void ConferenceCore::setIsReady(bool state) {
|
||||||
isReadyChanged();
|
isReadyChanged();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<ConferenceModel> ConferenceCore::getModel() const {
|
||||||
|
return mConferenceModel;
|
||||||
|
}
|
||||||
|
|
||||||
|
ParticipantDeviceCore *ConferenceCore::getActiveSpeaker() const {
|
||||||
|
return mActiveSpeaker.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
ParticipantDeviceGui *ConferenceCore::getActiveSpeakerGui() const {
|
||||||
|
return new ParticipantDeviceGui(mActiveSpeaker);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConferenceCore::setActiveSpeaker(const QSharedPointer<ParticipantDeviceCore> &device) {
|
||||||
|
if (mActiveSpeaker != device) {
|
||||||
|
mActiveSpeaker = device;
|
||||||
|
qDebug() << "Changing active speaker to " << device->getAddress();
|
||||||
|
emit activeSpeakerChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,8 @@
|
||||||
#ifndef CONFERENCE_CORE_H_
|
#ifndef CONFERENCE_CORE_H_
|
||||||
#define CONFERENCE_CORE_H_
|
#define CONFERENCE_CORE_H_
|
||||||
|
|
||||||
|
#include "core/participant/ParticipantDeviceCore.hpp"
|
||||||
|
#include "core/participant/ParticipantDeviceGui.hpp"
|
||||||
#include "model/conference/ConferenceModel.hpp"
|
#include "model/conference/ConferenceModel.hpp"
|
||||||
#include "tool/LinphoneEnums.hpp"
|
#include "tool/LinphoneEnums.hpp"
|
||||||
#include "tool/thread/SafeConnection.hpp"
|
#include "tool/thread/SafeConnection.hpp"
|
||||||
|
|
@ -38,6 +40,7 @@ public:
|
||||||
// Q_PROPERTY(ParticipantModel* localParticipant READ getLocalParticipant NOTIFY localParticipantChanged)
|
// Q_PROPERTY(ParticipantModel* localParticipant READ getLocalParticipant NOTIFY localParticipantChanged)
|
||||||
Q_PROPERTY(bool isReady MEMBER mIsReady WRITE setIsReady NOTIFY isReadyChanged)
|
Q_PROPERTY(bool isReady MEMBER mIsReady WRITE setIsReady NOTIFY isReadyChanged)
|
||||||
Q_PROPERTY(int participantDeviceCount READ getParticipantDeviceCount NOTIFY participantDeviceCountChanged)
|
Q_PROPERTY(int participantDeviceCount READ getParticipantDeviceCount NOTIFY participantDeviceCountChanged)
|
||||||
|
Q_PROPERTY(ParticipantDeviceGui *activeSpeaker READ getActiveSpeakerGui NOTIFY activeSpeakerChanged)
|
||||||
|
|
||||||
// Should be call from model Thread. Will be automatically in App thread after initialization
|
// Should be call from model Thread. Will be automatically in App thread after initialization
|
||||||
static QSharedPointer<ConferenceCore> create(const std::shared_ptr<linphone::Conference> &conference);
|
static QSharedPointer<ConferenceCore> create(const std::shared_ptr<linphone::Conference> &conference);
|
||||||
|
|
@ -55,19 +58,26 @@ public:
|
||||||
// std::list<std::shared_ptr<linphone::Participant>>
|
// std::list<std::shared_ptr<linphone::Participant>>
|
||||||
// getParticipantList() const; // SDK exclude me. We want to get ALL participants.
|
// getParticipantList() const; // SDK exclude me. We want to get ALL participants.
|
||||||
int getParticipantDeviceCount() const;
|
int getParticipantDeviceCount() const;
|
||||||
|
ParticipantDeviceCore *getActiveSpeaker() const;
|
||||||
|
ParticipantDeviceGui *getActiveSpeakerGui() const;
|
||||||
|
void setActiveSpeaker(const QSharedPointer<ParticipantDeviceCore> &device);
|
||||||
|
|
||||||
void setIsReady(bool state);
|
void setIsReady(bool state);
|
||||||
|
|
||||||
|
std::shared_ptr<ConferenceModel> getModel() const;
|
||||||
|
|
||||||
//---------------------------------------------------------------------------
|
//---------------------------------------------------------------------------
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void subjectChanged();
|
void subjectChanged();
|
||||||
void isReadyChanged();
|
void isReadyChanged();
|
||||||
void participantDeviceCountChanged();
|
void participantDeviceCountChanged();
|
||||||
|
void activeSpeakerChanged();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QSharedPointer<SafeConnection<ConferenceCore, ConferenceModel>> mConferenceModelConnection;
|
QSharedPointer<SafeConnection<ConferenceCore, ConferenceModel>> mConferenceModelConnection;
|
||||||
std::shared_ptr<ConferenceModel> mConferenceModel;
|
std::shared_ptr<ConferenceModel> mConferenceModel;
|
||||||
|
QSharedPointer<ParticipantDeviceCore> mActiveSpeaker;
|
||||||
|
|
||||||
bool mIsReady = false;
|
bool mIsReady = false;
|
||||||
QString mSubject;
|
QString mSubject;
|
||||||
|
|
|
||||||
|
|
@ -25,13 +25,11 @@
|
||||||
DEFINE_ABSTRACT_OBJECT(ConferenceGui)
|
DEFINE_ABSTRACT_OBJECT(ConferenceGui)
|
||||||
|
|
||||||
ConferenceGui::ConferenceGui() {
|
ConferenceGui::ConferenceGui() {
|
||||||
qDebug() << "[ConferenceGui] new" << this;
|
|
||||||
mCore = ConferenceCore::create(nullptr);
|
mCore = ConferenceCore::create(nullptr);
|
||||||
App::getInstance()->mEngine->setObjectOwnership(this, QQmlEngine::JavaScriptOwnership);
|
App::getInstance()->mEngine->setObjectOwnership(this, QQmlEngine::JavaScriptOwnership);
|
||||||
if (isInLinphoneThread()) moveToThread(App::getInstance()->thread());
|
if (isInLinphoneThread()) moveToThread(App::getInstance()->thread());
|
||||||
}
|
}
|
||||||
ConferenceGui::ConferenceGui(QSharedPointer<ConferenceCore> core) {
|
ConferenceGui::ConferenceGui(QSharedPointer<ConferenceCore> core) {
|
||||||
qDebug() << "[ConferenceGui] new" << this;
|
|
||||||
App::getInstance()->mEngine->setObjectOwnership(this, QQmlEngine::JavaScriptOwnership);
|
App::getInstance()->mEngine->setObjectOwnership(this, QQmlEngine::JavaScriptOwnership);
|
||||||
mCore = core;
|
mCore = core;
|
||||||
if (isInLinphoneThread()) moveToThread(App::getInstance()->thread());
|
if (isInLinphoneThread()) moveToThread(App::getInstance()->thread());
|
||||||
|
|
|
||||||
|
|
@ -25,13 +25,11 @@
|
||||||
DEFINE_ABSTRACT_OBJECT(ConferenceInfoGui)
|
DEFINE_ABSTRACT_OBJECT(ConferenceInfoGui)
|
||||||
|
|
||||||
ConferenceInfoGui::ConferenceInfoGui() {
|
ConferenceInfoGui::ConferenceInfoGui() {
|
||||||
// qDebug() << "[ConferenceInfoGui] new" << this;
|
|
||||||
mCore = ConferenceInfoCore::create(nullptr);
|
mCore = ConferenceInfoCore::create(nullptr);
|
||||||
App::getInstance()->mEngine->setObjectOwnership(this, QQmlEngine::JavaScriptOwnership);
|
App::getInstance()->mEngine->setObjectOwnership(this, QQmlEngine::JavaScriptOwnership);
|
||||||
if (isInLinphoneThread()) moveToThread(App::getInstance()->thread());
|
if (isInLinphoneThread()) moveToThread(App::getInstance()->thread());
|
||||||
}
|
}
|
||||||
ConferenceInfoGui::ConferenceInfoGui(QSharedPointer<ConferenceInfoCore> core) {
|
ConferenceInfoGui::ConferenceInfoGui(QSharedPointer<ConferenceInfoCore> core) {
|
||||||
// qDebug() << "[ConferenceInfoGui] new" << this;
|
|
||||||
App::getInstance()->mEngine->setObjectOwnership(this, QQmlEngine::JavaScriptOwnership);
|
App::getInstance()->mEngine->setObjectOwnership(this, QQmlEngine::JavaScriptOwnership);
|
||||||
mCore = core;
|
mCore = core;
|
||||||
if (isInLinphoneThread()) moveToThread(App::getInstance()->thread());
|
if (isInLinphoneThread()) moveToThread(App::getInstance()->thread());
|
||||||
|
|
@ -39,7 +37,6 @@ ConferenceInfoGui::ConferenceInfoGui(QSharedPointer<ConferenceInfoCore> core) {
|
||||||
|
|
||||||
ConferenceInfoGui::~ConferenceInfoGui() {
|
ConferenceInfoGui::~ConferenceInfoGui() {
|
||||||
mustBeInMainThread("~" + getClassName());
|
mustBeInMainThread("~" + getClassName());
|
||||||
// qDebug() << "[ConferenceInfoGui] delete" << this;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ConferenceInfoCore *ConferenceInfoGui::getCore() const {
|
ConferenceInfoCore *ConferenceInfoGui::getCore() const {
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@
|
||||||
|
|
||||||
#include "ParticipantDeviceCore.hpp"
|
#include "ParticipantDeviceCore.hpp"
|
||||||
#include "core/App.hpp"
|
#include "core/App.hpp"
|
||||||
|
#include "model/tool/ToolModel.hpp"
|
||||||
#include "tool/Utils.hpp"
|
#include "tool/Utils.hpp"
|
||||||
#include <QQmlApplicationEngine>
|
#include <QQmlApplicationEngine>
|
||||||
|
|
||||||
|
|
@ -31,7 +32,7 @@ ParticipantDeviceCore::create(std::shared_ptr<linphone::ParticipantDevice> devic
|
||||||
QSharedPointer<ParticipantDeviceCore>(new ParticipantDeviceCore(device, isMe, parent), &QObject::deleteLater);
|
QSharedPointer<ParticipantDeviceCore>(new ParticipantDeviceCore(device, isMe, parent), &QObject::deleteLater);
|
||||||
sharedPointer->setSelf(sharedPointer);
|
sharedPointer->setSelf(sharedPointer);
|
||||||
sharedPointer->moveToThread(App::getInstance()->thread());
|
sharedPointer->moveToThread(App::getInstance()->thread());
|
||||||
return nullptr;
|
return sharedPointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
ParticipantDeviceCore::ParticipantDeviceCore(const std::shared_ptr<linphone::ParticipantDevice> &device,
|
ParticipantDeviceCore::ParticipantDeviceCore(const std::shared_ptr<linphone::ParticipantDevice> &device,
|
||||||
|
|
@ -48,6 +49,8 @@ ParticipantDeviceCore::ParticipantDeviceCore(const std::shared_ptr<linphone::Par
|
||||||
mParticipantDeviceModel = Utils::makeQObject_ptr<ParticipantDeviceModel>(device);
|
mParticipantDeviceModel = Utils::makeQObject_ptr<ParticipantDeviceModel>(device);
|
||||||
mParticipantDeviceModel->setSelf(mParticipantDeviceModel);
|
mParticipantDeviceModel->setSelf(mParticipantDeviceModel);
|
||||||
mState = LinphoneEnums::fromLinphone(device->getState());
|
mState = LinphoneEnums::fromLinphone(device->getState());
|
||||||
|
qDebug() << "Address = " << Utils::coreStringToAppString(device->getAddress()->asStringUriOnly());
|
||||||
|
mIsLocal = ToolModel::findAccount(device->getAddress()) != nullptr; // TODO set local
|
||||||
// mCall = callModel;
|
// mCall = callModel;
|
||||||
// if (mCall) connect(mCall, &CallModel::statusChanged, this, &ParticipantDeviceCore::onCallStatusChanged);
|
// if (mCall) connect(mCall, &CallModel::statusChanged, this, &ParticipantDeviceCore::onCallStatusChanged);
|
||||||
mIsVideoEnabled = mParticipantDeviceModel->isVideoEnabled();
|
mIsVideoEnabled = mParticipantDeviceModel->isVideoEnabled();
|
||||||
|
|
@ -182,6 +185,10 @@ bool ParticipantDeviceCore::isLocal() const {
|
||||||
return mIsLocal;
|
return mIsLocal;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<ParticipantDeviceModel> ParticipantDeviceCore::getModel() const {
|
||||||
|
return mParticipantDeviceModel;
|
||||||
|
}
|
||||||
|
|
||||||
// void ParticipantDeviceCore::updateIsLocal() {
|
// void ParticipantDeviceCore::updateIsLocal() {
|
||||||
// auto deviceAddress = mParticipantDeviceModel->getAddress();
|
// auto deviceAddress = mParticipantDeviceModel->getAddress();
|
||||||
// auto callAddress = mCall->getConferenceSharedModel()->getConference()->getMe()->getAddress();
|
// auto callAddress = mCall->getConferenceSharedModel()->getConference()->getMe()->getAddress();
|
||||||
|
|
@ -247,4 +254,4 @@ void ParticipantDeviceCore::onStreamAvailabilityChanged(
|
||||||
const std::shared_ptr<linphone::ParticipantDevice> &participantDevice,
|
const std::shared_ptr<linphone::ParticipantDevice> &participantDevice,
|
||||||
bool available,
|
bool available,
|
||||||
linphone::StreamType streamType) {
|
linphone::StreamType streamType) {
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -74,6 +74,7 @@ public:
|
||||||
LinphoneEnums::ParticipantDeviceState getState() const;
|
LinphoneEnums::ParticipantDeviceState getState() const;
|
||||||
|
|
||||||
std::shared_ptr<linphone::ParticipantDevice> getDevice();
|
std::shared_ptr<linphone::ParticipantDevice> getDevice();
|
||||||
|
std::shared_ptr<ParticipantDeviceModel> getModel() const;
|
||||||
|
|
||||||
void setPaused(bool paused);
|
void setPaused(bool paused);
|
||||||
void setIsSpeaking(bool speaking);
|
void setIsSpeaking(bool speaking);
|
||||||
|
|
|
||||||
41
Linphone/core/participant/ParticipantDeviceGui.cpp
Normal file
41
Linphone/core/participant/ParticipantDeviceGui.cpp
Normal file
|
|
@ -0,0 +1,41 @@
|
||||||
|
/*
|
||||||
|
* 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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "ParticipantDeviceGui.hpp"
|
||||||
|
#include "core/App.hpp"
|
||||||
|
|
||||||
|
DEFINE_ABSTRACT_OBJECT(ParticipantDeviceGui)
|
||||||
|
|
||||||
|
ParticipantDeviceGui::ParticipantDeviceGui(QObject *parent) : QObject(parent) {
|
||||||
|
mCore = ParticipantDeviceCore::create(nullptr);
|
||||||
|
}
|
||||||
|
ParticipantDeviceGui::ParticipantDeviceGui(QSharedPointer<ParticipantDeviceCore> core) {
|
||||||
|
App::getInstance()->mEngine->setObjectOwnership(this, QQmlEngine::JavaScriptOwnership);
|
||||||
|
mCore = core;
|
||||||
|
if (isInLinphoneThread()) moveToThread(App::getInstance()->thread());
|
||||||
|
}
|
||||||
|
|
||||||
|
ParticipantDeviceGui::~ParticipantDeviceGui() {
|
||||||
|
mustBeInMainThread("~" + getClassName());
|
||||||
|
}
|
||||||
|
|
||||||
|
ParticipantDeviceCore *ParticipantDeviceGui::getCore() const {
|
||||||
|
return mCore.get();
|
||||||
|
}
|
||||||
42
Linphone/core/participant/ParticipantDeviceGui.hpp
Normal file
42
Linphone/core/participant/ParticipantDeviceGui.hpp
Normal file
|
|
@ -0,0 +1,42 @@
|
||||||
|
/*
|
||||||
|
* 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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef PARTICIPANT_DEVICE_GUI_H_
|
||||||
|
#define PARTICIPANT_DEVICE_GUI_H_
|
||||||
|
|
||||||
|
#include "ParticipantDeviceCore.hpp"
|
||||||
|
#include <QObject>
|
||||||
|
#include <QSharedPointer>
|
||||||
|
|
||||||
|
class ParticipantDeviceGui : public QObject, public AbstractObject {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
Q_PROPERTY(ParticipantDeviceCore *core READ getCore CONSTANT)
|
||||||
|
|
||||||
|
public:
|
||||||
|
ParticipantDeviceGui(QSharedPointer<ParticipantDeviceCore> core);
|
||||||
|
ParticipantDeviceGui(QObject *parent = nullptr);
|
||||||
|
~ParticipantDeviceGui();
|
||||||
|
ParticipantDeviceCore *getCore() const;
|
||||||
|
QSharedPointer<ParticipantDeviceCore> mCore;
|
||||||
|
DECLARE_ABSTRACT_OBJECT
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2021 Belledonne Communications SARL.
|
* Copyright (c) 2024 Belledonne Communications SARL.
|
||||||
*
|
*
|
||||||
* This file is part of linphone-desktop
|
* This file is part of linphone-desktop
|
||||||
* (see https://www.linphone.org).
|
* (see https://www.linphone.org).
|
||||||
|
|
@ -20,21 +20,14 @@
|
||||||
|
|
||||||
#include "ParticipantDeviceList.hpp"
|
#include "ParticipantDeviceList.hpp"
|
||||||
#include "core/App.hpp"
|
#include "core/App.hpp"
|
||||||
#include "core/participant/ParticipantCore.hpp"
|
#include "core/participant/ParticipantDeviceCore.hpp"
|
||||||
|
#include "core/participant/ParticipantDeviceGui.hpp"
|
||||||
|
|
||||||
#include <QQmlApplicationEngine>
|
#include <QQmlApplicationEngine>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
|
||||||
DEFINE_ABSTRACT_OBJECT(ParticipantDeviceList)
|
DEFINE_ABSTRACT_OBJECT(ParticipantDeviceList)
|
||||||
|
|
||||||
QSharedPointer<ParticipantDeviceList>
|
|
||||||
ParticipantDeviceList::create(const std::shared_ptr<linphone::Participant> &participant) {
|
|
||||||
auto model = QSharedPointer<ParticipantDeviceList>(new ParticipantDeviceList(participant), &QObject::deleteLater);
|
|
||||||
model->moveToThread(App::getInstance()->thread());
|
|
||||||
model->setSelf(model);
|
|
||||||
return model;
|
|
||||||
}
|
|
||||||
|
|
||||||
QSharedPointer<ParticipantDeviceList> ParticipantDeviceList::create() {
|
QSharedPointer<ParticipantDeviceList> ParticipantDeviceList::create() {
|
||||||
auto model = QSharedPointer<ParticipantDeviceList>(new ParticipantDeviceList(), &QObject::deleteLater);
|
auto model = QSharedPointer<ParticipantDeviceList>(new ParticipantDeviceList(), &QObject::deleteLater);
|
||||||
model->moveToThread(App::getInstance()->thread());
|
model->moveToThread(App::getInstance()->thread());
|
||||||
|
|
@ -42,302 +35,112 @@ QSharedPointer<ParticipantDeviceList> ParticipantDeviceList::create() {
|
||||||
return model;
|
return model;
|
||||||
}
|
}
|
||||||
|
|
||||||
ParticipantDeviceList::ParticipantDeviceList(const std::shared_ptr<linphone::Participant> &participant, QObject *parent)
|
QSharedPointer<ParticipantDeviceList>
|
||||||
: ListProxy(parent) {
|
ParticipantDeviceList::create(const std::shared_ptr<ConferenceModel> &conferenceModel) {
|
||||||
std::list<std::shared_ptr<linphone::ParticipantDevice>> devices = participant->getDevices();
|
auto model = create();
|
||||||
for (auto device : devices) {
|
model->setConferenceModel(conferenceModel);
|
||||||
auto deviceModel = ParticipantDeviceCore::create(device, isMe(device));
|
return model;
|
||||||
// connect(this, &ParticipantDeviceList::securityLevelChanged, deviceModel.get(),
|
|
||||||
// &ParticipantDeviceCore::onSecurityLevelChanged);
|
|
||||||
connect(deviceModel.get(), &ParticipantDeviceCore::isSpeakingChanged, this,
|
|
||||||
&ParticipantDeviceList::onParticipantDeviceSpeaking);
|
|
||||||
mList << deviceModel;
|
|
||||||
}
|
|
||||||
mInitialized = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ParticipantDeviceList::ParticipantDeviceList(QObject *parent) {
|
ParticipantDeviceList::ParticipantDeviceList() {
|
||||||
mustBeInMainThread(getClassName());
|
App::getInstance()->mEngine->setObjectOwnership(this, QQmlEngine::CppOwnership);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ParticipantDeviceList::ParticipantDeviceList(CallModel *callModel, QObject *parent) : ProxyListModel(parent) {
|
|
||||||
// if (callModel && callModel->isConference()) {
|
|
||||||
// mCallModel = callModel;
|
|
||||||
// connect(mCallModel, &CallModel::conferenceModelChanged, this, &ParticipantDeviceList::onConferenceModelChanged);
|
|
||||||
// initConferenceModel();
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
ParticipantDeviceList::~ParticipantDeviceList() {
|
ParticipantDeviceList::~ParticipantDeviceList() {
|
||||||
mustBeInMainThread(getClassName());
|
}
|
||||||
|
|
||||||
|
QList<QSharedPointer<ParticipantDeviceCore>>
|
||||||
|
ParticipantDeviceList::buildDevices(const std::shared_ptr<ConferenceModel> &conferenceModel) const {
|
||||||
|
mustBeInLinphoneThread(log().arg(Q_FUNC_INFO));
|
||||||
|
QList<QSharedPointer<ParticipantDeviceCore>> devices;
|
||||||
|
auto lDevices = conferenceModel->getMonitor()->getParticipantDeviceList();
|
||||||
|
bool haveMe = false;
|
||||||
|
for (auto device : lDevices) {
|
||||||
|
auto deviceCore = ParticipantDeviceCore::create(device);
|
||||||
|
devices << deviceCore;
|
||||||
|
if (deviceCore->isMe()) haveMe = true;
|
||||||
|
}
|
||||||
|
if (!haveMe) {
|
||||||
|
}
|
||||||
|
return devices;
|
||||||
|
}
|
||||||
|
|
||||||
|
QSharedPointer<ParticipantDeviceCore> ParticipantDeviceList::getMe() const {
|
||||||
|
if (mList.size() > 0) {
|
||||||
|
return mList[0].objectCast<ParticipantDeviceCore>();
|
||||||
|
} else return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ParticipantDeviceList::setDevices(QList<QSharedPointer<ParticipantDeviceCore>> devices) {
|
||||||
|
mustBeInMainThread(log().arg(Q_FUNC_INFO));
|
||||||
|
add(devices);
|
||||||
|
qDebug() << "[ParticipantDeviceList] : add " << devices.size() << " devices";
|
||||||
|
}
|
||||||
|
|
||||||
|
void ParticipantDeviceList::setConferenceModel(const std::shared_ptr<ConferenceModel> &conferenceModel) {
|
||||||
|
mustBeInMainThread(log().arg(Q_FUNC_INFO));
|
||||||
|
mConferenceModel = conferenceModel;
|
||||||
|
qDebug() << "[ParticipantDeviceList] : set Conference " << mConferenceModel.get();
|
||||||
|
if (mConferenceModelConnection->mCore.lock()) { // Unsure to get myself
|
||||||
|
auto oldConnect = mConferenceModelConnection->mCore; // Setself rebuild safepointer
|
||||||
|
setSelf(mConferenceModelConnection->mCore.mQData); // reset connections
|
||||||
|
oldConnect.unlock();
|
||||||
|
}
|
||||||
|
beginResetModel();
|
||||||
|
mList.clear();
|
||||||
|
endResetModel();
|
||||||
|
if (mConferenceModel) {
|
||||||
|
qDebug() << "[ParticipantDeviceList] : request devices";
|
||||||
|
mConferenceModelConnection->invokeToModel([this]() {
|
||||||
|
qDebug() << "[ParticipantDeviceList] : build devices";
|
||||||
|
auto devices = buildDevices(mConferenceModel);
|
||||||
|
mConferenceModelConnection->invokeToCore([this, devices]() {
|
||||||
|
qDebug() << "[ParticipantDeviceList] : set devices";
|
||||||
|
setDevices(devices);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ParticipantDeviceList::setSelf(QSharedPointer<ParticipantDeviceList> me) {
|
void ParticipantDeviceList::setSelf(QSharedPointer<ParticipantDeviceList> me) {
|
||||||
}
|
if (mConferenceModelConnection) mConferenceModelConnection->disconnect();
|
||||||
|
mConferenceModelConnection = QSharedPointer<SafeConnection<ParticipantDeviceList, ConferenceModel>>(
|
||||||
void ParticipantDeviceList::initConferenceModel() {
|
new SafeConnection<ParticipantDeviceList, ConferenceModel>(me, mConferenceModel), &QObject::deleteLater);
|
||||||
// if (!mInitialized && mCallModel) {
|
if (mConferenceModel) {
|
||||||
// auto conferenceModel = mCallModel->getConferenceSharedModel();
|
mConferenceModelConnection->makeConnectToModel(
|
||||||
// if (conferenceModel) {
|
&ConferenceModel::participantDeviceAdded,
|
||||||
// updateDevices(conferenceModel->getConference()->getMe()->getDevices(), true);
|
[this](const std::shared_ptr<linphone::ParticipantDevice> &device) {
|
||||||
// updateDevices(conferenceModel->getConference()->getParticipantDeviceList(), false);
|
auto deviceCore = ParticipantDeviceCore::create(device);
|
||||||
|
mConferenceModelConnection->invokeToCore([this, deviceCore]() {
|
||||||
// qDebug() << "Conference have " << mList.size() << " devices";
|
qDebug() << "[ParticipantDeviceList] : add a device";
|
||||||
// connect(conferenceModel.get(), &ConferenceModel::activeSpeakerParticipantDevice, this,
|
this->add(deviceCore);
|
||||||
// &ParticipantDeviceList::onActiveSpeakerParticipantDevice);
|
});
|
||||||
// connect(conferenceModel.get(), &ConferenceModel::participantAdded, this,
|
});
|
||||||
// &ParticipantDeviceList::onParticipantAdded);
|
mConferenceModelConnection->makeConnectToModel(
|
||||||
// connect(conferenceModel.get(), &ConferenceModel::participantRemoved, this,
|
&ConferenceModel::conferenceStateChanged, [this](linphone::Conference::State state) {
|
||||||
// &ParticipantDeviceList::onParticipantRemoved);
|
qDebug() << "[ParticipantDeviceList] new state = " << (int)state;
|
||||||
// connect(conferenceModel.get(), &ConferenceModel::participantDeviceAdded, this,
|
if (state == linphone::Conference::State::Created) {
|
||||||
// &ParticipantDeviceList::onParticipantDeviceAdded);
|
qDebug() << "[ParticipantDeviceList] : build devices";
|
||||||
// connect(conferenceModel.get(), &ConferenceModel::participantDeviceRemoved, this,
|
auto devices = buildDevices(mConferenceModel);
|
||||||
// &ParticipantDeviceList::onParticipantDeviceRemoved);
|
mConferenceModelConnection->invokeToCore([this, devices]() {
|
||||||
// connect(conferenceModel.get(), &ConferenceModel::conferenceStateChanged, this,
|
qDebug() << "[ParticipantDeviceList] : set devices" << devices.size();
|
||||||
// &ParticipantDeviceList::onConferenceStateChanged);
|
setDevices(devices);
|
||||||
// connect(conferenceModel.get(), &ConferenceModel::participantDeviceMediaCapabilityChanged, this,
|
});
|
||||||
// &ParticipantDeviceList::onParticipantDeviceMediaCapabilityChanged);
|
}
|
||||||
// connect(conferenceModel.get(), &ConferenceModel::participantDeviceMediaAvailabilityChanged, this,
|
});
|
||||||
// &ParticipantDeviceList::onParticipantDeviceMediaAvailabilityChanged);
|
mConferenceModelConnection->makeConnectToCore(
|
||||||
// connect(conferenceModel.get(), &ConferenceModel::participantDeviceIsSpeakingChanged, this,
|
&ParticipantDeviceList::lSetConferenceModel,
|
||||||
// &ParticipantDeviceList::onParticipantDeviceIsSpeakingChanged);
|
[this](const std::shared_ptr<ConferenceModel> &conferenceModel) {
|
||||||
// mActiveSpeaker = get(conferenceModel->getConference()->getActiveSpeakerParticipantDevice());
|
mConferenceModelConnection->invokeToCore(
|
||||||
// mInitialized = true;
|
[this, conferenceModel]() { setConferenceModel(conferenceModel); });
|
||||||
// }
|
});
|
||||||
// }
|
|
||||||
}
|
|
||||||
|
|
||||||
void ParticipantDeviceList::updateDevices(std::shared_ptr<linphone::Participant> participant) {
|
|
||||||
std::list<std::shared_ptr<linphone::ParticipantDevice>> devices = participant->getDevices();
|
|
||||||
bool meAdded = false;
|
|
||||||
beginResetModel();
|
|
||||||
qDebug() << "Update devices from participant";
|
|
||||||
mList.clear();
|
|
||||||
for (auto device : devices) {
|
|
||||||
bool addMe = isMe(device);
|
|
||||||
auto deviceModel = ParticipantDeviceCore::create(device, addMe);
|
|
||||||
// connect(this, &ParticipantDeviceList::securityLevelChanged, deviceModel.get(),
|
|
||||||
// &ParticipantDeviceCore::onSecurityLevelChanged);
|
|
||||||
connect(deviceModel.get(), &ParticipantDeviceCore::isSpeakingChanged, this,
|
|
||||||
&ParticipantDeviceList::onParticipantDeviceSpeaking);
|
|
||||||
mList << deviceModel;
|
|
||||||
if (addMe) meAdded = true;
|
|
||||||
}
|
|
||||||
endResetModel();
|
|
||||||
if (meAdded) emit meChanged();
|
|
||||||
}
|
|
||||||
|
|
||||||
void ParticipantDeviceList::updateDevices(const std::list<QSharedPointer<ParticipantDeviceCore>> &devices,
|
|
||||||
const bool &isMe) {
|
|
||||||
for (auto device : devices) {
|
|
||||||
add(device);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ParticipantDeviceList::add(const QSharedPointer<ParticipantDeviceCore> &deviceToAdd) {
|
QVariant ParticipantDeviceList::data(const QModelIndex &index, int role) const {
|
||||||
auto deviceToAddAddr = deviceToAdd->getAddress();
|
int row = index.row();
|
||||||
int row = 0;
|
if (!index.isValid() || row < 0 || row >= mList.count()) return QVariant();
|
||||||
qDebug() << "Adding device " << deviceToAdd->getAddress();
|
if (role == Qt::DisplayRole)
|
||||||
for (auto item : mList) {
|
return QVariant::fromValue(new ParticipantDeviceGui(mList[row].objectCast<ParticipantDeviceCore>()));
|
||||||
auto deviceCore = item.objectCast<ParticipantDeviceCore>();
|
return QVariant();
|
||||||
if (deviceCore == deviceToAdd) {
|
|
||||||
qDebug() << "Device already exist. Send video update event";
|
|
||||||
// deviceCore->updateVideoEnabled();
|
|
||||||
return false;
|
|
||||||
} else if (deviceToAddAddr == deviceCore->getAddress()) { // Address is the same (same device) but the model
|
|
||||||
// is using another linphone object. Replace it.
|
|
||||||
qDebug() << "Replacing device : Device exists but the model is using another linphone object.";
|
|
||||||
// deviceCore->updateVideoEnabled();
|
|
||||||
removeRow(row);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
++row;
|
|
||||||
}
|
|
||||||
bool addMe = isMe(deviceToAdd);
|
|
||||||
auto deviceModel = ParticipantDeviceCore::create(deviceToAdd, addMe);
|
|
||||||
// connect(this, &ParticipantDeviceList::securityLevelChanged, deviceModel.get(),
|
|
||||||
// &ParticipantDeviceCore::onSecurityLevelChanged);
|
|
||||||
connect(deviceModel.get(), &ParticipantDeviceCore::isSpeakingChanged, this,
|
|
||||||
&ParticipantDeviceList::onParticipantDeviceSpeaking);
|
|
||||||
ListProxy::add<ParticipantDeviceCore>(deviceModel);
|
|
||||||
qDebug() << "Device added. Count=" << mList.count();
|
|
||||||
QStringList debugDevices;
|
|
||||||
for (auto i : mList) {
|
|
||||||
auto item = i.objectCast<ParticipantDeviceCore>();
|
|
||||||
debugDevices.push_back(item->getAddress());
|
|
||||||
}
|
|
||||||
qDebug() << debugDevices.join("\n");
|
|
||||||
if (addMe) {
|
|
||||||
qDebug() << "Added a me device";
|
|
||||||
emit meChanged();
|
|
||||||
} else if (mList.size() == 1 ||
|
|
||||||
(mList.size() == 2 && isMe(mList.front().objectCast<ParticipantDeviceCore>()->getDevice()))) {
|
|
||||||
mActiveSpeaker = mList.back().objectCast<ParticipantDeviceCore>();
|
|
||||||
emit activeSpeakerChanged();
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ParticipantDeviceList::remove(std::shared_ptr<const linphone::ParticipantDevice> deviceToRemove) {
|
|
||||||
int row = 0;
|
|
||||||
for (auto item : mList) {
|
|
||||||
auto device = item.objectCast<ParticipantDeviceCore>();
|
|
||||||
if (device->getDevice() == deviceToRemove) {
|
|
||||||
// device->updateVideoEnabled();
|
|
||||||
removeRow(row);
|
|
||||||
return true;
|
|
||||||
} else ++row;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
QSharedPointer<ParticipantDeviceCore>
|
|
||||||
ParticipantDeviceList::get(std::shared_ptr<const linphone::ParticipantDevice> deviceToGet, int *index) {
|
|
||||||
int row = 0;
|
|
||||||
for (auto item : mList) {
|
|
||||||
auto device = item.objectCast<ParticipantDeviceCore>();
|
|
||||||
if (device->getDevice() == deviceToGet) {
|
|
||||||
if (index) *index = row;
|
|
||||||
return device;
|
|
||||||
} else ++row;
|
|
||||||
}
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
QSharedPointer<ParticipantDeviceCore> ParticipantDeviceList::getMe(int *index) const {
|
|
||||||
int row = 0;
|
|
||||||
for (auto item : mList) {
|
|
||||||
auto device = item.objectCast<ParticipantDeviceCore>();
|
|
||||||
if (device->isMe() && device->isLocal()) {
|
|
||||||
if (index) *index = row;
|
|
||||||
return device;
|
|
||||||
} else ++row;
|
|
||||||
}
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
ParticipantDeviceCore *ParticipantDeviceList::getActiveSpeakerModel() const {
|
|
||||||
return mActiveSpeaker.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ParticipantDeviceList::isMe(std::shared_ptr<linphone::ParticipantDevice> deviceToCheck) const {
|
|
||||||
// if (mCallModel) {
|
|
||||||
// auto devices = mCallModel->getConferenceSharedModel()->getConference()->getMe()->getDevices();
|
|
||||||
// auto deviceToCheckAddress = deviceToCheck->getAddress();
|
|
||||||
// for (auto device : devices) {
|
|
||||||
// if (deviceToCheckAddress == device->getAddress()) return true;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ParticipantDeviceList::isMeAlone() const {
|
|
||||||
for (auto item : mList) {
|
|
||||||
auto device = item.objectCast<ParticipantDeviceCore>();
|
|
||||||
if (!isMe(device->getDevice())) return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ParticipantDeviceList::onConferenceModelChanged() {
|
|
||||||
if (!mInitialized) {
|
|
||||||
initConferenceModel();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void ParticipantDeviceList::onSecurityLevelChanged(std::shared_ptr<const linphone::Address> device) {
|
|
||||||
emit securityLevelChanged(device);
|
|
||||||
}
|
|
||||||
|
|
||||||
//----------------------------------------------------------------------------------------------------------
|
|
||||||
void ParticipantDeviceList::onParticipantAdded(const std::shared_ptr<const linphone::Participant> &participant) {
|
|
||||||
std::list<std::shared_ptr<linphone::ParticipantDevice>> devices = participant->getDevices();
|
|
||||||
if (devices.size() == 0)
|
|
||||||
qDebug() << "Participant has no device. It will not be added : "
|
|
||||||
<< participant->getAddress()->asString().c_str();
|
|
||||||
else
|
|
||||||
for (auto device : devices)
|
|
||||||
add(device);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ParticipantDeviceList::onParticipantRemoved(const std::shared_ptr<const linphone::Participant> &participant) {
|
|
||||||
std::list<std::shared_ptr<linphone::ParticipantDevice>> devices = participant->getDevices();
|
|
||||||
for (auto device : devices)
|
|
||||||
remove(device);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ParticipantDeviceList::onParticipantDeviceAdded(
|
|
||||||
const std::shared_ptr<const linphone::ParticipantDevice> &participantDevice) {
|
|
||||||
qDebug() << "Adding new device : " << mList.count();
|
|
||||||
// auto conferenceModel = mCallModel->getConferenceSharedModel();
|
|
||||||
std::list<std::shared_ptr<linphone::ParticipantDevice>> devices;
|
|
||||||
for (int i = 0; i < 2; ++i) {
|
|
||||||
// if (i == 0) devices = conferenceModel->getConference()->getParticipantDeviceList(); // Active devices.
|
|
||||||
// else devices = conferenceModel->getConference()->getMe()->getDevices();
|
|
||||||
for (auto realParticipantDevice : devices) {
|
|
||||||
if (realParticipantDevice == participantDevice) {
|
|
||||||
add(realParticipantDevice);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
qDebug() << "No participant device found from linphone::ParticipantDevice at onParticipantDeviceAdded";
|
|
||||||
}
|
|
||||||
|
|
||||||
void ParticipantDeviceList::onParticipantDeviceRemoved(
|
|
||||||
const std::shared_ptr<const linphone::ParticipantDevice> &participantDevice) {
|
|
||||||
qDebug() << "Removing participant device : " << mList.count();
|
|
||||||
if (!remove(participantDevice))
|
|
||||||
qDebug() << "No participant device found from linphone::ParticipantDevice at onParticipantDeviceRemoved";
|
|
||||||
}
|
|
||||||
|
|
||||||
void ParticipantDeviceList::onConferenceStateChanged(linphone::Conference::State newState) {
|
|
||||||
// if (newState == linphone::Conference::State::Created) {
|
|
||||||
// if (mCallModel && mCallModel->isConference()) {
|
|
||||||
// auto conferenceModel = mCallModel->getConferenceSharedModel();
|
|
||||||
// updateDevices(conferenceModel->getConference()->getMe()->getDevices(), true);
|
|
||||||
// updateDevices(conferenceModel->getConference()->getParticipantDeviceList(), false);
|
|
||||||
// }
|
|
||||||
// emit conferenceCreated();
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
|
|
||||||
void ParticipantDeviceList::onParticipantDeviceMediaCapabilityChanged(
|
|
||||||
const std::shared_ptr<const linphone::ParticipantDevice> &participantDevice) {
|
|
||||||
// auto device = get(participantDevice);
|
|
||||||
// if (device) device->updateVideoEnabled();
|
|
||||||
// else onParticipantDeviceAdded(participantDevice);
|
|
||||||
|
|
||||||
// device = get(participantDevice);
|
|
||||||
// if (device && device->isMe()) { // Capability change for me. Update all videos.
|
|
||||||
// for (auto item : mList) {
|
|
||||||
// auto device = item.objectCast<ParticipantDeviceCore>();
|
|
||||||
// device->updateVideoEnabled();
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
|
|
||||||
void ParticipantDeviceList::onParticipantDeviceMediaAvailabilityChanged(
|
|
||||||
const std::shared_ptr<const linphone::ParticipantDevice> &participantDevice) {
|
|
||||||
// auto device = get(participantDevice);
|
|
||||||
// if (device) device->updateVideoEnabled();
|
|
||||||
// else onParticipantDeviceAdded(participantDevice);
|
|
||||||
}
|
|
||||||
void ParticipantDeviceList::onActiveSpeakerParticipantDevice(
|
|
||||||
const std::shared_ptr<const linphone::ParticipantDevice> &participantDevice) {
|
|
||||||
// auto device = get(participantDevice);
|
|
||||||
// if (device) {
|
|
||||||
// mActiveSpeaker = device;
|
|
||||||
// emit activeSpeakerChanged();
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
|
|
||||||
void ParticipantDeviceList::onParticipantDeviceIsSpeakingChanged(
|
|
||||||
const std::shared_ptr<const linphone::ParticipantDevice> &participantDevice, bool isSpeaking) {
|
|
||||||
auto device = get(participantDevice);
|
|
||||||
if (device) emit participantSpeaking(device.get());
|
|
||||||
}
|
|
||||||
|
|
||||||
void ParticipantDeviceList::onParticipantDeviceSpeaking() {
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -22,70 +22,39 @@
|
||||||
#define PARTICIPANT_DEVICE_LIST_H_
|
#define PARTICIPANT_DEVICE_LIST_H_
|
||||||
|
|
||||||
#include "../proxy/ListProxy.hpp"
|
#include "../proxy/ListProxy.hpp"
|
||||||
#include "core/call/CallCore.hpp"
|
#include "ParticipantDeviceCore.hpp"
|
||||||
#include "core/participant/ParticipantDeviceCore.hpp"
|
#include "model/conference/ConferenceModel.hpp"
|
||||||
#include <QDateTime>
|
#include "tool/AbstractObject.hpp"
|
||||||
#include <QObject>
|
#include "tool/thread/SafeConnection.hpp"
|
||||||
#include <QString>
|
|
||||||
|
|
||||||
class ParticipantDeviceList : public ListProxy, public AbstractObject {
|
class ParticipantDeviceList : public ListProxy, public AbstractObject {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
static QSharedPointer<ParticipantDeviceList> create(const std::shared_ptr<linphone::Participant> &participant);
|
|
||||||
static QSharedPointer<ParticipantDeviceList> create();
|
static QSharedPointer<ParticipantDeviceList> create();
|
||||||
|
static QSharedPointer<ParticipantDeviceList> create(const std::shared_ptr<ConferenceModel> &conferenceModel);
|
||||||
|
|
||||||
ParticipantDeviceList(const std::shared_ptr<linphone::Participant> &participant, QObject *parent = nullptr);
|
ParticipantDeviceList();
|
||||||
// ParticipantDeviceList(CallCore *callCore, QObject *parent = nullptr);
|
|
||||||
ParticipantDeviceList(QObject *parent = Q_NULLPTR);
|
|
||||||
~ParticipantDeviceList();
|
~ParticipantDeviceList();
|
||||||
|
|
||||||
|
QList<QSharedPointer<ParticipantDeviceCore>>
|
||||||
|
buildDevices(const std::shared_ptr<ConferenceModel> &conferenceModel) const;
|
||||||
|
|
||||||
|
QSharedPointer<ParticipantDeviceCore> getMe() const;
|
||||||
|
|
||||||
|
void setDevices(QList<QSharedPointer<ParticipantDeviceCore>> devices);
|
||||||
|
void setConferenceModel(const std::shared_ptr<ConferenceModel> &conferenceModel);
|
||||||
|
|
||||||
void setSelf(QSharedPointer<ParticipantDeviceList> me);
|
void setSelf(QSharedPointer<ParticipantDeviceList> me);
|
||||||
|
|
||||||
void initConferenceModel();
|
virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
|
||||||
void updateDevices(std::shared_ptr<linphone::Participant> participant);
|
|
||||||
void updateDevices(const std::list<QSharedPointer<ParticipantDeviceCore>> &devices, const bool &isMe);
|
|
||||||
|
|
||||||
bool add(const QSharedPointer<ParticipantDeviceCore> &deviceToAdd);
|
|
||||||
bool remove(std::shared_ptr<const linphone::ParticipantDevice> deviceToAdd);
|
|
||||||
QSharedPointer<ParticipantDeviceCore> get(std::shared_ptr<const linphone::ParticipantDevice> deviceToGet,
|
|
||||||
int *index = nullptr);
|
|
||||||
QSharedPointer<ParticipantDeviceCore> getMe(int *index = nullptr) const;
|
|
||||||
ParticipantDeviceCore *getActiveSpeakerModel() const;
|
|
||||||
|
|
||||||
bool isMe(std::shared_ptr<linphone::ParticipantDevice> device) const;
|
|
||||||
bool isMeAlone() const;
|
|
||||||
|
|
||||||
public slots:
|
|
||||||
void onActiveSpeakerParticipantDevice(const std::shared_ptr<const linphone::ParticipantDevice> &participantDevice);
|
|
||||||
void onConferenceModelChanged();
|
|
||||||
void onSecurityLevelChanged(std::shared_ptr<const linphone::Address> device);
|
|
||||||
void onParticipantAdded(const std::shared_ptr<const linphone::Participant> &participant);
|
|
||||||
void onParticipantRemoved(const std::shared_ptr<const linphone::Participant> &participant);
|
|
||||||
void onParticipantDeviceAdded(const std::shared_ptr<const linphone::ParticipantDevice> &participantDevice);
|
|
||||||
void onParticipantDeviceRemoved(const std::shared_ptr<const linphone::ParticipantDevice> &participantDevice);
|
|
||||||
void onConferenceStateChanged(linphone::Conference::State newState);
|
|
||||||
void onParticipantDeviceMediaCapabilityChanged(
|
|
||||||
const std::shared_ptr<const linphone::ParticipantDevice> &participantDevice);
|
|
||||||
void onParticipantDeviceMediaAvailabilityChanged(
|
|
||||||
const std::shared_ptr<const linphone::ParticipantDevice> &participantDevice);
|
|
||||||
void onParticipantDeviceIsSpeakingChanged(const std::shared_ptr<const linphone::ParticipantDevice> &device,
|
|
||||||
bool isSpeaking);
|
|
||||||
void onParticipantDeviceSpeaking();
|
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void activeSpeakerChanged();
|
void lSetConferenceModel(const std::shared_ptr<ConferenceModel> &conferenceModel);
|
||||||
void securityLevelChanged(std::shared_ptr<const linphone::Address> device);
|
|
||||||
void participantSpeaking(ParticipantDeviceCore *speakingDevice);
|
|
||||||
void conferenceCreated();
|
|
||||||
void meChanged();
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
CallCore *mCallCore = nullptr;
|
std::shared_ptr<ConferenceModel> mConferenceModel;
|
||||||
QSharedPointer<ParticipantDeviceCore> mActiveSpeaker;
|
QSharedPointer<SafeConnection<ParticipantDeviceList, ConferenceModel>> mConferenceModelConnection;
|
||||||
// QList<ParticipantDeviceCore*> mActiveSpeakers;// First item is last speaker
|
|
||||||
bool mInitialized = false;
|
|
||||||
QSharedPointer<SafeConnection<ParticipantDeviceList, CallModel>> mModelConnection;
|
|
||||||
|
|
||||||
DECLARE_ABSTRACT_OBJECT
|
DECLARE_ABSTRACT_OBJECT
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2021 Belledonne Communications SARL.
|
* Copyright (c) 2024 Belledonne Communications SARL.
|
||||||
*
|
*
|
||||||
* This file is part of linphone-desktop
|
* This file is part of linphone-desktop
|
||||||
* (see https://www.linphone.org).
|
* (see https://www.linphone.org).
|
||||||
|
|
@ -26,96 +26,71 @@
|
||||||
|
|
||||||
// =============================================================================
|
// =============================================================================
|
||||||
|
|
||||||
|
DEFINE_ABSTRACT_OBJECT(ParticipantDeviceProxy)
|
||||||
|
|
||||||
ParticipantDeviceProxy::ParticipantDeviceProxy(QObject *parent) : SortFilterProxy(parent) {
|
ParticipantDeviceProxy::ParticipantDeviceProxy(QObject *parent) : SortFilterProxy(parent) {
|
||||||
mDeleteSourceModel = true;
|
mParticipants = ParticipantDeviceList::create();
|
||||||
mList = ParticipantDeviceList::create();
|
connect(mParticipants.get(), &ParticipantDeviceList::countChanged, this, &ParticipantDeviceProxy::meChanged);
|
||||||
setSourceModel(mList.get());
|
|
||||||
|
setSourceModel(mParticipants.get());
|
||||||
|
sort(0); //, Qt::DescendingOrder);
|
||||||
}
|
}
|
||||||
|
|
||||||
ParticipantDeviceProxy::~ParticipantDeviceProxy() {
|
ParticipantDeviceProxy::~ParticipantDeviceProxy() {
|
||||||
|
setSourceModel(nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ParticipantDeviceProxy::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const {
|
CallGui *ParticipantDeviceProxy::getCurrentCall() const {
|
||||||
const QModelIndex index = mList->index(sourceRow, 0, sourceParent);
|
return mCurrentCall;
|
||||||
const ParticipantDeviceCore *device = index.data().value<ParticipantDeviceCore *>();
|
|
||||||
return device && (isShowMe() /*|| !(device->isMe() && device->isLocal())*/);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ParticipantDeviceProxy::lessThan(const QModelIndex &left, const QModelIndex &right) const {
|
void ParticipantDeviceProxy::setCurrentCall(CallGui *call) {
|
||||||
const ParticipantDeviceCore *deviceA = sourceModel()->data(left).value<ParticipantDeviceCore *>();
|
qDebug() << "[ParticipantDeviceProxy] set current call " << this << " => " << call;
|
||||||
const ParticipantDeviceCore *deviceB = sourceModel()->data(right).value<ParticipantDeviceCore *>();
|
if (mCurrentCall != call) {
|
||||||
// 'me' at end (for grid).
|
CallCore *callCore = nullptr;
|
||||||
return /*deviceB->isLocal() || !deviceA->isLocal() && deviceB->isMe() ||*/ left.row() < right.row();
|
if (mCurrentCall) {
|
||||||
}
|
callCore = mCurrentCall->getCore();
|
||||||
//---------------------------------------------------------------------------------
|
if (callCore) callCore->disconnect(mParticipants.get());
|
||||||
|
callCore = nullptr;
|
||||||
ParticipantDeviceCore *ParticipantDeviceProxy::getAt(int row) {
|
}
|
||||||
if (row >= 0) {
|
mCurrentCall = call;
|
||||||
QModelIndex sourceIndex = mapToSource(this->index(row, 0));
|
if (mCurrentCall) callCore = mCurrentCall->getCore();
|
||||||
return sourceModel()->data(sourceIndex).value<ParticipantDeviceCore *>();
|
if (callCore) {
|
||||||
} else return nullptr;
|
connect(callCore, &CallCore::conferenceChanged, mParticipants.get(), [this]() {
|
||||||
}
|
auto conference = mCurrentCall->getCore()->getConferenceCore();
|
||||||
|
qDebug() << "[ParticipantDeviceProxy] set conference " << this << " => " << conference;
|
||||||
ParticipantDeviceCore *ParticipantDeviceProxy::getActiveSpeakerModel() {
|
mParticipants->setConferenceModel(conference ? conference->getModel() : nullptr);
|
||||||
return mList->getActiveSpeakerModel();
|
// mParticipants->lSetConferenceModel(conference ? conference->getModel() : nullptr);
|
||||||
}
|
});
|
||||||
|
auto conference = callCore->getConferenceCore();
|
||||||
CallModel *ParticipantDeviceProxy::getCallModel() const {
|
qDebug() << "[ParticipantDeviceProxy] set conference " << this << " => " << conference;
|
||||||
return mCallModel;
|
mParticipants->setConferenceModel(conference ? conference->getModel() : nullptr);
|
||||||
}
|
// mParticipants->lSetConferenceModel(conference ? conference->getModel() : nullptr);
|
||||||
|
}
|
||||||
ParticipantDeviceCore *ParticipantDeviceProxy::getMe() const {
|
emit currentCallChanged();
|
||||||
return mList->getMe().get();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ParticipantDeviceProxy::isShowMe() const {
|
|
||||||
return mShowMe;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ParticipantDeviceProxy::connectTo(ParticipantDeviceList *model) {
|
|
||||||
connect(model, &ParticipantDeviceList::countChanged, this, &ParticipantDeviceProxy::onCountChanged);
|
|
||||||
connect(model, &ParticipantDeviceList::participantSpeaking, this, &ParticipantDeviceProxy::onParticipantSpeaking);
|
|
||||||
connect(model, &ParticipantDeviceList::conferenceCreated, this, &ParticipantDeviceProxy::conferenceCreated);
|
|
||||||
connect(model, &ParticipantDeviceList::meChanged, this, &ParticipantDeviceProxy::meChanged);
|
|
||||||
connect(model, &ParticipantDeviceList::activeSpeakerChanged, this, &ParticipantDeviceProxy::activeSpeakerChanged);
|
|
||||||
}
|
|
||||||
void ParticipantDeviceProxy::setCallModel(CallModel *callModel) {
|
|
||||||
setFilterType(1);
|
|
||||||
mCallModel = callModel;
|
|
||||||
deleteSourceModel();
|
|
||||||
auto newSourceModel = new ParticipantDeviceList(mCallModel);
|
|
||||||
connectTo(newSourceModel);
|
|
||||||
setSourceModel(newSourceModel);
|
|
||||||
mDeleteSourceModel = true;
|
|
||||||
sort(0);
|
|
||||||
emit countChanged();
|
|
||||||
emit meChanged();
|
|
||||||
}
|
|
||||||
|
|
||||||
// void ParticipantDeviceProxy::setParticipant(ParticipantCore *participantCore) {
|
|
||||||
// setFilterType(0);
|
|
||||||
// deleteSourceModel();
|
|
||||||
// auto newSourceModel = participant->getParticipantDevices().get();
|
|
||||||
// connectTo(newSourceModel);
|
|
||||||
// setSourceModel(newSourceModel);
|
|
||||||
// mDeleteSourceModel = false;
|
|
||||||
// sort(0);
|
|
||||||
// emit countChanged();
|
|
||||||
// emit meChanged();
|
|
||||||
// }
|
|
||||||
|
|
||||||
void ParticipantDeviceProxy::setShowMe(const bool &show) {
|
|
||||||
if (mShowMe != show) {
|
|
||||||
mShowMe = show;
|
|
||||||
emit showMeChanged();
|
|
||||||
invalidate();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ParticipantDeviceProxy::onCountChanged() {
|
ParticipantDeviceGui *ParticipantDeviceProxy::getMe() const {
|
||||||
qDebug() << "Count changed : " << getCount();
|
auto core = mParticipants->getMe();
|
||||||
|
if (!core) return nullptr;
|
||||||
|
else {
|
||||||
|
return new ParticipantDeviceGui(core);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ParticipantDeviceProxy::onParticipantSpeaking(ParticipantDeviceCore *speakingDevice) {
|
void ParticipantDeviceProxy::setMe(ParticipantDeviceGui *me) {
|
||||||
emit participantSpeaking(speakingDevice);
|
}
|
||||||
|
|
||||||
|
bool ParticipantDeviceProxy::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ParticipantDeviceProxy::lessThan(const QModelIndex &left, const QModelIndex &right) const {
|
||||||
|
auto deviceA = sourceModel()->data(left).value<ParticipantDeviceGui *>()->getCore();
|
||||||
|
auto deviceB = sourceModel()->data(right).value<ParticipantDeviceGui *>()->getCore();
|
||||||
|
// auto deviceB = getItemAt<ParticipantDeviceList, ParticipantDeviceGui>(right.row())->getCore();
|
||||||
|
// return deviceB->isLocal() || !deviceA->isLocal() && deviceB->isMe() || left.row() < right.row();
|
||||||
|
|
||||||
|
return deviceB->isMe() || (!deviceB->isMe() && left.row() < right.row());
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2021 Belledonne Communications SARL.
|
* Copyright (c) 2024 Belledonne Communications SARL.
|
||||||
*
|
*
|
||||||
* This file is part of linphone-desktop
|
* This file is part of linphone-desktop
|
||||||
* (see https://www.linphone.org).
|
* (see https://www.linphone.org).
|
||||||
|
|
@ -21,64 +21,43 @@
|
||||||
#ifndef PARTICIPANT_DEVICE_PROXY_MODEL_H_
|
#ifndef PARTICIPANT_DEVICE_PROXY_MODEL_H_
|
||||||
#define PARTICIPANT_DEVICE_PROXY_MODEL_H_
|
#define PARTICIPANT_DEVICE_PROXY_MODEL_H_
|
||||||
|
|
||||||
#include <linphone++/linphone.hh>
|
|
||||||
// =============================================================================
|
|
||||||
#include "../proxy/SortFilterProxy.hpp"
|
#include "../proxy/SortFilterProxy.hpp"
|
||||||
#include <QDateTime>
|
#include "core/call/CallGui.hpp"
|
||||||
#include <QObject>
|
#include "core/participant/ParticipantDeviceGui.hpp"
|
||||||
#include <QSharedPointer>
|
#include "tool/AbstractObject.hpp"
|
||||||
#include <QString>
|
|
||||||
|
|
||||||
class ParticipantDeviceList;
|
class ParticipantDeviceList;
|
||||||
class ParticipantDeviceCore;
|
class ParticipantDeviceGui;
|
||||||
class ParticipantCore;
|
|
||||||
class CallModel;
|
|
||||||
|
|
||||||
class ParticipantDeviceProxy : public SortFilterProxy {
|
class ParticipantDeviceProxy : public SortFilterProxy, public AbstractObject {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
Q_PROPERTY(CallGui *currentCall READ getCurrentCall WRITE setCurrentCall NOTIFY currentCallChanged)
|
||||||
|
Q_PROPERTY(ParticipantDeviceGui *me READ getMe WRITE setMe NOTIFY meChanged)
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Q_PROPERTY(CallModel *callModel READ getCallModel WRITE setCallModel NOTIFY callModelChanged)
|
ParticipantDeviceProxy(QObject *parent = Q_NULLPTR);
|
||||||
Q_PROPERTY(bool showMe READ isShowMe WRITE setShowMe NOTIFY showMeChanged)
|
|
||||||
Q_PROPERTY(ParticipantDeviceCore *me READ getMe NOTIFY meChanged)
|
|
||||||
Q_PROPERTY(ParticipantDeviceCore *activeSpeaker READ getActiveSpeakerModel NOTIFY activeSpeakerChanged)
|
|
||||||
|
|
||||||
ParticipantDeviceProxy(QObject *parent = nullptr);
|
|
||||||
~ParticipantDeviceProxy();
|
~ParticipantDeviceProxy();
|
||||||
|
|
||||||
Q_INVOKABLE ParticipantDeviceCore *getAt(int row);
|
CallGui *getCurrentCall() const;
|
||||||
ParticipantDeviceCore *getActiveSpeakerModel();
|
void setCurrentCall(CallGui *callGui);
|
||||||
ParticipantDeviceCore *getMe() const;
|
|
||||||
|
|
||||||
CallModel *getCallModel() const;
|
ParticipantDeviceGui *getMe() const;
|
||||||
bool isShowMe() const;
|
void setMe(ParticipantDeviceGui *me);
|
||||||
|
|
||||||
void setCallModel(CallModel *callModel);
|
|
||||||
// void setParticipant(ParticipantCore *participantCore);
|
|
||||||
void setShowMe(const bool &show);
|
|
||||||
|
|
||||||
void connectTo(ParticipantDeviceList *model);
|
|
||||||
|
|
||||||
public slots:
|
|
||||||
void onCountChanged();
|
|
||||||
void onParticipantSpeaking(ParticipantDeviceCore *speakingDevice);
|
|
||||||
|
|
||||||
signals:
|
|
||||||
void activeSpeakerChanged();
|
|
||||||
void callModelChanged();
|
|
||||||
void showMeChanged();
|
|
||||||
void meChanged();
|
|
||||||
void participantSpeaking(ParticipantDeviceCore *speakingDevice);
|
|
||||||
void conferenceCreated();
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override;
|
bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override;
|
||||||
virtual bool lessThan(const QModelIndex &left, const QModelIndex &right) const override;
|
bool lessThan(const QModelIndex &left, const QModelIndex &right) const override;
|
||||||
|
|
||||||
CallModel *mCallModel;
|
signals:
|
||||||
bool mShowMe = true;
|
void lUpdate();
|
||||||
|
void currentCallChanged();
|
||||||
|
void meChanged();
|
||||||
|
|
||||||
QSharedPointer<ParticipantDeviceList> mList;
|
private:
|
||||||
|
QString mSearchText;
|
||||||
|
CallGui *mCurrentCall = nullptr;
|
||||||
|
QSharedPointer<ParticipantDeviceList> mParticipants;
|
||||||
|
DECLARE_ABSTRACT_OBJECT
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -24,13 +24,20 @@
|
||||||
|
|
||||||
#include "core/path/Paths.hpp"
|
#include "core/path/Paths.hpp"
|
||||||
#include "model/core/CoreModel.hpp"
|
#include "model/core/CoreModel.hpp"
|
||||||
|
#include "tool/Utils.hpp"
|
||||||
|
|
||||||
DEFINE_ABSTRACT_OBJECT(ConferenceModel)
|
DEFINE_ABSTRACT_OBJECT(ConferenceModel)
|
||||||
|
|
||||||
|
std::shared_ptr<ConferenceModel> ConferenceModel::create(const std::shared_ptr<linphone::Conference> &conference) {
|
||||||
|
auto model = Utils::makeQObject_ptr<ConferenceModel>(conference);
|
||||||
|
model->setSelf(model);
|
||||||
|
return model;
|
||||||
|
}
|
||||||
|
|
||||||
ConferenceModel::ConferenceModel(const std::shared_ptr<linphone::Conference> &conference, QObject *parent)
|
ConferenceModel::ConferenceModel(const std::shared_ptr<linphone::Conference> &conference, QObject *parent)
|
||||||
: ::Listener<linphone::Conference, linphone::ConferenceListener>(conference, parent) {
|
: ::Listener<linphone::Conference, linphone::ConferenceListener>(conference, parent) {
|
||||||
mustBeInLinphoneThread(getClassName());
|
mustBeInLinphoneThread(getClassName());
|
||||||
qDebug() << "[ConferenceModel] new" << this;
|
qDebug() << "[ConferenceModel] new" << this << conference.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
ConferenceModel::~ConferenceModel() {
|
ConferenceModel::~ConferenceModel() {
|
||||||
|
|
@ -136,7 +143,8 @@ void ConferenceModel::onActiveSpeakerParticipantDevice(
|
||||||
const std::shared_ptr<linphone::Conference> &conference,
|
const std::shared_ptr<linphone::Conference> &conference,
|
||||||
const std::shared_ptr<const linphone::ParticipantDevice> &participantDevice) {
|
const std::shared_ptr<const linphone::ParticipantDevice> &participantDevice) {
|
||||||
qDebug() << "onActiveSpeakerParticipantDevice: " << participantDevice->getAddress()->asString().c_str();
|
qDebug() << "onActiveSpeakerParticipantDevice: " << participantDevice->getAddress()->asString().c_str();
|
||||||
emit activeSpeakerParticipantDevice(participantDevice);
|
|
||||||
|
emit activeSpeakerParticipantDevice(conference->getActiveSpeakerParticipantDevice());
|
||||||
}
|
}
|
||||||
|
|
||||||
void ConferenceModel::onParticipantAdded(const std::shared_ptr<linphone::Conference> &conference,
|
void ConferenceModel::onParticipantAdded(const std::shared_ptr<linphone::Conference> &conference,
|
||||||
|
|
|
||||||
|
|
@ -35,6 +35,7 @@ class ConferenceModel : public ::Listener<linphone::Conference, linphone::Confer
|
||||||
public:
|
public:
|
||||||
ConferenceModel(const std::shared_ptr<linphone::Conference> &conference, QObject *parent = nullptr);
|
ConferenceModel(const std::shared_ptr<linphone::Conference> &conference, QObject *parent = nullptr);
|
||||||
~ConferenceModel();
|
~ConferenceModel();
|
||||||
|
static std::shared_ptr<ConferenceModel> create(const std::shared_ptr<linphone::Conference> &conference);
|
||||||
|
|
||||||
void terminate();
|
void terminate();
|
||||||
|
|
||||||
|
|
@ -105,11 +106,11 @@ private:
|
||||||
const std::shared_ptr<const linphone::AudioDevice> &audioDevice) override;
|
const std::shared_ptr<const linphone::AudioDevice> &audioDevice) override;
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void activeSpeakerParticipantDevice(const std::shared_ptr<const linphone::ParticipantDevice> &participantDevice);
|
void activeSpeakerParticipantDevice(const std::shared_ptr<linphone::ParticipantDevice> &participantDevice);
|
||||||
void participantAdded(const std::shared_ptr<const linphone::Participant> &participant);
|
void participantAdded(const std::shared_ptr<linphone::Participant> &participant);
|
||||||
void participantRemoved(const std::shared_ptr<const linphone::Participant> &participant);
|
void participantRemoved(const std::shared_ptr<const linphone::Participant> &participant);
|
||||||
void participantAdminStatusChanged(const std::shared_ptr<const linphone::Participant> &participant);
|
void participantAdminStatusChanged(const std::shared_ptr<const linphone::Participant> &participant);
|
||||||
void participantDeviceAdded(const std::shared_ptr<const linphone::ParticipantDevice> &participantDevice);
|
void participantDeviceAdded(const std::shared_ptr<linphone::ParticipantDevice> &participantDevice);
|
||||||
void participantDeviceRemoved(const std::shared_ptr<const linphone::ParticipantDevice> &participantDevice);
|
void participantDeviceRemoved(const std::shared_ptr<const linphone::ParticipantDevice> &participantDevice);
|
||||||
void participantDeviceStateChanged(const std::shared_ptr<linphone::Conference> &conference,
|
void participantDeviceStateChanged(const std::shared_ptr<linphone::Conference> &conference,
|
||||||
const std::shared_ptr<const linphone::ParticipantDevice> &device,
|
const std::shared_ptr<const linphone::ParticipantDevice> &device,
|
||||||
|
|
|
||||||
|
|
@ -159,3 +159,43 @@ QSharedPointer<CallCore> ToolModel::createCall(const QString &sipAddress,
|
||||||
// CallModel::prepareTransfert(core->inviteAddressWithParams(address, params), prepareTransfertAddress);
|
// CallModel::prepareTransfert(core->inviteAddressWithParams(address, params), prepareTransfertAddress);
|
||||||
*/
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<linphone::Account> ToolModel::findAccount(const std::shared_ptr<const linphone::Address> &address) {
|
||||||
|
std::shared_ptr<linphone::Account> account;
|
||||||
|
for (auto item : CoreModel::getInstance()->getCore()->getAccountList()) {
|
||||||
|
if (item->getContactAddress()->weakEqual(address)) {
|
||||||
|
account = item;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return account;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ToolModel::isMe(const QString &address) {
|
||||||
|
bool isMe = false;
|
||||||
|
auto linAddr = ToolModel::interpretUrl(address);
|
||||||
|
if (!CoreModel::getInstance()->getCore()->getDefaultAccount()) {
|
||||||
|
// for (auto &account : CoreModel::getInstance()->getCore()->getAccountList()) {
|
||||||
|
// if (account->getContactAddress()->weakEqual(linAddr)) return true;
|
||||||
|
// }
|
||||||
|
isMe = false;
|
||||||
|
} else {
|
||||||
|
auto accountAddr = CoreModel::getInstance()->getCore()->getDefaultAccount()->getContactAddress();
|
||||||
|
isMe = linAddr && accountAddr ? accountAddr->weakEqual(linAddr) : false;
|
||||||
|
}
|
||||||
|
return isMe;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ToolModel::isMe(const std::shared_ptr<const linphone::Address> &address) {
|
||||||
|
auto currentAccount = CoreModel::getInstance()->getCore()->getDefaultAccount();
|
||||||
|
if (!currentAccount) { // Default account is selected : Me is all local accounts.
|
||||||
|
return findAccount(address) != nullptr;
|
||||||
|
} else return address ? currentAccount->getContactAddress()->weakEqual(address) : false;
|
||||||
|
}
|
||||||
|
bool ToolModel::isLocal(const std::shared_ptr<linphone::Conference> &conference,
|
||||||
|
const std::shared_ptr<const linphone::ParticipantDevice> &device) {
|
||||||
|
auto deviceAddress = device->getAddress();
|
||||||
|
auto callAddress = conference->getMe()->getAddress();
|
||||||
|
auto gruuAddress = findAccount(callAddress)->getContactAddress();
|
||||||
|
return deviceAddress->equal(gruuAddress);
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -37,6 +37,11 @@ public:
|
||||||
static std::shared_ptr<linphone::Address> interpretUrl(const QString &address);
|
static std::shared_ptr<linphone::Address> interpretUrl(const QString &address);
|
||||||
static std::shared_ptr<linphone::FriendPhoneNumber> makeLinphoneNumber(const QString &label, const QString &number);
|
static std::shared_ptr<linphone::FriendPhoneNumber> makeLinphoneNumber(const QString &label, const QString &number);
|
||||||
static std::shared_ptr<linphone::AudioDevice> findAudioDevice(const QString &id);
|
static std::shared_ptr<linphone::AudioDevice> findAudioDevice(const QString &id);
|
||||||
|
static std::shared_ptr<linphone::Account> findAccount(const std::shared_ptr<const linphone::Address> &address);
|
||||||
|
static bool isMe(const QString &address);
|
||||||
|
static bool isMe(const std::shared_ptr<const linphone::Address> &address);
|
||||||
|
static bool isLocal(const std::shared_ptr<linphone::Conference> &conference,
|
||||||
|
const std::shared_ptr<const linphone::ParticipantDevice> &device);
|
||||||
|
|
||||||
static QString getDisplayName(const std::shared_ptr<const linphone::Address> &address);
|
static QString getDisplayName(const std::shared_ptr<const linphone::Address> &address);
|
||||||
static QString getDisplayName(QString address);
|
static QString getDisplayName(QString address);
|
||||||
|
|
|
||||||
|
|
@ -1195,18 +1195,7 @@ int Utils::getYear(const QDate &date) {
|
||||||
|
|
||||||
bool Utils::isMe(const QString &address) {
|
bool Utils::isMe(const QString &address) {
|
||||||
bool isMe = false;
|
bool isMe = false;
|
||||||
App::postModelSync([&isMe, address]() {
|
App::postModelSync([&isMe, address]() { isMe = ToolModel::isMe(address); });
|
||||||
auto linAddr = ToolModel::interpretUrl(address);
|
|
||||||
if (!CoreModel::getInstance()->getCore()->getDefaultAccount()) {
|
|
||||||
// for (auto &account : CoreModel::getInstance()->getCore()->getAccountList()) {
|
|
||||||
// if (account->getContactAddress()->weakEqual(linAddr)) return true;
|
|
||||||
// }
|
|
||||||
isMe = false;
|
|
||||||
} else {
|
|
||||||
auto accountAddr = CoreModel::getInstance()->getCore()->getDefaultAccount()->getContactAddress();
|
|
||||||
isMe = linAddr && accountAddr ? accountAddr->weakEqual(linAddr) : false;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return isMe;
|
return isMe;
|
||||||
}
|
}
|
||||||
// QDateTime dateTime(QDateTime::fromString(date, "yyyy-MM-dd hh:mm:ss"));
|
// QDateTime dateTime(QDateTime::fromString(date, "yyyy-MM-dd hh:mm:ss"));
|
||||||
|
|
|
||||||
|
|
@ -97,6 +97,14 @@ public:
|
||||||
return connect(mCoreObject, signal, mModelObject, slot, Qt::DirectConnection);
|
return connect(mCoreObject, signal, mModelObject, slot, Qt::DirectConnection);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline void disconnect() {
|
||||||
|
if (!tryLock()) // To avoid disconnections while being in call.
|
||||||
|
return; // Return to avoid to disconnect other connections than the pair.
|
||||||
|
QObject::disconnect(mModelObject, nullptr, mCoreObject, nullptr);
|
||||||
|
QObject::disconnect(mCoreObject, nullptr, mModelObject, nullptr);
|
||||||
|
unlock();
|
||||||
|
}
|
||||||
|
|
||||||
template <typename Func, typename... Args>
|
template <typename Func, typename... Args>
|
||||||
void invokeToModel(Func &&callable, Args &&...args) {
|
void invokeToModel(Func &&callable, Args &&...args) {
|
||||||
if (!tryLock()) return;
|
if (!tryLock()) return;
|
||||||
|
|
|
||||||
|
|
@ -28,11 +28,12 @@ Window {
|
||||||
if (call && !conferenceInfo) middleItemStackView.replace(inCallItem)
|
if (call && !conferenceInfo) middleItemStackView.replace(inCallItem)
|
||||||
}
|
}
|
||||||
Component.onCompleted: if (call && !conferenceInfo) middleItemStackView.replace(inCallItem)
|
Component.onCompleted: if (call && !conferenceInfo) middleItemStackView.replace(inCallItem)
|
||||||
|
property var callObj
|
||||||
|
|
||||||
function joinConference(withVideo) {
|
function joinConference(withVideo) {
|
||||||
if (!conferenceInfo || conferenceInfo.core.uri.length === 0) UtilsCpp.showInformationPopup(qsTr("Erreur"), qsTr("La conférence n'a pas pu démarrer en raison d'une erreur d'uri."))
|
if (!conferenceInfo || conferenceInfo.core.uri.length === 0) UtilsCpp.showInformationPopup(qsTr("Erreur"), qsTr("La conférence n'a pas pu démarrer en raison d'une erreur d'uri."))
|
||||||
else {
|
else {
|
||||||
var callObj = UtilsCpp.createCall(conferenceInfo.core.uri, withVideo)
|
callObj = UtilsCpp.createCall(conferenceInfo.core.uri, withVideo)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -358,6 +359,7 @@ Window {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.fillHeight: true
|
Layout.fillHeight: true
|
||||||
Layout.margins: 20 * DefaultStyle.dp
|
Layout.margins: 20 * DefaultStyle.dp
|
||||||
|
|
||||||
}
|
}
|
||||||
OngoingCallRightPanel {
|
OngoingCallRightPanel {
|
||||||
id: rightPanel
|
id: rightPanel
|
||||||
|
|
@ -606,6 +608,10 @@ Window {
|
||||||
onJoinConfRequested: mainWindow.joinConference(cameraEnabled)
|
onJoinConfRequested: mainWindow.joinConference(cameraEnabled)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Component {
|
Component {
|
||||||
id: inCallItem
|
id: inCallItem
|
||||||
Control.Control {
|
Control.Control {
|
||||||
|
|
@ -614,163 +620,18 @@ Window {
|
||||||
// implicitHeight: parent.height
|
// implicitHeight: parent.height
|
||||||
Layout.fillHeight: true
|
Layout.fillHeight: true
|
||||||
Layout.leftMargin: 10 * DefaultStyle.dp
|
Layout.leftMargin: 10 * DefaultStyle.dp
|
||||||
|
|
||||||
Layout.rightMargin: 10 * DefaultStyle.dp
|
Layout.rightMargin: 10 * DefaultStyle.dp
|
||||||
Layout.alignment: Qt.AlignCenter
|
Layout.alignment: Qt.AlignCenter
|
||||||
|
/*
|
||||||
background: Rectangle {
|
background: Rectangle {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
color: DefaultStyle.grey_600
|
color: DefaultStyle.grey_600
|
||||||
radius: 15 * DefaultStyle.dp
|
radius: 15 * DefaultStyle.dp
|
||||||
}
|
}*/
|
||||||
contentItem: Item {
|
contentItem: CallLayout{
|
||||||
id: centerItem
|
call: mainWindow.call
|
||||||
anchors.fill: parent
|
callTerminatedByUser: mainWindow.callTerminatedByUser
|
||||||
Text {
|
|
||||||
id: callTerminatedText
|
|
||||||
Connections {
|
|
||||||
target: mainWindow
|
|
||||||
onCallStateChanged: {
|
|
||||||
if (mainWindow.callState === LinphoneEnums.CallState.End) {
|
|
||||||
callTerminatedText.visible = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
visible: false
|
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
|
||||||
anchors.top: parent.top
|
|
||||||
anchors.topMargin: 25 * DefaultStyle.dp
|
|
||||||
text: mainWindow.callTerminatedByUser ? qsTr("Vous avez terminé l'appel") : qsTr("Votre correspondant a terminé l'appel")
|
|
||||||
color: DefaultStyle.grey_0
|
|
||||||
z: 1
|
|
||||||
font {
|
|
||||||
pixelSize: 22 * DefaultStyle.dp
|
|
||||||
weight: 300 * DefaultStyle.dp
|
|
||||||
}
|
|
||||||
}
|
|
||||||
StackLayout {
|
|
||||||
id: centerLayout
|
|
||||||
currentIndex: 0
|
|
||||||
anchors.fill: parent
|
|
||||||
Connections {
|
|
||||||
target: mainWindow
|
|
||||||
onCallStateChanged: {
|
|
||||||
if (mainWindow.callState === LinphoneEnums.CallState.Error) {
|
|
||||||
centerLayout.currentIndex = 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Sticker {
|
|
||||||
call: mainWindow.call
|
|
||||||
Layout.fillWidth: true
|
|
||||||
Layout.fillHeight: true
|
|
||||||
// visible: mainWindow.callState != LinphoneEnums.CallState.End
|
|
||||||
Connections {
|
|
||||||
target: mainWindow
|
|
||||||
onCallChanged: {
|
|
||||||
waitingTime.seconds = 0
|
|
||||||
waitingTimer.restart()
|
|
||||||
console.log("call changed", call, waitingTime.seconds)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Timer {
|
|
||||||
id: waitingTimer
|
|
||||||
interval: 1000
|
|
||||||
repeat: true
|
|
||||||
onTriggered: waitingTime.seconds += 1
|
|
||||||
}
|
|
||||||
ColumnLayout {
|
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
|
||||||
anchors.top: parent.top
|
|
||||||
anchors.topMargin: 30 * DefaultStyle.dp
|
|
||||||
visible: mainWindow.callState == LinphoneEnums.CallState.OutgoingInit
|
|
||||||
|| mainWindow.callState == LinphoneEnums.CallState.OutgoingProgress
|
|
||||||
|| mainWindow.callState == LinphoneEnums.CallState.OutgoingRinging
|
|
||||||
|| mainWindow.callState == LinphoneEnums.CallState.OutgoingEarlyMedia
|
|
||||||
|| mainWindow.callState == LinphoneEnums.CallState.IncomingReceived
|
|
||||||
BusyIndicator {
|
|
||||||
indicatorColor: DefaultStyle.main2_100
|
|
||||||
Layout.alignment: Qt.AlignHCenter
|
|
||||||
}
|
|
||||||
Text {
|
|
||||||
id: waitingTime
|
|
||||||
property int seconds
|
|
||||||
text: UtilsCpp.formatElapsedTime(seconds)
|
|
||||||
color: DefaultStyle.grey_0
|
|
||||||
Layout.alignment: Qt.AlignHCenter
|
|
||||||
horizontalAlignment: Text.AlignHCenter
|
|
||||||
font {
|
|
||||||
pixelSize: 30 * DefaultStyle.dp
|
|
||||||
weight: 300 * DefaultStyle.dp
|
|
||||||
}
|
|
||||||
Component.onCompleted: {
|
|
||||||
waitingTimer.restart()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ColumnLayout {
|
|
||||||
id: errorLayout
|
|
||||||
Layout.preferredWidth: parent.width
|
|
||||||
Layout.preferredHeight: parent.height
|
|
||||||
Layout.alignment: Qt.AlignCenter
|
|
||||||
Text {
|
|
||||||
text: qsTr(mainWindow.call.core.lastErrorMessage)
|
|
||||||
Layout.alignment: Qt.AlignCenter
|
|
||||||
color: DefaultStyle.grey_0
|
|
||||||
font.pixelSize: 40 * DefaultStyle.dp
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Sticker {
|
|
||||||
id: preview
|
|
||||||
visible: mainWindow.callState != LinphoneEnums.CallState.End
|
|
||||||
&& mainWindow.callState != LinphoneEnums.CallState.Released
|
|
||||||
height: 180 * DefaultStyle.dp
|
|
||||||
width: 300 * DefaultStyle.dp
|
|
||||||
anchors.right: centerItem.right
|
|
||||||
anchors.bottom: centerItem.bottom
|
|
||||||
anchors.rightMargin: 10 * DefaultStyle.dp
|
|
||||||
anchors.bottomMargin: 10 * DefaultStyle.dp
|
|
||||||
AccountProxy{
|
|
||||||
id: accounts
|
|
||||||
}
|
|
||||||
account: accounts.defaultAccount
|
|
||||||
enablePersonalCamera: mainWindow.call.core.cameraEnabled
|
|
||||||
|
|
||||||
MovableMouseArea {
|
|
||||||
id: previewMouseArea
|
|
||||||
anchors.fill: parent
|
|
||||||
// visible: mainWindow.participantCount <= 2
|
|
||||||
movableArea: centerItem
|
|
||||||
margin: 10 * DefaultStyle.dp
|
|
||||||
function resetPosition(){
|
|
||||||
preview.anchors.right = centerItem.right
|
|
||||||
preview.anchors.bottom = centerItem.bottom
|
|
||||||
preview.anchors.rightMargin = previewMouseArea.margin
|
|
||||||
preview.anchors.bottomMargin = previewMouseArea.margin
|
|
||||||
}
|
|
||||||
onVisibleChanged: if(!visible){
|
|
||||||
resetPosition()
|
|
||||||
}
|
|
||||||
drag.target: preview
|
|
||||||
onDraggingChanged: if(dragging) {
|
|
||||||
preview.anchors.right = undefined
|
|
||||||
preview.anchors.bottom = undefined
|
|
||||||
}
|
|
||||||
onRequestResetPosition: resetPosition()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
property int previousWidth
|
|
||||||
Component.onCompleted: {
|
|
||||||
previousWidth = width
|
|
||||||
}
|
|
||||||
onWidthChanged: {
|
|
||||||
if (width < previousWidth) {
|
|
||||||
previewMouseArea.updatePosition(0, 0)
|
|
||||||
} else {
|
|
||||||
previewMouseArea.updatePosition(width - previousWidth, 0)
|
|
||||||
}
|
|
||||||
previousWidth = width
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -210,13 +210,14 @@ Item {
|
||||||
background: Item{}
|
background: Item{}
|
||||||
Layout.preferredWidth: 24 * DefaultStyle.dp
|
Layout.preferredWidth: 24 * DefaultStyle.dp
|
||||||
Layout.preferredHeight: 24 * DefaultStyle.dp
|
Layout.preferredHeight: 24 * DefaultStyle.dp
|
||||||
|
property var callObj
|
||||||
contentItem: Image {
|
contentItem: Image {
|
||||||
width: 24 * DefaultStyle.dp
|
width: 24 * DefaultStyle.dp
|
||||||
height: 24 * DefaultStyle.dp
|
height: 24 * DefaultStyle.dp
|
||||||
source: AppIcons.phone
|
source: AppIcons.phone
|
||||||
}
|
}
|
||||||
onClicked: {
|
onClicked: {
|
||||||
var callObj = UtilsCpp.createCall(sipAddr)
|
callObj = UtilsCpp.createCall(sipAddr.text)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,10 @@ list(APPEND _LINPHONEAPP_QML_FILES
|
||||||
view/App/Layout/LoginLayout.qml
|
view/App/Layout/LoginLayout.qml
|
||||||
view/App/Layout/MainLayout.qml
|
view/App/Layout/MainLayout.qml
|
||||||
|
|
||||||
|
view/Layout/Call/ActiveSpeakerLayout.qml
|
||||||
|
view/Layout/Call/CallLayout.qml
|
||||||
|
#view/Layout/Call/GridLayout.qml
|
||||||
|
|
||||||
view/Layout/Conference/IncallGrid.qml
|
view/Layout/Conference/IncallGrid.qml
|
||||||
view/Layout/Contact/ContactLayout.qml
|
view/Layout/Contact/ContactLayout.qml
|
||||||
view/Layout/Meeting/AddParticipantsLayout.qml
|
view/Layout/Meeting/AddParticipantsLayout.qml
|
||||||
|
|
@ -86,6 +90,8 @@ list(APPEND _LINPHONEAPP_QML_FILES
|
||||||
view/Page/Main/ContactPage.qml
|
view/Page/Main/ContactPage.qml
|
||||||
view/Page/Main/MeetingPage.qml
|
view/Page/Main/MeetingPage.qml
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
view/Tool/utils.js
|
view/Tool/utils.js
|
||||||
# Prototypes
|
# Prototypes
|
||||||
view/Prototype/PhoneNumberPrototype.qml
|
view/Prototype/PhoneNumberPrototype.qml
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,7 @@ RowLayout {
|
||||||
id: accounts
|
id: accounts
|
||||||
}
|
}
|
||||||
account: accounts.defaultAccount
|
account: accounts.defaultAccount
|
||||||
enablePersonalCamera: mainItem.cameraEnabled
|
previewEnabled: mainItem.cameraEnabled
|
||||||
}
|
}
|
||||||
RowLayout {
|
RowLayout {
|
||||||
Layout.alignment: Qt.AlignHCenter
|
Layout.alignment: Qt.AlignHCenter
|
||||||
|
|
@ -146,4 +146,4 @@ RowLayout {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -16,13 +16,19 @@ Item {
|
||||||
width: 200
|
width: 200
|
||||||
property CallGui call: null
|
property CallGui call: null
|
||||||
property AccountGui account: null
|
property AccountGui account: null
|
||||||
property bool enablePersonalCamera: false
|
property ParticipantDeviceGui participantDevice: null
|
||||||
onEnablePersonalCameraChanged: console.log ("enable camera", enablePersonalCamera)
|
property bool previewEnabled: false
|
||||||
property color color: DefaultStyle.grey_600
|
property color color: DefaultStyle.grey_600
|
||||||
property int radius: 15 * DefaultStyle.dp
|
property int radius: 15 * DefaultStyle.dp
|
||||||
property var peerAddressObj: call ? UtilsCpp.getDisplayName(call.core.peerAddress) : null
|
property var peerAddressObj: participantDevice && participantDevice.core
|
||||||
property string peerAddress: peerAddressObj ? peerAddressObj.value : ""
|
? UtilsCpp.getDisplayName(participantDevice.core.address)
|
||||||
|
: call && call.core
|
||||||
|
? UtilsCpp.getDisplayName(call.core.peerAddress)
|
||||||
|
: null
|
||||||
|
property string peerAddress:peerAddressObj ? peerAddressObj.value : ""
|
||||||
property var identityAddress: account ? UtilsCpp.getDisplayName(account.core.identityAddress) : null
|
property var identityAddress: account ? UtilsCpp.getDisplayName(account.core.identityAddress) : null
|
||||||
|
property bool cameraEnabled: previewEnabled
|
||||||
|
property string qmlName
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
id: background
|
id: background
|
||||||
|
|
@ -70,10 +76,7 @@ Item {
|
||||||
interval: 1
|
interval: 1
|
||||||
onTriggered: {cameraLoader.active=false; cameraLoader.active=true;}
|
onTriggered: {cameraLoader.active=false; cameraLoader.active=true;}
|
||||||
}
|
}
|
||||||
active: mainItem.visible && call
|
active: mainItem.visible && mainItem.cameraEnabled
|
||||||
? call.core.remoteVideoEnabled && (mainItem.call.core.state != LinphoneEnums.CallState.End
|
|
||||||
&& mainItem.call.core.state != LinphoneEnums.CallState.Released)
|
|
||||||
: mainItem.enablePersonalCamera
|
|
||||||
onActiveChanged: console.log("camera active", active)
|
onActiveChanged: console.log("camera active", active)
|
||||||
sourceComponent: cameraComponent
|
sourceComponent: cameraComponent
|
||||||
}
|
}
|
||||||
|
|
@ -88,7 +91,9 @@ Item {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
visible: isReady
|
visible: isReady
|
||||||
call: mainItem.call
|
call: mainItem.call
|
||||||
|
participantDevice: mainItem.participantDevice
|
||||||
|
isPreview: mainItem.previewEnabled
|
||||||
|
qmlName: mainItem.qmlName
|
||||||
onRequestNewRenderer: {
|
onRequestNewRenderer: {
|
||||||
console.log("Request new renderer")
|
console.log("Request new renderer")
|
||||||
resetTimer.restart()
|
resetTimer.restart()
|
||||||
|
|
@ -103,7 +108,7 @@ Item {
|
||||||
anchors.leftMargin: 10 * DefaultStyle.dp
|
anchors.leftMargin: 10 * DefaultStyle.dp
|
||||||
anchors.bottomMargin: 10 * DefaultStyle.dp
|
anchors.bottomMargin: 10 * DefaultStyle.dp
|
||||||
width: implicitWidth
|
width: implicitWidth
|
||||||
text: mainItem.peerAddress.length != 0
|
text: mainItem.peerAddress != ''
|
||||||
? mainItem.peerAddress
|
? mainItem.peerAddress
|
||||||
: mainItem.account && mainItem.identityAddress
|
: mainItem.account && mainItem.identityAddress
|
||||||
? mainItem.identityAddress.value
|
? mainItem.identityAddress.value
|
||||||
|
|
|
||||||
365
Linphone/view/Layout/Call/ActiveSpeakerLayout.qml
Normal file
365
Linphone/view/Layout/Call/ActiveSpeakerLayout.qml
Normal file
|
|
@ -0,0 +1,365 @@
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Layouts
|
||||||
|
import QtQuick.Effects
|
||||||
|
import QtQml.Models
|
||||||
|
import QtQuick.Controls as Control
|
||||||
|
import Linphone
|
||||||
|
import EnumsToStringCpp 1.0
|
||||||
|
import UtilsCpp 1.0
|
||||||
|
import SettingsCpp 1.0
|
||||||
|
// =============================================================================
|
||||||
|
|
||||||
|
Item{
|
||||||
|
id: mainItem
|
||||||
|
property alias call: allDevices.currentCall
|
||||||
|
property ConferenceGui conference: call && call.core.conference || null
|
||||||
|
property var callState: call && call.core.state || undefined
|
||||||
|
|
||||||
|
property ParticipantDeviceProxy participantDevices : ParticipantDeviceProxy {
|
||||||
|
id: allDevices
|
||||||
|
}
|
||||||
|
onCallChanged: {
|
||||||
|
waitingTime.seconds = 0
|
||||||
|
waitingTimer.restart()
|
||||||
|
console.log("call changed", call, waitingTime.seconds)
|
||||||
|
}
|
||||||
|
RowLayout{
|
||||||
|
anchors.fill: parent
|
||||||
|
spacing: 16 * DefaultStyle.dp
|
||||||
|
|
||||||
|
Sticker {
|
||||||
|
id: activeSpeakerSticker
|
||||||
|
//call: mainItem.call
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.fillHeight: true
|
||||||
|
call: mainItem.call
|
||||||
|
participantDevice: mainItem.conference && mainItem.conference.core.activeSpeaker
|
||||||
|
property var address: participantDevice && participantDevice.core.address
|
||||||
|
onAddressChanged: console.log(address)
|
||||||
|
cameraEnabled: true
|
||||||
|
qmlName: 'AS'
|
||||||
|
|
||||||
|
Timer {
|
||||||
|
id: waitingTimer
|
||||||
|
interval: 1000
|
||||||
|
repeat: true
|
||||||
|
onTriggered: waitingTime.seconds += 1
|
||||||
|
}
|
||||||
|
ColumnLayout {
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
anchors.top: parent.top
|
||||||
|
anchors.topMargin: 30 * DefaultStyle.dp
|
||||||
|
visible: mainItem.callState === LinphoneEnums.CallState.OutgoingInit
|
||||||
|
|| mainItem.callState === LinphoneEnums.CallState.OutgoingProgress
|
||||||
|
|| mainItem.callState === LinphoneEnums.CallState.OutgoingRinging
|
||||||
|
|| mainItem.callState === LinphoneEnums.CallState.OutgoingEarlyMedia
|
||||||
|
|| mainItem.callState === LinphoneEnums.CallState.IncomingReceived
|
||||||
|
BusyIndicator {
|
||||||
|
indicatorColor: DefaultStyle.main2_100
|
||||||
|
Layout.alignment: Qt.AlignHCenter
|
||||||
|
}
|
||||||
|
Text {
|
||||||
|
id: waitingTime
|
||||||
|
property int seconds
|
||||||
|
text: UtilsCpp.formatElapsedTime(seconds)
|
||||||
|
color: DefaultStyle.grey_0
|
||||||
|
Layout.alignment: Qt.AlignHCenter
|
||||||
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
font {
|
||||||
|
pixelSize: 30 * DefaultStyle.dp
|
||||||
|
weight: 300 * DefaultStyle.dp
|
||||||
|
}
|
||||||
|
Component.onCompleted: {
|
||||||
|
waitingTimer.restart()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ListView{
|
||||||
|
Layout.fillHeight: true
|
||||||
|
Layout.preferredWidth: 300 * DefaultStyle.dp
|
||||||
|
Layout.rightMargin: 10 * DefaultStyle.dp
|
||||||
|
Layout.bottomMargin: 10 * DefaultStyle.dp
|
||||||
|
visible: allDevices.count > 2
|
||||||
|
spacing: 15 * DefaultStyle.dp
|
||||||
|
model: allDevices
|
||||||
|
delegate:
|
||||||
|
Sticker {
|
||||||
|
visible: mainItem.callState != LinphoneEnums.CallState.End && mainItem.callState != LinphoneEnums.CallState.Released
|
||||||
|
&& modelData.core.address != activeSpeakerSticker.address
|
||||||
|
onVisibleChanged: console.log(modelData.core.address)
|
||||||
|
height: visible ? 180 * DefaultStyle.dp : 0
|
||||||
|
width: 300 * DefaultStyle.dp
|
||||||
|
qmlName: 'M_'+index
|
||||||
|
|
||||||
|
participantDevice: modelData
|
||||||
|
cameraEnabled: visible
|
||||||
|
Component.onCompleted: console.log(modelData.core.address)
|
||||||
|
//previewEnabled: mainItem.call.core.cameraEnabled
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Sticker {
|
||||||
|
id: preview
|
||||||
|
visible: allDevices.count <= 2
|
||||||
|
height: 180 * DefaultStyle.dp
|
||||||
|
width: 300 * DefaultStyle.dp
|
||||||
|
anchors.right: mainItem.right
|
||||||
|
anchors.bottom: mainItem.bottom
|
||||||
|
anchors.rightMargin: 10 * DefaultStyle.dp
|
||||||
|
anchors.bottomMargin: 10 * DefaultStyle.dp
|
||||||
|
//participantDevice: allDevices.me
|
||||||
|
cameraEnabled: allDevices.count <= 2
|
||||||
|
previewEnabled: true
|
||||||
|
qmlName: 'P'
|
||||||
|
|
||||||
|
MovableMouseArea {
|
||||||
|
id: previewMouseArea
|
||||||
|
anchors.fill: parent
|
||||||
|
movableArea: mainItem
|
||||||
|
margin: 10 * DefaultStyle.dp
|
||||||
|
function resetPosition(){
|
||||||
|
preview.anchors.right = mainItem.right
|
||||||
|
preview.anchors.bottom = mainItem.bottom
|
||||||
|
preview.anchors.rightMargin = previewMouseArea.margin
|
||||||
|
preview.anchors.bottomMargin = previewMouseArea.margin
|
||||||
|
}
|
||||||
|
onVisibleChanged: if(!visible){
|
||||||
|
resetPosition()
|
||||||
|
}
|
||||||
|
drag.target: preview
|
||||||
|
onDraggingChanged: if(dragging) {
|
||||||
|
preview.anchors.right = undefined
|
||||||
|
preview.anchors.bottom = undefined
|
||||||
|
}
|
||||||
|
onRequestResetPosition: resetPosition()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
Sticker {
|
||||||
|
id: preview
|
||||||
|
visible: mainItem.callState != LinphoneEnums.CallState.End
|
||||||
|
&& mainItem.callState != LinphoneEnums.CallState.Released
|
||||||
|
height: 180 * DefaultStyle.dp
|
||||||
|
width: 300 * DefaultStyle.dp
|
||||||
|
anchors.right: mainItem.right
|
||||||
|
anchors.bottom: mainItem.bottom
|
||||||
|
anchors.rightMargin: 10 * DefaultStyle.dp
|
||||||
|
anchors.bottomMargin: 10 * DefaultStyle.dp
|
||||||
|
AccountProxy{
|
||||||
|
id: accounts
|
||||||
|
}
|
||||||
|
account: accounts.defaultAccount
|
||||||
|
previewEnabled: mainItem.call.core.cameraEnabled
|
||||||
|
|
||||||
|
MovableMouseArea {
|
||||||
|
id: previewMouseArea
|
||||||
|
anchors.fill: parent
|
||||||
|
// visible: mainItem.participantCount <= 2
|
||||||
|
movableArea: mainItem
|
||||||
|
margin: 10 * DefaultStyle.dp
|
||||||
|
function resetPosition(){
|
||||||
|
preview.anchors.right = mainItem.right
|
||||||
|
preview.anchors.bottom = mainItem.bottom
|
||||||
|
preview.anchors.rightMargin = previewMouseArea.margin
|
||||||
|
preview.anchors.bottomMargin = previewMouseArea.margin
|
||||||
|
}
|
||||||
|
onVisibleChanged: if(!visible){
|
||||||
|
resetPosition()
|
||||||
|
}
|
||||||
|
drag.target: preview
|
||||||
|
onDraggingChanged: if(dragging) {
|
||||||
|
preview.anchors.right = undefined
|
||||||
|
preview.anchors.bottom = undefined
|
||||||
|
}
|
||||||
|
onRequestResetPosition: resetPosition()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
property int previousWidth
|
||||||
|
Component.onCompleted: {
|
||||||
|
previousWidth = width
|
||||||
|
}
|
||||||
|
onWidthChanged: {
|
||||||
|
if (width < previousWidth) {
|
||||||
|
previewMouseArea.updatePosition(0, 0)
|
||||||
|
} else {
|
||||||
|
previewMouseArea.updatePosition(width - previousWidth, 0)
|
||||||
|
}
|
||||||
|
previousWidth = width
|
||||||
|
}*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: mainItem
|
||||||
|
property CallModel callModel
|
||||||
|
property bool isRightReducedLayout: false
|
||||||
|
property bool isLeftReducedLayout: false
|
||||||
|
property bool cameraEnabled: true
|
||||||
|
property bool isConference: callModel && callModel.isConference
|
||||||
|
property bool isConferenceReady: isConference && callModel.conferenceModel && callModel.conferenceModel.isReady
|
||||||
|
|
||||||
|
property int participantCount: isConference ? allDevices.count + 1 : 2 // +me. allDevices==0 if !conference
|
||||||
|
|
||||||
|
property ParticipantDeviceProxyModel participantDevices : ParticipantDeviceProxyModel {
|
||||||
|
id: allDevices
|
||||||
|
callModel: mainItem.callModel
|
||||||
|
showMe: false
|
||||||
|
|
||||||
|
onConferenceCreated: cameraView.resetCamera()
|
||||||
|
}
|
||||||
|
|
||||||
|
Sticker{
|
||||||
|
id: cameraView
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.leftMargin: isRightReducedLayout || isLeftReducedLayout? 30 : 140
|
||||||
|
anchors.rightMargin: isRightReducedLayout ? 10 : 140
|
||||||
|
cameraQmlName: 'AS'
|
||||||
|
callModel: mainItem.callModel
|
||||||
|
currentDevice: isPreview
|
||||||
|
? allDevices.me
|
||||||
|
: mainItem.isConference
|
||||||
|
? allDevices.activeSpeaker
|
||||||
|
: null
|
||||||
|
deactivateCamera: !mainItem.cameraEnabled || (isPreview && callModel.pausedByUser)
|
||||||
|
? true
|
||||||
|
: mainItem.isConference
|
||||||
|
? (callModel && (callModel.pausedByUser || callModel.status === CallModel.CallStatusPaused) )
|
||||||
|
|| (!(callModel && callModel.cameraEnabled) && mainItem.participantCount == 1)
|
||||||
|
|| (currentDevice && !currentDevice.videoEnabled)// && mainItem.participantCount == 2)
|
||||||
|
|| !mainItem.isConferenceReady
|
||||||
|
: (callModel && (callModel.pausedByUser || callModel.status === CallModel.CallStatusPaused || !callModel.videoEnabled) )
|
||||||
|
|| currentDevice && !currentDevice.videoEnabled
|
||||||
|
isPreview: !preview.visible && mainItem.participantCount == 1
|
||||||
|
onIsPreviewChanged: {cameraView.resetCamera() }
|
||||||
|
isCameraFromDevice: isPreview
|
||||||
|
isPaused: isPreview && callModel.pausedByUser
|
||||||
|
? false
|
||||||
|
: mainItem.isConference
|
||||||
|
? //callModel && callModel.pausedByUser && mainItem.participantCount != 2 ||
|
||||||
|
(currentDevice && currentDevice.isPaused)
|
||||||
|
: callModel && !callModel.pausedByUser && (callModel.status === CallModel.CallStatusPaused)
|
||||||
|
|
||||||
|
quickTransition: true
|
||||||
|
showCloseButton: false
|
||||||
|
showActiveSpeakerOverlay: false // This is an active speaker. We don't need to show the indicator.
|
||||||
|
showCustomButton: false
|
||||||
|
avatarStickerBackgroundColor: isPreview ? IncallStyle.container.avatar.stickerPreviewBackgroundColor.color : IncallStyle.container.avatar.stickerBackgroundColor.color
|
||||||
|
avatarBackgroundColor: IncallStyle.container.avatar.backgroundColor.color
|
||||||
|
}
|
||||||
|
Item{// Need an item to not override Sticker internal states. States are needed for changing anchors.
|
||||||
|
id: preview
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.bottom: parent.bottom
|
||||||
|
anchors.rightMargin: 30
|
||||||
|
anchors.bottomMargin: 15
|
||||||
|
|
||||||
|
height: visible ? miniViews.cellHeight : 0
|
||||||
|
width: 16 * height / 9
|
||||||
|
|
||||||
|
visible: mainItem.isConferenceReady && allDevices.count >= 1
|
||||||
|
|| (!mainItem.isConference && mainItem.callModel && mainItem.callModel.cameraEnabled)// use videoEnabled if we want to show the preview sticker
|
||||||
|
|
||||||
|
Loader{
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.margins: 3
|
||||||
|
sourceComponent:
|
||||||
|
Sticker{
|
||||||
|
id: previewSticker
|
||||||
|
cameraQmlName: 'AS_Preview'
|
||||||
|
deactivateCamera: !mainItem.cameraEnabled || !mainItem.callModel || callModel.pausedByUser || !mainItem.callModel.cameraEnabled
|
||||||
|
currentDevice: allDevices.me
|
||||||
|
isPreview: true
|
||||||
|
callModel: mainItem.callModel
|
||||||
|
isCameraFromDevice: true
|
||||||
|
showCloseButton: false
|
||||||
|
showCustomButton: false
|
||||||
|
showAvatarBorder: true
|
||||||
|
avatarStickerBackgroundColor: IncallStyle.container.avatar.stickerPreviewBackgroundColor.color
|
||||||
|
avatarBackgroundColor: IncallStyle.container.avatar.backgroundColor.color
|
||||||
|
}
|
||||||
|
active: parent.visible
|
||||||
|
}
|
||||||
|
|
||||||
|
MovableMouseArea{
|
||||||
|
id: dragger
|
||||||
|
anchors.fill: parent
|
||||||
|
visible: mainItem.participantCount <= 2
|
||||||
|
function resetPosition(){
|
||||||
|
preview.anchors.right = mainItem.right
|
||||||
|
preview.anchors.bottom = mainItem.bottom
|
||||||
|
}
|
||||||
|
onVisibleChanged: if(!visible){
|
||||||
|
resetPosition()
|
||||||
|
}
|
||||||
|
drag.target: preview
|
||||||
|
onDraggingChanged: if(dragging){
|
||||||
|
preview.anchors.right = undefined
|
||||||
|
preview.anchors.bottom = undefined
|
||||||
|
}
|
||||||
|
onRequestResetPosition: resetPosition()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Item{
|
||||||
|
id: miniViewArea
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.top: parent.top
|
||||||
|
anchors.bottom: preview.top
|
||||||
|
anchors.rightMargin: 30
|
||||||
|
anchors.topMargin: 15
|
||||||
|
anchors.bottomMargin: 0
|
||||||
|
//---------------
|
||||||
|
width: 16 * miniViews.cellHeight / 9
|
||||||
|
visible: mainItem.isConferenceReady || !mainItem.isConference
|
||||||
|
property int heightLeft: parent.height - preview.height
|
||||||
|
onHeightLeftChanged: {Qt.callLater(miniViewArea.forceRefresh)}
|
||||||
|
function forceRefresh(){// Force a content refresh via margins. Qt is buggy when managing sizes in ListView.
|
||||||
|
++miniViewArea.anchors.topMargin
|
||||||
|
--miniViewArea.anchors.topMargin
|
||||||
|
}
|
||||||
|
|
||||||
|
ScrollableListView{
|
||||||
|
id: miniViews
|
||||||
|
property int cellHeight: 150
|
||||||
|
anchors.fill: parent
|
||||||
|
model : mainItem.isConference && mainItem.participantDevices.count > 1 ? mainItem.participantDevices : []
|
||||||
|
spacing: 0
|
||||||
|
verticalLayoutDirection: ListView.BottomToTop
|
||||||
|
fitCacheToContent: false
|
||||||
|
property int oldCount : 0// Count changed can be called without a change... (bug?). Use oldCount to avoid it.
|
||||||
|
onCountChanged: {if(oldCount != count){ oldCount = count ; Qt.callLater(miniViewArea.forceRefresh)}}
|
||||||
|
Component.onCompleted: {Qt.callLater(miniViewArea.forceRefresh)}
|
||||||
|
delegate:Item{
|
||||||
|
height: visible ? miniViews.cellHeight + 15 : 0
|
||||||
|
width: visible ? miniViews.width : 0
|
||||||
|
visible: cameraView.currentDevice != modelData
|
||||||
|
clip:false
|
||||||
|
Sticker{
|
||||||
|
id: miniView
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.topMargin: 3
|
||||||
|
anchors.leftMargin: 3
|
||||||
|
anchors.rightMargin: 3
|
||||||
|
anchors.bottomMargin: 18
|
||||||
|
cameraQmlName: 'S_'+index
|
||||||
|
deactivateCamera: (!mainItem.isConferenceReady || !mainItem.isConference)
|
||||||
|
&& (index <0 || !mainItem.cameraEnabled || (!modelData.videoEnabled) || (callModel && callModel.pausedByUser) )
|
||||||
|
currentDevice: modelData.isPreview ? null : modelData
|
||||||
|
callModel: modelData.isPreview ? null : mainItem.callModel
|
||||||
|
isCameraFromDevice: mainItem.isConference
|
||||||
|
isPaused: currentDevice && currentDevice.isPaused
|
||||||
|
showCloseButton: false
|
||||||
|
showCustomButton: false
|
||||||
|
showAvatarBorder: true
|
||||||
|
avatarStickerBackgroundColor: IncallStyle.container.avatar.stickerBackgroundColor.color
|
||||||
|
avatarBackgroundColor: IncallStyle.container.avatar.backgroundColor.color
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
293
Linphone/view/Layout/Call/CallLayout.qml
Normal file
293
Linphone/view/Layout/Call/CallLayout.qml
Normal file
|
|
@ -0,0 +1,293 @@
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Layouts
|
||||||
|
import QtQuick.Effects
|
||||||
|
import QtQml.Models
|
||||||
|
import QtQuick.Controls as Control
|
||||||
|
import Linphone
|
||||||
|
import EnumsToStringCpp 1.0
|
||||||
|
import UtilsCpp 1.0
|
||||||
|
import SettingsCpp 1.0
|
||||||
|
// =============================================================================
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: mainItem
|
||||||
|
anchors.fill: parent
|
||||||
|
|
||||||
|
property CallGui call
|
||||||
|
property bool callTerminatedByUser: false
|
||||||
|
readonly property var callState: call && call.core.state || undefined
|
||||||
|
onCallStateChanged: if (callState === LinphoneEnums.CallState.End) {
|
||||||
|
callTerminatedText.visible = true
|
||||||
|
}else if( callState === LinphoneEnums.CallState.Error) {
|
||||||
|
centerLayout.currentIndex = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
Text {
|
||||||
|
id: callTerminatedText
|
||||||
|
visible: false
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
anchors.top: parent.top
|
||||||
|
anchors.topMargin: 25 * DefaultStyle.dp
|
||||||
|
text: mainItem.callTerminatedByUser ? qsTr("Vous avez terminé l'appel") : qsTr("Votre correspondant a terminé l'appel")
|
||||||
|
color: DefaultStyle.grey_0
|
||||||
|
z: 1
|
||||||
|
font {
|
||||||
|
pixelSize: 22 * DefaultStyle.dp
|
||||||
|
weight: 300 * DefaultStyle.dp
|
||||||
|
}
|
||||||
|
}
|
||||||
|
StackLayout {
|
||||||
|
id: centerLayout
|
||||||
|
currentIndex: 0
|
||||||
|
anchors.fill: parent
|
||||||
|
Loader{
|
||||||
|
id: callLayout
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.fillHeight: true
|
||||||
|
sourceComponent:ActiveSpeakerLayout{
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.fillHeight: true
|
||||||
|
call: mainItem.call
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ColumnLayout {
|
||||||
|
id: userNotFoundLayout
|
||||||
|
Layout.preferredWidth: parent.width
|
||||||
|
Layout.preferredHeight: parent.height
|
||||||
|
Layout.alignment: Qt.AlignCenter
|
||||||
|
Text {
|
||||||
|
text: qsTr(mainItem.call.core.lastErrorMessage)
|
||||||
|
Layout.alignment: Qt.AlignCenter
|
||||||
|
color: DefaultStyle.grey_0
|
||||||
|
font.pixelSize: 40 * DefaultStyle.dp
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
Sticker {
|
||||||
|
id: preview
|
||||||
|
visible: mainItem.callState != LinphoneEnums.CallState.End
|
||||||
|
&& mainItem.callState != LinphoneEnums.CallState.Released
|
||||||
|
height: 180 * DefaultStyle.dp
|
||||||
|
width: 300 * DefaultStyle.dp
|
||||||
|
anchors.right: mainItem.right
|
||||||
|
anchors.bottom: mainItem.bottom
|
||||||
|
anchors.rightMargin: 10 * DefaultStyle.dp
|
||||||
|
anchors.bottomMargin: 10 * DefaultStyle.dp
|
||||||
|
AccountProxy{
|
||||||
|
id: accounts
|
||||||
|
}
|
||||||
|
account: accounts.defaultAccount
|
||||||
|
previewEnabled: mainItem.call.core.cameraEnabled
|
||||||
|
|
||||||
|
MovableMouseArea {
|
||||||
|
id: previewMouseArea
|
||||||
|
anchors.fill: parent
|
||||||
|
// visible: mainItem.participantCount <= 2
|
||||||
|
movableArea: mainItem
|
||||||
|
margin: 10 * DefaultStyle.dp
|
||||||
|
function resetPosition(){
|
||||||
|
preview.anchors.right = mainItem.right
|
||||||
|
preview.anchors.bottom = mainItem.bottom
|
||||||
|
preview.anchors.rightMargin = previewMouseArea.margin
|
||||||
|
preview.anchors.bottomMargin = previewMouseArea.margin
|
||||||
|
}
|
||||||
|
onVisibleChanged: if(!visible){
|
||||||
|
resetPosition()
|
||||||
|
}
|
||||||
|
drag.target: preview
|
||||||
|
onDraggingChanged: if(dragging) {
|
||||||
|
preview.anchors.right = undefined
|
||||||
|
preview.anchors.bottom = undefined
|
||||||
|
}
|
||||||
|
onRequestResetPosition: resetPosition()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
property int previousWidth
|
||||||
|
Component.onCompleted: {
|
||||||
|
previousWidth = width
|
||||||
|
}
|
||||||
|
onWidthChanged: {
|
||||||
|
if (width < previousWidth) {
|
||||||
|
previewMouseArea.updatePosition(0, 0)
|
||||||
|
} else {
|
||||||
|
previewMouseArea.updatePosition(width - previousWidth, 0)
|
||||||
|
}
|
||||||
|
previousWidth = width
|
||||||
|
}*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: mainItem
|
||||||
|
property CallModel callModel
|
||||||
|
property bool isRightReducedLayout: false
|
||||||
|
property bool isLeftReducedLayout: false
|
||||||
|
property bool cameraEnabled: true
|
||||||
|
property bool isConference: callModel && callModel.isConference
|
||||||
|
property bool isConferenceReady: isConference && callModel.conferenceModel && callModel.conferenceModel.isReady
|
||||||
|
|
||||||
|
property int participantCount: isConference ? allDevices.count + 1 : 2 // +me. allDevices==0 if !conference
|
||||||
|
|
||||||
|
property ParticipantDeviceProxyModel participantDevices : ParticipantDeviceProxyModel {
|
||||||
|
id: allDevices
|
||||||
|
callModel: mainItem.callModel
|
||||||
|
showMe: false
|
||||||
|
|
||||||
|
onConferenceCreated: cameraView.resetCamera()
|
||||||
|
}
|
||||||
|
|
||||||
|
Sticker{
|
||||||
|
id: cameraView
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.leftMargin: isRightReducedLayout || isLeftReducedLayout? 30 : 140
|
||||||
|
anchors.rightMargin: isRightReducedLayout ? 10 : 140
|
||||||
|
cameraQmlName: 'AS'
|
||||||
|
callModel: mainItem.callModel
|
||||||
|
currentDevice: isPreview
|
||||||
|
? allDevices.me
|
||||||
|
: mainItem.isConference
|
||||||
|
? allDevices.activeSpeaker
|
||||||
|
: null
|
||||||
|
deactivateCamera: !mainItem.cameraEnabled || (isPreview && callModel.pausedByUser)
|
||||||
|
? true
|
||||||
|
: mainItem.isConference
|
||||||
|
? (callModel && (callModel.pausedByUser || callModel.status === CallModel.CallStatusPaused) )
|
||||||
|
|| (!(callModel && callModel.cameraEnabled) && mainItem.participantCount == 1)
|
||||||
|
|| (currentDevice && !currentDevice.videoEnabled)// && mainItem.participantCount == 2)
|
||||||
|
|| !mainItem.isConferenceReady
|
||||||
|
: (callModel && (callModel.pausedByUser || callModel.status === CallModel.CallStatusPaused || !callModel.videoEnabled) )
|
||||||
|
|| currentDevice && !currentDevice.videoEnabled
|
||||||
|
isPreview: !preview.visible && mainItem.participantCount == 1
|
||||||
|
onIsPreviewChanged: {cameraView.resetCamera() }
|
||||||
|
isCameraFromDevice: isPreview
|
||||||
|
isPaused: isPreview && callModel.pausedByUser
|
||||||
|
? false
|
||||||
|
: mainItem.isConference
|
||||||
|
? //callModel && callModel.pausedByUser && mainItem.participantCount != 2 ||
|
||||||
|
(currentDevice && currentDevice.isPaused)
|
||||||
|
: callModel && !callModel.pausedByUser && (callModel.status === CallModel.CallStatusPaused)
|
||||||
|
|
||||||
|
quickTransition: true
|
||||||
|
showCloseButton: false
|
||||||
|
showActiveSpeakerOverlay: false // This is an active speaker. We don't need to show the indicator.
|
||||||
|
showCustomButton: false
|
||||||
|
avatarStickerBackgroundColor: isPreview ? IncallStyle.container.avatar.stickerPreviewBackgroundColor.color : IncallStyle.container.avatar.stickerBackgroundColor.color
|
||||||
|
avatarBackgroundColor: IncallStyle.container.avatar.backgroundColor.color
|
||||||
|
}
|
||||||
|
Item{// Need an item to not override Sticker internal states. States are needed for changing anchors.
|
||||||
|
id: preview
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.bottom: parent.bottom
|
||||||
|
anchors.rightMargin: 30
|
||||||
|
anchors.bottomMargin: 15
|
||||||
|
|
||||||
|
height: visible ? miniViews.cellHeight : 0
|
||||||
|
width: 16 * height / 9
|
||||||
|
|
||||||
|
visible: mainItem.isConferenceReady && allDevices.count >= 1
|
||||||
|
|| (!mainItem.isConference && mainItem.callModel && mainItem.callModel.cameraEnabled)// use videoEnabled if we want to show the preview sticker
|
||||||
|
|
||||||
|
Loader{
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.margins: 3
|
||||||
|
sourceComponent:
|
||||||
|
Sticker{
|
||||||
|
id: previewSticker
|
||||||
|
cameraQmlName: 'AS_Preview'
|
||||||
|
deactivateCamera: !mainItem.cameraEnabled || !mainItem.callModel || callModel.pausedByUser || !mainItem.callModel.cameraEnabled
|
||||||
|
currentDevice: allDevices.me
|
||||||
|
isPreview: true
|
||||||
|
callModel: mainItem.callModel
|
||||||
|
isCameraFromDevice: true
|
||||||
|
showCloseButton: false
|
||||||
|
showCustomButton: false
|
||||||
|
showAvatarBorder: true
|
||||||
|
avatarStickerBackgroundColor: IncallStyle.container.avatar.stickerPreviewBackgroundColor.color
|
||||||
|
avatarBackgroundColor: IncallStyle.container.avatar.backgroundColor.color
|
||||||
|
}
|
||||||
|
active: parent.visible
|
||||||
|
}
|
||||||
|
|
||||||
|
MovableMouseArea{
|
||||||
|
id: dragger
|
||||||
|
anchors.fill: parent
|
||||||
|
visible: mainItem.participantCount <= 2
|
||||||
|
function resetPosition(){
|
||||||
|
preview.anchors.right = mainItem.right
|
||||||
|
preview.anchors.bottom = mainItem.bottom
|
||||||
|
}
|
||||||
|
onVisibleChanged: if(!visible){
|
||||||
|
resetPosition()
|
||||||
|
}
|
||||||
|
drag.target: preview
|
||||||
|
onDraggingChanged: if(dragging){
|
||||||
|
preview.anchors.right = undefined
|
||||||
|
preview.anchors.bottom = undefined
|
||||||
|
}
|
||||||
|
onRequestResetPosition: resetPosition()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Item{
|
||||||
|
id: miniViewArea
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.top: parent.top
|
||||||
|
anchors.bottom: preview.top
|
||||||
|
anchors.rightMargin: 30
|
||||||
|
anchors.topMargin: 15
|
||||||
|
anchors.bottomMargin: 0
|
||||||
|
//---------------
|
||||||
|
width: 16 * miniViews.cellHeight / 9
|
||||||
|
visible: mainItem.isConferenceReady || !mainItem.isConference
|
||||||
|
property int heightLeft: parent.height - preview.height
|
||||||
|
onHeightLeftChanged: {Qt.callLater(miniViewArea.forceRefresh)}
|
||||||
|
function forceRefresh(){// Force a content refresh via margins. Qt is buggy when managing sizes in ListView.
|
||||||
|
++miniViewArea.anchors.topMargin
|
||||||
|
--miniViewArea.anchors.topMargin
|
||||||
|
}
|
||||||
|
|
||||||
|
ScrollableListView{
|
||||||
|
id: miniViews
|
||||||
|
property int cellHeight: 150
|
||||||
|
anchors.fill: parent
|
||||||
|
model : mainItem.isConference && mainItem.participantDevices.count > 1 ? mainItem.participantDevices : []
|
||||||
|
spacing: 0
|
||||||
|
verticalLayoutDirection: ListView.BottomToTop
|
||||||
|
fitCacheToContent: false
|
||||||
|
property int oldCount : 0// Count changed can be called without a change... (bug?). Use oldCount to avoid it.
|
||||||
|
onCountChanged: {if(oldCount != count){ oldCount = count ; Qt.callLater(miniViewArea.forceRefresh)}}
|
||||||
|
Component.onCompleted: {Qt.callLater(miniViewArea.forceRefresh)}
|
||||||
|
delegate:Item{
|
||||||
|
height: visible ? miniViews.cellHeight + 15 : 0
|
||||||
|
width: visible ? miniViews.width : 0
|
||||||
|
visible: cameraView.currentDevice != modelData
|
||||||
|
clip:false
|
||||||
|
Sticker{
|
||||||
|
id: miniView
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.topMargin: 3
|
||||||
|
anchors.leftMargin: 3
|
||||||
|
anchors.rightMargin: 3
|
||||||
|
anchors.bottomMargin: 18
|
||||||
|
cameraQmlName: 'S_'+index
|
||||||
|
deactivateCamera: (!mainItem.isConferenceReady || !mainItem.isConference)
|
||||||
|
&& (index <0 || !mainItem.cameraEnabled || (!modelData.videoEnabled) || (callModel && callModel.pausedByUser) )
|
||||||
|
currentDevice: modelData.isPreview ? null : modelData
|
||||||
|
callModel: modelData.isPreview ? null : mainItem.callModel
|
||||||
|
isCameraFromDevice: mainItem.isConference
|
||||||
|
isPaused: currentDevice && currentDevice.isPaused
|
||||||
|
showCloseButton: false
|
||||||
|
showCustomButton: false
|
||||||
|
showAvatarBorder: true
|
||||||
|
avatarStickerBackgroundColor: IncallStyle.container.avatar.stickerBackgroundColor.color
|
||||||
|
avatarBackgroundColor: IncallStyle.container.avatar.backgroundColor.color
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
67
Linphone/view/Layout/Call/GridLayout.qml
Normal file
67
Linphone/view/Layout/Call/GridLayout.qml
Normal file
|
|
@ -0,0 +1,67 @@
|
||||||
|
import QtQuick 2.7
|
||||||
|
import QtQuick.Layouts 1.3
|
||||||
|
import QtQml.Models 2.12
|
||||||
|
import QtGraphicalEffects 1.12
|
||||||
|
|
||||||
|
import Common 1.0
|
||||||
|
import Common.Styles 1.0
|
||||||
|
import Linphone 1.0
|
||||||
|
import LinphoneEnums 1.0
|
||||||
|
|
||||||
|
import UtilsCpp 1.0
|
||||||
|
|
||||||
|
import App.Styles 1.0
|
||||||
|
|
||||||
|
import ConstantsCpp 1.0
|
||||||
|
// Temp
|
||||||
|
import 'Incall.js' as Logic
|
||||||
|
import 'qrc:/ui/scripts/Utils/utils.js' as Utils
|
||||||
|
|
||||||
|
// =============================================================================
|
||||||
|
|
||||||
|
Mosaic {
|
||||||
|
id: grid
|
||||||
|
property alias callModel: participantDevices.callModel
|
||||||
|
property bool cameraEnabled: true
|
||||||
|
property int participantCount: gridModel.count
|
||||||
|
|
||||||
|
// On grid view, we limit the quality if there are enough participants// The vga mode has been activated from the factory rc
|
||||||
|
//onParticipantCountChanged: participantCount > ConstantsCpp.maxMosaicParticipants ? SettingsModel.setLimitedMosaicQuality() : SettingsModel.setHighMosaicQuality()
|
||||||
|
delegateModel: DelegateModel{
|
||||||
|
id: gridModel
|
||||||
|
property ParticipantDeviceProxyModel participantDevices : ParticipantDeviceProxyModel {
|
||||||
|
id: participantDevices
|
||||||
|
showMe: true
|
||||||
|
}
|
||||||
|
model: participantDevices
|
||||||
|
delegate: Item{
|
||||||
|
id: avatarCell
|
||||||
|
property ParticipantDeviceModel currentDevice: gridModel.participantDevices.getAt(index)
|
||||||
|
onCurrentDeviceChanged: {
|
||||||
|
if(index < 0) cameraView.enabled = false // this is a delegate destruction. We need to stop camera before Qt change its currentDevice (and then, let CameraView to delete wrong renderer)
|
||||||
|
}
|
||||||
|
|
||||||
|
height: grid.cellHeight - 10
|
||||||
|
width: grid.cellWidth - 10
|
||||||
|
|
||||||
|
Sticker{
|
||||||
|
id: cameraView
|
||||||
|
anchors.fill: parent
|
||||||
|
|
||||||
|
cameraQmlName: 'G_'+index
|
||||||
|
callModel: index >= 0 ? participantDevices.callModel : null // do this before to prioritize changing call on remove
|
||||||
|
deactivateCamera: index <0 || !grid.cameraEnabled || grid.callModel.pausedByUser
|
||||||
|
currentDevice: gridModel.participantDevices.getAt(index)
|
||||||
|
|
||||||
|
isCameraFromDevice: true
|
||||||
|
isPaused: !isPreview && avatarCell.currentDevice && avatarCell.currentDevice.isPaused
|
||||||
|
showCloseButton: false
|
||||||
|
showCustomButton: false
|
||||||
|
avatarStickerBackgroundColor: isPreview? IncallStyle.container.avatar.stickerPreviewBackgroundColor.color : IncallStyle.container.avatar.stickerBackgroundColor.color
|
||||||
|
avatarBackgroundColor: IncallStyle.container.avatar.backgroundColor.color
|
||||||
|
|
||||||
|
//onCloseRequested: participantDevices.showMe = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue