Automatic LDAP Friends lookup for calls

This commit is contained in:
Christophe Deschamps 2024-10-25 08:09:56 +02:00
parent b6b16650bf
commit a91b29cb17
20 changed files with 171 additions and 29 deletions

View file

@ -79,6 +79,28 @@ CallHistoryCore::~CallHistoryCore() {
void CallHistoryCore::setSelf(QSharedPointer<CallHistoryCore> me) {
mHistoryModelConnection = QSharedPointer<SafeConnection<CallHistoryCore, CallHistoryModel>>(
new SafeConnection<CallHistoryCore, CallHistoryModel>(me, mCallHistoryModel), &QObject::deleteLater);
mCoreModelConnection = QSharedPointer<SafeConnection<CallHistoryCore, CoreModel>>(
new SafeConnection<CallHistoryCore, CoreModel>(me, CoreModel::getInstance()), &QObject::deleteLater);
if (!ToolModel::findFriendByAddress(mRemoteAddress)) {
mCoreModelConnection->makeConnectToModel(
&CoreModel::friendCreated,
[this, remoteAddress = mRemoteAddress](const std::shared_ptr<linphone::Friend> &f) {
auto inFriend = Utils::findFriendByAddress(remoteAddress);
QString displayName;
if (inFriend) {
auto friendGui = inFriend->getValue().value<FriendGui *>();
if (friendGui) displayName = friendGui->getCore()->getDisplayName();
}
if (!displayName.isEmpty()) {
mCoreModelConnection->invokeToCore([this, displayName]() {
if (displayName != mDisplayName) {
mDisplayName = displayName;
emit displayNameChanged();
}
});
}
});
}
}
ConferenceInfoGui *CallHistoryCore::getConferenceInfoGui() const {

View file

@ -34,7 +34,7 @@ class CallHistoryModel;
class CallHistoryCore : public QObject, public AbstractObject {
Q_OBJECT
Q_PROPERTY(QString displayName MEMBER mDisplayName CONSTANT)
Q_PROPERTY(QString displayName MEMBER mDisplayName NOTIFY displayNameChanged)
Q_PROPERTY(QString remoteAddress MEMBER mRemoteAddress CONSTANT)
Q_PROPERTY(bool isOutgoing MEMBER mIsOutgoing CONSTANT)
Q_PROPERTY(bool isConference MEMBER mIsConference CONSTANT)
@ -66,12 +66,14 @@ public:
signals:
void durationChanged(QString duration);
void displayNameChanged();
private:
QString mDuration;
QSharedPointer<ConferenceInfoCore> mConferenceInfo = nullptr;
std::shared_ptr<CallHistoryModel> mCallHistoryModel;
QSharedPointer<SafeConnection<CallHistoryCore, CallHistoryModel>> mHistoryModelConnection;
QSharedPointer<SafeConnection<CallHistoryCore, CoreModel>> mCoreModelConnection;
DECLARE_ABSTRACT_OBJECT
};

View file

@ -22,6 +22,7 @@
#include "core/App.hpp"
#include "core/conference/ConferenceCore.hpp"
#include "core/conference/ConferenceGui.hpp"
#include "core/friend/FriendCore.hpp"
#include "core/setting/SettingsCore.hpp"
#include "model/tool/ToolModel.hpp"
#include "tool/Utils.hpp"
@ -129,6 +130,7 @@ CallCore::CallCore(const std::shared_ptr<linphone::Call> &call) : QObject(nullpt
mRemoteName = Utils::coreStringToAppString(
linphoneFriend->getVcard() ? linphoneFriend->getVcard()->getFullName() : linphoneFriend->getName());
if (mRemoteName.isEmpty()) mRemoteName = ToolModel::getDisplayName(mRemoteAddress);
mShouldFindRemoteLdapFriend = !linphoneFriend && !CoreModel::getInstance()->getCore()->getLdapList().empty();
mLocalAddress = Utils::coreStringToAppString(call->getCallLog()->getLocalAddress()->asStringUriOnly());
mStatus = LinphoneEnums::fromLinphone(call->getCallLog()->getStatus());
@ -466,14 +468,11 @@ void CallCore::setSelf(QSharedPointer<CallCore> me) {
setVideoStats(videoStats);
}
});
if (mShouldFindRemoteLdapFriend) findRemoteLdapFriend(me);
}
DEFINE_GET_SET_API(CallCore, bool, isStarted, IsStarted)
QString CallCore::getRemoteName() const {
return mRemoteName;
}
QString CallCore::getRemoteAddress() const {
return mRemoteAddress;
}
@ -818,3 +817,32 @@ void CallCore::setVideoStats(VideoStats stats) {
emit videoStatsChanged();
}
}
void CallCore::findRemoteLdapFriend(QSharedPointer<CallCore> me) {
mustBeInLinphoneThread(log().arg(Q_FUNC_INFO));
auto linphoneSearch = CoreModel::getInstance()->getCore()->createMagicSearch();
linphoneSearch->setLimitedSearch(true);
mLdapMagicSearchModel = Utils::makeQObject_ptr<MagicSearchModel>(linphoneSearch);
mLdapMagicSearchModel->setSourceFlags((int)LinphoneEnums::MagicSearchSource::LdapServers);
mLdapMagicSearchModel->setAggregationFlag(LinphoneEnums::MagicSearchAggregation::Friend);
mLdapMagicSearchModel->setSelf(mLdapMagicSearchModel);
mLdapMagicSearchModelConnection = QSharedPointer<SafeConnection<CallCore, MagicSearchModel>>(
new SafeConnection<CallCore, MagicSearchModel>(me, mLdapMagicSearchModel), &QObject::deleteLater);
mLdapMagicSearchModelConnection->makeConnectToModel(
&MagicSearchModel::searchResultsReceived,
[this, remoteAdress = mRemoteAddress](const std::list<std::shared_ptr<linphone::SearchResult>> &results) {
mustBeInLinphoneThread(log().arg(Q_FUNC_INFO));
auto ldapFriend = ToolModel::findFriendByAddress(remoteAdress);
if (ldapFriend) {
auto ldapName = Utils::coreStringToAppString(ldapFriend->getName());
mLdapMagicSearchModelConnection->invokeToCore([this, ldapName]() {
mustBeInMainThread(log().arg(Q_FUNC_INFO));
if (ldapName != mRemoteName) {
mRemoteName = ldapName;
emit remoteNameChanged();
}
});
}
});
mLdapMagicSearchModel->search(mRemoteAddress);
}

