diff --git a/Linphone/core/call/CallCore.cpp b/Linphone/core/call/CallCore.cpp index 1dcec141d..319b7fef3 100644 --- a/Linphone/core/call/CallCore.cpp +++ b/Linphone/core/call/CallCore.cpp @@ -58,6 +58,9 @@ CallCore::CallCore(const std::shared_ptr &call) : QObject(nullpt encryption == LinphoneEnums::MediaEncryption::Dtls; mPaused = mState == LinphoneEnums::CallState::Pausing || mState == LinphoneEnums::CallState::Paused || mState == LinphoneEnums::CallState::PausedByRemote; + mRecording = call->getParams() && call->getParams()->isRecording(); + mRemoteRecording = call->getRemoteParams() && call->getRemoteParams()->isRecording(); + mRecordable = mState == LinphoneEnums::CallState::StreamsRunning; } CallCore::~CallCore() { @@ -87,6 +90,19 @@ void CallCore::setSelf(QSharedPointer me) { mAccountModelConnection->makeConnectToCore(&CallCore::lSetCameraEnabled, [this](bool enabled) { mAccountModelConnection->invokeToModel([this, enabled]() { mCallModel->setCameraEnabled(enabled); }); }); + mAccountModelConnection->makeConnectToCore(&CallCore::lStartRecording, [this]() { + mAccountModelConnection->invokeToModel([this]() { mCallModel->startRecording(); }); + }); + mAccountModelConnection->makeConnectToCore(&CallCore::lStopRecording, [this]() { + mAccountModelConnection->invokeToModel([this]() { mCallModel->stopRecording(); }); + }); + mAccountModelConnection->makeConnectToModel(&CallModel::recordingChanged, [this](bool recording) { + mAccountModelConnection->invokeToCore([this, recording]() { setRecording(recording); }); + }); + mAccountModelConnection->makeConnectToModel( + &CallModel::remoteRecording, [this](const std::shared_ptr &call, bool recording) { + mAccountModelConnection->invokeToCore([this, recording]() { setRemoteRecording(recording); }); + }); mAccountModelConnection->makeConnectToModel(&CallModel::cameraEnabledChanged, [this](bool enabled) { mAccountModelConnection->invokeToCore([this, enabled]() { setCameraEnabled(enabled); }); }); @@ -102,6 +118,12 @@ void CallCore::setSelf(QSharedPointer me) { mAccountModelConnection->makeConnectToModel(&CallModel::statusChanged, [this](linphone::Call::Status status) { mAccountModelConnection->invokeToCore([this, status]() { setStatus(LinphoneEnums::fromLinphone(status)); }); }); + mAccountModelConnection->makeConnectToModel(&CallModel::stateChanged, + [this](linphone::Call::State state, const std::string &message) { + mAccountModelConnection->invokeToCore([this, state]() { + setRecordable(state == linphone::Call::State::StreamsRunning); + }); + }); mAccountModelConnection->makeConnectToCore(&CallCore::lSetPaused, [this](bool paused) { mAccountModelConnection->invokeToModel([this, paused]() { mCallModel->setPaused(paused); }); }); @@ -275,6 +297,36 @@ void CallCore::setRemoteVideoEnabled(bool enabled) { } } +bool CallCore::getRecording() const { + return mRecording; +} +void CallCore::setRecording(bool recording) { + if (mRecording != recording) { + mRecording = recording; + emit recordingChanged(); + } +} + +bool CallCore::getRemoteRecording() const { + return mRemoteRecording; +} +void CallCore::setRemoteRecording(bool recording) { + if (mRemoteRecording != recording) { + mRemoteRecording = recording; + emit remoteRecordingChanged(); + } +} + +bool CallCore::getRecordable() const { + return mRecordable; +} +void CallCore::setRecordable(bool recordable) { + if (mRecordable != recordable) { + mRecordable = recordable; + emit recordableChanged(); + } +} + LinphoneEnums::CallState CallCore::getTransferState() const { return mTransferState; } diff --git a/Linphone/core/call/CallCore.hpp b/Linphone/core/call/CallCore.hpp index 3f55ca7f1..360a0ec2e 100644 --- a/Linphone/core/call/CallCore.hpp +++ b/Linphone/core/call/CallCore.hpp @@ -45,6 +45,9 @@ class CallCore : public QObject, public AbstractObject { Q_PROPERTY(bool peerSecured READ getPeerSecured WRITE setPeerSecured NOTIFY peerSecuredChanged) Q_PROPERTY( bool remoteVideoEnabled READ getRemoteVideoEnabled WRITE setRemoteVideoEnabled NOTIFY remoteVideoEnabledChanged) + Q_PROPERTY(bool recording READ getRecording WRITE setRecording NOTIFY recordingChanged) + Q_PROPERTY(bool remoteRecording READ getRemoteRecording WRITE setRemoteRecording NOTIFY remoteRecordingChanged) + Q_PROPERTY(bool recordable READ getRecordable WRITE setRecordable NOTIFY recordableChanged) Q_PROPERTY(LinphoneEnums::CallState transferState READ getTransferState NOTIFY transferStateChanged) public: @@ -89,6 +92,15 @@ public: bool getRemoteVideoEnabled() const; void setRemoteVideoEnabled(bool enabled); + bool getRecording() const; + void setRecording(bool recording); + + bool getRemoteRecording() const; + void setRemoteRecording(bool recording); + + bool getRecordable() const; + void setRecordable(bool recordable); + LinphoneEnums::CallState getTransferState() const; void setTransferState(LinphoneEnums::CallState state, const QString &message); @@ -108,6 +120,9 @@ signals: void transferStateChanged(); void peerSecuredChanged(); void remoteVideoEnabledChanged(bool remoteVideoEnabled); + void recordingChanged(); + void remoteRecordingChanged(); + void recordableChanged(); // Linphone commands void lAccept(bool withVideo); // Accept an incoming call @@ -119,6 +134,8 @@ signals: void lSetCameraEnabled(bool enabled); void lSetPaused(bool paused); void lTransferCall(const QString &dest); + void lStartRecording(); + void lStopRecording(); /* TODO Q_INVOKABLE void acceptWithVideo(); @@ -133,13 +150,10 @@ signals: Q_INVOKABLE void rejectVideoRequest(); Q_INVOKABLE void takeSnapshot(); - Q_INVOKABLE void startRecording(); - Q_INVOKABLE void stopRecording(); Q_INVOKABLE void sendDtmf(const QString &dtmf); Q_INVOKABLE void verifyAuthenticationToken(bool verify); Q_INVOKABLE void updateStreams(); - Q_INVOKABLE void toggleSpeakerMute(); */ private: std::shared_ptr mCallModel; @@ -156,6 +170,9 @@ private: bool mCameraEnabled; bool mPaused = false; bool mRemoteVideoEnabled = false; + bool mRecording = false; + bool mRemoteRecording = false; + bool mRecordable = false; QSharedPointer> mAccountModelConnection; DECLARE_ABSTRACT_OBJECT diff --git a/Linphone/core/friend/FriendCore.cpp b/Linphone/core/friend/FriendCore.cpp index 3983941cb..a84c413e0 100644 --- a/Linphone/core/friend/FriendCore.cpp +++ b/Linphone/core/friend/FriendCore.cpp @@ -50,7 +50,10 @@ FriendCore::FriendCore(const std::shared_ptr &contact) : QObje name.empty() ? Utils::getDisplayName(mAddress)->getValue().toString() : Utils::coreStringToAppString(name); mStarred = contact->getStarred(); mIsSaved = true; - } else mIsSaved = false; + } else { + mIsSaved = false; + mStarred = false; + } } FriendCore::FriendCore(const FriendCore &friendCore) { @@ -60,6 +63,8 @@ FriendCore::FriendCore(const FriendCore &friendCore) { } FriendCore::~FriendCore() { + mustBeInMainThread("~" + getClassName()); + emit mFriendModel->removeListener(); } void FriendCore::setSelf(SafeSharedPointer me) { @@ -217,7 +222,6 @@ void FriendCore::remove() { void FriendCore::save() { // Save Values to model FriendCore *thisCopy = new FriendCore(*this); // Pointer to avoid multiple copies in lambdas - auto linphoneAddr = ToolModel::interpretUrl(mAddress); if (mFriendModel) { mFriendModelConnection->invokeToModel([this, thisCopy]() { // Copy values to avoid concurrency @@ -227,7 +231,8 @@ void FriendCore::save() { // Save Values to model mFriendModelConnection->invokeToCore([this]() { saved(); }); }); } else { - mCoreModelConnection->invokeToModel([this, thisCopy, linphoneAddr]() { + mCoreModelConnection->invokeToModel([this, thisCopy]() { + auto linphoneAddr = ToolModel::interpretUrl(mAddress); auto core = CoreModel::getInstance()->getCore(); auto contact = core->findFriend(linphoneAddr); auto friendExists = contact != nullptr; diff --git a/Linphone/core/friend/FriendInitialProxy.cpp b/Linphone/core/friend/FriendInitialProxy.cpp index fc3530e4e..d5b0d768e 100644 --- a/Linphone/core/friend/FriendInitialProxy.cpp +++ b/Linphone/core/friend/FriendInitialProxy.cpp @@ -58,7 +58,6 @@ bool FriendInitialProxy::filterAcceptsRow(int sourceRow, const QModelIndex &sour QRegularExpression search(mFilterText, QRegularExpression::CaseInsensitiveOption | QRegularExpression::UseUnicodePropertiesOption); auto friendData = sourceModel()->data(sourceModel()->index(sourceRow, 0, sourceParent)).value(); - auto name = friendData->getCore()->getName(); show = friendData->getCore()->getName().indexOf(search) == 0; } diff --git a/Linphone/core/friend/FriendInitialProxy.hpp b/Linphone/core/friend/FriendInitialProxy.hpp index 27083950b..e6857dc6f 100644 --- a/Linphone/core/friend/FriendInitialProxy.hpp +++ b/Linphone/core/friend/FriendInitialProxy.hpp @@ -35,7 +35,6 @@ class FriendInitialProxy : public SortFilterProxy, public AbstractObject { Q_OBJECT Q_PROPERTY(QString filterText READ getFilterText WRITE setFilterText NOTIFY filterTextChanged) - // Q_PROPERTY(QAbstractItemModel *sourceModel READ sourceModel WRITE setSourceModel NOTIFY sourceModelChanged) public: FriendInitialProxy(QObject *parent = Q_NULLPTR); @@ -52,7 +51,6 @@ signals: protected: virtual bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override; - // virtual bool lessThan(const QModelIndex &left, const QModelIndex &right) const override; QString mFilterText; QSharedPointer mSource; diff --git a/Linphone/data/CMakeLists.txt b/Linphone/data/CMakeLists.txt index 92f2fd935..24c451a77 100644 --- a/Linphone/data/CMakeLists.txt +++ b/Linphone/data/CMakeLists.txt @@ -36,6 +36,7 @@ list(APPEND _LINPHONEAPP_RC_FILES data/assistant/use-app-sip-account.rc "data/image/users-three-selected.svg" "data/image/noItemImage.svg" "data/image/dots-three-vertical.svg" + "data/image/more.svg" "data/image/plus-circle.svg" "data/image/microphone-stage.svg" "data/image/group-call.svg" @@ -64,6 +65,7 @@ list(APPEND _LINPHONEAPP_RC_FILES data/assistant/use-app-sip-account.rc "data/image/empty.svg" "data/image/heart.svg" "data/image/heart-fill.svg" + "data/image/record-fill.svg" data/shaders/roundEffect.vert.qsb data/shaders/roundEffect.frag.qsb diff --git a/Linphone/data/image/return_arrow.svg b/Linphone/data/image/return_arrow.svg deleted file mode 100644 index 8b1378917..000000000 --- a/Linphone/data/image/return_arrow.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/Linphone/model/call-history/CallHistoryModel.cpp b/Linphone/model/call-history/CallHistoryModel.cpp index 7c8fe0798..734308278 100644 --- a/Linphone/model/call-history/CallHistoryModel.cpp +++ b/Linphone/model/call-history/CallHistoryModel.cpp @@ -33,7 +33,6 @@ CallHistoryModel::CallHistoryModel(const std::shared_ptr &cal } CallHistoryModel::~CallHistoryModel() { - // qDebug() << "[CallHistoryModel] delete" << this; // mustBeInLinphoneThread("~" + getClassName()); } diff --git a/Linphone/model/call/CallModel.cpp b/Linphone/model/call/CallModel.cpp index 58c6aeb80..06fcf1b97 100644 --- a/Linphone/model/call/CallModel.cpp +++ b/Linphone/model/call/CallModel.cpp @@ -22,6 +22,7 @@ #include +#include "core/path/Paths.hpp" #include "model/core/CoreModel.hpp" #include "tool/Utils.hpp" @@ -49,6 +50,11 @@ void CallModel::accept(bool withVideo) { auto core = CoreModel::getInstance()->getCore(); auto params = core->createCallParams(mMonitor); params->enableVideo(withVideo); + params->setRecordFile( + Paths::getCapturesDirPath() + .append(Utils::generateSavedFilename(QString::fromStdString(mMonitor->getToAddress()->getUsername()), "")) + .append(".mkv") + .toStdString()); mMonitor->enableCamera(withVideo); // Answer with local call address. auto localAddress = mMonitor->getCallLog()->getLocalAddress(); @@ -116,6 +122,30 @@ void CallModel::setCameraEnabled(bool enabled) { emit cameraEnabledChanged(enabled); } +void CallModel::startRecording() { + mustBeInLinphoneThread(log().arg(Q_FUNC_INFO)); + mMonitor->startRecording(); + emit recordingChanged(mMonitor->getParams()->isRecording()); +} + +void CallModel::stopRecording() { + mustBeInLinphoneThread(log().arg(Q_FUNC_INFO)); + mMonitor->stopRecording(); + emit recordingChanged(mMonitor->getParams()->isRecording()); + // TODO : display notification +} + +void CallModel::setRecordFile(const std::string &path) { + auto core = CoreModel::getInstance()->getCore(); + auto params = core->createCallParams(mMonitor); + params->setRecordFile(path); + mMonitor->update(params); +} + +std::string CallModel::getRecordFile() const { + return mMonitor->getParams()->getRecordFile(); +} + std::shared_ptr CallModel::getRemoteAddress() { mustBeInLinphoneThread(log().arg(Q_FUNC_INFO)); return mMonitor->getRemoteAddress(); diff --git a/Linphone/model/call/CallModel.hpp b/Linphone/model/call/CallModel.hpp index b9cce7dbb..bc7f290ff 100644 --- a/Linphone/model/call/CallModel.hpp +++ b/Linphone/model/call/CallModel.hpp @@ -33,7 +33,7 @@ class CallModel : public ::Listener, public AbstractObject { Q_OBJECT public: - CallModel(const std::shared_ptr &account, QObject *parent = nullptr); + CallModel(const std::shared_ptr &call, QObject *parent = nullptr); ~CallModel(); void accept(bool withVideo); @@ -43,10 +43,15 @@ public: void setMicrophoneMuted(bool isMuted); void setSpeakerMuted(bool isMuted); void setCameraEnabled(bool enabled); + void startRecording(); + void stopRecording(); + void setRecordFile(const std::string &path); + void setPaused(bool paused); void transferTo(const std::shared_ptr &address); void terminateAllCalls(); + std::string getRecordFile() const; std::shared_ptr getRemoteAddress(); bool getAuthenticationTokenVerified(); @@ -57,6 +62,7 @@ signals: void durationChanged(int); void pausedChanged(bool paused); void remoteVideoEnabledChanged(bool remoteVideoEnabled); + void recordingChanged(bool recording); private: QTimer mDurationTimer; diff --git a/Linphone/model/tool/ToolModel.cpp b/Linphone/model/tool/ToolModel.cpp index 2f26d174f..4fd012cc6 100644 --- a/Linphone/model/tool/ToolModel.cpp +++ b/Linphone/model/tool/ToolModel.cpp @@ -20,6 +20,7 @@ #include "ToolModel.hpp" #include "core/App.hpp" +#include "core/path/Paths.hpp" #include "model/core/CoreModel.hpp" #include "tool/Utils.hpp" #include @@ -51,7 +52,10 @@ QString ToolModel::getDisplayName(const std::shared_ptr QString displayName; if (address) { displayName = Utils::coreStringToAppString(address->getDisplayName()); - if (displayName.isEmpty()) displayName = Utils::coreStringToAppString(address->getUsername()); + if (displayName.isEmpty()) { + displayName = Utils::coreStringToAppString(address->getUsername()); + displayName.replace('.', ' '); + } // TODO // std::shared_ptr cleanAddress = address->clone(); // cleanAddress->clean(); @@ -59,7 +63,6 @@ QString ToolModel::getDisplayName(const std::shared_ptr // auto sipAddressEntry = getSipAddressEntry(qtAddress, cleanAddress); // displayName = sipAddressEntry->displayNames.get(); } - displayName.replace('.', ' '); return displayName; } @@ -85,6 +88,14 @@ QSharedPointer ToolModel::createCall(const QString &sipAddress, std::shared_ptr params = core->createCallParams(nullptr); params->enableVideo(false); + if (Utils::coreStringToAppString(params->getRecordFile()).isEmpty()) { + + params->setRecordFile( + Paths::getCapturesDirPath() + .append(Utils::generateSavedFilename(QString::fromStdString(address->getUsername()), "")) + .append(".mkv") + .toStdString()); + } QHashIterator iterator(headers); while (iterator.hasNext()) { @@ -92,8 +103,8 @@ QSharedPointer ToolModel::createCall(const QString &sipAddress, params->addCustomHeader(Utils::appStringToCoreString(iterator.key()), Utils::appStringToCoreString(iterator.value())); } + if (core->getDefaultAccount()) params->setAccount(core->getDefaultAccount()); - // CallModel::setRecordFile(params, Utils::coreStringToAppString(address->getUsername())); auto call = core->inviteAddressWithParams(address, params); return call ? CallCore::create(call) : nullptr; diff --git a/Linphone/tool/Utils.cpp b/Linphone/tool/Utils.cpp index 700b10e6f..7cefb31ff 100644 --- a/Linphone/tool/Utils.cpp +++ b/Linphone/tool/Utils.cpp @@ -239,4 +239,16 @@ QString Utils::formatDateElapsedTime(const QDateTime &date) { auto s = dateSec - h * 3600 - m * 60; return QString::number(s) + " s"; +} + +QString Utils::generateSavedFilename(const QString &from, const QString &to) { + auto escape = [](const QString &str) { + constexpr char ReservedCharacters[] = "[<|>|:|\"|/|\\\\|\\?|\\*|\\+|\\||_|-]+"; + static QRegularExpression regexp(ReservedCharacters); + return QString(str).replace(regexp, ""); + }; + return QStringLiteral("%1_%2_%3") + .arg(QDateTime::currentDateTime().toString("yyyy-MM-dd_hh-mm-ss")) + .arg(escape(from)) + .arg(escape(to)); } \ No newline at end of file diff --git a/Linphone/tool/Utils.hpp b/Linphone/tool/Utils.hpp index f38746ab6..d465cd5b3 100644 --- a/Linphone/tool/Utils.hpp +++ b/Linphone/tool/Utils.hpp @@ -68,13 +68,14 @@ public: bool dotsSeparator = true); // Return the elapsed time formated Q_INVOKABLE static QString formatDate(const QDateTime &date, bool includeTime = true); // Return the date formated Q_INVOKABLE static QString formatDateElapsedTime(const QDateTime &date); // Return the date formated + static QString generateSavedFilename(const QString &from, const QString &to); static inline QString coreStringToAppString(const std::string &str) { if (Constants::LinphoneLocaleEncoding == QString("UTF-8")) return QString::fromStdString(str); else return QString::fromLocal8Bit(str.c_str(), - int(str.size())); // When using Locale. Be careful about conversion bijection - // with UTF-8, you may loss characters + int(str.size())); // When using Locale. Be careful about conversion + // bijection with UTF-8, you may loss characters } static inline std::string appStringToCoreString(const QString &str) { diff --git a/Linphone/view/App/CallsWindow.qml b/Linphone/view/App/CallsWindow.qml index 47df562d6..a1ec8046c 100644 --- a/Linphone/view/App/CallsWindow.qml +++ b/Linphone/view/App/CallsWindow.qml @@ -48,7 +48,8 @@ Window { } onClosing: (close) => { close.accepted = false - terminateAllCallsDialog.open() + if (callsModel.haveCall) + terminateAllCallsDialog.open() } Timer { @@ -69,41 +70,11 @@ Window { } } - Popup { + Dialog { id: terminateAllCallsDialog - modal: true - anchors.centerIn: parent - closePolicy: Control.Popup.NoAutoClose - padding: 10 * DefaultStyle.dp - contentItem: ColumnLayout { - height: terminateAllCallsDialog.height - width: 278 * DefaultStyle.dp - spacing: 8 * DefaultStyle.dp - Text { - text: qsTr("La fenêtre est sur le point d'être fermée. Cela terminera tous les appels en cours. Souhaitez vous continuer ?") - Layout.preferredWidth: parent.width - font { - pixelSize: 14 * DefaultStyle.dp - weight: 400 * DefaultStyle.dp - } - wrapMode: Text.Wrap - horizontalAlignment: Text.AlignHCenter - } - RowLayout { - Layout.alignment: Qt.AlignHCenter - Button { - text: qsTr("Oui") - onClicked: { - call.core.lTerminateAllCalls() - terminateAllCallsDialog.close() - } - } - Button { - text: qsTr("Non") - onClicked: terminateAllCallsDialog.close() - } - } - } + onAccepted: call.core.lTerminateAllCalls() + width: 278 * DefaultStyle.dp + text: qsTr("La fenêtre est sur le point d'être fermée. Cela terminera tous les appels en cours. Souhaitez vous continuer ?") } CallProxy{ @@ -127,11 +98,13 @@ Window { required property string enabledIcon property string disabledIcon enabled: call != undefined - padding: 18 * DefaultStyle.dp + leftPadding: 0 + rightPadding: 0 + topPadding: 0 + bottomPadding: 0 checkable: true background: Rectangle { anchors.fill: parent - RectangleTest{} color: bottomButton.enabled ? disabledIcon ? DefaultStyle.grey_500 @@ -142,10 +115,10 @@ Window { radius: 71 * DefaultStyle.dp } contentItem: EffectImage { - image.source: disabledIcon && bottomButton.checked ? disabledIcon : enabledIcon - anchors.fill: parent - image.width: 32 * DefaultStyle.dp - image.height: 32 * DefaultStyle.dp + source: disabledIcon && bottomButton.checked ? disabledIcon : enabledIcon + imageWidth: 32 * DefaultStyle.dp + imageHeight: 32 * DefaultStyle.dp + anchors.centerIn: parent colorizationColor: disabledIcon && bottomButton.checked ? DefaultStyle.main2_0 : DefaultStyle.grey_0 } } @@ -211,20 +184,20 @@ Window { Layout.fillWidth: true Layout.minimumHeight: 25 * DefaultStyle.dp RowLayout { + anchors.left: parent.left + anchors.right: parent.right anchors.verticalCenter: parent.verticalCenter spacing: 10 * DefaultStyle.dp EffectImage { id: callStatusIcon - image.fillMode: Image.PreserveAspectFit - image.width: 15 * DefaultStyle.dp - image.height: 15 * DefaultStyle.dp - image.sourceSize.width: 15 * DefaultStyle.dp - image.sourceSize.height: 15 * DefaultStyle.dp - image.source: mainWindow.call.core.paused - ? AppIcons.pause - : (mainWindow.call.core.state === LinphoneEnums.CallState.End + fillMode: Image.PreserveAspectFit + width: 15 * DefaultStyle.dp + height: 15 * DefaultStyle.dp + source:(mainWindow.call.core.state === LinphoneEnums.CallState.End || mainWindow.call.core.state === LinphoneEnums.CallState.Released) - ? AppIcons.endCall + ? AppIcons.endCall + : mainWindow.call.core.paused + ? AppIcons.pause : mainWindow.call.core.dir === LinphoneEnums.CallDir.Outgoing ? AppIcons.outgoingCall : AppIcons.incomingCall @@ -261,6 +234,23 @@ Window { visible: mainWindow.call.core.state === LinphoneEnums.CallState.Connected || mainWindow.call.core.state === LinphoneEnums.CallState.StreamsRunning } + Item { + Layout.fillWidth: true + } + RowLayout { + visible: mainWindow.call.core.recording || mainWindow.call.core.remoteRecording + Text { + color: DefaultStyle.danger_500main + font.pixelSize: 14 * DefaultStyle.dp + text: mainWindow.call.core.recording ? qsTr("Vous enregistrez l'appel") : qsTr("Votre correspondant enregistre l'appel") + } + EffectImage { + source: AppIcons.recordFill + colorizationColor: DefaultStyle.danger_500main + Layout.preferredWidth: 24 * DefaultStyle.dp + Layout.preferredHeight: 24 * DefaultStyle.dp + } + } } Control.Control { @@ -481,7 +471,7 @@ Window { visible: parent.visible closeButtonVisible: false onLaunchCall: { - var callVarObject = UtilsCpp.createCall(dialerTextInput.text + "@sip.linphone.org") + UtilsCpp.createCall(dialerTextInput.text + "@sip.linphone.org") } } } @@ -595,7 +585,7 @@ Window { background: Item {} contentItem: RowLayout { EffectImage { - image.source: AppIcons.endCall + source: AppIcons.endCall colorizationColor: DefaultStyle.danger_500main width: 32 * DefaultStyle.dp height: 32 * DefaultStyle.dp @@ -745,7 +735,6 @@ Window { Layout.preferredWidth: 55 * DefaultStyle.dp Layout.preferredHeight: 55 * DefaultStyle.dp onClicked: mainWindow.call.core.lSetCameraEnabled(!mainWindow.call.core.cameraEnabled) - } BottomButton { enabledIcon: AppIcons.microphone @@ -755,25 +744,27 @@ Window { Layout.preferredHeight: 55 * DefaultStyle.dp onClicked: mainWindow.call.core.lSetMicrophoneMuted(!mainWindow.call.core.microphoneMuted) } - BottomButton { + PopupButton { id: moreOptionsButton - checkable: true - enabledIcon: AppIcons.verticalDots Layout.preferredWidth: 55 * DefaultStyle.dp Layout.preferredHeight: 55 * DefaultStyle.dp - onPressed: { - moreOptionsMenu.visible = !moreOptionsMenu.visible + background: Rectangle { + anchors.fill: moreOptionsButton + color: moreOptionsButton.checked ? DefaultStyle.grey_0 : DefaultStyle.grey_500 + radius: 40 * DefaultStyle.dp } - } - Popup { - id: moreOptionsMenu - x: moreOptionsButton.x - y: moreOptionsButton.y - height - padding: 20 * DefaultStyle.dp - - // closePolicy: Control.Popup.CloseOnEscape - onAboutToHide: moreOptionsButton.checked = false - contentItem: ColumnLayout { + contentItem: Item { + EffectImage { + source: AppIcons.more + width: 24 * DefaultStyle.dp + height: 24 * DefaultStyle.dp + anchors.centerIn: parent + colorizationColor: moreOptionsButton.checked ? DefaultStyle.grey_500 : DefaultStyle.grey_0 + } + } + popup.x: width/2 + popup.y: y - popup.height + height/4 + popup.contentItem: ColumnLayout { id: optionsList spacing: 10 * DefaultStyle.dp @@ -785,8 +776,9 @@ Window { } contentItem: RowLayout { Image { - width: 24 * DefaultStyle.dp - height: 24 * DefaultStyle.dp + Layout.preferredWidth: 24 * DefaultStyle.dp + Layout.preferredHeight: 24 * DefaultStyle.dp + fillMode: Image.PreserveAspectFit source: AppIcons.callList } Text { @@ -807,8 +799,9 @@ Window { } contentItem: RowLayout { Image { - width: 24 * DefaultStyle.dp - height: 24 * DefaultStyle.dp + Layout.preferredWidth: 24 * DefaultStyle.dp + Layout.preferredHeight: 24 * DefaultStyle.dp + fillMode: Image.PreserveAspectFit source: AppIcons.dialer } Text { @@ -829,13 +822,16 @@ Window { visible: false } contentItem: RowLayout { - Image { - width: 24 * DefaultStyle.dp - height: 24 * DefaultStyle.dp + EffectImage { + Layout.preferredWidth: 24 * DefaultStyle.dp + Layout.preferredHeight: 24 * DefaultStyle.dp + fillMode: Image.PreserveAspectFit source: mainWindow.call.core.speakerMuted ? AppIcons.speakerSlash : AppIcons.speaker + colorizationColor: mainWindow.call.core.speakerMuted ? DefaultStyle.danger_500main : undefined } Text { text: mainWindow.call.core.speakerMuted ? qsTr("Activer le son") : qsTr("Désactiver le son") + color: mainWindow.call.core.speakerMuted ? DefaultStyle.danger_500main : DefaultStyle.main2_600 } } @@ -843,6 +839,32 @@ Window { mainWindow.call.core.lSetSpeakerMuted(!mainWindow.call.core.speakerMuted) } } + Control.Button { + id: recordButton + Layout.fillWidth: true + enabled: mainWindow.call.core.recordable + checkable: true + background: Item { + visible: false + } + contentItem: RowLayout { + EffectImage { + Layout.preferredWidth: 24 * DefaultStyle.dp + Layout.preferredHeight: 24 * DefaultStyle.dp + fillMode: Image.PreserveAspectFit + source: AppIcons.recordFill + colorizationColor: mainWindow.call.core.recording ? DefaultStyle.danger_500main : undefined + } + Text { + color: mainWindow.call.core.recording ? DefaultStyle.danger_500main : DefaultStyle.main2_600 + text: mainWindow.call.core.recording ? qsTr("Terminer l'enregistrement") : qsTr("Enregistrer l'appel") + } + + } + onClicked: { + mainWindow.call.core.recording ? mainWindow.call.core.lStopRecording() : mainWindow.call.core.lStartRecording() + } + } } } } diff --git a/Linphone/view/App/Layout/MainLayout.qml b/Linphone/view/App/Layout/MainLayout.qml index 0096fb533..6a6bb1a0a 100644 --- a/Linphone/view/App/Layout/MainLayout.qml +++ b/Linphone/view/App/Layout/MainLayout.qml @@ -46,7 +46,7 @@ Item { contentItem: RowLayout { spacing: 15 * DefaultStyle.dp EffectImage { - image.source: AppIcons.smiley + source: AppIcons.smiley colorizationColor: DefaultStyle.success_500main Layout.preferredWidth: 32 * DefaultStyle.dp Layout.preferredHeight: 32 * DefaultStyle.dp @@ -136,7 +136,7 @@ Item { background: Item { } contentItem: Image { - source: AppIcons.verticalDots + source: AppIcons.more } Popup{ id: accountList diff --git a/Linphone/view/CMakeLists.txt b/Linphone/view/CMakeLists.txt index ce6fbea42..25a26da11 100644 --- a/Linphone/view/CMakeLists.txt +++ b/Linphone/view/CMakeLists.txt @@ -27,6 +27,7 @@ list(APPEND _LINPHONEAPP_QML_FILES view/Item/ComboBox.qml view/Item/ContactsList.qml view/Item/DesktopPopup.qml + view/Item/Dialog.qml view/Item/DigitInput.qml view/Item/EffectImage.qml view/Item/ErrorText.qml diff --git a/Linphone/view/Item/Account/Accounts.qml b/Linphone/view/Item/Account/Accounts.qml index a452ba59a..e60bc4297 100644 --- a/Linphone/view/Item/Account/Accounts.qml +++ b/Linphone/view/Item/Account/Accounts.qml @@ -68,11 +68,11 @@ Item { spacing: 5 * DefaultStyle.dp EffectImage { id: newAccount - image.source: AppIcons.plusCircle + source: AppIcons.plusCircle Layout.fillHeight: true Layout.preferredWidth: height Layout.alignment: Qt.AlignHCenter - image.fillMode: Image.PreserveAspectFit + fillMode: Image.PreserveAspectFit colorizationColor: DefaultStyle.main2_500main } Text{ diff --git a/Linphone/view/Item/Call/CallContactsLists.qml b/Linphone/view/Item/Call/CallContactsLists.qml index aa30c4703..dc798e1cb 100644 --- a/Linphone/view/Item/Call/CallContactsLists.qml +++ b/Linphone/view/Item/Call/CallContactsLists.qml @@ -231,31 +231,10 @@ Item { weight: 800 * DefaultStyle.dp } } - Repeater { - model: ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "\\d"] - RowLayout { - visible: contactList.count > 0 - spacing: 8 * DefaultStyle.dp - Layout.fillWidth: true - Text { - Layout.preferredWidth: 20 * DefaultStyle.dp - Layout.alignment: Qt.AlignTop - Layout.topMargin: 15 * DefaultStyle.dp // Align center with the first row - text: modelData == "\\d" ? " " : modelData - color: DefaultStyle.main2_400 - font { - pixelSize: 20 * DefaultStyle.dp - weight: 500 * DefaultStyle.dp - } - } - ContactsList{ - Layout.fillWidth: true - id: contactList - initialProxyModel: modelData - searchBarText: searchBar.text - // contactMenuVisible: false - } - } + ContactsList{ + Layout.fillWidth: true + id: contactList + searchBarText: searchBar.text } } ColumnLayout { @@ -269,16 +248,11 @@ Item { ContactsList{ contactMenuVisible: false Layout.fillHeight: true - - model: FriendInitialProxy { - filterText: "" - property int sourceFlags: LinphoneEnums.MagicSearchSource.FavoriteFriends//mainItem.magicSearchSourceFlags - sourceModel: MagicSearchProxy { - id: search - searchText: searchBar.text.length === 0 ? "*" : searchBar.text - aggregationFlag: LinphoneEnums.MagicSearchAggregation.Friend - sourceFlags: LinphoneEnums.MagicSearchSource.FavoriteFriends - } + initialHeadersVisible: false + model: MagicSearchProxy { + searchText: searchBar.text.length === 0 ? "*" : searchBar.text + sourceFlags: LinphoneEnums.MagicSearchSource.FavoriteFriends + aggregationFlag: LinphoneEnums.MagicSearchAggregation.Friend } } } @@ -299,7 +273,7 @@ Item { id: numPad width: parent.width onLaunchCall: { - var callVarObject = UtilsCpp.createCall(searchBar.text + "@sip.linphone.org") + UtilsCpp.createCall(searchBar.text + "@sip.linphone.org") // TODO : auto completion instead of sip linphone } } diff --git a/Linphone/view/Item/CheckBox.qml b/Linphone/view/Item/CheckBox.qml index 19a04249c..d51c2ee81 100644 --- a/Linphone/view/Item/CheckBox.qml +++ b/Linphone/view/Item/CheckBox.qml @@ -16,7 +16,7 @@ Control.CheckBox { // color: mainItem.checked ? DefaultStyle.main1_500_main : "transparent" EffectImage { visible: mainItem.checked - image.source: AppIcons.check + source: AppIcons.check colorizationColor: DefaultStyle.main1_500_main anchors.fill: parent } diff --git a/Linphone/view/Item/Contact/Avatar.qml b/Linphone/view/Item/Contact/Avatar.qml index 205d7d124..4b8bfebbe 100644 --- a/Linphone/view/Item/Contact/Avatar.qml +++ b/Linphone/view/Item/Contact/Avatar.qml @@ -33,7 +33,7 @@ StackView{ id: initials Rectangle { id: initialItem - property string initials: UtilsCpp.getInitials(mainItem.displayNameObj.value) + property string initials: displayNameObj ? UtilsCpp.getInitials(mainItem.displayNameObj.value) : "" radius: width / 2 color: DefaultStyle.main2_200 height: mainItem.height diff --git a/Linphone/view/Item/Contact/Contact.qml b/Linphone/view/Item/Contact/Contact.qml index 6407c2414..0a024499c 100644 --- a/Linphone/view/Item/Contact/Contact.qml +++ b/Linphone/view/Item/Contact/Contact.qml @@ -123,12 +123,12 @@ Rectangle{ } EffectImage { id: manageAccount - image.source: AppIcons.manageProfile + source: AppIcons.manageProfile Layout.preferredWidth: 24 * DefaultStyle.dp Layout.preferredHeight: 24 * DefaultStyle.dp Layout.alignment: Qt.AlignHCenter - image.sourceSize.width: 24 * DefaultStyle.dp - image.fillMode: Image.PreserveAspectFit + width: 24 * DefaultStyle.dp + fillMode: Image.PreserveAspectFit colorizationColor: DefaultStyle.main2_500main MouseArea{ // TODO anchors.fill: parent diff --git a/Linphone/view/Item/ContactsList.qml b/Linphone/view/Item/ContactsList.qml index 60cd33478..f515b7732 100644 --- a/Linphone/view/Item/ContactsList.qml +++ b/Linphone/view/Item/ContactsList.qml @@ -11,118 +11,128 @@ ListView { height: contentHeight visible: count > 0 - property string initialProxyModel - - property bool favorite: true - // Component.onCompleted: model.sourceModel.sourceFlags = magicSearchSourceFlags - property string searchBarText property bool contactMenuVisible: true + property bool initialHeadersVisible: true - model: FriendInitialProxy { - filterText: initialProxyModel - property int sourceFlags: LinphoneEnums.MagicSearchSource.FavoriteFriends//mainItem.magicSearchSourceFlags - sourceModel: MagicSearchProxy { - id: search - searchText: searchBarText.length === 0 ? "*" : searchBarText - } + model: MagicSearchProxy { + searchText: searchBarText.length === 0 ? "*" : searchBarText } - delegate: Item { - width: mainItem.width - height: 56 * DefaultStyle.dp - RowLayout { - anchors.fill: parent - z: 1 - Avatar { - Layout.preferredWidth: 45 * DefaultStyle.dp - Layout.preferredHeight: 45 * DefaultStyle.dp - contact: modelData + + delegate: RowLayout { + id: itemDelegate + property var previousItem : mainItem.model.count > 0 && index > 0 ? mainItem.model.getAt(index-1) : null + property var previousDisplayName: previousItem ? UtilsCpp.getDisplayName(previousItem.core.address) : null + property string previousDisplayNameText: previousDisplayName ? previousDisplayName.value : "" + property var displayName: UtilsCpp.getDisplayName(modelData.core.address) + property string displayNameText: displayName ? displayName.value : "" + Text { + Layout.preferredWidth: 20 * DefaultStyle.dp + opacity: (!previousItem || !previousDisplayNameText.startsWith(displayNameText[0])) ? 1 : 0 + text: displayNameText[0] + color: DefaultStyle.main2_400 + font { + pixelSize: 20 * DefaultStyle.dp + weight: 500 * DefaultStyle.dp + capitalization: Font.AllUppercase } - Text { - text: UtilsCpp.getDisplayName(modelData.core.address).value - font.pixelSize: 14 * DefaultStyle.dp - font.capitalization: Font.Capitalize - - } - Item { - Layout.fillWidth: true - } - PopupButton { - id: friendPopup - hoverEnabled: true - visible: mainItem.contactMenuVisible && (contactArea.containsMouse || hovered || popup.opened) - popup.x: 0 - popup.padding: 10 * DefaultStyle.dp - popup.contentItem: ColumnLayout { - Button { - background: Item{} - contentItem: RowLayout { - Image { - source: modelData.core.starred ? AppIcons.heartFill : AppIcons.heart - fillMode: Image.PreserveAspectFit - width: 24 * DefaultStyle.dp - height: 24 * DefaultStyle.dp - Layout.preferredWidth: 24 * DefaultStyle.dp - Layout.preferredHeight: 24 * DefaultStyle.dp - } - Text { - text: modelData.core.starred ? qsTr("Enlever des favoris") : qsTr("Mettre en favori") - color: DefaultStyle.main2_500main - font { - pixelSize: 14 * DefaultStyle.dp - weight: 400 * DefaultStyle.dp + } + Item { + width: mainItem.width + height: 56 * DefaultStyle.dp + RowLayout { + anchors.fill: parent + z: 1 + Avatar { + Layout.preferredWidth: 45 * DefaultStyle.dp + Layout.preferredHeight: 45 * DefaultStyle.dp + contact: modelData + } + Text { + text: itemDelegate.displayNameText + font.pixelSize: 14 * DefaultStyle.dp + font.capitalization: Font.Capitalize + } + Item { + Layout.fillWidth: true + } + PopupButton { + id: friendPopup + hoverEnabled: true + visible: mainItem.contactMenuVisible && (contactArea.containsMouse || hovered || popup.opened) + popup.x: 0 + popup.padding: 10 * DefaultStyle.dp + popup.contentItem: ColumnLayout { + Button { + background: Item{} + contentItem: RowLayout { + Image { + source: modelData.core.starred ? AppIcons.heartFill : AppIcons.heart + fillMode: Image.PreserveAspectFit + width: 24 * DefaultStyle.dp + height: 24 * DefaultStyle.dp + Layout.preferredWidth: 24 * DefaultStyle.dp + Layout.preferredHeight: 24 * DefaultStyle.dp + } + Text { + text: modelData.core.starred ? qsTr("Enlever des favoris") : qsTr("Mettre en favori") + color: DefaultStyle.main2_500main + font { + pixelSize: 14 * DefaultStyle.dp + weight: 400 * DefaultStyle.dp + } } } - } - onClicked: { - modelData.core.lSetStarred(!modelData.core.starred) - friendPopup.close() - } - } - Button { - background: Item{} - contentItem: RowLayout { - EffectImage { - image.source: AppIcons.trashCan - width: 24 * DefaultStyle.dp - height: 24 * DefaultStyle.dp - Layout.preferredWidth: 24 * DefaultStyle.dp - Layout.preferredHeight: 24 * DefaultStyle.dp - image.fillMode: Image.PreserveAspectFit - colorizationColor: DefaultStyle.danger_500main + onClicked: { + modelData.core.lSetStarred(!modelData.core.starred) + friendPopup.close() } - Text { - text: qsTr("Supprimmer") - color: DefaultStyle.danger_500main - font { - pixelSize: 14 * DefaultStyle.dp - weight: 400 * DefaultStyle.dp + } + Button { + background: Item{} + contentItem: RowLayout { + EffectImage { + source: AppIcons.trashCan + width: 24 * DefaultStyle.dp + height: 24 * DefaultStyle.dp + Layout.preferredWidth: 24 * DefaultStyle.dp + Layout.preferredHeight: 24 * DefaultStyle.dp + fillMode: Image.PreserveAspectFit + colorizationColor: DefaultStyle.danger_500main + } + Text { + text: qsTr("Supprimmer") + color: DefaultStyle.danger_500main + font { + pixelSize: 14 * DefaultStyle.dp + weight: 400 * DefaultStyle.dp + } } } - } - onClicked: { - modelData.core.remove() - friendPopup.close() + onClicked: { + modelData.core.remove() + friendPopup.close() + } } } } } - } - MouseArea { - id: contactArea - hoverEnabled: true - anchors.fill: parent - Rectangle { - anchors.fill: contactArea - opacity: 0.1 - color: DefaultStyle.main2_500main - visible: contactArea.containsMouse || friendPopup.hovered - } - onClicked: { - startCallPopup.contact = modelData - startCallPopup.open() - // mainItem.callButtonPressed(modelData.core.address) + MouseArea { + id: contactArea + hoverEnabled: true + anchors.fill: parent + Rectangle { + anchors.fill: contactArea + opacity: 0.1 + color: DefaultStyle.main2_500main + visible: contactArea.containsMouse || friendPopup.hovered + } + onClicked: { + startCallPopup.contact = modelData + startCallPopup.open() + // mainItem.callButtonPressed(modelData.core.address) + } } } } diff --git a/Linphone/view/Item/Dialog.qml b/Linphone/view/Item/Dialog.qml new file mode 100644 index 000000000..de03a7e09 --- /dev/null +++ b/Linphone/view/Item/Dialog.qml @@ -0,0 +1,81 @@ +import QtQuick 2.7 +import QtQuick.Controls 2.2 as Control +import QtQuick.Effects +import QtQuick.Layouts +import Linphone + +Popup { + id: mainItem + modal: true + anchors.centerIn: parent + closePolicy: Control.Popup.NoAutoClose + rightPadding: 10 * DefaultStyle.dp + leftPadding: 10 * DefaultStyle.dp + topPadding: 10 * DefaultStyle.dp + bottomPadding: 10 * DefaultStyle.dp + buttonsLayout.height + property int radius: 16 * DefaultStyle.dp + property color underlineColor: DefaultStyle.main1_500_main + property string text + signal accepted() + signal rejected() + + background: Item { + anchors.fill: parent + Rectangle { + visible: mainItem.underlineColor != undefined + width: mainItem.width + height: mainItem.height + 2 * DefaultStyle.dp + color: mainItem.underlineColor + radius: mainItem.radius + } + Rectangle{ + id: backgroundItem + width: mainItem.width + height: mainItem.height + radius: mainItem.radius + color: DefaultStyle.grey_0 + border.color: DefaultStyle.grey_0 + } + MultiEffect { + anchors.fill: backgroundItem + source: backgroundItem + shadowEnabled: true + shadowColor: DefaultStyle.grey_900 + shadowBlur: 1.0 + shadowOpacity: 0.1 + } + RowLayout { + id: buttonsLayout + anchors.bottom: parent.bottom + anchors.horizontalCenter: parent.horizontalCenter + Layout.alignment: Qt.AlignHCenter + Layout.bottomMargin: 10 * DefaultStyle.dp + Button { + text: qsTr("Oui") + onClicked: { + mainItem.accepted() + mainItem.close() + } + } + Button { + text: qsTr("Non") + onClicked: { + mainItem.rejected() + mainItem.close() + } + } + } + } + + contentItem: Text { + width: parent.width + Layout.preferredWidth: 278 * DefaultStyle.dp + text: mainItem.text + font { + pixelSize: 14 * DefaultStyle.dp + weight: 400 * DefaultStyle.dp + } + wrapMode: Text.Wrap + horizontalAlignment: Text.AlignHCenter + } +} \ No newline at end of file diff --git a/Linphone/view/Item/EffectImage.qml b/Linphone/view/Item/EffectImage.qml index 37eb400b2..65006902d 100644 --- a/Linphone/view/Item/EffectImage.qml +++ b/Linphone/view/Item/EffectImage.qml @@ -5,41 +5,46 @@ import QtQuick.Effects import Linphone -Item { +// The loader is needed here to refresh the colorization effect (effect2) which is not refreshed when visibility change +// and causes colorization issue when effect image is inside a popup +Loader { id: mainItem - property alias image: image - property alias effect: effect - property alias effect2: effect2 + active: visible + property var source + property var fillMode: Image.PreserveAspectFit property var colorizationColor - readonly property bool useColor: colorizationColor != undefined - width: image.width - height: image.height + property int imageWidth: width + property int imageHeight: height + property bool useColor: colorizationColor != undefined + sourceComponent: Item { + Image { + id: image + visible: !effect2.enabled + source: mainItem.source + fillMode: mainItem.fillMode + sourceSize.width: width + sourceSize.height: height + width: mainItem.imageWidth + height: mainItem.imageHeight + anchors.centerIn: parent + } + MultiEffect { + id: effect + visible: false + anchors.fill: image + source: image + maskSource: image + brightness: effect2.enabled ? 1.0 : 0.0 + } - Image { - id: image - visible: !effect2.enabled - sourceSize.width: parent.width - sourceSize.height: parent.height - width: parent.width - height: parent.height - fillMode: Image.PreserveAspectFit - anchors.centerIn: parent - } - MultiEffect { - id: effect - visible: !effect2.enabled - anchors.fill: image - source: image - maskSource: image - brightness: effect2.enabled ? 1.0 : 0.0 - } - MultiEffect { - id: effect2 - enabled: mainItem.useColor - anchors.fill: effect - source: effect - maskSource: effect - colorizationColor: effect2.enabled ? mainItem.colorizationColor : 'black' - colorization: effect2.enabled ? 1.0: 0.0 + MultiEffect { + id: effect2 + enabled: mainItem.useColor + anchors.fill: effect + source: effect + maskSource: effect + colorizationColor: effect2.enabled && mainItem.colorizationColor ? mainItem.colorizationColor : 'black' + colorization: effect2.enabled ? 1.0: 0.0 + } } } diff --git a/Linphone/view/Item/NumericPad.qml b/Linphone/view/Item/NumericPad.qml index 3888cdb39..691425bc3 100644 --- a/Linphone/view/Item/NumericPad.qml +++ b/Linphone/view/Item/NumericPad.qml @@ -153,11 +153,11 @@ Control.Popup { } contentItem: EffectImage { id: buttonIcon - image.source: AppIcons.phone + source: AppIcons.phone anchors.centerIn: parent width: 24 * DefaultStyle.dp height: 24 * DefaultStyle.dp - image.fillMode: Image.PreserveAspectFit + fillMode: Image.PreserveAspectFit colorizationColor: DefaultStyle.grey_0 } onClicked: mainItem.launchCall() diff --git a/Linphone/view/Item/Popup.qml b/Linphone/view/Item/Popup.qml index 03b2616bb..de9dbcaba 100644 --- a/Linphone/view/Item/Popup.qml +++ b/Linphone/view/Item/Popup.qml @@ -21,8 +21,8 @@ Control.Popup{ width: mainItem.width height: mainItem.height radius: mainItem.radius + color: DefaultStyle.grey_0 border.color: DefaultStyle.grey_0 - // border.width: 1 } MultiEffect { anchors.fill: backgroundItem diff --git a/Linphone/view/Item/PopupButton.qml b/Linphone/view/Item/PopupButton.qml index 7a4ef307f..63540ca56 100644 --- a/Linphone/view/Item/PopupButton.qml +++ b/Linphone/view/Item/PopupButton.qml @@ -23,7 +23,7 @@ Button { radius: 40 * DefaultStyle.dp } contentItem: Image { - source: AppIcons.verticalDots + source: AppIcons.more sourceSize.width: 24 * DefaultStyle.dp sourceSize.height: 24 * DefaultStyle.dp width: 24 * DefaultStyle.dp @@ -37,7 +37,7 @@ Button { id: popup x: - width y: mainItem.height - closePolicy: Popup.CloseOnPressOutsideParent | Popup.CloseOnEscape + closePolicy: Popup.CloseOnPressOutsideParent |Popup.CloseOnPressOutside padding: 20 * DefaultStyle.dp diff --git a/Linphone/view/Item/VerticalTabBar.qml b/Linphone/view/Item/VerticalTabBar.qml index 595cb783c..29d76be9a 100644 --- a/Linphone/view/Item/VerticalTabBar.qml +++ b/Linphone/view/Item/VerticalTabBar.qml @@ -60,16 +60,18 @@ Control.TabBar { width: mainItem.width contentItem: ColumnLayout { - height: tabButton.height - width: tabButton.width + // height: tabButton.height + // width: tabButton.width EffectImage { id: buttonIcon property int buttonSize: 24 * DefaultStyle.dp - image.source: mainItem.currentIndex === index ? modelData.selectedIcon : modelData.icon + source: mainItem.currentIndex === index ? modelData.selectedIcon : modelData.icon Layout.preferredWidth: buttonSize Layout.preferredHeight: buttonSize + width: buttonSize + height: buttonSize Layout.alignment: Qt.AlignHCenter - image.fillMode: Image.PreserveAspectFit + fillMode: Image.PreserveAspectFit colorizationColor: DefaultStyle.grey_0 } Text { diff --git a/Linphone/view/Page/Main/AbstractMainPage.qml b/Linphone/view/Page/Main/AbstractMainPage.qml index bb9749456..d1197c370 100644 --- a/Linphone/view/Page/Main/AbstractMainPage.qml +++ b/Linphone/view/Page/Main/AbstractMainPage.qml @@ -77,10 +77,10 @@ Item { Layout.alignment: Qt.AlignVCenter EffectImage { colorizationColor: DefaultStyle.grey_0 - image.source: mainItem.newItemIconSource - image.width: 24 * DefaultStyle.dp - image.height: 24 * DefaultStyle.dp - image.fillMode: Image.PreserveAspectFit + source: mainItem.newItemIconSource + width: 24 * DefaultStyle.dp + height: 24 * DefaultStyle.dp + fillMode: Image.PreserveAspectFit } Text { text: mainItem.noItemButtonText diff --git a/Linphone/view/Page/Main/CallPage.qml b/Linphone/view/Page/Main/CallPage.qml index 99d3fd292..9076caaec 100644 --- a/Linphone/view/Page/Main/CallPage.qml +++ b/Linphone/view/Page/Main/CallPage.qml @@ -23,6 +23,18 @@ AbstractMainPage { listStackView.push(newCallItem) } + + Dialog { + id: deleteHistoryPopup + width: 278 * DefaultStyle.dp + text: qsTr("L'historique d'appel sera supprimé. Souhaitez-vous continuer ?") + } + Dialog { + id: deleteForUserPopup + width: 278 * DefaultStyle.dp + text: qsTr("L'historique d'appel de l'utilisateur sera supprimé. Souhaitez-vous continuer ?") + } + leftPanelContent: Item { id: leftPanel Layout.fillWidth: true @@ -61,12 +73,12 @@ AbstractMainPage { background: Item{} contentItem: RowLayout { EffectImage { - image.source: AppIcons.trashCan + source: AppIcons.trashCan width: 24 * DefaultStyle.dp height: 24 * DefaultStyle.dp Layout.preferredWidth: 24 * DefaultStyle.dp Layout.preferredHeight: 24 * DefaultStyle.dp - image.fillMode: Image.PreserveAspectFit + fillMode: Image.PreserveAspectFit colorizationColor: DefaultStyle.danger_500main } Text { @@ -78,9 +90,13 @@ AbstractMainPage { } } } + Connections { + target: deleteHistoryPopup + onAccepted: historyListView.model.removeAllEntries() + } onClicked: { - historyListView.model.removeAllEntries() removeHistory.close() + deleteHistoryPopup.open() } } } @@ -182,50 +198,58 @@ AbstractMainPage { height: 45 * DefaultStyle.dp } } - ColumnLayout { - Layout.alignment: Qt.AlignVCenter - Text { - property var remoteAddress: modelData ? UtilsCpp.getDisplayName(modelData.core.remoteAddress) : undefined - text: remoteAddress ? remoteAddress.value : "" - font { - pixelSize: 14 * DefaultStyle.dp - weight: 400 * DefaultStyle.dp - } - } - RowLayout { - Image { - source: modelData.core.status === LinphoneEnums.CallStatus.Declined - || modelData.core.status === LinphoneEnums.CallStatus.DeclinedElsewhere - || modelData.core.status === LinphoneEnums.CallStatus.Aborted - || modelData.core.status === LinphoneEnums.CallStatus.EarlyAborted - ? modelData.core.isOutgoing - ? AppIcons.outgoingCallRejected - : AppIcons.incomingCallRejected - : modelData.core.status === LinphoneEnums.CallStatus.Missed - ? modelData.core.isOutgoing - ? AppIcons.outgoingCallMissed - : AppIcons.incomingCallMissed - : modelData.core.isOutgoing - ? AppIcons.outgoingCall - : AppIcons.incomingCall - Layout.preferredWidth: 5 * DefaultStyle.dp - Layout.preferredHeight: 5 * DefaultStyle.dp - sourceSize.width: 5 * DefaultStyle.dp - sourceSize.height: 5 * DefaultStyle.dp - } + Item { + Layout.fillWidth: true + Layout.fillHeight: true + ColumnLayout { + spacing: 5 * DefaultStyle.dp + anchors.verticalCenter: parent.verticalCenter Text { - // text: modelData.core.date - text: UtilsCpp.formatDateElapsedTime(modelData.core.date) + id: friendAddress + property var remoteAddress: modelData ? UtilsCpp.getDisplayName(modelData.core.remoteAddress) : undefined + text: remoteAddress ? remoteAddress.value : "" font { - pixelSize: 12 * DefaultStyle.dp - weight: 300 * DefaultStyle.dp + pixelSize: 14 * DefaultStyle.dp + weight: 400 * DefaultStyle.dp + } + } + RowLayout { + spacing: 3 * DefaultStyle.dp + Image { + source: modelData.core.status === LinphoneEnums.CallStatus.Declined + || modelData.core.status === LinphoneEnums.CallStatus.DeclinedElsewhere + || modelData.core.status === LinphoneEnums.CallStatus.Aborted + || modelData.core.status === LinphoneEnums.CallStatus.EarlyAborted + ? modelData.core.isOutgoing + ? AppIcons.outgoingCallRejected + : AppIcons.incomingCallRejected + : modelData.core.status === LinphoneEnums.CallStatus.Missed + ? modelData.core.isOutgoing + ? AppIcons.outgoingCallMissed + : AppIcons.incomingCallMissed + : modelData.core.isOutgoing + ? AppIcons.outgoingCall + : AppIcons.incomingCall + Layout.preferredWidth: 5 * DefaultStyle.dp + Layout.preferredHeight: 5 * DefaultStyle.dp + sourceSize.width: 5 * DefaultStyle.dp + sourceSize.height: 5 * DefaultStyle.dp + RectangleTest{anchors.fill: parent} + } + Text { + // text: modelData.core.date + text: UtilsCpp.formatDateElapsedTime(modelData.core.date) + font { + pixelSize: 12 * DefaultStyle.dp + weight: 300 * DefaultStyle.dp + } } } } } - Item { - Layout.fillWidth: true - } + // Item { + // Layout.fillWidth: true + // } Control.Button { implicitWidth: 24 * DefaultStyle.dp implicitHeight: 24 * DefaultStyle.dp @@ -244,7 +268,7 @@ AbstractMainPage { var addr = modelData.core.remoteAddress var addressEnd = "@sip.linphone.org" if (!addr.endsWith(addressEnd)) addr += addressEnd - var callVarObject = UtilsCpp.createCall(addr) + UtilsCpp.createCall(addr) } } } @@ -342,7 +366,7 @@ AbstractMainPage { onCallButtonPressed: (address) => { var addressEnd = "@sip.linphone.org" if (!address.endsWith(addressEnd)) address += addressEnd - var callVarObject = UtilsCpp.createCall(address) + UtilsCpp.createCall(address) // var window = UtilsCpp.getCallsWindow() } } @@ -427,10 +451,13 @@ AbstractMainPage { iconSource: AppIcons.trashCan colorizationColor: DefaultStyle.danger_500main } + Connections { + target: deleteForUserPopup + onAccepted: detailListView.model.removeEntriesWithFilter() + } onClicked: { - detailListView.model.removeEntriesWithFilter() - // detailListView.currentIndex = -1 // reset index for ui detailOptions.close() + deleteForUserPopup.open() } } } @@ -659,12 +686,12 @@ AbstractMainPage { property string iconSource property color colorizationColor: DefaultStyle.main2_500main EffectImage { - image.source: iconLabel.iconSource + source: iconLabel.iconSource width: 24 * DefaultStyle.dp height: 24 * DefaultStyle.dp Layout.preferredWidth: 24 * DefaultStyle.dp Layout.preferredHeight: 24 * DefaultStyle.dp - image.fillMode: Image.PreserveAspectFit + fillMode: Image.PreserveAspectFit colorizationColor: iconLabel.colorizationColor } Text { diff --git a/Linphone/view/Style/AppIcons.qml b/Linphone/view/Style/AppIcons.qml index 613fd649c..0dab48b78 100644 --- a/Linphone/view/Style/AppIcons.qml +++ b/Linphone/view/Style/AppIcons.qml @@ -36,6 +36,7 @@ QtObject { property string usersThreeSelected: "image://internal/users-three-selected.svg" property string noItemImage: "image://internal/noItemImage.svg" property string verticalDots: "image://internal/dots-three-vertical.svg" + property string more: "image://internal/more.svg" property string plusCircle: "image://internal/plus-circle.svg" property string micro: "image://internal/microphone-stage.svg" property string groupCall: "image://internal/group-call.svg" @@ -64,4 +65,5 @@ QtObject { property string empty: "image://internal/empty.svg" property string heart: "image://internal/heart.svg" property string heartFill: "image://internal/heart-fill.svg" + property string recordFill: "image://internal/record-fill.svg" } \ No newline at end of file diff --git a/Linphone/view/Style/DefaultStyle.qml b/Linphone/view/Style/DefaultStyle.qml index 9a424b3d9..13685ab6c 100644 --- a/Linphone/view/Style/DefaultStyle.qml +++ b/Linphone/view/Style/DefaultStyle.qml @@ -31,7 +31,7 @@ QtObject { - property double dp: 0.8 //1 + property double dp: 1 property string emojiFont: "Noto Color Emoji" property string defaultFont: "Noto Sans"