mirror of
https://gitlab.linphone.org/BC/public/linphone-desktop.git
synced 2026-01-17 03:18:07 +00:00
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/notifier/Notifier.hpp"
|
||||
#include "core/participant/ParticipantDeviceCore.hpp"
|
||||
#include "core/participant/ParticipantDeviceProxy.hpp"
|
||||
#include "core/participant/ParticipantGui.hpp"
|
||||
#include "core/participant/ParticipantProxy.hpp"
|
||||
#include "core/phone-number/PhoneNumber.hpp"
|
||||
|
|
@ -217,6 +218,10 @@ void App::initCppInterfaces() {
|
|||
qmlRegisterType<FPSCounter>(Constants::MainQmlUri, 1, 0, "FPSCounter");
|
||||
|
||||
qmlRegisterType<TimeZoneProxy>(Constants::MainQmlUri, 1, 0, "TimeZoneProxy");
|
||||
|
||||
qmlRegisterType<ParticipantDeviceGui>(Constants::MainQmlUri, 1, 0, "ParticipantDeviceGui");
|
||||
qmlRegisterType<ParticipantDeviceProxy>(Constants::MainQmlUri, 1, 0, "ParticipantDeviceProxy");
|
||||
|
||||
LinphoneEnums::registerMetaTypes();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -50,8 +50,9 @@ list(APPEND _LINPHONEAPP_SOURCES
|
|||
core/participant/ParticipantCore.cpp
|
||||
core/participant/ParticipantGui.cpp
|
||||
core/participant/ParticipantDeviceCore.cpp
|
||||
# core/participant/ParticipantDeviceList.cpp
|
||||
# core/participant/ParticipantDeviceProxy.cpp
|
||||
core/participant/ParticipantDeviceGui.cpp
|
||||
core/participant/ParticipantDeviceList.cpp
|
||||
core/participant/ParticipantDeviceProxy.cpp
|
||||
core/participant/ParticipantList.cpp
|
||||
core/participant/ParticipantProxy.cpp
|
||||
)
|
||||
|
|
|
|||
|
|
@ -380,10 +380,15 @@ ConferenceGui *CallCore::getConferenceGui() const {
|
|||
return mConference ? new ConferenceGui(mConference) : nullptr;
|
||||
}
|
||||
|
||||
QSharedPointer<ConferenceCore> CallCore::getConferenceCore() const {
|
||||
return mConference;
|
||||
}
|
||||
|
||||
void CallCore::setConference(const QSharedPointer<ConferenceCore> &conference) {
|
||||
if (mConference != conference) {
|
||||
mConference = conference;
|
||||
mIsConference = (mConference != nullptr);
|
||||
qDebug() << "[CallCore] Set conference : " << mConference;
|
||||
emit conferenceChanged();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -103,6 +103,7 @@ public:
|
|||
|
||||
bool isConference() const;
|
||||
ConferenceGui *getConferenceGui() const;
|
||||
QSharedPointer<ConferenceCore> getConferenceCore() const;
|
||||
void setConference(const QSharedPointer<ConferenceCore> &conference);
|
||||
|
||||
QString getLocalSas();
|
||||
|
|
|
|||
|
|
@ -24,7 +24,6 @@
|
|||
DEFINE_ABSTRACT_OBJECT(CallGui)
|
||||
|
||||
CallGui::CallGui(QSharedPointer<CallCore> core) {
|
||||
qDebug() << "[CallGui] new" << this;
|
||||
App::getInstance()->mEngine->setObjectOwnership(this, QQmlEngine::JavaScriptOwnership);
|
||||
mCore = core;
|
||||
if (isInLinphoneThread()) moveToThread(App::getInstance()->thread());
|
||||
|
|
@ -32,7 +31,6 @@ CallGui::CallGui(QSharedPointer<CallCore> core) {
|
|||
|
||||
CallGui::~CallGui() {
|
||||
mustBeInMainThread("~" + getClassName());
|
||||
qDebug() << "[CallGui] delete" << this;
|
||||
}
|
||||
|
||||
CallCore *CallGui::getCore() const {
|
||||
|
|
|
|||
|
|
@ -28,9 +28,14 @@
|
|||
#include "core/App.hpp"
|
||||
#include "core/call/CallCore.hpp"
|
||||
#include "core/call/CallGui.hpp"
|
||||
#include "core/participant/ParticipantDeviceCore.hpp"
|
||||
#include "core/participant/ParticipantDeviceGui.hpp"
|
||||
|
||||
DEFINE_ABSTRACT_OBJECT(CameraGui)
|
||||
|
||||
QMutex CameraGui::mPreviewCounterMutex;
|
||||
int CameraGui::mPreviewCounter = 0;
|
||||
|
||||
// =============================================================================
|
||||
CameraGui::CameraGui(QQuickItem *parent) : QQuickFramebufferObject(parent) {
|
||||
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)
|
||||
CameraGui::~CameraGui() {
|
||||
mustBeInMainThread("~" + getClassName());
|
||||
mRefreshTimer.stop();
|
||||
App::postModelSync([this]() { CoreModel::getInstance()->getCore()->enableVideoPreview(false); });
|
||||
}
|
||||
|
||||
QQuickFramebufferObject::Renderer *CameraGui::createRenderer() const {
|
||||
QQuickFramebufferObject::Renderer *renderer = NULL;
|
||||
// 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: {
|
||||
}
|
||||
}
|
||||
auto renderer = createRenderer(false);
|
||||
if (!renderer) {
|
||||
qInfo() << "[Camera] (" << mQmlName << ") Setting Camera to Dummy, " << getSourceLocation();
|
||||
QTimer::singleShot(1, this, &CameraGui::isNotReady);
|
||||
renderer = new CameraDummy(); // Used to fill a renderer to avoid pushing a NULL.
|
||||
QTimer::singleShot(1000, this, &CameraGui::requestNewRenderer);
|
||||
|
|
@ -84,6 +65,88 @@ QQuickFramebufferObject::Renderer *CameraGui::createRenderer() const {
|
|||
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 {
|
||||
return mIsReady;
|
||||
}
|
||||
|
|
@ -100,6 +163,21 @@ void CameraGui::isNotReady() {
|
|||
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 {
|
||||
return mCallGui;
|
||||
}
|
||||
|
|
@ -107,11 +185,70 @@ CallGui *CameraGui::getCallGui() const {
|
|||
void CameraGui::setCallGui(CallGui *callGui) {
|
||||
if (mCallGui != callGui) {
|
||||
mCallGui = callGui;
|
||||
qDebug() << "Set Call " << 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 {
|
||||
if (mCallGui != nullptr) return Call;
|
||||
else return CorePreview;
|
||||
return mWindowIdLocation;
|
||||
}
|
||||
|
||||
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 <QTimer>
|
||||
|
||||
#include "core/participant/ParticipantDeviceGui.hpp"
|
||||
// =============================================================================
|
||||
|
||||
class CallGui;
|
||||
|
||||
class CameraGui : public QQuickFramebufferObject, public AbstractObject {
|
||||
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:
|
||||
CameraGui(QQuickItem *parent = Q_NULLPTR);
|
||||
virtual ~CameraGui();
|
||||
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;
|
||||
void setIsReady(bool isReady);
|
||||
void isReady();
|
||||
void isNotReady();
|
||||
bool getIsPreview() const;
|
||||
void setIsPreview(bool status);
|
||||
|
||||
CallGui *getCallGui() const;
|
||||
void setCallGui(CallGui *callGui);
|
||||
|
||||
typedef enum { None = -1, CorePreview = 0, Call, Device, Player, Core } WindowIdLocation;
|
||||
ParticipantDeviceGui *getParticipantDeviceGui() const;
|
||||
void setParticipantDeviceGui(ParticipantDeviceGui *participantDeviceGui);
|
||||
QString getQmlName() const;
|
||||
void setQmlName(const QString &name);
|
||||
WindowIdLocation getSourceLocation() const;
|
||||
void setWindowIdLocation(const WindowIdLocation &location);
|
||||
|
||||
void activatePreview();
|
||||
void deactivatePreview();
|
||||
void updateWindowIdLocation();
|
||||
void removeParticipantDeviceModel();
|
||||
void removeCallModel();
|
||||
void removeLinphonePlayer();
|
||||
|
||||
signals:
|
||||
void requestNewRenderer();
|
||||
void isReadyChanged(bool isReady);
|
||||
void callGuiChanged(CallGui *callGui);
|
||||
void isPreviewChanged(bool isPreview);
|
||||
void isReadyChanged();
|
||||
void participantDeviceGuiChanged(ParticipantDeviceGui *participantDeviceGui);
|
||||
void videoDefinitionChanged();
|
||||
// void linphonePlayerChanged(SoundPlayer * linphonePlayer);
|
||||
void qmlNameChanged();
|
||||
|
||||
private:
|
||||
bool mIsPreview = false;
|
||||
bool mIsReady = false;
|
||||
QTimer mRefreshTimer;
|
||||
int mMaxFps = 30;
|
||||
QVariantMap mLastVideoDefinition;
|
||||
QTimer mLastVideoDefinitionChecker;
|
||||
CallGui *mCallGui = nullptr;
|
||||
ParticipantDeviceGui *mParticipantDeviceGui = nullptr;
|
||||
QString mQmlName;
|
||||
|
||||
WindowIdLocation mWindowIdLocation = None;
|
||||
mutable bool mIsWindowIdSet = false;
|
||||
|
||||
DECLARE_ABSTRACT_OBJECT
|
||||
};
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@
|
|||
|
||||
#include "ConferenceCore.hpp"
|
||||
#include "core/App.hpp"
|
||||
#include "model/conference/ConferenceModel.hpp"
|
||||
#include "tool/Utils.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);
|
||||
// Should be call from model Thread
|
||||
mustBeInLinphoneThread(getClassName());
|
||||
mConferenceModel = ConferenceModel::create(conference);
|
||||
mSubject = Utils::coreStringToAppString(conference->getSubject());
|
||||
}
|
||||
ConferenceCore::~ConferenceCore() {
|
||||
|
|
@ -45,6 +47,12 @@ ConferenceCore::~ConferenceCore() {
|
|||
void ConferenceCore::setSelf(QSharedPointer<ConferenceCore> me) {
|
||||
mConferenceModelConnection = QSharedPointer<SafeConnection<ConferenceCore, ConferenceModel>>(
|
||||
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->invokeToModel([this, isMuted]() { mCallModel->setMicrophoneMuted(isMuted); });
|
||||
// });
|
||||
|
|
@ -79,3 +87,23 @@ void ConferenceCore::setIsReady(bool state) {
|
|||
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_
|
||||
#define CONFERENCE_CORE_H_
|
||||
|
||||
#include "core/participant/ParticipantDeviceCore.hpp"
|
||||
#include "core/participant/ParticipantDeviceGui.hpp"
|
||||
#include "model/conference/ConferenceModel.hpp"
|
||||
#include "tool/LinphoneEnums.hpp"
|
||||
#include "tool/thread/SafeConnection.hpp"
|
||||
|
|
@ -38,6 +40,7 @@ public:
|
|||
// Q_PROPERTY(ParticipantModel* localParticipant READ getLocalParticipant NOTIFY localParticipantChanged)
|
||||
Q_PROPERTY(bool isReady MEMBER mIsReady WRITE setIsReady NOTIFY isReadyChanged)
|
||||
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
|
||||
static QSharedPointer<ConferenceCore> create(const std::shared_ptr<linphone::Conference> &conference);
|
||||
|
|
@ -55,19 +58,26 @@ public:
|
|||
// std::list<std::shared_ptr<linphone::Participant>>
|
||||
// getParticipantList() const; // SDK exclude me. We want to get ALL participants.
|
||||
int getParticipantDeviceCount() const;
|
||||
ParticipantDeviceCore *getActiveSpeaker() const;
|
||||
ParticipantDeviceGui *getActiveSpeakerGui() const;
|
||||
void setActiveSpeaker(const QSharedPointer<ParticipantDeviceCore> &device);
|
||||
|
||||
void setIsReady(bool state);
|
||||
|
||||
std::shared_ptr<ConferenceModel> getModel() const;
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
signals:
|
||||
void subjectChanged();
|
||||
void isReadyChanged();
|
||||
void participantDeviceCountChanged();
|
||||
void activeSpeakerChanged();
|
||||
|
||||
private:
|
||||
QSharedPointer<SafeConnection<ConferenceCore, ConferenceModel>> mConferenceModelConnection;
|
||||
std::shared_ptr<ConferenceModel> mConferenceModel;
|
||||
QSharedPointer<ParticipantDeviceCore> mActiveSpeaker;
|
||||
|
||||
bool mIsReady = false;
|
||||
QString mSubject;
|
||||
|
|
|
|||
|
|
@ -25,13 +25,11 @@
|
|||
DEFINE_ABSTRACT_OBJECT(ConferenceGui)
|
||||
|
||||
ConferenceGui::ConferenceGui() {
|
||||
qDebug() << "[ConferenceGui] new" << this;
|
||||
mCore = ConferenceCore::create(nullptr);
|
||||
App::getInstance()->mEngine->setObjectOwnership(this, QQmlEngine::JavaScriptOwnership);
|
||||
if (isInLinphoneThread()) moveToThread(App::getInstance()->thread());
|
||||
}
|
||||
ConferenceGui::ConferenceGui(QSharedPointer<ConferenceCore> core) {
|
||||
qDebug() << "[ConferenceGui] new" << this;
|
||||
App::getInstance()->mEngine->setObjectOwnership(this, QQmlEngine::JavaScriptOwnership);
|
||||
mCore = core;
|
||||
if (isInLinphoneThread()) moveToThread(App::getInstance()->thread());
|
||||
|
|
|
|||
|
|
@ -25,13 +25,11 @@
|
|||
DEFINE_ABSTRACT_OBJECT(ConferenceInfoGui)
|
||||
|
||||
ConferenceInfoGui::ConferenceInfoGui() {
|
||||
// qDebug() << "[ConferenceInfoGui] new" << this;
|
||||
mCore = ConferenceInfoCore::create(nullptr);
|
||||
App::getInstance()->mEngine->setObjectOwnership(this, QQmlEngine::JavaScriptOwnership);
|
||||
if (isInLinphoneThread()) moveToThread(App::getInstance()->thread());
|
||||
}
|
||||
ConferenceInfoGui::ConferenceInfoGui(QSharedPointer<ConferenceInfoCore> core) {
|
||||
// qDebug() << "[ConferenceInfoGui] new" << this;
|
||||
App::getInstance()->mEngine->setObjectOwnership(this, QQmlEngine::JavaScriptOwnership);
|
||||
mCore = core;
|
||||
if (isInLinphoneThread()) moveToThread(App::getInstance()->thread());
|
||||
|
|
@ -39,7 +37,6 @@ ConferenceInfoGui::ConferenceInfoGui(QSharedPointer<ConferenceInfoCore> core) {
|
|||
|
||||
ConferenceInfoGui::~ConferenceInfoGui() {
|
||||
mustBeInMainThread("~" + getClassName());
|
||||
// qDebug() << "[ConferenceInfoGui] delete" << this;
|
||||
}
|
||||
|
||||
ConferenceInfoCore *ConferenceInfoGui::getCore() const {
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@
|
|||
|
||||
#include "ParticipantDeviceCore.hpp"
|
||||
#include "core/App.hpp"
|
||||
#include "model/tool/ToolModel.hpp"
|
||||
#include "tool/Utils.hpp"
|
||||
#include <QQmlApplicationEngine>
|
||||
|
||||
|
|
@ -31,7 +32,7 @@ ParticipantDeviceCore::create(std::shared_ptr<linphone::ParticipantDevice> devic
|
|||
QSharedPointer<ParticipantDeviceCore>(new ParticipantDeviceCore(device, isMe, parent), &QObject::deleteLater);
|
||||
sharedPointer->setSelf(sharedPointer);
|
||||
sharedPointer->moveToThread(App::getInstance()->thread());
|
||||
return nullptr;
|
||||
return sharedPointer;
|
||||
}
|
||||
|
||||
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->setSelf(mParticipantDeviceModel);
|
||||
mState = LinphoneEnums::fromLinphone(device->getState());
|
||||
qDebug() << "Address = " << Utils::coreStringToAppString(device->getAddress()->asStringUriOnly());
|
||||
mIsLocal = ToolModel::findAccount(device->getAddress()) != nullptr; // TODO set local
|
||||
// mCall = callModel;
|
||||
// if (mCall) connect(mCall, &CallModel::statusChanged, this, &ParticipantDeviceCore::onCallStatusChanged);
|
||||
mIsVideoEnabled = mParticipantDeviceModel->isVideoEnabled();
|
||||
|
|
@ -182,6 +185,10 @@ bool ParticipantDeviceCore::isLocal() const {
|
|||
return mIsLocal;
|
||||
}
|
||||
|
||||
std::shared_ptr<ParticipantDeviceModel> ParticipantDeviceCore::getModel() const {
|
||||
return mParticipantDeviceModel;
|
||||
}
|
||||
|
||||
// void ParticipantDeviceCore::updateIsLocal() {
|
||||
// auto deviceAddress = mParticipantDeviceModel->getAddress();
|
||||
// auto callAddress = mCall->getConferenceSharedModel()->getConference()->getMe()->getAddress();
|
||||
|
|
@ -247,4 +254,4 @@ void ParticipantDeviceCore::onStreamAvailabilityChanged(
|
|||
const std::shared_ptr<linphone::ParticipantDevice> &participantDevice,
|
||||
bool available,
|
||||
linphone::StreamType streamType) {
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -74,6 +74,7 @@ public:
|
|||
LinphoneEnums::ParticipantDeviceState getState() const;
|
||||
|
||||
std::shared_ptr<linphone::ParticipantDevice> getDevice();
|
||||
std::shared_ptr<ParticipantDeviceModel> getModel() const;
|
||||
|
||||
void setPaused(bool paused);
|
||||
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
|
||||
* (see https://www.linphone.org).
|
||||
|
|
@ -20,21 +20,14 @@
|
|||
|
||||
#include "ParticipantDeviceList.hpp"
|
||||
#include "core/App.hpp"
|
||||
#include "core/participant/ParticipantCore.hpp"
|
||||
#include "core/participant/ParticipantDeviceCore.hpp"
|
||||
#include "core/participant/ParticipantDeviceGui.hpp"
|
||||
|
||||
#include <QQmlApplicationEngine>
|
||||
#include <algorithm>
|
||||
|
||||
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() {
|
||||
auto model = QSharedPointer<ParticipantDeviceList>(new ParticipantDeviceList(), &QObject::deleteLater);
|
||||
model->moveToThread(App::getInstance()->thread());
|
||||
|
|
@ -42,302 +35,112 @@ QSharedPointer<ParticipantDeviceList> ParticipantDeviceList::create() {
|
|||
return model;
|
||||
}
|
||||
|
||||
ParticipantDeviceList::ParticipantDeviceList(const std::shared_ptr<linphone::Participant> &participant, QObject *parent)
|
||||
: ListProxy(parent) {
|
||||
std::list<std::shared_ptr<linphone::ParticipantDevice>> devices = participant->getDevices();
|
||||
for (auto device : devices) {
|
||||
auto deviceModel = ParticipantDeviceCore::create(device, isMe(device));
|
||||
// connect(this, &ParticipantDeviceList::securityLevelChanged, deviceModel.get(),
|
||||
// &ParticipantDeviceCore::onSecurityLevelChanged);
|
||||
connect(deviceModel.get(), &ParticipantDeviceCore::isSpeakingChanged, this,
|
||||
&ParticipantDeviceList::onParticipantDeviceSpeaking);
|
||||
mList << deviceModel;
|
||||
}
|
||||
mInitialized = true;
|
||||
QSharedPointer<ParticipantDeviceList>
|
||||
ParticipantDeviceList::create(const std::shared_ptr<ConferenceModel> &conferenceModel) {
|
||||
auto model = create();
|
||||
model->setConferenceModel(conferenceModel);
|
||||
return model;
|
||||
}
|
||||
|
||||
ParticipantDeviceList::ParticipantDeviceList(QObject *parent) {
|
||||
mustBeInMainThread(getClassName());
|
||||
ParticipantDeviceList::ParticipantDeviceList() {
|
||||
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() {
|
||||
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::initConferenceModel() {
|
||||
// if (!mInitialized && mCallModel) {
|
||||
// auto conferenceModel = mCallModel->getConferenceSharedModel();
|
||||
// if (conferenceModel) {
|
||||
// updateDevices(conferenceModel->getConference()->getMe()->getDevices(), true);
|
||||
// updateDevices(conferenceModel->getConference()->getParticipantDeviceList(), false);
|
||||
|
||||
// qDebug() << "Conference have " << mList.size() << " devices";
|
||||
// connect(conferenceModel.get(), &ConferenceModel::activeSpeakerParticipantDevice, this,
|
||||
// &ParticipantDeviceList::onActiveSpeakerParticipantDevice);
|
||||
// connect(conferenceModel.get(), &ConferenceModel::participantAdded, this,
|
||||
// &ParticipantDeviceList::onParticipantAdded);
|
||||
// connect(conferenceModel.get(), &ConferenceModel::participantRemoved, this,
|
||||
// &ParticipantDeviceList::onParticipantRemoved);
|
||||
// connect(conferenceModel.get(), &ConferenceModel::participantDeviceAdded, this,
|
||||
// &ParticipantDeviceList::onParticipantDeviceAdded);
|
||||
// connect(conferenceModel.get(), &ConferenceModel::participantDeviceRemoved, this,
|
||||
// &ParticipantDeviceList::onParticipantDeviceRemoved);
|
||||
// connect(conferenceModel.get(), &ConferenceModel::conferenceStateChanged, this,
|
||||
// &ParticipantDeviceList::onConferenceStateChanged);
|
||||
// connect(conferenceModel.get(), &ConferenceModel::participantDeviceMediaCapabilityChanged, this,
|
||||
// &ParticipantDeviceList::onParticipantDeviceMediaCapabilityChanged);
|
||||
// connect(conferenceModel.get(), &ConferenceModel::participantDeviceMediaAvailabilityChanged, this,
|
||||
// &ParticipantDeviceList::onParticipantDeviceMediaAvailabilityChanged);
|
||||
// connect(conferenceModel.get(), &ConferenceModel::participantDeviceIsSpeakingChanged, this,
|
||||
// &ParticipantDeviceList::onParticipantDeviceIsSpeakingChanged);
|
||||
// mActiveSpeaker = get(conferenceModel->getConference()->getActiveSpeakerParticipantDevice());
|
||||
// mInitialized = true;
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
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);
|
||||
if (mConferenceModelConnection) mConferenceModelConnection->disconnect();
|
||||
mConferenceModelConnection = QSharedPointer<SafeConnection<ParticipantDeviceList, ConferenceModel>>(
|
||||
new SafeConnection<ParticipantDeviceList, ConferenceModel>(me, mConferenceModel), &QObject::deleteLater);
|
||||
if (mConferenceModel) {
|
||||
mConferenceModelConnection->makeConnectToModel(
|
||||
&ConferenceModel::participantDeviceAdded,
|
||||
[this](const std::shared_ptr<linphone::ParticipantDevice> &device) {
|
||||
auto deviceCore = ParticipantDeviceCore::create(device);
|
||||
mConferenceModelConnection->invokeToCore([this, deviceCore]() {
|
||||
qDebug() << "[ParticipantDeviceList] : add a device";
|
||||
this->add(deviceCore);
|
||||
});
|
||||
});
|
||||
mConferenceModelConnection->makeConnectToModel(
|
||||
&ConferenceModel::conferenceStateChanged, [this](linphone::Conference::State state) {
|
||||
qDebug() << "[ParticipantDeviceList] new state = " << (int)state;
|
||||
if (state == linphone::Conference::State::Created) {
|
||||
qDebug() << "[ParticipantDeviceList] : build devices";
|
||||
auto devices = buildDevices(mConferenceModel);
|
||||
mConferenceModelConnection->invokeToCore([this, devices]() {
|
||||
qDebug() << "[ParticipantDeviceList] : set devices" << devices.size();
|
||||
setDevices(devices);
|
||||
});
|
||||
}
|
||||
});
|
||||
mConferenceModelConnection->makeConnectToCore(
|
||||
&ParticipantDeviceList::lSetConferenceModel,
|
||||
[this](const std::shared_ptr<ConferenceModel> &conferenceModel) {
|
||||
mConferenceModelConnection->invokeToCore(
|
||||
[this, conferenceModel]() { setConferenceModel(conferenceModel); });
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
bool ParticipantDeviceList::add(const QSharedPointer<ParticipantDeviceCore> &deviceToAdd) {
|
||||
auto deviceToAddAddr = deviceToAdd->getAddress();
|
||||
int row = 0;
|
||||
qDebug() << "Adding device " << deviceToAdd->getAddress();
|
||||
for (auto item : mList) {
|
||||
auto deviceCore = item.objectCast<ParticipantDeviceCore>();
|
||||
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() {
|
||||
QVariant ParticipantDeviceList::data(const QModelIndex &index, int role) const {
|
||||
int row = index.row();
|
||||
if (!index.isValid() || row < 0 || row >= mList.count()) return QVariant();
|
||||
if (role == Qt::DisplayRole)
|
||||
return QVariant::fromValue(new ParticipantDeviceGui(mList[row].objectCast<ParticipantDeviceCore>()));
|
||||
return QVariant();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,70 +22,39 @@
|
|||
#define PARTICIPANT_DEVICE_LIST_H_
|
||||
|
||||
#include "../proxy/ListProxy.hpp"
|
||||
#include "core/call/CallCore.hpp"
|
||||
#include "core/participant/ParticipantDeviceCore.hpp"
|
||||
#include <QDateTime>
|
||||
#include <QObject>
|
||||
#include <QString>
|
||||
#include "ParticipantDeviceCore.hpp"
|
||||
#include "model/conference/ConferenceModel.hpp"
|
||||
#include "tool/AbstractObject.hpp"
|
||||
#include "tool/thread/SafeConnection.hpp"
|
||||
|
||||
class ParticipantDeviceList : public ListProxy, public AbstractObject {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
static QSharedPointer<ParticipantDeviceList> create(const std::shared_ptr<linphone::Participant> &participant);
|
||||
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(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 initConferenceModel();
|
||||
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();
|
||||
virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
|
||||
|
||||
signals:
|
||||
void activeSpeakerChanged();
|
||||
void securityLevelChanged(std::shared_ptr<const linphone::Address> device);
|
||||
void participantSpeaking(ParticipantDeviceCore *speakingDevice);
|
||||
void conferenceCreated();
|
||||
void meChanged();
|
||||
void lSetConferenceModel(const std::shared_ptr<ConferenceModel> &conferenceModel);
|
||||
|
||||
private:
|
||||
CallCore *mCallCore = nullptr;
|
||||
QSharedPointer<ParticipantDeviceCore> mActiveSpeaker;
|
||||
// QList<ParticipantDeviceCore*> mActiveSpeakers;// First item is last speaker
|
||||
bool mInitialized = false;
|
||||
QSharedPointer<SafeConnection<ParticipantDeviceList, CallModel>> mModelConnection;
|
||||
std::shared_ptr<ConferenceModel> mConferenceModel;
|
||||
QSharedPointer<SafeConnection<ParticipantDeviceList, ConferenceModel>> mConferenceModelConnection;
|
||||
|
||||
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
|
||||
* (see https://www.linphone.org).
|
||||
|
|
@ -26,96 +26,71 @@
|
|||
|
||||
// =============================================================================
|
||||
|
||||
DEFINE_ABSTRACT_OBJECT(ParticipantDeviceProxy)
|
||||
|
||||
ParticipantDeviceProxy::ParticipantDeviceProxy(QObject *parent) : SortFilterProxy(parent) {
|
||||
mDeleteSourceModel = true;
|
||||
mList = ParticipantDeviceList::create();
|
||||
setSourceModel(mList.get());
|
||||
mParticipants = ParticipantDeviceList::create();
|
||||
connect(mParticipants.get(), &ParticipantDeviceList::countChanged, this, &ParticipantDeviceProxy::meChanged);
|
||||
|
||||
setSourceModel(mParticipants.get());
|
||||
sort(0); //, Qt::DescendingOrder);
|
||||
}
|
||||
|
||||
ParticipantDeviceProxy::~ParticipantDeviceProxy() {
|
||||
setSourceModel(nullptr);
|
||||
}
|
||||
|
||||
bool ParticipantDeviceProxy::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const {
|
||||
const QModelIndex index = mList->index(sourceRow, 0, sourceParent);
|
||||
const ParticipantDeviceCore *device = index.data().value<ParticipantDeviceCore *>();
|
||||
return device && (isShowMe() /*|| !(device->isMe() && device->isLocal())*/);
|
||||
CallGui *ParticipantDeviceProxy::getCurrentCall() const {
|
||||
return mCurrentCall;
|
||||
}
|
||||
|
||||
bool ParticipantDeviceProxy::lessThan(const QModelIndex &left, const QModelIndex &right) const {
|
||||
const ParticipantDeviceCore *deviceA = sourceModel()->data(left).value<ParticipantDeviceCore *>();
|
||||
const ParticipantDeviceCore *deviceB = sourceModel()->data(right).value<ParticipantDeviceCore *>();
|
||||
// 'me' at end (for grid).
|
||||
return /*deviceB->isLocal() || !deviceA->isLocal() && deviceB->isMe() ||*/ left.row() < right.row();
|
||||
}
|
||||
//---------------------------------------------------------------------------------
|
||||
|
||||
ParticipantDeviceCore *ParticipantDeviceProxy::getAt(int row) {
|
||||
if (row >= 0) {
|
||||
QModelIndex sourceIndex = mapToSource(this->index(row, 0));
|
||||
return sourceModel()->data(sourceIndex).value<ParticipantDeviceCore *>();
|
||||
} else return nullptr;
|
||||
}
|
||||
|
||||
ParticipantDeviceCore *ParticipantDeviceProxy::getActiveSpeakerModel() {
|
||||
return mList->getActiveSpeakerModel();
|
||||
}
|
||||
|
||||
CallModel *ParticipantDeviceProxy::getCallModel() const {
|
||||
return mCallModel;
|
||||
}
|
||||
|
||||
ParticipantDeviceCore *ParticipantDeviceProxy::getMe() const {
|
||||
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::setCurrentCall(CallGui *call) {
|
||||
qDebug() << "[ParticipantDeviceProxy] set current call " << this << " => " << call;
|
||||
if (mCurrentCall != call) {
|
||||
CallCore *callCore = nullptr;
|
||||
if (mCurrentCall) {
|
||||
callCore = mCurrentCall->getCore();
|
||||
if (callCore) callCore->disconnect(mParticipants.get());
|
||||
callCore = nullptr;
|
||||
}
|
||||
mCurrentCall = call;
|
||||
if (mCurrentCall) callCore = mCurrentCall->getCore();
|
||||
if (callCore) {
|
||||
connect(callCore, &CallCore::conferenceChanged, mParticipants.get(), [this]() {
|
||||
auto conference = mCurrentCall->getCore()->getConferenceCore();
|
||||
qDebug() << "[ParticipantDeviceProxy] set conference " << this << " => " << conference;
|
||||
mParticipants->setConferenceModel(conference ? conference->getModel() : nullptr);
|
||||
// mParticipants->lSetConferenceModel(conference ? conference->getModel() : nullptr);
|
||||
});
|
||||
auto conference = callCore->getConferenceCore();
|
||||
qDebug() << "[ParticipantDeviceProxy] set conference " << this << " => " << conference;
|
||||
mParticipants->setConferenceModel(conference ? conference->getModel() : nullptr);
|
||||
// mParticipants->lSetConferenceModel(conference ? conference->getModel() : nullptr);
|
||||
}
|
||||
emit currentCallChanged();
|
||||
}
|
||||
}
|
||||
|
||||
void ParticipantDeviceProxy::onCountChanged() {
|
||||
qDebug() << "Count changed : " << getCount();
|
||||
ParticipantDeviceGui *ParticipantDeviceProxy::getMe() const {
|
||||
auto core = mParticipants->getMe();
|
||||
if (!core) return nullptr;
|
||||
else {
|
||||
return new ParticipantDeviceGui(core);
|
||||
}
|
||||
}
|
||||
|
||||
void ParticipantDeviceProxy::onParticipantSpeaking(ParticipantDeviceCore *speakingDevice) {
|
||||
emit participantSpeaking(speakingDevice);
|
||||
void ParticipantDeviceProxy::setMe(ParticipantDeviceGui *me) {
|
||||
}
|
||||
|
||||
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
|
||||
* (see https://www.linphone.org).
|
||||
|
|
@ -21,64 +21,43 @@
|
|||
#ifndef PARTICIPANT_DEVICE_PROXY_MODEL_H_
|
||||
#define PARTICIPANT_DEVICE_PROXY_MODEL_H_
|
||||
|
||||
#include <linphone++/linphone.hh>
|
||||
// =============================================================================
|
||||
#include "../proxy/SortFilterProxy.hpp"
|
||||
#include <QDateTime>
|
||||
#include <QObject>
|
||||
#include <QSharedPointer>
|
||||
#include <QString>
|
||||
#include "core/call/CallGui.hpp"
|
||||
#include "core/participant/ParticipantDeviceGui.hpp"
|
||||
#include "tool/AbstractObject.hpp"
|
||||
|
||||
class ParticipantDeviceList;
|
||||
class ParticipantDeviceCore;
|
||||
class ParticipantCore;
|
||||
class CallModel;
|
||||
class ParticipantDeviceGui;
|
||||
|
||||
class ParticipantDeviceProxy : public SortFilterProxy {
|
||||
class ParticipantDeviceProxy : public SortFilterProxy, public AbstractObject {
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(CallGui *currentCall READ getCurrentCall WRITE setCurrentCall NOTIFY currentCallChanged)
|
||||
Q_PROPERTY(ParticipantDeviceGui *me READ getMe WRITE setMe NOTIFY meChanged)
|
||||
|
||||
public:
|
||||
Q_PROPERTY(CallModel *callModel READ getCallModel WRITE setCallModel NOTIFY callModelChanged)
|
||||
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(QObject *parent = Q_NULLPTR);
|
||||
~ParticipantDeviceProxy();
|
||||
|
||||
Q_INVOKABLE ParticipantDeviceCore *getAt(int row);
|
||||
ParticipantDeviceCore *getActiveSpeakerModel();
|
||||
ParticipantDeviceCore *getMe() const;
|
||||
CallGui *getCurrentCall() const;
|
||||
void setCurrentCall(CallGui *callGui);
|
||||
|
||||
CallModel *getCallModel() const;
|
||||
bool isShowMe() const;
|
||||
|
||||
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();
|
||||
ParticipantDeviceGui *getMe() const;
|
||||
void setMe(ParticipantDeviceGui *me);
|
||||
|
||||
protected:
|
||||
virtual bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override;
|
||||
virtual bool lessThan(const QModelIndex &left, const QModelIndex &right) const override;
|
||||
bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override;
|
||||
bool lessThan(const QModelIndex &left, const QModelIndex &right) const override;
|
||||
|
||||
CallModel *mCallModel;
|
||||
bool mShowMe = true;
|
||||
signals:
|
||||
void lUpdate();
|
||||
void currentCallChanged();
|
||||
void meChanged();
|
||||
|
||||
QSharedPointer<ParticipantDeviceList> mList;
|
||||
private:
|
||||
QString mSearchText;
|
||||
CallGui *mCurrentCall = nullptr;
|
||||
QSharedPointer<ParticipantDeviceList> mParticipants;
|
||||
DECLARE_ABSTRACT_OBJECT
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -24,13 +24,20 @@
|
|||
|
||||
#include "core/path/Paths.hpp"
|
||||
#include "model/core/CoreModel.hpp"
|
||||
#include "tool/Utils.hpp"
|
||||
|
||||
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)
|
||||
: ::Listener<linphone::Conference, linphone::ConferenceListener>(conference, parent) {
|
||||
mustBeInLinphoneThread(getClassName());
|
||||
qDebug() << "[ConferenceModel] new" << this;
|
||||
qDebug() << "[ConferenceModel] new" << this << conference.get();
|
||||
}
|
||||
|
||||
ConferenceModel::~ConferenceModel() {
|
||||
|
|
@ -136,7 +143,8 @@ void ConferenceModel::onActiveSpeakerParticipantDevice(
|
|||
const std::shared_ptr<linphone::Conference> &conference,
|
||||
const std::shared_ptr<const linphone::ParticipantDevice> &participantDevice) {
|
||||
qDebug() << "onActiveSpeakerParticipantDevice: " << participantDevice->getAddress()->asString().c_str();
|
||||
emit activeSpeakerParticipantDevice(participantDevice);
|
||||
|
||||
emit activeSpeakerParticipantDevice(conference->getActiveSpeakerParticipantDevice());
|
||||
}
|
||||
|
||||
void ConferenceModel::onParticipantAdded(const std::shared_ptr<linphone::Conference> &conference,
|
||||
|
|
|
|||
|
|
@ -35,6 +35,7 @@ class ConferenceModel : public ::Listener<linphone::Conference, linphone::Confer
|
|||
public:
|
||||
ConferenceModel(const std::shared_ptr<linphone::Conference> &conference, QObject *parent = nullptr);
|
||||
~ConferenceModel();
|
||||
static std::shared_ptr<ConferenceModel> create(const std::shared_ptr<linphone::Conference> &conference);
|
||||
|
||||
void terminate();
|
||||
|
||||
|
|
@ -105,11 +106,11 @@ private:
|
|||
const std::shared_ptr<const linphone::AudioDevice> &audioDevice) override;
|
||||
|
||||
signals:
|
||||
void activeSpeakerParticipantDevice(const std::shared_ptr<const linphone::ParticipantDevice> &participantDevice);
|
||||
void participantAdded(const std::shared_ptr<const linphone::Participant> &participant);
|
||||
void activeSpeakerParticipantDevice(const std::shared_ptr<linphone::ParticipantDevice> &participantDevice);
|
||||
void participantAdded(const std::shared_ptr<linphone::Participant> &participant);
|
||||
void participantRemoved(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 participantDeviceStateChanged(const std::shared_ptr<linphone::Conference> &conference,
|
||||
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);
|
||||
*/
|
||||
}
|
||||
|
||||
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::FriendPhoneNumber> makeLinphoneNumber(const QString &label, const QString &number);
|
||||
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(QString address);
|
||||
|
|
|
|||
|
|
@ -1195,18 +1195,7 @@ int Utils::getYear(const QDate &date) {
|
|||
|
||||
bool Utils::isMe(const QString &address) {
|
||||
bool isMe = false;
|
||||
App::postModelSync([&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;
|
||||
}
|
||||
});
|
||||
App::postModelSync([&isMe, address]() { isMe = ToolModel::isMe(address); });
|
||||
return isMe;
|
||||
}
|
||||
// QDateTime dateTime(QDateTime::fromString(date, "yyyy-MM-dd hh:mm:ss"));
|
||||
|
|
|
|||
|
|
@ -97,6 +97,14 @@ public:
|
|||
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>
|
||||
void invokeToModel(Func &&callable, Args &&...args) {
|
||||
if (!tryLock()) return;
|
||||
|
|
|
|||
|
|
@ -28,11 +28,12 @@ Window {
|
|||
if (call && !conferenceInfo) middleItemStackView.replace(inCallItem)
|
||||
}
|
||||
Component.onCompleted: if (call && !conferenceInfo) middleItemStackView.replace(inCallItem)
|
||||
property var callObj
|
||||
|
||||
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."))
|
||||
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.fillHeight: true
|
||||
Layout.margins: 20 * DefaultStyle.dp
|
||||
|
||||
}
|
||||
OngoingCallRightPanel {
|
||||
id: rightPanel
|
||||
|
|
@ -606,6 +608,10 @@ Window {
|
|||
onJoinConfRequested: mainWindow.joinConference(cameraEnabled)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
Component {
|
||||
id: inCallItem
|
||||
Control.Control {
|
||||
|
|
@ -614,163 +620,18 @@ Window {
|
|||
// implicitHeight: parent.height
|
||||
Layout.fillHeight: true
|
||||
Layout.leftMargin: 10 * DefaultStyle.dp
|
||||
|
||||
Layout.rightMargin: 10 * DefaultStyle.dp
|
||||
Layout.alignment: Qt.AlignCenter
|
||||
/*
|
||||
background: Rectangle {
|
||||
anchors.fill: parent
|
||||
color: DefaultStyle.grey_600
|
||||
radius: 15 * DefaultStyle.dp
|
||||
}
|
||||
contentItem: Item {
|
||||
id: centerItem
|
||||
anchors.fill: parent
|
||||
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
|
||||
}
|
||||
}*/
|
||||
contentItem: CallLayout{
|
||||
call: mainWindow.call
|
||||
callTerminatedByUser: mainWindow.callTerminatedByUser
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -210,13 +210,14 @@ Item {
|
|||
background: Item{}
|
||||
Layout.preferredWidth: 24 * DefaultStyle.dp
|
||||
Layout.preferredHeight: 24 * DefaultStyle.dp
|
||||
property var callObj
|
||||
contentItem: Image {
|
||||
width: 24 * DefaultStyle.dp
|
||||
height: 24 * DefaultStyle.dp
|
||||
source: AppIcons.phone
|
||||
}
|
||||
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/MainLayout.qml
|
||||
|
||||
view/Layout/Call/ActiveSpeakerLayout.qml
|
||||
view/Layout/Call/CallLayout.qml
|
||||
#view/Layout/Call/GridLayout.qml
|
||||
|
||||
view/Layout/Conference/IncallGrid.qml
|
||||
view/Layout/Contact/ContactLayout.qml
|
||||
view/Layout/Meeting/AddParticipantsLayout.qml
|
||||
|
|
@ -86,6 +90,8 @@ list(APPEND _LINPHONEAPP_QML_FILES
|
|||
view/Page/Main/ContactPage.qml
|
||||
view/Page/Main/MeetingPage.qml
|
||||
|
||||
|
||||
|
||||
view/Tool/utils.js
|
||||
# Prototypes
|
||||
view/Prototype/PhoneNumberPrototype.qml
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ RowLayout {
|
|||
id: accounts
|
||||
}
|
||||
account: accounts.defaultAccount
|
||||
enablePersonalCamera: mainItem.cameraEnabled
|
||||
previewEnabled: mainItem.cameraEnabled
|
||||
}
|
||||
RowLayout {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
|
|
@ -146,4 +146,4 @@ RowLayout {
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,13 +16,19 @@ Item {
|
|||
width: 200
|
||||
property CallGui call: null
|
||||
property AccountGui account: null
|
||||
property bool enablePersonalCamera: false
|
||||
onEnablePersonalCameraChanged: console.log ("enable camera", enablePersonalCamera)
|
||||
property ParticipantDeviceGui participantDevice: null
|
||||
property bool previewEnabled: false
|
||||
property color color: DefaultStyle.grey_600
|
||||
property int radius: 15 * DefaultStyle.dp
|
||||
property var peerAddressObj: call ? UtilsCpp.getDisplayName(call.core.peerAddress) : null
|
||||
property string peerAddress: peerAddressObj ? peerAddressObj.value : ""
|
||||
property var peerAddressObj: participantDevice && participantDevice.core
|
||||
? 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 bool cameraEnabled: previewEnabled
|
||||
property string qmlName
|
||||
|
||||
Rectangle {
|
||||
id: background
|
||||
|
|
@ -70,10 +76,7 @@ Item {
|
|||
interval: 1
|
||||
onTriggered: {cameraLoader.active=false; cameraLoader.active=true;}
|
||||
}
|
||||
active: mainItem.visible && call
|
||||
? call.core.remoteVideoEnabled && (mainItem.call.core.state != LinphoneEnums.CallState.End
|
||||
&& mainItem.call.core.state != LinphoneEnums.CallState.Released)
|
||||
: mainItem.enablePersonalCamera
|
||||
active: mainItem.visible && mainItem.cameraEnabled
|
||||
onActiveChanged: console.log("camera active", active)
|
||||
sourceComponent: cameraComponent
|
||||
}
|
||||
|
|
@ -88,7 +91,9 @@ Item {
|
|||
anchors.fill: parent
|
||||
visible: isReady
|
||||
call: mainItem.call
|
||||
|
||||
participantDevice: mainItem.participantDevice
|
||||
isPreview: mainItem.previewEnabled
|
||||
qmlName: mainItem.qmlName
|
||||
onRequestNewRenderer: {
|
||||
console.log("Request new renderer")
|
||||
resetTimer.restart()
|
||||
|
|
@ -103,7 +108,7 @@ Item {
|
|||
anchors.leftMargin: 10 * DefaultStyle.dp
|
||||
anchors.bottomMargin: 10 * DefaultStyle.dp
|
||||
width: implicitWidth
|
||||
text: mainItem.peerAddress.length != 0
|
||||
text: mainItem.peerAddress != ''
|
||||
? mainItem.peerAddress
|
||||
: mainItem.account && mainItem.identityAddress
|
||||
? 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…
Add table
Reference in a new issue