View file

@ -25,6 +25,7 @@
#include "core/conference/ConferenceGui.hpp"
#include "core/videoSource/VideoSourceDescriptorGui.hpp"
#include "model/call/CallModel.hpp"
#include "model/search/MagicSearchModel.hpp"
#include "tool/LinphoneEnums.hpp"
#include "tool/thread/SafeConnection.hpp"
#include <QObject>
@ -104,7 +105,7 @@ class CallCore : public QObject, public AbstractObject {
Q_PROPERTY(bool speakerMuted READ getSpeakerMuted WRITE lSetSpeakerMuted NOTIFY speakerMutedChanged)
Q_PROPERTY(bool microphoneMuted READ getMicrophoneMuted WRITE lSetMicrophoneMuted NOTIFY microphoneMutedChanged)
Q_PROPERTY(bool paused READ getPaused WRITE lSetPaused NOTIFY pausedChanged)
Q_PROPERTY(QString remoteName READ getRemoteName CONSTANT)
Q_PROPERTY(QString remoteName MEMBER mRemoteName NOTIFY remoteNameChanged)
Q_PROPERTY(QString remoteAddress READ getRemoteAddress CONSTANT)
Q_PROPERTY(QString localAddress READ getLocalAddress CONSTANT)
Q_PROPERTY(bool tokenVerified READ getTokenVerified WRITE setTokenVerified NOTIFY securityUpdated)
@ -146,7 +147,6 @@ public:
~CallCore();
void setSelf(QSharedPointer<CallCore> me);
QString getRemoteName() const;
QString getRemoteAddress() const;
QString getLocalAddress() const;
@ -245,6 +245,8 @@ public:
VideoStats getVideoStats() const;
void setVideoStats(VideoStats stats);
void findRemoteLdapFriend(QSharedPointer<CallCore> me);
signals:
void statusChanged(LinphoneEnums::CallStatus status);
void stateChanged(LinphoneEnums::CallState state);
@ -274,6 +276,7 @@ signals:
void zrtpStatsChanged();
void audioStatsChanged();
void videoStatsChanged();
void remoteNameChanged();
// Linphone commands
void lAccept(bool withVideo); // Accept an incoming call
@ -355,6 +358,9 @@ private:
ZrtpStats mZrtpStats;
AudioStats mAudioStats;
VideoStats mVideoStats;
std::shared_ptr<MagicSearchModel> mLdapMagicSearchModel;
bool mShouldFindRemoteLdapFriend;
QSharedPointer<SafeConnection<CallCore, MagicSearchModel>> mLdapMagicSearchModelConnection;
DECLARE_ABSTRACT_OBJECT
};

