Fix crashes on SafeConnection.

Display missed notifications and fit text size from content.
Add account from profile menu and display Login Page with back button.
Default account selection and change avatar from accounts list.
Remove deprecated function on Friends DB.
Return username by default on display name computation.
Update SDK.
This commit is contained in:
Julien Wadel 2023-12-06 10:07:08 +01:00
parent 4ea1b96246
commit a43430fa34
35 changed files with 313 additions and 234 deletions

View file

@ -46,6 +46,8 @@ AccountCore::AccountCore(const std::shared_ptr<linphone::Account> &account) : QO
mPictureUri = Utils::coreStringToAppString(params->getPictureUri());
mRegistrationState = LinphoneEnums::fromLinphone(account->getState());
mIsDefaultAccount = CoreModel::getInstance()->getCore()->getDefaultAccount() == account;
// mUnreadNotifications = account->getUnreadChatMessageCount() + account->getMissedCallsCount(); // TODO
mUnreadNotifications = account->getMissedCallsCount();
// Add listener
mAccountModel = Utils::makeQObject_ptr<AccountModel>(account); // OK
@ -58,30 +60,34 @@ AccountCore::~AccountCore() {
}
void AccountCore::setSelf(QSharedPointer<AccountCore> me) {
mAccountModelConnection = QSharedPointer<SafeConnection>(
new SafeConnection(me.objectCast<QObject>(), std::dynamic_pointer_cast<QObject>(mAccountModel)));
mAccountModelConnection->makeConnect(mAccountModel.get(), &AccountModel::registrationStateChanged,
[this](const std::shared_ptr<linphone::Account> &account,
linphone::RegistrationState state, const std::string &message) {
mAccountModelConnection->invokeToCore([this, account, state, message]() {
this->onRegistrationStateChanged(account, state, message);
});
});
// From Model
mAccountModelConnection->makeConnect(
mAccountModel.get(), &AccountModel::defaultAccountChanged, [this](bool isDefault) {
mAccountModelConnection->invokeToCore([this, isDefault]() { this->onDefaultAccountChanged(isDefault); });
mAccountModelConnection = QSharedPointer<SafeConnection<AccountCore, AccountModel>>(
new SafeConnection<AccountCore, AccountModel>(me, mAccountModel));
mAccountModelConnection->makeConnectToModel(
&AccountModel::registrationStateChanged, [this](const std::shared_ptr<linphone::Account> &account,
linphone::RegistrationState state, const std::string &message) {
mAccountModelConnection->invokeToCore(
[this, account, state, message]() { this->onRegistrationStateChanged(account, state, message); });
});
// From Model
mAccountModelConnection->makeConnectToModel(&AccountModel::defaultAccountChanged, [this](bool isDefault) {
mAccountModelConnection->invokeToCore([this, isDefault]() { this->onDefaultAccountChanged(isDefault); });
});
mAccountModelConnection->makeConnect(mAccountModel.get(), &AccountModel::pictureUriChanged, [this](QString uri) {
mAccountModelConnection->makeConnectToModel(&AccountModel::pictureUriChanged, [this](QString uri) {
mAccountModelConnection->invokeToCore([this, uri]() { this->onPictureUriChanged(uri); });
});
mAccountModelConnection->makeConnectToModel(
&AccountModel::unreadNotificationsChanged, [this](int unreadMessagesCount, int unreadCallsCount) {
mAccountModelConnection->invokeToCore([this, unreadMessagesCount, unreadCallsCount]() {
this->setUnreadNotifications(unreadMessagesCount + unreadCallsCount);
});
});
// From GUI
mAccountModelConnection->makeConnect(this, &AccountCore::lSetPictureUri, [this](QString uri) {
mAccountModelConnection->makeConnectToCore(&AccountCore::lSetPictureUri, [this](QString uri) {
mAccountModelConnection->invokeToModel([this, uri]() { mAccountModel->setPictureUri(uri); });
});
mAccountModelConnection->makeConnect(this, &AccountCore::lSetDefaultAccount, [this]() {
mAccountModelConnection->makeConnectToCore(&AccountCore::lSetDefaultAccount, [this]() {
mAccountModelConnection->invokeToModel([this]() { mAccountModel->setDefault(); });
});
}
@ -106,6 +112,16 @@ bool AccountCore::getIsDefaultAccount() const {
return mIsDefaultAccount;
}
int AccountCore::getUnreadNotifications() const {
return mUnreadNotifications;
}
void AccountCore::setUnreadNotifications(int unread) {
if (mUnreadNotifications != unread) {
mUnreadNotifications = unread;
emit unreadNotificationsChanged(unread);
}
}
void AccountCore::onRegistrationStateChanged(const std::shared_ptr<linphone::Account> &account,
linphone::RegistrationState state,
const std::string &message) {
@ -124,3 +140,7 @@ void AccountCore::onPictureUriChanged(QString uri) {
mPictureUri = uri;
emit pictureUriChanged();
}
void AccountCore::removeAccount() {
mAccountModelConnection->invokeToModel([this]() { mAccountModel->removeAccount(); });
}

View file

@ -37,6 +37,7 @@ class AccountCore : public QObject, public AbstractObject {
Q_PROPERTY(
LinphoneEnums::RegistrationState registrationState READ getRegistrationState NOTIFY registrationStateChanged)
Q_PROPERTY(bool isDefaultAccount READ getIsDefaultAccount NOTIFY defaultAccountChanged)
Q_PROPERTY(int unreadNotifications READ getUnreadNotifications NOTIFY unreadNotificationsChanged)
public:
static QSharedPointer<AccountCore> create(const std::shared_ptr<linphone::Account> &account);
@ -50,6 +51,8 @@ public:
QString getPictureUri() const;
LinphoneEnums::RegistrationState getRegistrationState() const;
bool getIsDefaultAccount() const;
int getUnreadNotifications() const;
void setUnreadNotifications(int unread);
void onPictureUriChanged(QString uri);
void onRegistrationStateChanged(const std::shared_ptr<linphone::Account> &account,
@ -57,11 +60,13 @@ public:
const std::string &message);
void onDefaultAccountChanged(bool isDefault);
Q_INVOKABLE void removeAccount();
signals:
void pictureUriChanged();
void registrationStateChanged(const QString &message);
void defaultAccountChanged(bool isDefault);
void unreadNotificationsChanged(int unreadNotifications);
// Account requests
void lSetPictureUri(QString pictureUri);
@ -73,8 +78,9 @@ private:
QString mPictureUri;
bool mIsDefaultAccount = false;
LinphoneEnums::RegistrationState mRegistrationState;
int mUnreadNotifications = 0;
std::shared_ptr<AccountModel> mAccountModel;
QSharedPointer<SafeConnection> mAccountModelConnection;
QSharedPointer<SafeConnection<AccountCore, AccountModel>> mAccountModelConnection;
DECLARE_ABSTRACT_OBJECT
};

View file

@ -24,7 +24,6 @@
DEFINE_ABSTRACT_OBJECT(AccountGui)
AccountGui::AccountGui(QSharedPointer<AccountCore> core) {
qDebug() << "[AccountGui] new" << this;
App::getInstance()->mEngine->setObjectOwnership(this, QQmlEngine::JavaScriptOwnership);
mCore = core;
if (isInLinphoneThread()) moveToThread(App::getInstance()->thread());
@ -32,7 +31,6 @@ AccountGui::AccountGui(QSharedPointer<AccountCore> core) {
AccountGui::~AccountGui() {
mustBeInMainThread("~" + getClassName());
qDebug() << "[AccountGui] delete" << this;
}
AccountCore *AccountGui::getCore() const {

View file

@ -37,51 +37,54 @@ QSharedPointer<AccountList> AccountList::create() {
}
AccountList::AccountList(QObject *parent) : ListProxy(parent) {
qDebug() << "[AccountList] new" << this;
mustBeInMainThread(getClassName());
connect(CoreModel::getInstance().get(), &CoreModel::accountAdded, this, &AccountList::lUpdate);
connect(CoreModel::getInstance().get(), &CoreModel::accountRemoved, this, &AccountList::lUpdate);
}
AccountList::~AccountList() {
qDebug() << "[AccountList] delete" << this;
mustBeInMainThread("~" + getClassName());
mModelConnection = nullptr;
}
void AccountList::setSelf(QSharedPointer<AccountList> me) {
mModelConnection = QSharedPointer<SafeConnection>(
new SafeConnection(me.objectCast<QObject>(), std::dynamic_pointer_cast<QObject>(CoreModel::getInstance())),
&QObject::deleteLater);
mModelConnection->makeConnect(this, &AccountList::lUpdate, [this]() {
mModelConnection = QSharedPointer<SafeConnection<AccountList, CoreModel>>(
new SafeConnection<AccountList, CoreModel>(me, CoreModel::getInstance()), &QObject::deleteLater);
mModelConnection->makeConnectToCore(&AccountList::lUpdate, [this]() {
mModelConnection->invokeToModel([this]() {
// Avoid copy to lambdas
QList<QSharedPointer<AccountCore>> *accounts = new QList<QSharedPointer<AccountCore>>();
// Model thread.
mustBeInLinphoneThread(getClassName());
auto linphoneAccounts = CoreModel::getInstance()->getCore()->getAccountList();
auto defaultAccount = CoreModel::getInstance()->getCore()->getDefaultAccount();
QSharedPointer<AccountCore> defaultAccountCore;
for (auto it : linphoneAccounts) {
auto model = AccountCore::create(it);
if (it == defaultAccount) defaultAccountCore = AccountCore::create(defaultAccount);
accounts->push_back(model);
}
mModelConnection->invokeToCore([this, accounts]() {
mModelConnection->invokeToCore([this, accounts, defaultAccountCore]() {
mustBeInMainThread(getClassName());
resetData();
add(*accounts);
setHaveAccount(accounts->size() > 0);
setDefaultAccount(defaultAccountCore);
delete accounts;
});
});
});
mModelConnection->makeConnect(CoreModel::getInstance().get(), &CoreModel::defaultAccountChanged,
[this]() { mModelConnection->invokeToCore([this]() { defaultAccountChanged(); }); });
mModelConnection->makeConnectToModel(
&CoreModel::defaultAccountChanged,
[this](const std::shared_ptr<linphone::Core> &core, const std::shared_ptr<linphone::Account> &account) {
auto model = AccountCore::create(account);
mModelConnection->invokeToCore([this, model]() { setDefaultAccount(model); });
});
lUpdate();
}
QSharedPointer<AccountCore> AccountList::getDefaultAccountCore() const {
for (auto it : mList) {
auto account = it.objectCast<AccountCore>();
if (account->getIsDefaultAccount()) return account;
}
return nullptr;
return mDefaultAccount;
}
AccountGui *AccountList::getDefaultAccount() const {
@ -89,6 +92,13 @@ AccountGui *AccountList::getDefaultAccount() const {
if (account) return new AccountGui(account);
else return nullptr;
}
void AccountList::setDefaultAccount(QSharedPointer<AccountCore> account) {
if (mDefaultAccount != account) {
mDefaultAccount = account;
emit defaultAccountChanged();
}
}
bool AccountList::getHaveAccount() const {
return mHaveAccount;
}

View file

@ -28,6 +28,7 @@
class AccountGui;
class AccountCore;
class CoreModel;
// =============================================================================
class AccountList : public ListProxy, public AbstractObject {
@ -41,6 +42,7 @@ public:
AccountGui *getDefaultAccount() const;
QSharedPointer<AccountCore> getDefaultAccountCore() const;
void setDefaultAccount(QSharedPointer<AccountCore> account);
bool getHaveAccount() const;
void setHaveAccount(bool haveAccount);
@ -53,7 +55,8 @@ signals:
private:
bool mHaveAccount = false;
QSharedPointer<SafeConnection> mModelConnection;
QSharedPointer<AccountCore> mDefaultAccount;
QSharedPointer<SafeConnection<AccountList, CoreModel>> mModelConnection;
DECLARE_ABSTRACT_OBJECT
};

View file

@ -23,7 +23,6 @@
#include "AccountList.hpp"
AccountProxy::AccountProxy(QObject *parent) : SortFilterProxy(parent) {
qDebug() << "[AccountProxy] new" << this;
mList = AccountList::create();
connect(mList.get(), &AccountList::countChanged, this, &AccountProxy::resetDefaultAccount);
connect(mList.get(), &AccountList::defaultAccountChanged, this, &AccountProxy::resetDefaultAccount);
@ -33,7 +32,7 @@ AccountProxy::AccountProxy(QObject *parent) : SortFilterProxy(parent) {
}
AccountProxy::~AccountProxy() {
qDebug() << "[AccountProxy] delete" << this;
setSourceModel(nullptr);
}
QString AccountProxy::getFilterText() const {
@ -59,7 +58,7 @@ void AccountProxy::setDefaultAccount(AccountGui *account) {
// Reset the default account to let UI build its new object if needed.
void AccountProxy::resetDefaultAccount() {
mDefaultAccount = nullptr;
this->defaultAccountChanged(); // Warn the UI
emit this->defaultAccountChanged(); // Warn the UI
}
bool AccountProxy::getHaveAccount() const {

View file

@ -67,13 +67,12 @@ CallCore::~CallCore() {
}
void CallCore::setSelf(QSharedPointer<CallCore> me) {
mAccountModelConnection = QSharedPointer<SafeConnection>(
new SafeConnection(me.objectCast<QObject>(), std::dynamic_pointer_cast<QObject>(mCallModel)),
&QObject::deleteLater);
mAccountModelConnection->makeConnect(this, &CallCore::lSetMicrophoneMuted, [this](bool isMuted) {
mAccountModelConnection = QSharedPointer<SafeConnection<CallCore, CallModel>>(
new SafeConnection<CallCore, CallModel>(me, mCallModel), &QObject::deleteLater);
mAccountModelConnection->makeConnectToCore(&CallCore::lSetMicrophoneMuted, [this](bool isMuted) {
mAccountModelConnection->invokeToModel([this, isMuted]() { mCallModel->setMicrophoneMuted(isMuted); });
});
mAccountModelConnection->makeConnect(mCallModel.get(), &CallModel::microphoneMutedChanged, [this](bool isMuted) {
mAccountModelConnection->makeConnectToModel(&CallModel::microphoneMutedChanged, [this](bool isMuted) {
mAccountModelConnection->invokeToCore([this, isMuted]() { setMicrophoneMuted(isMuted); });
});
// mAccountModelConnection->makeConnect(this, &CallCore::lSetSpeakerMuted, [this](bool isMuted) {
@ -82,37 +81,37 @@ void CallCore::setSelf(QSharedPointer<CallCore> me) {
// mAccountModelConnection->makeConnect(mCallModel.get(), &CallModel::speakerMutedChanged, [this](bool isMuted) {
// mAccountModelConnection->invokeToCore([this, isMuted]() { setSpeakerMuted(isMuted); });
// });
mAccountModelConnection->makeConnect(this, &CallCore::lSetCameraEnabled, [this](bool enabled) {
mAccountModelConnection->makeConnectToCore(&CallCore::lSetCameraEnabled, [this](bool enabled) {
mAccountModelConnection->invokeToModel([this, enabled]() { mCallModel->setCameraEnabled(enabled); });
});
mAccountModelConnection->makeConnect(mCallModel.get(), &CallModel::cameraEnabledChanged, [this](bool enabled) {
mAccountModelConnection->makeConnectToModel(&CallModel::cameraEnabledChanged, [this](bool enabled) {
mAccountModelConnection->invokeToCore([this, enabled]() { setCameraEnabled(enabled); });
});
mAccountModelConnection->makeConnect(mCallModel.get(), &CallModel::durationChanged, [this](int duration) {
mAccountModelConnection->makeConnectToModel(&CallModel::durationChanged, [this](int duration) {
mAccountModelConnection->invokeToCore([this, duration]() { setDuration(duration); });
});
connect(mCallModel.get(), &CallModel::stateChanged, this,
[this](linphone::Call::State state, const std::string &message) {
mAccountModelConnection->invokeToCore([this, state, message]() {
setState(LinphoneEnums::fromLinphone(state), Utils::coreStringToAppString(message));
});
});
connect(mCallModel.get(), &CallModel::statusChanged, this, [this](linphone::Call::Status status) {
mAccountModelConnection->makeConnectToModel(
&CallModel::stateChanged, [this](linphone::Call::State state, const std::string &message) {
mAccountModelConnection->invokeToCore([this, state, message]() {
setState(LinphoneEnums::fromLinphone(state), Utils::coreStringToAppString(message));
});
});
mAccountModelConnection->makeConnectToModel(&CallModel::statusChanged, [this](linphone::Call::Status status) {
mAccountModelConnection->invokeToCore([this, status]() { setStatus(LinphoneEnums::fromLinphone(status)); });
});
mAccountModelConnection->makeConnect(this, &CallCore::lSetPaused, [this](bool paused) {
mAccountModelConnection->makeConnectToCore(&CallCore::lSetPaused, [this](bool paused) {
mAccountModelConnection->invokeToModel([this, paused]() { mCallModel->setPaused(paused); });
});
mAccountModelConnection->makeConnect(mCallModel.get(), &CallModel::pausedChanged, [this](bool paused) {
mAccountModelConnection->makeConnectToModel(&CallModel::pausedChanged, [this](bool paused) {
mAccountModelConnection->invokeToCore([this, paused]() { setPaused(paused); });
});
mAccountModelConnection->makeConnect(this, &CallCore::lTransferCall, [this](const QString &address) {
mAccountModelConnection->makeConnectToCore(&CallCore::lTransferCall, [this](const QString &address) {
mAccountModelConnection->invokeToModel(
[this, address]() { mCallModel->transferTo(ToolModel::interpretUrl(address)); });
});
mAccountModelConnection->makeConnect(
mCallModel.get(), &CallModel::transferStateChanged,
mAccountModelConnection->makeConnectToModel(
&CallModel::transferStateChanged,
[this](const std::shared_ptr<linphone::Call> &call, linphone::Call::State state) {
mAccountModelConnection->invokeToCore([this, state]() {
QString message;
@ -122,8 +121,8 @@ void CallCore::setSelf(QSharedPointer<CallCore> me) {
setTransferState(LinphoneEnums::fromLinphone(state), message);
});
});
mAccountModelConnection->makeConnect(
mCallModel.get(), &CallModel::encryptionChanged,
mAccountModelConnection->makeConnectToModel(
&CallModel::encryptionChanged,
[this](const std::shared_ptr<linphone::Call> &call, bool on, const std::string &authenticationToken) {
auto encryption = LinphoneEnums::fromLinphone(call->getCurrentParams()->getMediaEncryption());
auto tokenVerified = mCallModel->getAuthenticationTokenVerified();
@ -133,13 +132,12 @@ void CallCore::setSelf(QSharedPointer<CallCore> me) {
encryption == LinphoneEnums::MediaEncryption::Dtls);
});
});
mAccountModelConnection->makeConnect(this, &CallCore::lAccept, [this](bool withVideo) {
mAccountModelConnection->makeConnectToCore(&CallCore::lAccept, [this](bool withVideo) {
mAccountModelConnection->invokeToModel([this, withVideo]() { mCallModel->accept(withVideo); });
});
mAccountModelConnection->makeConnect(this, &CallCore::lDecline, [this]() {
mAccountModelConnection->invokeToModel([this]() { mCallModel->decline(); });
});
mAccountModelConnection->makeConnect(this, &CallCore::lTerminate, [this]() {
mAccountModelConnection->makeConnectToCore(
&CallCore::lDecline, [this]() { mAccountModelConnection->invokeToModel([this]() { mCallModel->decline(); }); });
mAccountModelConnection->makeConnectToCore(&CallCore::lTerminate, [this]() {
mAccountModelConnection->invokeToModel([this]() { mCallModel->terminate(); });
});
}
@ -255,4 +253,4 @@ void CallCore::setTransferState(LinphoneEnums::CallState state, const QString &m
if (state == LinphoneEnums::CallState::Error) setLastErrorMessage(message);
emit transferStateChanged();
}
}
}

View file

@ -23,12 +23,11 @@
#include "model/call/CallModel.hpp"
#include "tool/LinphoneEnums.hpp"
#include "tool/thread/SafeConnection.hpp"
#include <QObject>
#include <QSharedPointer>
#include <linphone++/linphone.hh>
class SafeConnection;
class CallCore : public QObject, public AbstractObject {
Q_OBJECT
@ -138,7 +137,7 @@ private:
bool mMicrophoneMuted;
bool mCameraEnabled;
bool mPaused = false;
QSharedPointer<SafeConnection> mAccountModelConnection;
QSharedPointer<SafeConnection<CallCore, CallModel>> mAccountModelConnection;
DECLARE_ABSTRACT_OBJECT
};

View file

@ -56,35 +56,36 @@ FriendCore::FriendCore(const FriendCore &friendCore) {
FriendCore::~FriendCore() {
}
void FriendCore::setSelf(QSharedPointer<FriendCore> me) {
setSelf(me.objectCast<QObject>());
void FriendCore::setSelf(SafeSharedPointer<FriendCore> me) {
setSelf(me.mQDataWeak.lock());
}
void FriendCore::setSelf(SafeSharedPointer<QObject> me) {
if (mFriendModel) {
mFriendModelConnection = QSharedPointer<SafeConnection>(
new SafeConnection(me, std::dynamic_pointer_cast<QObject>(mFriendModel)), &QObject::deleteLater);
mFriendModelConnection->makeConnect(
mFriendModel.get(), &FriendModel::presenceReceived,
[this](LinphoneEnums::ConsolidatedPresence consolidatedPresence, QDateTime presenceTimestamp) {
mFriendModelConnection->invokeToCore([this, consolidatedPresence, presenceTimestamp]() {
setConsolidatedPresence(consolidatedPresence);
setPresenceTimestamp(presenceTimestamp);
void FriendCore::setSelf(QSharedPointer<FriendCore> me) {
if (me) {
if (mFriendModel) {
mCoreModelConnection = nullptr; // No more needed
mFriendModelConnection = QSharedPointer<SafeConnection<FriendCore, FriendModel>>(
new SafeConnection<FriendCore, FriendModel>(me, mFriendModel), &QObject::deleteLater);
mFriendModelConnection->makeConnectToModel(
&FriendModel::presenceReceived,
[this](LinphoneEnums::ConsolidatedPresence consolidatedPresence, QDateTime presenceTimestamp) {
mFriendModelConnection->invokeToCore([this, consolidatedPresence, presenceTimestamp]() {
setConsolidatedPresence(consolidatedPresence);
setPresenceTimestamp(presenceTimestamp);
});
});
});
mFriendModelConnection->makeConnect(mFriendModel.get(), &FriendModel::pictureUriChanged, [this](QString uri) {
mFriendModelConnection->invokeToCore([this, uri]() { this->onPictureUriChanged(uri); });
});
mFriendModelConnection->makeConnectToModel(&FriendModel::pictureUriChanged, [this](QString uri) {
mFriendModelConnection->invokeToCore([this, uri]() { this->onPictureUriChanged(uri); });
});
// From GUI
mFriendModelConnection->makeConnect(this, &FriendCore::lSetPictureUri, [this](QString uri) {
mFriendModelConnection->invokeToModel([this, uri]() { mFriendModel->setPictureUri(uri); });
});
// From GUI
mFriendModelConnection->makeConnectToCore(&FriendCore::lSetPictureUri, [this](QString uri) {
mFriendModelConnection->invokeToModel([this, uri]() { mFriendModel->setPictureUri(uri); });
});
} else { // Create
mFriendModelConnection = QSharedPointer<SafeConnection>(
new SafeConnection(me, std::dynamic_pointer_cast<QObject>(CoreModel::getInstance())),
&QObject::deleteLater);
} else { // Create
mCoreModelConnection = QSharedPointer<SafeConnection<FriendCore, CoreModel>>(
new SafeConnection<FriendCore, CoreModel>(me, CoreModel::getInstance()), &QObject::deleteLater);
}
}
}
@ -200,7 +201,7 @@ void FriendCore::save() { // Save Value
mFriendModelConnection->invokeToCore([this]() { saved(); });
});
} else { // Creation
mFriendModelConnection->invokeToModel([this, thisCopy]() {
mCoreModelConnection->invokeToModel([this, thisCopy]() {
auto core = CoreModel::getInstance()->getCore();
auto contact = core->createFriend();
thisCopy->writeInto(contact);
@ -212,8 +213,8 @@ void FriendCore::save() { // Save Value
core->getDefaultFriendList()->updateSubscriptions();
}
emit CoreModel::getInstance()->friendAdded();
mFriendModelConnection->invokeToCore([this, created]() {
if (created) setSelf(mFriendModelConnection->mCore);
mCoreModelConnection->invokeToCore([this, created]() {
if (created) setSelf(mCoreModelConnection->mCore);
setIsSaved(created);
});
});

View file

@ -23,18 +23,19 @@
#include "model/friend/FriendModel.hpp"
#include "tool/LinphoneEnums.hpp"
#include "tool/thread/SafeConnection.hpp"
#include "tool/thread/SafeSharedPointer.hpp"
#include <QDateTime>
#include <QObject>
#include <QSharedPointer>
#include <linphone++/linphone.hh>
class SafeConnection;
// This object is defferent from usual Core. It set internal data from directly from GUI.
// Values are saved on request.
// This allow revert feature.
class CoreModel;
class FriendCore : public QObject, public AbstractObject {
Q_OBJECT
@ -53,7 +54,7 @@ public:
FriendCore(const FriendCore &friendCore);
~FriendCore();
void setSelf(QSharedPointer<FriendCore> me);
void setSelf(SafeSharedPointer<QObject> me);
void setSelf(SafeSharedPointer<FriendCore> me);
void reset(const FriendCore &contact);
QString getName() const;
@ -106,7 +107,8 @@ protected:
QString mPictureUri;
bool mIsSaved;
std::shared_ptr<FriendModel> mFriendModel;
QSharedPointer<SafeConnection> mFriendModelConnection;
QSharedPointer<SafeConnection<FriendCore, FriendModel>> mFriendModelConnection;
QSharedPointer<SafeConnection<FriendCore, CoreModel>> mCoreModelConnection;
DECLARE_ABSTRACT_OBJECT
};

View file

@ -162,10 +162,6 @@ static inline QString getAppRootCaFilePath() {
return "";
}
static inline QString getAppFriendsFilePath() {
return QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation) + Constants::PathFriendsList;
}
static inline QString getAppMessageHistoryFilePath() {
return QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation) + Constants::PathMessageHistoryList;
}
@ -243,10 +239,6 @@ QString Paths::getFactoryConfigFilePath() {
return getReadableFilePath(getAppFactoryConfigFilePath());
}
QString Paths::getFriendsListFilePath() {
return getWritableFilePath(getAppFriendsFilePath());
}
QString Paths::getDownloadDirPath() {
return getWritableDirPath(QStandardPaths::writableLocation(QStandardPaths::DownloadLocation) + QDir::separator());
}

View file

@ -39,7 +39,6 @@ QString getConfigFilePath(const QString &configPath = QString(), bool writable =
QString getDatabaseFilePath();
QString getDownloadDirPath();
QString getFactoryConfigFilePath();
QString getFriendsListFilePath();
QString getLimeDatabasePath();
QString getLogsDirPath();
QString getMessageHistoryFilePath();

View file

@ -57,38 +57,37 @@ MagicSearchList::~MagicSearchList() {
}
void MagicSearchList::setSelf(QSharedPointer<MagicSearchList> me) {
mModelConnection = QSharedPointer<SafeConnection>(
new SafeConnection(me.objectCast<QObject>(), std::dynamic_pointer_cast<QObject>(mMagicSearch)),
&QObject::deleteLater);
mModelConnection->makeConnect(this, &MagicSearchList::lSearch, [this](QString filter) {
mModelConnection = QSharedPointer<SafeConnection<MagicSearchList, MagicSearchModel>>(
new SafeConnection<MagicSearchList, MagicSearchModel>(me, mMagicSearch), &QObject::deleteLater);
mModelConnection->makeConnectToCore(&MagicSearchList::lSearch, [this](QString filter) {
mModelConnection->invokeToModel([this, filter]() { mMagicSearch->search(filter); });
});
mModelConnection->makeConnect(this, &MagicSearchList::lSetSourceFlags, [this](int flags) {
mModelConnection->makeConnectToCore(&MagicSearchList::lSetSourceFlags, [this](int flags) {
mModelConnection->invokeToModel([this, flags]() { mMagicSearch->setSourceFlags(flags); });
});
mModelConnection->makeConnect(mMagicSearch.get(), &MagicSearchModel::sourceFlagsChanged, [this](int flags) {
mModelConnection->makeConnectToModel(&MagicSearchModel::sourceFlagsChanged, [this](int flags) {
mModelConnection->invokeToCore([this, flags]() { setSourceFlags(flags); });
});
mModelConnection->makeConnect(mMagicSearch.get(), &MagicSearchModel::aggregationFlagChanged,
[this](LinphoneEnums::MagicSearchAggregation flag) {
mModelConnection->invokeToCore([this, flag]() { setAggregationFlag(flag); });
});
mModelConnection->makeConnectToModel(
&MagicSearchModel::aggregationFlagChanged, [this](LinphoneEnums::MagicSearchAggregation flag) {
mModelConnection->invokeToCore([this, flag]() { setAggregationFlag(flag); });
});
mModelConnection->makeConnect(mMagicSearch.get(), &MagicSearchModel::searchResultsReceived,
[this](const std::list<std::shared_ptr<linphone::SearchResult>> &results) {
auto *contacts = new QList<QSharedPointer<FriendCore>>();
for (auto it : results) {
QSharedPointer<FriendCore> contact;
if (it->getFriend()) {
contact = FriendCore::create(it->getFriend());
contacts->append(contact);
}
}
mModelConnection->invokeToCore([this, contacts]() {
setResults(*contacts);
delete contacts;
});
});
mModelConnection->makeConnectToModel(&MagicSearchModel::searchResultsReceived,
[this](const std::list<std::shared_ptr<linphone::SearchResult>> &results) {
auto *contacts = new QList<QSharedPointer<FriendCore>>();
for (auto it : results) {
QSharedPointer<FriendCore> contact;
if (it->getFriend()) {
contact = FriendCore::create(it->getFriend());
contacts->append(contact);
}
}
mModelConnection->invokeToCore([this, contacts]() {
setResults(*contacts);
delete contacts;
});
});
}
void MagicSearchList::setResults(const QList<QSharedPointer<FriendCore>> &contacts) {

View file

@ -28,6 +28,7 @@
#include <QLocale>
class FriendCore;
class CoreModel;
// =============================================================================
// Return FriendGui list to Ui
@ -63,8 +64,8 @@ private:
LinphoneEnums::MagicSearchAggregation mAggregationFlag;
std::shared_ptr<MagicSearchModel> mMagicSearch;
QSharedPointer<SafeConnection> mModelConnection;
QSharedPointer<SafeConnection> mCoreModelConnection;
QSharedPointer<SafeConnection<MagicSearchList, MagicSearchModel>> mModelConnection;
QSharedPointer<SafeConnection<MagicSearchList, CoreModel>> mCoreModelConnection;
DECLARE_ABSTRACT_OBJECT
};

View file

@ -38,6 +38,11 @@ AccountModel::AccountModel(const std::shared_ptr<linphone::Account> &account, QO
// Hack because Account doesn't provide callbacks on updated data
connect(this, &AccountModel::defaultAccountChanged, this,
[this]() { emit pictureUriChanged(Utils::coreStringToAppString(mMonitor->getParams()->getPictureUri())); });
connect(CoreModel::getInstance().get(), &CoreModel::unreadNotificationsChanged, this, [this]() {
emit unreadNotificationsChanged(0 /*mMonitor->getUnreadChatMessageCount()*/,
mMonitor->getMissedCallsCount()); // TODO
});
}
AccountModel::~AccountModel() {
@ -80,3 +85,7 @@ void AccountModel::setDefault() {
auto core = CoreModel::getInstance()->getCore();
core->setDefaultAccount(mMonitor);
}
void AccountModel::removeAccount() {
CoreModel::getInstance()->getCore()->removeAccount(mMonitor);
}

View file

@ -42,6 +42,7 @@ public:
void setPictureUri(QString uri);
void setDefault();
void removeAccount();
signals:
void registrationStateChanged(const std::shared_ptr<linphone::Account> &account,
@ -50,6 +51,7 @@ signals:
void defaultAccountChanged(bool isDefault);
void pictureUriChanged(QString uri);
void unreadNotificationsChanged(int unreadMessagesCount, int unreadCallsCount);
private:
DECLARE_ABSTRACT_OBJECT

View file

@ -63,18 +63,23 @@ std::shared_ptr<CoreModel> CoreModel::create(const QString &configPath, QThread
void CoreModel::start() {
mIterateTimer = new QTimer(this);
mIterateTimer->setInterval(30);
connect(mIterateTimer, &QTimer::timeout, [this]() { mCore->iterate(); });
connect(mIterateTimer, &QTimer::timeout, [this]() {
static int iterateCount = 0;
if (iterateCount != 0) qCritical() << log().arg("Multi Iterate ! ");
++iterateCount;
mCore->iterate();
--iterateCount;
});
setPathBeforeCreation();
mCore =
linphone::Factory::get()->createCore(Utils::appStringToCoreString(Paths::getConfigFilePath(mConfigPath)),
Utils::appStringToCoreString(Paths::getFactoryConfigFilePath()), nullptr);
setMonitor(mCore);
setPathsAfterCreation();
mCore->start();
setPathAfterStart();
mCore->enableFriendListSubscription(true);
mCore->enableRecordAware(true);
mCore->getCallsNb();
mCore->start();
setPathAfterStart();
mIterateTimer->start();
}
// -----------------------------------------------------------------------------
@ -92,7 +97,7 @@ void CoreModel::setConfigPath(QString path) {
if (mConfigPath != path) {
mConfigPath = path;
if (!mCore) {
qWarning() << "[CoreModel] Setting config path after core creation is not yet supported";
qWarning() << log().arg("Setting config path after core creation is not yet supported");
}
}
}
@ -118,9 +123,6 @@ void CoreModel::setPathBeforeCreation() {
}
void CoreModel::setPathsAfterCreation() {
QString friendsDb = Paths::getFriendsListFilePath();
qInfo() << QStringLiteral("[CoreModel] Set Database `Friends` path: `%1`").arg(friendsDb);
mCore->setFriendsDatabasePath(Utils::appStringToCoreString(friendsDb));
}
void CoreModel::setPathAfterStart() {
@ -169,6 +171,7 @@ void CoreModel::onCallEncryptionChanged(const std::shared_ptr<linphone::Core> &c
}
void CoreModel::onCallLogUpdated(const std::shared_ptr<linphone::Core> &core,
const std::shared_ptr<linphone::CallLog> &callLog) {
if (callLog && callLog->getStatus() == linphone::Call::Status::Missed) emit unreadNotificationsChanged();
emit callLogUpdated(core, callLog);
}
void CoreModel::onCallStateChanged(const std::shared_ptr<linphone::Core> &core,
@ -242,11 +245,13 @@ void CoreModel::onLogCollectionUploadProgressIndication(const std::shared_ptr<li
void CoreModel::onMessageReceived(const std::shared_ptr<linphone::Core> &core,
const std::shared_ptr<linphone::ChatRoom> &room,
const std::shared_ptr<linphone::ChatMessage> &message) {
emit unreadNotificationsChanged();
emit messageReceived(core, room, message);
}
void CoreModel::onMessagesReceived(const std::shared_ptr<linphone::Core> &core,
const std::shared_ptr<linphone::ChatRoom> &room,
const std::list<std::shared_ptr<linphone::ChatMessage>> &messages) {
emit unreadNotificationsChanged();
emit messagesReceived(core, room, messages);
}

View file

@ -58,6 +58,7 @@ signals:
void loggerInitialized();
void friendAdded();
void friendRemoved();
void unreadNotificationsChanged();
private:
QString mConfigPath;

View file

@ -44,7 +44,6 @@ public:
setMonitor(monitor);
}
~Listener() {
qDebug() << "Destroying Listener";
setSelf(nullptr);
}
virtual void onRemoveListener() {

View file

@ -33,17 +33,16 @@ VariantObject::VariantObject(QVariant defaultValue, QObject *parent) {
mModelObject = QSharedPointer<SafeObject>::create();
mModelObject->moveToThread(CoreModel::getInstance()->thread());
mConnection = QSharedPointer<SafeConnection>(
new SafeConnection(mCoreObject.objectCast<QObject>(), mModelObject.objectCast<QObject>()),
&QObject::deleteLater);
mConnection = QSharedPointer<SafeConnection<SafeObject, SafeObject>>(
new SafeConnection<SafeObject, SafeObject>(mCoreObject, mModelObject), &QObject::deleteLater);
mConnection->makeConnect(mCoreObject.get(), &SafeObject::setValue, [this](QVariant value) {
mConnection->makeConnectToCore(&SafeObject::setValue, [this](QVariant value) {
mConnection->invokeToModel([this, value]() { mModelObject->onSetValue(value); });
});
mConnection->makeConnect(mModelObject.get(), &SafeObject::setValue, [this](QVariant value) {
mConnection->makeConnectToModel(&SafeObject::setValue, [this](QVariant value) {
mConnection->invokeToCore([this, value]() { mCoreObject->onSetValue(value); });
});
mConnection->makeConnect(mModelObject.get(), &SafeObject::valueChanged, [this](QVariant value) {
mConnection->makeConnectToModel(&SafeObject::valueChanged, [this](QVariant value) {
mConnection->invokeToCore([this, value]() { mCoreObject->valueChanged(value); });
});
connect(mCoreObject.get(), &SafeObject::valueChanged, this, &VariantObject::valueChanged);

View file

@ -30,6 +30,8 @@
#include "SafeObject.hpp"
#include "tool/thread/SafeConnection.hpp"
class CoreModel;
class VariantObject : public QObject, public AbstractObject {
Q_OBJECT
Q_PROPERTY(QVariant value READ getValue NOTIFY valueChanged)
@ -40,21 +42,21 @@ public:
template <typename Func, typename... Args>
void makeRequest(Func &&callable, Args &&...args) {
mConnection->makeConnect(mCoreObject.get(), &SafeObject::requestValue, [this, callable, args...]() {
mConnection->makeConnectToCore(&SafeObject::requestValue, [this, callable, args...]() {
mConnection->invokeToModel([this, callable, args...]() { mModelObject->setValue(callable(args...)); });
});
}
template <typename SenderClass>
void makeUpdate(const typename QtPrivate::FunctionPointer<SenderClass>::Object *sender, SenderClass signal) {
mConnection->makeConnect(sender, signal,
[this]() { mConnection->invokeToCore([this]() { mCoreObject->requestValue(); }); });
template <typename Sender, typename SenderClass>
void makeUpdate(Sender sender, SenderClass signal) {
mConnection->makeConnectToModel(
sender, signal, [this]() { mConnection->invokeToCore([this]() { mCoreObject->requestValue(); }); });
}
QVariant getValue() const;
void requestValue();
QSharedPointer<SafeObject> mCoreObject, mModelObject;
QSharedPointer<SafeConnection> mConnection;
QSharedPointer<SafeConnection<SafeObject, SafeObject>> mConnection;
signals:
void valueChanged(QVariant value);

View file

@ -5,7 +5,7 @@ list(APPEND _LINPHONEAPP_SOURCES
tool/LinphoneEnums.cpp
tool/thread/SafeSharedPointer.hpp
tool/thread/SafeConnection.cpp
tool/thread/SafeConnection.hpp
tool/thread/Thread.cpp
tool/providers/AvatarProvider.cpp
tool/providers/ImageProvider.cpp

View file

@ -75,7 +75,6 @@ constexpr char Constants::PathConfig[];
constexpr char Constants::PathDatabase[];
constexpr char Constants::PathFactoryConfig[];
constexpr char Constants::PathRootCa[];
constexpr char Constants::PathFriendsList[];
constexpr char Constants::PathLimeDatabase[];
constexpr char Constants::PathMessageHistoryList[];
constexpr char Constants::PathZrtpSecrets[];

View file

@ -146,7 +146,6 @@ public:
static constexpr char PathDatabase[] = "/linphone.db";
static constexpr char PathFactoryConfig[] = "/" EXECUTABLE_NAME "/linphonerc-factory";
static constexpr char PathRootCa[] = "/" EXECUTABLE_NAME "/rootca.pem";
static constexpr char PathFriendsList[] = "/friends.db";
static constexpr char PathLimeDatabase[] = "/x3dh.c25519.sqlite3";
static constexpr char PathMessageHistoryList[] = "/message-history.db";
static constexpr char PathZrtpSecrets[] = "/zidcache";

View file

@ -45,7 +45,9 @@ char *Utils::rstrstr(const char *a, const char *b) {
}
VariantObject *Utils::getDisplayName(const QString &address) {
VariantObject *data = new VariantObject(address); // Scope : GUI
QStringList splitted = address.split(":");
if (splitted.size() > 0 && splitted[0] == "sip") splitted.removeFirst();
VariantObject *data = new VariantObject(splitted.first().split("@").first()); // Scope : GUI
data->makeRequest([address]() {
QString displayName = ToolModel::getDisplayName(address);
return displayName;

View file

@ -1,32 +0,0 @@
/*
* Copyright (c) 2010-2024 Belledonne Communications SARL.
*
* This file is part of linphone-desktop
* (see https://www.linphone.org).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "SafeConnection.hpp"
SafeConnection::SafeConnection(SafeSharedPointer<QObject> core, SafeSharedPointer<QObject> model)
: mCore(core), mModel(model) {
}
SafeConnection::~SafeConnection() {
mLocker.lock();
if (mCore.mCountRef != 0 || mModel.mCountRef != 0)
qCritical() << "[SafeConnection] Destruction while still having references. CoreRef=" << mCore.mCountRef
<< "ModelRef=" << mModel.mCountRef;
mLocker.unlock();
}

View file

@ -22,6 +22,7 @@
#define SAFE_CONNECTION_H_
#include "SafeSharedPointer.hpp"
#include "model/core/CoreModel.hpp"
#include <QMutex>
#include <QObject>
@ -60,19 +61,40 @@
*
*/
template <class A, class B>
class SafeConnection : public QObject {
public:
SafeConnection(SafeSharedPointer<QObject> a, SafeSharedPointer<QObject> b);
~SafeConnection();
SafeSharedPointer<QObject> mCore, mModel;
// SafeConnection(SafeSharedPointer<QObject> a, SafeSharedPointer<QObject> b);
SafeConnection(QSharedPointer<A> a, std::shared_ptr<B> b)
: mCore(a), mModel(b), mCoreObject(a.get()), mModelObject(b.get()) {
}
SafeConnection(QSharedPointer<A> a, QSharedPointer<B> b)
: mCore(a), mModel(b), mCoreObject(a.get()), mModelObject(b.get()) {
}
~SafeConnection() {
mLocker.lock();
if (mCore.mCountRef != 0 || mModel.mCountRef != 0)
qCritical() << "[SafeConnection] Destruction while still having references. CoreRef=" << mCore.mCountRef
<< "ModelRef=" << mModel.mCountRef;
mCore.reset();
mModel.reset();
mLocker.unlock();
}
SafeSharedPointer<A> mCore;
SafeSharedPointer<B> mModel;
QMutex mLocker;
template <typename Func1, typename Func2>
static inline QMetaObject::Connection makeConnect(const typename QtPrivate::FunctionPointer<Func1>::Object *sender,
Func1 signal,
Func2 slot,
Qt::ConnectionType type = Qt::AutoConnection) {
return connect(sender, signal, sender, slot, type);
inline QMetaObject::Connection makeConnectToModel(Func1 signal, Func2 slot) {
return connect(mModelObject, signal, mCoreObject, slot, Qt::DirectConnection);
}
template <typename Sender, typename Func1, typename Func2>
inline QMetaObject::Connection makeConnectToModel(Sender sender, Func1 signal, Func2 slot) {
return connect(sender, signal, mCoreObject, slot, Qt::DirectConnection);
}
template <typename Func1, typename Func2>
inline QMetaObject::Connection makeConnectToCore(Func1 signal, Func2 slot) {
return connect(mCoreObject, signal, mModelObject, slot, Qt::DirectConnection);
}
template <typename Func, typename... Args>
@ -112,6 +134,10 @@ public:
mModel.unlock();
mLocker.unlock();
}
protected:
A *mCoreObject = nullptr;
B *mModelObject = nullptr; // Use only for makeConnects
};
#endif

View file

@ -13,6 +13,8 @@ import UtilsCpp
Item {
id: mainItem
signal addAccountRequest()
RowLayout {
anchors.fill: parent
@ -79,6 +81,7 @@ Item {
contentHeight: accounts.height
Accounts{
id: accounts
onAddAccountRequest: mainItem.addAccountRequest()
}
}
}

View file

@ -36,6 +36,8 @@ Window {
Component {
id: loginPage
LoginPage {
showBackButton: accountProxy.haveAccount
onGoBack: mainWindowStackView.replace(mainPage)
onUseSIPButtonClicked: mainWindowStackView.push(sipLoginPage)
onGoToRegister: mainWindowStackView.replace(registerPage)
onConnectionSucceed: {
@ -50,7 +52,7 @@ Window {
Component {
id: sipLoginPage
SIPLoginPage {
onReturnToLogin: mainWindowStackView.pop()
onGoBack: mainWindowStackView.pop()
onGoToRegister: mainWindowStackView.replace(registerPage)
onConnectionSucceed: {
@ -90,6 +92,7 @@ Window {
Component {
id: mainPage
MainLayout {
onAddAccountRequest: mainWindowStackView.replace(loginPage)
}
}
}

View file

@ -1,6 +1,8 @@
import QtQuick 2.15
import QtQuick.Layouts 1.3
import QtQuick.Controls 2.2 as Control
import QtCore
import QtQuick
import QtQuick.Layouts
import QtQuick.Controls as Control
import QtQuick.Dialogs
import Linphone
import UtilsCpp
@ -13,6 +15,9 @@ Item {
readonly property int leftPadding: 32 * DefaultStyle.dp
readonly property int rightPadding: 32 * DefaultStyle.dp
readonly property int spacing: 16 * DefaultStyle.dp
signal addAccountRequest()
implicitHeight: list.contentHeight + topPadding + bottomPadding + 32 * DefaultStyle.dp + 1 + newAccountArea.height
ColumnLayout{
anchors.top: parent.top
@ -28,8 +33,21 @@ Item {
spacing: mainItem.spacing
model: AccountProxy{}
delegate: Contact{
id: contactItem
width: list.width
account: modelData
onAvatarClicked: fileDialog.open()
onBackgroundClicked: modelData.core.lSetDefaultAccount()
FileDialog {
id: fileDialog
currentFolder: StandardPaths.standardLocations(StandardPaths.PicturesLocation)[0]
onAccepted: {
var avatarPath = UtilsCpp.createAvatar( selectedFile )
if(avatarPath){
modelData.core.pictureUri = avatarPath
}
}
}
}
}
Rectangle{
@ -40,10 +58,10 @@ Item {
height: 1
color: DefaultStyle.main2_300
}
MouseArea{ // TODO
MouseArea{
Layout.fillWidth: true
Layout.preferredHeight: 32 * DefaultStyle.dp
onClicked: console.log('New!')
onClicked: mainItem.addAccountRequest()
RowLayout{
id: newAccountArea
anchors.fill: parent

View file

@ -13,11 +13,11 @@ StackView{
id: mainItem
property FriendGui contact
property AccountGui account
onAccountChanged: if(account) replace(avatar, StackView.Immediate)
property string address: account ? account.core.identityAddress : ''
property var displayNameObj: UtilsCpp.getDisplayName(address)
property bool haveAvatar: (account && account.core.pictureUri )
|| (contact && contact.core.pictureUri)
onHaveAvatarChanged: replace(haveAvatar ? avatar : initials, StackView.Immediate)
initialItem: haveAvatar ? avatar : initials
Component{
@ -25,12 +25,10 @@ StackView{
Rectangle {
id: initialItem
property string initials: UtilsCpp.getInitials(mainItem.displayNameObj.value)
onInitialsChanged: console.log("newInit:"+initials)
radius: width / 2
color: DefaultStyle.main2_200
height: mainItem.height
width: height
Component.onCompleted: console.log("init:"+initials)
Text {
anchors.fill: parent
anchors.centerIn: parent

View file

@ -1,7 +1,6 @@
import QtCore
import QtQuick
import QtQuick.Effects
import QtQuick.Dialogs
import QtQuick.Layouts
@ -11,7 +10,14 @@ import UtilsCpp
Rectangle{
id: mainItem
property AccountGui account
signal avatarClicked()
signal backgroundClicked()
height: 45 * DefaultStyle.dp
MouseArea{
anchors.fill: parent
onClicked: mainItem.backgroundClicked()
}
RowLayout{
anchors.fill: parent
spacing: 0
@ -22,17 +28,7 @@ Rectangle{
account: mainItem.account
MouseArea{
anchors.fill: parent
onClicked: fileDialog.open()
}
FileDialog {
id: fileDialog
currentFolder: StandardPaths.standardLocations(StandardPaths.PicturesLocation)[0]
onAccepted: {
var avatarPath = UtilsCpp.createAvatar( selectedFile )
if(avatarPath){
mainItem.account.core.pictureUri = avatarPath
}
}
onClicked: mainItem.avatarClicked()
}
}
ContactDescription{
@ -95,7 +91,7 @@ Rectangle{
}
}
}
Item{ // TODO
Item{
Layout.preferredWidth: 100 * DefaultStyle.dp
Layout.fillHeight: true
Rectangle{
@ -103,6 +99,7 @@ Rectangle{
anchors.left: parent.left
anchors.leftMargin: 10 * DefaultStyle.dp
anchors.verticalCenter: parent.verticalCenter
visible: unreadCount.text > 0
width: 22 * DefaultStyle.dp
height: 22 * DefaultStyle.dp
radius: width/2
@ -112,10 +109,15 @@ Rectangle{
Text{
id: unreadCount
anchors.fill: parent
anchors.margins: 2 * DefaultStyle.dp
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
color: DefaultStyle.grey_0
text: '2'
minimumPixelSize: 5
fontSizeMode: Text.Fit
font.pixelSize: 20 * DefaultStyle.dp
property var unread: mainItem.account.core.unreadNotifications
text: unread > 100 ? '+' : unread
}
}
}
@ -130,7 +132,7 @@ Rectangle{
colorizationColor: DefaultStyle.main2_500main
MouseArea{ // TODO
anchors.fill: parent
onClicked: console.log('Manage!')
onClicked: console.log('TODO: Manage!')
}
}
}

View file

@ -5,11 +5,28 @@ import Linphone
LoginLayout {
id: mainItem
property bool showBackButton: false
signal goBack()
signal useSIPButtonClicked()
signal goToRegister()
signal connectionSucceed()
titleContent: RowLayout {
Control.Button {
Layout.preferredHeight: 40 * DefaultStyle.dp
Layout.preferredWidth: height
visible: mainItem.showBackButton
icon.width: width
icon.height: height
icon.source: AppIcons.returnArrow
background: Rectangle {
color: "transparent"
}
onClicked: {
console.debug("[LoginLayout] User: return")
mainItem.goBack()
}
}
Image {
fillMode: Image.PreserveAspectFit
source: AppIcons.profile
@ -24,7 +41,7 @@ LoginLayout {
Layout.fillWidth: true
}
Text {
Layout.rightMargin: 15
Layout.rightMargin: 15 * DefaultStyle.dp
text: "No account yet ?"
font.pointSize: DefaultStyle.indicatorMessageTextSize
}
@ -47,7 +64,7 @@ LoginLayout {
onConnectionSucceed: mainItem.connectionSucceed()
}
Button {
Layout.topMargin: 40
Layout.topMargin: 40 * DefaultStyle.dp
inversedColors: true
text: "Use SIP Account"
onClicked: {mainItem.useSIPButtonClicked()}
@ -57,8 +74,8 @@ LoginLayout {
Layout.fillWidth: true
}
Image {
Layout.rightMargin: 40
Layout.preferredWidth: 300
Layout.rightMargin: 40 * DefaultStyle.dp
Layout.preferredWidth: 300 * DefaultStyle.dp
fillMode: Image.PreserveAspectFit
source: AppIcons.loginImage
}

View file

@ -6,7 +6,7 @@ import ConstantsCpp 1.0
LoginLayout {
id: mainItem
signal returnToLogin()
signal goBack()
signal goToRegister()
signal connectionSucceed()
@ -22,7 +22,7 @@ LoginLayout {
}
onClicked: {
console.debug("[SIPLoginPage] User: return")
mainItem.returnToLogin()
mainItem.goBack()
}
}
Image {

@ -1 +1 @@
Subproject commit bf9106ee57b8a32aea2693f0fc69c2397a66d570
Subproject commit d07e85507cb6ddba7fe397f5fc699835516945aa