zrtp validation toast + blason

security level for specific address
dtmfs
ui fixes
key navigation contact page
This commit is contained in:
Gaelle Braud 2024-08-19 12:09:51 +02:00
parent 16e1b7b7c2
commit 1e234cdc25
31 changed files with 374 additions and 242 deletions

View file

@ -385,6 +385,9 @@ void CallCore::setSelf(QSharedPointer<CallCore> me) {
mCallModelConnection->invokeToModel(
[this, model = gui->getCore()->getModel()]() { mCallModel->setVideoSourceDescriptorModel(model); });
});
mCallModelConnection->makeConnectToCore(&CallCore::lSendDtmf, [this](QString dtmf) {
mCallModelConnection->invokeToModel([this, dtmf]() { mCallModel->sendDtmf(dtmf); });
});
mCallModelConnection->makeConnectToModel(&CallModel::videoDescriptorChanged, [this]() {
auto videoSource = mCallModel->getMonitor()->getVideoSource();

View file

@ -287,6 +287,7 @@ signals:
void lSetOutputAudioDevice(QString id);
void lSetConferenceVideoLayout(LinphoneEnums::ConferenceLayout layout);
void lSetVideoSourceDescriptor(VideoSourceDescriptorGui *gui);
void lSendDtmf(QString dtmf);
/* TODO
Q_INVOKABLE void acceptWithVideo();
@ -302,7 +303,6 @@ signals:
Q_INVOKABLE void takeSnapshot();
Q_INVOKABLE void sendDtmf(const QString &dtmf);
Q_INVOKABLE void verifyAuthenticationToken(bool verify);
Q_INVOKABLE void updateStreams();
*/

View file

@ -412,6 +412,16 @@ void FriendCore::setDevices(QVariantList devices) {
emit devicesChanged();
}
LinphoneEnums::SecurityLevel FriendCore::getSecurityLevelForAddress(const QString &address) const {
for (auto &device : mDeviceList) {
auto map = device.toMap();
if (map["address"].toString() == address) {
return map["securityLevel"].value<LinphoneEnums::SecurityLevel>();
}
}
return LinphoneEnums::SecurityLevel::None;
}
QString FriendCore::getDefaultAddress() const {
return mDefaultAddress;
}

View file

@ -117,6 +117,7 @@ public:
QList<QVariant> getDevices() const;
void updateVerifiedDevicesCount();
void setDevices(QVariantList devices);
Q_INVOKABLE LinphoneEnums::SecurityLevel getSecurityLevelForAddress(const QString &address) const;
LinphoneEnums::ConsolidatedPresence getConsolidatedPresence() const;
void setConsolidatedPresence(LinphoneEnums::ConsolidatedPresence presence);

View file

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

View file

@ -382,6 +382,13 @@ void CallModel::setVideoSourceDescriptorModel(std::shared_ptr<VideoSourceDescrip
}
}
void CallModel::sendDtmf(const QString &dtmf) {
const char key = dtmf.constData()[0].toLatin1();
qInfo() << QStringLiteral("Send dtmf: `%1`.").arg(key);
if (mMonitor) mMonitor->sendDtmf(key);
CoreModel::getInstance()->getCore()->playDtmf(key, dtmfSoundDelay);
}
void CallModel::onDtmfReceived(const std::shared_ptr<linphone::Call> &call, int dtmf) {
emit dtmfReceived(call, dtmf);
}

View file

@ -88,6 +88,8 @@ public:
const std::shared_ptr<const linphone::CallParams> &currentParams,
bool enable);
void sendDtmf(const QString &dtmf);
signals:
void microphoneMutedChanged(bool isMuted);
void speakerMutedChanged(bool isMuted);
@ -111,6 +113,7 @@ private:
QTimer mMicroVolumeTimer;
std::shared_ptr<linphone::Conference> mConference;
LinphoneEnums::ConferenceLayout mConferenceVideoLayout;
static constexpr int dtmfSoundDelay = 200;
DECLARE_ABSTRACT_OBJECT

View file