View file

@ -99,7 +99,7 @@ FriendCore::FriendCore(const std::shared_ptr<linphone::Friend> &contact) : QObje
mStarred = false;
}
mIsLdap = false;
mIsLdap = ToolModel::friendIsInLdapFriendList(contact);
connect(this, &FriendCore::addressChanged, &FriendCore::allAddressesChanged);
connect(this, &FriendCore::phoneNumberChanged, &FriendCore::allAddressesChanged);
}
@ -670,17 +670,11 @@ void FriendCore::undo() { // Retrieve values from model
}
}
bool FriendCore::getIsLdap() const {
bool FriendCore::isLdap() const {
return mIsLdap;
}
void FriendCore::setIsLdap(bool data) {
if (mIsLdap != data) {
mIsLdap = data;
emit readOnlyChanged();
}
}
bool FriendCore::getReadOnly() const {
return getIsLdap(); // TODO add conditions for friends retrieved via HTTP [misc]vcards-contacts-list=<URL> &
// CardDAV
return isLdap(); // TODO add conditions for friends retrieved via HTTP [misc]vcards-contacts-list=<URL> &
// CardDAV
}

View file

@ -67,7 +67,8 @@ class FriendCore : public QObject, public AbstractObject {
Q_PROPERTY(bool isSaved READ getIsSaved NOTIFY isSavedChanged)
Q_PROPERTY(QString pictureUri READ getPictureUri WRITE setPictureUri NOTIFY pictureUriChanged)
Q_PROPERTY(bool starred READ getStarred WRITE lSetStarred NOTIFY starredChanged)
Q_PROPERTY(bool readOnly READ getReadOnly NOTIFY readOnlyChanged)
Q_PROPERTY(bool readOnly READ getReadOnly CONSTANT)
Q_PROPERTY(bool isLdap READ isLdap CONSTANT)
public:
// Should be call from model Thread. Will be automatically in App thread after initialization
@ -135,8 +136,7 @@ public:
void onPresenceReceived(LinphoneEnums::ConsolidatedPresence consolidatedPresence, QDateTime presenceTimestamp);
bool getIsLdap() const;
void setIsLdap(bool isLdap);
bool isLdap() const;
bool getReadOnly() const;
Q_INVOKABLE void remove();
@ -168,7 +168,6 @@ signals:
void devicesChanged();
void verifiedDevicesChanged();
void lSetStarred(bool starred);
void readOnlyChanged();
protected:
void writeIntoModel(std::shared_ptr<FriendModel> model) const;

View file

@ -122,8 +122,6 @@ void MagicSearchList::setSelf(QSharedPointer<MagicSearchList> me) {
contact->appendPhoneNumber(tr("Phone"), Utils::coreStringToAppString(it->getPhoneNumber()));
contacts->append(contact);
}
bool isLdap = (it->getSourceFlags() & (int)LinphoneEnums::MagicSearchSource::LdapServers) != 0;
if (contact) contact->setIsLdap(isLdap);
}
mModelConnection->invokeToCore([this, contacts]() {
setResults(*contacts);

View file

@ -74,6 +74,7 @@ private:
std::shared_ptr<MagicSearchModel> mMagicSearch;
QSharedPointer<SafeConnection<MagicSearchList, MagicSearchModel>> mModelConnection;
QSharedPointer<SafeConnection<MagicSearchList, CoreModel>> mCoreModelConnection;
DECLARE_ABSTRACT_OBJECT
};

View file

@ -68,7 +68,10 @@ void MagicSearchProxy::setList(QSharedPointer<MagicSearchList> newList) {
Qt::QueuedConnection);
}
auto sortFilterList = new SortFilterList(mList.get(), Qt::AscendingOrder);
if (oldModel) sortFilterList->mShowFavoritesOnly = oldModel->mShowFavoritesOnly;
if (oldModel) {
sortFilterList->mShowFavoritesOnly = oldModel->mShowFavoritesOnly;
sortFilterList->mShowLdapContacts = oldModel->mShowLdapContacts;
}
setSourceModels(sortFilterList);
}
@ -133,7 +136,7 @@ void MagicSearchProxy::setAggregationFlag(LinphoneEnums::MagicSearchAggregation
bool MagicSearchProxy::SortFilterList::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const {
auto friendCore = getItemAtSource<MagicSearchList, FriendCore>(sourceRow);
if (friendCore) {
return !mShowFavoritesOnly || friendCore->getStarred();
return (!mShowFavoritesOnly || friendCore->getStarred()) && (mShowLdapContacts || !friendCore->isLdap());
}
return false;
}
@ -149,3 +152,15 @@ bool MagicSearchProxy::SortFilterList::lessThan(const QModelIndex &sourceLeft, c
}
return true;
}
bool MagicSearchProxy::showLdapContacts() const {
return dynamic_cast<SortFilterList *>(sourceModel())->mShowLdapContacts;
}
void MagicSearchProxy::setShowLdapContacts(bool show) {
auto list = dynamic_cast<SortFilterList *>(sourceModel());
if (list->mShowLdapContacts != show) {
list->mShowLdapContacts = show;
list->invalidate();
}
}

