Fixes and Features : Fullscreen, crashs, contacts, messages

Fixes:

* Change download URL to `https://www.linphone.org/technical-corner/linphone`
* Crash on contacts list when deleting a friend and trying to load logs.
* Message status behaviour : Resuming status when changing logs, cursor shapes updates, bind the `resend message` action to error icon
* Synchronize settings and main presence.
* QML Crashs on fullscreen

Features :

* Add utilities in fullscreen view (media quality, security, mutable speaker)
* Show fullscreen on the current call screen
* Show Display name of participant in conference
* Participants in conference are mutable
This commit is contained in:
Julien Wadel 2020-05-15 09:00:09 +02:00
parent f71a7c8950
commit 3031cc856a
21 changed files with 286 additions and 184 deletions

View file

@ -615,7 +615,9 @@ void CallModel::verifyAuthenticationToken (bool verify) {
void CallModel::updateStreams () {
mCall->update(nullptr);
}
void CallModel::toggleSpeakerMute(){
setSpeakerMuted(!getSpeakerMuted());
}
// -----------------------------------------------------------------------------
CallModel::CallEncryption CallModel::getEncryption () const {

View file

@ -130,6 +130,8 @@ public:
Q_INVOKABLE void verifyAuthenticationToken (bool verify);
Q_INVOKABLE void updateStreams ();
Q_INVOKABLE void toggleSpeakerMute();
signals:
void callErrorChanged (const QString &callError);

View file

@ -200,7 +200,7 @@ static inline void fillMessageEntry (QVariantMap &dest, const shared_ptr<linphon
if (state == linphone::ChatMessage::State::InProgress)
dest["status"] = ChatModel::MessageStatusNotDelivered;
else
dest["status"] = static_cast<ChatModel::MessageStatus>(message->getState());
dest["status"] = static_cast<ChatModel::MessageStatus>(message->getState());
shared_ptr<const linphone::Content> content = message->getFileTransferInformation();
if (content) {
@ -310,13 +310,21 @@ ChatModel::ChatModel (const QString &peerAddress, const QString &localAddress) {
mMessageHandlers = make_shared<MessageHandlers>(this);
setSipAddresses(peerAddress, localAddress);
// Rebind lost handlers
for(auto i = mEntries.begin() ; i != mEntries.end() ; ++i){
if(i->first["type"] == EntryType::MessageEntry){
shared_ptr<linphone::ChatMessage> message = static_pointer_cast<linphone::ChatMessage>(i->second);
message->removeListener(mMessageHandlers);// Remove old listener if already exists
message->addListener(mMessageHandlers);
}
}
{
CoreHandlers *coreHandlers = mCoreHandlers.get();
QObject::connect(coreHandlers, &CoreHandlers::messageReceived, this, &ChatModel::handleMessageReceived);
QObject::connect(coreHandlers, &CoreHandlers::callStateChanged, this, &ChatModel::handleCallStateChanged);
QObject::connect(coreHandlers, &CoreHandlers::isComposingChanged, this, &ChatModel::handleIsComposingChanged);
}
}
ChatModel::~ChatModel () {
@ -343,7 +351,7 @@ QVariant ChatModel::data (const QModelIndex &index, int role) const {
switch (role) {
case Roles::ChatEntry: {
auto &data = mEntries[row].first;
if (!data.contains("status"))
if (data.contains("type") && data["type"]==EntryType::MessageEntry && !data.contains("content"))
fillMessageEntry(data, static_pointer_cast<linphone::ChatMessage>(mEntries[row].second));
return QVariant::fromValue(data);
}
@ -406,7 +414,7 @@ QString ChatModel::getFullLocalAddress () const {
void ChatModel::setSipAddresses (const QString &peerAddress, const QString &localAddress) {
shared_ptr<linphone::Core> core = CoreManager::getInstance()->getCore();
shared_ptr<linphone::Factory> factory(linphone::Factory::get());
mChatRoom = core->getChatRoom(
factory->createAddress(Utils::appStringToCoreString(peerAddress)),
factory->createAddress(Utils::appStringToCoreString(localAddress))
@ -436,6 +444,7 @@ void ChatModel::setSipAddresses (const QString &peerAddress, const QString &loca
qInfo() << QStringLiteral("ChatModel (%1, %2) loaded in %3 milliseconds.")
.arg(peerAddress).arg(localAddress).arg(timer.elapsed());
}
bool ChatModel::getIsRemoteComposing () const {
@ -472,6 +481,7 @@ void ChatModel::removeAllEntries () {
void ChatModel::sendMessage (const QString &message) {
shared_ptr<linphone::ChatMessage> _message = mChatRoom->createMessage(Utils::appStringToCoreString(message));
_message->removeListener(mMessageHandlers);// Remove old listener if already exists
_message->addListener(mMessageHandlers);
insertMessageAtEnd(_message);
@ -498,6 +508,7 @@ void ChatModel::resendMessage (int id) {
case MessageStatusFileTransferError:
case MessageStatusNotDelivered: {
shared_ptr<linphone::ChatMessage> message = static_pointer_cast<linphone::ChatMessage>(entry.second);
message->removeListener(mMessageHandlers);// Remove old listener if already exists
message->addListener(mMessageHandlers);
message->resend();
@ -534,6 +545,7 @@ void ChatModel::sendFileMessage (const QString &path) {
content->setName(Utils::appStringToCoreString( QFileInfo(file).fileName()));
shared_ptr<linphone::ChatMessage> message = mChatRoom->createFileTransferMessage(content);
message->getContents().front()->setFilePath(Utils::appStringToCoreString(path));
message->removeListener(mMessageHandlers);// Remove old listener if already exists
message->addListener(mMessageHandlers);
createThumbnail(message);
@ -575,7 +587,8 @@ void ChatModel::downloadFile (int id) {
if (!soFarSoGood) {
qWarning() << QStringLiteral("Unable to create safe file path for: %1.").arg(id);
return;
}
}
message->removeListener(mMessageHandlers);// Remove old listener if already exists
message->addListener(mMessageHandlers);
message->getContents().front()->setFilePath(Utils::appStringToCoreString(safeFilePath));

View file

@ -59,7 +59,7 @@ namespace {
constexpr char LinphoneDomain[] = "sip.linphone.org";
constexpr char DefaultContactParameters[] = "message-expires=604800";
constexpr int DefaultExpires = 3600;
constexpr char DownloadUrl[] = "https://www.linphone.org/technical-corner/linphone/downloads";
constexpr char DownloadUrl[] = "https://www.linphone.org/technical-corner/linphone";
}
// -----------------------------------------------------------------------------

View file

@ -21,6 +21,7 @@
#include <QVariantMap>
#include "components/core/CoreManager.hpp"
#include "components/settings/AccountSettingsModel.hpp"
#include "OwnPresenceModel.hpp"
@ -29,6 +30,16 @@
using namespace std;
OwnPresenceModel::OwnPresenceModel (QObject *parent) : QObject(parent) {
AccountSettingsModel *accountSettingsModel = CoreManager::getInstance()->getAccountSettingsModel();
// Update status when changing the publish presence from settings
QObject::connect(accountSettingsModel, &AccountSettingsModel::publishPresenceChanged, this, [this]() {
Presence::PresenceStatus status = static_cast<Presence::PresenceStatus>(CoreManager::getInstance()->getCore()->getConsolidatedPresence());
emit presenceStatusChanged(status);
emit presenceLevelChanged(Presence::getPresenceLevel(status));
});
}
Presence::PresenceLevel OwnPresenceModel::getPresenceLevel () const {
return Presence::getPresenceLevel(getPresenceStatus());
}

View file

@ -37,7 +37,7 @@ class OwnPresenceModel : public QObject {
Q_PROPERTY(Presence::PresenceStatus presenceStatus READ getPresenceStatus WRITE setPresenceStatus NOTIFY presenceStatusChanged);
public:
OwnPresenceModel (QObject *parent = Q_NULLPTR) : QObject(parent) {}
OwnPresenceModel (QObject *parent = Q_NULLPTR);
signals:
void presenceLevelChanged (Presence::PresenceLevel level);

View file

@ -212,6 +212,7 @@ bool AccountSettingsModel::addOrUpdateProxyConfig (
const QVariantMap &data
) {
Q_CHECK_PTR(proxyConfig);
bool newPublishPresence = false;
proxyConfig->edit();
@ -249,6 +250,7 @@ bool AccountSettingsModel::addOrUpdateProxyConfig (
proxyConfig->setContactParameters(Utils::appStringToCoreString(data["contactParams"].toString()));
proxyConfig->setAvpfRrInterval(uint8_t(data["avpfInterval"].toInt()));
proxyConfig->enableRegister(data["registerEnabled"].toBool());
newPublishPresence = proxyConfig->publishEnabled() != data["publishPresence"].toBool();
proxyConfig->enablePublish(data["publishPresence"].toBool());
proxyConfig->setAvpfMode(data["avpfEnabled"].toBool()
? linphone::AVPFMode::Enabled
@ -291,7 +293,8 @@ bool AccountSettingsModel::addOrUpdateProxyConfig (
"",
""
));
if( newPublishPresence)
emit publishPresenceChanged();
return addOrUpdateProxyConfig(proxyConfig);
}

View file

@ -84,6 +84,7 @@ public:
signals:
void accountSettingsUpdated ();
void publishPresenceChanged();
private:
QString getUsername () const;

View file

@ -438,9 +438,8 @@ void SipAddressesModel::handleIsComposingChanged (const shared_ptr<linphone::Cha
void SipAddressesModel::addOrUpdateSipAddress (SipAddressEntry &sipAddressEntry, ContactModel *contact) {
const QString &sipAddress = sipAddressEntry.sipAddress;
if (contact)
sipAddressEntry.contact = contact;
else if (!sipAddressEntry.contact)
sipAddressEntry.contact = contact;
if (!sipAddressEntry.contact)
qWarning() << QStringLiteral("`contact` field is empty on sip address: `%1`.").arg(sipAddress);
updateObservers(sipAddress, contact);

View file

@ -76,6 +76,17 @@ Item {
hoverEnabled: true
MouseArea
{
id: mouseArea
anchors.fill: parent
onPressed: mouse.accepted = false
hoverEnabled: true
cursorShape: containsMouse
? Qt.PointingHandCursor
: Qt.ArrowCursor
}
height: parent.height
width: parent.width

View file

@ -32,4 +32,14 @@ Button {
rightPadding: SmallButtonStyle.rightPadding
}
hoverEnabled: true
MouseArea
{
id: mouseArea
anchors.fill: parent
onPressed: mouse.accepted = false
hoverEnabled: true
cursorShape: containsMouse
? Qt.PointingHandCursor
: Qt.ArrowCursor
}
}

View file

@ -36,8 +36,9 @@ Item {
Layout.preferredWidth: AccountStatusStyle.presenceLevel.size
PresenceLevel {
anchors.fill: parent
level: SettingsModel.rlsUriEnabled ? OwnPresenceModel.presenceLevel : Presence.Green
level: OwnPresenceModel.presenceStatus===Presence.Offline?Presence.White:( SettingsModel.rlsUriEnabled ? OwnPresenceModel.presenceLevel : Presence.Green)
visible: AccountSettingsModel.registrationState === AccountSettingsModel.RegistrationStateRegistered
}

View file

@ -29,7 +29,7 @@ Popup {
Loader {
property string $label: qsTr('audioStatsLabel')
property var $data: callStatistics.call.audioStats
property var $data: callStatistics.call?callStatistics.call.audioStats:null
sourceComponent: media
width: parent.width / 2
@ -37,7 +37,7 @@ Popup {
Loader {
property string $label: qsTr('videoStatsLabel')
property var $data: callStatistics.call.videoStats
property var $data: callStatistics.call?callStatistics.call.videoStats:null
sourceComponent: media
width: parent.width / 2

View file

@ -47,13 +47,12 @@ Row {
Rectangle {
id: rectangle
readonly property bool isNotDelivered: Utils.includes([
readonly property bool isError: Utils.includes([
ChatModel.MessageStatusFileTransferError,
ChatModel.MessageStatusIdle,
ChatModel.MessageStatusInProgress,
ChatModel.MessageStatusNotDelivered
], $chatEntry.status)
ChatModel.MessageStatusNotDelivered,
], $chatEntry.status)
readonly property bool isUploaded: $chatEntry.status === ChatModel.MessageStatusDelivered
readonly property bool isDelivered: $chatEntry.status === ChatModel.MessageStatusDeliveredToUser
readonly property bool isRead: $chatEntry.status === ChatModel.MessageStatusDisplayed
color: $chatEntry.isOutgoing
@ -257,7 +256,7 @@ Row {
? Qt.PointingHandCursor
: Qt.ArrowCursor
hoverEnabled: true
visible: !rectangle.isNotDelivered && !$chatEntry.isOutgoing
visible: (rectangle.isUploaded || rectangle.isRead) && !$chatEntry.isOutgoing
onClicked: {
if (Utils.pointIsInItem(this, thumbnailProvider, mouse)) {
@ -288,15 +287,20 @@ Row {
Icon {
anchors.centerIn: parent
icon: rectangle.isNotDelivered
? 'chat_error'
: (rectangle.isRead ? 'chat_read' : 'chat_delivered')
icon: rectangle.isError ? 'chat_error' :
(rectangle.isRead ? 'chat_read' :
(rectangle.isDelivered ? 'chat_delivered' : ''))
iconSize: ChatStyle.entry.message.outgoing.sendIconSize
MouseArea {
anchors.fill: parent
onClicked: isNotDelivered && proxyModel.resendMessage(index)
cursorShape: containsMouse
? Qt.PointingHandCursor
: Qt.ArrowCursor
hoverEnabled: true
visible: (rectangle.isError || $chatEntry.status === ChatModel.MessageStatusIdle) && $chatEntry.isOutgoing
onClicked: proxyModel.resendMessage(index)
}
}
}

View file

@ -33,28 +33,32 @@ Item {
id: icon
Icon {
readonly property bool isNotDelivered: Utils.includes([
ChatModel.MessageStatusFileTransferError,
ChatModel.MessageStatusIdle,
ChatModel.MessageStatusInProgress,
ChatModel.MessageStatusNotDelivered
], $chatEntry.status)
readonly property bool isError: Utils.includes([
ChatModel.MessageStatusFileTransferError,
ChatModel.MessageStatusNotDelivered,
], $chatEntry.status)
readonly property bool isUploaded: $chatEntry.status === ChatModel.MessageStatusDelivered
readonly property bool isDelivered: $chatEntry.status === ChatModel.MessageStatusDeliveredToUser
readonly property bool isRead: $chatEntry.status === ChatModel.MessageStatusDisplayed
readonly property bool isDeliveredToUser: $chatEntry.status === ChatModel.MessageStatusDeliveredToUser
icon: isNotDelivered
icon: isError
? 'chat_error'
: (isRead ? 'chat_read' : (isDeliveredToUser ? 'chat_delivered' : ''))
: (isRead ? 'chat_read' : (isDelivered ? 'chat_delivered' : ''))
iconSize: ChatStyle.entry.message.outgoing.sendIconSize
MouseArea {
anchors.fill: parent
onClicked: isNotDelivered && proxyModel.resendMessage(index)
cursorShape: containsMouse
? Qt.PointingHandCursor
: Qt.ArrowCursor
hoverEnabled: true
visible: icon.isError || $chatEntry.status === ChatModel.MessageStatusIdle
onClicked: proxyModel.resendMessage(index)
}
TooltipArea {
text: isNotDelivered
text: isError
? qsTr('messageError')
: (isRead ? qsTr('messageRead') : qsTr('messageDelivered'))
}

View file

@ -185,7 +185,7 @@ function getTopParent (object, useFakeParent) {
// Supported options: isString, exitHandler, properties.
//
// If exitHandler is used, window must implement exitStatus signal.
function openWindow (window, parent, options) {
function openWindow (window, parent, options, fullscreen) {
var object = createObject(window, parent, options)
object.closing.connect(object.destroy.bind(object))
@ -196,8 +196,10 @@ function openWindow (window, parent, options) {
options.exitHandler.bind(parent)
)
}
object.show()
if(fullscreen)
object.showFullScreen()
else
object.show()
return object
}

View file

@ -117,7 +117,7 @@ Rectangle {
Column {
readonly property string sipAddress: $call.peerAddress
readonly property string fullSipAddress: $call.fullPeerAddress
property var _sipAddressObserver : SipAddressesModel.getSipAddressObserver($call.peerAddress, $call.localAddress)
anchors {
fill: parent
@ -134,8 +134,8 @@ Rectangle {
horizontalTextAlignment: Text.AlignHCenter
sipAddress: parent.sipAddress
username: LinphoneUtils.getContactUsername(parent.fullSipAddress)
}
username: LinphoneUtils.getContactUsername(parent._sipAddressObserver)
}
IncallAvatar {
readonly property int size: Math.min(
@ -172,15 +172,19 @@ Rectangle {
leftMargin: ConferenceStyle.grid.spacing
bottomMargin: ConferenceStyle.grid.spacing
}
enabled:!$call.speakerMuted
Timer {
interval: 50
repeat: true
running: true
onTriggered: parent.value = $call.speakerVu
}
}
MouseArea{
anchors.fill:parent
onClicked:$call.toggleSpeakerMute()
}
}
}
}

View file

@ -124,26 +124,27 @@ function openCallStatistics () {
callStatistics.open()
}
function openMediaParameters () {
function openMediaParameters (incall) {
window.attachVirtualWindow(Qt.resolvedUrl('Dialogs/MultimediaParameters.qml'), {
call: incall.call
})
}
function showFullscreen () {
function showFullscreen (position) {
if (incall._fullscreen) {
return
}
DesktopTools.DesktopTools.screenSaverStatus = false
incall._fullscreen = Utils.openWindow(Qt.resolvedUrl('IncallFullscreenWindow.qml'), window, {
properties: {
caller: incall
caller: incall,
x:position.x,
y:position.y
}
})
}, true)
}
function updateCallQualityIcon () {
function updateCallQualityIcon (callQuality,call) {
var quality = call.quality
callQuality.icon = 'call_quality_' + (
// Note: `quality` is in the [0, 5] interval.

View file

@ -103,7 +103,7 @@ Rectangle {
running: true
triggeredOnStart: true
onTriggered: Logic.updateCallQualityIcon()
onTriggered: Logic.updateCallQualityIcon(callQuality, call)
}
CallStatistics {
@ -203,7 +203,7 @@ Rectangle {
icon: 'fullscreen'
visible: incall.call.videoEnabled
onClicked: Logic.showFullscreen()
onClicked: Logic.showFullscreen(contactDescription.mapToGlobal(0,0))
}
}
}
@ -263,9 +263,8 @@ Rectangle {
ZrtpTokenAuthentication {
id: zrtp
Layout.fillWidth: true
Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter
Layout.margins: CallStyle.container.margins
Layout.preferredHeight: CallStyle.zrtpArea.height
call: incall.call
visible: !call.isSecured && call.encryption !== CallModel.CallEncryptionNone
@ -365,7 +364,7 @@ Rectangle {
icon: 'options'
iconSize: CallStyle.actionArea.iconSize
onClicked: Logic.openMediaParameters()
onClicked: Logic.openMediaParameters(incall)
}
}

View file

@ -1,4 +1,5 @@
import QtQuick 2.7
import QtQuick.Controls 2.2
import QtQuick.Layouts 1.3
import QtQuick.Window 2.2
@ -6,6 +7,7 @@ import Common 1.0
import Common.Styles 1.0
import DesktopTools 1.0
import Linphone 1.0
import LinphoneUtils 1.0
import Utils 1.0
import App.Styles 1.0
@ -15,7 +17,7 @@ import 'Incall.js' as Logic
// =============================================================================
Window {
id: window
id: windowId
// ---------------------------------------------------------------------------
@ -29,16 +31,14 @@ Window {
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) {
// The `windowId` id can be null if the windowId was closed in this view.
if (!windowId) {
return
}
// It's necessary to call `showNormal` before close on MacOs
// because the dock will be hidden forever!
window.visible = false
window.showNormal()
window.close()
if(!windowId.close() && parent)
parent.close()
if (cb) {
cb()
@ -46,42 +46,33 @@ Window {
}
// ---------------------------------------------------------------------------
Component.onCompleted: {
window.call = caller.call
var show = function (visibility) {
if (visibility === Window.Windowed) {
window.visibilityChanged.disconnect(show)
window.showFullScreen()
}
}
window.visibilityChanged.connect(show)
}
visible: false
onCallChanged: if(!call) windowId.exit()
Component.onCompleted: {
windowId.call = caller.call
}
// ---------------------------------------------------------------------------
Shortcut {
sequence: StandardKey.Close
onActivated: window.exit()
onActivated: windowId.exit()
}
// ---------------------------------------------------------------------------
Rectangle {
id:container
anchors.fill: parent
color: '#000000' // Not a style.
focus: true
Keys.onEscapePressed: window.exit()
Keys.onEscapePressed: windowId.exit()
Loader {
anchors.fill: parent
active: {
var caller = window.caller
var caller = windowId.caller
return caller && !caller.cameraActivated
}
@ -91,11 +82,10 @@ Window {
id: camera
Camera {
call: window.call
call: windowId.call
}
}
}
// -------------------------------------------------------------------------
// Handle mouse move / Hide buttons.
// -------------------------------------------------------------------------
@ -150,36 +140,57 @@ Window {
anchors.left: parent.left
iconSize: CallStyle.header.iconSize
visible: !hideButtons
Icon {
ActionButton {
id: callQuality
icon: 'call_quality_0'
iconSize: parent.iconSize
useStates: false
// See: http://www.linphone.org/docs/liblinphone/group__call__misc.html#ga62c7d3d08531b0cc634b797e273a0a73
onClicked: Logic.openCallStatistics()
// See: http://www.linphone.org/docs/liblinphone/group__call__misc.html#ga62c7d3d08531b0cc634b797e273a0a73
Timer {
interval: 5000
repeat: true
running: true
triggeredOnStart: true
onTriggered: {
var quality = call.quality
callQuality.icon = 'call_quality_' + (
// Note: `quality` is in the [0, 5] interval.
// It's necessary to map in the `call_quality_` interval. ([0, 3])
quality >= 0 ? Math.round(quality / (5 / 3)) : 0
)
}
onTriggered: Logic.updateCallQualityIcon(callQuality, windowId.call)
}
CallStatistics {
id: callStatistics
enabled: windowId.call
call: windowId.call
width: container.width
relativeTo: callQuality
relativeY: CallStyle.header.stats.relativeY
onClosed: Logic.handleCallStatisticsClosed()
}
}
ActionButton {
icon: 'tel_keypad'
onClicked: telKeypad.visible = !telKeypad.visible
}
ActionButton {
id: callSecure
icon: windowId.call && windowId.call.isSecured ? 'call_chat_secure' : 'call_chat_unsecure'
onClicked: zrtp.visible = (windowId.call.encryption === CallModel.CallEncryptionZrtp)
TooltipArea {
text: windowId.call?Logic.makeReadableSecuredString(windowId.call.securedString):''
}
}
}
// ---------------------------------------------------------------------
@ -196,7 +207,7 @@ Window {
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
visible: !window.hideButtons
visible: !windowId.hideButtons
// Not a customizable style.
color: 'white'
@ -205,9 +216,11 @@ Window {
Component.onCompleted: {
var updateDuration = function () {
var call = window.caller.call
text = Utils.formatElapsedTime(call.duration)
Utils.setTimeout(elapsedTime, 1000, updateDuration)
if(windowId.caller){
var call = windowId.caller.call
text = Utils.formatElapsedTime(call.duration)
Utils.setTimeout(elapsedTime, 1000, updateDuration)
}
}
updateDuration()
@ -236,7 +249,7 @@ Window {
ActionSwitch {
id: recordingSwitch
enabled: call.recording
enabled: call && call.recording
icon: 'record'
useStates: false
visible: SettingsModel.callRecorderEnabled
@ -255,15 +268,30 @@ Window {
ActionButton {
icon: 'fullscreen'
onClicked: window.exit()
onClicked: windowId.exit()
}
}
}
// -----------------------------------------------------------------------
// Action Buttons.
// -----------------------------------------------------------------------
// -------------------------------------------------------------------------
// Zrtp.
// -------------------------------------------------------------------------
ZrtpTokenAuthentication {
id: zrtp
Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter
Layout.margins: CallStyle.container.margins
call: windowId.call
visible: call && !call.isSecured && call.encryption !== CallModel.CallEncryptionNone
z: Constants.zPopup
color: CallStyle.backgroundColor
}
Item {
Layout.alignment: Qt.AlignBottom
Layout.fillWidth: true
@ -298,7 +326,7 @@ Window {
ActionSwitch {
id: micro
enabled: !call.microMuted
enabled: call && !call.microMuted
icon: 'micro'
iconSize: CallStyle.actionArea.iconSize
@ -324,11 +352,11 @@ Window {
ActionSwitch {
id: speaker
enabled: true
enabled: call && !call.speakerMuted
icon: 'speaker'
iconSize: CallStyle.actionArea.iconSize
onClicked: console.log('TODO')
onClicked: call.speakerMuted = enabled
}
}
@ -336,9 +364,9 @@ Window {
enabled: true
icon: 'camera'
iconSize: CallStyle.actionArea.iconSize
updating: call.updating
updating: call && call.updating
onClicked: window.exit(function () { call.videoEnabled = false })
onClicked: windowId.exit(function () { call.videoEnabled = false })
}
ActionButton {
@ -348,7 +376,7 @@ Window {
icon: 'options'
iconSize: CallStyle.actionArea.iconSize
onClicked: Logic.openMediaParameters()
onClicked: Logic.openMediaParameters(windowId)
}
}
@ -361,18 +389,18 @@ Window {
iconSize: CallStyle.actionArea.iconSize
ActionSwitch {
enabled: !call.pausedByUser
enabled: call && !call.pausedByUser
icon: 'pause'
updating: call.updating
updating: call && call.updating
visible: SettingsModel.callPauseEnabled
onClicked: window.exit(function () { call.pausedByUser = enabled })
onClicked: windowId.exit(function () { call.pausedByUser = enabled })
}
ActionButton {
icon: 'hangup'
onClicked: window.exit(call.terminate)
onClicked: windowId.exit(call.terminate)
}
}
}
@ -385,7 +413,7 @@ Window {
Loader {
active: {
var caller = window.caller
var caller = windowId.caller
return caller && !caller.cameraActivated
}
@ -398,21 +426,21 @@ Window {
property bool scale: false
function xPosition () {
return window.width / 2 - width / 2
return windowId.width / 2 - width / 2
}
function yPosition () {
return window.height - height
return windowId.height - height
}
call: window.call
call: windowId.call
isPreview: true
height: CallStyle.actionArea.userVideo.height * (scale ? 2 : 1)
width: CallStyle.actionArea.userVideo.width * (scale ? 2 : 1)
DragBox {
container: window
container: windowId
draggable: parent
xPosition: parent.xPosition
@ -431,7 +459,7 @@ Window {
TelKeypad {
id: telKeypad
call: window.call
call: windowId.call
visible: false
}
}

View file

@ -6,111 +6,118 @@ import Common 1.0
import App.Styles 1.0
// =============================================================================
ColumnLayout {
Rectangle{
id: zrtp
// ---------------------------------------------------------------------------
property var call
visible: false
color:"transparent"
implicitWidth: columnLayout.implicitWidth
implicitHeight: columnLayout.implicitHeight+CallStyle.container.margins
radius: 10
ColumnLayout {
id:columnLayout
// ---------------------------------------------------------------------------
visible: false
Layout.fillWidth: true
anchors.bottom: parent.bottom
// ---------------------------------------------------------------------------
// Main text.
// ---------------------------------------------------------------------------
Text {
Layout.fillWidth: true
Text {
Layout.fillWidth: true
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
text: qsTr('confirmSas')
text: qsTr('confirmSas')
color: CallStyle.zrtpArea.text.colorA
elide: Text.ElideRight
color: CallStyle.zrtpArea.text.colorA
elide: Text.ElideRight
font {
bold: true
pointSize: CallStyle.zrtpArea.text.pointSize
font {
bold: true
pointSize: CallStyle.zrtpArea.text.pointSize
}
}
}
// ---------------------------------------------------------------------------
// Rules.
// ---------------------------------------------------------------------------
Row {
Layout.alignment: Qt.AlignHCenter
Row {
Layout.alignment: Qt.AlignHCenter
spacing: CallStyle.zrtpArea.text.wordsSpacing
spacing: CallStyle.zrtpArea.text.wordsSpacing
Text {
color: CallStyle.zrtpArea.text.colorA
font.pointSize: CallStyle.zrtpArea.text.pointSize
text: qsTr('codeA')
}
Text {
color: CallStyle.zrtpArea.text.colorB
font {
bold: true
pointSize: CallStyle.zrtpArea.text.pointSize
Text {
color: CallStyle.zrtpArea.text.colorA
font.pointSize: CallStyle.zrtpArea.text.pointSize
text: qsTr('codeA')
}
text: zrtp.call.localSas
}
Text {
color: CallStyle.zrtpArea.text.colorB
Text {
color: CallStyle.zrtpArea.text.colorA
font.pointSize: CallStyle.zrtpArea.text.pointSize
text: '-'
}
font {
bold: true
pointSize: CallStyle.zrtpArea.text.pointSize
}
Text {
color: CallStyle.zrtpArea.text.colorA
font.pointSize: CallStyle.zrtpArea.text.pointSize
text: qsTr('codeB')
}
Text {
color: CallStyle.zrtpArea.text.colorB
font {
bold: true
pointSize: CallStyle.zrtpArea.text.pointSize
text: zrtp.call?zrtp.call.localSas:''
}
text: zrtp.call.remoteSas
Text {
color: CallStyle.zrtpArea.text.colorA
font.pointSize: CallStyle.zrtpArea.text.pointSize
text: '-'
}
Text {
color: CallStyle.zrtpArea.text.colorA
font.pointSize: CallStyle.zrtpArea.text.pointSize
text: qsTr('codeB')
}
Text {
color: CallStyle.zrtpArea.text.colorB
font {
bold: true
pointSize: CallStyle.zrtpArea.text.pointSize
}
text: zrtp.call?zrtp.call.remoteSas:''
}
}
}
// ---------------------------------------------------------------------------
// Buttons.
// ---------------------------------------------------------------------------
Row {
Layout.alignment: Qt.AlignHCenter
Row {
Layout.alignment: Qt.AlignHCenter
spacing: CallStyle.zrtpArea.buttons.spacing
spacing: CallStyle.zrtpArea.buttons.spacing
TextButtonA {
text: qsTr('deny')
onClicked: {
zrtp.visible = false
zrtp.call.verifyAuthenticationToken(false)
TextButtonA {
text: qsTr('deny')
onClicked: {
zrtp.visible = false
zrtp.call.verifyAuthenticationToken(false)
}
}
}
TextButtonB {
text: qsTr('accept')
onClicked: {
zrtp.visible = false
zrtp.call.verifyAuthenticationToken(true)
TextButtonB {
text: qsTr('accept')
onClicked: {
zrtp.visible = false
zrtp.call.verifyAuthenticationToken(true)
}
}
}
}