@ -271,6 +271,11 @@ QString FriendModel::getPictureUri() const {
}
QString FriendModel::getVCardAsString() const {
auto vcard = mMonitor->getVcard();
bool created = false;
if (!vcard) {
created = mMonitor->createVcard(mMonitor->getName());
}
assert(mMonitor->getVcard());
return Utils::coreStringToAppString(mMonitor->getVcard()->asVcard4String());
}
@ -279,6 +284,15 @@ std::list<std::shared_ptr<linphone::FriendDevice>> FriendModel::getDevices() con
return mMonitor->getDevices();
}
linphone::SecurityLevel FriendModel::getSecurityLevel() const {
return mMonitor->getSecurityLevel();
}
linphone::SecurityLevel
FriendModel::getSecurityLevelForAddress(const std::shared_ptr<linphone::Address> address) const {
return mMonitor->getSecurityLevelForAddress(address);
}
void FriendModel::setPictureUri(const QString &uri) {
mustBeInLinphoneThread(log().arg(Q_FUNC_INFO));
auto oldPictureUri = Utils::coreStringToAppString(mMonitor->getPhoto());

View file

@ -55,6 +55,8 @@ public:
QString getPictureUri() const;
QString getVCardAsString() const;
std::list<std::shared_ptr<linphone::FriendDevice>> getDevices() const;
linphone::SecurityLevel getSecurityLevel() const;
linphone::SecurityLevel getSecurityLevelForAddress(const std::shared_ptr<linphone::Address> address) const;
protected:
void setAddress(const std::shared_ptr<linphone::Address> &address);

View file

@ -367,6 +367,24 @@ VariantObject *Utils::findFriendByAddress(const QString &address) {
return data;
}
VariantObject *Utils::getFriendAddressSecurityLevel(const QString &address) {
VariantObject *data = new VariantObject();
if (!data) return nullptr;
data->makeRequest([address]() {
auto defaultFriendList = CoreModel::getInstance()->getCore()->getDefaultFriendList();
if (!defaultFriendList) return QVariant();
auto linphoneAddr = ToolModel::interpretUrl(address);
auto linFriend = CoreModel::getInstance()->getCore()->findFriend(linphoneAddr);
if (!linFriend) return QVariant();
auto linAddr = ToolModel::interpretUrl(address);
if (!linAddr) return QVariant();
auto secuLevel = linFriend->getSecurityLevelForAddress(linAddr);
return QVariant::fromValue(LinphoneEnums::fromLinphone(secuLevel));
});
data->requestValue();
return data;
}
QString Utils::generateSavedFilename(const QString &from, const QString &to) {
auto escape = [](const QString &str) {
constexpr char ReservedCharacters[] = "[<|>|:|\"|/|\\\\|\\?|\\*|\\+|\\||_|-]+";
@ -1328,3 +1346,8 @@ bool Utils::isUsername(const QString &txt) {
void Utils::useFetchConfig(const QString &configUrl) {
App::getInstance()->receivedMessage(0, ("fetch-config=" + configUrl).toLocal8Bit());
}
void Utils::playDtmf(const QString &dtmf) {
const char key = dtmf.constData()[0].toLatin1();
App::postModelSync([key]() { CoreModel::getInstance()->getCore()->playDtmf(key, 200); });
}

View file

@ -120,12 +120,14 @@ public:
Q_INVOKABLE static bool isValidURL(const QString &url);
Q_INVOKABLE static QString findAvatarByAddress(const QString &address);
Q_INVOKABLE static VariantObject *findFriendByAddress(const QString &address);
Q_INVOKABLE static VariantObject *getFriendAddressSecurityLevel(const QString &address);
static QString generateSavedFilename(const QString &from, const QString &to);
Q_INVOKABLE static bool isMe(const QString &address);
Q_INVOKABLE static bool isLocal(const QString &address);
Q_INVOKABLE static bool isUsername(const QString &txt); // Regex check
static QString getCountryName(const QLocale::Territory &p_country);
Q_INVOKABLE static void useFetchConfig(const QString &configUrl);
Q_INVOKABLE void playDtmf(const QString &dtmf);
static QString getApplicationProduct();
static QString getOsProduct();

View file

@ -25,7 +25,7 @@ AppWindow {
onCallStateChanged: {
if (callState === LinphoneEnums.CallState.Connected || callState === LinphoneEnums.CallState.StreamsRunning) {
if (middleItemStackView.currentItem.objectName != inCallItem) {
if (middleItemStackView.currentItem.objectName != "inCallItem") {
middleItemStackView.replace(inCallItem)
bottomButtonsLayout.visible = true
}
@ -34,9 +34,11 @@ AppWindow {
}
}
else if (callState === LinphoneEnums.CallState.Error || callState === LinphoneEnums.CallState.End) {
zrtpValidation.close()
callEnded(call)
}
}
onTransferStateChanged: {
console.log("Transfer state:", transferState)
if (transferState === LinphoneEnums.CallState.Error) {
@ -115,12 +117,21 @@ AppWindow {
target: call && call.core
function onRemoteVideoEnabledChanged() { console.log("remote video enabled", call.core.remoteVideoEnabled)}
function onSecurityUpdated() {
if (call.core.encryption != LinphoneEnums.MediaEncryption.Zrtp || call.core.tokenVerified) {
if (call.core.encryption === LinphoneEnums.MediaEncryption.Zrtp) {
if (call.core.tokenVerified) {
zrtpValidation.close()
zrtpValidationToast.open()
} else {
zrtpValidation.open()
}
} else {
zrtpValidation.close()
}
else if(call.core.encryption === LinphoneEnums.MediaEncryption.Zrtp) {
zrtpValidation.open()
}
}
function onTokenVerified() {
if (!zrtpValidation.isTokenVerified) {
zrtpValidation.securityError = true
} else zrtpValidation.close()
}
}
@ -222,6 +233,55 @@ AppWindow {
}
}
}
Timer {
id: autoCloseZrtpToast
interval: 4000
onTriggered: {
zrtpValidationToast.y = -zrtpValidationToast.height*2
}
}
Control.Control {
id: zrtpValidationToast
// width: 269 * DefaultStyle.dp
y: -height*2
z: 1
topPadding: 8 * DefaultStyle.dp
bottomPadding: 8 * DefaultStyle.dp
leftPadding: 50 * DefaultStyle.dp
rightPadding: 50 * DefaultStyle.dp
anchors.horizontalCenter: parent.horizontalCenter
clip: true
function open() {
y = headerItem.height/2
autoCloseZrtpToast.restart()
}
Behavior on y {NumberAnimation {duration: 1000}}
background: Rectangle {
anchors.fill: parent
color: DefaultStyle.grey_0
border.color: DefaultStyle.info_500_main
border.width: 1 * DefaultStyle.dp
radius: 50 * DefaultStyle.dp
}
contentItem: RowLayout {
// anchors.centerIn: parent
Image {
source: AppIcons.trusted
Layout.preferredWidth: 24 * DefaultStyle.dp
Layout.preferredHeight: 24 * DefaultStyle.dp
fillMode: Image.PreserveAspectFit
Layout.fillWidth: true
}
Text {
color: DefaultStyle.info_500_main
text: qsTr("Appareil vérifié")
Layout.fillWidth: true
font {
pixelSize: 14 * DefaultStyle.dp
}
}
}
}
/************************* CONTENT ********************************/
Rectangle {
@ -233,6 +293,7 @@ AppWindow {
anchors.bottomMargin: 10 * DefaultStyle.dp
anchors.topMargin: 10 * DefaultStyle.dp
Item {
id: headerItem
Layout.margins: 10 * DefaultStyle.dp
Layout.fillWidth: true
Layout.minimumHeight: 25 * DefaultStyle.dp
@ -710,7 +771,7 @@ AppWindow {
spacing: 0
Avatar {
id: delegateAvatar
address: modelData.core.peerAddress
_address: modelData.core.peerAddress
Layout.preferredWidth: 45 * DefaultStyle.dp
Layout.preferredHeight: 45 * DefaultStyle.dp
}
@ -1176,6 +1237,7 @@ AppWindow {
id: waitingRoom
WaitingRoom {
id: waitingRoomIn
objectName: "waitingRoom"
Layout.alignment: Qt.AlignCenter
onSettingsButtonCheckedChanged: {
if (settingsButtonChecked) {
@ -1187,21 +1249,21 @@ AppWindow {
}
Binding {
target: callStatusIcon
when: middleItemStackView.currentItem === waitingRoomIn
when: middleItemStackView.currentItem.objectName === "waitingRoom"
property: "imageSource"
value: AppIcons.usersThree
restoreMode: Binding.RestoreBindingOrValue
}
Binding {
target: callStatusText
when: middleItemStackView.currentItem === waitingRoomIn
when: middleItemStackView.currentItem.objectName === "waitingRoom"
property: "text"
value: waitingRoomIn.conferenceInfo ? waitingRoomIn.conferenceInfo.core.subject : ''
restoreMode: Binding.RestoreBindingOrValue
}
Binding {
target: conferenceDate
when: middleItemStackView.currentItem === waitingRoomIn
when: middleItemStackView.currentItem.objectName === "waitingRoom"
property: "text"
value: waitingRoomIn.conferenceInfo ? waitingRoomIn.conferenceInfo.core.startEndDateString : ''
}

View file

@ -61,7 +61,7 @@ Item {
AccountProxy {
id: accountProxy
onDefaultAccountChanged: if (tabbar.currentIndex === 0) defaultAccount.core.lResetMissedCalls()
onDefaultAccountChanged: if (tabbar.currentIndex === 0 && defaultAccount) defaultAccount.core.lResetMissedCalls()
}
Timer {
@ -107,7 +107,6 @@ Item {
Layout.fillHeight: true
Layout.preferredWidth: 82 * DefaultStyle.dp
defaultAccount: accountProxy.defaultAccount
property int unreadMessages: defaultAccount.core.unreadMessageNotifications
currentIndex: SettingsCpp.getLastActiveTabIndex()
model: [
{icon: AppIcons.phone, selectedIcon: AppIcons.phoneSelected, label: qsTr("Appels")},
@ -117,7 +116,7 @@ Item {
]
onCurrentIndexChanged: {
SettingsCpp.setLastActiveTabIndex(currentIndex)
if (currentIndex === 0) accountProxy.defaultAccount.core.lResetMissedCalls()
if (currentIndex === 0 && accountProxy.defaultAccount) accountProxy.defaultAccount.core.lResetMissedCalls()
if (mainItem.contextualMenuOpenedComponent) {
closeContextualMenuComponent()
}
@ -304,7 +303,7 @@ Item {
Avatar {
Layout.preferredWidth: 45 * DefaultStyle.dp
Layout.preferredHeight: 45 * DefaultStyle.dp
address: magicSearchBar.text
_address: magicSearchBar.text
}
ColumnLayout {
Text {

View file

@ -34,6 +34,7 @@ FocusScope {
id: searchBar
Layout.alignment: Qt.AlignTop
Layout.fillWidth: true
Layout.rightMargin: 39 * DefaultStyle.dp
Layout.maximumWidth: mainItem.width
focus: true
color: mainItem.searchBarColor

View file

@ -23,19 +23,26 @@ StackView {
: contact
? contact.core.defaultAddress
: ''
property string address: SettingsCpp.onlyDisplaySipUriUsername ? UtilsCpp.getUsername(_address) : _address
property var displayNameObj: UtilsCpp.getDisplayName(address)
readonly property string address: SettingsCpp.onlyDisplaySipUriUsername ? UtilsCpp.getUsername(_address) : _address
property var displayNameObj: UtilsCpp.getDisplayName(_address)
property string displayNameVal: displayNameObj ? displayNameObj.value : ""
property bool haveAvatar: (account && account.core.pictureUri )
|| (contact && contact.core.pictureUri)
|| computedAvatarUri.length != 0
property string computedAvatarUri: UtilsCpp.findAvatarByAddress(address)
property string computedAvatarUri: UtilsCpp.findAvatarByAddress(_address)
onHaveAvatarChanged: replace(haveAvatar ? avatar : initials, StackView.Immediate)
property bool secured: contact
? contact.core.devices.length != 0 && contact.core.verifiedDeviceCount === contact.core.devices.length
: false
property var securityLevelObj: UtilsCpp.getFriendAddressSecurityLevel(_address)
property var securityLevel: securityLevelObj ? securityLevelObj.value : LinphoneEnums.SecurityLevel.None
property bool secured: call && call.core.encryption === LinphoneEnums.MediaEncryption.Zrtp
? call.core.tokenVerified
: contact
? contact.core.devices.length != 0 && contact.core.verifiedDeviceCount === contact.core.devices.length
: securityLevel === LinphoneEnums.SecurityLevel.EndToEndEncrypted
property bool securityBreach: securityLevel === LinphoneEnums.SecurityLevel.Unsafe
property bool displayPresence: (account || contact) && (account
? account.core.registrationState != LinphoneEnums.RegistrationState.Progress && account.core.registrationState != LinphoneEnums.RegistrationState.Refreshing
: contact.core.consolidatedPresence != LinphoneEnums.ConsolidatedPresence.Offline)
@ -43,17 +50,17 @@ StackView {
initialItem: haveAvatar ? avatar : initials
Rectangle {
visible: mainItem.secured
visible: mainItem.secured || mainItem.securityBreach
anchors.fill: mainItem.currentItem
radius: mainItem.width / 2
z: 1
color: "transparent"
border {
width: 3 * DefaultStyle.dp
color: DefaultStyle.info_500_main
color: mainItem.secured ? DefaultStyle.info_500_main : DefaultStyle.danger_500main
}
Image {
source: AppIcons.trusted
source: mainItem.secured ? AppIcons.trusted : AppIcons.notTrusted
x: mainItem.width / 7
width: mainItem.width / 4.5
height: width

View file

@ -316,7 +316,7 @@ RightPanelLayout {
}
}
onEditingFinished: {
if (text.length != 0) mainItem.contact.core.appendAddress(text)
if (text != "sip:") mainItem.contact.core.appendAddress(text)
text = "sip:"
}
Component.onCompleted: text = "sip:"

View file

@ -112,7 +112,7 @@ Item {
visible: !joiningView.visible
account: mainItem.account
call: !mainItem.previewEnabled ? mainItem.call : null
address: mainItem.peerAddress
_address: mainItem.peerAddress
}
ColumnLayout{
id: joiningView

View file

@ -28,9 +28,9 @@ Popup {
signal accepted()
signal rejected()
contentItem: FocusScope{
height: child.implicitHeight
contentItem: FocusScope {
width: child.implicitWidth
height: child.implicitHeight
onVisibleChanged: {
if(visible) forceActiveFocus()
}
@ -44,6 +44,7 @@ Popup {
id: child
anchors.fill: parent
spacing: 15 * DefaultStyle.dp
Text{
id: titleText
Layout.fillWidth: true
@ -94,9 +95,8 @@ Popup {
ColumnLayout {
id: contentLayout
Layout.fillWidth: true
Layout.fillHeight: true
Layout.alignment: Qt.AlignHCenter
Layout.fillHeight: false
}
RowLayout {

View file

@ -337,7 +337,7 @@ FocusScope {
Avatar {
Layout.preferredWidth: 45 * DefaultStyle.dp
Layout.preferredHeight: 45 * DefaultStyle.dp
address: modelData.address
_address: modelData.address
}
Text {
text: modelData.displayName

View file

@ -3,12 +3,10 @@ import QtQuick.Controls as Control
import QtQuick.Layouts as Layout
import QtQuick.Effects
import Linphone
import UtilsCpp
Control.Popup {
id: mainItem
signal buttonPressed(string text)
signal launchCall()
signal wipe()
property bool closeButtonVisible: true
property bool roundedBottom: false
closePolicy: Control.Popup.CloseOnEscape
@ -17,6 +15,16 @@ Control.Popup {
topPadding: 41 * DefaultStyle.dp
bottomPadding: 18 * DefaultStyle.dp
onOpened: numPad.forceActiveFocus()
signal buttonPressed(string text)
onButtonPressed: (text) => {
if (callsModel.currentCall) callsModel.currentCall.core.lSendDtmf(text)
else UtilsCpp.playDtmf(text)
}
signal launchCall()
signal wipe()
CallProxy{
id: callsModel
}
background: Item {
anchors.fill: parent
Rectangle {

View file

@ -46,7 +46,7 @@ ListView {
Avatar {
Layout.preferredWidth: 45 * DefaultStyle.dp
Layout.preferredHeight: 45 * DefaultStyle.dp
address: modelData.core.address
_address: modelData.core.address
}
Text {
text: modelData.core.displayName

View file

@ -50,7 +50,7 @@ ListView {
Avatar {
Layout.preferredWidth: 45 * DefaultStyle.dp
Layout.preferredHeight: 45 * DefaultStyle.dp
address: modelData.core.sipAddress
_address: modelData.core.sipAddress
}
Text {
text: modelData.core.displayName

View file

@ -15,7 +15,7 @@ Control.TabBar {
property AccountGui defaultAccount
onDefaultAccountChanged: {
defaultAccount.core.lRefreshNotifications()
if (defaultAccount) defaultAccount.core.lRefreshNotifications()
}
component UnreadNotification: Rectangle {
@ -91,7 +91,13 @@ Control.TabBar {
visible: modelData?.visible != undefined ? modelData?.visible : true
UnreadNotification {
unread: index == 0 ? defaultAccount.core.unreadCallNotifications : index == 2 ? defaultAccount.core.unreadMessageNotifications : 0// modelData.unreadNotifications
unread: !defaultAccount
? -1
: index == 0
? defaultAccount.core.unreadCallNotifications
: index == 2
? defaultAccount.core.unreadMessageNotifications
: 0
anchors.right: parent.right
anchors.rightMargin: 15 * DefaultStyle.dp
anchors.top: parent.top

View file

@ -11,7 +11,7 @@ Dialog {
width: 436 * DefaultStyle.dp
rightPadding: 0 * DefaultStyle.dp
leftPadding: 0 * DefaultStyle.dp
topPadding: 85 * DefaultStyle.dp + 23 * DefaultStyle.dp
topPadding: 85 * DefaultStyle.dp + 24 * DefaultStyle.dp
bottomPadding: 24 * DefaultStyle.dp
modal: true
closePolicy: Popup.NoAutoClose
@ -21,25 +21,7 @@ Dialog {
property bool isTokenVerified: call && call.core.tokenVerified || false
property bool isCaseMismatch: call && call.core.isMismatch || false
property bool securityError: false
property bool firstTry: true
Connections {
enabled: call != undefined && call != null
target: call && call.core
onStatusChanged: if (status === CallModel.CallStatusEnded) close()
function onSecurityUpdated() {
if (mainItem.isTokenVerified) {
close()
// mainItem.securityError = true
// } else close()
}
}
function onTokenVerified() {
if (!mainItem.isTokenVerified) {
mainItem.securityError = true
} else close()
}
}
// property bool firstTry: true
background: Item {
anchors.fill: parent
@ -118,7 +100,7 @@ Dialog {
Rectangle {
z: 1
width: mainItem.width
height: parent.height - 87 * DefaultStyle.dp
height: parent.height - 85 * DefaultStyle.dp
x: parent.x
y: parent.y + 85 * DefaultStyle.dp
color: DefaultStyle.grey_0
@ -242,9 +224,10 @@ Dialog {
}
},
Layout.ColumnLayout {
visible: mainItem.securityError
spacing: 0
Text {
visible: mainItem.securityError
width: 303 * DefaultStyle.dp
// Layout.Layout.preferredWidth: 343 * DefaultStyle.dp
Layout.Layout.alignment: Qt.AlignHCenter
@ -255,7 +238,6 @@ Dialog {
font.pixelSize: 14 * DefaultStyle.dp
}
Text {
visible: mainItem.securityError
width: 303 * DefaultStyle.dp
// Layout.Layout.preferredWidth: 343 * DefaultStyle.dp
Layout.Layout.alignment: Qt.AlignHCenter
@ -286,21 +268,6 @@ Dialog {
if(mainItem.call) mainItem.call.core.lCheckAuthenticationTokenSelected(" ")
}
}
Button {
Layout.Layout.preferredWidth: 247 * DefaultStyle.dp
visible: mainItem.securityError && mainItem.firstTry
text: qsTr("Réessayer")
color: DefaultStyle.danger_500main
inversedColors: true
leftPadding: 16 * DefaultStyle.dp
rightPadding: 16 * DefaultStyle.dp
topPadding: 10 * DefaultStyle.dp
bottomPadding: 10 * DefaultStyle.dp
onClicked: {
mainItem.firstTry = false
mainItem.securityError = false
}
}
Button {
Layout.Layout.preferredWidth: 247 * DefaultStyle.dp
visible: mainItem.securityError

View file

@ -13,9 +13,13 @@ ColumnLayout {
property FriendGui contact
property ConferenceInfoGui conferenceInfo
property bool isConference: conferenceInfo != undefined && conferenceInfo != null
property string contactAddress: contact && contact.core.defaultAddress || ""
property string contactAddress: specificAddress != "" ? specificAddress : contact && contact.core.defaultAddress || ""
property string contactName: contact && contact.core.displayName || ""
// Set this property to get the security informations
// for a specific address and not for the entire contact
property string specificAddress: ""
property alias buttonContent: rightButton.data
property alias detailContent: detailControl.data
@ -60,12 +64,12 @@ ColumnLayout {
Layout.alignment: Qt.AlignHCenter
Avatar {
id: detailAvatar
anchors.horizontalCenter: parent.horizontalCenter
anchors.horizontalCenter: parent.horizontalCenter
width: 100 * DefaultStyle.dp
height: 100 * DefaultStyle.dp
contact: mainItem.contact || null
address: mainItem.conferenceInfo
? mainItem.conferenceInfo.core.subject
contact: mainItem.specificAddress == "" ? mainItem.contact : null
_address: mainItem.conferenceInfo
? mainItem.conferenceInfo.core.subject
: mainItem.contactAddress || mainItem.contactName
}
Item {
@ -95,6 +99,19 @@ ColumnLayout {
capitalization: Font.Capitalize
}
}
Text {
Layout.alignment: Qt.AlignHCenter
Layout.fillWidth: true
horizontalAlignment: Text.AlignHCenter
visible: mainItem.specificAddress != ""
text: mainItem.specificAddress
elide: Text.ElideMiddle
maximumLineCount: 1
font {
pixelSize: 12 * DefaultStyle.dp
weight: 300 * DefaultStyle.dp
}
}
Text {
property var mode : contact ? contact.core.consolidatedPresence : -1
Layout.alignment: Qt.AlignHCenter
@ -119,9 +136,6 @@ ColumnLayout {
weight: 300 * DefaultStyle.dp
}
}
Text {
// connection status
}
}
}
RowLayout {
@ -153,8 +167,8 @@ ColumnLayout {
button.icon.source: AppIcons.phone
label: qsTr("Appel")
button.onClicked: {
if (mainItem.contact) mainWindow.startCallWithContact(mainItem.contact, false, mainItem)
else UtilsCpp.createCall(mainItem.contactAddress)
if (mainItem.specificAddress === "") mainWindow.startCallWithContact(mainItem.contact, false, mainItem)
else UtilsCpp.createCall(mainItem.specificAddress)
}
}
LabelButton {
@ -176,8 +190,8 @@ ColumnLayout {
button.icon.source: AppIcons.videoCamera
label: qsTr("Appel Video")
button.onClicked: {
if (mainItem.contact) mainWindow.startCallWithContact(mainItem.contact, true, mainItem)
else UtilsCpp.createCall(mainItem.contactAddress, {'localVideoEnabled': true})
if (mainItem.specificAddress === "") mainWindow.startCallWithContact(mainItem.contact, true, mainItem)
else UtilsCpp.createCall(mainItem.specificAddress, {'localVideoEnabled': true})
}
}
}

View file

@ -80,7 +80,7 @@ FocusScope{
Avatar {
Layout.preferredWidth: 45 * DefaultStyle.dp
Layout.preferredHeight: 45 * DefaultStyle.dp
address: modelData
_address: modelData
}
Text {
property var nameObj: UtilsCpp.getDisplayName(modelData)
@ -167,7 +167,7 @@ FocusScope{
Avatar {
Layout.preferredWidth: 45 * DefaultStyle.dp
Layout.preferredHeight: 45 * DefaultStyle.dp
address: sipAddr.text
_address: sipAddr.text
}
ColumnLayout {
spacing: 0

View file

@ -296,7 +296,7 @@ AbstractMainPage {
}
Avatar {
id: historyAvatar
address: modelData.core.remoteAddress
_address: modelData.core.remoteAddress
width: 45 * DefaultStyle.dp
height: 45 * DefaultStyle.dp
}
@ -647,7 +647,7 @@ AbstractMainPage {
property var contactObj: UtilsCpp.findFriendByAddress(contactAddress)
contact: contactObj && contactObj.value || null
conferenceInfo: mainItem.selectedRowHistoryGui && mainItem.selectedRowHistoryGui.core.conferenceInfo || null
contactAddress: mainItem.selectedRowHistoryGui && mainItem.selectedRowHistoryGui.core.remoteAddress || ""
specificAddress: mainItem.selectedRowHistoryGui && mainItem.selectedRowHistoryGui.core.remoteAddress || ""
contactName: mainItem.selectedRowHistoryGui ? mainItem.selectedRowHistoryGui.core.displayName : ""
buttonContent: PopupButton {

View file

@ -147,7 +147,7 @@ AbstractMainPage {
}
}
leftPanelContent: Item {
leftPanelContent: FocusScope {
id: leftPanel
property int leftMargin: 45 * DefaultStyle.dp
property int rightMargin: 39 * DefaultStyle.dp
@ -173,6 +173,7 @@ AbstractMainPage {
Layout.fillWidth: true
}
Button {
id: createContactButton
background: Item {
}
icon.source: AppIcons.plusCircle
@ -183,6 +184,7 @@ AbstractMainPage {
onClicked: {
mainItem.createContact("", "")
}
KeyNavigation.down: searchBar
}
}
@ -201,153 +203,163 @@ AbstractMainPage {
Layout.topMargin: 18 * DefaultStyle.dp
Layout.fillWidth: true
placeholderText: qsTr("Rechercher un contact")
KeyNavigation.up: createContactButton
KeyNavigation.down: favoriteList.contentHeight > 0 ? favoriteExpandButton : contactExpandButton
}
Flickable {
id: listLayout
contentWidth: width
contentHeight: content.height
clip: true
Control.ScrollBar.vertical: contactsScrollbar
Layout.fillWidth: true
Layout.fillHeight: true
RowLayout {
Flickable {
id: listLayout
contentWidth: width
contentHeight: content.height
clip: true
Control.ScrollBar.vertical: contactsScrollbar
Layout.fillWidth: true
Layout.fillHeight: true
ColumnLayout {
id: content
width: parent.width
spacing: 15 * DefaultStyle.dp
Text {
visible: contactList.count === 0 && favoriteList.count === 0
Layout.alignment: Qt.AlignHCenter
Layout.topMargin: 137 * DefaultStyle.dp
text: qsTr("Aucun contact")
font {
pixelSize: 16 * DefaultStyle.dp
weight: 800 * DefaultStyle.dp
}
}
ColumnLayout {
visible: favoriteList.contentHeight > 0
onVisibleChanged: if (visible && !favoriteList.visible) favoriteList.visible = true
Layout.leftMargin: leftPanel.leftMargin
Layout.rightMargin: leftPanel.rightMargin
spacing: 18 * DefaultStyle.dp
RowLayout {
spacing: 0
Text {
text: qsTr("Favoris")
font {
pixelSize: 16 * DefaultStyle.dp
weight: 800 * DefaultStyle.dp
id: content
width: parent.width
spacing: 15 * DefaultStyle.dp
Text {
visible: contactList.count === 0 && favoriteList.count === 0
Layout.alignment: Qt.AlignHCenter
Layout.topMargin: 137 * DefaultStyle.dp
text: qsTr("Aucun contact")
font {
pixelSize: 16 * DefaultStyle.dp
weight: 800 * DefaultStyle.dp
}
}
ColumnLayout {
visible: favoriteList.contentHeight > 0
onVisibleChanged: if (visible && !favoriteList.visible) favoriteList.visible = true
Layout.leftMargin: leftPanel.leftMargin
Layout.rightMargin: leftPanel.rightMargin
spacing: 18 * DefaultStyle.dp
RowLayout {
spacing: 0
Text {
text: qsTr("Favoris")
font {
pixelSize: 16 * DefaultStyle.dp
weight: 800 * DefaultStyle.dp
}
}
Item {
Layout.fillWidth: true
}
Button {
id: favoriteExpandButton
background: Item{}
icon.source: favoriteList.visible ? AppIcons.upArrow : AppIcons.downArrow
Layout.preferredWidth: 24 * DefaultStyle.dp
Layout.preferredHeight: 24 * DefaultStyle.dp
icon.width: 24 * DefaultStyle.dp
icon.height: 24 * DefaultStyle.dp
onClicked: favoriteList.visible = !favoriteList.visible
KeyNavigation.up: searchBar
KeyNavigation.down: favoriteList
}
}
Item {
ContactsList{
id: favoriteList
onActiveFocusChanged: if (activeFocus) console.log("favorite list focus")
hoverEnabled: mainItem.leftPanelEnabled
Layout.fillWidth: true
}
Button {
background: Item{}
icon.source: favoriteList.visible ? AppIcons.upArrow : AppIcons.downArrow
Layout.preferredWidth: 24 * DefaultStyle.dp
Layout.preferredHeight: 24 * DefaultStyle.dp
icon.width: 24 * DefaultStyle.dp
icon.height: 24 * DefaultStyle.dp
onClicked: favoriteList.visible = !favoriteList.visible
}
}
ContactsList{
id: favoriteList
hoverEnabled: mainItem.leftPanelEnabled
Layout.fillWidth: true
Layout.preferredHeight: contentHeight
Control.ScrollBar.vertical.visible: false
showOnlyFavourites: true
contactMenuVisible: true
model: allFriends
onSelectedContactChanged: {
if (selectedContact) {
contactList.currentIndex = -1
Layout.preferredHeight: contentHeight
Control.ScrollBar.vertical.visible: false
showOnlyFavourites: true
contactMenuVisible: true
model: allFriends
onSelectedContactChanged: {
if (selectedContact) {
contactList.currentIndex = -1
}
mainItem.selectedContact = selectedContact
}
mainItem.selectedContact = selectedContact
}
onContactDeletionRequested: (contact) => {
dialog.contact = contact
dialog.open()
}
}
}
ColumnLayout {
visible: contactList.count > 0
onVisibleChanged: if (visible && !contactList.visible) contactList.visible = true
Layout.leftMargin: leftPanel.leftMargin
Layout.rightMargin: leftPanel.rightMargin
spacing: 16 * DefaultStyle.dp
RowLayout {
spacing: 0
Text {
text: qsTr("All contacts")
font {
pixelSize: 16 * DefaultStyle.dp
weight: 800 * DefaultStyle.dp
onContactDeletionRequested: (contact) => {
dialog.contact = contact
dialog.open()
}
}
Item {
}
ColumnLayout {
visible: contactList.contentHeight > 0
onVisibleChanged: if (visible && !contactList.visible) contactList.visible = true
Layout.leftMargin: leftPanel.leftMargin
Layout.rightMargin: leftPanel.rightMargin
spacing: 16 * DefaultStyle.dp
RowLayout {
spacing: 0
Text {
text: qsTr("All contacts")
font {
pixelSize: 16 * DefaultStyle.dp
weight: 800 * DefaultStyle.dp
}
}
Item {
Layout.fillWidth: true
}
Button {
id: contactExpandButton
background: Item{}
icon.source: contactList.visible ? AppIcons.upArrow : AppIcons.downArrow
Layout.preferredWidth: 24 * DefaultStyle.dp
Layout.preferredHeight: 24 * DefaultStyle.dp
icon.width: 24 * DefaultStyle.dp
icon.height: 24 * DefaultStyle.dp
onClicked: contactList.visible = !contactList.visible
KeyNavigation.up: favoriteList.visible ? favoriteList : searchBar
KeyNavigation.down: contactList
}
}
ContactsList{
id: contactList
onActiveFocusChanged: if (activeFocus) console.log("contact list focus")
onCountChanged: {
if (initialFriendToDisplay.length !== 0) {
if (selectContact(initialFriendToDisplay) != -1) initialFriendToDisplay = ""
}
}
Layout.fillWidth: true
}
Button {
background: Item{}
icon.source: contactList.visible ? AppIcons.upArrow : AppIcons.downArrow
Layout.preferredWidth: 24 * DefaultStyle.dp
Layout.preferredHeight: 24 * DefaultStyle.dp
icon.width: 24 * DefaultStyle.dp
icon.height: 24 * DefaultStyle.dp
onClicked: contactList.visible = !contactList.visible
}
}
ContactsList{
id: contactList
onCountChanged: {
if (initialFriendToDisplay.length !== 0) {
if (selectContact(initialFriendToDisplay) != -1) initialFriendToDisplay = ""
Layout.preferredHeight: contentHeight
interactive: false
Control.ScrollBar.vertical.visible: false
hoverEnabled: mainItem.leftPanelEnabled
contactMenuVisible: true
searchBarText: searchBar.text
model: allFriends
Connections {
target: allFriends
function onFriendCreated(index) {
contactList.currentIndex = index
}
}
}
Layout.fillWidth: true
Layout.preferredHeight: contentHeight
interactive: false
Control.ScrollBar.vertical.visible: false
hoverEnabled: mainItem.leftPanelEnabled
contactMenuVisible: true
searchBarText: searchBar.text
model: allFriends
Connections {
target: allFriends
function onFriendCreated(index) {
contactList.currentIndex = index
onSelectedContactChanged: {
if (selectedContact) {
favoriteList.currentIndex = -1
}
mainItem.selectedContact = selectedContact
}
}
onSelectedContactChanged: {
if (selectedContact) {
favoriteList.currentIndex = -1
onContactDeletionRequested: (contact) => {
dialog.contact = contact
dialog.open()
}
mainItem.selectedContact = selectedContact
}
onContactDeletionRequested: (contact) => {
dialog.contact = contact
dialog.open()
}
}
}
}
}
ScrollBar {
id: contactsScrollbar
anchors.right: listLayout.right
anchors.rightMargin: 8 * DefaultStyle.dp
anchors.top: listLayout.top
anchors.bottom: listLayout.bottom
height: listLayout.height
active: true
interactive: true
policy: Control.ScrollBar.AsNeeded
ScrollBar {
id: contactsScrollbar
Layout.fillHeight: true
Layout.rightMargin: 8 * DefaultStyle.dp
height: listLayout.height
active: true
interactive: true
policy: Control.ScrollBar.AsNeeded
}
}
}
}

View file

@ -93,20 +93,12 @@ AbstractMainPage {
]
}
leftPanelContent: Item {
leftPanelContent: Control.StackView {
id: leftPanelStackView
Layout.fillHeight: true
Layout.fillWidth: true
Control.StackView {
id: leftPanelStackView
initialItem: listLayout
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
anchors.bottom: parent.bottom
anchors.leftMargin: 45 * DefaultStyle.dp
}
Layout.leftMargin: 45 * DefaultStyle.dp
initialItem: listLayout
}
Item {
@ -124,8 +116,6 @@ AbstractMainPage {
Component {
id: listLayout
FocusScope{
width: parent.width
height: listLayoutIn.implicitHeight
property string objectName: "listLayout"
Control.StackView.onDeactivated: {
mainItem.selectedConference = null
@ -171,11 +161,10 @@ AbstractMainPage {
}
SearchBar {
id: searchBar
Layout.fillWidth: true
Layout.topMargin: 18 * DefaultStyle.dp
// Layout.fillWidth: true
//Layout.topMargin: 18 * DefaultStyle.dp
Layout.rightMargin: 39 * DefaultStyle.dp
placeholderText: qsTr("Rechercher une réunion")
Layout.preferredWidth: 331 * DefaultStyle.dp
KeyNavigation.up: conferenceList
KeyNavigation.down: conferenceList
}
@ -760,7 +749,7 @@ AbstractMainPage {
Avatar {
Layout.preferredWidth: 45 * DefaultStyle.dp
Layout.preferredHeight: 45 * DefaultStyle.dp
address: mainItem.selectedConference ? mainItem.selectedConference.core.organizerAddress : ""
_address: mainItem.selectedConference ? mainItem.selectedConference.core.organizerAddress : ""
}
Text {
text: mainItem.selectedConference ? mainItem.selectedConference.core.organizerName : ""
@ -795,7 +784,7 @@ AbstractMainPage {
Avatar {
Layout.preferredWidth: 45 * DefaultStyle.dp
Layout.preferredHeight: 45 * DefaultStyle.dp
address: modelData.address
_address: modelData.address
}
Text {
text: modelData.displayName

View file

@ -18,7 +18,7 @@ Window{
anchors.centerIn: parent
height: 100
width: height
address: 'sip:jul@toto.com'
_address: 'sip:jul@toto.com'
}
Loader{
id: cameraLoader

View file

@ -9,6 +9,7 @@ QtObject {
property string lockSimpleOpen: "image://internal/lock-simple-open.svg"
property string lockKey: "image://internal/lock-key.svg"
property string shieldWarning: "image://internal/shield-warning.svg"
property string notTrusted: "image://internal/not-trusted.svg"
property string welcomeOpenSource: "image://internal/open_source.svg"
property string splashscreenLogo: "image://internal/splashscreen-logo.svg"
property string eyeHide: "image://internal/eye.svg"
@ -64,6 +65,7 @@ QtObject {
property string speakerSlash: "image://internal/speaker-slash.svg"
property string trusted: "image://internal/trusted.svg"
property string trustedWhite: "image://internal/trusted-white.svg"
property string trustedMask: "image://internal/trusted-mask.svg"
property string avatar: "image://internal/randomAvatar.png"
property string pause: "image://internal/pause.svg"
property string play: "image://internal/play.svg"