View file

@ -36,9 +36,11 @@ class MagicSearchProxy : public LimitProxy {
NOTIFY aggregationFlagChanged)
Q_PROPERTY(bool showFavoritesOnly READ showFavoritesOnly WRITE setShowFavoritesOnly NOTIFY showFavoriteOnlyChanged)
Q_PROPERTY(MagicSearchProxy *parentProxy WRITE setParentProxy NOTIFY parentProxyChanged)
Q_PROPERTY(bool showLdapContacts READ showLdapContacts WRITE setShowLdapContacts CONSTANT)
public:
DECLARE_SORTFILTER_CLASS(bool mShowFavoritesOnly = false;)
DECLARE_SORTFILTER_CLASS(bool mShowFavoritesOnly = false; bool mShowLdapContacts = false;)
MagicSearchProxy(QObject *parent = Q_NULLPTR);
~MagicSearchProxy();
@ -54,6 +56,9 @@ public:
bool showFavoritesOnly() const;
void setShowFavoritesOnly(bool show);
bool showLdapContacts() const;
void setShowLdapContacts(bool show);
void setList(QSharedPointer<MagicSearchList> list);
Q_INVOKABLE void setParentProxy(MagicSearchProxy *proxy);

View file

@ -23,6 +23,7 @@
#include <QDebug>
#include "model/core/CoreModel.hpp"
#include "model/tool/ToolModel.hpp"
#include "tool/Utils.hpp"
DEFINE_ABSTRACT_OBJECT(MagicSearchModel)
@ -60,6 +61,10 @@ void MagicSearchModel::setAggregationFlag(LinphoneEnums::MagicSearchAggregation
}
void MagicSearchModel::onSearchResultsReceived(const std::shared_ptr<linphone::MagicSearch> &magicSearch) {
for (auto it : magicSearch->getLastSearch()) {
bool isLdap = (it->getSourceFlags() & (int)LinphoneEnums::MagicSearchSource::LdapServers) != 0;
if (isLdap && it->getFriend()) updateLdapFriendListWithFriend(it->getFriend());
}
emit searchResultsReceived(magicSearch->getLastSearch());
}
@ -67,3 +72,32 @@ void MagicSearchModel::onLdapHaveMoreResults(const std::shared_ptr<linphone::Mag
const std::shared_ptr<linphone::Ldap> &ldap) {
// emit ldapHaveMoreResults(ldap);
}
// Store LDAP friends in separate list so they can still be retrieved by core->findFriend() for display names,
// but can be amended only by LDAP received friends.
// Done in this place so the application can benefit from user initiated searches for faster ldap name retrieval
// upon incoming call / chat message.
void MagicSearchModel::updateLdapFriendListWithFriend(const std::shared_ptr<linphone::Friend> &linphoneFriend) {
mustBeInLinphoneThread(log().arg(Q_FUNC_INFO));
auto core = CoreModel::getInstance()->getCore();
auto ldapFriendList = ToolModel::getLdapFriendList();
for (auto address : linphoneFriend->getAddresses()) {
auto existingFriend = ldapFriendList->findFriendByAddress(address);
if (existingFriend) {
ldapFriendList->removeFriend(existingFriend);
ldapFriendList->addFriend(linphoneFriend);
return;
}
}
for (auto number : linphoneFriend->getPhoneNumbers()) {
auto existingFriend = ldapFriendList->findFriendByPhoneNumber(number);
if (existingFriend) {
ldapFriendList->removeFriend(existingFriend);
ldapFriendList->addFriend(linphoneFriend);
return;
}
}
ldapFriendList->addFriend(linphoneFriend);
emit CoreModel::getInstance()->friendCreated(linphoneFriend);
}

