- Calendar ICS view on grids

- Scheduling conferences
- CameraDummy in case of unavailable Linphone streams
- Remove some tests
This commit is contained in:
Julien Wadel 2022-01-17 12:10:30 +01:00
parent c83212fbeb
commit 9029c33215
52 changed files with 1121 additions and 336 deletions

View file

@ -133,6 +133,7 @@ set(SOURCES
src/components/calls/CallsListModel.cpp
src/components/calls/CallsListProxyModel.cpp
src/components/camera/Camera.cpp
src/components/camera/CameraDummy.cpp
src/components/camera/CameraPreview.cpp
src/components/chat/ChatModel.cpp
src/components/chat-events/ChatCallModel.cpp
@ -151,7 +152,9 @@ set(SOURCES
src/components/conference/ConferenceProxyModel.cpp
src/components/conferenceInfo/ConferenceInfoModel.cpp
src/components/conferenceInfo/ConferenceInfoListModel.cpp
src/components/conferenceInfo/ConferenceInfoMapModel.cpp
src/components/conferenceInfo/ConferenceInfoProxyModel.cpp
src/components/conferenceScheduler/ConferenceSchedulerModel.cpp
src/components/contact/ContactModel.cpp
src/components/contact/VcardModel.cpp
src/components/contacts/ContactsImporterModel.cpp
@ -248,6 +251,7 @@ set(HEADERS
src/components/calls/CallsListModel.hpp
src/components/calls/CallsListProxyModel.hpp
src/components/camera/Camera.hpp
src/components/camera/CameraDummy.hpp
src/components/camera/CameraPreview.hpp
src/components/chat/ChatModel.hpp
src/components/chat-events/ChatCallModel.hpp
@ -267,7 +271,9 @@ set(HEADERS
src/components/conference/ConferenceProxyModel.hpp
src/components/conferenceInfo/ConferenceInfoModel.hpp
src/components/conferenceInfo/ConferenceInfoListModel.hpp
src/components/conferenceInfo/ConferenceInfoMapModel.hpp
src/components/conferenceInfo/ConferenceInfoProxyModel.hpp
src/components/conferenceScheduler/ConferenceSchedulerModel.hpp
src/components/contact/ContactModel.hpp
src/components/contact/VcardModel.hpp
src/components/contacts/ContactsImporterModel.hpp

View file

@ -14,6 +14,7 @@
<file>assets/images/attachment_custom.svg</file>
<file>assets/images/auto_answer_custom.svg</file>
<file>assets/images/burger_menu_custom.svg</file>
<file>assets/images/calendar_participants_custom.svg</file>
<file>assets/images/call_accept_custom.svg</file>
<file>assets/images/call_chat_secure_custom.svg</file>
<file>assets/images/call_chat_unsecure_custom.svg</file>
@ -292,6 +293,7 @@
<file>ui/modules/Linphone/Chat/ChatMenu.qml</file>
<file>ui/modules/Linphone/Chat/ChatAudioMessage.qml</file>
<file>ui/modules/Linphone/Chat/ChatAudioPreview.qml</file>
<file>ui/modules/Linphone/Chat/ChatCalendarMessage.qml</file>
<file>ui/modules/Linphone/Chat/ChatFileMessage.qml</file>
<file>ui/modules/Linphone/Chat/ChatFilePreview.qml</file>
<file>ui/modules/Linphone/Chat/ChatForwardMessage.qml</file>
@ -342,6 +344,7 @@
<file>ui/modules/Linphone/Styles/Chat/ChatAudioMessageStyle.qml</file>
<file>ui/modules/Linphone/Styles/Chat/ChatAudioPreviewStyle.qml</file>
<file>ui/modules/Linphone/Styles/Chat/ChatFilePreviewStyle.qml</file>
<file>ui/modules/Linphone/Styles/Chat/ChatCalendarMessageStyle.qml</file>
<file>ui/modules/Linphone/Styles/Chat/ChatForwardMessageStyle.qml</file>
<file>ui/modules/Linphone/Styles/Chat/ChatReplyMessageStyle.qml</file>
<file>ui/modules/Linphone/Styles/Codecs/CodecsViewerStyle.qml</file>

View file

@ -40,6 +40,7 @@
#include "conferenceInfo/ConferenceInfoModel.hpp"
#include "conferenceInfo/ConferenceInfoListModel.hpp"
#include "conferenceInfo/ConferenceInfoProxyModel.hpp"
#include "conferenceScheduler/ConferenceSchedulerModel.hpp"
#include "contact/ContactModel.hpp"
#include "contact/VcardModel.hpp"
#include "contacts/ContactsListModel.hpp"

View file

@ -420,6 +420,7 @@ void CallModel::handleCallStateChanged (const shared_ptr<linphone::Call> &call,
break;
case linphone::Call::State::UpdatedByRemote:
qWarning() << "UpdatedByRemote :" << (mCall ? mCall->getCurrentParams()->videoEnabled() + QString(" ")+mCall->getRemoteParams()->videoEnabled() : " call NULL");
if (mCall && !mCall->getCurrentParams()->videoEnabled() && mCall->getRemoteParams()->videoEnabled()) {
mCall->deferUpdate();
emit videoRequested();
@ -663,7 +664,7 @@ bool CallModel::getRemoteVideoEnabled () const {
bool CallModel::getVideoEnabled () const {
if(mCall){
shared_ptr<const linphone::CallParams> params = mCall->getCurrentParams();
return params && params->videoEnabled() && getStatus() == CallStatusConnected;
return params && params->videoEnabled() && getStatus() == CallStatusConnected && mCall->getState() == linphone::Call::State::StreamsRunning;
}else
return true;
}
@ -795,7 +796,8 @@ void CallModel::setRemoteDisplayName(const std::string& name){
auto callLog = mCall->getCallLog();
if(name!= "") {
auto core = CoreManager::getInstance()->getCore();
callLog->setRemoteAddress(Utils::interpretUrl(getFullPeerAddress()));
auto address = Utils::interpretUrl(getFullPeerAddress());
callLog->setRemoteAddress(address);
}
}
emit fullPeerAddressChanged();

View file

@ -189,7 +189,7 @@ signals:
void fullPeerAddressChanged();
void transferAddressChanged (const QString &transferAddress);
private:
public:
void handleCallEncryptionChanged (const std::shared_ptr<linphone::Call> &call);
void handleCallStateChanged (const std::shared_ptr<linphone::Call> &call, linphone::Call::State state);
@ -255,6 +255,7 @@ private:
static QString generateSavedFilename (const QString &from, const QString &to);
private:
bool mIsInConference = false;
bool mPausedByRemote = false;

View file

@ -389,67 +389,8 @@ QVariantMap CallsListModel::createConference(ConferenceInfoModel * conferenceInf
std::shared_ptr<TimelineModel> timeline;
auto timelineList = CoreManager::getInstance()->getTimelineListModel();
qInfo() << "Conference creation of " << conferenceInfo->getSubject() << " at " << securityLevel << " security";// and with " << conferenceInfo->getConferenceInfo()->getParticipants().size();
std::shared_ptr<linphone::ConferenceParams> params = core->createConferenceParams();
std::list <shared_ptr<linphone::Address> > participants = conferenceInfo->getConferenceInfo()->getParticipants();
std::shared_ptr<const linphone::Address> localAddress;
/*
for(auto p : participants){
ParticipantModel* participant = p.value<ParticipantModel*>();
std::shared_ptr<linphone::Address> address;
if(participant) {
address = Utils::interpretUrl(participant->getSipAddress());
if(participant->getAdminStatus())
admins << address;
}else{
QString participant = p.toString();
if( participant != "")
address = Utils::interpretUrl(participant);
}
if( address)
chatRoomParticipants.push_back( address );
}*/
auto proxy = core->getDefaultProxyConfig();
params->setVideoEnabled(true);
params->setStartTime(conferenceInfo->getConferenceInfo()->getDateTime());
params->setEndTime(conferenceInfo->getConferenceInfo()->getDateTime()+conferenceInfo->getConferenceInfo()->getDuration()*60);
params->setLayout(linphone::ConferenceLayout::Grid);
//params->setDescription(conferenceInfo->getConferenceInfo()->getDescription());
//params->enableEncryption(securityLevel>0);
// if( securityLevel>0){
// params->enableEncryption(true);
// }else
// params->setBackend(linphone::ChatRoomBackend::Basic);
// params->enableGroup( subject!="" );
if(participants.size() > 0) {
params->setSubject(conferenceInfo->getSubject() != ""?Utils::appStringToCoreString(conferenceInfo->getSubject()):"Dummy Subject");
if( !conference) {
core->createConferenceOnServer(params, localAddress, participants);
/*
//conference = core->createConferenceWithParams(params);
if(conference != nullptr && admins.size() > 0){
for(auto a : admins) {
auto p = conference->findParticipant(a);
if( p )
conference->setParticipantAdminStatus(p, true);
else
qWarning() <<"Cannot set admin for " << a->asString().c_str() << ". It is not found in conference.";
}
}
// Warning: Should not be needed but SDK doesn't ref it
mConferences.append(std::make_shared<ConferenceModel>(conference));
//ChatRoomInitializer::setAdminsAsync(params->getSubject(), params->getBackend(), params->groupEnabled(), admins );
// timeline = timelineList->getTimeline(conference, false);
*/
}
}
//result["created"] = (conference != nullptr);
auto conferenceScheduler = core->createConferenceScheduler();
conferenceScheduler->setInfo(conferenceInfo->getConferenceInfo());
return result;
}

View file

@ -28,6 +28,7 @@
#include "components/participant/ParticipantDeviceModel.hpp"
#include "Camera.hpp"
#include "CameraDummy.hpp"
// =============================================================================
@ -37,7 +38,6 @@ namespace {
constexpr int MaxFps = 30;
}
// =============================================================================
Camera::Camera (QQuickItem *parent) : QQuickFramebufferObject(parent) {
// The fbo content must be y-mirrored because the ms rendering is y-inverted.
@ -77,41 +77,53 @@ public:
QQuickFramebufferObject::Renderer *Camera::createRenderer () const {
QQuickFramebufferObject::Renderer * renderer = NULL;
if(mIsPreview){
CoreManager::getInstance()->getCore()->setNativePreviewWindowId(NULL);// Reset
renderer=(QQuickFramebufferObject::Renderer *)CoreManager::getInstance()->getCore()->getNativePreviewWindowId();
CoreManager::getInstance()->getCore()->setNativePreviewWindowId(renderer);
renderer = (QQuickFramebufferObject::Renderer *)CoreManager::getInstance()->getCore()->getNativePreviewWindowId();
if(renderer)
CoreManager::getInstance()->getCore()->setNativePreviewWindowId(NULL);// Reset
renderer=(QQuickFramebufferObject::Renderer *)CoreManager::getInstance()->getCore()->createNativePreviewWindowId();
if(renderer)
CoreManager::getInstance()->getCore()->setNativePreviewWindowId(renderer);
}else{
bool useDefaultWindow = false;
if(mCallModel){
auto call = mCallModel->getCall();
if(call){
call->setNativeVideoWindowId(NULL);// Reset
renderer = (QQuickFramebufferObject::Renderer *) call->getNativeVideoWindowId();
call->setNativeVideoWindowId(renderer);
}else{
CoreManager::getInstance()->getCore()->setNativeVideoWindowId(NULL);
renderer = (QQuickFramebufferObject::Renderer *) CoreManager::getInstance()->getCore()->getNativeVideoWindowId();
CoreManager::getInstance()->getCore()->setNativeVideoWindowId(renderer);
}
if(renderer)
call->setNativeVideoWindowId(NULL);// Reset
renderer = (QQuickFramebufferObject::Renderer *) call->createNativeVideoWindowId();
if(renderer)
call->setNativeVideoWindowId(renderer);
}else
useDefaultWindow = true;
}else if( mParticipantDeviceModel){
auto participantDevice = mParticipantDeviceModel->getDevice();
if(participantDevice){
participantDevice->setNativeVideoWindowId(NULL);// Reset
renderer = (QQuickFramebufferObject::Renderer *) participantDevice->getNativeVideoWindowId();
participantDevice->setNativeVideoWindowId(renderer);
}else{
renderer = (QQuickFramebufferObject::Renderer *)participantDevice->getNativeVideoWindowId();
if(renderer)
participantDevice->setNativeVideoWindowId(NULL);// Reset
renderer = (QQuickFramebufferObject::Renderer *) participantDevice->createNativeVideoWindowId();
if(renderer)
participantDevice->setNativeVideoWindowId(renderer);
}else
useDefaultWindow = true;
}
if(useDefaultWindow){
renderer = (QQuickFramebufferObject::Renderer *)CoreManager::getInstance()->getCore()->getNativeVideoWindowId();
if(renderer)
CoreManager::getInstance()->getCore()->setNativeVideoWindowId(NULL);
enderer = (QQuickFramebufferObject::Renderer *) CoreManager::getInstance()->getCore()->getNativeVideoWindowId();
renderer = (QQuickFramebufferObject::Renderer *) CoreManager::getInstance()->getCore()->createNativeVideoWindowId();
if(renderer)
CoreManager::getInstance()->getCore()->setNativeVideoWindowId(renderer);
}
}
}
if(renderer)
return renderer;
else{
qWarning() << "Camera stream couldn't start for Rendering";
return new SafeFramebuffer();
if( !renderer){
qWarning() << "Camera stream couldn't start for Rendering. Retrying in 1s";
renderer = new CameraDummy();
QTimer::singleShot(1000, this, &Camera::requestNewRenderer);
}
return renderer;
}
// -----------------------------------------------------------------------------

View file

@ -55,6 +55,7 @@ signals:
void callChanged (CallModel *callModel);
void isPreviewChanged (bool isPreview);
void participantDeviceModelChanged(ParticipantDeviceModel *participantDeviceModel);
void requestNewRenderer();
private:
CallModel *getCallModel () const;

View file

@ -0,0 +1,44 @@
/*
* Copyright (c) 2010-2020 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 <QOpenGLFramebufferObject>
#include <QQuickWindow>
#include <QThread>
#include <QTimer>
#include "components/core/CoreManager.hpp"
#include "CameraDummy.hpp"
// =============================================================================
CameraDummy::CameraDummy(){
}
QOpenGLFramebufferObject *CameraDummy::createFramebufferObject (const QSize &size){
return new QOpenGLFramebufferObject(size);
}
void CameraDummy::render (){
}
void CameraDummy::synchronize (QQuickFramebufferObject *item){
}

View file

@ -0,0 +1,37 @@
/*
* Copyright (c) 2022 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 CAMERA_DUMMY_H_
#define CAMERA_DUMMY_H_
#include <QMutex>
#include <QQuickFramebufferObject>
// =============================================================================
class CameraDummy : public QQuickFramebufferObject::Renderer{
public:
CameraDummy();
QOpenGLFramebufferObject *createFramebufferObject (const QSize &size) override;
void render () override;
void synchronize (QQuickFramebufferObject *item) override;
};
#endif

View file

@ -26,6 +26,7 @@
#include "components/core/CoreManager.hpp"
#include "CameraPreview.hpp"
#include "CameraDummy.hpp"
// =============================================================================
@ -74,25 +75,16 @@ CameraPreview::~CameraPreview () {
}
}
class SafeFramebuffer : public QQuickFramebufferObject::Renderer{
public:
SafeFramebuffer(){}
QOpenGLFramebufferObject *createFramebufferObject (const QSize &size) override{
return new QOpenGLFramebufferObject(size);
}
void render () override{}
void synchronize (QQuickFramebufferObject *item) override{}
};
QQuickFramebufferObject::Renderer *CameraPreview::createRenderer () const {
QQuickFramebufferObject::Renderer * renderer;
CoreManager::getInstance()->getCore()->setNativePreviewWindowId(NULL);// Reset
renderer=(QQuickFramebufferObject::Renderer *)CoreManager::getInstance()->getCore()->getNativePreviewWindowId();
CoreManager::getInstance()->getCore()->setNativePreviewWindowId(renderer);
QQuickFramebufferObject::Renderer * renderer = (QQuickFramebufferObject::Renderer *)CoreManager::getInstance()->getCore()->getNativePreviewWindowId();
if(renderer)
return renderer;
else{
qWarning() << "Preview stream couldn't start for Rendering";
return new SafeFramebuffer();
}
CoreManager::getInstance()->getCore()->setNativePreviewWindowId(NULL);// Reset
renderer = (QQuickFramebufferObject::Renderer *) CoreManager::getInstance()->getCore()->createNativePreviewWindowId();
if( !renderer ) {
qWarning() << "Preview stream couldn't start for Rendering. Retrying in 1s";
renderer = new CameraDummy();
QTimer::singleShot(1000, this, &CameraPreview::requestNewRenderer);
}else
CoreManager::getInstance()->getCore()->setNativePreviewWindowId(renderer);
return renderer;
}

View file

@ -23,24 +23,26 @@
#include <QMutex>
#include <QQuickFramebufferObject>
#include <mediastreamer2/msogl.h>
// =============================================================================
class CameraPreview : public QQuickFramebufferObject {
Q_OBJECT;
Q_OBJECT
public:
CameraPreview (QQuickItem *parent = Q_NULLPTR);
~CameraPreview ();
QQuickFramebufferObject::Renderer *createRenderer () const override;
CameraPreview (QQuickItem *parent = Q_NULLPTR);
~CameraPreview ();
QQuickFramebufferObject::Renderer *createRenderer () const override;
signals:
void requestNewRenderer();
private:
QTimer *mRefreshTimer = nullptr;
static QMutex mCounterMutex;
static int mCounter;
QTimer *mRefreshTimer = nullptr;
static QMutex mCounterMutex;
static int mCounter;
};
#endif // CAMERA_PREVIEW_H_

View file

@ -236,10 +236,17 @@ ChatRoomModel::ChatRoomModel (std::shared_ptr<linphone::ChatRoom> chatRoom, QObj
connect(contact, &ContactModel::contactUpdated, this, &ChatRoomModel::fullPeerAddressChanged);
}
}
// Get Max updatetime from chat room and last call event
auto callHistory = CallsListModel::getCallHistory(getParticipantAddress(), Utils::coreStringToAppString(mChatRoom->getLocalAddress()->asStringUriOnly()));
if(callHistory.size() > 0){
auto callDate = callHistory.front()->getStartDate();
if( callHistory.front()->getStatus() == linphone::Call::Status::Success )
callDate += callHistory.front()->getDuration();
setLastUpdateTime(QDateTime::fromMSecsSinceEpoch(max(mChatRoom->getLastUpdateTime(), callDate )*1000));
}else
setLastUpdateTime(QDateTime::fromMSecsSinceEpoch(mChatRoom->getLastUpdateTime()*1000));
}else
mParticipantListModel = nullptr;
}
ChatRoomModel::~ChatRoomModel () {

View file

@ -64,29 +64,36 @@ std::shared_ptr<linphone::Conference> ConferenceModel::getConference()const{
//-----------------------------------------------------------------------------------------------------------------------
void ConferenceModel::onParticipantAdded(const std::shared_ptr<linphone::Conference> & conference, const std::shared_ptr<const linphone::Participant> & participant){
qWarning() << "onParticipantAdded is not yet implemented.";
qWarning() << "Me devices : " << conference->getMe()->getDevices().size();
}
void ConferenceModel::onParticipantRemoved(const std::shared_ptr<linphone::Conference> & conference, const std::shared_ptr<const linphone::Participant> & participant){
qWarning() << "onParticipantRemoved is not yet implemented.";
qWarning() << "Me devices : " << conference->getMe()->getDevices().size();
}
void ConferenceModel::onParticipantDeviceAdded(const std::shared_ptr<linphone::Conference> & conference, const std::shared_ptr<const linphone::ParticipantDevice> & participantDevice){
qWarning() << "Me devices : " << conference->getMe()->getDevices().size();
emit participantDeviceAdded(participantDevice);
}
void ConferenceModel::onParticipantDeviceRemoved(const std::shared_ptr<linphone::Conference> & conference, const std::shared_ptr<const linphone::ParticipantDevice> & participantDevice){
qWarning() << "Me devices : " << conference->getMe()->getDevices().size();
emit participantDeviceRemoved(participantDevice);
}
void ConferenceModel::onParticipantAdminStatusChanged(const std::shared_ptr<linphone::Conference> & conference, const std::shared_ptr<const linphone::Participant> & participant){
}
void ConferenceModel::onParticipantDeviceLeft(const std::shared_ptr<linphone::Conference> & conference, const std::shared_ptr<const linphone::ParticipantDevice> & participantDevice){
qWarning() << "Me devices : " << conference->getMe()->getDevices().size();
emit participantDeviceLeft(participantDevice);
}
void ConferenceModel::onParticipantDeviceJoined(const std::shared_ptr<linphone::Conference> & conference, const std::shared_ptr<const linphone::ParticipantDevice> & participantDevice){
qWarning() << "Me devices : " << conference->getMe()->getDevices().size();
emit participantDeviceJoined(participantDevice);
}
void ConferenceModel::onParticipantDeviceMediaChanged(const std::shared_ptr<linphone::Conference> & conference, const std::shared_ptr<const linphone::ParticipantDevice> & device){
qWarning() << "onParticipantDeviceMediaChanged is not yet implemented.";
void ConferenceModel::onParticipantDeviceMediaChanged(const std::shared_ptr<linphone::Conference> & conference, const std::shared_ptr<const linphone::ParticipantDevice> & participantDevice){
qWarning() << "ConferenceModel::onParticipantDeviceMediaChanged: " << (int)participantDevice->getStreamAvailability(linphone::StreamType::Video) << ". Me devices : " << conference->getMe()->getDevices().size();
emit participantDeviceMediaChanged(participantDevice);
}
void ConferenceModel::onStateChanged(const std::shared_ptr<linphone::Conference> & conference, linphone::Conference::State newState){
qWarning() << "onStateChanged is not yet implemented.";
emit conferenceStateChanged(newState);
}
void ConferenceModel::onSubjectChanged(const std::shared_ptr<linphone::Conference> & conference, const std::string & subject){
qWarning() << "onSubjectChanged is not yet implemented.";

View file

@ -56,6 +56,8 @@ signals:
void participantDeviceRemoved(const std::shared_ptr<const linphone::ParticipantDevice> & participantDevice);
void participantDeviceLeft(const std::shared_ptr<const linphone::ParticipantDevice> & participantDevice);
void participantDeviceJoined(const std::shared_ptr<const linphone::ParticipantDevice> & participantDevice);
void participantDeviceMediaChanged(const std::shared_ptr<const linphone::ParticipantDevice> & participantDevice);
void conferenceStateChanged(linphone::Conference::State newState);
private:
std::shared_ptr<linphone::Conference> mConference;

View file

@ -36,14 +36,16 @@
// =============================================================================
ConferenceInfoListModel::ConferenceInfoListModel (QObject *parent) : QAbstractListModel(parent) {
auto conferenceInfos = CoreManager::getInstance()->getCore()->getConferenceInformationList();
for(auto conferenceInfo : conferenceInfos){
mList << ConferenceInfoModel::create( conferenceInfo );
}
//auto conferenceInfos = CoreManager::getInstance()->getCore()->getConferenceInformationList();
//for(auto conferenceInfo : conferenceInfos){
// auto conferenceInfoModel = ConferenceInfoModel::create( conferenceInfo );
// mList << conferenceInfoModel;
//mMappedList[conferenceInfoModel->getDateTime().date()].push_back(conferenceInfoModel.get());
// }
}
int ConferenceInfoListModel::rowCount (const QModelIndex &) const {
return mList.count();
return mList.size();
}
QHash<int, QByteArray> ConferenceInfoListModel::roleNames () const {
@ -55,35 +57,46 @@ QHash<int, QByteArray> ConferenceInfoListModel::roleNames () const {
QVariant ConferenceInfoListModel::data (const QModelIndex &index, int role) const {
int row = index.row();
if (!index.isValid() || row < 0 || row >= mList.count())
if (!index.isValid() || row < 0 || row >= mList.size())
return QVariant();
auto it = mList.begin() + row;
if (role == Qt::DisplayRole)
return QVariant::fromValue(mList[row].get());
return QVariant::fromValue(it->get());
//return QVariant();
return QVariant();
}
ConferenceInfoModel* ConferenceInfoListModel::getAt(const int& index) const {
return mList[index].get();
}
// -----------------------------------------------------------------------------
void ConferenceInfoListModel::add(std::shared_ptr<ConferenceInfoModel> conferenceInfoModel){
int row = mList.size();
beginInsertRows(QModelIndex(), row,row);
mList << conferenceInfoModel;
endInsertRows();
}
// Should not be called as item that need to be removed are usually a cell, not a row
bool ConferenceInfoListModel::removeRow (int row, const QModelIndex &parent) {
return removeRows(row, 1, parent);
}
bool ConferenceInfoListModel::removeRows (int row, int count, const QModelIndex &parent) {
/*
int limit = row + count - 1;
if (row < 0 || count < 0 || limit >= mList.count())
if (row < 0 || count < 0 || limit >= mMappedList.count())
return false;
beginRemoveRows(parent, row, limit);
for (int i = 0; i < count; ++i)
mList.takeAt(row)->deleteLater();
mMappedList.takeAt(row)->deleteLater();
endRemoveRows();
*/
return true;
}

View file

@ -23,6 +23,7 @@
#include <linphone++/linphone.hh>
#include <QAbstractListModel>
#include <QDate>
// =============================================================================
@ -38,14 +39,16 @@ public:
QHash<int, QByteArray> roleNames () const override;
QVariant data (const QModelIndex &index, int role = Qt::DisplayRole) const override;
ConferenceInfoModel* getAt(const int& index) const;
void add(std::shared_ptr<ConferenceInfoModel> conferenceInfoModel);
private:
bool removeRow (int row, const QModelIndex &parent = QModelIndex());
bool removeRows (int row, int count, const QModelIndex &parent = QModelIndex()) override;
QList<std::shared_ptr<ConferenceInfoModel>> mList;
//QMap<QDate, QList<ConferenceInfoModel*>> mMappedList;
};
Q_DECLARE_METATYPE(QList<ConferenceInfoModel*>*)
#endif

View file

@ -0,0 +1,77 @@
/*
* Copyright (c) 2021 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 "ConferenceInfoMapModel.hpp"
#include <QQmlApplicationEngine>
#include <QQuickWindow>
#include <QTimer>
#include "app/App.hpp"
#include "components/conference/ConferenceAddModel.hpp"
#include "components/conference/ConferenceHelperModel.hpp"
#include "components/core/CoreHandlers.hpp"
#include "components/core/CoreManager.hpp"
#include "components/settings/SettingsModel.hpp"
#include "utils/Utils.hpp"
#include "ConferenceInfoListModel.hpp"
#include "ConferenceInfoModel.hpp"
// =============================================================================
ConferenceInfoMapModel::ConferenceInfoMapModel (QObject *parent) : QAbstractListModel(parent) {
auto conferenceInfos = CoreManager::getInstance()->getCore()->getConferenceInformationList();
for(auto conferenceInfo : conferenceInfos){
auto conferenceInfoModel = ConferenceInfoModel::create( conferenceInfo );
QDate t = conferenceInfoModel->getDateTime().date();
if( !mMappedList.contains(t))
mMappedList[t] = new ConferenceInfoListModel();
mMappedList[t]->add(conferenceInfoModel);
}
}
int ConferenceInfoMapModel::rowCount (const QModelIndex &) const {
return mMappedList.size();
}
QHash<int, QByteArray> ConferenceInfoMapModel::roleNames () const {
QHash<int, QByteArray> roles;
roles[Qt::DisplayRole] = "modelData";
roles[Qt::DisplayRole + 1] = "date";
return roles;
}
QVariant ConferenceInfoMapModel::data (const QModelIndex &index, int role) const {
int row = index.row();
if (!index.isValid() || row < 0 || row >= mMappedList.size())
return QVariant();
auto it = mMappedList.begin() + row;
if (role == Qt::DisplayRole)
return QVariant::fromValue(*it);
else if( role == Qt::DisplayRole+1)
return QVariant::fromValue(it.key());
return QVariant();
}
// -----------------------------------------------------------------------------

View file

@ -0,0 +1,48 @@
/*
* Copyright (c) 2021 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 _CONFERENCE_INFO_MAP_MODEL_H_
#define _CONFERENCE_INFO_MAP_MODEL_H_
#include <linphone++/linphone.hh>
#include <QAbstractListModel>
#include <QDate>
// =============================================================================
class ConferenceInfoListModel;
class ConferenceInfoMapModel : public QAbstractListModel {
Q_OBJECT
public:
ConferenceInfoMapModel (QObject *parent = Q_NULLPTR);
int rowCount (const QModelIndex &index = QModelIndex()) const override;
QHash<int, QByteArray> roleNames () const override;
QVariant data (const QModelIndex &index, int role = Qt::DisplayRole) const override;
private:
QMap<QDate, ConferenceInfoListModel*> mMappedList;
};
Q_DECLARE_METATYPE(ConferenceInfoMapModel*)
#endif

View file

@ -45,6 +45,7 @@
#include "components/contact/ContactModel.hpp"
#include "components/contact/VcardModel.hpp"
#include "components/contacts/ContactsListModel.hpp"
#include "components/conferenceScheduler/ConferenceSchedulerModel.hpp"
#include "components/core/CoreHandlers.hpp"
#include "components/core/CoreManager.hpp"
#include "components/notifier/Notifier.hpp"
@ -75,7 +76,7 @@ using namespace std;
std::shared_ptr<ConferenceInfoModel> ConferenceInfoModel::create(std::shared_ptr<linphone::ConferenceInfo> conferenceInfo){
std::shared_ptr<ConferenceInfoModel> model = std::make_shared<ConferenceInfoModel>(conferenceInfo);
if(model){
//model->mSelf = model;
model->mSelf = model;
//chatRoom->addListener(model);
return model;
}else
@ -85,6 +86,9 @@ std::shared_ptr<ConferenceInfoModel> ConferenceInfoModel::create(std::shared_ptr
ConferenceInfoModel::ConferenceInfoModel (QObject * parent) : QObject(parent){
//App::getInstance()->getEngine()->setObjectOwnership(this, QQmlEngine::CppOwnership);// Avoid QML to destroy it when passing by Q_INVOKABLE
mConferenceInfo = linphone::Factory::get()->createConferenceInfo();
auto accountAddress = CoreManager::getInstance()->getCore()->getDefaultAccount()->getContactAddress();
accountAddress->clean();
mConferenceInfo->setOrganizer(accountAddress);
}
ConferenceInfoModel::ConferenceInfoModel (std::shared_ptr<linphone::ConferenceInfo> conferenceInfo, QObject * parent) : QObject(parent){
@ -147,6 +151,10 @@ QString ConferenceInfoModel::getUri() const{
return QString::fromStdString(mConferenceInfo->getUri()->asString());
}
bool ConferenceInfoModel::isScheduled() const{
return mIsScheduled;
}
//------------------------------------------------------------------------------------------------
void ConferenceInfoModel::setDateTime(const QDateTime& dateTime){
@ -178,3 +186,55 @@ void ConferenceInfoModel::setDescription(const QString& description){
void ConferenceInfoModel::setParticipants(ParticipantListModel * participants){
mConferenceInfo->setParticipants(participants->getParticipants());
}
void ConferenceInfoModel::setIsScheduled(const bool& on){
if( mIsScheduled != on){
mIsScheduled = on;
emit isScheduledChanged();
}
}
//-------------------------------------------------------------------------------------------------
void ConferenceInfoModel::createConference(const int& securityLevel, const int& inviteMode) {
CoreManager::getInstance()->getTimelineListModel()->mAutoSelectAfterCreation = false;
shared_ptr<linphone::Core> core = CoreManager::getInstance()->getCore();
static std::shared_ptr<linphone::Conference> conference;
qInfo() << "Conference creation of " << getSubject() << " at " << securityLevel << " security, organized by " << getOrganizer();// and with " << conferenceInfo->getConferenceInfo()->getParticipants().size();
if( true || isScheduled()){
mConferenceSchedulerModel = ConferenceSchedulerModel::create(this);
connect(mConferenceSchedulerModel.get(), &ConferenceSchedulerModel::invitationsSent, this, &ConferenceInfoModel::onInvitationsSent);
mConferenceSchedulerModel->getConferenceScheduler()->setInfo(mConferenceInfo);
}else{
auto conferenceParameters = core->createConferenceParams();
conferenceParameters->enableAudio(true);
conferenceParameters->enableVideo(true);
conferenceParameters->setDescription(mConferenceInfo->getDescription());
conferenceParameters->setSubject(mConferenceInfo->getSubject());
conferenceParameters->setStartTime(mConferenceInfo->getDateTime());
conferenceParameters->setEndTime(mConferenceInfo->getDateTime() + (mConferenceInfo->getDuration() * 60));
conferenceParameters->enableLocalParticipant(true);
//conferenceParameters->enableOneParticipantConference(true);
/*
if(true) {//Remote
conferenceParameters->setConferenceFactoryUri(core->getDefaultAccount()->getContactAddress()->asStringUriOnly());
}else
conferenceParameters->setConferenceFactoryUri(nullptr);
*/
conference = core->createConferenceWithParams(conferenceParameters);
//auto parameters = CoreManager::getInstance()->getCore()->createCallParams(nullptr);
//parameters->enableVideo(true);
//conference->inviteParticipants(mConferenceInfo->getParticipants(), parameters);
}
}
//-------------------------------------------------------------------------------------------------
void ConferenceInfoModel::onInvitationsSent(const std::list<std::shared_ptr<linphone::Address>> & failedInvitations) {
mConferenceSchedulerModel->getConferenceScheduler()->removeListener(mConferenceSchedulerModel);
emit invitationsSent();
}

View file

@ -26,8 +26,9 @@
#include <QObject>
class ParticipantListModel;
class ConferenceSchedulerModel;
class ConferenceInfoModel : public QObject{
class ConferenceInfoModel : public QObject {
Q_OBJECT
public:
@ -40,6 +41,7 @@ public:
Q_PROPERTY(QString description READ getDescription WRITE setDescription NOTIFY descriptionChanged)
Q_PROPERTY(QString displayNamesToString READ displayNamesToString NOTIFY participantsChanged)
Q_PROPERTY(QString uri READ getUri NOTIFY uriChanged)
Q_PROPERTY(bool isScheduled READ isScheduled WRITE setIsScheduled NOTIFY isScheduledChanged)
//Q_PROPERTY(participants READ getParticipants WRITE setParticipants NOTIFY participantsChanged)
@ -59,15 +61,25 @@ public:
QString getDescription() const;
Q_INVOKABLE QString displayNamesToString()const;
QString getUri() const;
bool isScheduled() const;
void setDateTime(const QDateTime& dateTime);
void setDuration(const int& duration);
void setSubject(const QString& subject);
void setOrganizer(const QString& organizerAddress);
void setDescription(const QString& description);
void setIsScheduled(const bool& on);
Q_INVOKABLE void setParticipants(ParticipantListModel * participants);
// Tools
Q_INVOKABLE void createConference(const int& securityLevel, const int& inviteMode);
// SCHEDULER
//virtual void onStateChanged(const std::shared_ptr<linphone::ConferenceScheduler> & conferenceScheduler, linphone::ConferenceSchedulerState state) override;
virtual void onInvitationsSent(const std::list<std::shared_ptr<linphone::Address>> & failedInvitations);
signals:
void dateTimeChanged();
@ -77,12 +89,19 @@ signals:
void descriptionChanged();
void participantsChanged();
void uriChanged();
void isScheduledChanged();
void conferenceCreated();
void invitationsSent();
private:
std::shared_ptr<linphone::ConferenceInfo> mConferenceInfo;
std::shared_ptr<ConferenceSchedulerModel> mConferenceSchedulerModel = nullptr;
std::weak_ptr<ConferenceInfoModel> mSelf; // For Linphone listener
bool mIsScheduled = true;
};
Q_DECLARE_METATYPE(std::shared_ptr<ConferenceInfoModel>)
Q_DECLARE_METATYPE(ConferenceInfoModel*)
#endif

View file

@ -22,6 +22,7 @@
#include "components/core/CoreManager.hpp"
#include "ConferenceInfoListModel.hpp"
#include "ConferenceInfoMapModel.hpp"
#include "ConferenceInfoProxyModel.hpp"
// =============================================================================
@ -29,7 +30,7 @@
using namespace std;
ConferenceInfoProxyModel::ConferenceInfoProxyModel (QObject *parent) : QSortFilterProxyModel(parent) {
setSourceModel(new ConferenceInfoListModel());
setSourceModel(new ConferenceInfoMapModel());
sort(0, Qt::DescendingOrder);
}
@ -38,10 +39,10 @@ bool ConferenceInfoProxyModel::filterAcceptsRow (int sourceRow, const QModelInde
}
bool ConferenceInfoProxyModel::lessThan (const QModelIndex &left, const QModelIndex &right) const {
const ConferenceInfoModel *deviceA = sourceModel()->data(left).value<ConferenceInfoModel *>();
const ConferenceInfoModel *deviceB = sourceModel()->data(right).value<ConferenceInfoModel *>();
const ConferenceInfoListModel* deviceA = sourceModel()->data(left).value<ConferenceInfoListModel*>();
const ConferenceInfoListModel* deviceB = sourceModel()->data(right).value<ConferenceInfoListModel*>();
return deviceA->getDateTime() < deviceB->getDateTime();
return deviceA->getAt(0)->getDateTime() < deviceB->getAt(0)->getDateTime();
}
QVariant ConferenceInfoProxyModel::getAt(int row){

View file

@ -0,0 +1,62 @@
/*
* Copyright (c) 2021 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 "ConferenceSchedulerModel.hpp"
#include <QQmlApplicationEngine>
#include "app/App.hpp"
#include "components/core/CoreManager.hpp"
// =============================================================================
std::shared_ptr<ConferenceSchedulerModel> ConferenceSchedulerModel::create( QObject *parent){
std::shared_ptr<ConferenceSchedulerModel> model = std::make_shared<ConferenceSchedulerModel>(parent);
if(model){
model->mSelf = model;
model->mConferenceScheduler->addListener(model);
return model;
}
return nullptr;
}
ConferenceSchedulerModel::ConferenceSchedulerModel (QObject * parent) : QObject(parent){
App::getInstance()->getEngine()->setObjectOwnership(this, QQmlEngine::CppOwnership);// Avoid QML to destroy it when passing by Q_INVOKABLE
mConferenceScheduler = CoreManager::getInstance()->getCore()->createConferenceScheduler();
}
ConferenceSchedulerModel::~ConferenceSchedulerModel () {
}
std::shared_ptr<linphone::ConferenceScheduler> ConferenceSchedulerModel::getConferenceScheduler(){
return mConferenceScheduler;
}
void ConferenceSchedulerModel::onStateChanged(const std::shared_ptr<linphone::ConferenceScheduler> & conferenceScheduler, linphone::ConferenceSchedulerState state) {
emit stateChanged(state);
qWarning() << "ConferenceSchedulerModel::onStateChanged : " << (int)state;
if( state == linphone::ConferenceSchedulerState::Ready) {
std::shared_ptr<linphone::ChatRoomParams> params = CoreManager::getInstance()->getCore()->createDefaultChatRoomParams();
params->setBackend(linphone::ChatRoomBackend::Basic);
mConferenceScheduler->sendInvitations(params);
}
}
void ConferenceSchedulerModel::onInvitationsSent(const std::shared_ptr<linphone::ConferenceScheduler> & conferenceScheduler, const std::list<std::shared_ptr<linphone::Address>> & failedInvitations) {
emit invitationsSent(failedInvitations);
}

View file

@ -0,0 +1,54 @@
/*
* Copyright (c) 2021 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 CONFERENCE_SCHEDULER_MODEL_H_
#define CONFERENCE_SCHEDULER_MODEL_H_
#include <linphone++/linphone.hh>
#include <QDateTime>
#include <QObject>
class ConferenceSchedulerModel : public QObject
, public linphone::ConferenceSchedulerListener
{
Q_OBJECT
public:
static std::shared_ptr<ConferenceSchedulerModel> create(QObject *parent = Q_NULLPTR);
ConferenceSchedulerModel (QObject * parent = nullptr);
~ConferenceSchedulerModel ();
std::shared_ptr<linphone::ConferenceScheduler> getConferenceScheduler();
virtual void onStateChanged(const std::shared_ptr<linphone::ConferenceScheduler> & conferenceScheduler, linphone::ConferenceSchedulerState state) override;
virtual void onInvitationsSent(const std::shared_ptr<linphone::ConferenceScheduler> & conferenceScheduler, const std::list<std::shared_ptr<linphone::Address>> & failedInvitations) override;
signals:
void stateChanged(linphone::ConferenceSchedulerState state);
void invitationsSent(const std::list<std::shared_ptr<linphone::Address>> & failedInvitations);
private:
std::shared_ptr<linphone::ConferenceScheduler> mConferenceScheduler;
std::weak_ptr<ConferenceSchedulerModel> mSelf; // Used for Linphone Listener
};
Q_DECLARE_METATYPE(std::shared_ptr<ConferenceSchedulerModel>)
#endif

View file

@ -87,6 +87,13 @@ QString ContentModel::getUtf8Text() const{
return QString::fromStdString(mContent->getUtf8Text());
}
ConferenceInfoModel * ContentModel::getConferenceInfoModel(){
if( !mConferenceInfoModel && isIcalendar()){
mConferenceInfoModel = ConferenceInfoModel::create(linphone::Factory::get()->createConferenceInfoFromIcalendarContent(mContent));
}
return mConferenceInfoModel.get();
}
void ContentModel::setFileOffset(quint64 fileOffset){
if( mFileOffset != fileOffset) {
mFileOffset = fileOffset;
@ -109,6 +116,7 @@ void ContentModel::setWasDownloaded(bool wasDownloaded){
void ContentModel::setContent(std::shared_ptr<linphone::Content> content){
mContent = content;
emit nameChanged();
mConferenceInfoModel = nullptr;
if(isFile() || isFileEncrypted() || isFileTransfer() ){
QString path = Utils::coreStringToAppString(mContent->getFilePath());
if (!path.isEmpty())

View file

@ -29,6 +29,8 @@
#include <QString>
#include "components/chat-events/ChatMessageModel.hpp"
class ChatMessageModel;
class ConferenceInfoModel;
class ContentModel : public QObject{
Q_OBJECT
@ -44,6 +46,7 @@ public:
Q_PROPERTY(bool wasDownloaded MEMBER mWasDownloaded WRITE setWasDownloaded NOTIFY wasDownloadedChanged)
Q_PROPERTY(QString filePath READ getFilePath CONSTANT)
Q_PROPERTY(ChatMessageModel * chatMessageModel READ getChatMessageModel CONSTANT)
Q_PROPERTY(ConferenceInfoModel * conferenceInfoModel READ getConferenceInfoModel CONSTANT)
Q_PROPERTY(QString text READ getUtf8Text CONSTANT)
std::shared_ptr<linphone::Content> getContent()const;
@ -54,6 +57,7 @@ public:
QString getThumbnail() const;
QString getFilePath() const;
QString getUtf8Text() const;
ConferenceInfoModel * getConferenceInfoModel();//Create a conference Info if not set
void setFileOffset(quint64 fileOffset);
void setThumbnail(const QString& data);
@ -93,6 +97,7 @@ private:
std::shared_ptr<linphone::Content> mContent;
ChatMessageModel* mChatMessageModel;
ChatMessageModel::AppDataManager mAppData; // Used if there is no Chat Message model set.
std::shared_ptr<ConferenceInfoModel> mConferenceInfoModel;
};
Q_DECLARE_METATYPE(std::shared_ptr<ContentModel>)

View file

@ -325,7 +325,32 @@ void CoreHandlers::onEcCalibrationResult(
}
//------------------------------ CONFERENCE INFO
void CoreHandlers::onConferenceInfoReceived(const std::shared_ptr<linphone::Core> & core, const std::shared_ptr<const linphone::ConferenceInfo> & conferenceInfo) {
qWarning() << "onConferenceInfoReceived";
/*
qWarning() << "onConferenceInfoReceived : sending invitation only for known participants (API fail? this should be done from SDK)";
qWarning() << "onConferenceInfoReceived : Duration: " << conferenceInfo->getDuration();
for(auto participant : conferenceInfo->getParticipants()){
std::shared_ptr<linphone::ChatRoomParams> params = core->createDefaultChatRoomParams();
std::list<std::shared_ptr<linphone::Address>> participants;
std::shared_ptr<linphone::ChatRoom> chatRoom;
chatRoom = core->searchChatRoom(params, conferenceInfo->getOrganizer()
, participant
, participants);
if(chatRoom) {
auto timeLine = CoreManager::getInstance()->getTimelineListModel()->getChatRoomModel(chatRoom, true);
if(timeLine)
timeLine->sendMessage("Conference invitation : " +QString::fromStdString(conferenceInfo->getUri()->asString()));
else
qWarning() << "Cannot use a timeline for invitation";
}else
qWarning() << "Cannot use a chatroom for invitation";
}
*/
}
/*
void CoreHandlers::onConferenceInfoCreated(const std::shared_ptr<linphone::Core> & core, const std::shared_ptr<const linphone::ConferenceInfo> & conferenceInfo){
qWarning() << "onConferenceInfoCreated : sending invitation only for known participants (API fail? this should be done from SDK)";
qWarning() << "onConferenceInfoCreated : Duration: " << conferenceInfo->getDuration();
@ -358,4 +383,5 @@ void CoreHandlers::onConferenceInfoParticipantSent(const std::shared_ptr<linphon
void CoreHandlers::onConferenceInfoParticipantError(const std::shared_ptr<linphone::Core> & core, const std::shared_ptr<const linphone::ConferenceInfo> & conferenceInfo, const std::shared_ptr<const linphone::Address> & participant, linphone::ConferenceInfoError error){
qWarning() << "onConferenceInfoParticipantError";
}
}
*/

View file

@ -186,13 +186,14 @@ private:
) override;
// Conference Info
virtual void onConferenceInfoReceived(const std::shared_ptr<linphone::Core> & core, const std::shared_ptr<const linphone::ConferenceInfo> & conferenceInfo) override;
/*
virtual void onConferenceInfoCreated(const std::shared_ptr<linphone::Core> & core, const std::shared_ptr<const linphone::ConferenceInfo> & conferenceInfo);
virtual void onConferenceInfoOnSent(const std::shared_ptr<linphone::Core> & core, const std::shared_ptr<const linphone::ConferenceInfo> & conferenceInfo);
virtual void onConferenceInfoParticipantSent(const std::shared_ptr<linphone::Core> & core, const std::shared_ptr<const linphone::ConferenceInfo> & conferenceInfo, const std::shared_ptr<const linphone::Address> & participant);
virtual void onConferenceInfoParticipantError(const std::shared_ptr<linphone::Core> & core, const std::shared_ptr<const linphone::ConferenceInfo> & conferenceInfo, const std::shared_ptr<const linphone::Address> & participant, linphone::ConferenceInfoError error);
*/
// ---------------------------------------------------------------------------
};
#endif // CORE_HANDLERS_H_

View file

@ -19,6 +19,7 @@
*/
#include <QQmlApplicationEngine>
#include <algorithm>
#include "app/App.hpp"
@ -34,6 +35,7 @@ ParticipantDeviceListModel::ParticipantDeviceListModel (std::shared_ptr<linphone
for(auto device : devices){
auto deviceModel = std::make_shared<ParticipantDeviceModel>(device, false);
connect(this, &ParticipantDeviceListModel::securityLevelChanged, deviceModel.get(), &ParticipantDeviceModel::onSecurityLevelChanged);
connect(this, &ParticipantDeviceListModel::participantDeviceMediaChanged, deviceModel.get(), &ParticipantDeviceModel::videoEnabledChanged);
mList << deviceModel;
}
}
@ -42,26 +44,30 @@ ParticipantDeviceListModel::ParticipantDeviceListModel (CallModel * callModel, Q
if(callModel && callModel->isConference()) {
mCallModel = callModel;
auto conferenceModel = callModel->getConferenceModel();
auto meDevices = conferenceModel->getConference()->getMe()->getDevices();
if(meDevices.size() > 0)
mList << std::make_shared<ParticipantDeviceModel>(meDevices.front(), true);// Add Me in device list
else
mList << std::make_shared<ParticipantDeviceModel>(nullptr, true);
/*
mList << std::make_shared<ParticipantDeviceModel>(callModel, true);// Add Me in device list
qWarning() << "Me devices : " << conferenceModel->getConference()->getMe()->getDevices().size();
// auto meDevices = conferenceModel->getConference()->getMe()->getDevices();
// if(meDevices.size() > 0)
std::list<std::shared_ptr<linphone::ParticipantDevice>> devices = conferenceModel->getConference()->getParticipantDeviceList();
for(auto device : devices){
auto deviceModel = std::make_shared<ParticipantDeviceModel>(device, false);
connect(this, &ParticipantDeviceListModel::securityLevelChanged, deviceModel.get(), &ParticipantDeviceModel::onSecurityLevelChanged);
mList << deviceModel;
}
updateDevices(devices);
qWarning() << "Instanciate Participant Device list model with " << mList.size() << " devices";
*/
connect(conferenceModel.get(), &ConferenceModel::participantDeviceAdded, this, &ParticipantDeviceListModel::onParticipantDeviceAdded);
connect(conferenceModel.get(), &ConferenceModel::participantDeviceRemoved, this, &ParticipantDeviceListModel::onParticipantDeviceRemoved);
connect(conferenceModel.get(), &ConferenceModel::participantDeviceMediaChanged, this, &ParticipantDeviceListModel::onParticipantDeviceMediaChanged);
connect(conferenceModel.get(), &ConferenceModel::conferenceStateChanged, this, &ParticipantDeviceListModel::onConferenceStateChanged);
}
}
int ParticipantDeviceListModel::rowCount (const QModelIndex &index) const{
qWarning() << "rowCount: " << mList.count();
return mList.count();
}
int ParticipantDeviceListModel::count(){
qWarning() << "count: " << mList.count();
return mList.count();
}
@ -72,12 +78,43 @@ void ParticipantDeviceListModel::updateDevices(std::shared_ptr<linphone::Partici
for(auto device : devices){
auto deviceModel = std::make_shared<ParticipantDeviceModel>(device, false);
connect(this, &ParticipantDeviceListModel::securityLevelChanged, deviceModel.get(), &ParticipantDeviceModel::onSecurityLevelChanged);
connect(this, &ParticipantDeviceListModel::participantDeviceMediaChanged, deviceModel.get(), &ParticipantDeviceModel::videoEnabledChanged);
mList << deviceModel;
}
endResetModel();
emit countChanged();
emit layoutChanged();
}
void ParticipantDeviceListModel::updateDevices(const std::list<std::shared_ptr<linphone::ParticipantDevice>>& devices, const bool& isMe){
QList<std::shared_ptr<ParticipantDeviceModel>> devicesToAdd;
//auto meDevices = mCallModel->getConferenceModel()->getConference()->getMe()->getDevices();
for(auto device : devices){
auto deviceAddress = device->getAddress();
//bool isMe = false;
//for(auto meDevice : meDevices)
//isMe |= meDevice->getAddress() == deviceAddress;
//if( !isMe) {
auto exist = std::find_if(mList.begin(), mList.end(), [deviceAddress](const std::shared_ptr<ParticipantDeviceModel>& activeDevice){
return deviceAddress == activeDevice->getDevice()->getAddress();
});
if(exist == mList.end()){
auto deviceModel = std::make_shared<ParticipantDeviceModel>(device, isMe);
connect(this, &ParticipantDeviceListModel::securityLevelChanged, deviceModel.get(), &ParticipantDeviceModel::onSecurityLevelChanged);
connect(this, &ParticipantDeviceListModel::participantDeviceMediaChanged, deviceModel.get(), &ParticipantDeviceModel::videoEnabledChanged);
devicesToAdd << deviceModel;
}
//}
}
if(devicesToAdd.size() > 0){
int row = mList.count();
beginInsertRows(QModelIndex(), row, row+devicesToAdd.size()-1);
mList << devicesToAdd;
endInsertRows();
emit countChanged();
}
}
QHash<int, QByteArray> ParticipantDeviceListModel::roleNames () const {
QHash<int, QByteArray> roles;
roles[Qt::DisplayRole] = "$participantDevice";
@ -124,37 +161,79 @@ void ParticipantDeviceListModel::onSecurityLevelChanged(std::shared_ptr<const li
//----------------------------------------------------------------------------------------------------------
void ParticipantDeviceListModel::onParticipantDeviceAdded(const std::shared_ptr<const linphone::ParticipantDevice> & participantDevice){
qWarning() << "Adding participant";
auto conferenceModel = mCallModel->getConferenceModel();
std::list<std::shared_ptr<linphone::ParticipantDevice>> devices = conferenceModel->getConference()->getParticipantDeviceList();
for(auto realParticipantDevice : devices){
if( realParticipantDevice == participantDevice){
int row = mList.count();
beginInsertRows(QModelIndex(), row, row);
auto deviceModel = std::make_shared<ParticipantDeviceModel>(realParticipantDevice, this);
auto deviceModel = std::make_shared<ParticipantDeviceModel>(realParticipantDevice, false);
connect(this, &ParticipantDeviceListModel::securityLevelChanged, deviceModel.get(), &ParticipantDeviceModel::onSecurityLevelChanged);
connect(this, &ParticipantDeviceListModel::participantDeviceMediaChanged, deviceModel.get(), &ParticipantDeviceModel::videoEnabledChanged);
mList << deviceModel;
endInsertRows();
emit layoutChanged();
emit countChanged();
//emit layoutChanged();
return;
}
}
qWarning() << "No participant device found from const linphone::ParticipantDevice at onParticipantDeviceAdded";
}
void ParticipantDeviceListModel::onParticipantDeviceRemoved(const std::shared_ptr<const linphone::ParticipantDevice> & participantDevice){
qWarning() << "Removing participant";
int row = 0;
for(auto device : mList){
if( device->getDevice() == participantDevice){
removeRow(row);
emit layoutChanged();
emit countChanged();
//emit layoutChanged();
return;
}
++row;
}
qWarning() << "No participant device found from const linphone::ParticipantDevice at onParticipantDeviceRemoved";
}
void ParticipantDeviceListModel::onParticipantDeviceJoined(const std::shared_ptr<const linphone::ParticipantDevice> & participantDevice){
qWarning() << "onParticipantDeviceJoined is not yet implemented. Current participants count: " << mList.size();
}
void ParticipantDeviceListModel::onParticipantDeviceLeft(const std::shared_ptr<const linphone::ParticipantDevice> & participantDevice){
qWarning() << "onParticipantDeviceLeft is not yet implemented. Current participants count: " << mList.size();
}
void ParticipantDeviceListModel::onParticipantDeviceMediaChanged(const std::shared_ptr<const linphone::ParticipantDevice> & participantDevice) {
emit participantDeviceMediaChanged();
}
void ParticipantDeviceListModel::onConferenceStateChanged(linphone::Conference::State newState){
if(newState == linphone::Conference::State::Created){
if(mCallModel && mCallModel->isConference()) {
auto conferenceModel = mCallModel->getConferenceModel();
updateDevices(mCallModel->getConferenceModel()->getConference()->getMe()->getDevices(), true);
updateDevices(conferenceModel->getConference()->getParticipantDeviceList(), false);
}
/*
auto devices = mCallModel->getConferenceModel()->getConference()->getMe()->getDevices();
if(devices.size() > 0 && mList.size() == 1){
//qWarning() << "Adding Me in list. Count=" << mList.size();
beginInsertRows(QModelIndex(), 0, 0);
mList.push_front(std::make_shared<ParticipantDeviceModel>(mCallModel, true));// Add Me in device list
endInsertRows();
emit countChanged();
emit layoutChanged();
qWarning() << "M added in list. Count=" << mList.size() << ".\n\tConfVideo is enabled:" << mCallModel->getConferenceModel()->getConference()->getCurrentParams()->videoEnabled()
<< "\n\tCallVideo is enabled: " << mCallModel->getVideoEnabled();
}else
qWarning() << "Me cannot be add : no Me device.";
}else {
if(!mCallModel)
qWarning() << "Cannot add me : no call.";
else
qWarning() << "Cannot add me : No in conf.";
}
*/
}
}

View file

@ -43,6 +43,7 @@ public:
int count();
void updateDevices(std::shared_ptr<linphone::Participant> participant);
void updateDevices(const std::list<std::shared_ptr<linphone::ParticipantDevice>>& devices, const bool& isMe);
virtual QHash<int, QByteArray> roleNames () const override;
virtual QVariant data (const QModelIndex &index, int role = Qt::DisplayRole) const override;
@ -54,9 +55,13 @@ public slots:
void onParticipantDeviceRemoved(const std::shared_ptr<const linphone::ParticipantDevice> & participantDevice);
void onParticipantDeviceJoined(const std::shared_ptr<const linphone::ParticipantDevice> & participantDevice);
void onParticipantDeviceLeft(const std::shared_ptr<const linphone::ParticipantDevice> & participantDevice);
void onParticipantDeviceMediaChanged(const std::shared_ptr<const linphone::ParticipantDevice> & participantDevice);
void onConferenceStateChanged(linphone::Conference::State newState);
signals:
void securityLevelChanged(std::shared_ptr<const linphone::Address> device);
void countChanged();
void participantDeviceMediaChanged();
private:
bool removeRow (int row, const QModelIndex &parent = QModelIndex());

View file

@ -28,8 +28,17 @@
// =============================================================================
ParticipantDeviceModel::ParticipantDeviceModel (std::shared_ptr<linphone::ParticipantDevice> device, const bool& isMe, QObject *parent) : QObject(parent) {
App::getInstance()->getEngine()->setObjectOwnership(this, QQmlEngine::CppOwnership);// Avoid QML to destroy it when passing by Q_INVOKABLE
mIsMe = isMe;
mParticipantDevice = device;
mCall = nullptr;
}
ParticipantDeviceModel::ParticipantDeviceModel (CallModel * call, const bool& isMe, QObject *parent) : QObject(parent) {
App::getInstance()->getEngine()->setObjectOwnership(this, QQmlEngine::CppOwnership);// Avoid QML to destroy it when passing by Q_INVOKABLE
mIsMe = isMe;
mCall = call;
connect(call, &CallModel::statusChanged, this, &ParticipantDeviceModel::videoEnabledChanged);
}
// -----------------------------------------------------------------------------
@ -60,8 +69,9 @@ std::shared_ptr<linphone::ParticipantDevice> ParticipantDeviceModel::getDevice(
}
bool ParticipantDeviceModel::isVideoEnabled() const{
return mParticipantDevice && (mParticipantDevice->getVideoDirection() == linphone::MediaDirection::SendRecv
|| mParticipantDevice->getVideoDirection() == linphone::MediaDirection::SendOnly);
if(mParticipantDevice)
qWarning() << "VideoEnabled: " << (int)mParticipantDevice->getStreamAvailability(linphone::StreamType::Video);
return mParticipantDevice && mParticipantDevice->getStreamAvailability(linphone::StreamType::Video);
}
bool ParticipantDeviceModel::isMe() const{

View file

@ -28,11 +28,14 @@
#include <QDateTime>
#include <QString>
class CallModel;
class ParticipantDeviceModel : public QObject {
Q_OBJECT
public:
ParticipantDeviceModel (std::shared_ptr<linphone::ParticipantDevice> device, const bool& isMe = false, QObject *parent = nullptr);
ParticipantDeviceModel (CallModel * call, const bool& isMe = true, QObject *parent = nullptr);
Q_PROPERTY(QString name READ getName CONSTANT)
Q_PROPERTY(QString address READ getAddress CONSTANT)
@ -63,6 +66,7 @@ private:
bool mIsMe = false;
std::shared_ptr<linphone::ParticipantDevice> mParticipantDevice;
CallModel * mCall;
};

View file

@ -61,12 +61,26 @@ ParticipantDeviceModel *ParticipantDeviceProxyModel::getAt(int row){
CallModel * ParticipantDeviceProxyModel::getCallModel() const{
return mCallModel;
}
int ParticipantDeviceProxyModel::getCount() const{
ParticipantDeviceListModel* devices = dynamic_cast<ParticipantDeviceListModel*>(sourceModel());
if(devices)
return devices->rowCount();
else
return 0;
}
void ParticipantDeviceProxyModel::setCallModel(CallModel * callModel){
mCallModel = callModel;
setSourceModel(new ParticipantDeviceListModel(mCallModel));
auto sourceModel = new ParticipantDeviceListModel(mCallModel);
connect(sourceModel, &ParticipantDeviceListModel::countChanged, this, &ParticipantDeviceProxyModel::countChanged);
setSourceModel(sourceModel);
emit countChanged();
}
void ParticipantDeviceProxyModel::setParticipant(ParticipantModel * participant){
setSourceModel(participant->getParticipantDevices().get());
auto sourceModel = participant->getParticipantDevices().get();
connect(sourceModel, &ParticipantDeviceListModel::countChanged, this, &ParticipantDeviceProxyModel::countChanged);
setSourceModel(sourceModel);
emit countChanged();
}

View file

@ -39,10 +39,12 @@ class ParticipantDeviceProxyModel : public QSortFilterProxyModel {
public:
Q_PROPERTY(CallModel * callModel READ getCallModel WRITE setCallModel NOTIFY callModelChanged)
Q_PROPERTY(int count READ getCount NOTIFY countChanged)
ParticipantDeviceProxyModel (QObject *parent = nullptr);
Q_INVOKABLE ParticipantDeviceModel* getAt(int row);
CallModel * getCallModel() const;
Q_INVOKABLE int getCount() const;
void setCallModel(CallModel * callModel);
@ -50,6 +52,7 @@ public:
signals:
void callModelChanged();
void countChanged();
protected:
virtual bool filterAcceptsRow (int sourceRow, const QModelIndex &sourceParent) const override;

View file

@ -384,11 +384,11 @@ void TimelineListModel::onCallStateChanged (const std::shared_ptr<linphone::Call
}
void TimelineListModel::onCallCreated(const std::shared_ptr<linphone::Call> &call){
std::shared_ptr<linphone::Core> core = CoreManager::getInstance()->getCore();
std::shared_ptr<linphone::ChatRoomParams> params = core->createDefaultChatRoomParams();
std::list<std::shared_ptr<linphone::Address>> participants;
// Find all chat rooms with local address. If not, create one.
std::shared_ptr<linphone::Core> core = CoreManager::getInstance()->getCore();
std::shared_ptr<linphone::ChatRoomParams> params = core->createDefaultChatRoomParams();
std::list<std::shared_ptr<linphone::Address>> participants;
if( !call->getConference() && false ){
// Find all chat rooms with local address. If not, create one.
bool isOutgoing = (call->getDir() == linphone::Call::Dir::Outgoing) ;
bool found = false;
auto callLog = call->getCallLog();
@ -429,6 +429,7 @@ void TimelineListModel::onCallCreated(const std::shared_ptr<linphone::Call> &cal
participants << Utils::coreStringToAppString(remoteAddress->asStringUriOnly());
CoreManager::getInstance()->getCallsListModel()->createChatRoom("", (createSecureChatRoom?1:0), callLocalAddress, participants, isOutgoing);
}
}
}
/*

View file

@ -164,6 +164,8 @@ ColumnLayout{
removeDisplaced: defaultTransition
populate:defaultTransition
onItemCountChanged: console.log("Mosaic "+model+" itemCount: " +itemCount +" => " + (model.count ? " count="+model.count :( model.length ? " length":" no" )))
}
/*
ListView{

View file

@ -0,0 +1,143 @@
import QtQuick 2.7
import QtQuick.Layouts 1.3
import Clipboard 1.0
import Common 1.0
import Linphone 1.0
import Common.Styles 1.0
import Linphone.Styles 1.0
import TextToSpeech 1.0
import Utils 1.0
import Units 1.0
import UtilsCpp 1.0
import LinphoneEnums 1.0
import ColorsList 1.0
import 'Message.js' as Logic
// =============================================================================
Loader{
id: mainItem
property ContentModel contentModel
property ConferenceInfoModel conferenceInfoModel: contentModel && active ? contentModel.conferenceInfoModel : null
property int maxWidth : parent.width
property int fitHeight: active && item ? item.fitHeight + ChatCalendarMessageStyle.heightMargin*2 : 0
property int fitWidth: active && item ? Math.max(item.fitWidth, maxWidth/2) + ChatCalendarMessageStyle.widthMargin*2 : 0
width: parent.width
height: fitHeight
property font customFont : SettingsModel.textMessageFont
active: (mainItem.contentModel && mainItem.contentModel.isIcalendar()) || (!mainItem.contentModel && mainItem.conferenceInfoModel)
sourceComponent: MouseArea{
id: loadedItem
property int fitHeight: layout.fitHeight
property int fitWidth: layout.fitWidth
anchors.fill: parent
anchors.leftMargin: ChatCalendarMessageStyle.widthMargin
anchors.rightMargin: ChatCalendarMessageStyle.widthMargin
anchors.topMargin: ChatCalendarMessageStyle.heightMargin
anchors.bottomMargin: ChatCalendarMessageStyle.heightMargin
clip: false
cursorShape: Qt.ArrowCursor
hoverEnabled: true
onClicked: CallsListModel.launchVideoCall(mainItem.conferenceInfoModel.uri, '', 0)
ColumnLayout{
id: layout
property int fitHeight: Layout.minimumHeight
property int fitWidth: Layout.minimumWidth
anchors.fill: parent
spacing: 0
RowLayout {
Layout.fillWidth: true
Layout.preferredWidth: parent.width // Need this because fillWidth is not enough...
Layout.preferredHeight: ChatCalendarMessageStyle.schedule.iconSize
spacing: 10
RowLayout {
id: scheduleRow
Layout.fillWidth: true
Layout.preferredHeight: ChatCalendarMessageStyle.schedule.iconSize
Layout.leftMargin: 5
spacing: ChatCalendarMessageStyle.schedule.spacing
Icon{
icon: ChatCalendarMessageStyle.schedule.icon
iconSize: ChatCalendarMessageStyle.schedule.iconSize
overwriteColor: ChatCalendarMessageStyle.schedule.color
}
Text {
id: conferenceTime
Layout.fillWidth: true
Layout.minimumWidth: implicitWidth
verticalAlignment: Qt.AlignVCenter
color: ChatCalendarMessageStyle.schedule.color
elide: Text.ElideRight
font.pointSize: ChatCalendarMessageStyle.schedule.pointSize
text: Qt.formatDateTime(mainItem.conferenceInfoModel.dateTime, 'hh:mm')
+' - ' +Qt.formatDateTime(mainItem.conferenceInfoModel.endDateTime, 'hh:mm')
}
}
Item{
Layout.fillWidth: true
Layout.fillHeight: true
}
Text{
Layout.fillHeight: true
Layout.minimumWidth: implicitWidth
Layout.preferredWidth: implicitWidth
Layout.rightMargin: 10
verticalAlignment: Qt.AlignVCenter
color: ChatCalendarMessageStyle.schedule.color
elide: Text.ElideRight
font.pointSize: ChatCalendarMessageStyle.schedule.pointSize
text: 'Organisateur : ' +UtilsCpp.getDisplayName(mainItem.conferenceInfoModel.organizer)
}
}
Text{
id: title
Layout.fillWidth: true
Layout.minimumWidth: implicitWidth
Layout.topMargin: 10
Layout.leftMargin: 10
Layout.alignment: Qt.AlignRight
color: ChatCalendarMessageStyle.subject.color
font.pointSize: ChatCalendarMessageStyle.subject.pointSize
font.weight: Font.Bold
text: mainItem.conferenceInfoModel.subject
}
RowLayout {
id: participantsRow
Layout.fillWidth: true
Layout.fillHeight: true
Layout.leftMargin: 5
spacing: ChatCalendarMessageStyle.participants.spacing
Icon{
icon: ChatCalendarMessageStyle.participants.icon
iconSize: ChatCalendarMessageStyle.participants.iconSize
overwriteColor: ChatCalendarMessageStyle.participants.color
}
Text {
id: participantsList
Layout.fillWidth: true
Layout.minimumWidth: implicitWidth
color: ChatCalendarMessageStyle.participants.color
elide: Text.ElideRight
font.pointSize: ChatCalendarMessageStyle.participants.pointSize
text: mainItem.conferenceInfoModel.displayNamesToString
}
}
}
}
}

View file

@ -20,8 +20,8 @@ Column{
id: mainItem
property ContentModel contentModel
property int fitHeight: message.fitHeight + fileMessage.fitHeight + audioMessage.fitHeight
property int fitWidth: message.fitWidth + fileMessage.fitWidth + audioMessage.fitWidth
property int fitHeight: calendarMessage.fitHeight + message.fitHeight + fileMessage.fitHeight + audioMessage.fitHeight
property int fitWidth: calendarMessage.fitWidth + message.fitWidth + fileMessage.fitWidth + audioMessage.fitWidth
property color backgroundColor
property string lastTextSelected
property alias textColor: message.color
@ -29,6 +29,7 @@ Column{
signal rightClicked()
property int maxWidth
height: fitHeight
anchors.left: parent ? parent.left : undefined
anchors.right: parent ? parent.right : undefined
@ -37,6 +38,12 @@ Column{
property bool isOutgoing : contentModel && contentModel.chatMessageModel && (contentModel.chatMessageModel.isOutgoing || contentModel.chatMessageModel.state == LinphoneEnums.ChatMessageStateIdle);
ChatCalendarMessage{
id: calendarMessage
contentModel: mainItem.contentModel
width: parent.width
maxWidth: mainItem.maxWidth
}
ChatAudioMessage{
id: audioMessage
contentModel: mainItem.contentModel
@ -54,4 +61,4 @@ Column{
color: isOutgoing ? ChatStyle.entry.message.outgoing.text.color : ChatStyle.entry.message.incoming.text.color
onRightClicked: mainItem.rightClicked()
}
}
}

View file

@ -27,7 +27,7 @@ Row {
signal copySelectionDone()
signal forwardClicked()
height: fitHeight
visible: contentModel && (contentModel.isFile() || contentModel.isFileTransfer()) && !contentModel.isVoiceRecording()
visible: contentModel && !contentModel.isIcalendar() && (contentModel.isFile() || contentModel.isFileTransfer()) && !contentModel.isVoiceRecording()
// ---------------------------------------------------------------------------
// File message.
// ---------------------------------------------------------------------------

View file

@ -50,7 +50,7 @@ Item {
id: headerText
height: icon.height
verticalAlignment: Qt.AlignVCenter
// Anonymized forward : this is wanted.
// Anonymized forward : do not get display name, this is wanted.
//property string forwardInfo: mainChatMessageModel ? mainChatMessageModel.getForwardInfoDisplayName : ''
//: 'Forwarded' : Header on a message that contains a forward.
text: qsTr('Forwarded')// + (forwardInfo ? ' : ' +forwardInfo : '')

View file

@ -104,6 +104,7 @@ Item {
interactive: false
delegate:
ChatContent{
maxWidth: container.width
contentModel: modelData
onFitWidthChanged:{
rectangle.updateWidth()

View file

@ -0,0 +1,82 @@
pragma Singleton
import QtQml 2.2
import Units 1.0
import ColorsList 1.0
// =============================================================================
QtObject {
property string sectionName : 'ChatCalendarMessage'
property int heightMargin: 5
property int widthMargin: 5
property int minWidth: 300
property int actionButtonsSize: 36
property int avatarSize: 30
property int deleteButtonSize: 22
property int height: 50
property int leftMargin: 40
property int bottomMargin: 10
property int presenceLevelSize: 12
property int rightMargin: 25
property int spacing: 15
property QtObject backgroundColor: QtObject {
property color normal: ColorsList.add(sectionName+'_conference_bg_n', 'conference_bg').color
property color hovered: ColorsList.add(sectionName+'_conference_bg_h', 'g10').color
}
property QtObject border: QtObject {
property color color: ColorsList.add(sectionName+'_conference_border', 'f').color
property int width: 1
}
property QtObject indicator: QtObject {
property color color: ColorsList.add(sectionName+'_conference_indicator', 'i').color
property int width: 5
}
property QtObject schedule: QtObject {
property int spacing: 0
property int pointSize: Units.dp * 10
property string icon : 'schedule_custom'
property int iconSize: 30
property color color: ColorsList.add(sectionName+'_schedule', 'j').color
}
property QtObject subject: QtObject {
property int spacing: 5
property int pointSize: Units.dp * 11
property color color: ColorsList.add(sectionName+'_subject', 'j').color
}
property QtObject participants: QtObject {
property int spacing: 5
property int pointSize: Units.dp * 10
property string icon : 'calendar_participants_custom'
property int iconSize: 30
property color color: ColorsList.add(sectionName+'_participants', 'j').color
}
property QtObject organizer: QtObject {
property color color: ColorsList.add(sectionName+'_conference_organizer', 'j').color
property int pointSize: Units.dp * 10
property int width: 220
}
/*
property color color: ColorsList.add(sectionName, 'q').color
property QtObject header: QtObject{
property color color: ColorsList.add(sectionName+'_header', 'h').color
property int pointSizeOffset: -3
property QtObject forwardIcon: QtObject{
property string icon : 'menu_forward_custom'
property int iconSize: 22
}
}
property int padding: 8
*/
}

View file

@ -13,6 +13,7 @@ singleton ChatStyle 1.0 Chat/ChatStyle.qml
singleton ChatAudioMessageStyle 1.0 Chat/ChatAudioMessageStyle.qml
singleton ChatAudioPreviewStyle 1.0 Chat/ChatAudioPreviewStyle.qml
singleton ChatFilePreviewStyle 1.0 Chat/ChatFilePreviewStyle.qml
singleton ChatCalendarMessageStyle 1.0 Chat/ChatCalendarMessageStyle.qml
singleton ChatForwardMessageStyle 1.0 Chat/ChatForwardMessageStyle.qml
singleton ChatReplyMessageStyle 1.0 Chat/ChatReplyMessageStyle.qml

View file

@ -17,6 +17,7 @@ CallStatistics 1.0 Calls/CallStatistics.qml
Chat 1.0 Chat/Chat.qml
ChatAudioMessage 1.0 Chat/ChatAudioMessage.qml
ChatAudioPreview 1.0 Chat/ChatAudioPreview.qml
ChatCalendarMessage 1.0 Chat/ChatCalendarMessage.qml
ChatMessagePreview 1.0 Chat/ChatMessagePreview.qml
ChatForwardMessage 1.0 Chat/ChatForwardMessage.qml
ChatReplyMessage 1.0 Chat/ChatReplyMessage.qml

View file

@ -68,7 +68,7 @@ Rectangle {
image: _sipAddressObserver.contact && _sipAddressObserver.contact.vcard.avatar
username: contactDescription.username
height: Logic.computeAvatarSize(CallStyle.container.avatar.maxSize)
height: Logic.computeAvatarSize(container, CallStyle.container.avatar.maxSize)
width: height
}
}

View file

@ -28,7 +28,7 @@
// =============================================================================
function computeAvatarSize (maxSize) {
function computeAvatarSize (container, maxSize) {
var height = container.height
var width = container.width
@ -71,39 +71,39 @@ function handleStatusChanged (status) {
}
}
function handleVideoRequested () {
var call = incall.call
function handleVideoRequested (call) {
console.log("handleVideoRequested")
if (window.virtualWindowVisible || !Linphone.SettingsModel.videoSupported) {
call.rejectVideoRequest()
return
}
/*
// Close dialog after 10s.
var timeout = Utils.setTimeout(incall, 10000, function () {
call.statusChanged.disconnect(endedHandler)
window.detachVirtualWindow()
call.rejectVideoRequest()
})
*/
// Close dialog if call is ended.
var endedHandler = function (status) {
if (status === Linphone.CallModel.CallStatusEnded) {
Utils.clearTimeout(timeout)
Utils.clearTimeout(timeout)
call.statusChanged.disconnect(endedHandler)
window.detachVirtualWindow()
}
}
call.statusChanged.connect(endedHandler)
console.log("D")
// Ask video to user.
window.attachVirtualWindow(Utils.buildDialogUri('ConfirmDialog'), {
descriptionText: qsTr('acceptVideoDescription'),
}, function (status) {
Utils.clearTimeout(timeout)
//Utils.clearTimeout(timeout)
call.statusChanged.disconnect(endedHandler)
console.log("E: "+status)
if (status) {
console.log("TOTO")
call.acceptVideoRequest()
} else {
call.rejectVideoRequest()

View file

@ -45,7 +45,7 @@ Rectangle {
onCameraFirstFrameReceived: Logic.handleCameraFirstFrameReceived(width, height)
onStatusChanged: Logic.handleStatusChanged (status)
onVideoRequested: Logic.handleVideoRequested()
onVideoRequested: Logic.handleVideoRequested(call)
}
ColumnLayout {
@ -239,7 +239,7 @@ Rectangle {
IncallAvatar {
call: incall.call
height: Logic.computeAvatarSize(CallStyle.container.avatar.maxSize)
height: Logic.computeAvatarSize(container, CallStyle.container.avatar.maxSize)
width: height
}
}
@ -261,7 +261,9 @@ Rectangle {
width: container.width
Component.onDestruction: {
resetWindowId()
console.log("Camera incall destroyed")
}
Component.onCompleted: console.log("Camera incall completed")
}
}
@ -406,7 +408,9 @@ Rectangle {
isPreview: true
Component.onDestruction: {
resetWindowId()
console.log("Camera preview incall destroyed")
}
Component.onCompleted: console.log("Camera preview incall completed")
}
}
}

View file

@ -14,8 +14,9 @@ Avatar {
property var participantDeviceModel
readonly property var _sipAddressObserver: call ? SipAddressesModel.getSipAddressObserver(call.fullPeerAddress, call.fullLocalAddress)
: SipAddressesModel.getSipAddressObserver(participantDeviceModel.address, '')
readonly property var _username: UtilsCpp.getDisplayName(_sipAddressObserver.peerAddress)
: participantDeviceModel ? SipAddressesModel.getSipAddressObserver(participantDeviceModel.address, '')
: null
readonly property var _username: _sipAddressObserver ? UtilsCpp.getDisplayName(_sipAddressObserver.peerAddress) : ''
backgroundColor: CallStyle.container.avatar.backgroundColor
foregroundColor: call && call.status === CallModel.CallStatusPaused
@ -30,7 +31,7 @@ Avatar {
return null;
}
username: call && call.status === CallModel.CallStatusPaused || !_username? '' : _username
username: call && (call.status === CallModel.CallStatusPaused) || !_username? '' : _username
Text {
anchors.fill: parent
@ -44,6 +45,6 @@ Avatar {
text: '&#10073;&#10073;'
textFormat: Text.RichText
visible: call && call.status === CallModel.CallStatusPaused
visible: call && (call.status === CallModel.CallStatusPaused) || false
}
}

View file

@ -21,21 +21,31 @@ Rectangle {
id: conference
property CallModel callModel
property var _fullscreen: null
/*
onCallModelChanged: if(callModel) {
grid.setParticipantDevicesMode()
}else
grid.setTestMode()
*/
// ---------------------------------------------------------------------------
color: VideoConferenceStyle.backgroundColor
/*
Component.onCompleted: {
if(!callModel){
grid.setTestMode()
}else
grid.setParticipantDevicesMode()
}
*/
Connections {
target: callModel
onCameraFirstFrameReceived: Logic.handleCameraFirstFrameReceived(width, height)
onStatusChanged: Logic.handleStatusChanged (status)
onVideoRequested: Logic.handleVideoRequested(callModel)
}
// ---------------------------------------------------------------------------
ColumnLayout {
@ -120,6 +130,7 @@ Rectangle {
id: grid
anchors.fill: parent
property int radius : 8
function setTestMode(){
grid.clear()
@ -142,6 +153,7 @@ Rectangle {
id: participantDevices
callModel: conference.callModel
}
/*
property ListModel defaultList : ListModel{}
Component.onCompleted: {
if( conference.callModel ){
@ -150,15 +162,20 @@ Rectangle {
}
}
model: defaultList
*/
model: participantDevices
onCountChanged: {console.log("Delegate count = "+count+"/"+participantDevices.count)}
delegate: Rectangle{
color: !conference.callModel && gridModel.defaultList.get(index).color ? gridModel.defaultList.get(index).color : ''
id: avatarCell
property ParticipantDeviceModel currentDevice: gridModel.participantDevices.getAt(index)
onCurrentDeviceChanged: console.log("currentDevice changed: " +currentDevice +", me:"+currentDevice.isMe+" ["+index+"]")
color: /*!conference.callModel && gridModel.defaultList.get(index).color ? gridModel.defaultList.get(index).color : */'#AAAAAAAA'
//color: gridModel.model.get(index) && gridModel.model.get(index).color ? gridModel.model.get(index).color : '' // modelIndex is a custom index because by Mosaic modelisation, it is not accessible.
//color: modelData.color ? modelData.color : ''
radius: grid.radius
height: grid.cellHeight - 5
width: grid.cellWidth - 5
Component.onCompleted: console.log("Completed: ["+index+"] " +currentDevice.peerAddress+", isMe:"+currentDevice.isMe)
Item {
id: container
@ -174,30 +191,60 @@ Rectangle {
IncallAvatar {
//call: gridModel.participantDevices.get(index).call
participantDeviceModel: gridModel.participantDevices.getAt(index)
height: Logic.computeAvatarSize(CallStyle.container.avatar.maxSize)
participantDeviceModel: avatarCell.currentDevice
height: Logic.computeAvatarSize(container, CallStyle.container.avatar.maxSize)
width: height
Component.onCompleted: console.log("Avatar completed"+ " ["+index+"]")
Component.onDestruction: console.log("Avatar destroyed"+ " ["+index+"]")
}
}
Loader {
anchors.centerIn: parent
active: avatarCell.currentDevice && (!avatarCell.currentDevice.videoEnabled || conference._fullscreen)
sourceComponent: avatar
}
Loader {
id: cameraLoader
anchors.centerIn: parent
//anchors.centerIn: parent
anchors.fill: parent
property bool isVideoEnabled : avatarCell.currentDevice && avatarCell.currentDevice.videoEnabled
property bool t_fullscreen: conference._fullscreen
property bool tCallModel: conference.callModel
property bool resetActive: false
onIsVideoEnabledChanged: console.log("Video is enabled : " +isVideoEnabled + " ["+index+"]")
onT_fullscreenChanged: console.log("_fullscreen changed: " +t_fullscreen+ " ["+index+"]")
onTCallModelChanged: console.log("CallModel changed: " +tCallModel+ " ["+index+"]")
active: conference.callModel && (gridModel.participantDevices.getAt(index).videoEnabled && !_fullscreen)
active: !resetActive && avatarCell.currentDevice && (avatarCell.currentDevice.videoEnabled && !conference._fullscreen)
onActiveChanged: {console.log("Active Changed: "+active+ " ["+index+"]")
if(!active && resetActive){
resetActive = false
active = true
}
}
sourceComponent: conference.callModel ? gridModel.participantDevices.getAt(index).isMe ? cameraPreview : camera
sourceComponent: avatarCell.currentDevice ?
avatarCell.currentDevice.isMe ? ( true ? cameraPreview : null )
: camera
: null
onSourceComponentChanged: console.log("SourceComponent Changed: "+sourceComponent+ " ["+index+"]")
Component {
id: camera
Camera {
//call: grid.get(modelIndex).call
participantDeviceModel: gridModel.participantDevices.getAt(index)
height: container.height
width: container.width
participantDeviceModel: avatarCell.currentDevice
//height: container.height
//width: container.width
anchors.fill: parent
onRequestNewRenderer: {cameraLoader.resetActive = true}
Component.onCompleted: console.log("Camera completed"+ " ["+index+"]")
Component.onDestruction: console.log("Camera destroyed"+ " ["+index+"]")
}
}
Component {
@ -205,18 +252,15 @@ Rectangle {
Camera {
anchors.fill: parent
call: incall.call
//call: incall.call
isPreview: true
onRequestNewRenderer: {cameraLoader.resetActive = true}
Component.onCompleted: console.log("Preview completed"+ " ["+index+"]")
Component.onDestruction: console.log("Preview destroyed"+ " ["+index+"]")
}
}
}
Loader {
anchors.centerIn: parent
active: conference.callModel && (!gridModel.participantDevices.getAt(index).videoEnabled || _fullscreen)
sourceComponent: avatar
}
}
MouseArea{
anchors.fill: parent

View file

@ -27,8 +27,7 @@ ColumnLayout {
color: ConferencesStyle.bar.backgroundColor
Text{
anchors.verticalCenter: parent.center
anchors.left: parent.left
anchors.right: parent.right
anchors.fill: parent
anchors.leftMargin: 40
@ -77,25 +76,11 @@ ColumnLayout {
section {
criteria: ViewSection.FullString
delegate: sectionHeading
property: 'dateTime'
property: 'date'
}
model: ConferenceInfoProxyModel{}
/* ListModel{
ListElement{date: '2020/12/01'; time: '10:00:00';duration: 60;organizerName: 'Dupont';subject:'Atelier loisir: boumbo en folie';participantes: 'Martin, Jordy, allelouilla, Artemis Gordon, jobarteam' }
ListElement{date: '2020/12/01'; time: '14:00:00';duration: 30;organizerName: 'Moi';subject:'TOTO';participantes: 'Julien' }
ListElement{date: '2020/12/01'; time: '10:10:00';duration: 120;organizerName: 'Henri';subject:'Eskimirbief, mais ou est donc Willy?';participantes: 'Julien'}
ListElement{date: '2020/12/04'; time: '09:00:00';duration: 300;organizerName: 'Houlahoup';subject:'Atelier loisir: boumbo en folie';participantes: 'Martin, Jordy, allelouilla, Artemis Gordon, jobarteam'}
ListElement{date: '2020/12/05'; time: '10:00:00';duration: 60;organizerName: 'Dupont';subject:'1. Atelier loisir: boumbo en folie';participantes: 'Martin, Jordy, allelouilla, Artemis Gordon, jobarteam' }
ListElement{date: '2020/12/05'; time: '10:00:00';duration: 60;organizerName: 'Dupont';subject:'2. Atelier loisir: boumbo en folie';participantes: 'Martin, Jordy, allelouilla, Artemis Gordon, jobarteam' }
ListElement{date: '2020/12/06'; time: '10:00:00';duration: 60;organizerName: 'Dupont';subject:'1. Atelier loisir: boumbo en folie';participantes: 'Martin, Jordy, allelouilla, Artemis Gordon, jobarteam' }
ListElement{date: '2020/12/06'; time: '10:00:00';duration: 60;organizerName: 'Dupont';subject:'2. Atelier loisir: boumbo en folie';participantes: 'Martin, Jordy, allelouilla, Artemis Gordon, jobarteam' }
ListElement{date: '2020/12/06'; time: '10:00:00';duration: 60;organizerName: 'Dupont';subject:'3. Atelier loisir: boumbo en folie';participantes: 'Martin, Jordy, allelouilla, Artemis Gordon, jobarteam' }
ListElement{date: '2020/12/06'; time: '10:00:00';duration: 60;organizerName: 'Dupont';subject:'4. Atelier loisir: boumbo en folie';participantes: 'Martin, Jordy, allelouilla, Artemis Gordon, jobarteam' }
}*/
// -----------------------------------------------------------------------
// Heading.
// -----------------------------------------------------------------------
@ -144,92 +129,39 @@ ColumnLayout {
//----------------------------------------------------------------------------------------------
//----------------------------------------------------------------------------------------------
delegate: Rectangle {
id: entry
delegate: Item {
implicitHeight: calendarGrid.height + ConferencesStyle.conference.bottomMargin
anchors {
left: parent ? parent.left : undefined
leftMargin: 0
right: parent ? parent.right : undefined
rightMargin: 0
}
radius: 6
color: ConferencesStyle.conference.backgroundColor.normal
implicitHeight: layout.height + ConferencesStyle.conference.bottomMargin
// ---------------------------------------------------------------------
MouseArea {
id: mouseArea
cursorShape: Qt.ArrowCursor
hoverEnabled: true
implicitHeight: layout.height
width: parent.width + parent.anchors.rightMargin
//acceptedButtons: Qt.NoButton
onClicked: CallsListModel.launchVideoCall(modelData.uri, '', 0)
ColumnLayout{
id: layout
spacing: 0
width: entry.width
left: parent ? parent.left : undefined
leftMargin: 10
right: parent ? parent.right : undefined
rightMargin: 10
}
GridView{
id: calendarGrid
//anchors.fill: parent
cellWidth: (container.width-20)/2
cellHeight: 112
model: modelData
height: cellHeight * ( (count+1) /2)
width: container.width - 20
delegate:Rectangle {
id: entry
width: calendarGrid.cellWidth -10
height: calendarGrid.cellHeight -10
radius: 6
color: ConferencesStyle.conference.backgroundColor.normal
RowLayout {
RowLayout {
id: scheduleRow
spacing: ConferencesStyle.conference.spacing
Icon{
icon: ConferencesStyle.conference.schedule.icon
iconSize: ConferencesStyle.conference.schedule.iconSize
overwriteColor: ConferencesStyle.conference.schedule.color
}
Text {
Layout.fillWidth: true
color: ConferencesStyle.conference.schedule.color
elide: Text.ElideRight
font.pointSize: ConferencesStyle.conference.schedule.pointSize
text: Qt.formatDateTime(modelData.dateTime, 'yyyy/MM/dd hh:mm')
+', end at: ' +Qt.formatDateTime(modelData.endDateTime, 'yyyy/MM/dd hh:mm')
}
}
Text{
Layout.fillWidth: true
Layout.alignment: Qt.AlignRight
color: ConferencesStyle.conference.schedule.color
font.pointSize: ConferencesStyle.conference.schedule.pointSize
text: 'Organisateur : ' +UtilsCpp.getDisplayName(modelData.organizer)
}
ChatCalendarMessage{
id: calendarMessage
conferenceInfoModel: modelData
width: calendarGrid.cellWidth
maxWidth: calendarGrid.cellWidth
}
Text{
Layout.fillWidth: true
Layout.alignment: Qt.AlignRight
color: ConferencesStyle.conference.schedule.color
font.pointSize: ConferencesStyle.conference.schedule.pointSize
text: modelData.subject
}
RowLayout {
id: participantsRow
spacing: ConferencesStyle.conference.spacing
Icon{
icon: ConferencesStyle.conference.participants.icon
iconSize: ConferencesStyle.conference.participants.iconSize
overwriteColor: ConferencesStyle.conference.participants.color
}
Text {
Layout.fillWidth: true
color: ConferencesStyle.conference.participants.color
elide: Text.ElideRight
font.pointSize: ConferencesStyle.conference.participants.pointSize
text: modelData.displayNamesToString
}
}
}
}
}
}
}
}

View file

@ -17,7 +17,13 @@ import ColorsList 1.0
DialogPlus {
id: conferenceManager
property ConferenceInfoModel conferenceInfoModel: ConferenceInfoModel{}
property ConferenceInfoModel conferenceInfoModel: ConferenceInfoModel{
onConferenceCreated: console.log("Conference has been created.")
onInvitationsSent: {
console.log("Conference => invitations sent. Check in log for invite states.")
exit(1)
}
}
readonly property int minParticipants: 1
@ -98,6 +104,7 @@ DialogPlus {
}
onClicked: {
conferenceInfoModel.isScheduled = scheduledSwitch.checked
if( scheduledSwitch.checked){
var startDateTime = new Date()
startDateTime.setDate(dateField.getDate())
@ -110,11 +117,11 @@ DialogPlus {
conferenceInfoModel.setParticipants(selectedParticipants.participantListModel)
var callsWindow = App.getCallsWindow()
App.smartShowWindow(callsWindow)
callsWindow.openConference()
CallsListModel.createConference(conferenceInfoModel, secureSwitch.checked, getInviteMode(), false )
exit(1)
//var callsWindow = App.getCallsWindow()
//App.smartShowWindow(callsWindow)
//callsWindow.openConference()
//CallsListModel.createConference(conferenceInfoModel, secureSwitch.checked, getInviteMode(), false )
conferenceInfoModel.createConference(secureSwitch.checked, getInviteMode())
}
TooltipArea{
visible: AccountSettingsModel.conferenceURI == '' || subject.text == '' || selectedParticipants.count < conferenceManager.minParticipants
@ -205,7 +212,7 @@ DialogPlus {
checked: true
onClicked: {
//checked = !checked
checked = !checked
}
indicatorStyle: SwitchStyle.aux
}

View file

@ -8,23 +8,32 @@ import App.Styles 1.0
// =============================================================================
DialogPlus {
id: dialog
buttons: [
TextButtonB {
text: qsTr('confirm')
onClicked: exit(1)
}
]
buttonsAlignment: Qt.AlignCenter
height: SettingsVideoPreviewStyle.height
width: SettingsVideoPreviewStyle.width
// ---------------------------------------------------------------------------
CameraPreview {
anchors.fill: parent
}
id: dialog
buttons: [
TextButtonB {
text: qsTr('confirm')
onClicked: exit(1)
}
]
buttonsAlignment: Qt.AlignCenter
height: SettingsVideoPreviewStyle.height
width: SettingsVideoPreviewStyle.width
// ---------------------------------------------------------------------------
Loader{
id: previewLoader
anchors.fill: parent
sourceComponent: CameraPreview {
anchors.fill: parent
onRequestNewRenderer: previewLoader.active = false
}
active: true
onActiveChanged: {
console.log("Active changed : " +active)
if(!active) active = true
}
}
}