Imdn fix + text to speech debug feedback

This commit is contained in:
Julien Wadel 2021-08-04 09:04:26 +02:00
parent aca3b5038f
commit 1397e483b3
16 changed files with 315 additions and 200 deletions

View file

@ -617,6 +617,7 @@ void App::registerTypes () {
registerType<FileExtractor>("FileExtractor");
registerType<HistoryProxyModel>("HistoryProxyModel");
registerType<LdapProxyModel>("LdapProxyModel");
registerType<ParticipantImdnStateProxyModel>("ParticipantImdnStateProxyModel");
registerType<SipAddressesProxyModel>("SipAddressesProxyModel");
registerType<SearchSipAddressesModel>("SearchSipAddressesModel");
registerType<SearchSipAddressesProxyModel>("SearchSipAddressesProxyModel");
@ -660,7 +661,6 @@ void App::registerTypes () {
registerUncreatableType<ParticipantDeviceProxyModel>("ParticipantDeviceProxyModel");
registerUncreatableType<ParticipantImdnStateModel>("ParticipantImdnStateModel");
registerUncreatableType<ParticipantImdnStateListModel>("ParticipantImdnStateListModel");
registerUncreatableType<ParticipantImdnStateProxyModel>("ParticipantImdnStateProxyModel");

View file

@ -377,6 +377,13 @@ QList<QString> ChatRoomModel::getComposers(){
//------------------------------------------------------------------------------------------------
void ChatRoomModel::setSubject(QString& subject){
if(mChatRoom && getSubject() != subject){
mChatRoom->setSubject(Utils::appStringToCoreString(subject));
emit subjectChanged(subject);
}
}
void ChatRoomModel::setLastUpdateTime(const QDateTime& lastUpdateDate) {
if(mLastUpdateTime != lastUpdateDate ) {
mLastUpdateTime = lastUpdateDate;
@ -822,5 +829,6 @@ void ChatRoomModel::onChatMessageShouldBeStored(const std::shared_ptr<linphone::
}
void ChatRoomModel::onChatMessageParticipantImdnStateChanged(const std::shared_ptr<linphone::ChatRoom> & chatRoom, const std::shared_ptr<linphone::ChatMessage> & message, const std::shared_ptr<const linphone::ParticipantImdnState> & state){
qWarning() << "ChatRoom Imdn received :" << (state->getParticipant() != nullptr);
}

View file

@ -57,7 +57,7 @@ public:
//Q_PROPERTY(QString participants READ getParticipants NOTIFY participantsChanged);
//Q_PROPERTY(ParticipantProxyModel participants READ getParticipants NOTIFY participantsChanged);
Q_PROPERTY(QString subject READ getSubject NOTIFY subjectChanged)
Q_PROPERTY(QString subject READ getSubject WRITE setSubject NOTIFY subjectChanged)
Q_PROPERTY(QDateTime lastUpdateTime MEMBER mLastUpdateTime WRITE setLastUpdateTime NOTIFY lastUpdateTimeChanged)
Q_PROPERTY(int unreadMessagesCount MEMBER mUnreadMessagesCount WRITE setUnreadMessagesCount NOTIFY unreadMessagesCountChanged)
Q_PROPERTY(int missedCallsCount MEMBER mMissedCallsCount WRITE setMissedCallsCount NOTIFY missedCallsCountChanged)
@ -130,7 +130,8 @@ public:
std::shared_ptr<linphone::ChatRoom> getChatRoom();
QList<QString> getComposers();
//---- Setters
//---- Setters
void setSubject(QString& subject);
void setLastUpdateTime(const QDateTime& lastUpdateDate);
void setUnreadMessagesCount(const int& count);

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2010-2020 Belledonne Communications SARL.
* Copyright (c) 2010-2021 Belledonne Communications SARL.
*
* This file is part of linphone-desktop
* (see https://www.linphone.org).
@ -19,33 +19,47 @@
*/
#ifdef TEXTTOSPEECH_ENABLED
#include <QTextToSpeech>
#include <QTextToSpeech>
#include <QVoice>
#endif // ifdef TEXTTOSPEECH_ENABLED
#include "TextToSpeech.hpp"
#include <QDebug>
// =============================================================================
#ifdef TEXTTOSPEECH_ENABLED
TextToSpeech::TextToSpeech (QObject *parent) : QObject(parent) {
mQtTextToSpeech = new QTextToSpeech(this);
}
TextToSpeech::TextToSpeech (QObject *parent) : QObject(parent) {
mQtTextToSpeech = new QTextToSpeech(this);
connect(mQtTextToSpeech, &QTextToSpeech::stateChanged, this, &TextToSpeech::onStateChanged);
}
void TextToSpeech::say (const QString &text) {
mQtTextToSpeech->say(text);
}
void TextToSpeech::say (const QString &text) {
if(mQtTextToSpeech->volume() == 0.0)
mQtTextToSpeech->setVolume(1.0);
QStringList names;
for(auto i : mQtTextToSpeech->availableVoices())
names << i.name();
qInfo() << "Speech request : Volume " << mQtTextToSpeech->volume() << "; voices: " << names.join(",") << "; Engines: " << QTextToSpeech::availableEngines();
mQtTextToSpeech->say(text);
}
bool TextToSpeech::available () const {
return true;
}
bool TextToSpeech::available () const {
return true;
}
void TextToSpeech::onStateChanged(QTextToSpeech::State state){
qInfo() << "Speech Status : " << (int)state;
}
#else
TextToSpeech::TextToSpeech (QObject *parent) : QObject(parent) {}
TextToSpeech::TextToSpeech (QObject *parent) : QObject(parent) {}
void TextToSpeech::say (const QString &) {}
void TextToSpeech::say (const QString &) {}
bool TextToSpeech::available () const {
return false;
}
bool TextToSpeech::available () const {
return false;
}
#endif // ifdef TEXTTOSPEECH_ENABLED

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2010-2020 Belledonne Communications SARL.
* Copyright (c) 2010-2021 Belledonne Communications SARL.
*
* This file is part of linphone-desktop
* (see https://www.linphone.org).
@ -22,25 +22,32 @@
#define TEXT_TO_SPEECH_H_
#include <QObject>
#ifdef TEXTTOSPEECH_ENABLED
#include <QTextToSpeech>
#endif // ifdef TEXTTOSPEECH_ENABLED
// =============================================================================
class QTextToSpeech;
class TextToSpeech : public QObject {
Q_OBJECT;
Q_PROPERTY(bool available READ available CONSTANT);
Q_OBJECT;
Q_PROPERTY(bool available READ available CONSTANT);
public:
TextToSpeech (QObject *parent = Q_NULLPTR);
Q_INVOKABLE void say (const QString &text);
TextToSpeech (QObject *parent = Q_NULLPTR);
Q_INVOKABLE void say (const QString &text);
#ifdef TEXTTOSPEECH_ENABLED
public slots:
void onStateChanged(QTextToSpeech::State state);
#endif
private:
bool available () const;
QTextToSpeech *mQtTextToSpeech = nullptr;
bool available () const;
QTextToSpeech *mQtTextToSpeech = nullptr;
};
#endif // ifndef TEXT_TO_SPEECH_H_

View file

@ -76,7 +76,8 @@ void ParticipantImdnStateListModel::add(std::shared_ptr<ParticipantImdnStateMode
beginInsertRows(QModelIndex(), row, row);
mList << imdn;
endInsertRows();
resetInternalData();
emit countChanged();
//resetInternalData();
}
bool ParticipantImdnStateListModel::removeRow (int row, const QModelIndex &parent){
@ -93,6 +94,7 @@ bool ParticipantImdnStateListModel::removeRows (int row, int count, const QModel
mList.takeAt(row);
endRemoveRows();
emit countChanged();
return true;
}

View file

@ -51,6 +51,7 @@ public slots:
signals:
void imdnStateChanged();
void countChanged();
private:
void add(std::shared_ptr<ParticipantImdnStateModel> imdn);

View file

@ -52,7 +52,17 @@ bool ParticipantImdnStateProxyModel::lessThan (const QModelIndex &left, const QM
|| (imdnA->getState() == imdnB->getState() && imdnA->getStateChangeTime() < imdnB->getStateChangeTime());
}
//---------------------------------------------------------------------------------
int ParticipantImdnStateProxyModel::getCount(){
return rowCount();
}
void ParticipantImdnStateProxyModel::setChatMessageModel(ChatMessageModel * message){
setSourceModel(message->getParticipantImdnStates().get());
ParticipantImdnStateListModel *model = static_cast<ParticipantImdnStateListModel*>(sourceModel());
ParticipantImdnStateListModel *messageModel = message->getParticipantImdnStates().get();
if( model != messageModel){
setSourceModel(messageModel);
connect(messageModel, &ParticipantImdnStateListModel::countChanged, this, &ParticipantImdnStateProxyModel::countChanged);
sort(0);
emit chatMessageModelChanged();
}
}

View file

@ -36,10 +36,16 @@ class ParticipantImdnStateProxyModel : public QSortFilterProxyModel {
Q_OBJECT
public:
Q_PROPERTY(ChatMessageModel * chatMessageModel WRITE setChatMessageModel NOTIFY chatMessageModelChanged)
Q_PROPERTY(int count READ getCount NOTIFY countChanged)
ParticipantImdnStateProxyModel (QObject *parent = nullptr);
void setChatMessageModel(ChatMessageModel* message);
int getCount();
signals:
void chatMessageModelChanged();
void countChanged();
protected:
virtual bool filterAcceptsRow (int sourceRow, const QModelIndex &sourceParent) const override;
virtual bool lessThan (const QModelIndex &left, const QModelIndex &right) const override;
@ -47,5 +53,4 @@ protected:
std::shared_ptr<ParticipantImdnStateListModel> mImdns;
};
#endif

View file

@ -103,4 +103,8 @@ Controls.TextField {
visible:error!= ''
text:error
}
Keys.onPressed:{
if( event.key == Qt.Key_Escape)
focus = false
}
}

View file

@ -57,6 +57,35 @@ QtObject {
}
}
property QtObject text: QtObject {
property color color: Colors.d.color
property int pointSize: Units.dp * 10
property int rightPadding: 10
}
}
property QtObject flat : QtObject {
property QtObject background: QtObject {
property int height: 36
property int width: 200
property int radius: 0
property QtObject border: QtObject {
property QtObject color: QtObject {
property color error: Colors.error.color
property color normal: Colors.c.color
property color selected: Colors.i.color
}
property int width: 0
}
property QtObject color: QtObject {
property color normal: Colors.q.color
property color readOnly: Colors.e.color
}
}
property QtObject text: QtObject {
property color color: Colors.d.color
property int pointSize: Units.dp * 10

View file

@ -3,6 +3,8 @@ import QtQuick.Layouts 1.3
import Clipboard 1.0
import Common 1.0
import Linphone 1.0
import Common.Styles 1.0
import Linphone.Styles 1.0
import TextToSpeech 1.0
@ -130,7 +132,7 @@ Item {
iconSizeMenu: 17
iconLayoutDirection: Qt.RightToLeft
menuItemStyle : MenuItemStyle.aux
visible: deliveryLayout.model.rowCount() > 0
visible: deliveryLayout.model.count > 0
onTriggered: deliveryLayout.visible = !deliveryLayout.visible
}
MenuItem {
@ -140,7 +142,7 @@ Item {
iconSizeMenu: 17
iconLayoutDirection: Qt.RightToLeft
menuItemStyle : MenuItemStyle.auxRed
onTriggered: deliveryLayout.visible = !deliveryLayout.visible
onTriggered: removeEntry()
}
}
@ -179,20 +181,13 @@ Item {
anchors.right:parent.right
anchors.rightMargin: 50
//height: visible ? ChatStyle.composingText.height*container.proxyModel.composers.length : 0
height: visible ? (ChatStyle.composingText.height-5)*deliveryLayout.model.rowCount() : 0
height: visible ? (ChatStyle.composingText.height-5)*deliveryLayout.model.count : 0
cellWidth: parent.width; cellHeight: ChatStyle.composingText.height-5
visible:false
/*
property var composersLength : container.proxyModel.composers.length
onComposersLengthChanged:{
model.clear()
console.log(container.proxyModel.composers)
for(var j = 0 ; j < container.proxyModel.composers.length ; ++j) {
console.log(container.proxyModel.composers[j])
model.append({text:container.proxyModel.composers[j]})
}
}*/
model: $chatEntry.getProxyImdnStates()
model: ParticipantImdnStateProxyModel{
id: imdnStatesModel
chatMessageModel: $chatEntry
}
function getText(state){
if(state == LinphoneEnums.ChatMessageStateDelivered)
//: 'Send to %1 - %2' Little message to indicate the state of a message

View file

@ -7,6 +7,7 @@ import Common 1.0
// =============================================================================
Column {
id:mainItem
property alias username: username.text
property string sipAddress
property alias statusText : status.text
@ -23,6 +24,11 @@ Column {
readonly property int statusWidth : (status.visible ? status.width + 5 : 0)
property bool usernameDoubleClickable: false
signal usernameDoubleClicked()
// ---------------------------------------------------------------------------
@ -51,6 +57,11 @@ Column {
font.pointSize: contactDescriptionStyle.username.status.pointSize
font.italic : true
}
MouseArea{
anchors.fill:parent
visible: usernameDoubleClickable
onDoubleClicked: usernameDoubleClicked()
}
}
Text {

View file

@ -9,80 +9,84 @@ import App.Styles 1.0
// =============================================================================
DialogPlus {
buttons: [
TextButtonA {
text: qsTr('cancel')
onClicked: exit(0)
}
]
buttonsAlignment: Qt.AlignCenter
descriptionText: qsTr('callSipAddressDescription')
height: CallSipAddressStyle.height + 30
width: CallSipAddressStyle.width
// ---------------------------------------------------------------------------
ColumnLayout {
anchors.fill: parent
spacing: 0
// -------------------------------------------------------------------------
// Address selector.
// -------------------------------------------------------------------------
Item {
Layout.fillHeight: true
Layout.fillWidth: true
ColumnLayout {
anchors.fill: parent
spacing: CallSipAddressStyle.spacing
TextField {
id: filter
Layout.fillWidth: true
icon: 'search'
onTextChanged: sipAddressesModel.setFilter(text)
}
ScrollableListViewField {
Layout.fillHeight: true
Layout.fillWidth: true
SipAddressesView {
anchors.fill: parent
actions: [{
icon: 'video_call',
handler: function (entry) {
CallsListModel.launchVideoCall(entry.sipAddress)
exit(1)
},
visible: SettingsModel.videoSupported && SettingsModel.showStartVideoCallButton
}, {
icon: 'call',
handler: function (entry) {
CallsListModel.launchAudioCall(entry.sipAddress)
exit(1)
}
}]
genSipAddress: filter.text
model: SearchSipAddressesModel {
id: sipAddressesModel
}
onEntryClicked: actions[0].handler(entry)
}
}
}
}
}
buttons: [
TextButtonA {
text: qsTr('cancel')
onClicked: exit(0)
}
]
buttonsAlignment: Qt.AlignCenter
descriptionText: qsTr('callSipAddressDescription')
height: CallSipAddressStyle.height + 30
width: CallSipAddressStyle.width
// ---------------------------------------------------------------------------
ColumnLayout {
anchors.fill: parent
spacing: 0
// -------------------------------------------------------------------------
// Address selector.
// -------------------------------------------------------------------------
Item {
Layout.fillHeight: true
Layout.fillWidth: true
ColumnLayout {
anchors.fill: parent
spacing: CallSipAddressStyle.spacing
TextField {
id: filter
Layout.fillWidth: true
icon: 'search'
onTextChanged: sipAddressesModel.setFilter(text)
}
ScrollableListViewField {
Layout.fillHeight: true
Layout.fillWidth: true
SipAddressesView {
anchors.fill: parent
actions: [{
icon: 'video_call',
secure:0,
visible:true,
handler: function (entry) {
CallsListModel.launchVideoCall(entry.sipAddress)
exit(1)
},
visible: SettingsModel.videoSupported && SettingsModel.showStartVideoCallButton
}, {
icon: 'call',
secure:0,
visible:true,
handler: function (entry) {
CallsListModel.launchAudioCall(entry.sipAddress)
exit(1)
}
}]
genSipAddress: filter.text
model: SearchSipAddressesModel {
id: sipAddressesModel
}
onEntryClicked: actions[0].handler(entry)
}
}
}
}
}
}

View file

@ -83,93 +83,116 @@ ColumnLayout {
iconSize: ConversationStyle.bar.groupChatSize
visible: chatRoomModel.groupEnabled
}
RowLayout{
Item{
Layout.fillHeight: true
Layout.fillWidth: true
spacing:0
ColumnLayout{
Layout.fillHeight: true
Layout.minimumWidth: 20
Layout.maximumWidth: contactBar.width-avatar.width-actionBar.width-3*ConversationStyle.bar.spacing
Layout.preferredWidth: contactDescription.contentWidth
spacing: 5
Row{
Layout.topMargin: 15
Layout.preferredHeight: implicitHeight
Layout.alignment: Qt.AlignBottom
visible:chatRoomModel.isMeAdmin
Icon{
id:adminIcon
icon : 'admin_selected'
iconSize:14
}
Text{
anchors.verticalCenter: parent.verticalCenter
//: 'Admin' : Admin(istrator)
//~ Context One word title for describing the current admin status
text: qsTr('adminStatus')
color:"#9FA6AB"
font.pointSize: Units.dp * 8
}
}
RowLayout{
anchors.fill: parent
spacing:0
ContactDescription {
id:contactDescription
ColumnLayout{
Layout.fillHeight: true
Layout.minimumWidth: 20
Layout.maximumWidth: contactBar.width-avatar.width-actionBar.width-3*ConversationStyle.bar.spacing
Layout.preferredWidth: contentWidth
Layout.preferredHeight: contentHeight
Layout.alignment: Qt.AlignTop | Qt.AlignLeft
contactDescriptionStyle: ConversationStyle.bar.contactDescription
username: avatar.username
sipAddress: {
if(chatRoomModel) {
if(chatRoomModel.groupEnabled) {
return chatRoomModel.participants.displayNamesToString();
}else if(chatRoomModel.isSecure()) {
return chatRoomModel.participants.addressesToString();
}else {
return chatRoomModel.sipAddress;
}
}else {
return conversation.sipAddress || conversation.fullPeerAddress || conversation.peerAddress || '';
}
Layout.preferredWidth: contactDescription.contentWidth
spacing: 5
Row{
Layout.topMargin: 15
Layout.preferredHeight: implicitHeight
Layout.alignment: Qt.AlignBottom
visible:chatRoomModel.isMeAdmin
Icon{
id:adminIcon
icon : 'admin_selected'
iconSize:14
}
Text{
anchors.verticalCenter: parent.verticalCenter
//: 'Admin' : Admin(istrator)
//~ Context One word title for describing the current admin status
text: qsTr('adminStatus')
color:"#9FA6AB"
font.pointSize: Units.dp * 8
}
}
ContactDescription {
id:contactDescription
Layout.minimumWidth: 20
Layout.maximumWidth: contactBar.width-avatar.width-actionBar.width-3*ConversationStyle.bar.spacing
Layout.preferredWidth: contentWidth
Layout.preferredHeight: contentHeight
Layout.alignment: Qt.AlignTop | Qt.AlignLeft
contactDescriptionStyle: ConversationStyle.bar.contactDescription
username: avatar.username
usernameDoubleClickable: chatRoomModel.isMeAdmin
sipAddress: {
if(chatRoomModel) {
if(chatRoomModel.groupEnabled) {
return chatRoomModel.participants.displayNamesToString();
}else if(chatRoomModel.isSecure()) {
return chatRoomModel.participants.addressesToString();
}else {
return chatRoomModel.sipAddress;
}
}else {
return conversation.sipAddress || conversation.fullPeerAddress || conversation.peerAddress || '';
}
}
onUsernameDoubleClicked: {
usernameEdit.visible = !usernameEdit.visible
usernameEdit.forceActiveFocus()
}
}
Item{
Layout.fillHeight: true
Layout.fillWidth: true
visible: chatRoomModel.isMeAdmin
}
}
Item{
Layout.fillHeight: true
Icon{
Layout.alignment: Qt.AlignVCenter
visible: securityLevel != 1
icon: securityLevel === 2?'secure_level_1': securityLevel===3? 'secure_level_2' : 'secure_level_unsafe'
iconSize:30
MouseArea{
anchors.fill:parent
onClicked : {
window.detachVirtualWindow()
window.attachVirtualWindow(Qt.resolvedUrl('Dialogs/InfoEncryption.qml')
,{securityLevel:securityLevel}
, function (status) {
if(status){
window.detachVirtualWindow()
window.attachVirtualWindow(Qt.resolvedUrl('Dialogs/ParticipantsDevices.qml')
,{chatRoomModel:chatRoomModel
, window:window})
}
})
}
}
}
Item{//Spacer
Layout.fillWidth: true
visible: chatRoomModel.isMeAdmin
}
}
Icon{
Layout.alignment: Qt.AlignVCenter
visible: securityLevel != 1
icon: securityLevel === 2?'secure_level_1': securityLevel===3? 'secure_level_2' : 'secure_level_unsafe'
iconSize:30
MouseArea{
anchors.fill:parent
onClicked : {
window.detachVirtualWindow()
window.attachVirtualWindow(Qt.resolvedUrl('Dialogs/InfoEncryption.qml')
,{securityLevel:securityLevel}
, function (status) {
if(status){
window.detachVirtualWindow()
window.attachVirtualWindow(Qt.resolvedUrl('Dialogs/ParticipantsDevices.qml')
,{chatRoomModel:chatRoomModel
, window:window})
}
})
TextField{
id: usernameEdit
anchors.fill: parent
//anchors.margins: -1
//textFieldStyle : TextFieldStyle.flat
text: avatar.username
visible: false
onEditingFinished: {
chatRoomModel.subject = text
visible = false
}
font.bold: true
onFocusChanged: if(!focus) visible=false
}
}
}
Item{//Spacer
Layout.fillWidth: true
}
}
Row {

View file

@ -200,7 +200,8 @@ ApplicationWindow {
ActionButton {
icon: 'new_chat_group'
tooltipText : 'Open Conference'
//: 'Open Conference' : Tooltip to illustrate a button
tooltipText : qsTr('newChatRoom')
iconSize: MainWindowStyle.newConferenceSize
//autoIcon: true
onClicked: {