View file

@ -58,6 +58,8 @@ private:
virtual void onSearchResultsReceived(const std::shared_ptr<linphone::MagicSearch> &magicSearch) override;
virtual void onLdapHaveMoreResults(const std::shared_ptr<linphone::MagicSearch> &magicSearch,
const std::shared_ptr<linphone::Ldap> &ldap) override;
void updateLdapFriendListWithFriend(const std::shared_ptr<linphone::Friend> &linphoneFriend);
signals:
void searchResultsReceived(const std::list<std::shared_ptr<linphone::SearchResult>> &results);
};

View file

@ -275,3 +275,22 @@ bool ToolModel::isLocal(const std::shared_ptr<linphone::Conference> &conference,
auto gruuAddress = findAccount(callAddress)->getContactAddress();
return deviceAddress->equal(gruuAddress);
}
std::shared_ptr<linphone::FriendList> ToolModel::getLdapFriendList() {
auto core = CoreModel::getInstance()->getCore();
auto ldapFriendList = core->getFriendListByName("ldap_friends");
if (!ldapFriendList) {
ldapFriendList = core->createFriendList();
ldapFriendList->setDisplayName("ldap_friends");
core->addFriendList(ldapFriendList);
}
return ldapFriendList;
}
bool ToolModel::friendIsInLdapFriendList(const std::shared_ptr<linphone::Friend> &f) {
auto ldapFriendList = getLdapFriendList();
for (auto ldapFriend : ldapFriendList->getFriends()) {
if (f == ldapFriend) return true;
}
return false;
}

