record message
auto download attached files setting
This commit is contained in:
parent
6d4506c5ae
commit
f82931d6c6
49 changed files with 3097 additions and 1984 deletions
|
|
@ -78,6 +78,7 @@
|
||||||
#include "core/payload-type/PayloadTypeProxy.hpp"
|
#include "core/payload-type/PayloadTypeProxy.hpp"
|
||||||
#include "core/phone-number/PhoneNumber.hpp"
|
#include "core/phone-number/PhoneNumber.hpp"
|
||||||
#include "core/phone-number/PhoneNumberProxy.hpp"
|
#include "core/phone-number/PhoneNumberProxy.hpp"
|
||||||
|
#include "core/recorder/RecorderGui.hpp"
|
||||||
#include "core/register/RegisterPage.hpp"
|
#include "core/register/RegisterPage.hpp"
|
||||||
#include "core/screen/ScreenList.hpp"
|
#include "core/screen/ScreenList.hpp"
|
||||||
#include "core/screen/ScreenProxy.hpp"
|
#include "core/screen/ScreenProxy.hpp"
|
||||||
|
|
@ -686,6 +687,7 @@ void App::initCppInterfaces() {
|
||||||
qmlRegisterType<FPSCounter>(Constants::MainQmlUri, 1, 0, "FPSCounter");
|
qmlRegisterType<FPSCounter>(Constants::MainQmlUri, 1, 0, "FPSCounter");
|
||||||
qmlRegisterType<EmojiModel>(Constants::MainQmlUri, 1, 0, "EmojiModel");
|
qmlRegisterType<EmojiModel>(Constants::MainQmlUri, 1, 0, "EmojiModel");
|
||||||
qmlRegisterType<SoundPlayerGui>(Constants::MainQmlUri, 1, 0, "SoundPlayerGui");
|
qmlRegisterType<SoundPlayerGui>(Constants::MainQmlUri, 1, 0, "SoundPlayerGui");
|
||||||
|
qmlRegisterType<RecorderGui>(Constants::MainQmlUri, 1, 0, "RecorderGui");
|
||||||
|
|
||||||
qmlRegisterType<TimeZoneProxy>(Constants::MainQmlUri, 1, 0, "TimeZoneProxy");
|
qmlRegisterType<TimeZoneProxy>(Constants::MainQmlUri, 1, 0, "TimeZoneProxy");
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -86,6 +86,9 @@ list(APPEND _LINPHONEAPP_SOURCES
|
||||||
core/sound-player/SoundPlayerCore.cpp
|
core/sound-player/SoundPlayerCore.cpp
|
||||||
core/sound-player/SoundPlayerGui.cpp
|
core/sound-player/SoundPlayerGui.cpp
|
||||||
|
|
||||||
|
core/recorder/RecorderCore.cpp
|
||||||
|
core/recorder/RecorderGui.cpp
|
||||||
|
|
||||||
core/videoSource/VideoSourceDescriptorCore.cpp
|
core/videoSource/VideoSourceDescriptorCore.cpp
|
||||||
core/videoSource/VideoSourceDescriptorGui.cpp
|
core/videoSource/VideoSourceDescriptorGui.cpp
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -117,6 +117,15 @@ void ChatCore::setSelf(QSharedPointer<ChatCore> me) {
|
||||||
mChatModelConnection->makeConnectToCore(&ChatCore::lDeleteHistory, [this]() {
|
mChatModelConnection->makeConnectToCore(&ChatCore::lDeleteHistory, [this]() {
|
||||||
mChatModelConnection->invokeToModel([this]() { mChatModel->deleteHistory(); });
|
mChatModelConnection->invokeToModel([this]() { mChatModel->deleteHistory(); });
|
||||||
});
|
});
|
||||||
|
mChatModelConnection->makeConnectToCore(&ChatCore::lDeleteMessage, [this](ChatMessageGui *message) {
|
||||||
|
mChatModelConnection->invokeToModel([this, core = message ? message->mCore : nullptr]() {
|
||||||
|
auto messageModel = core ? core->getModel() : nullptr;
|
||||||
|
if (messageModel) {
|
||||||
|
mChatModel->deleteMessage(messageModel->getMonitor());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
mChatModelConnection->makeConnectToCore(
|
mChatModelConnection->makeConnectToCore(
|
||||||
&ChatCore::lLeave, [this]() { mChatModelConnection->invokeToModel([this]() { mChatModel->leave(); }); });
|
&ChatCore::lLeave, [this]() { mChatModelConnection->invokeToModel([this]() { mChatModel->leave(); }); });
|
||||||
mChatModelConnection->makeConnectToModel(&ChatModel::historyDeleted, [this]() {
|
mChatModelConnection->makeConnectToModel(&ChatModel::historyDeleted, [this]() {
|
||||||
|
|
@ -458,9 +467,14 @@ void ChatCore::appendEventLogsToEventLogList(QList<QSharedPointer<EventLogCore>>
|
||||||
|
|
||||||
void ChatCore::appendEventLogToEventLogList(QSharedPointer<EventLogCore> e) {
|
void ChatCore::appendEventLogToEventLogList(QSharedPointer<EventLogCore> e) {
|
||||||
if (mEventLogList.contains(e)) return;
|
if (mEventLogList.contains(e)) return;
|
||||||
|
auto it = std::find_if(mEventLogList.begin(), mEventLogList.end(), [e](QSharedPointer<EventLogCore> event) {
|
||||||
|
return e->getEventLogId() == event->getEventLogId();
|
||||||
|
});
|
||||||
|
if (it == mEventLogList.end()) {
|
||||||
mEventLogList.append(e);
|
mEventLogList.append(e);
|
||||||
emit eventsInserted({e});
|
emit eventsInserted({e});
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void ChatCore::removeEventLogsFromEventLogList(QList<QSharedPointer<EventLogCore>> list) {
|
void ChatCore::removeEventLogsFromEventLogList(QList<QSharedPointer<EventLogCore>> list) {
|
||||||
int nbRemoved = 0;
|
int nbRemoved = 0;
|
||||||
|
|
|
||||||
|
|
@ -150,7 +150,7 @@ signals:
|
||||||
void meAdminChanged();
|
void meAdminChanged();
|
||||||
void participantsChanged();
|
void participantsChanged();
|
||||||
|
|
||||||
void lDeleteMessage();
|
void lDeleteMessage(ChatMessageGui *message);
|
||||||
void lDelete();
|
void lDelete();
|
||||||
void lDeleteHistory();
|
void lDeleteHistory();
|
||||||
void lMarkAsRead();
|
void lMarkAsRead();
|
||||||
|
|
@ -159,6 +159,7 @@ signals:
|
||||||
void lUpdateLastUpdatedTime();
|
void lUpdateLastUpdatedTime();
|
||||||
void lSendTextMessage(QString message);
|
void lSendTextMessage(QString message);
|
||||||
void lSendMessage(QString message, QVariantList files);
|
void lSendMessage(QString message, QVariantList files);
|
||||||
|
void lSendVoiceMessage();
|
||||||
void lCompose();
|
void lCompose();
|
||||||
void lLeave();
|
void lLeave();
|
||||||
void lSetMuted(bool muted);
|
void lSetMuted(bool muted);
|
||||||
|
|
|
||||||
|
|
@ -90,6 +90,12 @@ ChatMessageCore::ChatMessageCore(const std::shared_ptr<linphone::ChatMessage> &c
|
||||||
for (auto content : chatmessage->getContents()) {
|
for (auto content : chatmessage->getContents()) {
|
||||||
auto contentCore = ChatMessageContentCore::create(content, mChatMessageModel);
|
auto contentCore = ChatMessageContentCore::create(content, mChatMessageModel);
|
||||||
mChatMessageContentList.push_back(contentCore);
|
mChatMessageContentList.push_back(contentCore);
|
||||||
|
if (content->isFile() && !content->isVoiceRecording()) mHasFileContent = true;
|
||||||
|
if (content->isIcalendar()) mIsCalendarInvite = true;
|
||||||
|
if (content->isVoiceRecording()) {
|
||||||
|
mIsVoiceRecording = true;
|
||||||
|
mVoiceRecordingContent = contentCore;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
auto reac = chatmessage->getOwnReaction();
|
auto reac = chatmessage->getOwnReaction();
|
||||||
mOwnReaction = reac ? Utils::coreStringToAppString(reac->getBody()) : QString();
|
mOwnReaction = reac ? Utils::coreStringToAppString(reac->getBody()) : QString();
|
||||||
|
|
@ -122,11 +128,6 @@ ChatMessageCore::ChatMessageCore(const std::shared_ptr<linphone::ChatMessage> &c
|
||||||
|
|
||||||
mIsForward = chatmessage->isForward();
|
mIsForward = chatmessage->isForward();
|
||||||
mIsReply = chatmessage->isReply();
|
mIsReply = chatmessage->isReply();
|
||||||
for (auto &content : chatmessage->getContents()) {
|
|
||||||
if (content->isFile() && !content->isVoiceRecording()) mHasFileContent = true;
|
|
||||||
if (content->isIcalendar()) mIsCalendarInvite = true;
|
|
||||||
if (content->isVoiceRecording()) mIsVoiceRecording = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ChatMessageCore::~ChatMessageCore() {
|
ChatMessageCore::~ChatMessageCore() {
|
||||||
|
|
@ -156,6 +157,9 @@ void ChatMessageCore::setSelf(QSharedPointer<ChatMessageCore> me) {
|
||||||
mChatMessageModelConnection->makeConnectToCore(&ChatMessageCore::lRemoveReaction, [this]() {
|
mChatMessageModelConnection->makeConnectToCore(&ChatMessageCore::lRemoveReaction, [this]() {
|
||||||
mChatMessageModelConnection->invokeToModel([this] { mChatMessageModel->removeReaction(); });
|
mChatMessageModelConnection->invokeToModel([this] { mChatMessageModel->removeReaction(); });
|
||||||
});
|
});
|
||||||
|
mChatMessageModelConnection->makeConnectToCore(&ChatMessageCore::lSend, [this]() {
|
||||||
|
mChatMessageModelConnection->invokeToModel([this] { mChatMessageModel->send(); });
|
||||||
|
});
|
||||||
mChatMessageModelConnection->makeConnectToModel(
|
mChatMessageModelConnection->makeConnectToModel(
|
||||||
&ChatMessageModel::newMessageReaction,
|
&ChatMessageModel::newMessageReaction,
|
||||||
[this](const std::shared_ptr<linphone::ChatMessage> &message,
|
[this](const std::shared_ptr<linphone::ChatMessage> &message,
|
||||||
|
|
@ -431,3 +435,7 @@ std::shared_ptr<ChatMessageModel> ChatMessageCore::getModel() const {
|
||||||
// ConferenceInfoGui *ChatMessageCore::getConferenceInfoGui() const {
|
// ConferenceInfoGui *ChatMessageCore::getConferenceInfoGui() const {
|
||||||
// return mConferenceInfo ? new ConferenceInfoGui(mConferenceInfo) : nullptr;
|
// return mConferenceInfo ? new ConferenceInfoGui(mConferenceInfo) : nullptr;
|
||||||
// }
|
// }
|
||||||
|
|
||||||
|
ChatMessageContentGui *ChatMessageCore::getVoiceRecordingContent() const {
|
||||||
|
return new ChatMessageContentGui(mVoiceRecordingContent);
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -18,11 +18,11 @@
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef CHATMESSAGECORE_H_
|
#ifndef CHAT_MESSAGE_CORE_H_
|
||||||
#define CHATMESSAGECORE_H_
|
#define CHAT_MESSAGE_CORE_H_
|
||||||
|
|
||||||
#include "EventLogCore.hpp"
|
#include "EventLogCore.hpp"
|
||||||
#include "core/chat/message/content/ChatMessageContentCore.hpp"
|
#include "core/chat/message/content/ChatMessageContentGui.hpp"
|
||||||
#include "core/chat/message/content/ChatMessageContentProxy.hpp"
|
#include "core/chat/message/content/ChatMessageContentProxy.hpp"
|
||||||
#include "core/conference/ConferenceInfoCore.hpp"
|
#include "core/conference/ConferenceInfoCore.hpp"
|
||||||
#include "core/conference/ConferenceInfoGui.hpp"
|
#include "core/conference/ConferenceInfoGui.hpp"
|
||||||
|
|
@ -118,7 +118,7 @@ public:
|
||||||
void setMessageState(LinphoneEnums::ChatMessageState state);
|
void setMessageState(LinphoneEnums::ChatMessageState state);
|
||||||
|
|
||||||
std::shared_ptr<ChatMessageModel> getModel() const;
|
std::shared_ptr<ChatMessageModel> getModel() const;
|
||||||
// ConferenceInfoGui *getConferenceInfoGui() const;
|
Q_INVOKABLE ChatMessageContentGui *getVoiceRecordingContent() const;
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void timestampChanged(QDateTime timestamp);
|
void timestampChanged(QDateTime timestamp);
|
||||||
|
|
@ -136,6 +136,7 @@ signals:
|
||||||
void readChanged();
|
void readChanged();
|
||||||
void lSendReaction(const QString &reaction);
|
void lSendReaction(const QString &reaction);
|
||||||
void lRemoveReaction();
|
void lRemoveReaction();
|
||||||
|
void lSend();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
DECLARE_ABSTRACT_OBJECT
|
DECLARE_ABSTRACT_OBJECT
|
||||||
|
|
@ -164,10 +165,12 @@ private:
|
||||||
bool mIsOutgoing = false;
|
bool mIsOutgoing = false;
|
||||||
LinphoneEnums::ChatMessageState mMessageState;
|
LinphoneEnums::ChatMessageState mMessageState;
|
||||||
QList<QSharedPointer<ChatMessageContentCore>> mChatMessageContentList;
|
QList<QSharedPointer<ChatMessageContentCore>> mChatMessageContentList;
|
||||||
|
// for voice recording creation message
|
||||||
|
QSharedPointer<ChatMessageContentCore> mVoiceRecordingContent;
|
||||||
// QSharedPointer<ConferenceInfoCore> mConferenceInfo = nullptr;
|
// QSharedPointer<ConferenceInfoCore> mConferenceInfo = nullptr;
|
||||||
|
|
||||||
std::shared_ptr<ChatMessageModel> mChatMessageModel;
|
std::shared_ptr<ChatMessageModel> mChatMessageModel;
|
||||||
QSharedPointer<SafeConnection<ChatMessageCore, ChatMessageModel>> mChatMessageModelConnection;
|
QSharedPointer<SafeConnection<ChatMessageCore, ChatMessageModel>> mChatMessageModelConnection;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // CHATMESSAGECORE_H_
|
#endif // CHAT_MESSAGE_CORE_H_
|
||||||
|
|
|
||||||
|
|
@ -88,7 +88,7 @@ bool ChatMessageContentProxy::SortFilterList::filterAcceptsRow(int sourceRow, co
|
||||||
if (contentCore) {
|
if (contentCore) {
|
||||||
if (mFilterType == (int)FilterContentType::Unknown) return false;
|
if (mFilterType == (int)FilterContentType::Unknown) return false;
|
||||||
else if (mFilterType == (int)FilterContentType::File) {
|
else if (mFilterType == (int)FilterContentType::File) {
|
||||||
return contentCore->isFile() || contentCore->isFileTransfer();
|
return !contentCore->isVoiceRecording() && (contentCore->isFile() || contentCore->isFileTransfer());
|
||||||
} else if (mFilterType == (int)FilterContentType::Text) return contentCore->isText();
|
} else if (mFilterType == (int)FilterContentType::Text) return contentCore->isText();
|
||||||
else if (mFilterType == (int)FilterContentType::Voice) return contentCore->isVoiceRecording();
|
else if (mFilterType == (int)FilterContentType::Voice) return contentCore->isVoiceRecording();
|
||||||
else if (mFilterType == (int)FilterContentType::Conference) return contentCore->isCalendar();
|
else if (mFilterType == (int)FilterContentType::Conference) return contentCore->isCalendar();
|
||||||
|
|
|
||||||
|
|
@ -324,7 +324,6 @@ void Notifier::notifyReceivedMessages(const std::shared_ptr<linphone::ChatRoom>
|
||||||
|
|
||||||
if (messages.size() > 0) {
|
if (messages.size() > 0) {
|
||||||
shared_ptr<linphone::ChatMessage> message = messages.front();
|
shared_ptr<linphone::ChatMessage> message = messages.front();
|
||||||
|
|
||||||
auto receiverAccount = ToolModel::findAccount(message->getToAddress());
|
auto receiverAccount = ToolModel::findAccount(message->getToAddress());
|
||||||
if (receiverAccount) {
|
if (receiverAccount) {
|
||||||
auto senderAccount = ToolModel::findAccount(message->getFromAddress());
|
auto senderAccount = ToolModel::findAccount(message->getFromAddress());
|
||||||
|
|
@ -340,7 +339,8 @@ void Notifier::notifyReceivedMessages(const std::shared_ptr<linphone::ChatRoom>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (messages.size() == 1) { // Display only sender on mono message.
|
auto getMessage = [this, &remoteAddress, &txt](const shared_ptr<linphone::ChatMessage> &message) {
|
||||||
|
if (message->isRead()) return;
|
||||||
auto remoteAddr = message->getFromAddress()->clone();
|
auto remoteAddr = message->getFromAddress()->clone();
|
||||||
remoteAddr->clean();
|
remoteAddr->clean();
|
||||||
remoteAddress = Utils::coreStringToAppString(remoteAddr->asStringUriOnly());
|
remoteAddress = Utils::coreStringToAppString(remoteAddr->asStringUriOnly());
|
||||||
|
|
@ -356,7 +356,20 @@ void Notifier::notifyReceivedMessages(const std::shared_ptr<linphone::ChatRoom>
|
||||||
if (txt.isEmpty() && message->hasConferenceInvitationContent())
|
if (txt.isEmpty() && message->hasConferenceInvitationContent())
|
||||||
//: 'Conference invitation received!' : Notification about receiving an invitation to a conference.
|
//: 'Conference invitation received!' : Notification about receiving an invitation to a conference.
|
||||||
txt = tr("new_conference_invitation");
|
txt = tr("new_conference_invitation");
|
||||||
|
};
|
||||||
|
|
||||||
|
if (messages.size() == 1) { // Display only sender on mono message.
|
||||||
|
getMessage(message);
|
||||||
} else {
|
} else {
|
||||||
|
int unreadCount = 0;
|
||||||
|
for (auto &message : messages) {
|
||||||
|
if (!message->isRead()) {
|
||||||
|
++unreadCount;
|
||||||
|
if (unreadCount == 1) getMessage(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (unreadCount == 0) return;
|
||||||
|
if (unreadCount > 1)
|
||||||
//: 'New messages received!' Notification that warn the user of new messages.
|
//: 'New messages received!' Notification that warn the user of new messages.
|
||||||
txt = tr("new_chat_room_messages");
|
txt = tr("new_chat_room_messages");
|
||||||
}
|
}
|
||||||
|
|
|
||||||
150
Linphone/core/recorder/RecorderCore.cpp
Normal file
150
Linphone/core/recorder/RecorderCore.cpp
Normal file
|
|
@ -0,0 +1,150 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2021 Belledonne Communications SARL.
|
||||||
|
*
|
||||||
|
* This file is part of linphone-desktop
|
||||||
|
* (see https://www.linphone.org).
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <QFile>
|
||||||
|
|
||||||
|
#include "core/App.hpp"
|
||||||
|
#include "core/path/Paths.hpp"
|
||||||
|
#include "model/core/CoreModel.hpp"
|
||||||
|
#include "model/setting/SettingsModel.hpp"
|
||||||
|
#include "tool/Utils.hpp"
|
||||||
|
|
||||||
|
#include "RecorderCore.hpp"
|
||||||
|
|
||||||
|
DEFINE_ABSTRACT_OBJECT(RecorderCore)
|
||||||
|
|
||||||
|
// =============================================================================
|
||||||
|
|
||||||
|
QSharedPointer<RecorderCore> RecorderCore::create(QObject *parent) {
|
||||||
|
auto sharedPointer = QSharedPointer<RecorderCore>(new RecorderCore(), &QObject::deleteLater);
|
||||||
|
sharedPointer->setSelf(sharedPointer);
|
||||||
|
sharedPointer->moveToThread(App::getInstance()->thread());
|
||||||
|
return sharedPointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
RecorderCore::RecorderCore(QObject *parent) : QObject(parent) {
|
||||||
|
App::getInstance()->mEngine->setObjectOwnership(
|
||||||
|
this, QQmlEngine::CppOwnership); // Avoid QML to destroy it when passing by Q_INVOKABLE
|
||||||
|
}
|
||||||
|
|
||||||
|
RecorderCore::~RecorderCore() {
|
||||||
|
}
|
||||||
|
|
||||||
|
void RecorderCore::buildRecorder(QSharedPointer<RecorderCore> me) {
|
||||||
|
mustBeInLinphoneThread(log().arg(Q_FUNC_INFO));
|
||||||
|
auto core = CoreModel::getInstance()->getCore();
|
||||||
|
std::shared_ptr<linphone::RecorderParams> params = core->createRecorderParams();
|
||||||
|
params->setFileFormat(linphone::MediaFileFormat::Mkv);
|
||||||
|
params->setVideoCodec("");
|
||||||
|
auto recorder = core->createRecorder(params);
|
||||||
|
if (recorder) {
|
||||||
|
mDuration = recorder->getDuration();
|
||||||
|
mCaptureVolume = recorder->getCaptureVolume();
|
||||||
|
if (mRecorderModelConnection) mRecorderModelConnection->disconnect();
|
||||||
|
mRecorderModel = Utils::makeQObject_ptr<RecorderModel>(recorder);
|
||||||
|
mRecorderModelConnection = SafeConnection<RecorderCore, RecorderModel>::create(me, mRecorderModel);
|
||||||
|
mRecorderModelConnection->makeConnectToCore(&RecorderCore::lStart, [this] {
|
||||||
|
mRecorderModelConnection->invokeToModel([this] { mRecorderModel->start(); });
|
||||||
|
});
|
||||||
|
mRecorderModelConnection->makeConnectToCore(&RecorderCore::lPause, [this] {
|
||||||
|
mRecorderModelConnection->invokeToModel([this] { mRecorderModel->pause(); });
|
||||||
|
});
|
||||||
|
mRecorderModelConnection->makeConnectToCore(&RecorderCore::lStop, [this] {
|
||||||
|
mRecorderModelConnection->invokeToModel([this] { mRecorderModel->stop(); });
|
||||||
|
});
|
||||||
|
mRecorderModelConnection->makeConnectToCore(&RecorderCore::lRefresh, [this] {
|
||||||
|
mRecorderModelConnection->invokeToModel([this] {
|
||||||
|
auto duration = mRecorderModel->getDuration();
|
||||||
|
auto volume = mRecorderModel->getCaptureVolume();
|
||||||
|
mRecorderModelConnection->invokeToModel([this, duration, volume] {
|
||||||
|
setDuration(duration);
|
||||||
|
setCaptureVolume(volume);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
mRecorderModelConnection->makeConnectToModel(&RecorderModel::stateChanged, [this] {
|
||||||
|
auto state = LinphoneEnums::fromLinphone(mRecorderModel->getState());
|
||||||
|
mRecorderModelConnection->invokeToCore([this, state] { setState(state); });
|
||||||
|
});
|
||||||
|
mRecorderModelConnection->makeConnectToModel(&RecorderModel::fileChanged, [this] {
|
||||||
|
auto file = mRecorderModel->getFile();
|
||||||
|
mRecorderModelConnection->invokeToCore([this, file] { setFile(file); });
|
||||||
|
});
|
||||||
|
mRecorderModelConnection->makeConnectToModel(&RecorderModel::errorChanged, [this](QString error) {
|
||||||
|
mRecorderModelConnection->invokeToCore([this, error] { emit errorChanged(error); });
|
||||||
|
});
|
||||||
|
emit ready();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void RecorderCore::setSelf(QSharedPointer<RecorderCore> me) {
|
||||||
|
auto coreModel = CoreModel::getInstance();
|
||||||
|
|
||||||
|
mCoreModelConnection = SafeConnection<RecorderCore, CoreModel>::create(me, coreModel);
|
||||||
|
mCoreModelConnection->invokeToModel([this, me, coreModel] { buildRecorder(me); });
|
||||||
|
}
|
||||||
|
|
||||||
|
void RecorderCore::setCaptureVolume(float volume) {
|
||||||
|
if (mCaptureVolume != volume) {
|
||||||
|
mCaptureVolume = volume;
|
||||||
|
emit captureVolumeChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int RecorderCore::getDuration() const {
|
||||||
|
return mDuration;
|
||||||
|
}
|
||||||
|
|
||||||
|
void RecorderCore::setDuration(int duration) {
|
||||||
|
if (mDuration != duration) {
|
||||||
|
mDuration = duration;
|
||||||
|
emit durationChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
float RecorderCore::getCaptureVolume() const {
|
||||||
|
return mCaptureVolume;
|
||||||
|
}
|
||||||
|
|
||||||
|
LinphoneEnums::RecorderState RecorderCore::getState() const {
|
||||||
|
return mState;
|
||||||
|
}
|
||||||
|
|
||||||
|
void RecorderCore::setState(LinphoneEnums::RecorderState state) {
|
||||||
|
if (mState != state) {
|
||||||
|
mState = state;
|
||||||
|
emit stateChanged(state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QString RecorderCore::getFile() const {
|
||||||
|
return mFile;
|
||||||
|
}
|
||||||
|
|
||||||
|
void RecorderCore::setFile(QString file) {
|
||||||
|
if (mFile != file) {
|
||||||
|
mFile = file;
|
||||||
|
emit fileChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::shared_ptr<RecorderModel> &RecorderCore::getModel() const {
|
||||||
|
return mRecorderModel;
|
||||||
|
}
|
||||||
82
Linphone/core/recorder/RecorderCore.hpp
Normal file
82
Linphone/core/recorder/RecorderCore.hpp
Normal file
|
|
@ -0,0 +1,82 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2021 Belledonne Communications SARL.
|
||||||
|
*
|
||||||
|
* This file is part of linphone-desktop
|
||||||
|
* (see https://www.linphone.org).
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef RECORDER_CORE_H
|
||||||
|
#define RECORDER_CORE_H
|
||||||
|
|
||||||
|
#include "model/recorder/RecorderModel.hpp"
|
||||||
|
#include "tool/LinphoneEnums.hpp"
|
||||||
|
#include "tool/thread/SafeConnection.hpp"
|
||||||
|
#include <linphone++/linphone.hh>
|
||||||
|
|
||||||
|
// =============================================================================
|
||||||
|
|
||||||
|
class RecorderCore : public QObject, public AbstractObject {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
static QSharedPointer<RecorderCore> create(QObject *parent = nullptr);
|
||||||
|
RecorderCore(QObject *parent = nullptr);
|
||||||
|
~RecorderCore();
|
||||||
|
void setSelf(QSharedPointer<RecorderCore> me);
|
||||||
|
|
||||||
|
Q_PROPERTY(LinphoneEnums::RecorderState state READ getState NOTIFY stateChanged)
|
||||||
|
Q_PROPERTY(QString file READ getFile NOTIFY fileChanged)
|
||||||
|
Q_PROPERTY(int duration READ getDuration NOTIFY durationChanged)
|
||||||
|
Q_PROPERTY(int captureVolume READ getCaptureVolume NOTIFY captureVolumeChanged)
|
||||||
|
|
||||||
|
void buildRecorder(QSharedPointer<RecorderCore> me);
|
||||||
|
|
||||||
|
int getDuration() const;
|
||||||
|
void setDuration(int duration);
|
||||||
|
float getCaptureVolume() const;
|
||||||
|
void setCaptureVolume(float volume);
|
||||||
|
LinphoneEnums::RecorderState getState() const;
|
||||||
|
void setState(LinphoneEnums::RecorderState state);
|
||||||
|
QString getFile() const;
|
||||||
|
void setFile(QString file);
|
||||||
|
const std::shared_ptr<RecorderModel> &getModel() const;
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void lStart();
|
||||||
|
void lPause();
|
||||||
|
void lStop();
|
||||||
|
void lRefresh();
|
||||||
|
|
||||||
|
void stateChanged(LinphoneEnums::RecorderState state);
|
||||||
|
void fileChanged();
|
||||||
|
void durationChanged();
|
||||||
|
void captureVolumeChanged();
|
||||||
|
void errorChanged(QString error);
|
||||||
|
void ready();
|
||||||
|
|
||||||
|
private:
|
||||||
|
DECLARE_ABSTRACT_OBJECT
|
||||||
|
|
||||||
|
std::shared_ptr<RecorderModel> mRecorderModel;
|
||||||
|
QString mFile;
|
||||||
|
LinphoneEnums::RecorderState mState;
|
||||||
|
int mDuration = 0;
|
||||||
|
int mCaptureVolume = 0;
|
||||||
|
bool mIsReady = false;
|
||||||
|
QSharedPointer<SafeConnection<RecorderCore, RecorderModel>> mRecorderModelConnection;
|
||||||
|
QSharedPointer<SafeConnection<RecorderCore, CoreModel>> mCoreModelConnection;
|
||||||
|
};
|
||||||
|
#endif
|
||||||
49
Linphone/core/recorder/RecorderGui.cpp
Normal file
49
Linphone/core/recorder/RecorderGui.cpp
Normal file
|
|
@ -0,0 +1,49 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2010-2024 Belledonne Communications SARL.
|
||||||
|
*
|
||||||
|
* This file is part of linphone-desktop
|
||||||
|
* (see https://www.linphone.org).
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "RecorderGui.hpp"
|
||||||
|
#include "core/App.hpp"
|
||||||
|
|
||||||
|
DEFINE_ABSTRACT_OBJECT(RecorderGui)
|
||||||
|
|
||||||
|
RecorderGui::RecorderGui(QObject *parent) : QObject(parent) {
|
||||||
|
mustBeInMainThread(getClassName());
|
||||||
|
mCore = RecorderCore::create();
|
||||||
|
if (mCore) connect(mCore.get(), &RecorderCore::errorChanged, this, &RecorderGui::errorChanged);
|
||||||
|
if (mCore) connect(mCore.get(), &RecorderCore::stateChanged, this, &RecorderGui::stateChanged);
|
||||||
|
if (mCore) connect(mCore.get(), &RecorderCore::ready, this, &RecorderGui::ready);
|
||||||
|
}
|
||||||
|
RecorderGui::RecorderGui(QSharedPointer<RecorderCore> core) {
|
||||||
|
App::getInstance()->mEngine->setObjectOwnership(this, QQmlEngine::JavaScriptOwnership);
|
||||||
|
mCore = core;
|
||||||
|
if (isInLinphoneThread()) moveToThread(App::getInstance()->thread());
|
||||||
|
}
|
||||||
|
|
||||||
|
LinphoneEnums::RecorderState RecorderGui::getState() const {
|
||||||
|
return mCore ? mCore->getState() : LinphoneEnums::RecorderState::Closed;
|
||||||
|
}
|
||||||
|
|
||||||
|
RecorderGui::~RecorderGui() {
|
||||||
|
mustBeInMainThread("~" + getClassName());
|
||||||
|
}
|
||||||
|
|
||||||
|
RecorderCore *RecorderGui::getCore() const {
|
||||||
|
return mCore.get();
|
||||||
|
}
|
||||||
52
Linphone/core/recorder/RecorderGui.hpp
Normal file
52
Linphone/core/recorder/RecorderGui.hpp
Normal file
|
|
@ -0,0 +1,52 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2010-2024 Belledonne Communications SARL.
|
||||||
|
*
|
||||||
|
* This file is part of linphone-desktop
|
||||||
|
* (see https://www.linphone.org).
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef RECORDER_GUI_H_
|
||||||
|
#define RECORDER_GUI_H_
|
||||||
|
|
||||||
|
#include "RecorderCore.hpp"
|
||||||
|
#include "tool/AbstractObject.hpp"
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
#include <QSharedPointer>
|
||||||
|
|
||||||
|
class RecorderGui : public QObject, public AbstractObject {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
Q_PROPERTY(RecorderCore *core READ getCore CONSTANT)
|
||||||
|
|
||||||
|
public:
|
||||||
|
RecorderGui(QObject *parent = nullptr);
|
||||||
|
RecorderGui(QSharedPointer<RecorderCore> core);
|
||||||
|
~RecorderGui();
|
||||||
|
RecorderCore *getCore() const;
|
||||||
|
LinphoneEnums::RecorderState getState() const;
|
||||||
|
QSharedPointer<RecorderCore> mCore;
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void errorChanged(QString error);
|
||||||
|
void ready();
|
||||||
|
void stateChanged(LinphoneEnums::RecorderState state);
|
||||||
|
|
||||||
|
private:
|
||||||
|
DECLARE_ABSTRACT_OBJECT
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
@ -49,6 +49,7 @@ SettingsCore::SettingsCore(QObject *parent) : QObject(parent) {
|
||||||
// Call
|
// Call
|
||||||
mVideoEnabled = settingsModel->getVideoEnabled();
|
mVideoEnabled = settingsModel->getVideoEnabled();
|
||||||
mEchoCancellationEnabled = settingsModel->getEchoCancellationEnabled();
|
mEchoCancellationEnabled = settingsModel->getEchoCancellationEnabled();
|
||||||
|
mAutoDownloadReceivedFiles = settingsModel->getAutoDownloadReceivedFiles();
|
||||||
mAutomaticallyRecordCallsEnabled = settingsModel->getAutomaticallyRecordCallsEnabled();
|
mAutomaticallyRecordCallsEnabled = settingsModel->getAutomaticallyRecordCallsEnabled();
|
||||||
|
|
||||||
// Audio
|
// Audio
|
||||||
|
|
@ -143,6 +144,7 @@ SettingsCore::SettingsCore(const SettingsCore &settingsCore) {
|
||||||
// Call
|
// Call
|
||||||
mVideoEnabled = settingsCore.mVideoEnabled;
|
mVideoEnabled = settingsCore.mVideoEnabled;
|
||||||
mEchoCancellationEnabled = settingsCore.mEchoCancellationEnabled;
|
mEchoCancellationEnabled = settingsCore.mEchoCancellationEnabled;
|
||||||
|
mAutoDownloadReceivedFiles = settingsCore.mAutoDownloadReceivedFiles;
|
||||||
mAutomaticallyRecordCallsEnabled = settingsCore.mAutomaticallyRecordCallsEnabled;
|
mAutomaticallyRecordCallsEnabled = settingsCore.mAutomaticallyRecordCallsEnabled;
|
||||||
|
|
||||||
// Audio
|
// Audio
|
||||||
|
|
@ -233,6 +235,12 @@ void SettingsCore::setSelf(QSharedPointer<SettingsCore> me) {
|
||||||
mSettingsModelConnection->invokeToCore([this, enabled]() { setEchoCancellationEnabled(enabled); });
|
mSettingsModelConnection->invokeToCore([this, enabled]() { setEchoCancellationEnabled(enabled); });
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Auto download incoming files
|
||||||
|
mSettingsModelConnection->makeConnectToModel(
|
||||||
|
&SettingsModel::autoDownloadReceivedFilesChanged, [this](const bool enabled) {
|
||||||
|
mSettingsModelConnection->invokeToCore([this, enabled]() { setAutoDownloadReceivedFiles(enabled); });
|
||||||
|
});
|
||||||
|
|
||||||
// Auto recording
|
// Auto recording
|
||||||
mSettingsModelConnection->makeConnectToModel(
|
mSettingsModelConnection->makeConnectToModel(
|
||||||
&SettingsModel::automaticallyRecordCallsEnabledChanged, [this](const bool enabled) {
|
&SettingsModel::automaticallyRecordCallsEnabledChanged, [this](const bool enabled) {
|
||||||
|
|
@ -462,6 +470,7 @@ void SettingsCore::reset(const SettingsCore &settingsCore) {
|
||||||
setEchoCancellationEnabled(settingsCore.mEchoCancellationEnabled);
|
setEchoCancellationEnabled(settingsCore.mEchoCancellationEnabled);
|
||||||
setAutomaticallyRecordCallsEnabled(settingsCore.mAutomaticallyRecordCallsEnabled);
|
setAutomaticallyRecordCallsEnabled(settingsCore.mAutomaticallyRecordCallsEnabled);
|
||||||
|
|
||||||
|
setAutoDownloadReceivedFiles(settingsCore.mAutoDownloadReceivedFiles);
|
||||||
// Audio
|
// Audio
|
||||||
setCaptureDevices(settingsCore.mCaptureDevices);
|
setCaptureDevices(settingsCore.mCaptureDevices);
|
||||||
setPlaybackDevices(settingsCore.mPlaybackDevices);
|
setPlaybackDevices(settingsCore.mPlaybackDevices);
|
||||||
|
|
@ -576,6 +585,14 @@ void SettingsCore::setEchoCancellationEnabled(bool enabled) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SettingsCore::setAutoDownloadReceivedFiles(bool enabled) {
|
||||||
|
if (mAutoDownloadReceivedFiles != enabled) {
|
||||||
|
mAutoDownloadReceivedFiles = enabled;
|
||||||
|
emit autoDownloadReceivedFilesChanged();
|
||||||
|
setIsSaved(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void SettingsCore::setAutomaticallyRecordCallsEnabled(bool enabled) {
|
void SettingsCore::setAutomaticallyRecordCallsEnabled(bool enabled) {
|
||||||
if (mAutomaticallyRecordCallsEnabled != enabled) {
|
if (mAutomaticallyRecordCallsEnabled != enabled) {
|
||||||
mAutomaticallyRecordCallsEnabled = enabled;
|
mAutomaticallyRecordCallsEnabled = enabled;
|
||||||
|
|
@ -960,6 +977,9 @@ void SettingsCore::writeIntoModel(std::shared_ptr<SettingsModel> model) const {
|
||||||
model->setEchoCancellationEnabled(mEchoCancellationEnabled);
|
model->setEchoCancellationEnabled(mEchoCancellationEnabled);
|
||||||
model->setAutomaticallyRecordCallsEnabled(mAutomaticallyRecordCallsEnabled);
|
model->setAutomaticallyRecordCallsEnabled(mAutomaticallyRecordCallsEnabled);
|
||||||
|
|
||||||
|
// Chat
|
||||||
|
model->setAutoDownloadReceivedFiles(mAutoDownloadReceivedFiles);
|
||||||
|
|
||||||
// Audio
|
// Audio
|
||||||
model->setRingerDevice(mRingerDevice);
|
model->setRingerDevice(mRingerDevice);
|
||||||
model->setCaptureDevice(mCaptureDevice);
|
model->setCaptureDevice(mCaptureDevice);
|
||||||
|
|
@ -1022,6 +1042,9 @@ void SettingsCore::writeFromModel(const std::shared_ptr<SettingsModel> &model) {
|
||||||
mEchoCancellationEnabled = model->getEchoCancellationEnabled();
|
mEchoCancellationEnabled = model->getEchoCancellationEnabled();
|
||||||
mAutomaticallyRecordCallsEnabled = model->getAutomaticallyRecordCallsEnabled();
|
mAutomaticallyRecordCallsEnabled = model->getAutomaticallyRecordCallsEnabled();
|
||||||
|
|
||||||
|
// Chat
|
||||||
|
mAutoDownloadReceivedFiles = model->getAutoDownloadReceivedFiles();
|
||||||
|
|
||||||
// Audio
|
// Audio
|
||||||
mCaptureDevices = model->getCaptureDevices();
|
mCaptureDevices = model->getCaptureDevices();
|
||||||
mPlaybackDevices = model->getPlaybackDevices();
|
mPlaybackDevices = model->getPlaybackDevices();
|
||||||
|
|
|
||||||
|
|
@ -40,6 +40,8 @@ public:
|
||||||
Q_PROPERTY(bool videoEnabled READ getVideoEnabled WRITE setVideoEnabled NOTIFY videoEnabledChanged)
|
Q_PROPERTY(bool videoEnabled READ getVideoEnabled WRITE setVideoEnabled NOTIFY videoEnabledChanged)
|
||||||
Q_PROPERTY(bool echoCancellationEnabled READ getEchoCancellationEnabled WRITE setEchoCancellationEnabled NOTIFY
|
Q_PROPERTY(bool echoCancellationEnabled READ getEchoCancellationEnabled WRITE setEchoCancellationEnabled NOTIFY
|
||||||
echoCancellationEnabledChanged)
|
echoCancellationEnabledChanged)
|
||||||
|
Q_PROPERTY(bool autoDownloadReceivedFiles READ getAutoDownloadReceivedFiles WRITE setAutoDownloadReceivedFiles
|
||||||
|
NOTIFY autoDownloadReceivedFilesChanged)
|
||||||
Q_PROPERTY(
|
Q_PROPERTY(
|
||||||
int echoCancellationCalibration READ getEchoCancellationCalibration NOTIFY echoCancellationCalibrationChanged)
|
int echoCancellationCalibration READ getEchoCancellationCalibration NOTIFY echoCancellationCalibrationChanged)
|
||||||
Q_PROPERTY(bool automaticallyRecordCallsEnabled READ getAutomaticallyRecordCallsEnabled WRITE
|
Q_PROPERTY(bool automaticallyRecordCallsEnabled READ getAutomaticallyRecordCallsEnabled WRITE
|
||||||
|
|
@ -127,6 +129,11 @@ public:
|
||||||
}
|
}
|
||||||
void setEchoCancellationEnabled(bool enabled);
|
void setEchoCancellationEnabled(bool enabled);
|
||||||
|
|
||||||
|
bool getAutoDownloadReceivedFiles() {
|
||||||
|
return mAutoDownloadReceivedFiles;
|
||||||
|
}
|
||||||
|
void setAutoDownloadReceivedFiles(bool enabled);
|
||||||
|
|
||||||
bool getAutomaticallyRecordCallsEnabled() {
|
bool getAutomaticallyRecordCallsEnabled() {
|
||||||
return mAutomaticallyRecordCallsEnabled;
|
return mAutomaticallyRecordCallsEnabled;
|
||||||
}
|
}
|
||||||
|
|
@ -248,6 +255,7 @@ signals:
|
||||||
void videoEnabledChanged();
|
void videoEnabledChanged();
|
||||||
|
|
||||||
void echoCancellationEnabledChanged();
|
void echoCancellationEnabledChanged();
|
||||||
|
void autoDownloadReceivedFilesChanged();
|
||||||
|
|
||||||
void automaticallyRecordCallsEnabledChanged();
|
void automaticallyRecordCallsEnabledChanged();
|
||||||
|
|
||||||
|
|
@ -327,6 +335,7 @@ private:
|
||||||
// Call
|
// Call
|
||||||
bool mVideoEnabled;
|
bool mVideoEnabled;
|
||||||
bool mEchoCancellationEnabled;
|
bool mEchoCancellationEnabled;
|
||||||
|
bool mAutoDownloadReceivedFiles;
|
||||||
bool mAutomaticallyRecordCallsEnabled;
|
bool mAutomaticallyRecordCallsEnabled;
|
||||||
|
|
||||||
// Audio
|
// Audio
|
||||||
|
|
|
||||||
|
|
@ -29,14 +29,6 @@
|
||||||
|
|
||||||
DEFINE_ABSTRACT_OBJECT(SoundPlayerCore)
|
DEFINE_ABSTRACT_OBJECT(SoundPlayerCore)
|
||||||
|
|
||||||
// =============================================================================
|
|
||||||
|
|
||||||
using namespace std;
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
int ForceCloseTimerInterval = 20;
|
|
||||||
}
|
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
QSharedPointer<SoundPlayerCore> SoundPlayerCore::create() {
|
QSharedPointer<SoundPlayerCore> SoundPlayerCore::create() {
|
||||||
|
|
@ -114,7 +106,7 @@ void SoundPlayerCore::buildInternalPlayer(QSharedPointer<SoundPlayerCore> me) {
|
||||||
mSoundPlayerModelConnection->invokeToModel([this] { mSoundPlayerModel->play(mSource); });
|
mSoundPlayerModelConnection->invokeToModel([this] { mSoundPlayerModel->play(mSource); });
|
||||||
});
|
});
|
||||||
mSoundPlayerModelConnection->makeConnectToCore(&SoundPlayerCore::lSeek, [this](int offset) {
|
mSoundPlayerModelConnection->makeConnectToCore(&SoundPlayerCore::lSeek, [this](int offset) {
|
||||||
mSoundPlayerModelConnection->invokeToModel([this, offset] { mSoundPlayerModel->seek(offset); });
|
mSoundPlayerModelConnection->invokeToModel([this, offset] { mSoundPlayerModel->seek(mSource, offset); });
|
||||||
});
|
});
|
||||||
mSoundPlayerModelConnection->makeConnectToModel(&SoundPlayerModel::positionChanged, [this](int pos) {
|
mSoundPlayerModelConnection->makeConnectToModel(&SoundPlayerModel::positionChanged, [this](int pos) {
|
||||||
mSoundPlayerModelConnection->invokeToCore([this, pos] { setPosition(pos); });
|
mSoundPlayerModelConnection->invokeToCore([this, pos] { setPosition(pos); });
|
||||||
|
|
@ -126,12 +118,15 @@ void SoundPlayerCore::buildInternalPlayer(QSharedPointer<SoundPlayerCore> me) {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
mSoundPlayerModelConnection->makeConnectToModel(&SoundPlayerModel::eofReached,
|
mSoundPlayerModelConnection->makeConnectToModel(&SoundPlayerModel::eofReached,
|
||||||
[this](const shared_ptr<linphone::Player> &player) {
|
[this](const std::shared_ptr<linphone::Player> &player) {
|
||||||
mSoundPlayerModelConnection->invokeToCore([this] {
|
mSoundPlayerModelConnection->invokeToCore([this] {
|
||||||
mForceClose = true;
|
mForceClose = true;
|
||||||
handleEof();
|
handleEof();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
mSoundPlayerModelConnection->makeConnectToModel(&SoundPlayerModel::errorChanged, [this](QString error) {
|
||||||
|
mSoundPlayerModelConnection->invokeToCore([this, error] { setError(error); });
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,7 @@ SoundPlayerGui::SoundPlayerGui(QObject *parent) : QObject(parent) {
|
||||||
if (mCore) connect(mCore.get(), &SoundPlayerCore::sourceChanged, this, &SoundPlayerGui::sourceChanged);
|
if (mCore) connect(mCore.get(), &SoundPlayerCore::sourceChanged, this, &SoundPlayerGui::sourceChanged);
|
||||||
if (mCore) connect(mCore.get(), &SoundPlayerCore::stopped, this, &SoundPlayerGui::stopped);
|
if (mCore) connect(mCore.get(), &SoundPlayerCore::stopped, this, &SoundPlayerGui::stopped);
|
||||||
if (mCore) connect(mCore.get(), &SoundPlayerCore::positionChanged, this, &SoundPlayerGui::positionChanged);
|
if (mCore) connect(mCore.get(), &SoundPlayerCore::positionChanged, this, &SoundPlayerGui::positionChanged);
|
||||||
|
if (mCore) connect(mCore.get(), &SoundPlayerCore::errorChanged, this, &SoundPlayerGui::errorChanged);
|
||||||
}
|
}
|
||||||
SoundPlayerGui::SoundPlayerGui(QSharedPointer<SoundPlayerCore> core) {
|
SoundPlayerGui::SoundPlayerGui(QSharedPointer<SoundPlayerCore> core) {
|
||||||
App::getInstance()->mEngine->setObjectOwnership(this, QQmlEngine::JavaScriptOwnership);
|
App::getInstance()->mEngine->setObjectOwnership(this, QQmlEngine::JavaScriptOwnership);
|
||||||
|
|
|
||||||
|
|
@ -44,6 +44,7 @@ signals:
|
||||||
void sourceChanged();
|
void sourceChanged();
|
||||||
void stopped();
|
void stopped();
|
||||||
void positionChanged();
|
void positionChanged();
|
||||||
|
void errorChanged(QString error);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
DECLARE_ABSTRACT_OBJECT
|
DECLARE_ABSTRACT_OBJECT
|
||||||
|
|
|
||||||
|
|
@ -823,6 +823,19 @@
|
||||||
"puppy eyes"
|
"puppy eyes"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"code": "1f979",
|
||||||
|
"char": "🥹",
|
||||||
|
"name": "face holding back tears",
|
||||||
|
"keywords": [
|
||||||
|
"tears",
|
||||||
|
"emotive",
|
||||||
|
"admiration",
|
||||||
|
"face with tears",
|
||||||
|
"gratitude",
|
||||||
|
"admiration"
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"code": "1f626",
|
"code": "1f626",
|
||||||
"char": "😦",
|
"char": "😦",
|
||||||
|
|
|
||||||
56
Linphone/data/emoji/emojiSvgs/1f979.svg
Normal file
56
Linphone/data/emoji/emojiSvgs/1f979.svg
Normal file
|
|
@ -0,0 +1,56 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!-- Generator: Adobe Illustrator 25.2.3, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||||
|
<svg version="1.1" id="Raised-Hand" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 128 128" style="enable-background:new 0 0 128 128;" xml:space="preserve">
|
||||||
|
<radialGradient id="face_1_" cx="63.6" cy="7861.3501" r="56.9597" gradientTransform="matrix(1 0 0 1 0 -7798.4497)" gradientUnits="userSpaceOnUse">
|
||||||
|
<stop offset="0.5" style="stop-color:#FDE030"/>
|
||||||
|
<stop offset="0.92" style="stop-color:#F7C02B"/>
|
||||||
|
<stop offset="1" style="stop-color:#F4A223"/>
|
||||||
|
</radialGradient>
|
||||||
|
<path id="face" style="fill:url(#face_1_);" d="M63.6,118.8c-27.9,0-58-17.5-58-55.9S35.7,7,63.6,7c15.5,0,29.8,5.1,40.4,14.4 c11.5,10.2,17.6,24.6,17.6,41.5s-6.1,31.2-17.6,41.4C93.4,113.6,79,118.8,63.6,118.8z"/>
|
||||||
|
<g id="eyes">
|
||||||
|
<path style="fill:#FFFFFF;" d="M43,47.7c9.58,0.03,17.33,7.82,17.3,17.4s-7.82,17.33-17.4,17.3c-9.58-0.03-17.33-7.82-17.3-17.4 C25.66,55.43,33.43,47.71,43,47.7"/>
|
||||||
|
<circle style="fill:#422B0D;" cx="42.7" cy="62.8" r="15.4"/>
|
||||||
|
|
||||||
|
<ellipse transform="matrix(0.7659 -0.6429 0.6429 0.7659 -32.8541 47.0076)" style="fill:#FFFFFF;" cx="48.13" cy="68.62" rx="2.6" ry="2.4"/>
|
||||||
|
|
||||||
|
<ellipse transform="matrix(0.8022 -0.5971 0.5971 0.8022 -26.9821 34.5782)" style="fill:#FFFFFF;" cx="38.69" cy="58.01" rx="9" ry="7.3"/>
|
||||||
|
<path style="fill:#FFFFFF;" d="M86,47.7c9.58,0.03,17.33,7.82,17.3,17.4c-0.03,9.58-7.82,17.33-17.4,17.3 c-9.58-0.03-17.33-7.82-17.3-17.4C68.66,55.43,76.43,47.71,86,47.7"/>
|
||||||
|
<circle style="fill:#422B0D;" cx="85.7" cy="62.8" r="15.4"/>
|
||||||
|
|
||||||
|
<ellipse transform="matrix(0.7659 -0.6429 0.6429 0.7659 -22.7305 74.6885)" style="fill:#FFFFFF;" cx="91.21" cy="68.56" rx="2.6" ry="2.4"/>
|
||||||
|
|
||||||
|
<ellipse transform="matrix(0.8022 -0.5971 0.5971 0.8022 -18.4797 60.2586)" style="fill:#FFFFFF;" cx="81.7" cy="58.02" rx="9" ry="7.3"/>
|
||||||
|
</g>
|
||||||
|
<path style="fill:none;" d="M43,47.7c-9.58,0.03-17.33,7.82-17.3,17.4c0.03,9.58,7.82,17.33,17.4,17.3 c9.58-0.03,17.33-7.82,17.3-17.4C60.34,55.43,52.57,47.71,43,47.7"/>
|
||||||
|
<circle style="fill:none;" cx="43.3" cy="62.8" r="15.4"/>
|
||||||
|
<ellipse transform="matrix(0.6429 -0.7659 0.7659 0.6429 -39.0221 53.4261)" style="fill:none;" cx="37.79" cy="68.56" rx="2.4" ry="2.6"/>
|
||||||
|
<ellipse transform="matrix(0.5971 -0.8022 0.8022 0.5971 -27.4803 61.3103)" style="fill:none;" cx="47.29" cy="58.01" rx="7.3" ry="9"/>
|
||||||
|
<g>
|
||||||
|
<defs>
|
||||||
|
<path id="SVGID_1_" d="M86,47.7c-9.58,0.03-17.33,7.82-17.3,17.4c0.03,9.58,7.82,17.33,17.4,17.3c9.58-0.03,17.33-7.82,17.3-17.4 C103.34,55.43,95.57,47.71,86,47.7"/>
|
||||||
|
</defs>
|
||||||
|
<clipPath id="SVGID_2_">
|
||||||
|
<use xlink:href="#SVGID_1_" style="overflow:visible;"/>
|
||||||
|
</clipPath>
|
||||||
|
<g style="clip-path:url(#SVGID_2_);">
|
||||||
|
<path style="fill:#29B6F6;" d="M102.35,68.47c-2.16-0.36-4.35,0.48-5.71,2.21c-0.97,1.27-2.49,1.99-4.09,1.95h-0.27 c-1.1,0-2.19,0.3-3.13,0.87c-1.48,0.9-3.35,0.9-4.83,0c-0.94-0.57-2.03-0.87-3.13-0.87c-0.24-0.02-0.47-0.02-0.71,0 c-1.71,0.25-3.42-0.43-4.48-1.79c-1.14-1.55-2.95-2.46-4.88-2.45c-3.31,0.08-5.96,2.76-6,6.07c0.03,3.3,2.7,5.97,6,6 c0.28,0,0.57-0.02,0.85-0.06c1.65-0.27,3.31,0.4,4.31,1.74c1.85,2.65,5.46,3.36,8.17,1.61c1.41-0.89,3.2-0.89,4.61,0 c2.63,1.68,6.1,1.07,8-1.4c0.91-1.25,2.38-1.96,3.93-1.9h0.38c3.34-0.05,6.01-2.8,5.96-6.14c-0.04-2.89-2.12-5.34-4.96-5.86 L102.35,68.47z"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<defs>
|
||||||
|
<path id="SVGID_3_" d="M43,47.7c-9.58,0.03-17.33,7.82-17.3,17.4c0.03,9.58,7.82,17.33,17.4,17.3c9.58-0.03,17.33-7.82,17.3-17.4 C60.34,55.43,52.57,47.71,43,47.7"/>
|
||||||
|
</defs>
|
||||||
|
<clipPath id="SVGID_4_">
|
||||||
|
<use xlink:href="#SVGID_3_" style="overflow:visible;"/>
|
||||||
|
</clipPath>
|
||||||
|
<g style="clip-path:url(#SVGID_4_);">
|
||||||
|
<path style="fill:#29B6F6;" d="M59.9,68.47c-2.16-0.36-4.35,0.49-5.7,2.21c-0.98,1.27-2.5,1.99-4.1,1.95h-0.26 c-1.1-0.01-2.19,0.29-3.13,0.87c-1.49,0.9-3.35,0.9-4.84,0c-0.94-0.58-2.03-0.88-3.13-0.87c-0.23-0.01-0.47-0.01-0.7,0 c-1.73,0.26-3.47-0.44-4.53-1.83c-1.14-1.55-2.95-2.46-4.87-2.45c-3.31,0.08-5.96,2.76-6,6.07c0,3.31,2.69,6,6,6 c0.02,0,0.03,0,0.05,0c0.28,0,0.56-0.02,0.84-0.06c1.65-0.27,3.32,0.4,4.32,1.74c1.82,2.66,5.42,3.4,8.15,1.68 c1.4-0.89,3.2-0.89,4.6,0c2.63,1.68,6.1,1.07,8-1.4c0.9-1.25,2.38-1.96,3.92-1.9h0.39c3.31,0.28,6.22-2.19,6.5-5.5 S63.22,68.76,59.9,68.47L59.9,68.47z"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g id="eyebrows">
|
||||||
|
<path style="fill:#422B0D;" d="M27.4,39.8c-2.2,0.4-2.3,3.6,0.1,3.7c5.3,0.07,10.42-1.9,14.3-5.5c1.48-1.28,2.73-2.8,3.7-4.5 c0.58-0.83,0.38-1.97-0.45-2.55c-0.83-0.58-1.97-0.38-2.55,0.45l-0.1,0.1C38.48,35.88,33.19,38.81,27.4,39.8z"/>
|
||||||
|
<path style="fill:#422B0D;" d="M84.5,31.4c-0.58-0.83-1.72-1.03-2.55-0.45c-0.83,0.58-1.03,1.72-0.45,2.55 c0.97,1.7,2.22,3.22,3.7,4.5c3.9,3.57,9.01,5.54,14.3,5.5c2.5-0.1,2.3-3.3,0.1-3.7C93.74,38.84,88.41,35.87,84.5,31.4L84.5,31.4"/>
|
||||||
|
</g>
|
||||||
|
<path style="fill:#EB8F00;" d="M111.49,29.67c5.33,8.6,8.11,18.84,8.11,30.23c0,16.9-6.1,31.2-17.6,41.4 c-10.6,9.3-25,14.5-40.4,14.5c-18.06,0-37-7.35-48.18-22.94c10.76,17.66,31,25.94,50.18,25.94c15.4,0,29.8-5.2,40.4-14.5 c11.5-10.2,17.6-24.5,17.6-41.4C121.6,50.16,118.13,38.84,111.49,29.67z"/>
|
||||||
|
<path id="mouth" style="fill:#422B0D;" d="M64,103.2c10.8,0,17.8-7.9,19.7-11.6c0.7-1.4,0.7-2.6,0.1-3.1c-0.64-0.4-1.46-0.4-2.1,0 c-0.32,0.13-0.62,0.3-0.9,0.5c-4.9,3.52-10.77,5.44-16.8,5.5c-6.01-0.08-11.87-1.96-16.8-5.4c-0.28-0.2-0.58-0.37-0.9-0.5 c-0.64-0.4-1.46-0.4-2.1,0c-0.6,0.6-0.6,1.7,0.1,3.1C46.2,95.3,53.2,103.2,64,103.2z"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 5.3 KiB |
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
|
@ -43,6 +43,8 @@ list(APPEND _LINPHONEAPP_SOURCES
|
||||||
|
|
||||||
model/sound-player/SoundPlayerModel.cpp
|
model/sound-player/SoundPlayerModel.cpp
|
||||||
|
|
||||||
|
model/recorder/RecorderModel.cpp
|
||||||
|
|
||||||
model/tool/ToolModel.cpp
|
model/tool/ToolModel.cpp
|
||||||
model/tool/VfsUtils.cpp
|
model/tool/VfsUtils.cpp
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -119,6 +119,10 @@ void ChatModel::deleteHistory() {
|
||||||
emit historyDeleted();
|
emit historyDeleted();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ChatModel::deleteMessage(std::shared_ptr<linphone::ChatMessage> message) {
|
||||||
|
mMonitor->deleteMessage(message);
|
||||||
|
}
|
||||||
|
|
||||||
void ChatModel::leave() {
|
void ChatModel::leave() {
|
||||||
mMonitor->leave();
|
mMonitor->leave();
|
||||||
}
|
}
|
||||||
|
|
@ -128,6 +132,11 @@ void ChatModel::deleteChatRoom() {
|
||||||
emit deleted();
|
emit deleted();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<linphone::ChatMessage>
|
||||||
|
ChatModel::createVoiceRecordingMessage(const std::shared_ptr<linphone::Recorder> &recorder) {
|
||||||
|
return mMonitor->createVoiceRecordingMessage(recorder);
|
||||||
|
}
|
||||||
|
|
||||||
std::shared_ptr<linphone::ChatMessage> ChatModel::createTextMessageFromText(QString text) {
|
std::shared_ptr<linphone::ChatMessage> ChatModel::createTextMessageFromText(QString text) {
|
||||||
return mMonitor->createMessageFromUtf8(Utils::appStringToCoreString(text));
|
return mMonitor->createMessageFromUtf8(Utils::appStringToCoreString(text));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -47,8 +47,11 @@ public:
|
||||||
std::list<std::shared_ptr<linphone::ChatMessage>> getHistory() const;
|
std::list<std::shared_ptr<linphone::ChatMessage>> getHistory() const;
|
||||||
QString getIdentifier() const;
|
QString getIdentifier() const;
|
||||||
void deleteHistory();
|
void deleteHistory();
|
||||||
|
void deleteMessage(std::shared_ptr<linphone::ChatMessage> message);
|
||||||
void deleteChatRoom();
|
void deleteChatRoom();
|
||||||
void leave();
|
void leave();
|
||||||
|
std::shared_ptr<linphone::ChatMessage>
|
||||||
|
createVoiceRecordingMessage(const std::shared_ptr<linphone::Recorder> &recorder);
|
||||||
std::shared_ptr<linphone::ChatMessage> createTextMessageFromText(QString text);
|
std::shared_ptr<linphone::ChatMessage> createTextMessageFromText(QString text);
|
||||||
std::shared_ptr<linphone::ChatMessage> createMessage(QString text,
|
std::shared_ptr<linphone::ChatMessage> createMessage(QString text,
|
||||||
QList<std::shared_ptr<ChatMessageContentModel>> filesContent);
|
QList<std::shared_ptr<ChatMessageContentModel>> filesContent);
|
||||||
|
|
|
||||||
|
|
@ -102,6 +102,10 @@ void ChatMessageModel::removeReaction() {
|
||||||
sendReaction(QString());
|
sendReaction(QString());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ChatMessageModel::send() {
|
||||||
|
mMonitor->send();
|
||||||
|
}
|
||||||
|
|
||||||
QString ChatMessageModel::getOwnReaction() const {
|
QString ChatMessageModel::getOwnReaction() const {
|
||||||
auto reaction = mMonitor->getOwnReaction();
|
auto reaction = mMonitor->getOwnReaction();
|
||||||
return reaction ? Utils::coreStringToAppString(reaction->getBody()) : QString();
|
return reaction ? Utils::coreStringToAppString(reaction->getBody()) : QString();
|
||||||
|
|
|
||||||
|
|
@ -59,6 +59,8 @@ public:
|
||||||
|
|
||||||
void removeReaction();
|
void removeReaction();
|
||||||
|
|
||||||
|
void send();
|
||||||
|
|
||||||
linphone::ChatMessage::State getState() const;
|
linphone::ChatMessage::State getState() const;
|
||||||
|
|
||||||
QString getOwnReaction() const;
|
QString getOwnReaction() const;
|
||||||
|
|
|
||||||
118
Linphone/model/recorder/RecorderModel.cpp
Normal file
118
Linphone/model/recorder/RecorderModel.cpp
Normal file
|
|
@ -0,0 +1,118 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2021 Belledonne Communications SARL.
|
||||||
|
*
|
||||||
|
* This file is part of linphone-desktop
|
||||||
|
* (see https://www.linphone.org).
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "core/App.hpp"
|
||||||
|
#include "model/core/CoreModel.hpp"
|
||||||
|
#include "model/setting/SettingsModel.hpp"
|
||||||
|
#include "tool/Utils.hpp"
|
||||||
|
|
||||||
|
#include <QFile>
|
||||||
|
#include <QQmlApplicationEngine>
|
||||||
|
|
||||||
|
#include "RecorderModel.hpp"
|
||||||
|
|
||||||
|
DEFINE_ABSTRACT_OBJECT(RecorderModel)
|
||||||
|
|
||||||
|
// =============================================================================
|
||||||
|
|
||||||
|
RecorderModel::RecorderModel(std::shared_ptr<linphone::Recorder> recorder, QObject *parent) : QObject(parent) {
|
||||||
|
mustBeInLinphoneThread(getClassName());
|
||||||
|
mRecorder = recorder;
|
||||||
|
}
|
||||||
|
|
||||||
|
RecorderModel::~RecorderModel() {
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<linphone::Recorder> RecorderModel::getRecorder() {
|
||||||
|
return mRecorder;
|
||||||
|
}
|
||||||
|
|
||||||
|
int RecorderModel::getDuration() const {
|
||||||
|
mustBeInLinphoneThread(log().arg(Q_FUNC_INFO));
|
||||||
|
return mRecorder->getDuration();
|
||||||
|
}
|
||||||
|
|
||||||
|
float RecorderModel::getCaptureVolume() const {
|
||||||
|
mustBeInLinphoneThread(log().arg(Q_FUNC_INFO));
|
||||||
|
return mRecorder->getCaptureVolume();
|
||||||
|
}
|
||||||
|
|
||||||
|
linphone::Recorder::State RecorderModel::getState() const {
|
||||||
|
mustBeInLinphoneThread(log().arg(Q_FUNC_INFO));
|
||||||
|
return mRecorder->getState();
|
||||||
|
}
|
||||||
|
|
||||||
|
QString RecorderModel::getFile() const {
|
||||||
|
mustBeInLinphoneThread(log().arg(Q_FUNC_INFO));
|
||||||
|
return Utils::coreStringToAppString(mRecorder->getFile());
|
||||||
|
}
|
||||||
|
|
||||||
|
QStringList RecorderModel::splitSavedFilename(const QString &filename) {
|
||||||
|
QStringList fields = filename.split('_');
|
||||||
|
if (fields.size() == 3 && fields[0] == "vocal" && fields[1].split('-').size() == 3 &&
|
||||||
|
fields[2].split('-').size() == 4) {
|
||||||
|
return fields;
|
||||||
|
} else return QStringList(filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
QDateTime RecorderModel::getDateTimeSavedFilename(const QString &filename) {
|
||||||
|
auto fields = splitSavedFilename(filename);
|
||||||
|
if (fields.size() > 1) return QDateTime::fromString(fields[1] + "_" + fields[2], "yyyy-MM-dd_hh-mm-ss-zzz");
|
||||||
|
else return QDateTime();
|
||||||
|
}
|
||||||
|
|
||||||
|
void RecorderModel::start() {
|
||||||
|
mustBeInLinphoneThread(log().arg(Q_FUNC_INFO));
|
||||||
|
bool soFarSoGood;
|
||||||
|
QString filename =
|
||||||
|
QStringLiteral("vocal_%1.mkv").arg(QDateTime::currentDateTime().toString("yyyy-MM-dd_hh-mm-ss-zzz"));
|
||||||
|
const QString safeFilePath = Utils::getSafeFilePath(
|
||||||
|
QStringLiteral("%1%2").arg(SettingsModel::getInstance()->getSavedCallsFolder()).arg(filename), &soFarSoGood);
|
||||||
|
|
||||||
|
if (!soFarSoGood) {
|
||||||
|
qWarning() << QStringLiteral("Unable to create safe file path for: %1.").arg(filename);
|
||||||
|
emit errorChanged(QString("Unable to create safe file path for : %1.").arg(filename));
|
||||||
|
} else if (mRecorder->open(Utils::appStringToCoreString(safeFilePath)) < 0) {
|
||||||
|
qWarning() << QStringLiteral("Unable to open safe file path for: %1.").arg(filename);
|
||||||
|
emit errorChanged(QString("Unable to open safe file path for : %1.").arg(filename));
|
||||||
|
} else if (mRecorder->start() < 0) {
|
||||||
|
qWarning() << QStringLiteral("Unable to start recording to : %1.").arg(filename);
|
||||||
|
emit errorChanged(QString("Unable to start recording to : %1.").arg(filename));
|
||||||
|
}
|
||||||
|
emit stateChanged();
|
||||||
|
emit fileChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
void RecorderModel::pause() {
|
||||||
|
mustBeInLinphoneThread(log().arg(Q_FUNC_INFO));
|
||||||
|
mRecorder->pause();
|
||||||
|
emit stateChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
void RecorderModel::stop() {
|
||||||
|
mustBeInLinphoneThread(log().arg(Q_FUNC_INFO));
|
||||||
|
// if (mRecorder->getState() == linphone::Recorder::State::Running) // Remove these tests when the SDK do them.
|
||||||
|
// mRecorder->pause();
|
||||||
|
// if (mRecorder->getState() == linphone::Recorder::State::Paused) {
|
||||||
|
mRecorder->close();
|
||||||
|
emit stateChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------------------------------------------------------------
|
||||||
60
Linphone/model/recorder/RecorderModel.hpp
Normal file
60
Linphone/model/recorder/RecorderModel.hpp
Normal file
|
|
@ -0,0 +1,60 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2021 Belledonne Communications SARL.
|
||||||
|
*
|
||||||
|
* This file is part of linphone-desktop
|
||||||
|
* (see https://www.linphone.org).
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef RECORDER_MODEL_H
|
||||||
|
#define RECORDER_MODEL_H
|
||||||
|
|
||||||
|
#include "tool/AbstractObject.hpp"
|
||||||
|
#include <linphone++/linphone.hh>
|
||||||
|
|
||||||
|
// =============================================================================
|
||||||
|
|
||||||
|
class RecorderModel : public QObject, public AbstractObject {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
RecorderModel(std::shared_ptr<linphone::Recorder> recorder, QObject *parent = nullptr);
|
||||||
|
virtual ~RecorderModel();
|
||||||
|
|
||||||
|
std::shared_ptr<linphone::Recorder> getRecorder();
|
||||||
|
|
||||||
|
int getDuration() const;
|
||||||
|
float getCaptureVolume() const;
|
||||||
|
linphone::Recorder::State getState() const;
|
||||||
|
QString getFile() const;
|
||||||
|
|
||||||
|
static QStringList
|
||||||
|
splitSavedFilename(const QString &filename); // If doesn't match to generateSavedFilename, return filename
|
||||||
|
static QDateTime getDateTimeSavedFilename(const QString &filename);
|
||||||
|
|
||||||
|
void start();
|
||||||
|
void pause();
|
||||||
|
void stop();
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void stateChanged();
|
||||||
|
void fileChanged();
|
||||||
|
void errorChanged(QString error);
|
||||||
|
|
||||||
|
private:
|
||||||
|
DECLARE_ABSTRACT_OBJECT
|
||||||
|
std::shared_ptr<linphone::Recorder> mRecorder;
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
|
@ -428,6 +428,17 @@ void SettingsModel::setVideoEnabled(const bool enabled) {
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
bool SettingsModel::getAutoDownloadReceivedFiles() const {
|
||||||
|
mustBeInLinphoneThread(log().arg(Q_FUNC_INFO));
|
||||||
|
return CoreModel::getInstance()->getCore()->getMaxSizeForAutoDownloadIncomingFiles() == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SettingsModel::setAutoDownloadReceivedFiles(bool status) {
|
||||||
|
mustBeInLinphoneThread(log().arg(Q_FUNC_INFO));
|
||||||
|
CoreModel::getInstance()->getCore()->setMaxSizeForAutoDownloadIncomingFiles(status ? 0 : -1);
|
||||||
|
emit autoDownloadReceivedFilesChanged(status);
|
||||||
|
}
|
||||||
|
|
||||||
bool SettingsModel::getEchoCancellationEnabled() const {
|
bool SettingsModel::getEchoCancellationEnabled() const {
|
||||||
mustBeInLinphoneThread(log().arg(Q_FUNC_INFO));
|
mustBeInLinphoneThread(log().arg(Q_FUNC_INFO));
|
||||||
return CoreModel::getInstance()->getCore()->echoCancellationEnabled();
|
return CoreModel::getInstance()->getCore()->echoCancellationEnabled();
|
||||||
|
|
@ -558,6 +569,18 @@ QString SettingsModel::getLogsFolder(const shared_ptr<linphone::Config> &config)
|
||||||
: Paths::getLogsDirPath();
|
: Paths::getLogsDirPath();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline std::string getLegacySavedCallsFolder(const shared_ptr<linphone::Config> &config) {
|
||||||
|
auto path = config->getString(SettingsModel::UiSection, "saved_videos_folder", "");
|
||||||
|
if (path == "") path = Utils::appStringToCoreString(Paths::getCapturesDirPath());
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString SettingsModel::getSavedCallsFolder() const {
|
||||||
|
auto path = mConfig->getString(UiSection, "saved_calls_folder", ""); // Avoid to call default function if exist.
|
||||||
|
if (path == "") path = getLegacySavedCallsFolder(mConfig);
|
||||||
|
return QDir::cleanPath(Utils::coreStringToAppString(path)) + QDir::separator();
|
||||||
|
}
|
||||||
|
|
||||||
QString SettingsModel::getLogsUploadUrl() const {
|
QString SettingsModel::getLogsUploadUrl() const {
|
||||||
mustBeInLinphoneThread(log().arg(Q_FUNC_INFO));
|
mustBeInLinphoneThread(log().arg(Q_FUNC_INFO));
|
||||||
auto core = CoreModel::getInstance()->getCore();
|
auto core = CoreModel::getInstance()->getCore();
|
||||||
|
|
|
||||||
|
|
@ -61,6 +61,9 @@ public:
|
||||||
bool getEchoCancellationEnabled() const;
|
bool getEchoCancellationEnabled() const;
|
||||||
void setEchoCancellationEnabled(bool enabled);
|
void setEchoCancellationEnabled(bool enabled);
|
||||||
|
|
||||||
|
void setAutoDownloadReceivedFiles(bool enabled);
|
||||||
|
bool getAutoDownloadReceivedFiles() const;
|
||||||
|
|
||||||
// Audio. --------------------------------------------------------------------
|
// Audio. --------------------------------------------------------------------
|
||||||
|
|
||||||
bool getIsInCall() const;
|
bool getIsInCall() const;
|
||||||
|
|
@ -137,6 +140,7 @@ public:
|
||||||
QString getLogsFolder() const;
|
QString getLogsFolder() const;
|
||||||
void setLogsFolder(const QString &folder);
|
void setLogsFolder(const QString &folder);
|
||||||
static QString getLogsFolder(const std::shared_ptr<linphone::Config> &config);
|
static QString getLogsFolder(const std::shared_ptr<linphone::Config> &config);
|
||||||
|
QString getSavedCallsFolder() const;
|
||||||
|
|
||||||
QString getLogsUploadUrl() const;
|
QString getLogsUploadUrl() const;
|
||||||
void setLogsUploadUrl(const QString &url);
|
void setLogsUploadUrl(const QString &url);
|
||||||
|
|
@ -238,6 +242,9 @@ signals:
|
||||||
|
|
||||||
void dndChanged(bool value);
|
void dndChanged(bool value);
|
||||||
|
|
||||||
|
// Messages. --------------------------------------------------------------------
|
||||||
|
void autoDownloadReceivedFilesChanged(bool enabled);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void notifyConfigReady();
|
void notifyConfigReady();
|
||||||
MediastreamerUtils::SimpleCaptureGraph *mSimpleCaptureGraph = nullptr;
|
MediastreamerUtils::SimpleCaptureGraph *mSimpleCaptureGraph = nullptr;
|
||||||
|
|
|
||||||
|
|
@ -99,7 +99,13 @@ bool SoundPlayerModel::play(QString source) {
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
void SoundPlayerModel::seek(int offset) {
|
void SoundPlayerModel::seek(QString source, int offset) {
|
||||||
|
if (!open(source)) {
|
||||||
|
qWarning() << QStringLiteral("Unable to open: `%1`").arg(source);
|
||||||
|
//: Unable to open: `%1`
|
||||||
|
emit errorChanged(QString("sound_player_open_error").arg(source));
|
||||||
|
return;
|
||||||
|
}
|
||||||
mMonitor->seek(offset);
|
mMonitor->seek(offset);
|
||||||
emit positionChanged(mMonitor->getCurrentPosition());
|
emit positionChanged(mMonitor->getCurrentPosition());
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -46,7 +46,7 @@ public:
|
||||||
void pause();
|
void pause();
|
||||||
bool play(QString source);
|
bool play(QString source);
|
||||||
void stop(bool force = false);
|
void stop(bool force = false);
|
||||||
void seek(int offset);
|
void seek(QString source, int offset);
|
||||||
|
|
||||||
int getPosition() const;
|
int getPosition() const;
|
||||||
bool hasVideo() const; // Call it after playing a video because the detection is not outside this scope.
|
bool hasVideo() const; // Call it after playing a video because the detection is not outside this scope.
|
||||||
|
|
|
||||||
|
|
@ -32,6 +32,7 @@
|
||||||
#include "core/participant/ParticipantDeviceCore.hpp"
|
#include "core/participant/ParticipantDeviceCore.hpp"
|
||||||
#include "core/path/Paths.hpp"
|
#include "core/path/Paths.hpp"
|
||||||
#include "core/payload-type/DownloadablePayloadTypeCore.hpp"
|
#include "core/payload-type/DownloadablePayloadTypeCore.hpp"
|
||||||
|
#include "core/recorder/RecorderGui.hpp"
|
||||||
#include "model/object/VariantObject.hpp"
|
#include "model/object/VariantObject.hpp"
|
||||||
#include "model/tool/ToolModel.hpp"
|
#include "model/tool/ToolModel.hpp"
|
||||||
#include "tool/providers/AvatarProvider.hpp"
|
#include "tool/providers/AvatarProvider.hpp"
|
||||||
|
|
@ -1948,6 +1949,56 @@ QString Utils::getSafeFilePath(const QString &filePath, bool *soFarSoGood) {
|
||||||
return QString("");
|
return QString("");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
VariantObject *Utils::createVoiceRecordingMessage(RecorderGui *recorderGui, ChatGui *chatGui) {
|
||||||
|
VariantObject *data = new VariantObject("createVoiceRecordingMessage");
|
||||||
|
if (!data) return nullptr;
|
||||||
|
data->makeRequest([recorderCore = recorderGui ? recorderGui->getCore() : nullptr,
|
||||||
|
chatCore = chatGui ? chatGui->getCore() : nullptr]() {
|
||||||
|
if (!recorderCore || !chatCore) return QVariant();
|
||||||
|
auto model = recorderCore->getModel();
|
||||||
|
auto chatModel = chatCore->getModel();
|
||||||
|
if (!model || !chatModel) return QVariant();
|
||||||
|
auto recorder = model->getRecorder();
|
||||||
|
auto linMessage = chatModel->createVoiceRecordingMessage(recorder);
|
||||||
|
if (linMessage) {
|
||||||
|
auto messageCore = ChatMessageCore::create(linMessage);
|
||||||
|
return QVariant::fromValue(new ChatMessageGui(messageCore));
|
||||||
|
}
|
||||||
|
return QVariant();
|
||||||
|
});
|
||||||
|
data->requestValue();
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Utils::sendVoiceRecordingMessage(RecorderGui *recorderGui, ChatGui *chatGui) {
|
||||||
|
auto chatModel = chatGui && chatGui->mCore ? chatGui->mCore->getModel() : nullptr;
|
||||||
|
auto recorderModel = recorderGui && recorderGui->mCore ? recorderGui->mCore->getModel() : nullptr;
|
||||||
|
if (!chatModel || !recorderModel) {
|
||||||
|
//: Error with the recorder
|
||||||
|
QString error = !recorderModel ? tr("recorder_error")
|
||||||
|
//: Error in the chat
|
||||||
|
: tr("chat_error");
|
||||||
|
//: Error
|
||||||
|
showInformationPopup(tr("info_popup_error_title"),
|
||||||
|
//: Could not send voice message : %1
|
||||||
|
tr("info_popup_send_voice_message_error_message").arg(error));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
App::postModelAsync([chatModel, recorderModel] {
|
||||||
|
mustBeInLinphoneThread(sLog().arg(Q_FUNC_INFO));
|
||||||
|
auto chat = chatModel->getMonitor();
|
||||||
|
auto recorder = recorderModel->getRecorder();
|
||||||
|
auto linMessage = chatModel->createVoiceRecordingMessage(recorder);
|
||||||
|
if (linMessage) {
|
||||||
|
linMessage->send();
|
||||||
|
} else
|
||||||
|
//: Error
|
||||||
|
showInformationPopup(tr("info_popup_error_title"),
|
||||||
|
//: Failed to create message from record
|
||||||
|
tr("info_popup_send_voice_message_sending_error_message"));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
bool Utils::isVideo(const QString &path) {
|
bool Utils::isVideo(const QString &path) {
|
||||||
if (path.isEmpty()) return false;
|
if (path.isEmpty()) return false;
|
||||||
return QMimeDatabase().mimeTypeForFile(path).name().contains("video/");
|
return QMimeDatabase().mimeTypeForFile(path).name().contains("video/");
|
||||||
|
|
|
||||||
|
|
@ -52,6 +52,7 @@ class ConferenceCore;
|
||||||
class ParticipantDeviceCore;
|
class ParticipantDeviceCore;
|
||||||
class DownloadablePayloadTypeCore;
|
class DownloadablePayloadTypeCore;
|
||||||
class ChatGui;
|
class ChatGui;
|
||||||
|
class RecorderGui;
|
||||||
|
|
||||||
class Utils : public QObject, public AbstractObject {
|
class Utils : public QObject, public AbstractObject {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
@ -174,6 +175,9 @@ public:
|
||||||
static QDateTime getOffsettedUTC(const QDateTime &date);
|
static QDateTime getOffsettedUTC(const QDateTime &date);
|
||||||
Q_INVOKABLE static QString toTimeString(QDateTime date, const QString &format = "hh:mm:ss");
|
Q_INVOKABLE static QString toTimeString(QDateTime date, const QString &format = "hh:mm:ss");
|
||||||
|
|
||||||
|
Q_INVOKABLE static VariantObject *createVoiceRecordingMessage(RecorderGui *recorderGui, ChatGui *chatGui);
|
||||||
|
Q_INVOKABLE static void sendVoiceRecordingMessage(RecorderGui *recorderGui, ChatGui *chatGui);
|
||||||
|
|
||||||
// QDir findDirectoryByName(QString startPath, QString name);
|
// QDir findDirectoryByName(QString startPath, QString name);
|
||||||
|
|
||||||
static QString getApplicationProduct();
|
static QString getApplicationProduct();
|
||||||
|
|
|
||||||
|
|
@ -149,6 +149,7 @@ list(APPEND _LINPHONEAPP_QML_FILES
|
||||||
view/Page/Layout/Settings/DebugSettingsLayout.qml
|
view/Page/Layout/Settings/DebugSettingsLayout.qml
|
||||||
view/Page/Layout/Settings/LdapSettingsLayout.qml
|
view/Page/Layout/Settings/LdapSettingsLayout.qml
|
||||||
view/Page/Layout/Settings/CarddavSettingsLayout.qml
|
view/Page/Layout/Settings/CarddavSettingsLayout.qml
|
||||||
|
view/Page/Layout/Settings/ChatSettingsLayout.qml
|
||||||
view/Page/Layout/Settings/SecuritySettingsLayout.qml
|
view/Page/Layout/Settings/SecuritySettingsLayout.qml
|
||||||
view/Page/Layout/Settings/NetworkSettingsLayout.qml
|
view/Page/Layout/Settings/NetworkSettingsLayout.qml
|
||||||
view/Page/Layout/Settings/AdvancedSettingsLayout.qml
|
view/Page/Layout/Settings/AdvancedSettingsLayout.qml
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@ Control.Button {
|
||||||
property color pressedTextColor: style?.text?.pressed || Qt.darker(textColor, 1.1)
|
property color pressedTextColor: style?.text?.pressed || Qt.darker(textColor, 1.1)
|
||||||
property color borderColor: style?.borderColor || "transparent"
|
property color borderColor: style?.borderColor || "transparent"
|
||||||
ToolTip.visible: hovered && ToolTip.text != ""
|
ToolTip.visible: hovered && ToolTip.text != ""
|
||||||
ToolTip.delay: 1000
|
ToolTip.delay: 500
|
||||||
property color disabledFilterColor: color.hslLightness > 0.5
|
property color disabledFilterColor: color.hslLightness > 0.5
|
||||||
? DefaultStyle.grey_0
|
? DefaultStyle.grey_0
|
||||||
: DefaultStyle.grey_400
|
: DefaultStyle.grey_400
|
||||||
|
|
@ -199,9 +199,20 @@ Control.Button {
|
||||||
}
|
}
|
||||||
Component{
|
Component{
|
||||||
id: imageComponent
|
id: imageComponent
|
||||||
ButtonImage{
|
Item {
|
||||||
width: stacklayout.width
|
width: stacklayout.width
|
||||||
height: stacklayout.height
|
height: stacklayout.height
|
||||||
|
ButtonImage {
|
||||||
|
id: buttonIcon
|
||||||
|
anchors.fill: parent
|
||||||
|
}
|
||||||
|
ButtonImage {
|
||||||
|
z: buttonIcon.z + 1
|
||||||
|
visible: !mainItem.enabled
|
||||||
|
anchors.fill: parent
|
||||||
|
colorizationColor: DefaultStyle.grey_0
|
||||||
|
opacity: 0.5
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Component{
|
Component{
|
||||||
|
|
|
||||||
|
|
@ -12,8 +12,8 @@ Button {
|
||||||
// bottomPadding: Math.round(16 * DefaultStyle.dp)
|
// bottomPadding: Math.round(16 * DefaultStyle.dp)
|
||||||
// leftPadding: Math.round(16 * DefaultStyle.dp)
|
// leftPadding: Math.round(16 * DefaultStyle.dp)
|
||||||
// rightPadding: Math.round(16 * DefaultStyle.dp)
|
// rightPadding: Math.round(16 * DefaultStyle.dp)
|
||||||
icon.width: width
|
// icon.width: width
|
||||||
icon.height: width
|
// icon.height: width
|
||||||
radius: width * 2
|
radius: width * 2
|
||||||
// width: Math.round(24 * DefaultStyle.dp)
|
// width: Math.round(24 * DefaultStyle.dp)
|
||||||
height: width
|
height: width
|
||||||
|
|
|
||||||
|
|
@ -7,41 +7,45 @@ import UtilsCpp
|
||||||
|
|
||||||
// =============================================================================
|
// =============================================================================
|
||||||
|
|
||||||
Loader{
|
Item {
|
||||||
id: mainItem
|
id: mainItem
|
||||||
property ChatMessageContentGui chatMessageContentGui
|
property ChatMessageContentGui chatMessageContentGui
|
||||||
property int availableWidth : parent.width
|
property var chatMessageObj
|
||||||
|
property ChatMessageGui chatMessage: chatMessageObj && chatMessageObj.value || null
|
||||||
// property string filePath : tempFile.filePath
|
property bool isPlaying : soudPlayerLoader.item && soudPlayerLoader.item.core.playbackState === LinphoneEnums.PlaybackState.PlayingState
|
||||||
|
|
||||||
active: chatMessageContentGui && chatMessageContentGui.core.isVoiceRecording
|
|
||||||
|
|
||||||
// onChatMessageContentGuiChanged: if(chatMessageContentGui){
|
|
||||||
// tempFile.createFileFromContentModel(chatMessageContentGui, false);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// TemporaryFile {
|
|
||||||
// id: tempFile
|
|
||||||
// }
|
|
||||||
|
|
||||||
sourceComponent: Item {
|
|
||||||
id: loadedItem
|
|
||||||
property bool isPlaying : soundPlayerGui && soundPlayerGui.core.playbackState === LinphoneEnums.PlaybackState.PlayingState
|
|
||||||
onIsPlayingChanged: isPlaying ? mediaProgressBar.resume() : mediaProgressBar.stop()
|
onIsPlayingChanged: isPlaying ? mediaProgressBar.resume() : mediaProgressBar.stop()
|
||||||
|
property bool recording: false
|
||||||
|
property RecorderGui recorderGui: recorderLoader.item || null
|
||||||
|
|
||||||
width: mainItem.width
|
signal voiceRecordingMessageCreationRequested(RecorderGui recorderGui)
|
||||||
height: mainItem.height
|
signal stopRecording()
|
||||||
|
|
||||||
clip: false
|
function createVoiceMessageInChat(chat) {
|
||||||
|
if (recorderLoader.item) {
|
||||||
|
mainItem.chatMessageObj = UtilsCpp.createVoiceRecordingMessage(recorderLoader.item, chat)
|
||||||
|
} else {
|
||||||
|
//: Error
|
||||||
|
UtilsCpp.showInformationPopup(qsTr("information_popup_error_title"),
|
||||||
|
//: Failed to create voice message : error in recorder
|
||||||
|
qsTr("information_popup_voice_message_error_message"), false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
SoundPlayerGui {
|
Loader {
|
||||||
|
id: soudPlayerLoader
|
||||||
|
property int duration: mainItem.chatMessageContentGui
|
||||||
|
? mainItem.chatMessageContentGui.core.fileDuration
|
||||||
|
: item
|
||||||
|
? item.core.duration
|
||||||
|
: 0
|
||||||
|
property int position: item?.core.position || 0
|
||||||
|
active: mainItem.chatMessageContentGui && mainItem.chatMessageContentGui.core.isVoiceRecording
|
||||||
|
sourceComponent: SoundPlayerGui {
|
||||||
id: soundPlayerGui
|
id: soundPlayerGui
|
||||||
property int duration: mainItem.chatMessageContentGui ? mainItem.chatMessageContentGui.core.fileDuration : core.duration
|
|
||||||
property int position: core.position
|
|
||||||
source: mainItem.chatMessageContentGui && mainItem.chatMessageContentGui.core.filePath
|
source: mainItem.chatMessageContentGui && mainItem.chatMessageContentGui.core.filePath
|
||||||
|
|
||||||
function play(){
|
function play(){
|
||||||
if(loadedItem.isPlaying){// Pause the play
|
if(mainItem.isPlaying){// Pause the play
|
||||||
soundPlayerGui.core.lPause()
|
soundPlayerGui.core.lPause()
|
||||||
}else{// Play the audio
|
}else{// Play the audio
|
||||||
soundPlayerGui.core.lPlay()
|
soundPlayerGui.core.lPlay()
|
||||||
|
|
@ -51,40 +55,81 @@ Loader{
|
||||||
mediaProgressBar.value = 101
|
mediaProgressBar.value = 101
|
||||||
}
|
}
|
||||||
onPositionChanged: {
|
onPositionChanged: {
|
||||||
mediaProgressBar.progressPosition = position
|
mediaProgressBar.progressPosition = soudPlayerLoader.position
|
||||||
mediaProgressBar.value = 100 * ( mediaProgressBar.progressPosition / duration)
|
mediaProgressBar.value = 100 * ( mediaProgressBar.progressPosition / soudPlayerLoader.duration)
|
||||||
}
|
}
|
||||||
onSourceChanged: if (source != "") {
|
onSourceChanged: if (source != "") {
|
||||||
// core.lPlay()// This will open the file and allow seeking
|
core.lOpen() // Open the file and allow seeking
|
||||||
// core.lPause()
|
|
||||||
core.lOpen()
|
|
||||||
mediaProgressBar.value = 0
|
mediaProgressBar.value = 0
|
||||||
mediaProgressBar.refresh()
|
mediaProgressBar.refresh()
|
||||||
}
|
}
|
||||||
|
onErrorChanged: (error) => {
|
||||||
|
//: Error
|
||||||
|
UtilsCpp.showInformationPopup(qsTr("information_popup_error_title"), error, false)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loader {
|
||||||
|
id: recorderLoader
|
||||||
|
active: mainItem.recording && !mainItem.chatMessageContentGui
|
||||||
|
property int duration: item?.core.duration || 0
|
||||||
|
property int captureVolume: item?.core.captureVolume || 0
|
||||||
|
property var state: item?.core.state
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
target: mainItem
|
||||||
|
function onStopRecording() {
|
||||||
|
recorderLoader.item.core.lStop()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sourceComponent: RecorderGui {
|
||||||
|
id: recorderGui
|
||||||
|
onReady: core.lStart()
|
||||||
|
onStateChanged: (state) => {
|
||||||
|
if (state === LinphoneEnums.RecorderState.Running) mediaProgressBar.start()
|
||||||
|
if (state === LinphoneEnums.RecorderState.Closed) {
|
||||||
|
mediaProgressBar.stop()
|
||||||
|
mainItem.voiceRecordingMessageCreationRequested(recorderGui)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
MediaProgressBar{
|
MediaProgressBar{
|
||||||
id: mediaProgressBar
|
id: mediaProgressBar
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
progressDuration: soundPlayerGui ? soundPlayerGui.duration : chatMessageContentGui.core.fileDuration
|
progressDuration: soudPlayerLoader.active
|
||||||
|
? soudPlayerLoader.duration
|
||||||
|
: recorderLoader
|
||||||
|
? recorderLoader.duration
|
||||||
|
: chatMessageContentGui.core.fileDuration
|
||||||
progressPosition: 0
|
progressPosition: 0
|
||||||
value: 0
|
value: 0
|
||||||
|
recording: recorderLoader.state === LinphoneEnums.RecorderState.Running
|
||||||
function refresh(){
|
function refresh(){
|
||||||
if(soundPlayerGui){
|
if(soudPlayerLoader.item){
|
||||||
soundPlayerGui.core.lRefreshPosition()
|
soudPlayerLoader.item.core.lRefreshPosition()
|
||||||
|
} else if (recorderLoader.item) {
|
||||||
|
recorderLoader.item.core.lRefresh()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
onEndReached:{
|
onEndReached:{
|
||||||
if(soundPlayerGui)
|
if(soudPlayerLoader.item)
|
||||||
soundPlayerGui.core.lStop()
|
soudPlayerLoader.item.core.lStop()
|
||||||
|
}
|
||||||
|
onPlayStopButtonToggled: {
|
||||||
|
if(soudPlayerLoader.item) {
|
||||||
|
soudPlayerLoader.item.play()
|
||||||
|
} else if (recorderLoader.item) {
|
||||||
|
recorderLoader.item.core.lStop()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
onPlayStopButtonToggled: soundPlayerGui.play()
|
|
||||||
onRefreshPositionRequested: refresh()
|
onRefreshPositionRequested: refresh()
|
||||||
onSeekRequested: (ms) => {
|
onSeekRequested: (ms) => {
|
||||||
if(soundPlayerGui) {
|
if(soudPlayerLoader.active) {
|
||||||
soundPlayerGui.core.lSeek(ms)
|
soudPlayerLoader.item.core.lSeek(ms)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -342,9 +342,9 @@ ListView {
|
||||||
spacing: Math.round(10 * DefaultStyle.dp)
|
spacing: Math.round(10 * DefaultStyle.dp)
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
onClicked: {
|
onClicked: {
|
||||||
//: Delete the chat ?
|
//: Delete the conversation ?
|
||||||
mainWindow.showConfirmationLambdaPopup(qsTr("chat_list_delete_chat_popup_title"),
|
mainWindow.showConfirmationLambdaPopup(qsTr("chat_list_delete_chat_popup_title"),
|
||||||
//: This chat and all its messages will be deleted. Do You want to continue ?
|
//: This conversation and all its messages will be deleted. Do You want to continue ?
|
||||||
qsTr("chat_list_delete_chat_popup_message"),
|
qsTr("chat_list_delete_chat_popup_message"),
|
||||||
"",
|
"",
|
||||||
function(confirmed) {
|
function(confirmed) {
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,7 @@ ColumnLayout {
|
||||||
// VOICE MESSAGES
|
// VOICE MESSAGES
|
||||||
Repeater {
|
Repeater {
|
||||||
id: messagesVoicesList
|
id: messagesVoicesList
|
||||||
visible: mainItem.chatMessageGui.core.isVoiceRecording && count > 0
|
visible: count > 0
|
||||||
model: ChatMessageContentProxy{
|
model: ChatMessageContentProxy{
|
||||||
filterType: ChatMessageContentProxy.FilterContentType.Voice
|
filterType: ChatMessageContentProxy.FilterContentType.Voice
|
||||||
chatMessageGui: mainItem.chatMessageGui
|
chatMessageGui: mainItem.chatMessageGui
|
||||||
|
|
|
||||||
|
|
@ -60,7 +60,6 @@ ProgressBar {
|
||||||
mainItem.value = 100// Stay at 100
|
mainItem.value = 100// Stay at 100
|
||||||
progressPosition = progressDuration
|
progressPosition = progressDuration
|
||||||
}
|
}
|
||||||
console.log("end reached")
|
|
||||||
mainItem.endReached()
|
mainItem.endReached()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -128,8 +127,7 @@ ProgressBar {
|
||||||
onClicked: {
|
onClicked: {
|
||||||
mainItem.playStopButtonToggled()
|
mainItem.playStopButtonToggled()
|
||||||
}
|
}
|
||||||
borderColor: "transparent"
|
style: ButtonStyle.player
|
||||||
style: ButtonStyle.secondary
|
|
||||||
}
|
}
|
||||||
Control.Control {
|
Control.Control {
|
||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
|
|
@ -150,6 +148,8 @@ ProgressBar {
|
||||||
visible: mainItem.recording
|
visible: mainItem.recording
|
||||||
colorizationColor: DefaultStyle.danger_500main
|
colorizationColor: DefaultStyle.danger_500main
|
||||||
imageSource: AppIcons.recordFill
|
imageSource: AppIcons.recordFill
|
||||||
|
Layout.preferredWidth: Math.round(14 * DefaultStyle.dp)
|
||||||
|
Layout.preferredHeight: Math.round(14 * DefaultStyle.dp)
|
||||||
}
|
}
|
||||||
Text {
|
Text {
|
||||||
id: durationText
|
id: durationText
|
||||||
|
|
|
||||||
|
|
@ -11,23 +11,24 @@ import 'qrc:/qt/qml/Linphone/view/Control/Tool/Helper/utils.js' as Utils
|
||||||
Control.Control {
|
Control.Control {
|
||||||
id: mainItem
|
id: mainItem
|
||||||
|
|
||||||
property alias placeholderText: sendingTextArea.placeholderText
|
// property alias placeholderText: sendingTextArea.placeholderText
|
||||||
property alias text: sendingTextArea.text
|
property string text
|
||||||
property alias textArea: sendingTextArea
|
property var textArea
|
||||||
property alias cursorPosition: sendingTextArea.cursorPosition
|
// property alias cursorPosition: sendingTextArea.cursorPosition
|
||||||
property alias emojiPickerButtonChecked: emojiPickerButton.checked
|
property bool emojiPickerButtonChecked
|
||||||
|
|
||||||
property bool dropEnabled: true
|
property bool dropEnabled: true
|
||||||
property string dropDisabledReason
|
property string dropDisabledReason
|
||||||
property bool isEphemeral : false
|
property bool isEphemeral : false
|
||||||
property bool emojiVisible: false
|
property bool emojiVisible: false
|
||||||
|
|
||||||
|
property ChatGui chat
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
signal dropped (var files)
|
signal dropped (var files)
|
||||||
signal validText (string text)
|
signal validText (string text)
|
||||||
signal sendText()
|
signal sendMessage()
|
||||||
signal audioRecordRequest()
|
|
||||||
signal emojiClicked()
|
signal emojiClicked()
|
||||||
signal composing()
|
signal composing()
|
||||||
|
|
||||||
|
|
@ -63,25 +64,31 @@ Control.Control {
|
||||||
background: Rectangle {
|
background: Rectangle {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
color: DefaultStyle.grey_100
|
color: DefaultStyle.grey_100
|
||||||
MediumButton {
|
|
||||||
id: expandButton
|
|
||||||
anchors.top: parent.top
|
|
||||||
anchors.topMargin: Math.round(4 * DefaultStyle.dp)
|
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
|
||||||
style: ButtonStyle.noBackgroundOrange
|
|
||||||
icon.source: checked ? AppIcons.downArrow : AppIcons.upArrow
|
|
||||||
checkable: true
|
|
||||||
}
|
}
|
||||||
}
|
contentItem: Control.StackView {
|
||||||
contentItem: RowLayout {
|
id: sendingAreaStackView
|
||||||
spacing: Math.round(20 * DefaultStyle.dp)
|
initialItem: textAreaComp
|
||||||
|
Component {
|
||||||
|
id: textAreaComp
|
||||||
RowLayout {
|
RowLayout {
|
||||||
|
// disable record button if call ongoing
|
||||||
|
CallProxy {
|
||||||
|
id: callsModel
|
||||||
|
sourceModel: AppCpp.calls
|
||||||
|
}
|
||||||
spacing: Math.round(16 * DefaultStyle.dp)
|
spacing: Math.round(16 * DefaultStyle.dp)
|
||||||
BigButton {
|
BigButton {
|
||||||
id: emojiPickerButton
|
id: emojiPickerButton
|
||||||
style: ButtonStyle.noBackground
|
style: ButtonStyle.noBackground
|
||||||
checkable: true
|
checkable: true
|
||||||
icon.source: checked ? AppIcons.closeX : AppIcons.smiley
|
icon.source: checked ? AppIcons.closeX : AppIcons.smiley
|
||||||
|
onCheckedChanged: mainItem.emojiPickerButtonChecked = checked
|
||||||
|
Connections {
|
||||||
|
target: mainItem
|
||||||
|
function onEmojiPickerButtonCheckedChanged() {
|
||||||
|
emojiPickerButton.checked = mainItem.emojiPickerButtonChecked
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
BigButton {
|
BigButton {
|
||||||
style: ButtonStyle.noBackground
|
style: ButtonStyle.noBackground
|
||||||
|
|
@ -110,16 +117,9 @@ Control.Control {
|
||||||
contentItem: RowLayout {
|
contentItem: RowLayout {
|
||||||
Flickable {
|
Flickable {
|
||||||
id: sendingAreaFlickable
|
id: sendingAreaFlickable
|
||||||
Layout.fillWidth: true
|
|
||||||
Layout.preferredHeight: Math.min(Math.round(60 * DefaultStyle.dp), contentHeight)
|
Layout.preferredHeight: Math.min(Math.round(60 * DefaultStyle.dp), contentHeight)
|
||||||
Binding {
|
|
||||||
target: sendingAreaFlickable
|
|
||||||
when: expandButton.checked
|
|
||||||
property: "Layout.preferredHeight"
|
|
||||||
value: Math.round(250 * DefaultStyle.dp)
|
|
||||||
restoreMode: Binding.RestoreBindingOrValue
|
|
||||||
}
|
|
||||||
Layout.fillHeight: true
|
Layout.fillHeight: true
|
||||||
|
Layout.fillWidth: true
|
||||||
contentHeight: sendingTextArea.contentHeight
|
contentHeight: sendingTextArea.contentHeight
|
||||||
contentWidth: width
|
contentWidth: width
|
||||||
|
|
||||||
|
|
@ -139,6 +139,8 @@ Control.Control {
|
||||||
width: sendingAreaFlickable.width
|
width: sendingAreaFlickable.width
|
||||||
height: sendingAreaFlickable.height
|
height: sendingAreaFlickable.height
|
||||||
textFormat: TextEdit.AutoText
|
textFormat: TextEdit.AutoText
|
||||||
|
onTextChanged: mainItem.text = text
|
||||||
|
Component.onCompleted: mainItem.textArea = sendingTextArea
|
||||||
//: Say something… : placeholder text for sending message text area
|
//: Say something… : placeholder text for sending message text area
|
||||||
placeholderText: qsTr("chat_view_send_area_placeholder_text")
|
placeholderText: qsTr("chat_view_send_area_placeholder_text")
|
||||||
placeholderTextColor: DefaultStyle.main2_400
|
placeholderTextColor: DefaultStyle.main2_400
|
||||||
|
|
@ -152,21 +154,36 @@ Control.Control {
|
||||||
Keys.onPressed: (event) => {
|
Keys.onPressed: (event) => {
|
||||||
if ((event.key == Qt.Key_Enter || event.key == Qt.Key_Return))
|
if ((event.key == Qt.Key_Enter || event.key == Qt.Key_Return))
|
||||||
if(!(event.modifiers & Qt.ShiftModifier)) {
|
if(!(event.modifiers & Qt.ShiftModifier)) {
|
||||||
mainItem.sendText()
|
mainItem.sendMessage()
|
||||||
event.accepted = true
|
event.accepted = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Connections {
|
||||||
|
target: mainItem
|
||||||
|
function onTextChanged() {
|
||||||
|
if (mainItem.text !== text) text = mainItem.text
|
||||||
|
}
|
||||||
|
function onSendMessage() {
|
||||||
|
sendingTextArea.clear()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
RowLayout {
|
RowLayout {
|
||||||
id: stackButton
|
id: stackButton
|
||||||
spacing: 0
|
spacing: 0
|
||||||
BigButton {
|
BigButton {
|
||||||
|
id: recordButton
|
||||||
|
enabled: !callsModel.currentCall
|
||||||
|
ToolTip.visible: !enabled && hovered
|
||||||
|
//: Cannot record a message while a call is ongoing
|
||||||
|
ToolTip.text: qsTr("cannot_record_while_in_call_tooltip")
|
||||||
visible: sendingTextArea.text.length === 0
|
visible: sendingTextArea.text.length === 0
|
||||||
style: ButtonStyle.noBackground
|
style: ButtonStyle.noBackground
|
||||||
|
hoverEnabled: true
|
||||||
icon.source: AppIcons.microphone
|
icon.source: AppIcons.microphone
|
||||||
onClicked: {
|
onClicked: {
|
||||||
console.log("TODO : go to record message")
|
sendingAreaStackView.push(voiceMessageRecordComp)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
BigButton {
|
BigButton {
|
||||||
|
|
@ -174,7 +191,69 @@ Control.Control {
|
||||||
style: ButtonStyle.noBackgroundOrange
|
style: ButtonStyle.noBackgroundOrange
|
||||||
icon.source: AppIcons.paperPlaneRight
|
icon.source: AppIcons.paperPlaneRight
|
||||||
onClicked: {
|
onClicked: {
|
||||||
mainItem.sendText()
|
mainItem.sendMessage()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Component {
|
||||||
|
id: voiceMessageRecordComp
|
||||||
|
RowLayout {
|
||||||
|
spacing: Math.round(16 * DefaultStyle.dp)
|
||||||
|
RoundButton {
|
||||||
|
style: ButtonStyle.player
|
||||||
|
shadowEnabled: true
|
||||||
|
padding: Math.round(4 * DefaultStyle.dp)
|
||||||
|
icon.width: Math.round(22 * DefaultStyle.dp)
|
||||||
|
icon.height: Math.round(22 * DefaultStyle.dp)
|
||||||
|
icon.source: AppIcons.closeX
|
||||||
|
width: Math.round(30 * DefaultStyle.dp)
|
||||||
|
Layout.preferredWidth: width
|
||||||
|
Layout.preferredHeight: height
|
||||||
|
onClicked: {
|
||||||
|
if (voiceMessage.chatMessage) mainItem.chat.core.lDeleteMessage(voiceMessage.chatMessage)
|
||||||
|
sendingAreaStackView.pop()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ChatAudioContent {
|
||||||
|
id: voiceMessage
|
||||||
|
recording: true
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.preferredHeight: Math.round(48 * DefaultStyle.dp)
|
||||||
|
chatMessageContentGui: chatMessage ? chatMessage.core.getVoiceRecordingContent() : null
|
||||||
|
onVoiceRecordingMessageCreationRequested: (recorderGui) => {
|
||||||
|
chatMessageObj = UtilsCpp.createVoiceRecordingMessage(recorderGui, mainItem.chat)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
BigButton {
|
||||||
|
id: sendButton
|
||||||
|
style: ButtonStyle.noBackgroundOrange
|
||||||
|
icon.source: AppIcons.paperPlaneRight
|
||||||
|
icon.width: Math.round(22 * DefaultStyle.dp)
|
||||||
|
icon.height: Math.round(22 * DefaultStyle.dp)
|
||||||
|
// Layout.preferredWidth: icon.width
|
||||||
|
// Layout.preferredHeight: icon.height
|
||||||
|
property bool sendVoiceRecordingOnCreated: false
|
||||||
|
onClicked: {
|
||||||
|
if (voiceMessage.chatMessage) {
|
||||||
|
voiceMessage.chatMessage.core.lSend()
|
||||||
|
sendingAreaStackView.pop()
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
sendVoiceRecordingOnCreated = true
|
||||||
|
voiceMessage.stopRecording()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Connections {
|
||||||
|
target: voiceMessage
|
||||||
|
function onChatMessageChanged() {
|
||||||
|
if (sendButton.sendVoiceRecordingOnCreated) {
|
||||||
|
voiceMessage.chatMessage.core.lSend()
|
||||||
|
sendButton.sendVoiceRecordingOnCreated = false
|
||||||
|
sendingAreaStackView.pop()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,7 @@ RowLayout {
|
||||||
}
|
}
|
||||||
|
|
||||||
onGroupCall: {
|
onGroupCall: {
|
||||||
mainWindow.showConfirmationLambdaPopup(qsTr(""),
|
mainWindow.showConfirmationLambdaPopup("",
|
||||||
qsTr("chat_view_group_call_toast_message"),
|
qsTr("chat_view_group_call_toast_message"),
|
||||||
"",
|
"",
|
||||||
function(confirmed) {
|
function(confirmed) {
|
||||||
|
|
@ -115,9 +115,16 @@ RowLayout {
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
content: ColumnLayout {
|
content: Control.SplitView {
|
||||||
spacing: 0
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
|
orientation: Qt.Vertical
|
||||||
|
handle: Rectangle {
|
||||||
|
implicitHeight: Math.round(8 * DefaultStyle.dp)
|
||||||
|
color: Control.SplitHandle.hovered ? DefaultStyle.grey_200 : DefaultStyle.grey_100
|
||||||
|
}
|
||||||
|
ColumnLayout {
|
||||||
|
spacing: 0
|
||||||
|
Control.SplitView.fillHeight: true
|
||||||
Item {
|
Item {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.fillHeight: true
|
Layout.fillHeight: true
|
||||||
|
|
@ -257,11 +264,14 @@ RowLayout {
|
||||||
anchors.right: selectedFilesArea.right
|
anchors.right: selectedFilesArea.right
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
ChatDroppableTextArea {
|
ChatDroppableTextArea {
|
||||||
id: messageSender
|
id: messageSender
|
||||||
Layout.fillWidth: true
|
Control.SplitView.preferredHeight: mainItem.chat.core.isReadOnly ? 0 : Math.round(79 * DefaultStyle.dp)
|
||||||
Layout.preferredHeight: mainItem.chat.core.isReadOnly ? 0 : height
|
Control.SplitView.minimumHeight: mainItem.chat.core.isReadOnly ? 0 : Math.round(79 * DefaultStyle.dp)
|
||||||
|
chat: mainItem.chat
|
||||||
Component.onCompleted: {
|
Component.onCompleted: {
|
||||||
|
|
||||||
if (mainItem.chat) text = mainItem.chat.core.sendingText
|
if (mainItem.chat) text = mainItem.chat.core.sendingText
|
||||||
}
|
}
|
||||||
onTextChanged: {
|
onTextChanged: {
|
||||||
|
|
@ -270,12 +280,11 @@ RowLayout {
|
||||||
}
|
}
|
||||||
mainItem.chat.core.sendingText = text
|
mainItem.chat.core.sendingText = text
|
||||||
}
|
}
|
||||||
onSendText: {
|
onSendMessage: {
|
||||||
var filesContents = contents.getAll()
|
var filesContents = contents.getAll()
|
||||||
if (filesContents.length === 0)
|
if (filesContents.length === 0)
|
||||||
mainItem.chat.core.lSendTextMessage(text)
|
mainItem.chat.core.lSendTextMessage(text)
|
||||||
else mainItem.chat.core.lSendMessage(text, filesContents)
|
else mainItem.chat.core.lSendMessage(text, filesContents)
|
||||||
messageSender.textArea.clear()
|
|
||||||
contents.clear()
|
contents.clear()
|
||||||
}
|
}
|
||||||
onDropped: (files) => {
|
onDropped: (files) => {
|
||||||
|
|
|
||||||
39
Linphone/view/Page/Layout/Settings/ChatSettingsLayout.qml
Normal file
39
Linphone/view/Page/Layout/Settings/ChatSettingsLayout.qml
Normal file
|
|
@ -0,0 +1,39 @@
|
||||||
|
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Layouts
|
||||||
|
import QtQuick.Controls.Basic as Control
|
||||||
|
import SettingsCpp
|
||||||
|
import Linphone
|
||||||
|
|
||||||
|
AbstractSettingsLayout {
|
||||||
|
id: mainItem
|
||||||
|
width: parent?.width
|
||||||
|
contentModel: [
|
||||||
|
{
|
||||||
|
//: Attached files
|
||||||
|
title: qsTr("settings_chat_attached_files_title"),
|
||||||
|
subTitle: "",
|
||||||
|
contentComponent: attachedFilesParamComp,
|
||||||
|
// hideTopMargin: true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
Component {
|
||||||
|
id: attachedFilesParamComp
|
||||||
|
SwitchSetting {
|
||||||
|
//: "Automatic download"
|
||||||
|
titleText: qsTr("settings_chat_attached_files_auto_download_title")
|
||||||
|
//: "Automatically download transferred or received files in conversations"
|
||||||
|
subTitleText: qsTr("settings_chat_attached_files_auto_download_subtitle")
|
||||||
|
propertyName: "autoDownloadReceivedFiles"
|
||||||
|
propertyOwner: SettingsCpp
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
target: mainItem
|
||||||
|
function onSave() {
|
||||||
|
SettingsCpp.save()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Layouts
|
import QtQuick.Layouts
|
||||||
import QtQuick.Controls.Basic as Control
|
import QtQuick.Controls.Basic as Control
|
||||||
import SettingsCpp 1.0
|
import SettingsCpp
|
||||||
import Linphone
|
import Linphone
|
||||||
|
|
||||||
AbstractSettingsLayout {
|
AbstractSettingsLayout {
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
pragma Singleton
|
pragma Singleton
|
||||||
import QtQuick
|
import QtQuick
|
||||||
|
import Linphone
|
||||||
|
|
||||||
QtObject {
|
QtObject {
|
||||||
property color main1_100: "#FFEACB"
|
property color main1_100: "#FFEACB"
|
||||||
|
|
|
||||||
|
|
@ -38,6 +38,23 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// White with orange icon
|
||||||
|
var player = {
|
||||||
|
color: {
|
||||||
|
normal: Linphone.DefaultStyle.grey_0,
|
||||||
|
hovered: Linphone.DefaultStyle.main1_100,
|
||||||
|
pressed: Linphone.DefaultStyle.main1_500_main
|
||||||
|
},
|
||||||
|
text: {
|
||||||
|
normal: Linphone.DefaultStyle.main1_500_main,
|
||||||
|
pressed: Linphone.DefaultStyle.main1_500_main
|
||||||
|
},
|
||||||
|
image: {
|
||||||
|
normal: Linphone.DefaultStyle.main1_500_main,
|
||||||
|
pressed: Linphone.DefaultStyle.main1_500_main
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Light orange
|
// Light orange
|
||||||
var tertiary = {
|
var tertiary = {
|
||||||
color: {
|
color: {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue