diff --git a/linphone-app/resources.qrc b/linphone-app/resources.qrc
index cb6a18777..a16d12d70 100644
--- a/linphone-app/resources.qrc
+++ b/linphone-app/resources.qrc
@@ -400,6 +400,7 @@
ui/views/App/Calls/Conference.qml
ui/views/App/Calls/VideoConference.qml
ui/views/App/Calls/VideoConferenceActiveSpeaker.qml
+ ui/views/App/Calls/VideoConferenceFullscreen.qml
ui/views/App/Calls/VideoConferenceGrid.qml
ui/views/App/Calls/VideoConferenceMenu.qml
ui/views/App/Calls/Dialogs/CallSipAddress.qml
diff --git a/linphone-app/src/components/camera/Camera.cpp b/linphone-app/src/components/camera/Camera.cpp
index 0f8225829..02dcd969f 100644
--- a/linphone-app/src/components/camera/Camera.cpp
+++ b/linphone-app/src/components/camera/Camera.cpp
@@ -43,6 +43,7 @@ int Camera::mPreviewCounter;
// =============================================================================
Camera::Camera (QQuickItem *parent) : QQuickFramebufferObject(parent) {
+ updateWindowIdLocation();
setTextureFollowsItemSize(true);
// The fbo content must be y-mirrored because the ms rendering is y-inverted.
setMirrorVertically(true);
@@ -60,72 +61,109 @@ Camera::Camera (QQuickItem *parent) : QQuickFramebufferObject(parent) {
}
Camera::~Camera(){
+ qWarning() << "Camera destructor" << this;
if(mIsPreview)
deactivatePreview();
-// else
- // resetWindowId();
+ setWindowIdLocation(None);
}
-void Camera::resetWindowId() {
- if(mIsPreview)
- CoreManager::getInstance()->getCore()->setNativePreviewWindowId(NULL);
- else if( mCallModel && mCallModel->getCall())
- mCallModel->getCall()->setNativeVideoWindowId(NULL);
- else if(mParticipantDeviceModel){
- if(mParticipantDeviceModel->getDevice())
- mParticipantDeviceModel->getDevice()->setNativeVideoWindowId(NULL);
- }else
- CoreManager::getInstance()->getCore()->setNativeVideoWindowId(NULL);
+void Camera::resetWindowId() const{
+ if(mIsWindowIdSet){
+ QQuickFramebufferObject::Renderer * oldRenderer = NULL;
+ if(mWindowIdLocation == CorePreview){
+ oldRenderer = (QQuickFramebufferObject::Renderer *)CoreManager::getInstance()->getCore()->getNativePreviewWindowId();
+ if(oldRenderer)
+ CoreManager::getInstance()->getCore()->setNativePreviewWindowId(NULL);
+ }else if( mWindowIdLocation == Call){
+ if(mCallModel){
+ auto call = mCallModel->getCall();
+ if( call ){
+ oldRenderer = (QQuickFramebufferObject::Renderer *) call->getNativeVideoWindowId();
+ if(oldRenderer)
+ call->setNativeVideoWindowId(NULL);
+ }
+ }
+ }else if(mWindowIdLocation == Device){
+ if(mParticipantDeviceModel){
+ auto device = mParticipantDeviceModel->getDevice();
+ if( device ){
+ oldRenderer = (QQuickFramebufferObject::Renderer *)device->getNativeVideoWindowId();
+ if(oldRenderer)
+ mParticipantDeviceModel->getDevice()->setNativeVideoWindowId(NULL);
+ }
+ }
+ }else if( mWindowIdLocation == Core){
+ oldRenderer = (QQuickFramebufferObject::Renderer *)CoreManager::getInstance()->getCore()->getNativeVideoWindowId();
+ if(oldRenderer)
+ CoreManager::getInstance()->getCore()->setNativeVideoWindowId(NULL);
+ }
+ qWarning() << "Removed " << oldRenderer << " at " << mWindowIdLocation << " for " << this;
+ mIsWindowIdSet = false;
+ }
}
-QQuickFramebufferObject::Renderer *Camera::createRenderer () const {
- QQuickFramebufferObject::Renderer * renderer = NULL;
+void Camera::setWindowIdLocation(const WindowIdLocation& location){
+ if( mWindowIdLocation != location){
+ resetWindowId();// Location change: Reset old window ID.
+ mWindowIdLocation = location;
+ }
+}
+void Camera::updateWindowIdLocation(){
bool useDefaultWindow = true;
- if(mIsPreview){
- qWarning() << "Setting Camera to Preview";
- 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{
+ if(mIsPreview)
+ setWindowIdLocation( WindowIdLocation::CorePreview);
+ else{
if(mCallModel){
auto call = mCallModel->getCall();
if(call){
- qWarning() << "Setting Camera to CallModel";
- renderer = (QQuickFramebufferObject::Renderer *) call->getNativeVideoWindowId();
- if(renderer)
- call->setNativeVideoWindowId(NULL);// Reset
- renderer = (QQuickFramebufferObject::Renderer *) call->createNativeVideoWindowId();
- if(renderer)
- call->setNativeVideoWindowId(renderer);
+ setWindowIdLocation( WindowIdLocation::Call);
useDefaultWindow = false;
}
}else if( mParticipantDeviceModel){
auto participantDevice = mParticipantDeviceModel->getDevice();
if(participantDevice){
- qWarning() << "Setting Camera to Participant Device";
- renderer = (QQuickFramebufferObject::Renderer *)participantDevice->getNativeVideoWindowId();
- if(renderer)
- participantDevice->setNativeVideoWindowId(NULL);// Reset
- qWarning() << "Trying to create new window ID for " << participantDevice->getName().c_str() << ", addr=" << participantDevice->getAddress()->asString().c_str();
- renderer = (QQuickFramebufferObject::Renderer *) participantDevice->createNativeVideoWindowId();
- if(renderer)
- participantDevice->setNativeVideoWindowId(renderer);
+ setWindowIdLocation(WindowIdLocation::Device);
useDefaultWindow = false;
}
}
if(useDefaultWindow){
- qWarning() << "Setting Camera to Defaul tWindow";
- renderer = (QQuickFramebufferObject::Renderer *)CoreManager::getInstance()->getCore()->getNativeVideoWindowId();
- if(renderer)
- CoreManager::getInstance()->getCore()->setNativeVideoWindowId(NULL);
- renderer = (QQuickFramebufferObject::Renderer *) CoreManager::getInstance()->getCore()->createNativeVideoWindowId();
- if(renderer)
- CoreManager::getInstance()->getCore()->setNativeVideoWindowId(renderer);
+ setWindowIdLocation(WindowIdLocation::Core);
}
}
+}
+
+QQuickFramebufferObject::Renderer *Camera::createRenderer () const {
+ resetWindowId();
+
+ QQuickFramebufferObject::Renderer * renderer = NULL;
+ if(mWindowIdLocation == CorePreview){
+ qWarning() << "Setting Camera to Preview";
+ renderer=(QQuickFramebufferObject::Renderer *)CoreManager::getInstance()->getCore()->createNativePreviewWindowId();
+ if(renderer)
+ CoreManager::getInstance()->getCore()->setNativePreviewWindowId(renderer);
+ }else if(mWindowIdLocation == Call){
+ auto call = mCallModel->getCall();
+ if(call){
+ qWarning() << "Setting Camera to CallModel";
+ renderer = (QQuickFramebufferObject::Renderer *) call->createNativeVideoWindowId();
+ if(renderer)
+ call->setNativeVideoWindowId(renderer);
+ }
+ }else if( mWindowIdLocation == Device) {
+ auto participantDevice = mParticipantDeviceModel->getDevice();
+ if(participantDevice){
+ qWarning() << "Setting Camera to Participant Device";
+ qWarning() << "Trying to create new window ID for " << participantDevice->getName().c_str() << ", addr=" << participantDevice->getAddress()->asString().c_str();
+ renderer = (QQuickFramebufferObject::Renderer *) participantDevice->createNativeVideoWindowId();
+ if(renderer)
+ participantDevice->setNativeVideoWindowId(renderer);
+ }
+ }else if( mWindowIdLocation == Core){
+ qWarning() << "Setting Camera to Default Window";
+ renderer = (QQuickFramebufferObject::Renderer *) CoreManager::getInstance()->getCore()->createNativeVideoWindowId();
+ if(renderer)
+ CoreManager::getInstance()->getCore()->setNativeVideoWindowId(renderer);
+ }
if( !renderer){
QTimer::singleShot(1, this, &Camera::isNotReady);// Workaround for const createRenderer
qWarning() << "Camera stream couldn't start for Rendering. Retrying in 1s";
@@ -133,6 +171,8 @@ QQuickFramebufferObject::Renderer *Camera::createRenderer () const {
QTimer::singleShot(1000, this, &Camera::requestNewRenderer);
}else{
+ mIsWindowIdSet = true;
+ qWarning() << "Added " << renderer << " at " << mWindowIdLocation << " for " << this;
QTimer::singleShot(1, this, &Camera::isReady);// Workaround for const createRenderer
}
return renderer;
@@ -159,6 +199,7 @@ ParticipantDeviceModel * Camera::getParticipantDeviceModel() const{
void Camera::setCallModel (CallModel *callModel) {
if (mCallModel != callModel) {
mCallModel = callModel;
+ updateWindowIdLocation();
update();
emit callChanged(mCallModel);
@@ -172,6 +213,7 @@ void Camera::setIsPreview (bool status) {
activatePreview();
else
deactivatePreview();
+ updateWindowIdLocation();
update();
emit isPreviewChanged(status);
@@ -188,6 +230,7 @@ void Camera::setIsReady(bool status) {
void Camera::setParticipantDeviceModel(ParticipantDeviceModel * participantDeviceModel){
if (mParticipantDeviceModel != participantDeviceModel) {
mParticipantDeviceModel = participantDeviceModel;
+ updateWindowIdLocation();
update();
emit participantDeviceModelChanged(mParticipantDeviceModel);
}
@@ -214,6 +257,5 @@ void Camera::deactivatePreview(){
if (--mPreviewCounter == 0)
core->enableVideoPreview(false);
mPreviewCounterMutex.unlock();
- core->setNativePreviewWindowId(NULL);
}
-}
\ No newline at end of file
+}
diff --git a/linphone-app/src/components/camera/Camera.hpp b/linphone-app/src/components/camera/Camera.hpp
index ed5c54de7..6b0d79542 100644
--- a/linphone-app/src/components/camera/Camera.hpp
+++ b/linphone-app/src/components/camera/Camera.hpp
@@ -44,6 +44,14 @@ class Camera : public QQuickFramebufferObject {
Q_PROPERTY(ParticipantDeviceModel * participantDeviceModel READ getParticipantDeviceModel WRITE setParticipantDeviceModel NOTIFY participantDeviceModelChanged)
Q_PROPERTY(bool isPreview READ getIsPreview WRITE setIsPreview NOTIFY isPreviewChanged);
Q_PROPERTY(bool isReady READ getIsReady WRITE setIsReady NOTIFY isReadyChanged);
+
+ typedef enum{
+ None = -1,
+ CorePreview = 0,
+ Call,
+ Device,
+ Core
+ }WindowIdLocation;
public:
Camera (QQuickItem *parent = Q_NULLPTR);
@@ -51,7 +59,7 @@ public:
QQuickFramebufferObject::Renderer *createRenderer () const override;
- Q_INVOKABLE void resetWindowId();
+ Q_INVOKABLE void resetWindowId() const; // const to be used from createRenderer()
static QMutex mPreviewCounterMutex;
static int mPreviewCounter;
@@ -76,14 +84,19 @@ private:
void setIsPreview (bool status);
void setIsReady(bool status);
void setParticipantDeviceModel(ParticipantDeviceModel * participantDeviceModel);
+ void setWindowIdLocation(const WindowIdLocation& location);
void activatePreview();
void deactivatePreview();
+ void updateWindowIdLocation();
bool mIsPreview = false;
bool mIsReady = false;
CallModel *mCallModel = nullptr;
ParticipantDeviceModel *mParticipantDeviceModel = nullptr;
+
+ WindowIdLocation mWindowIdLocation = None;
+ mutable bool mIsWindowIdSet = false;
QTimer *mRefreshTimer = nullptr;
};
diff --git a/linphone-app/ui/modules/Linphone/Camera/CameraItem.qml b/linphone-app/ui/modules/Linphone/Camera/CameraItem.qml
index eaecc263c..2bf5c854e 100644
--- a/linphone-app/ui/modules/Linphone/Camera/CameraItem.qml
+++ b/linphone-app/ui/modules/Linphone/Camera/CameraItem.qml
@@ -78,7 +78,7 @@ Item {
isPreview: container.isPreview
onRequestNewRenderer: {resetTimer.resetActive()}
- Component.onDestruction: {resetWindowId(); console.log("Destroyed Camera [" + isPreview + "] : " + camera)}
+ Component.onDestruction: {/*resetWindowId();*/ console.log("Destroyed Camera [" + isPreview + "] : " + camera)}
Component.onCompleted: console.log("Completed Camera [" + isPreview + "] : " + camera)
}
}
diff --git a/linphone-app/ui/views/App/Calls/Incall.js b/linphone-app/ui/views/App/Calls/Incall.js
index 9093e80bd..29143ea97 100644
--- a/linphone-app/ui/views/App/Calls/Incall.js
+++ b/linphone-app/ui/views/App/Calls/Incall.js
@@ -123,27 +123,28 @@ function openMediaParameters (window, incall) {
call: incall.call
})
}
-
-function showFullscreen (position) {
- incall.isFullScreen = true
- if (incall._fullscreen) {
- incall._fullscreen.raise()
+// callerId = incall, qmlFile = 'IncallFullscreenWindow.qml'
+// callerId need to have : _fullscreen and isFullScreen
+function showFullscreen (window, callerId, qmlFile, position) {
+ callerId.isFullScreen = true
+ if (callerId._fullscreen) {
+ callerId._fullscreen.raise()
return
}
DesktopTools.DesktopTools.screenSaverStatus = false
var parameters = {
- caller: incall,
+ caller: callerId,
x:position.x,
y:position.y,
width:window.width,
height:window.height,
window:window
}
- incall._fullscreen = Utils.openWindow(Qt.resolvedUrl('IncallFullscreenWindow.qml'), parameters.window, {
+ callerId._fullscreen = Utils.openWindow(Qt.resolvedUrl(qmlFile), parameters.window, {
properties: parameters
}, true)
- if(incall._fullscreen) {
- incall._fullscreen.cameraIsReady = Qt.binding(function(){ return !incall.cameraIsReady})
- incall._fullscreen.previewIsReady = Qt.binding(function(){ return !incall.previewIsReady})
+ if(callerId._fullscreen) {
+ callerId._fullscreen.cameraIsReady = Qt.binding(function(){ return !callerId.cameraIsReady})
+ callerId._fullscreen.previewIsReady = Qt.binding(function(){ return !callerId.previewIsReady})
}
}
diff --git a/linphone-app/ui/views/App/Calls/Incall.qml b/linphone-app/ui/views/App/Calls/Incall.qml
index e1c6cf78d..cfcfc09c3 100644
--- a/linphone-app/ui/views/App/Calls/Incall.qml
+++ b/linphone-app/ui/views/App/Calls/Incall.qml
@@ -220,7 +220,7 @@ Rectangle {
colorSet: CallStyle.buttons.fullscreen
visible: incall.call.videoEnabled
- onClicked: Logic.showFullscreen(contactDescription.mapToGlobal(0,0))
+ onClicked: Logic.showFullscreen(window, incall, 'IncallFullscreenWindow.qml', contactDescription.mapToGlobal(0,0))
}
}
}
diff --git a/linphone-app/ui/views/App/Calls/VideoConference.qml b/linphone-app/ui/views/App/Calls/VideoConference.qml
index a7cce487e..1ff49b029 100644
--- a/linphone-app/ui/views/App/Calls/VideoConference.qml
+++ b/linphone-app/ui/views/App/Calls/VideoConference.qml
@@ -25,7 +25,12 @@ Rectangle {
property CallModel callModel
property ConferenceModel conferenceModel: callModel && callModel.getConferenceModel()
+ property bool cameraIsReady : false
+ property bool previewIsReady : false
+ property bool isFullScreen: false // Use this variable to test if we are in fullscreen. Do not test _fullscreen : we need to clean memory before having the window (see .js file)
property var _fullscreen: null
+ on_FullscreenChanged: if( !_fullscreen) isFullScreen = false
+
property bool listCallsOpened: true
signal openListCallsRequest()
@@ -115,6 +120,7 @@ Rectangle {
}
// Title
Text{
+ id: title
Timer{
id: elapsedTimeRefresher
running: true
@@ -152,7 +158,8 @@ Rectangle {
isCustom: true
backgroundRadius: width/2
colorSet: VideoConferenceStyle.buttons.fullscreen
- visible: false //TODO
+ visible: conference.callModel.videoEnabled
+ onClicked: Logic.showFullscreen(window, conference, 'VideoConferenceFullscreen.qml', title.mapToGlobal(0,0))
}
}
@@ -184,6 +191,7 @@ Rectangle {
anchors.leftMargin: 70
anchors.rightMargin: rightMenu.visible ? 15 : 70
callModel: conference.callModel
+ isFullScreen: conference.isFullScreen
}
}
Component{
@@ -193,6 +201,7 @@ Rectangle {
callModel: conference.callModel
isRightReducedLayout: rightMenu.visible
isLeftReducedLayout: conference.listCallsOpened
+ isFullScreen: conference.isFullScreen
}
}
RowLayout{
diff --git a/linphone-app/ui/views/App/Calls/VideoConferenceActiveSpeaker.qml b/linphone-app/ui/views/App/Calls/VideoConferenceActiveSpeaker.qml
index 13d66c64a..b9f9fc075 100644
--- a/linphone-app/ui/views/App/Calls/VideoConferenceActiveSpeaker.qml
+++ b/linphone-app/ui/views/App/Calls/VideoConferenceActiveSpeaker.qml
@@ -24,6 +24,7 @@ Item {
property alias callModel: allDevices.callModel
property bool isRightReducedLayout: false
property bool isLeftReducedLayout: false
+ property bool isFullScreen: false
property alias showMe : allDevices.showMe
property int participantCount: allDevices.count
@@ -39,11 +40,13 @@ Item {
CameraView{
id: cameraView
callModel: mainItem.callModel
+ enabled: !mainItem.isFullScreen
isCameraFromDevice: false
+ isPreview: false
anchors.fill: parent
anchors.leftMargin: isRightReducedLayout || isLeftReducedLayout? 30 : 140
anchors.rightMargin: isRightReducedLayout ? 10 : 140
- isPaused: callModel.pausedByUser || currentDevice && currentDevice.isPaused //callModel.pausedByUser
+ isPaused: (callModel && callModel.pausedByUser) || (currentDevice && currentDevice.isPaused) //callModel.pausedByUser
showCloseButton: false
color: 'black'
}
@@ -71,7 +74,7 @@ Item {
anchors.centerIn: parent
height: miniViews.cellHeight - 6
width: miniViews.width - 6
- enabled: index >=0
+ enabled: index >=0 && !mainItem.isFullScreen
currentDevice: modelData
callModel: mainItem.callModel
isCameraFromDevice: true
diff --git a/linphone-app/ui/views/App/Calls/VideoConferenceFullscreen.qml b/linphone-app/ui/views/App/Calls/VideoConferenceFullscreen.qml
new file mode 100644
index 000000000..c6b09407d
--- /dev/null
+++ b/linphone-app/ui/views/App/Calls/VideoConferenceFullscreen.qml
@@ -0,0 +1,482 @@
+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 LinphoneUtils 1.0
+
+import DesktopTools 1.0
+import LinphoneEnums 1.0
+import UtilsCpp 1.0
+
+
+import App.Styles 1.0
+
+
+// Temp
+import 'Incall.js' as Logic
+import 'qrc:/ui/scripts/Utils/utils.js' as Utils
+
+Window {
+ id: window
+
+ // ---------------------------------------------------------------------------
+
+ property alias callModel: conference.callModel
+ property var caller
+ property bool hideButtons: !hideButtonsTimer.running
+ property bool cameraIsReady : false
+ property bool previewIsReady : false
+
+ // ---------------------------------------------------------------------------
+
+ function exit (cb) {
+ DesktopTools.screenSaverStatus = true
+ // `exit` is called by `Incall.qml`.
+ // The `window` id can be null if the window was closed in this view.
+ if (!window) {
+ return
+ }
+ if(!window.close() && parent)
+ parent.close()
+ if (cb) {
+ cb()
+ }
+ }
+
+ // ---------------------------------------------------------------------------
+ onCallModelChanged: if(!callModel) window.exit()
+ Component.onCompleted: {
+ window.callModel = caller.callModel
+ }
+ // ---------------------------------------------------------------------------
+
+ Shortcut {
+ sequence: StandardKey.Close
+ onActivated: window.exit()
+ }
+
+ // ---------------------------------------------------------------------------
+ // =============================================================================
+
+ Rectangle {
+ id: conference
+
+ property CallModel callModel
+ property ConferenceModel conferenceModel: callModel && callModel.getConferenceModel()
+ property var _fullscreen: null
+ property bool listCallsOpened: true
+
+ signal openListCallsRequest()
+ // ---------------------------------------------------------------------------
+ anchors.fill: parent
+ focus: true
+
+ Keys.onEscapePressed: window.exit()
+ color: VideoConferenceStyle.backgroundColor
+
+ Connections {
+ target: callModel
+
+ onCameraFirstFrameReceived: Logic.handleCameraFirstFrameReceived(width, height)
+ onStatusChanged: Logic.handleStatusChanged (status)
+ onVideoRequested: Logic.handleVideoRequested(callModel)
+ }
+
+ // ---------------------------------------------------------------------------
+ Rectangle{
+ anchors.fill: parent
+ visible: callModel.pausedByUser
+ color: VideoConferenceStyle.pauseArea.backgroundColor
+ z: 1
+ ColumnLayout{
+ anchors.fill: parent
+ spacing: 10
+ Item{
+ Layout.fillWidth: true
+ Layout.fillHeight: true
+ }
+ ActionButton{
+ Layout.alignment: Qt.AlignCenter
+ isCustom: true
+ colorSet: VideoConferenceStyle.pauseArea.play
+ backgroundRadius: width/2
+ onClicked: callModel.pausedByUser = !callModel.pausedByUser
+ }
+ Text{
+ Layout.alignment: Qt.AlignCenter
+ text: 'Vous ĂȘtes actuellement en dehors de la confĂ©rence.'
+ font.pointSize: VideoConferenceStyle.pauseArea.title.pointSize
+ font.weight: VideoConferenceStyle.pauseArea.title.weight
+ color: VideoConferenceStyle.pauseArea.title.color
+ }
+ Text{
+ Layout.alignment: Qt.AlignCenter
+ text: 'Cliquez sur le bouton "play" pour la rejoindre.'
+ font.pointSize: VideoConferenceStyle.pauseArea.description.pointSize
+ font.weight: VideoConferenceStyle.pauseArea.description.weight
+ color: VideoConferenceStyle.pauseArea.description.color
+ }
+ Item{
+ Layout.fillWidth: true
+ Layout.preferredHeight: 140
+ }
+ }
+ }
+
+ // -------------------------------------------------------------------------
+ // Conference info.
+ // -------------------------------------------------------------------------
+ RowLayout{
+ id: featuresRow
+ // Aux features
+ anchors.top: parent.top
+ anchors.left: parent.left
+ anchors.right: parent.right
+
+ anchors.topMargin: 10
+ anchors.leftMargin: 25
+ anchors.rightMargin: 25
+ spacing: 10
+
+ visible: !window.hideButtons
+ /*
+ ActionButton{
+ isCustom: true
+ backgroundRadius: width/2
+ colorSet: VideoConferenceStyle.buttons.callsList
+ visible: !listCallsOpened && !window.hideButtons
+ onClicked: openListCallsRequest()
+ }*/
+ ActionButton{
+ id: keypadButton
+ isCustom: true
+ backgroundRadius: width/2
+ colorSet: VideoConferenceStyle.buttons.dialpad
+ onClicked: telKeypad.visible = !telKeypad.visible
+
+ visible: !window.hideButtons
+ }
+ // Title
+ Text{
+ Timer{
+ id: elapsedTimeRefresher
+ running: true
+ interval: 1000
+ repeat: true
+ onTriggered: if(conference.conferenceModel) parent.elaspedTime = ' - ' +Utils.formatElapsedTime(conference.conferenceModel.getElapsedSeconds())
+ }
+ property string elaspedTime
+ horizontalAlignment: Qt.AlignHCenter
+ Layout.fillWidth: true
+ text: conference.conferenceModel ? conference.conferenceModel.subject+ elaspedTime : ''
+ color: VideoConferenceStyle.title.color
+ font.pointSize: VideoConferenceStyle.title.pointSize
+ }
+ // Mode buttons
+ ActionButton{
+ isCustom: true
+ backgroundRadius: width/2
+ colorSet: VideoConferenceStyle.buttons.screenSharing
+ visible: false //TODO
+ }
+ ActionButton{
+ isCustom: true
+ backgroundRadius: width/2
+ colorSet: VideoConferenceStyle.buttons.recordOff
+ visible: false //TODO
+ }
+ ActionButton{
+ isCustom: true
+ backgroundRadius: width/2
+ colorSet: VideoConferenceStyle.buttons.screenshot
+ visible: false //TODO
+ }
+ ActionButton{
+ isCustom: true
+ backgroundRadius: width/2
+ colorSet: VideoConferenceStyle.buttons.fullscreen
+ onClicked: window.exit()
+ }
+
+ }
+
+ // -------------------------------------------------------------------------
+ // Contacts visual.
+ // -------------------------------------------------------------------------
+
+ MouseArea{
+ id: mainGrid
+ anchors.top: featuresRow.bottom
+ anchors.left: parent.left
+ anchors.right: parent.right
+ anchors.bottom: actionsButtons.top
+
+ anchors.topMargin: 15
+ anchors.bottomMargin: 20
+ onClicked: {
+ if(!conference.callModel)
+ grid.add({color: '#'+ Math.floor(Math.random()*255).toString(16)
+ +Math.floor(Math.random()*255).toString(16)
+ +Math.floor(Math.random()*255).toString(16)})
+ }
+
+ Component{
+ id: gridComponent
+ VideoConferenceGrid{
+ id: grid
+ anchors.leftMargin: 70
+ anchors.rightMargin: rightMenu.visible ? 15 : 70
+ callModel: conference.callModel
+ }
+ }
+ Component{
+ id: activeSpeakerComponent
+ VideoConferenceActiveSpeaker{
+ id: activeSpeaker
+ callModel: conference.callModel
+ isRightReducedLayout: rightMenu.visible
+ isLeftReducedLayout: conference.listCallsOpened
+ }
+ }
+ RowLayout{
+ anchors.fill: parent
+ Loader{
+ id: conferenceLayout
+ Layout.fillHeight: true
+ Layout.fillWidth: true
+ sourceComponent: conference.callModel.conferenceVideoLayout == LinphoneEnums.ConferenceLayoutGrid || !conference.callModel.videoEnabled? gridComponent : activeSpeakerComponent
+ onSourceComponentChanged: console.log(conference.callModel.conferenceVideoLayout)
+ active: conference.callModel
+ ColumnLayout {
+ anchors.fill: parent
+ visible: !conference.callModel || !conferenceLayout.item || conferenceLayout.item.participantCount == 0
+ BusyIndicator{
+ Layout.preferredHeight: 50
+ Layout.preferredWidth: 50
+ Layout.alignment: Qt.AlignCenter
+ running: parent.visible
+ color: VideoConferenceStyle.buzyColor
+ }
+ Text{
+ Layout.alignment: Qt.AlignCenter
+ text: "Video conference is not ready. Please Wait..."
+ color: VideoConferenceStyle.buzyColor
+ }
+ }
+ }
+ VideoConferenceMenu{
+ id: rightMenu
+ Layout.fillHeight: true
+ Layout.preferredWidth: 400
+ Layout.rightMargin: 30
+ callModel: conference.callModel
+ visible: false
+ onClose: rightMenu.visible = !rightMenu.visible
+ }
+ }
+ }
+ // -------------------------------------------------------------------------
+ // Action Buttons.
+ // -------------------------------------------------------------------------
+
+ // Security
+ ActionButton{
+ visible: false // TODO
+ anchors.left: parent.left
+ anchors.bottom: parent.bottom
+ anchors.bottomMargin: 30
+ anchors.leftMargin: 25
+ height: VideoConferenceStyle.buttons.secure.buttonSize
+ width: height
+ isCustom: true
+ iconIsCustom: false
+ backgroundRadius: width/2
+ colorSet: VideoConferenceStyle.buttons.secure
+
+ icon: 'secure_level_1'
+ }
+ // Action buttons
+ RowLayout{
+ id: actionsButtons
+ anchors.horizontalCenter: parent.horizontalCenter
+ anchors.bottom: parent.bottom
+ anchors.bottomMargin: 30
+ height: 60
+ spacing: 30
+ z: 2
+ visible: !window.hideButtons
+ RowLayout{
+ spacing: 10
+ Row {
+ spacing: 2
+ visible: SettingsModel.muteMicrophoneEnabled
+ property bool microMuted: callModel.microMuted
+
+ VuMeter {
+ enabled: !parent.microMuted
+ Timer {
+ interval: 50
+ repeat: true
+ running: parent.enabled
+
+ onTriggered: parent.value = callModel.microVu
+ }
+ }
+ ActionSwitch {
+ id: micro
+ isCustom: true
+ backgroundRadius: 90
+ colorSet: parent.microMuted ? VideoConferenceStyle.buttons.microOff : VideoConferenceStyle.buttons.microOn
+ onClicked: callModel.microMuted = !parent.microMuted
+ }
+ }
+ Row {
+ spacing: 2
+ property bool speakerMuted: callModel.speakerMuted
+ VuMeter {
+ enabled: !parent.speakerMuted
+ Timer {
+ interval: 50
+ repeat: true
+ running: parent.enabled
+ onTriggered: parent.value = callModel.speakerVu
+ }
+ }
+ ActionSwitch {
+ id: speaker
+ isCustom: true
+ backgroundRadius: 90
+ colorSet: parent.speakerMuted ? VideoConferenceStyle.buttons.speakerOff : VideoConferenceStyle.buttons.speakerOn
+ onClicked: callModel.speakerMuted = !parent.speakerMuted
+ }
+ }
+ ActionSwitch {
+ id: camera
+ isCustom: true
+ backgroundRadius: 90
+ colorSet: callModel && callModel.cameraEnabled ? VideoConferenceStyle.buttons.cameraOn : VideoConferenceStyle.buttons.cameraOff
+ updating: callModel.videoEnabled && callModel.updating
+ enabled: callModel.videoEnabled
+ onClicked: if(callModel) callModel.cameraEnabled = !callModel.cameraEnabled
+ }
+ }
+ RowLayout{
+ spacing: 10
+ ActionButton{
+ isCustom: true
+ backgroundRadius: width/2
+ visible: SettingsModel.callPauseEnabled
+ updating: callModel.updating
+ colorSet: callModel.pausedByUser ? VideoConferenceStyle.buttons.play : VideoConferenceStyle.buttons.pause
+ onClicked: callModel.pausedByUser = !callModel.pausedByUser
+ }
+ ActionButton{
+ isCustom: true
+ backgroundRadius: width/2
+ colorSet: VideoConferenceStyle.buttons.hangup
+
+ onClicked: callModel.terminate()
+ }
+ }
+ }
+
+ // Panel buttons
+ RowLayout{
+ anchors.right: parent.right
+ anchors.bottom: parent.bottom
+ anchors.bottomMargin: 30
+ anchors.rightMargin: 25
+ height: 60
+ visible: !window.hideButtons
+ ActionButton{
+ isCustom: true
+ backgroundRadius: width/2
+ colorSet: VideoConferenceStyle.buttons.chat
+ visible: false // TODO for next version
+ }
+ ActionButton{
+ isCustom: true
+ backgroundRadius: width/2
+ colorSet: VideoConferenceStyle.buttons.participants
+ visible: false // TODO
+ }
+ ActionButton {
+ id: callQuality
+
+ isCustom: true
+ backgroundRadius: 4
+ colorSet: VideoConferenceStyle.buttons.callQuality
+ percentageDisplayed: 0
+
+ onClicked: {Logic.openCallStatistics();}
+ Timer {
+ interval: 500
+ repeat: true
+ running: true
+ triggeredOnStart: true
+ onTriggered: {
+ // Note: `quality` is in the [0, 5] interval and -1.
+ var quality = callModel.quality
+ if(quality >= 0)
+ callQuality.percentageDisplayed = quality * 100 / 5
+ else
+ callQuality.percentageDisplayed = 0
+ }
+ }
+ }
+ ActionButton{
+ isCustom: true
+ backgroundRadius: width/2
+ colorSet: VideoConferenceStyle.buttons.options
+ onClicked: rightMenu.visible = !rightMenu.visible
+ }
+ }
+
+ // ---------------------------------------------------------------------------
+ // TelKeypad.
+ // ---------------------------------------------------------------------------
+ CallStatistics {
+ id: callStatistics
+
+ call: conference.callModel
+ width: conference.width - 20
+ height: conference.height * 2/3
+ relativeTo: conference
+ relativeY: CallStyle.header.stats.relativeY
+ relativeX: 10
+ onClosed: Logic.handleCallStatisticsClosed()
+ }
+ }
+ TelKeypad {
+ id: telKeypad
+
+ call: callModel
+ visible: SettingsModel.showTelKeypadAutomatically
+ }
+ MouseArea{
+ Timer {
+ id: hideButtonsTimer
+
+ interval: 5000
+ running: true
+
+ onTriggered: {console.log("hideButtons");}
+ }
+
+ anchors.fill: parent
+ acceptedButtons: Qt.NoButton
+ propagateComposedEvents: true
+ cursorShape: Qt.ArrowCursor
+
+ onEntered: hideButtonsTimer.start()
+ onExited: hideButtonsTimer.stop()
+
+ onPositionChanged: {
+ hideButtonsTimer.restart()
+ }
+ }
+}
diff --git a/linphone-app/ui/views/App/Calls/VideoConferenceGrid.qml b/linphone-app/ui/views/App/Calls/VideoConferenceGrid.qml
index 88e52ee42..9bb32ad0b 100644
--- a/linphone-app/ui/views/App/Calls/VideoConferenceGrid.qml
+++ b/linphone-app/ui/views/App/Calls/VideoConferenceGrid.qml
@@ -22,6 +22,7 @@ import 'qrc:/ui/scripts/Utils/utils.js' as Utils
Mosaic {
id: grid
property alias callModel: participantDevices.callModel
+ property bool isFullScreen: false
property int participantCount: gridModel.count
anchors.fill: parent
squaredDisplay: true
@@ -70,7 +71,7 @@ Mosaic {
CameraView{
id: cameraView
- enabled: index >=0
+ enabled: index >=0 && !grid.isFullScreen
anchors.fill: parent
currentDevice: avatarCell.currentDevice
callModel: participantDevices.callModel
diff --git a/linphone-sdk b/linphone-sdk
index dbc795c83..c8d936196 160000
--- a/linphone-sdk
+++ b/linphone-sdk
@@ -1 +1 @@
-Subproject commit dbc795c83ef288e5fd27eb4a2a8a1c9da127eb95
+Subproject commit c8d936196b84855415e90d41346e7b3a25374077