View file

@ -59,6 +59,9 @@ public:
linphone::MediaEncryption = linphone::MediaEncryption::None,
QString *errorMessage = nullptr);
static std::shared_ptr<linphone::FriendList> getLdapFriendList();
static bool friendIsInLdapFriendList(const std::shared_ptr<linphone::Friend> &f);
private:
DECLARE_ABSTRACT_OBJECT
};

View file

@ -175,6 +175,10 @@ Loader{
shadowColor: DefaultStyle.grey_1000
shadowOpacity: 0.1
}
Connections {
target: mainItem.call?.core ? mainItem.call.core : null
onRemoteNameChanged: initialItem.initials = UtilsCpp.getInitials(mainItem.call.core.remoteName)
}
}
}
Component{

View file

@ -26,6 +26,7 @@ ListView {
property bool displayNameCapitalization: true
property bool showFavoritesOnly: false
property bool showDefaultAddress: false
property bool showLdapContacts: false
property var listProxy: MagicSearchProxy{}
@ -112,6 +113,7 @@ ListView {
}
aggregationFlag: mainItem.aggregationFlag
parentProxy: mainItem.listProxy
showLdapContacts: mainItem.showLdapContacts
sourceFlags: mainItem.sourceFlags
onInitialized: {
magicSearchProxy.forceUpdate()

View file

@ -49,8 +49,7 @@ Notification {
}
ColumnLayout {
Text {
property var remoteAddress: UtilsCpp.getDisplayName(call.core.remoteAddress)
text: remoteAddress ? remoteAddress.value : ""
text: call.core.remoteName
color: DefaultStyle.grey_600
font {
pixelSize: 20 * DefaultStyle.dp

View file

@ -249,6 +249,7 @@ Item {
actionLayoutVisible: true
selectionEnabled: false
showDefaultAddress: true
showLdapContacts: true
Control.ScrollBar.vertical: scrollbar
searchText: magicSearchBar.text

View file

@ -655,7 +655,12 @@ AbstractMainPage {
contact: contactObj && contactObj.value || null
conferenceInfo: mainItem.selectedRowHistoryGui && mainItem.selectedRowHistoryGui.core.conferenceInfo || null
specificAddress: mainItem.selectedRowHistoryGui && mainItem.selectedRowHistoryGui.core.remoteAddress || ""
Connections {
target: mainItem.selectedRowHistoryGui?.core ? mainItem.selectedRowHistoryGui.core : null
onDisplayNameChanged: {
mainItem.onSelectedRowHistoryGuiChanged() // to cover displayName & Avatar.
}
}
buttonContent: PopupButton {
id: detailOptions
anchors.right: parent.right
@ -687,6 +692,7 @@ AbstractMainPage {
textColor: DefaultStyle.main2_500main
contentImageColor: DefaultStyle.main2_600
background: Item {}
visible: SettingsCpp.syncLdapContacts || !detailOptions.friendGui?.core?.isLdap
onClicked: {
detailOptions.close()
if (detailOptions.friendGui) mainWindow.displayContactPage(contactDetail.contactAddress)

View file

@ -56,6 +56,7 @@ AbstractMainPage {
MagicSearchProxy {
id: allFriends
showLdapContacts: SettingsCpp.syncLdapContacts
}
function deleteContact(contact) {
@ -446,6 +447,7 @@ AbstractMainPage {
button.topPadding: 10 * DefaultStyle.dp
button.bottomPadding: 10 * DefaultStyle.dp
button.onClicked: mainItem.editContact(mainItem.selectedContact)
button.visible: !mainItem.selectedContact?.core.readOnly
property string contactAddress: contact ? contact.core.defaultAddress : ""
property var computedContactNameObj: UtilsCpp.getDisplayName(contactAddress)
property string computedContactName: computedContactNameObj ? computedContactNameObj.value : ""