This commit is contained in:
Christophe Deschamps 2025-04-01 09:04:19 +02:00
parent 187c6753bd
commit dad3cb084f
45 changed files with 1293 additions and 296 deletions

View file

@ -1149,6 +1149,16 @@ bool App::event(QEvent *event) {
} else if (event->type() == QEvent::ApplicationStateChange) {
auto state = static_cast<QApplicationStateChangeEvent *>(event);
if (state->applicationState() == Qt::ApplicationActive) Utils::smartShowWindow(getLastActiveWindow());
} else if (event->type() == QEvent::ApplicationActivate) {
for (int i = 0; i < getAccountList()->rowCount(); ++i) {
auto accountCore = getAccountList()->getAt<AccountCore>(i);
emit accountCore->lSetPresence(LinphoneEnums::Presence::Online, false, false);
}
} else if (event->type() == QEvent::ApplicationDeactivate) {
for (int i = 0; i < getAccountList()->rowCount(); ++i) {
auto accountCore = getAccountList()->getAt<AccountCore>(i);
emit accountCore->lSetPresence(LinphoneEnums::Presence::Away, false, false);
}
}
return SingleApplication::event(event);

View file

@ -85,6 +85,14 @@ AccountCore::AccountCore(const std::shared_ptr<linphone::Account> &account) : QO
// Add listener
mAccountModel = Utils::makeQObject_ptr<AccountModel>(account); // OK
mAccountModel->setSelf(mAccountModel);
mExplicitPresence = LinphoneEnums::fromString(
Utils::coreStringToAppString(CoreModel::getInstance()->getCore()->getConfig()->getString(
ToolModel::configAccountSection(account), "explicit_presence", "")));
mPresenceNote = Utils::coreStringToAppString(CoreModel::getInstance()->getCore()->getConfig()->getString(
ToolModel::configAccountSection(account), "presence_note", ""));
mMaxPresenceNoteSize = CoreModel::getInstance()->getCore()->getConfig()->getInt(
ToolModel::configAccountSection(account), "max_presence_note_size", 140);
mPresence = mAccountModel->getPresence();
mNotificationsAllowed = mAccountModel->getNotificationsAllowed();
mDialPlan = Utils::createDialPlanVariant("", " ");
mDialPlans << mDialPlan;
@ -222,6 +230,16 @@ void AccountCore::setSelf(QSharedPointer<AccountCore> me) {
mAccountModelConnection->makeConnectToModel(
&AccountModel::removed, [this]() { mAccountModelConnection->invokeToCore([this]() { emit removed(); }); });
mAccountModelConnection->makeConnectToModel(
&AccountModel::presenceChanged, [this](LinphoneEnums::Presence presence, bool userInitiated) {
mAccountModelConnection->invokeToCore([this, presence, userInitiated]() {
if (userInitiated) mExplicitPresence = presence;
else mExplicitPresence = LinphoneEnums::Presence::Undefined;
mPresence = presence;
emit presenceChanged();
});
});
// From GUI
mAccountModelConnection->makeConnectToCore(&AccountCore::lSetPictureUri, [this](QString uri) {
mAccountModelConnection->invokeToModel([this, uri]() { mAccountModel->setPictureUri(uri); });
@ -258,6 +276,13 @@ void AccountCore::setSelf(QSharedPointer<AccountCore> me) {
mAccountModelConnection->makeConnectToCore(&AccountCore::lSetNotificationsAllowed, [this](bool value) {
mAccountModelConnection->invokeToModel([this, value]() { mAccountModel->setNotificationsAllowed(value); });
});
mAccountModelConnection->makeConnectToCore(
&AccountCore::lSetPresence, [this](LinphoneEnums::Presence presence, bool userInitiated, bool resetToAuto) {
mAccountModelConnection->invokeToModel(
[this, presence, userInitiated, resetToAuto, presenceNote = mPresenceNote]() {
mAccountModel->setPresence(presence, userInitiated, resetToAuto, presenceNote);
});
});
DEFINE_CORE_GET_CONNECT(mAccountModelConnection, AccountCore, AccountModel, mAccountModel, int, voicemailCount,
VoicemailCount)
@ -425,6 +450,30 @@ QString AccountCore::getHumanReadableRegistrationState() const {
}
}
QColor AccountCore::getRegistrationColor() const {
mustBeInMainThread(log().arg(Q_FUNC_INFO));
switch (mRegistrationState) {
case LinphoneEnums::RegistrationState::Ok:
return Utils::getDefaultStyleColor("success_500main");
case LinphoneEnums::RegistrationState::Refreshing:
return Utils::getDefaultStyleColor("main2_500main");
case LinphoneEnums::RegistrationState::Progress:
return Utils::getDefaultStyleColor("main2_500main");
case LinphoneEnums::RegistrationState::Failed:
return Utils::getDefaultStyleColor("danger_500main");
case LinphoneEnums::RegistrationState::None:
case LinphoneEnums::RegistrationState::Cleared:
return Utils::getDefaultStyleColor("warning_600");
default:
return " ";
}
}
QUrl AccountCore::getRegistrationIcon() const {
mustBeInMainThread(log().arg(Q_FUNC_INFO));
return Utils::getRegistrationStateIcon(mRegistrationState);
}
QString AccountCore::getHumanReadableRegistrationStateExplained() const {
switch (mRegistrationState) {
case LinphoneEnums::RegistrationState::Ok:
@ -804,3 +853,38 @@ void AccountCore::undo() {
});
}
}
LinphoneEnums::Presence AccountCore::getPresence() {
return mPresence;
}
QColor AccountCore::getPresenceColor() {
return Utils::getPresenceColor(mPresence);
}
QUrl AccountCore::getPresenceIcon() {
return Utils::getPresenceIcon(mPresence);
}
QString AccountCore::getPresenceStatus() {
return Utils::getPresenceStatus(mPresence);
}
void AccountCore::resetToAutomaticPresence() {
emit lSetPresence(LinphoneEnums::Presence::Online, false, true);
}
LinphoneEnums::Presence AccountCore::getExplicitPresence() {
return mExplicitPresence;
}
void AccountCore::setPresenceNote(QString presenceNote) {
if (presenceNote != mPresenceNote) {
mPresenceNote = presenceNote;
emit lSetPresence(mPresence, mExplicitPresence != LinphoneEnums::Presence::Undefined, false);
}
}
QString AccountCore::getPresenceNote() {
return mPresenceNote;
}

View file

@ -75,6 +75,15 @@ public:
Q_PROPERTY(bool isSaved READ isSaved WRITE setIsSaved NOTIFY isSavedChanged)
Q_PROPERTY(
QString voicemailAddress READ getVoicemailAddress WRITE setVoicemailAddress NOTIFY voicemailAddressChanged)
Q_PROPERTY(LinphoneEnums::Presence presence READ getPresence WRITE lSetPresence NOTIFY presenceChanged)
Q_PROPERTY(QColor presenceColor READ getPresenceColor NOTIFY presenceChanged)
Q_PROPERTY(QUrl presenceIcon READ getPresenceIcon NOTIFY presenceChanged)
Q_PROPERTY(QString presenceStatus READ getPresenceStatus NOTIFY presenceChanged)
Q_PROPERTY(QColor registrationColor READ getRegistrationColor NOTIFY registrationStateChanged)
Q_PROPERTY(QUrl registrationIcon READ getRegistrationIcon NOTIFY registrationStateChanged)
Q_PROPERTY(LinphoneEnums::Presence explicitPresence MEMBER mExplicitPresence NOTIFY presenceChanged)
Q_PROPERTY(QString presenceNote READ getPresenceNote WRITE setPresenceNote NOTIFY presenceChanged)
Q_PROPERTY(int maxPresenceNoteSize MEMBER mMaxPresenceNoteSize CONSTANT)
DECLARE_CORE_GET(int, voicemailCount, VoicemailCount)
static QSharedPointer<AccountCore> create(const std::shared_ptr<linphone::Account> &account);
@ -114,6 +123,8 @@ public:
void onDialPlanChanged(QVariantMap internationalPrefix);
QString getHumanReadableRegistrationState() const;
QString getHumanReadableRegistrationStateExplained() const;
QColor getRegistrationColor() const;
QUrl getRegistrationIcon() const;
bool getRegisterEnabled() const;
void onRegisterEnabledChanged(bool enabled);
@ -170,6 +181,15 @@ public:
Q_INVOKABLE void save();
Q_INVOKABLE void undo();
QColor getPresenceColor();
QUrl getPresenceIcon();
QString getPresenceStatus();
LinphoneEnums::Presence getPresence();
Q_INVOKABLE void resetToAutomaticPresence();
LinphoneEnums::Presence getExplicitPresence();
void setPresenceNote(QString presenceNote);
QString getPresenceNote();
signals:
void pictureUriChanged();
void registrationStateChanged(const QString &message);
@ -198,6 +218,7 @@ signals:
void removed();
void isSavedChanged();
void voicemailAddressChanged();
void presenceChanged();
// Account requests
void lSetPictureUri(QString pictureUri);
@ -209,6 +230,7 @@ signals:
void lSetDialPlan(QVariantMap internationalPrefix);
void lSetRegisterEnabled(bool enabled);
void lSetNotificationsAllowed(bool value);
void lSetPresence(LinphoneEnums::Presence presence, bool userInitiated = true, bool resetToAuto = false);
protected:
void writeIntoModel(std::shared_ptr<AccountModel> model) const;
@ -243,6 +265,10 @@ private:
QString mAudioVideoConferenceFactoryAddress;
QString mLimeServerUrl;
QString mVoicemailAddress;
LinphoneEnums::Presence mPresence = LinphoneEnums::Presence::Undefined;
LinphoneEnums::Presence mExplicitPresence;
QString mPresenceNote;
int mMaxPresenceNoteSize;
bool mIsSaved = true;
std::shared_ptr<AccountModel> mAccountModel;

View file

@ -68,6 +68,10 @@ void AccountList::setSelf(QSharedPointer<AccountList> me) {
setHaveAccount(accounts->size() > 0);
setDefaultAccount(defaultAccountCore);
if (isInitialization) setInitialized(true);
for (const QSharedPointer<AccountCore> &accountCore : *accounts) {
if (accountCore->getExplicitPresence() != LinphoneEnums::Presence::Undefined)
emit accountCore->lSetPresence(accountCore->getExplicitPresence(), true, false);
}
delete accounts;
});
});
@ -88,7 +92,8 @@ void AccountList::setSelf(QSharedPointer<AccountList> me) {
// with the open id account
mModelConnection->makeConnectToModel(&CoreModel::bearerAccountAdded, [this] {
setInitialized(false);
emit lUpdate(true); });
emit lUpdate(true);
});
mModelConnection->makeConnectToModel(
&CoreModel::globalStateChanged,

View file

@ -48,8 +48,9 @@ FriendCore::FriendCore(const std::shared_ptr<linphone::Friend> &contact, bool is
mustBeInLinphoneThread(getClassName());
mFriendModel = Utils::makeQObject_ptr<FriendModel>(contact);
mFriendModel->setSelf(mFriendModel);
mConsolidatedPresence = LinphoneEnums::fromLinphone(contact->getConsolidatedPresence());
mPresenceTimestamp = mFriendModel->getPresenceTimestamp();
auto presence = mFriendModel->getPresence(contact);
auto note = mFriendModel->getPresenceNote(contact);
App::postCoreAsync([this, presence, note]() { setPresence(presence, note); });
mPictureUri = Utils::coreStringToAppString(contact->getPhoto());
mFullName = mFriendModel->getFullName();
auto defaultAddress = contact->getAddress();
@ -65,7 +66,7 @@ FriendCore::FriendCore(const std::shared_ptr<linphone::Friend> &contact, bool is
auto addresses = contact->getAddresses();
for (auto &address : addresses) {
mAddressList.append(Utils::createFriendAddressVariant(
tr("sip_address"), Utils::coreStringToAppString(address->asStringUriOnly())));
tr("sip_address"), Utils::coreStringToAppString(address->asStringUriOnly())));
}
mDefaultAddress = defaultAddress ? Utils::coreStringToAppString(defaultAddress->asStringUriOnly()) : QString();
mDefaultFullAddress = defaultAddress ? Utils::coreStringToAppString(defaultAddress->asString()) : QString();
@ -142,8 +143,7 @@ void FriendCore::setSelf(QSharedPointer<FriendCore> me) {
mFriendModelConnection->makeConnectToModel(
&FriendModel::removed, [this]() { mFriendModelConnection->invokeToCore([this]() { removed(this); }); });
mFriendModelConnection->makeConnectToModel(
&FriendModel::presenceReceived,
[this](LinphoneEnums::ConsolidatedPresence consolidatedPresence, QDateTime presenceTimestamp) {
&FriendModel::presenceReceived, [this](LinphoneEnums::Presence presence, QString presenceNote) {
auto devices = mFriendModel->getDevices();
QVariantList devicesList;
for (auto &device : devices) {
@ -153,14 +153,11 @@ void FriendCore::setSelf(QSharedPointer<FriendCore> me) {
Utils::coreStringToAppString(device->getAddress()->asString()),
LinphoneEnums::fromLinphone(device->getSecurityLevel())));
}
mFriendModelConnection->invokeToCore(
[this, consolidatedPresence, presenceTimestamp, devicesList]() {
setConsolidatedPresence(consolidatedPresence);
setPresenceTimestamp(presenceTimestamp);
setDevices(devicesList);
updateVerifiedDevicesCount();
});
mFriendModelConnection->invokeToCore([this, presence, devicesList, presenceNote]() {
setPresence(presence, presenceNote);
setDevices(devicesList);
updateVerifiedDevicesCount();
});
});
mFriendModelConnection->makeConnectToModel(&FriendModel::pictureUriChanged, [this](const QString &uri) {
mFriendModelConnection->invokeToCore([this, uri]() { this->onPictureUriChanged(uri); });
@ -188,7 +185,7 @@ void FriendCore::setSelf(QSharedPointer<FriendCore> me) {
QList<QVariant> addr;
for (auto &num : numbers) {
addr.append(Utils::createFriendAddressVariant(
tr("sip_address"), Utils::coreStringToAppString(num->asStringUriOnly())));
tr("sip_address"), Utils::coreStringToAppString(num->asStringUriOnly())));
}
mFriendModelConnection->invokeToCore([this, addr]() { resetPhoneNumbers(addr); });
});
@ -419,9 +416,10 @@ void FriendCore::appendAddress(const QString &addr) {
QString interpretedFullAddress = linphoneAddr ? Utils::coreStringToAppString(linphoneAddr->asString()) : "";
QString interpretedAddress = linphoneAddr ? Utils::coreStringToAppString(linphoneAddr->asStringUriOnly()) : "";
mCoreModelConnection->invokeToCore([this, interpretedAddress, interpretedFullAddress]() {
if (interpretedAddress.isEmpty()) Utils::showInformationPopup(tr("information_popup_error_title"),
//: "Adresse invalide"
tr("information_popup_invalid_address_message"), false);
if (interpretedAddress.isEmpty())
Utils::showInformationPopup(tr("information_popup_error_title"),
//: "Adresse invalide"
tr("information_popup_invalid_address_message"), false);
else {
mAddressList.append(Utils::createFriendAddressVariant(tr("sip_address"), interpretedAddress));
if (mDefaultFullAddress.isEmpty()) mDefaultFullAddress = interpretedFullAddress;
@ -507,30 +505,6 @@ void FriendCore::setDefaultAddress(const QString &address) {
}
}
LinphoneEnums::ConsolidatedPresence FriendCore::getConsolidatedPresence() const {
return mConsolidatedPresence;
}
void FriendCore::setConsolidatedPresence(LinphoneEnums::ConsolidatedPresence presence) {
mustBeInMainThread(log().arg(Q_FUNC_INFO));
if (mConsolidatedPresence != presence) {
mConsolidatedPresence = presence;
emit consolidatedPresenceChanged(mConsolidatedPresence);
}
}
QDateTime FriendCore::getPresenceTimestamp() const {
return mPresenceTimestamp;
}
void FriendCore::setPresenceTimestamp(QDateTime presenceTimestamp) {
mustBeInMainThread(log().arg(Q_FUNC_INFO));
if (mPresenceTimestamp != presenceTimestamp) {
mPresenceTimestamp = presenceTimestamp;
emit presenceTimestampChanged(mPresenceTimestamp);
}
}
QString FriendCore::getPictureUri() const {
return mPictureUri;
}
@ -612,8 +586,8 @@ void FriendCore::writeFromModel(const std::shared_ptr<FriendModel> &model) {
QList<QVariant> addresses;
for (auto &addr : model->getAddresses()) {
addresses.append(
Utils::createFriendAddressVariant(tr("sip_address"), Utils::coreStringToAppString(addr->asStringUriOnly())));
addresses.append(Utils::createFriendAddressVariant(tr("sip_address"),
Utils::coreStringToAppString(addr->asStringUriOnly())));
}
mAddressList = addresses;
mDefaultAddress = model->getDefaultAddress();
@ -759,3 +733,29 @@ bool FriendCore::getReadOnly() const {
std::shared_ptr<FriendModel> FriendCore::getFriendModel() {
return mFriendModel;
}
void FriendCore::setPresence(LinphoneEnums::Presence presence, QString presenceNote) {
mustBeInMainThread(log().arg(Q_FUNC_INFO));
bool notify = false;
if (presence != mPresence) {
mPresence = presence;
notify = true;
}
if (presenceNote != mPresenceNote) {
mPresenceNote = presenceNote;
notify = true;
}
if (notify) emit presenceChanged();
}
QUrl FriendCore::getPresenceIcon() {
return Utils::getPresenceIcon(mPresence);
}
QColor FriendCore::getPresenceColor() {
return Utils::getPresenceColor(mPresence);
}
QString FriendCore::getPresenceStatus() {
return Utils::getPresenceStatus(mPresence);
}

View file

@ -29,6 +29,7 @@
#include "tool/thread/SafeSharedPointer.hpp"
#include <linphone++/linphone.hh>
#include <QColor>
#include <QDateTime>
#include <QMap>
#include <QObject>
@ -63,9 +64,11 @@ class FriendCore : public QObject, public AbstractObject {
Q_PROPERTY(QString defaultAddress READ getDefaultAddress WRITE setDefaultAddress NOTIFY defaultAddressChanged)
Q_PROPERTY(QString defaultFullAddress READ getDefaultFullAddress WRITE setDefaultFullAddress NOTIFY
defaultFullAddressChanged)
Q_PROPERTY(QDateTime presenceTimestamp READ getPresenceTimestamp NOTIFY presenceTimestampChanged)
Q_PROPERTY(LinphoneEnums::ConsolidatedPresence consolidatedPresence READ getConsolidatedPresence NOTIFY
consolidatedPresenceChanged)
Q_PROPERTY(LinphoneEnums::Presence presence MEMBER mPresence NOTIFY presenceChanged)
Q_PROPERTY(QUrl presenceIcon READ getPresenceIcon NOTIFY presenceChanged)
Q_PROPERTY(QColor presenceColor READ getPresenceColor NOTIFY presenceChanged)
Q_PROPERTY(QString presenceStatus READ getPresenceStatus NOTIFY presenceChanged)
Q_PROPERTY(QString presenceNote MEMBER mPresenceNote NOTIFY presenceChanged)
Q_PROPERTY(bool isSaved READ getIsSaved NOTIFY isSavedChanged)
Q_PROPERTY(bool isStored READ getIsStored NOTIFY isStoredChanged)
Q_PROPERTY(QString pictureUri READ getPictureUri WRITE setPictureUri NOTIFY pictureUriChanged)
@ -130,12 +133,6 @@ public:
void setDevices(QVariantList devices);
Q_INVOKABLE LinphoneEnums::SecurityLevel getSecurityLevelForAddress(const QString &address) const;
LinphoneEnums::ConsolidatedPresence getConsolidatedPresence() const;
void setConsolidatedPresence(LinphoneEnums::ConsolidatedPresence presence);
QDateTime getPresenceTimestamp() const;
void setPresenceTimestamp(QDateTime presenceTimestamp);
bool getIsSaved() const;
void setIsSaved(bool isSaved);
@ -146,8 +143,6 @@ public:
void setPictureUri(const QString &uri);
void onPictureUriChanged(QString uri);
void onPresenceReceived(LinphoneEnums::ConsolidatedPresence consolidatedPresence, QDateTime presenceTimestamp);
bool isLdap() const;
bool isAppFriend() const;
bool isCardDAV() const;
@ -159,6 +154,10 @@ public:
Q_INVOKABLE void save();
Q_INVOKABLE void undo();
QColor getPresenceColor();
QString getPresenceStatus();
QUrl getPresenceIcon();
protected:
void resetPhoneNumbers(QList<QVariant> newList);
void resetAddresses(QList<QVariant> newList);
@ -173,8 +172,6 @@ signals:
void addressChanged();
void organizationChanged();
void jobChanged();
void consolidatedPresenceChanged(LinphoneEnums::ConsolidatedPresence level);
void presenceTimestampChanged(QDateTime presenceTimestamp);
void pictureUriChanged();
void saved();
void isSavedChanged(bool isSaved);
@ -186,13 +183,16 @@ signals:
void devicesChanged();
void verifiedDevicesChanged();
void lSetStarred(bool starred);
void presenceChanged();
protected:
void writeIntoModel(std::shared_ptr<FriendModel> model) const;
void writeFromModel(const std::shared_ptr<FriendModel> &model);
LinphoneEnums::ConsolidatedPresence mConsolidatedPresence = LinphoneEnums::ConsolidatedPresence::Offline;
QDateTime mPresenceTimestamp;
LinphoneEnums::Presence mPresence = LinphoneEnums::Presence::Undefined;
QColor mPresenceColor;
QString mPresenceStatus;
QString mPresenceNote = "";
QString mGivenName;
QString mFamilyName;
QString mFullName;
@ -214,6 +214,9 @@ protected:
QSharedPointer<SafeConnection<FriendCore, FriendModel>> mFriendModelConnection;
QSharedPointer<SafeConnection<FriendCore, CoreModel>> mCoreModelConnection;
private:
void setPresence(LinphoneEnums::Presence presence, QString presenceNote);
DECLARE_ABSTRACT_OBJECT
};

View file

@ -290,6 +290,15 @@ void Notifier::notifyReceivedCall(const shared_ptr<linphone::Call> &call) {
if (!accountModel->getNotificationsAllowed()) {
qInfo()
<< "Notifications have been disabled for this account - not creating a notification for incoming call";
if (accountModel->forwardToVoiceMailInDndPresence()) {
lInfo() << log().arg("Transferring call to voicemail");
auto voicemailAddress = linphone::Factory::get()->createAddress(
Utils::appStringToCoreString(accountModel->getVoicemailAddress()));
if (voicemailAddress) call->transferTo(voicemailAddress);
} else {
lInfo() << log().arg("Declining call.");
call->decline(linphone::Reason::Busy);
}
return;
}
}
@ -337,7 +346,7 @@ void Notifier::notifyReceivedMessages(const std::shared_ptr<linphone::ChatRoom>
remoteAddress = Utils::coreStringToAppString(remoteAddr->asStringUriOnly());
auto fileContent = message->getFileTransferInformation();
if (!fileContent) {
foreach (auto content, message->getContents()) {
for (auto content : message->getContents()) {
if (content->isText()) txt += content->getUtf8Text().c_str();
}
} else if (fileContent->isVoiceRecording())

View file

@ -0,0 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="17" viewBox="0 0 18 17" fill="none">
<path d="M9.26562 1.59375C7.50502 1.59568 5.81707 2.29594 4.57213 3.54088C3.32719 4.78582 2.62693 6.47377 2.625 8.23438V13.8344C2.62535 14.1103 2.7351 14.3748 2.93017 14.5698C3.12524 14.7649 3.38971 14.8746 3.66559 14.875H9.26562C11.0268 14.875 12.7159 14.1754 13.9613 12.93C15.2066 11.6846 15.9062 9.99558 15.9062 8.23438C15.9062 6.47317 15.2066 4.7841 13.9613 3.53874C12.7159 2.29339 11.0268 1.59375 9.26562 1.59375ZM9.26562 13.8125H3.6875V8.23438C3.6875 7.13113 4.01465 6.05265 4.62758 5.13533C5.24052 4.21802 6.1117 3.50305 7.13097 3.08086C8.15024 2.65866 9.27181 2.5482 10.3539 2.76343C11.4359 2.97867 12.4298 3.50993 13.21 4.29004C13.9901 5.07016 14.5213 6.06409 14.7366 7.14614C14.9518 8.22819 14.8413 9.34976 14.4191 10.369C13.9969 11.3883 13.282 12.2595 12.3647 12.8724C11.4473 13.4853 10.3689 13.8125 9.26562 13.8125ZM10.0625 8.5C10.0625 8.65761 10.0158 8.81167 9.9282 8.94272C9.84064 9.07377 9.71619 9.1759 9.57058 9.23622C9.42497 9.29653 9.26474 9.31231 9.11016 9.28156C8.95558 9.25082 8.81359 9.17492 8.70215 9.06348C8.5907 8.95203 8.51481 8.81004 8.48406 8.65546C8.45331 8.50088 8.46909 8.34066 8.52941 8.19505C8.58972 8.04944 8.69186 7.92498 8.82291 7.83742C8.95395 7.74986 9.10802 7.70312 9.26562 7.70312C9.47697 7.70312 9.67966 7.78708 9.8291 7.93652C9.97854 8.08597 10.0625 8.28866 10.0625 8.5ZM7.14062 8.5C7.14062 8.65761 7.09389 8.81167 7.00633 8.94272C6.91877 9.07377 6.79431 9.1759 6.6487 9.23622C6.50309 9.29653 6.34287 9.31231 6.18829 9.28156C6.03371 9.25082 5.89172 9.17492 5.78027 9.06348C5.66883 8.95203 5.59293 8.81004 5.56219 8.65546C5.53144 8.50088 5.54722 8.34066 5.60753 8.19505C5.66785 8.04944 5.76998 7.92498 5.90103 7.83742C6.03208 7.74986 6.18614 7.70312 6.34375 7.70312C6.55509 7.70312 6.75778 7.78708 6.90723 7.93652C7.05667 8.08597 7.14062 8.28866 7.14062 8.5ZM12.9844 8.5C12.9844 8.65761 12.9376 8.81167 12.8501 8.94272C12.7625 9.07377 12.6381 9.1759 12.4925 9.23622C12.3468 9.29653 12.1866 9.31231 12.032 9.28156C11.8775 9.25082 11.7355 9.17492 11.624 9.06348C11.5126 8.95203 11.4367 8.81004 11.4059 8.65546C11.3752 8.50088 11.391 8.34066 11.4513 8.19505C11.5116 8.04944 11.6137 7.92498 11.7448 7.83742C11.8758 7.74986 12.0299 7.70312 12.1875 7.70312C12.3988 7.70312 12.6015 7.78708 12.751 7.93652C12.9004 8.08597 12.9844 8.28866 12.9844 8.5Z" fill="#4E6074"/>
</svg>

After

Width:  |  Height:  |  Size: 2.3 KiB

View file

@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" width="11" height="11" viewBox="0 0 11 11" fill="none">
<circle cx="5.5" cy="5.5" r="5" fill="#FFDC2E" stroke="white"/>
</svg>

After

Width:  |  Height:  |  Size: 167 B

View file

@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" width="11" height="11" viewBox="0 0 11 11" fill="none">
<circle cx="5.5" cy="5.5" r="5" fill="#DD5F5F" stroke="white"/>
</svg>

After

Width:  |  Height:  |  Size: 167 B

View file

@ -0,0 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" width="11" height="11" viewBox="0 0 11 11" fill="none">
<circle cx="5.5" cy="5.5" r="5" fill="#DD5F5F" stroke="white"/>
<path d="M3.4375 5.5H7.5625" stroke="white" stroke-width="1.5" stroke-linecap="round"/>
</svg>

After

Width:  |  Height:  |  Size: 255 B

View file

@ -0,0 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" width="11" height="11" viewBox="0 0 11 11" fill="none">
<circle cx="5.5" cy="5.5" r="5" fill="#4E6074" stroke="white"/>
</svg>

After

Width:  |  Height:  |  Size: 168 B

View file

@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" width="11" height="11" viewBox="0 0 11 11" fill="none">
<circle cx="5.5" cy="5.5" r="5" fill="#4FAE80" stroke="white"/>
</svg>

After

Width:  |  Height:  |  Size: 167 B

View file

@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" width="8" height="8" viewBox="0 0 8 8" fill="none">
<path d="M3.52 3.83333V0.5C3.52 0.367392 3.57057 0.240215 3.66059 0.146447C3.75061 0.0526784 3.8727 0 4 0C4.1273 0 4.24939 0.0526784 4.33941 0.146447C4.42943 0.240215 4.48 0.367392 4.48 0.5V3.83333C4.48 3.96594 4.42943 4.09312 4.33941 4.18689C4.24939 4.28065 4.1273 4.33333 4 4.33333C3.8727 4.33333 3.75061 4.28065 3.66059 4.18689C3.57057 4.09312 3.52 3.96594 3.52 3.83333ZM6.182 0.416667C6.07542 0.348304 5.94741 0.326015 5.82528 0.354551C5.70314 0.383086 5.59654 0.460189 5.5282 0.569422C5.45985 0.678655 5.43517 0.811376 5.45942 0.939291C5.48367 1.0672 5.55493 1.18019 5.658 1.25417C6.5364 1.84875 7.04 2.79167 7.04 3.83333C7.04 4.67319 6.71972 5.47864 6.1496 6.0725C5.57949 6.66637 4.80626 7 4 7C3.19374 7 2.42051 6.66637 1.8504 6.0725C1.28028 5.47864 0.96 4.67319 0.96 3.83333C0.96 2.79167 1.4636 1.84875 2.342 1.25208C2.43959 1.1759 2.50568 1.06387 2.52684 0.938764C2.54801 0.813656 2.52267 0.684847 2.45596 0.578498C2.38926 0.47215 2.28619 0.396237 2.1677 0.36618C2.04921 0.336122 1.92418 0.354173 1.818 0.416667C0.6624 1.19917 0 2.44542 0 3.83333C0 4.9384 0.421427 5.99821 1.17157 6.77961C1.92172 7.56101 2.93913 8 4 8C5.06087 8 6.07828 7.56101 6.82843 6.77961C7.57857 5.99821 8 4.9384 8 3.83333C8 2.44542 7.3376 1.19917 6.182 0.416667Z" fill="#DBB820"/>
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

View file

@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" width="8" height="8" viewBox="0 0 8 8" fill="none">
<path d="M4 0C3.20888 0 2.43552 0.234596 1.77772 0.674121C1.11992 1.11365 0.607234 1.73836 0.304484 2.46927C0.00173314 3.20017 -0.0774802 4.00444 0.0768607 4.78036C0.231202 5.55628 0.612165 6.26902 1.17157 6.82843C1.73098 7.38784 2.44372 7.7688 3.21964 7.92314C3.99556 8.07748 4.79983 7.99827 5.53073 7.69552C6.26164 7.39277 6.88635 6.88008 7.32588 6.22228C7.7654 5.56448 8 4.79112 8 4C7.99882 2.93949 7.57702 1.92276 6.82713 1.17287C6.07724 0.422981 5.06051 0.00117638 4 0ZM4 7.11111C3.38468 7.11111 2.78318 6.92865 2.27156 6.58679C1.75994 6.24494 1.36118 5.75905 1.12571 5.19057C0.890237 4.62209 0.828627 3.99655 0.94867 3.39305C1.06871 2.78956 1.36502 2.23521 1.80011 1.80011C2.23521 1.36502 2.78956 1.06871 3.39305 0.948668C3.99655 0.828625 4.62209 0.890235 5.19057 1.12571C5.75905 1.36118 6.24494 1.75994 6.58679 2.27156C6.92865 2.78318 7.11111 3.38468 7.11111 4C7.11023 4.82485 6.78217 5.61566 6.19891 6.19891C5.61566 6.78217 4.82485 7.11023 4 7.11111ZM3.55556 4.14815V2.22222C3.55556 2.10435 3.60238 1.9913 3.68573 1.90795C3.76908 1.8246 3.88213 1.77778 4 1.77778C4.11788 1.77778 4.23092 1.8246 4.31427 1.90795C4.39762 1.9913 4.44445 2.10435 4.44445 2.22222V4.14815C4.44445 4.26602 4.39762 4.37907 4.31427 4.46242C4.23092 4.54577 4.11788 4.59259 4 4.59259C3.88213 4.59259 3.76908 4.54577 3.68573 4.46242C3.60238 4.37907 3.55556 4.26602 3.55556 4.14815ZM4.59259 5.62963C4.59259 5.74683 4.55784 5.8614 4.49272 5.95886C4.42761 6.05631 4.33506 6.13226 4.22678 6.17711C4.11849 6.22196 3.99934 6.2337 3.88439 6.21083C3.76944 6.18797 3.66385 6.13153 3.58097 6.04865C3.4981 5.96578 3.44166 5.86019 3.41879 5.74524C3.39593 5.63029 3.40766 5.51114 3.45252 5.40285C3.49737 5.29457 3.57332 5.20202 3.67077 5.13691C3.76823 5.07179 3.8828 5.03704 4 5.03704C4.15717 5.03704 4.30789 5.09947 4.41903 5.2106C4.53016 5.32173 4.59259 5.47246 4.59259 5.62963Z" fill="#DD5F5F"/>
</svg>

After

Width:  |  Height:  |  Size: 1.9 KiB

View file

@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" width="8" height="8" viewBox="0 0 8 8" fill="none">
<circle cx="3.99999" cy="3.99999" r="3.5" transform="rotate(30 3.99999 3.99999)" fill="#EDEDED" stroke="#4FAE80" stroke-linecap="round" stroke-dasharray="4 5"/>
</svg>

After

Width:  |  Height:  |  Size: 260 B

View file

@ -704,30 +704,6 @@
</context>
<context>
<name>CallHistoryLayout</name>
<message>
<location filename="../../view/Control/Container/Call/CallHistoryLayout.qml" line="131"/>
<source>contact_presence_status_online</source>
<extracomment>&quot;En ligne&quot;</extracomment>
<translation>Online</translation>
</message>
<message>
<location filename="../../view/Control/Container/Call/CallHistoryLayout.qml" line="134"/>
<source>contact_presence_status_busy</source>
<extracomment>&quot;Occupé&quot;</extracomment>
<translation>Busy</translation>
</message>
<message>
<location filename="../../view/Control/Container/Call/CallHistoryLayout.qml" line="137"/>
<source>contact_presence_status_do_not_disturb</source>
<extracomment>&quot;Ne pas déranger&quot;</extracomment>
<translation>Do not disturb</translation>
</message>
<message>
<location filename="../../view/Control/Container/Call/CallHistoryLayout.qml" line="139"/>
<source>contact_presence_status_offline</source>
<extracomment>&quot;Hors ligne&quot;</extracomment>
<translation>Offline</translation>
</message>
<message>
<location filename="../../view/Control/Container/Call/CallHistoryLayout.qml" line="163"/>
<source>meeting_info_join_title</source>
@ -4049,6 +4025,36 @@ To enable them in a commercial project, please contact us.</translation>
</context>
<context>
<name>Utils</name>
<message>
<location filename="../../tool/Utils.cpp" line="1519"/>
<source>contact_presence_status_available</source>
<extracomment>&quot;En ligne&quot;</extracomment>
<translation>Available</translation>
</message>
<message>
<location filename="../../tool/Utils.cpp" line="1525"/>
<source>contact_presence_status_busy</source>
<extracomment>&quot;Occupé&quot;</extracomment>
<translation>Busy</translation>
</message>
<message>
<location filename="../../tool/Utils.cpp" line="1528"/>
<source>contact_presence_status_do_not_disturb</source>
<extracomment>&quot;Ne pas déranger&quot;</extracomment>
<translation>Do not disturb</translation>
</message>
<message>
<location filename="../../tool/Utils.cpp" line="1531"/>
<source>contact_presence_status_offline</source>
<extracomment>&quot;Hors ligne&quot;</extracomment>
<translation>Offline</translation>
</message>
<message>
<location filename="../../tool/Utils.cpp" line="1522"/>
<source>contact_presence_status_away</source>
<extracomment>&quot;Absent&quot;</extracomment>
<translation>Idle/Away</translation>
</message>
<message>
<location filename="../../tool/Utils.cpp" line="151"/>
<source>information_popup_call_not_created_message</source>
@ -5605,4 +5611,131 @@ To enable them in a commercial project, please contact us.</translation>
<translation>Call forward deactivated</translation>
</message>
</context>
<context>
<name>Presence</name>
<message>
<location filename="../../view/Control/Display/Contact/Presence.qml" line="12"/>
<source>contact_presence_status_available</source>
<extracomment>&quot;En ligne&quot;</extracomment>
<translation>Available</translation>
</message>
<message>
<location filename="../../view/Control/Display/Contact/Presence.qml" line="14"/>
<source>contact_presence_status_busy</source>
<extracomment>&quot;Occupé&quot;</extracomment>
<translation>Busy</translation>
</message>
<message>
<location filename="../../view/Control/Display/Contact/Presence.qml" line="15"/>
<source>contact_presence_status_do_not_disturb</source>
<extracomment>&quot;Ne pas déranger&quot;</extracomment>
<translation>Do not disturb</translation>
</message>
<message>
<location filename="../../view/Control/Display/Contact/Presence.qml" line="16"/>
<source>contact_presence_status_offline</source>
<extracomment>&quot;Hors ligne&quot;</extracomment>
<translation>Offline</translation>
</message>
<message>
<location filename="../../view/Control/Display/Contact/Presence.qml" line="13"/>
<source>contact_presence_status_away</source>
<extracomment>&quot;Absent&quot;</extracomment>
<translation>Idle/Away</translation>
</message>
<message>
<location filename="../../view/Control/Display/Contact/Presence.qml" line="13"/>
<source>contact_presence_reset_status</source>
<extracomment>&quot;Reset status&quot;</extracomment>
<translation>Reset status</translation>
</message>
<message>
<location filename="../../view/Control/Display/Contact/Presence.qml"/>
<source>contact_presence_save_status</source>
<extracomment>&quot;Save&quot;</extracomment>
<translation>Save</translation>
</message>
<message>
<location filename="../../view/Control/Display/Contact/Presence.qml"/>
<source>contact_presence_edit_status</source>
<extracomment>&quot;Edit&quot;</extracomment>
<translation>Edit</translation>
</message>
<message>
<location filename="../../view/Control/Display/Contact/Presence.qml"/>
<source>contact_presence_delete_status</source>
<extracomment>&quot;Delete&quot;</extracomment>
<translation>Delete</translation>
</message>
<message>
<location filename="../../view/Control/Display/Contact/Presence.qml"/>
<source>contact_presence_delete_status</source>
<extracomment>&quot;Delete&quot;</extracomment>
<translation>Delete</translation>
</message>
<message>
<location filename="../../view/Control/Display/Contact/Presence.qml"/>
<source>contact_presence_button_set_custom_status</source>
<extracomment>&quot;Set&quot;</extracomment>
<translation>Set</translation>
</message>
<message>
<location filename="../../view/Control/Display/Contact/Presence.qml"/>
<source>contact_presence_button_edit_custom_status</source>
<extracomment>&quot;Edit&quot;</extracomment>
<translation>Edit</translation>
</message>
<message>
<location filename="../../view/Control/Display/Contact/Presence.qml"/>
<source>contact_presence_button_delete_custom_status</source>
<extracomment>&quot;Delete&quot;</extracomment>
<translation>Delete</translation>
</message>
<message>
<location filename="../../view/Control/Display/Contact/Presence.qml"/>
<source>contact_presence_button_set_custom_status_title</source>
<extracomment>&quot;Set a custom status message&quot;</extracomment>
<translation>Set a custom status message</translation>
</message>
<message>
<location filename="../../view/Control/Display/Contact/Presence.qml"/>
<source>contact_presence_custom_status</source>
<extracomment>&quot;Custom status&quot;</extracomment>
<translation>Custom status</translation>
</message>
<message>
<location filename="../../view/Control/Display/Contact/Presence.qml"/>
<source>contact_presence_custom_status</source>
<extracomment>&quot;Custom status&quot;</extracomment>
<translation>Custom status</translation>
</message>
</context>
<context>
<name>PresenceSetCustomStatus</name>
<message>
<location filename="../../view/Control/Display/Contact/PresenceSetCustomStatus.qml"/>
<source>contact_presence_button_set_custom_status_title</source>
<extracomment>&quot;Set a custom status message&quot;</extracomment>
<translation>Set a custom status message</translation>
</message>
<message>
<location filename="../../view/Control/Display/Contact/PresenceSetCustomStatus.qml"/>
<source>contact_presence_button_save_custom_status</source>
<extracomment>&quot;Save&quot;</extracomment>
<translation>Save</translation>
</message>
</context>
<context>
<name>PresenceNoteLayout</name>
<message>
<location filename="../../view/Control/Container/Contact/PresenceNoteLayout.qml"/>
<source>contact_presence_note_title</source>
<extracomment>&quot;Message personnalisé&quot;</extracomment>
<translation>Custom message</translation>
</message>
</context>
</TS>

View file

@ -704,30 +704,6 @@
</context>
<context>
<name>CallHistoryLayout</name>
<message>
<location filename="../../view/Control/Container/Call/CallHistoryLayout.qml" line="131"/>
<source>contact_presence_status_online</source>
<extracomment>&quot;En ligne&quot;</extracomment>
<translation>En ligne</translation>
</message>
<message>
<location filename="../../view/Control/Container/Call/CallHistoryLayout.qml" line="134"/>
<source>contact_presence_status_busy</source>
<extracomment>&quot;Occupé&quot;</extracomment>
<translation>Occupé</translation>
</message>
<message>
<location filename="../../view/Control/Container/Call/CallHistoryLayout.qml" line="137"/>
<source>contact_presence_status_do_not_disturb</source>
<extracomment>&quot;Ne pas déranger&quot;</extracomment>
<translation>Ne pas déranger</translation>
</message>
<message>
<location filename="../../view/Control/Container/Call/CallHistoryLayout.qml" line="139"/>
<source>contact_presence_status_offline</source>
<extracomment>&quot;Hors ligne&quot;</extracomment>
<translation>Hors ligne</translation>
</message>
<message>
<location filename="../../view/Control/Container/Call/CallHistoryLayout.qml" line="163"/>
<source>meeting_info_join_title</source>
@ -4041,6 +4017,36 @@ Pour les activer dans un projet commercial, merci de nous contacter.</translatio
</context>
<context>
<name>Utils</name>
<message>
<location filename="../../tool/Utils.cpp" line="1519"/>
<source>contact_presence_status_available</source>
<extracomment>&quot;En ligne&quot;</extracomment>
<translation>Disponible</translation>
</message>
<message>
<location filename="../../tool/Utils.cpp" line="1525"/>
<source>contact_presence_status_busy</source>
<extracomment>&quot;Occupé&quot;</extracomment>
<translation>Occupé</translation>
</message>
<message>
<location filename="../../tool/Utils.cpp" line="1528"/>
<source>contact_presence_status_do_not_disturb</source>
<extracomment>&quot;Ne pas déranger&quot;</extracomment>
<translation>Ne pas déranger</translation>
</message>
<message>
<location filename="../../tool/Utils.cpp" line="1531"/>
<source>contact_presence_status_offline</source>
<extracomment>&quot;Hors ligne&quot;</extracomment>
<translation>Hors ligne</translation>
</message>
<message>
<location filename="../../tool/Utils.cpp" line="1522"/>
<source>contact_presence_status_away</source>
<extracomment>&quot;Absent&quot;</extracomment>
<translation>Inactif/Absent</translation>
</message>
<message>
<location filename="../../tool/Utils.cpp" line="151"/>
<source>information_popup_call_not_created_message</source>
@ -5520,4 +5526,46 @@ Pour les activer dans un projet commercial, merci de nous contacter.</translatio
<translation>Ok</translation>
</message>
</context>
<context>
<name>Presence</name>
<message>
<location filename="../../view/Control/Display/Contact/Presence.qml" line="12"/>
<source>contact_presence_status_available</source>
<extracomment>&quot;En ligne&quot;</extracomment>
<translation>Disponible</translation>
</message>
<message>
<location filename="../../view/Control/Display/Contact/Presence.qml" line="14"/>
<source>contact_presence_status_busy</source>
<extracomment>&quot;Occupé&quot;</extracomment>
<translation>Occupé</translation>
</message>
<message>
<location filename="../../view/Control/Display/Contact/Presence.qml" line="15"/>
<source>contact_presence_status_do_not_disturb</source>
<extracomment>&quot;Ne pas déranger&quot;</extracomment>
<translation>Ne pas déranger</translation>
</message>
<message>
<location filename="../../view/Control/Display/Contact/Presence.qml" line="16"/>
<source>contact_presence_status_offline</source>
<extracomment>&quot;Hors ligne&quot;</extracomment>
<translation>Hors ligne</translation>
</message>
<message>
<location filename="../../view/Control/Display/Contact/Presence.qml" line="13"/>
<source>contact_presence_status_away</source>
<extracomment>&quot;Absent&quot;</extracomment>
<translation>Inactif/Absent</translation>
</message>
</context>
<context>
<name>PresenceNoteLayout</name>
<message>
<location filename="../../view/Control/Container/Contact/PresenceNoteLayout.qml"/>
<source>contact_presence_note_title</source>
<extracomment>&quot;Message personnalisé&quot;</extracomment>
<translation>Message personnalisé</translation>
</message>
</context>
</TS>

View file

@ -22,6 +22,8 @@
#include "core/path/Paths.hpp"
#include "model/core/CoreModel.hpp"
#include "model/setting/SettingsModel.hpp"
#include "model/tool/ToolModel.hpp"
#include "tool/Utils.hpp"
#include "tool/providers/AvatarProvider.hpp"
#include <QDebug>
@ -467,3 +469,94 @@ void AccountModel::removeUserData(const std::shared_ptr<linphone::Account> &acco
mustBeInLinphoneThread(sLog().arg(Q_FUNC_INFO));
userDataMap.remove(account);
}
// Up to date there are api to get/set presence from/to a linphone account object
// therefore retrieved/set from core
LinphoneEnums::Presence AccountModel::getPresence() {
auto presenceModel = CoreModel::getInstance()->getCore()->getPresenceModel(); // No api (yet) at the account level
return ToolModel::corePresenceModelToAppPresence(presenceModel);
}
void AccountModel::setPresence(LinphoneEnums::Presence presence,
bool userInitiated,
bool resetToAuto,
QString presenceNote) {
mustBeInLinphoneThread(log().arg(Q_FUNC_INFO));
lDebug() << log().arg("presence set request to: " + LinphoneEnums::toString(presence) + " user initiated? " +
(userInitiated ? "true" : "false") + " reset to auto? " + (resetToAuto ? "true" : "false"));
auto core = CoreModel::getInstance()->getCore();
if (core->getGlobalState() != linphone::GlobalState::On) {
lWarning() << log().arg("Unable to set presence this time as core state is not ON");
return;
}
if (presence == LinphoneEnums::Presence::Undefined) {
lWarning() << log().arg("Unable to set Undefined presence");
return;
}
auto accountSection = ToolModel::configAccountSection(mMonitor);
if (!resetToAuto && !userInitiated &&
!core->getConfig()->getString(accountSection, "explicit_presence", "").empty()) {
lDebug() << log().arg("Ignoring automatic presence update, as user already set a presence explicitely : ")
<< Utils::coreStringToAppString(core->getConfig()->getString(accountSection, "explicit_presence", ""));
return;
}
if (userInitiated) {
core->getConfig()->setString(accountSection, "explicit_presence",
Utils::appStringToCoreString(LinphoneEnums::toString(presence)));
core->getConfig()->sync();
}
if (resetToAuto) {
core->getConfig()->cleanEntry(accountSection, "explicit_presence");
core->getConfig()->sync();
}
if (!presenceNote.isEmpty()) {
core->getConfig()->setString(accountSection, "presence_note", Utils::appStringToCoreString(presenceNote));
core->getConfig()->sync();
}
if (!mMonitor->getParams()->publishEnabled()) {
auto params = mMonitor->getParams()->clone();
params->enablePublish(true);
mMonitor->setParams(params);
}
auto presenceModel = ToolModel::appPresenceToCorePresenceModel(presence, presenceNote);
core->setPresenceModel(presenceModel); // No api (yet) at the account level
if (presence == LinphoneEnums::Presence::Offline) {
for (auto friendList : core->getFriendsLists())
friendList->enableSubscriptions(false);
} else {
for (auto friendList : core->getFriendsLists())
friendList->enableSubscriptions(true);
}
setNotificationsAllowed(
presence != LinphoneEnums::Presence::DoNotDisturb &&
(presence != LinphoneEnums::Presence::Away ||
core->getConfig()->getBool(accountSection, "allow_notifications_in_presence_away", true)) &&
(presence != LinphoneEnums::Presence::Busy ||
core->getConfig()->getBool(accountSection, "allow_notifications_in_presence_busy", true)));
if (!SettingsModel::dndEnabled(core->getConfig())) {
SettingsModel::getInstance()->enableRinging(presence != LinphoneEnums::Presence::DoNotDisturb);
}
emit presenceChanged(presence, userInitiated);
}
bool AccountModel::forwardToVoiceMailInDndPresence() {
auto accountSection = ToolModel::configAccountSection(mMonitor);
auto core = CoreModel::getInstance()->getCore();
return core->getConfig()->getBool(accountSection, "forward_to_voicemail_in_dnd_presence", false);
}

View file

@ -24,6 +24,7 @@
#include "model/listener/Listener.hpp"
#include "tool/AbstractObject.hpp"
#include "tool/LinphoneEnums.hpp"
#include <QObject>
#include <linphone++/linphone.hh>
@ -83,6 +84,10 @@ public:
bool getShowMwi();
void setVoicemailAddress(QString value);
QString getVoicemailAddress() const;
LinphoneEnums::Presence getPresence();
void setPresence(LinphoneEnums::Presence presence, bool userInitiated, bool resetToAuto, QString presenceNote);
std::string configAccountSection();
bool forwardToVoiceMailInDndPresence();
signals:
void registrationStateChanged(const std::shared_ptr<linphone::Account> &account,
@ -112,6 +117,7 @@ signals:
void voicemailCountChanged(int count);
void showMwiChanged(bool show);
void voicemailAddressChanged(QString value);
void presenceChanged(LinphoneEnums::Presence presence, bool userInitiated);
private:
/**Linphone **/

View file

@ -310,7 +310,7 @@ void OIDCModel::setBearers() {
qWarning() << "No refresh token found";
}
CoreModel::getInstance()->getCore()->addAuthInfo(mAuthInfo);
emit CoreModel::getInstance() -> bearerAccountAdded();
emit CoreModel::getInstance()->bearerAccountAdded();
emit finished();
}
QString OIDCModel::idToken() const {
@ -319,4 +319,4 @@ QString OIDCModel::idToken() const {
#else
return mIdToken;
#endif
}
}

View file

@ -425,6 +425,14 @@ void CoreModel::onCallStateChanged(const std::shared_ptr<linphone::Core> &core,
core->getCallsNb() == 0) { // Disable tones in DND mode if no more calls are running.
SettingsModel::getInstance()->setCallToneIndicationsEnabled(false);
}
App::postModelAsync([core]() {
for (int i = 0; i < App::getInstance()->getAccountList()->rowCount(); ++i) {
auto accountCore = App::getInstance()->getAccountList()->getAt<AccountCore>(i);
emit accountCore->lSetPresence(core->getCallsNb() == 0 ? LinphoneEnums::Presence::Online
: LinphoneEnums::Presence::Busy,
false, false);
}
});
emit callStateChanged(core, call, state, message);
}
void CoreModel::onCallStatsUpdated(const std::shared_ptr<linphone::Core> &core,

View file

@ -71,14 +71,6 @@ std::shared_ptr<linphone::Friend> FriendModel::getFriend() const {
return mMonitor;
}
QDateTime FriendModel::getPresenceTimestamp() const {
if (mMonitor && mMonitor->getPresenceModel()) {
time_t timestamp = mMonitor->getPresenceModel()->getLatestActivityTimestamp();
if (timestamp == -1) return QDateTime();
else return QDateTime::fromMSecsSinceEpoch(timestamp * 1000);
} else return QDateTime();
}
void FriendModel::setAddress(const std::shared_ptr<linphone::Address> &address) {
if (!mMonitor) return;
if (address) {
@ -316,8 +308,22 @@ void FriendModel::setStarred(bool starred) {
mMonitor->setStarred(starred);
emit starredChanged(starred);
}
void FriendModel::onPresenceReceived(const std::shared_ptr<linphone::Friend> &contact) {
emit presenceReceived(LinphoneEnums::fromLinphone(contact->getConsolidatedPresence()), getPresenceTimestamp());
emit presenceReceived(getPresence(contact), getPresenceNote(contact));
}
LinphoneEnums::Presence FriendModel::getPresence(const std::shared_ptr<linphone::Friend> &contact) {
auto presenceModel = contact->getPresenceModel();
return ToolModel::corePresenceModelToAppPresence(presenceModel);
}
QString FriendModel::getPresenceNote(const std::shared_ptr<linphone::Friend> &contact) {
auto presenceModel = contact->getPresenceModel();
auto note = presenceModel && presenceModel->getNote(Utils::appStringToCoreString(QLocale().name().left(2)))
? presenceModel->getNote(Utils::appStringToCoreString(QLocale().name().left(2)))->getContent()
: "";
return Utils::coreStringToAppString(note);
}
QString FriendModel::getPictureUri() const {

View file

@ -91,6 +91,8 @@ public:
void onUpdated(const std::shared_ptr<linphone::Friend> &data);
void onRemoved(const std::shared_ptr<linphone::Friend> &data);
LinphoneEnums::Presence getPresence(const std::shared_ptr<linphone::Friend> &contact);
QString mFullName;
signals:
@ -105,12 +107,13 @@ signals:
void familyNameChanged(const QString &name);
void organizationChanged(const QString &orga);
void jobChanged(const QString &job);
void presenceReceived(LinphoneEnums::ConsolidatedPresence consolidatedPresence, QDateTime presenceTimestamp);
void presenceReceived(LinphoneEnums::Presence presence, QString presenceNote);
void updated();
void removed();
private:
DECLARE_ABSTRACT_OBJECT
QString getPresenceNote(const std::shared_ptr<linphone::Friend> &contact);
//--------------------------------------------------------------------------------
// LINPHONE

View file

@ -579,4 +579,85 @@ std::shared_ptr<linphone::ChatRoom> ToolModel::createChatForAddress(std::shared_
}
auto chatRoom = core->createChatRoom(params, participants);
return chatRoom;
}
}
// Presence mapping from SDK PresenceModel/RFC 3863 <-> Linphone UI (5 statuses Online, Offline, Away, Busy, DND).
// Online = Basic Status open with no activity
// Busy = Basic Status open with activity Busy and description busy
// Away = Basic Status open with activity Away and description away
// Offline = Basic Status open with activity PermanentAbsence and description offline
// DND = Basic Status open with activity Other and description dnd
// Note : close status on the last 2 items would be preferrable, but they currently trigger multiple tuple NOTIFY from
// flexisip presence server Note 2 : close status with no activity triggers an unsubscribe.
LinphoneEnums::Presence
ToolModel::corePresenceModelToAppPresence(std::shared_ptr<const linphone::PresenceModel> presenceModel) {
if (!presenceModel) {
lWarning() << sLog().arg("presence model is null.");
return LinphoneEnums::Presence::Undefined;
}
auto presenceActivity = presenceModel->getActivity();
if (presenceModel->getBasicStatus() == linphone::PresenceBasicStatus::Open) {
if (!presenceActivity) return LinphoneEnums::Presence::Online;
else if (presenceActivity->getType() == linphone::PresenceActivity::Type::Busy)
return LinphoneEnums::Presence::Busy;
else if (presenceActivity->getType() == linphone::PresenceActivity::Type::Away)
return LinphoneEnums::Presence::Away;
else if (presenceActivity->getType() == linphone::PresenceActivity::Type::PermanentAbsence)
return LinphoneEnums::Presence::Offline;
else if (presenceActivity->getType() == linphone::PresenceActivity::Type::Other)
return LinphoneEnums::Presence::DoNotDisturb;
else {
lWarning() << sLog().arg("unhandled core activity type : ") << (int)presenceActivity->getType();
return LinphoneEnums::Presence::Undefined;
}
}
return LinphoneEnums::Presence::Undefined;
}
std::shared_ptr<linphone::PresenceModel> ToolModel::appPresenceToCorePresenceModel(LinphoneEnums::Presence presence,
QString presenceNote) {
auto presenceModel = CoreModel::getInstance()->getCore()->createPresenceModel();
switch (presence) {
case LinphoneEnums::Presence::Online:
presenceModel->setBasicStatus(linphone::PresenceBasicStatus::Open);
break;
case LinphoneEnums::Presence::Busy:
presenceModel->setBasicStatus(linphone::PresenceBasicStatus::Open);
presenceModel->setActivity(linphone::PresenceActivity::Type::Busy, "busy");
break;
case LinphoneEnums::Presence::Away:
presenceModel->setBasicStatus(linphone::PresenceBasicStatus::Open);
presenceModel->setActivity(linphone::PresenceActivity::Type::Away, "away");
break;
case LinphoneEnums::Presence::Offline:
presenceModel->setBasicStatus(linphone::PresenceBasicStatus::Open);
presenceModel->setActivity(linphone::PresenceActivity::Type::PermanentAbsence, "offline");
break;
case LinphoneEnums::Presence::DoNotDisturb:
presenceModel->setBasicStatus(linphone::PresenceBasicStatus::Open);
presenceModel->setActivity(linphone::PresenceActivity::Type::Other, "dnd");
break;
case LinphoneEnums::Presence::Undefined:
lWarning() << sLog().arg("Trying to build PresenceModel from Undefined presence ");
return nullptr;
}
if (!presenceNote.isEmpty()) {
auto note = CoreModel::getInstance()->getCore()->createPresenceNote(
Utils::appStringToCoreString(presenceNote), Utils::appStringToCoreString(QLocale().name().left(2)));
auto service = presenceModel->getNthService(0);
service->addNote(note);
}
return presenceModel;
}
std::string ToolModel::configAccountSection(const std::shared_ptr<linphone::Account> &account) {
int count = 0;
for (auto item : CoreModel::getInstance()->getCore()->getAccountList()) {
if (account == item) return "proxy_" + std::to_string(count);
count++;
}
return "account";
}

View file

@ -88,6 +88,12 @@ public:
static std::shared_ptr<linphone::ChatRoom> lookupChatForAddress(std::shared_ptr<linphone::Address> remoteAddress);
static std::shared_ptr<linphone::ChatRoom> createChatForAddress(std::shared_ptr<linphone::Address> remoteAddress);
static LinphoneEnums::Presence
corePresenceModelToAppPresence(std::shared_ptr<const linphone::PresenceModel> presenceModel);
static std::shared_ptr<linphone::PresenceModel> appPresenceToCorePresenceModel(LinphoneEnums::Presence presence,
QString presenceNote);
static std::string configAccountSection(const std::shared_ptr<linphone::Account> &account);
private:
DECLARE_ABSTRACT_OBJECT
};

View file

@ -44,6 +44,7 @@ void LinphoneEnums::registerMetaTypes() {
qRegisterMetaType<LinphoneEnums::TunnelMode>();
qRegisterMetaType<LinphoneEnums::TransportType>();
qRegisterMetaType<LinphoneEnums::VideoSourceScreenSharingType>();
qRegisterMetaType<LinphoneEnums::Presence>();
qmlRegisterUncreatableMetaObject(LinphoneEnums::staticMetaObject, Constants::MainQmlUri, 1, 0, "LinphoneEnums",
"Only enums");
}
@ -207,7 +208,8 @@ LinphoneEnums::ConferenceLayout LinphoneEnums::fromLinphone(const linphone::Conf
QString LinphoneEnums::toString(LinphoneEnums::ConferenceLayout layout) {
//: "Participant actif"
if (layout == LinphoneEnums::ConferenceLayout::ActiveSpeaker) return QObject::tr("conference_layout_active_speaker");
if (layout == LinphoneEnums::ConferenceLayout::ActiveSpeaker)
return QObject::tr("conference_layout_active_speaker");
//: "Mosaïque"
else if (layout == LinphoneEnums::ConferenceLayout::Grid) return QObject::tr("conference_layout_grid");
//: "Audio uniquement"
@ -249,11 +251,22 @@ LinphoneEnums::ConferenceSchedulerState LinphoneEnums::fromLinphone(const linpho
return static_cast<LinphoneEnums::ConferenceSchedulerState>(state);
}
linphone::ConsolidatedPresence LinphoneEnums::toLinphone(const LinphoneEnums::ConsolidatedPresence &data) {
return static_cast<linphone::ConsolidatedPresence>(data);
QString LinphoneEnums::toString(Presence presence) {
const QMetaObject &metaObj = LinphoneEnums::staticMetaObject;
int index = metaObj.indexOfEnumerator("Presence");
QMetaEnum metaEnum = metaObj.enumerator(index);
return metaEnum.valueToKey(static_cast<int>(presence));
}
LinphoneEnums::ConsolidatedPresence LinphoneEnums::fromLinphone(const linphone::ConsolidatedPresence &data) {
return static_cast<LinphoneEnums::ConsolidatedPresence>(data);
LinphoneEnums::Presence LinphoneEnums::fromString(const QString &key) {
const QMetaObject &metaObj = LinphoneEnums::staticMetaObject;
int index = metaObj.indexOfEnumerator("Presence");
QMetaEnum metaEnum = metaObj.enumerator(index);
int value = metaEnum.keyToValue(key.toUtf8().constData());
if (value == -1) {
return LinphoneEnums::Presence::Undefined;
}
return static_cast<LinphoneEnums::Presence>(value);
}
linphone::MagicSearch::Aggregation LinphoneEnums::toLinphone(const LinphoneEnums::MagicSearchAggregation &data) {

View file

@ -250,16 +250,12 @@ Q_ENUM_NS(ConferenceSchedulerState)
linphone::ConferenceScheduler::State toLinphone(const LinphoneEnums::ConferenceSchedulerState &state);
LinphoneEnums::ConferenceSchedulerState fromLinphone(const linphone::ConferenceScheduler::State &state);
enum class ConsolidatedPresence {
Online = int(linphone::ConsolidatedPresence::Online),
Busy = int(linphone::ConsolidatedPresence::Busy),
DoNotDisturb = int(linphone::ConsolidatedPresence::DoNotDisturb),
Offline = int(linphone::ConsolidatedPresence::Offline)
};
Q_ENUM_NS(ConsolidatedPresence);
// App Presence
enum class Presence { Undefined, Online, Busy, DoNotDisturb, Offline, Away };
Q_ENUM_NS(Presence);
linphone::ConsolidatedPresence toLinphone(const LinphoneEnums::ConsolidatedPresence &state);
LinphoneEnums::ConsolidatedPresence fromLinphone(const linphone::ConsolidatedPresence &state);
QString toString(Presence presence);
Presence fromString(const QString &key);
enum class MagicSearchAggregation {
Friend = int(linphone::MagicSearch::Aggregation::Friend),

View file

@ -43,6 +43,8 @@
#include <QHostAddress>
#include <QImageReader>
#include <QProcess>
#include <QQmlComponent>
#include <QQmlProperty>
#include <QQuickWindow>
#include <QRandomGenerator>
#include <QRegularExpression>
@ -1620,3 +1622,126 @@ void Utils::runCommandLine(const QString command) {
lWarning() << "Unsupported OS!";
#endif
}
// Presence
QColor Utils::getDefaultStyleColor(const QString &colorName) {
static QObject *defaultStyleSingleton = nullptr;
if (!defaultStyleSingleton) {
QQmlComponent component(App::getInstance()->mEngine, QUrl("qrc:/qt/qml/Linphone/view/Style/DefaultStyle.qml"));
defaultStyleSingleton = component.create();
}
return QQmlProperty::read(defaultStyleSingleton, colorName).value<QColor>();
}
QUrl Utils::getAppIcon(const QString &iconName) {
static QObject *appIconsSingleton = nullptr;
if (!appIconsSingleton) {
QQmlComponent component(App::getInstance()->mEngine, QUrl("qrc:/qt/qml/Linphone/view/Style/AppIcons.qml"));
appIconsSingleton = component.create();
}
return QQmlProperty::read(appIconsSingleton, iconName).value<QUrl>();
}
QColor Utils::getPresenceColor(LinphoneEnums::Presence presence) {
mustBeInMainThread(sLog().arg(Q_FUNC_INFO));
QColor presenceColor = QColorConstants::Transparent;
switch (presence) {
case LinphoneEnums::Presence::Online:
presenceColor = Utils::getDefaultStyleColor("success_500main");
break;
case LinphoneEnums::Presence::Away:
presenceColor = Utils::getDefaultStyleColor("warning_500_main");
break;
case LinphoneEnums::Presence::Busy:
presenceColor = Utils::getDefaultStyleColor("danger_500main");
break;
case LinphoneEnums::Presence::DoNotDisturb:
presenceColor = Utils::getDefaultStyleColor("danger_500main");
break;
case LinphoneEnums::Presence::Offline:
presenceColor = Utils::getDefaultStyleColor("main2_600");
break;
case LinphoneEnums::Presence::Undefined:
presenceColor = Utils::getDefaultStyleColor("transparent");
break;
}
return presenceColor;
}
QUrl Utils::getPresenceIcon(LinphoneEnums::Presence presence) {
mustBeInMainThread(sLog().arg(Q_FUNC_INFO));
QUrl presenceIcon;
switch (presence) {
case LinphoneEnums::Presence::Online:
presenceIcon = Utils::getAppIcon("presenceOnline");
break;
case LinphoneEnums::Presence::Away:
presenceIcon = Utils::getAppIcon("presenceAway");
break;
case LinphoneEnums::Presence::Busy:
presenceIcon = Utils::getAppIcon("presenceBusy");
break;
case LinphoneEnums::Presence::DoNotDisturb:
presenceIcon = Utils::getAppIcon("presenceDoNotDisturb");
break;
case LinphoneEnums::Presence::Offline:
presenceIcon = Utils::getAppIcon("presenceOffline");
break;
case LinphoneEnums::Presence::Undefined:
presenceIcon = QUrl("");
break;
}
return presenceIcon;
}
QUrl Utils::getRegistrationStateIcon(LinphoneEnums::RegistrationState state) {
mustBeInMainThread(sLog().arg(Q_FUNC_INFO));
QUrl registrationStateIcon;
switch (state) {
case LinphoneEnums::RegistrationState::Refreshing:
registrationStateIcon = Utils::getAppIcon("regitrationProgress");
break;
case LinphoneEnums::RegistrationState::Progress:
registrationStateIcon = Utils::getAppIcon("regitrationProgress");
break;
case LinphoneEnums::RegistrationState::Failed:
registrationStateIcon = Utils::getAppIcon("regitrationError");
break;
case LinphoneEnums::RegistrationState::Cleared:
registrationStateIcon = Utils::getAppIcon("regitrationDeactivated");
break;
case LinphoneEnums::RegistrationState::None:
registrationStateIcon = Utils::getAppIcon("regitrationDeactivated");
break;
default:
registrationStateIcon = QUrl();
}
return registrationStateIcon;
}
QString Utils::getPresenceStatus(LinphoneEnums::Presence presence) {
mustBeInMainThread(sLog().arg(Q_FUNC_INFO));
QString presenceStatus = "";
switch (presence) {
case LinphoneEnums::Presence::Online:
presenceStatus = tr("contact_presence_status_available");
break;
case LinphoneEnums::Presence::Away:
presenceStatus = tr("contact_presence_status_away");
break;
case LinphoneEnums::Presence::Busy:
presenceStatus = tr("contact_presence_status_busy");
break;
case LinphoneEnums::Presence::DoNotDisturb:
presenceStatus = tr("contact_presence_status_do_not_disturb");
break;
case LinphoneEnums::Presence::Offline:
presenceStatus = tr("contact_presence_status_offline");
break;
case LinphoneEnums::Presence::Undefined:
presenceStatus = "";
break;
}
return presenceStatus;
}

View file

@ -142,6 +142,9 @@ public:
Q_INVOKABLE static QString getFileChecksum(const QString &filePath);
Q_INVOKABLE QList<QVariant> append(const QList<QVariant> a, const QList<QVariant> b);
Q_INVOKABLE QString getAddressToDisplay(QVariantList addressList, QString filter, QString defaultAddress);
Q_INVOKABLE static QColor getPresenceColor(LinphoneEnums::Presence presence);
Q_INVOKABLE static QUrl getPresenceIcon(LinphoneEnums::Presence presence);
Q_INVOKABLE static QString getPresenceStatus(LinphoneEnums::Presence presence);
Q_INVOKABLE static VariantObject *getCurrentCallChat(CallGui *call);
Q_INVOKABLE static VariantObject *getChatForAddress(QString address);
@ -195,6 +198,12 @@ public:
static void runCommandLine(QString command);
// Presence
static QColor getDefaultStyleColor(const QString &colorName);
static QUrl getAppIcon(const QString &iconName);
static QUrl getRegistrationStateIcon(LinphoneEnums::RegistrationState state);
private:
DECLARE_ABSTRACT_OBJECT
};

View file

@ -34,6 +34,7 @@ list(APPEND _LINPHONEAPP_QML_FILES
view/Control/Container/Call/CallGridLayout.qml
view/Control/Container/Call/Mosaic.qml
view/Control/Container/Contact/ContactLayout.qml
view/Control/Container/Contact/PresenceNoteLayout.qml
view/Control/Container/Main/MainRightPanel.qml
view/Control/Display/BusyIndicator.qml
@ -54,6 +55,9 @@ list(APPEND _LINPHONEAPP_QML_FILES
view/Control/Display/Chat/ChatMessagesListView.qml
view/Control/Display/Contact/Avatar.qml
view/Control/Display/Contact/Contact.qml
view/Control/Display/Contact/Presence.qml
view/Control/Display/Contact/PresenceStatusItem.qml
view/Control/Display/Contact/PresenceSetCustomStatus.qml
view/Control/Display/Contact/ContactListItem.qml
view/Control/Display/Contact/ContactListView.qml
view/Control/Display/Contact/AllContactListView.qml

View file

@ -122,29 +122,12 @@ ColumnLayout {
}
}
Text {
property var mode : contact ? contact.core.consolidatedPresence : -1
Layout.alignment: Qt.AlignHCenter
horizontalAlignment: Text.AlignHCenter
Layout.fillWidth: true
visible: mainItem.contact
text: mode === LinphoneEnums.ConsolidatedPresence.Online
//: "En ligne"
? qsTr("contact_presence_status_online")
: mode === LinphoneEnums.ConsolidatedPresence.Busy
//: "Occupé"
? qsTr("contact_presence_status_busy")
: mode === LinphoneEnums.ConsolidatedPresence.DoNotDisturb
//: "Ne pas déranger"
? qsTr("contact_presence_status_do_not_disturb")
//: "Hors ligne"
: qsTr("contact_presence_status_offline")
color: mode === LinphoneEnums.ConsolidatedPresence.Online
? DefaultStyle.success_500main
: mode === LinphoneEnums.ConsolidatedPresence.Busy
? DefaultStyle.warning_600
: mode === LinphoneEnums.ConsolidatedPresence.DoNotDisturb
? DefaultStyle.danger_500main
: DefaultStyle.main2_500main
text: contact ? contact.core.presenceStatus : ""
color: contact ? contact.core.presenceColor : 'transparent'
font {
pixelSize: Math.round(12 * DefaultStyle.dp)
weight: Math.round(300 * DefaultStyle.dp)

View file

@ -30,40 +30,50 @@ ColumnLayout {
columnSpacing: Math.round(49 * DefaultStyle.dp)
rowSpacing: Math.round(27 * DefaultStyle.dp)
RowLayout {
Layout.preferredWidth: Math.round(341 * DefaultStyle.dp)
Control.Control {
// Layout.preferredWidth: Math.round(734 * DefaultStyle.dp)
Layout.fillWidth: true
width: Math.round(734 * DefaultStyle.dp)
height: Math.round(100 * DefaultStyle.dp)
rightPadding: Math.round(21 * DefaultStyle.dp)
background: GradientRectangle {
anchors.fill: parent
anchors.leftMargin: avatar.width / 2
radius: Math.round(15 * DefaultStyle.dp)
borderGradient: Gradient {
orientation: Gradient.Horizontal
GradientStop { position: 0.0; color: DefaultStyle.grey_100 }
GradientStop { position: 1.0; color: DefaultStyle.main2_200 }
ColumnLayout {
spacing: Math.round(16 * DefaultStyle.dp)
Layout.preferredWidth: Math.round(341 * DefaultStyle.dp)
RowLayout {
Layout.preferredWidth: Math.round(341 * DefaultStyle.dp)
Control.Control {
// Layout.preferredWidth: Math.round(734 * DefaultStyle.dp)
Layout.fillWidth: true
width: Math.round(734 * DefaultStyle.dp)
height: Math.round(100 * DefaultStyle.dp)
rightPadding: Math.round(21 * DefaultStyle.dp)
background: GradientRectangle {
anchors.fill: parent
anchors.leftMargin: avatar.width / 2
radius: Math.round(15 * DefaultStyle.dp)
borderGradient: Gradient {
orientation: Gradient.Horizontal
GradientStop { position: 0.0; color: DefaultStyle.grey_100 }
GradientStop { position: 1.0; color: DefaultStyle.main2_200 }
}
gradient: Gradient {
orientation: Gradient.Horizontal
GradientStop { position: 0.0; color: DefaultStyle.grey_0 }
GradientStop { position: 1.0; color: DefaultStyle.grey_100 }
}
}
gradient: Gradient {
orientation: Gradient.Horizontal
GradientStop { position: 0.0; color: DefaultStyle.grey_0 }
GradientStop { position: 1.0; color: DefaultStyle.grey_100 }
}
}
contentItem: RowLayout {
id: bannerLayout
spacing: Math.round(32 * DefaultStyle.dp)
Avatar {
id: avatar
contact: mainItem.contact
Layout.preferredWidth: Math.round(100 * DefaultStyle.dp)
Layout.preferredHeight: Math.round(100 * DefaultStyle.dp)
contentItem: RowLayout {
id: bannerLayout
spacing: Math.round(32 * DefaultStyle.dp)
Avatar {
id: avatar
contact: mainItem.contact
Layout.preferredWidth: Math.round(100 * DefaultStyle.dp)
Layout.preferredHeight: Math.round(100 * DefaultStyle.dp)
}
}
}
}
PresenceNoteLayout {
visible: contact.core.presenceNote.length > 0 && mainItem.useVerticalLayout
friendCore: contact.core
Layout.preferredWidth: 412 * DefaultStyle.dp
Layout.preferredHeight: 85 * DefaultStyle.dp
}
}
Item {
id: verticalLayoutSecondLine
@ -78,6 +88,18 @@ ColumnLayout {
style: ButtonStyle.main
}
}
Rectangle {
Layout.fillWidth:true
Layout.preferredHeight: 79 * DefaultStyle.dp
color: 'transparent'
visible: contact.core.presenceNote.length > 0 && !mainItem.useVerticalLayout
PresenceNoteLayout {
anchors.centerIn: parent
friendCore: contact.core
width: 412 * DefaultStyle.dp
height: 85 * DefaultStyle.dp
}
}
StackLayout {
id: detailLayout
Layout.alignment: Qt.AlignCenter

View file

@ -0,0 +1,52 @@
import QtQuick
import QtQuick.Effects
import QtQuick.Layouts
import QtQuick.Controls.Basic as Control
import Linphone
import UtilsCpp
import SettingsCpp
import 'qrc:/qt/qml/Linphone/view/Style/buttonStyle.js' as ButtonStyle
Rectangle {
id: mainItem
property var friendCore
color: DefaultStyle.grey_0
radius: 20 * DefaultStyle.dp
border.color: DefaultStyle.main2_200
border.width: 2 * DefaultStyle.dp
ColumnLayout {
anchors.left: parent.left
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
anchors.leftMargin: 16 * DefaultStyle.dp
anchors.rightMargin: 16 * DefaultStyle.dp
spacing: 8 * DefaultStyle.dp
RowLayout {
spacing: 6 * DefaultStyle.dp
EffectImage {
fillMode: Image.PreserveAspectFit
imageSource: AppIcons.presenceNote
colorizationColor: DefaultStyle.main2_600
Layout.preferredHeight: Math.round(17 * DefaultStyle.dp)
Layout.preferredWidth: Math.round(17 * DefaultStyle.dp)
}
Text {
font: Typography.p2
color: DefaultStyle.main2_600
text: qsTr("contact_presence_note_title")
}
}
Text {
font: Typography.p3
color: DefaultStyle.main2_500main
text: mainItem.friendCore.presenceNote
wrapMode: Text.Wrap
Layout.fillWidth: true
}
}
}

View file

@ -52,14 +52,8 @@ Loader{
: false
property bool securityBreach: securityLevel === LinphoneEnums.SecurityLevel.Unsafe
property bool displayPresence: account
? account.core?.registrationState != LinphoneEnums.RegistrationState.Progress && account.core?.registrationState != LinphoneEnums.RegistrationState.Refreshing || false
: contact
? contact.core?.consolidatedPresence != LinphoneEnums.ConsolidatedPresence.Offline || false
: false
property bool displayPresence: true
asynchronous: true
sourceComponent: Component{
Item {
@ -108,39 +102,22 @@ Loader{
}
}
Rectangle {
Image {
visible: mainItem.displayPresence
width: stackView.width/4.5
height: width
radius: width / 2
sourceSize.width: width
sourceSize.height: width
smooth: false
anchors.bottom: parent.bottom
anchors.right: parent.right
anchors.rightMargin: stackView.width / 15
z: 1
color: account
? account.core?.registrationState == LinphoneEnums.RegistrationState.Ok
? DefaultStyle.success_500main
: account.core?.registrationState == LinphoneEnums.RegistrationState.Cleared || account.core?.registrationState == LinphoneEnums.RegistrationState.None
? DefaultStyle.warning_600
: account.core?.registrationState == LinphoneEnums.RegistrationState.Progress || account.core?.registrationState == LinphoneEnums.RegistrationState.Refreshing
? DefaultStyle.main2_500main
: DefaultStyle.danger_500main
: contact
? contact.core.consolidatedPresence === LinphoneEnums.ConsolidatedPresence.Online
? DefaultStyle.success_500main
: contact.core.consolidatedPresence === LinphoneEnums.ConsolidatedPresence.Busy
? DefaultStyle.warning_600
: contact.core.consolidatedPresence === LinphoneEnums.ConsolidatedPresence.DoNotDisturb
? DefaultStyle.danger_500main
: DefaultStyle.main2_500main
: "transparent"
border {
width: Math.round(2 * DefaultStyle.dp)
color: DefaultStyle.grey_0
}
source: account ? (account.core?.registrationState != LinphoneEnums.RegistrationState.Ok ? account.core?.registrationStateIcon : account.core?.presenceIcon)
: (contact ? contact.core?.presenceIcon : "")
}
}
Component{

View file

@ -8,6 +8,8 @@ import QtQuick.Controls.Basic as Control
import Linphone
import UtilsCpp
import SettingsCpp
import 'qrc:/qt/qml/Linphone/view/Control/Tool/Helper/utils.js' as Utils
import 'qrc:/qt/qml/Linphone/view/Style/buttonStyle.js' as ButtonStyle
Control.Control{
id: mainItem
@ -55,66 +57,87 @@ Control.Control{
}
}
}
Control.Control {
id: registrationStatusItem
Layout.minimumWidth: Math.round(49 * DefaultStyle.dp)
Layout.maximumWidth: 150
Layout.preferredHeight: Math.round(24 * DefaultStyle.dp)
topPadding: Math.round(4 * DefaultStyle.dp)
bottomPadding: Math.round(4 * DefaultStyle.dp)
leftPadding: Math.round(8 * DefaultStyle.dp)
rightPadding: Math.round(8 * DefaultStyle.dp)
Layout.preferredWidth: text.implicitWidth + (2 * Math.round(8 * DefaultStyle.dp))
background: Rectangle{
id: registrationStatus
anchors.fill: parent
color: DefaultStyle.main2_200
radius: Math.round(90 * DefaultStyle.dp)
}
contentItem: Text {
id: text
anchors.fill: parent
anchors.leftMargin: registrationStatusItem.leftPadding
anchors.rightMargin: registrationStatusItem.rightPadding
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
visible: mainItem.account
property int mode : !mainItem.account || mainItem.account.core.registrationState == LinphoneEnums.RegistrationState.Ok
? 0
: mainItem.account.core.registrationState == LinphoneEnums.RegistrationState.Cleared || mainItem.account.core.registrationState == LinphoneEnums.RegistrationState.None
? 1
: mainItem.account.core.registrationState == LinphoneEnums.RegistrationState.Progress || mainItem.account.core.registrationState == LinphoneEnums.RegistrationState.Refreshing
? 2
: 3
// Test texts
// Timer{
// running: true
// interval: 1000
// repeat: true
// onTriggered: text.mode = (++text.mode) % 4
// }
font.weight: Math.round(300 * DefaultStyle.dp)
font.pixelSize: Math.round(12 * DefaultStyle.dp)
color: mode == 0
? DefaultStyle.success_500main
: mode == 1
? DefaultStyle.warning_600
: mode == 2
? DefaultStyle.main2_500main
: DefaultStyle.danger_500main
text: mode == 0
//: "Connecté"
? qsTr("drawer_menu_account_connection_status_connected")
: mode == 1
//: "Désactivé"
? qsTr("drawer_menu_account_connection_status_cleared")
: mode == 2
//: "Connexion"
? qsTr("drawer_menu_account_connection_status_refreshing")
//: "Erreur"
: qsTr("drawer_menu_account_connection_status_failed")
PopupButton {
id: presenceAndRegistrationItem
Layout.minimumWidth: Math.round(86 * DefaultStyle.dp)
Layout.maximumWidth: Math.round(150 * DefaultStyle.dp)
Layout.preferredHeight: Math.round(24 * DefaultStyle.dp)
Layout.preferredWidth: presenceOrRegistrationText.implicitWidth + Math.round(50 * DefaultStyle.dp)
contentItem:
Rectangle{
id: presenceBar
property bool isRegistered: mainItem.account?.core.registrationState == LinphoneEnums.RegistrationState.Ok
color: DefaultStyle.main2_200
radius: Math.round(15 * DefaultStyle.dp)
RowLayout {
anchors.fill: parent
Image {
sourceSize.width: 11 * DefaultStyle.dp
sourceSize.height: 11 * DefaultStyle.dp
smooth: false
Layout.preferredWidth: 11 * DefaultStyle.dp
Layout.preferredHeight: 11 * DefaultStyle.dp
source: presenceBar.isRegistered ? mainItem.account.core.presenceIcon : mainItem.account?.core.registrationStateIcon
Layout.leftMargin: 8 * DefaultStyle.dp
}
Text {
id: presenceOrRegistrationText
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
visible: mainItem.account
// Test texts
// Timer{
// running: true
// interval: 1000
// repeat: true
// onTriggered: text.mode = (++text.mode) % 4
// }
font.weight: Math.round(300 * DefaultStyle.dp)
font.pixelSize: Math.round(12 * DefaultStyle.dp)
color: presenceBar.isRegistered ? mainItem.account.core.presenceColor : mainItem.account?.core.registrationColor
text: presenceBar.isRegistered ? mainItem.account.core.presenceStatus : mainItem.account?.core.humaneReadableRegistrationState
}
EffectImage {
fillMode: Image.PreserveAspectFit
imageSource: AppIcons.downArrow
colorizationColor: DefaultStyle.main2_600
Layout.preferredHeight: Math.round(14 * DefaultStyle.dp)
Layout.preferredWidth: Math.round(14 * DefaultStyle.dp)
Layout.rightMargin: 8 * DefaultStyle.dp
}
}
}
popup.contentItem: Rectangle {
implicitWidth: 280 * DefaultStyle.dp
implicitHeight: 20 * DefaultStyle.dp + (setCustomStatus.visible ? 240 * DefaultStyle.dp : setPresence.implicitHeight)
Presence {
id: setPresence
anchors.fill: parent
anchors.margins: 20 * DefaultStyle.dp
accountCore: mainItem.account.core
onSetCustomStatusClicked: {
setPresence.visible = false
setCustomStatus.visible = true
}
onIsSet: presenceAndRegistrationItem.popup.close()
}
PresenceSetCustomStatus {
id: setCustomStatus
visible: false
anchors.fill: parent
anchors.margins: 20 * DefaultStyle.dp
accountCore: mainItem.account.core
onVisibleChanged: {
if (!visible) {
setPresence.visible = true
setCustomStatus.visible = false
}
}
onIsSet: presenceAndRegistrationItem.popup.close()
}
}
}
Item{
Layout.preferredWidth: Math.round(26 * DefaultStyle.dp)
Layout.preferredHeight: Math.round(26 * DefaultStyle.dp)

View file

@ -0,0 +1,114 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import Linphone
import 'qrc:/qt/qml/Linphone/view/Style/buttonStyle.js' as ButtonStyle
ColumnLayout {
id: mainItem
property var accountCore
signal setCustomStatusClicked
signal isSet
spacing: 8 * DefaultStyle.dp
PresenceStatusItem { presence: LinphoneEnums.Presence.Online; accountCore: mainItem.accountCore; onClick: mainItem.isSet()}
PresenceStatusItem { presence: LinphoneEnums.Presence.Away; accountCore: mainItem.accountCore; onClick: mainItem.isSet()}
PresenceStatusItem { presence: LinphoneEnums.Presence.Busy; accountCore: mainItem.accountCore; onClick: mainItem.isSet()}
PresenceStatusItem { presence: LinphoneEnums.Presence.DoNotDisturb; accountCore: mainItem.accountCore; onClick: mainItem.isSet()}
PresenceStatusItem { presence: LinphoneEnums.Presence.Offline; accountCore: mainItem.accountCore; onClick: mainItem.isSet()}
RowLayout {
spacing: 0
visible: accountCore.explicitPresence != LinphoneEnums.Presence.Undefined
Layout.alignment: Qt.AlignLeft
Layout.topMargin: Math.round(3 * DefaultStyle.dp)
Layout.bottomMargin: Math.round(3 * DefaultStyle.dp)
Label {
font: Typography.p1
text: qsTr("contact_presence_reset_status")
color: DefaultStyle.main2_600
}
Item {
Layout.fillWidth: true
}
Item {
width: Math.round(17 * DefaultStyle.dp)
height: Math.round(17 * DefaultStyle.dp)
MouseArea {
id: hoverArea
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: {
accountCore.resetToAutomaticPresence()
}
}
EffectImage {
fillMode: Image.PreserveAspectFit
imageSource: AppIcons.reloadArrow
colorizationColor: hoverArea.containsMouse ? DefaultStyle.main2_800 : DefaultStyle.main2_600
anchors.fill: parent
}
}
}
Rectangle {
height: 1
width: parent.width
color: DefaultStyle.main2_500main
Layout.topMargin: 8 * DefaultStyle.dp
}
ColumnLayout {
spacing: 19 * DefaultStyle.dp
RowLayout {
spacing: 10 * DefaultStyle.dp
Layout.topMargin: Math.round(3 * DefaultStyle.dp)
Layout.alignment: Qt.AlignLeft
Text {
font: Typography.p1
text: accountCore.presenceNote.length > 0 ? accountCore.presenceNote : qsTr("contact_presence_custom_status")
color: DefaultStyle.main2_600
wrapMode: Text.WordWrap
Layout.preferredWidth: (accountCore.presenceNote.length == 0 ? 175 : 230) * DefaultStyle.dp
}
Item {
Layout.fillWidth: true
}
SmallButton {
visible: accountCore.presenceNote.length == 0
style: ButtonStyle.secondary
text: qsTr("contact_presence_button_set_custom_status")
onClicked: {
mainItem.setCustomStatusClicked()
}
}
}
RowLayout {
visible: accountCore.presenceNote.length > 0
spacing: 10 * DefaultStyle.dp
Item {
Layout.fillWidth: true
}
SmallButton {
style: ButtonStyle.secondary
text: qsTr("contact_presence_button_edit_custom_status")
onClicked: {
mainItem.setCustomStatusClicked()
}
}
SmallButton {
style: ButtonStyle.secondary
visible: accountCore.presenceNote.length > 0
text: qsTr("contact_presence_button_delete_custom_status")
onClicked: {
mainItem.accountCore.presenceNote = ""
}
}
}
}
Item {
Layout.fillHeight: true
}
}

View file

@ -0,0 +1,78 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import Linphone
import SettingsCpp
import 'qrc:/qt/qml/Linphone/view/Style/buttonStyle.js' as ButtonStyle
Column {
id: mainItem
spacing: 20 * DefaultStyle.dp
anchors.centerIn: parent
property var accountCore
signal isSet
Text {
text: qsTr("contact_presence_button_set_custom_status_title")
horizontalAlignment: Text.AlignHCenter
color: DefaultStyle.main2_600
font: Typography.p2
}
Rectangle {
width: parent.width
height: 150 * DefaultStyle.dp
color: "transparent"
border.color: DefaultStyle.main1_500_main
border.width: 1 * DefaultStyle.dp
radius: 8 * DefaultStyle.dp
ColumnLayout {
anchors.fill: parent
anchors.margins: 10 * DefaultStyle.dp
TextEdit {
id: statusMessage
wrapMode: TextEdit.Wrap
font: Typography.p1
color: DefaultStyle.main2_500main
Layout.fillHeight: true
Layout.fillWidth: true
property string previoustext: ""
text: mainItem.accountCore.presenceNote
onTextChanged: {
if (statusMessage.text.length > accountCore.maxPresenceNoteSize) {
statusMessage.text = previoustext
statusMessage.cursorPosition = statusMessage.text.length
} else {
previoustext = statusMessage.text
}
}
}
Item {
Layout.fillHeight: true
}
Text {
Layout.fillWidth: true
text: statusMessage.text.length + " / " + accountCore.maxPresenceNoteSize
font: Typography.p1
color: DefaultStyle.main2_400
horizontalAlignment: Text.AlignRight
}
}
}
Row {
spacing: 10 * DefaultStyle.dp
anchors.right: parent.right
SmallButton {
style: ButtonStyle.secondary
text: qsTr("contact_presence_button_save_custom_status")
enabled: statusMessage.text.length > 0
onClicked: {
mainItem.accountCore.presenceNote = statusMessage.text
mainItem.isSet()
}
}
}
}

View file

@ -0,0 +1,53 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import Linphone
import UtilsCpp
Rectangle {
id: mainItem
property var accountCore
property var presence
signal click()
color: mouseArea.containsMouse ? DefaultStyle.main2_100 : "transparent"
width: 236 * DefaultStyle.dp
height: 22 * DefaultStyle.dp
radius: 5 * DefaultStyle.dp
RowLayout {
anchors.fill: parent
spacing: 10 * DefaultStyle.dp
Layout.alignment: Qt.AlignLeft
Image {
sourceSize.width: 11 * DefaultStyle.dp
sourceSize.height: 11 * DefaultStyle.dp
smooth: false
Layout.preferredWidth: 11 * DefaultStyle.dp
Layout.preferredHeight: 11 * DefaultStyle.dp
source: UtilsCpp.getPresenceIcon(mainItem.presence)
}
Text {
text: UtilsCpp.getPresenceStatus(mainItem.presence)
font: Typography.p1
horizontalAlignment: Text.AlignLeft
Layout.alignment: Qt.AlignLeft
Layout.fillWidth: true
color: UtilsCpp.getPresenceColor(mainItem.presence)
}
}
MouseArea {
id: mouseArea
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: {
mainItem.accountCore.presence = mainItem.presence
mainItem.click()
}
}
}

View file

@ -53,6 +53,9 @@ Item {
: call.core.remoteName
: ""
property var contactObj: UtilsCpp.findFriendByAddress(call.core.remoteAddress)
property var contact: contactObj && contactObj.value || null
property var identityAddress: account ? UtilsCpp.getDisplayName(account.core.identityAddress) : null
property bool videoEnabled: (previewEnabled && call && call.core.localVideoEnabled)
|| (!previewEnabled && call && call.core.remoteVideoEnabled)
@ -168,6 +171,7 @@ Item {
anchors.topMargin: Math.round(21 * DefaultStyle.dp)
anchors.left: parent.left
anchors.right: parent.right
Text {
Layout.fillWidth: true
horizontalAlignment: Text.AlignHCenter

View file

@ -52,7 +52,8 @@ Window{
RowLayout{
anchors.fill: parent
Text{
text: modelData.core.presenceTimestamp + " == " +modelData.core.consolidatedPresence + " / "
text: modelData.core.presenceStatus
color: modelData.core.presenceColor
}
Button {
text: 'X'

View file

@ -454,27 +454,10 @@ FriendGui{
}
Text {
visible: contactDetail.contact
property var mode: contactDetail.contact ? contactDetail.contact.core.consolidatedPresence : -1
horizontalAlignment: Text.AlignLeft
Layout.fillWidth: true
text: mode === LinphoneEnums.ConsolidatedPresence.Online
//: "En ligne"
? qsTr("contact_presence_status_online")
: mode === LinphoneEnums.ConsolidatedPresence.Busy
//: "Occupé"
? qsTr("contact_presence_status_busy")
: mode === LinphoneEnums.ConsolidatedPresence.DoNotDisturb
//: "Ne pas déranger"
? qsTr("contact_presence_status_do_not_disturb")
//: "Hors ligne"
: qsTr("contact_presence_status_offline")
color: mode === LinphoneEnums.ConsolidatedPresence.Online
? DefaultStyle.success_500main
: mode === LinphoneEnums.ConsolidatedPresence.Busy
? DefaultStyle.warning_600
: mode === LinphoneEnums.ConsolidatedPresence.DoNotDisturb
? DefaultStyle.danger_500main
: DefaultStyle.main2_500main
text: contactDetail.contact ? contactDetail.contact.core.presenceStatus : ""
color: contactDetail.contact ? contactDetail.contact.core.presenceColor : 'transparent'
font.pixelSize: Math.round(14 * DefaultStyle.dp)
}
},

View file

@ -128,4 +128,13 @@ QtObject {
property string appWindow: "image://internal/app-window.svg"
property string bellMwi: "image://internal/bell-simple.svg"
property string callForward: "image://internal/call-forward.svg"
property string regitrationDeactivated: "image://internal/regitration_deactivated.svg"
property string regitrationProgress: "image://internal/regitration_progress.svg"
property string regitrationError: "image://internal/regitration_error.svg"
property string presenceOnline: "image://internal/presence_online.svg"
property string presenceAway: "image://internal/presence_away.svg"
property string presenceBusy: "image://internal/presence_busy.svg"
property string presenceDoNotDisturb: "image://internal/presence_do_not_disturb.svg"
property string presenceOffline: "image://internal/presence_offline.svg"
property string presenceNote: "image://internal/presence-note.svg"
}

View file

@ -33,6 +33,7 @@ QtObject {
property color warning_600: "#DBB820"
property color danger_500main: "#DD5F5F"
property color warning_500_main: "#FFDC2E"
property color danger_700: "#9E3548"
property color danger_900: "#723333"
property color success_500main: "#4FAE80"
@ -62,5 +63,4 @@ QtObject {
property color placeholders: '#CACACA' // No name in design
property color warning_500_main: "#FFDC2E"
}

View file

@ -51,7 +51,7 @@ QtObject {
pixelSize: Math.round(13 * DefaultStyle.dp),
weight: Math.min(Math.round(700 * DefaultStyle.dp), 1000)
})
// Text/P2 - Large Bold, reduced paragraph text
property font p2l: Qt.font( {
family: DefaultStyle.defaultFont,