Feature : Avatar from default account.
- Update SDK for accountRemoved callback and fix on setting default account. - Update AccountGui from list modification and default account selection. - Add Avatar provider for Qml. - Create avatar file and store it into avatars folder. - Delete old avatar file if replaced.
This commit is contained in:
parent
9e1e797d8c
commit
b316074feb
14 changed files with 120 additions and 29 deletions
|
|
@ -46,9 +46,9 @@
|
|||
#include "model/object/VariantObject.hpp"
|
||||
#include "tool/Constants.hpp"
|
||||
#include "tool/Utils.hpp"
|
||||
#include "tool/thread/Thread.hpp"
|
||||
|
||||
#include "tool/providers/AvatarProvider.hpp"
|
||||
#include "tool/providers/ImageProvider.hpp"
|
||||
#include "tool/thread/Thread.hpp"
|
||||
|
||||
App::App(int &argc, char *argv[])
|
||||
: SingleApplication(argc, argv, true, Mode::User | Mode::ExcludeAppPath | Mode::ExcludeAppVersion) {
|
||||
|
|
@ -102,6 +102,7 @@ void App::init() {
|
|||
mEngine->rootContext()->setContextProperty("applicationDirPath", QGuiApplication::applicationDirPath());
|
||||
initCppInterfaces();
|
||||
mEngine->addImageProvider(ImageProvider::ProviderId, new ImageProvider());
|
||||
mEngine->addImageProvider(AvatarProvider::ProviderId, new AvatarProvider());
|
||||
|
||||
// Enable notifications.
|
||||
mNotifier = new Notifier(mEngine);
|
||||
|
|
|
|||
|
|
@ -71,7 +71,8 @@ void AccountList::setSelf(QSharedPointer<AccountList> me) {
|
|||
});
|
||||
});
|
||||
});
|
||||
|
||||
mModelConnection->makeConnect(CoreModel::getInstance().get(), &CoreModel::defaultAccountChanged,
|
||||
[this]() { mModelConnection->invokeToCore([this]() { defaultAccountChanged(); }); });
|
||||
lUpdate();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -49,6 +49,7 @@ public:
|
|||
signals:
|
||||
void lUpdate();
|
||||
void haveAccountChanged();
|
||||
void defaultAccountChanged();
|
||||
|
||||
private:
|
||||
bool mHaveAccount = false;
|
||||
|
|
|
|||
|
|
@ -25,7 +25,8 @@
|
|||
AccountProxy::AccountProxy(QObject *parent) : SortFilterProxy(parent) {
|
||||
qDebug() << "[AccountProxy] new" << this;
|
||||
mList = AccountList::create();
|
||||
connect(mList.get(), &AccountList::countChanged, this, &AccountProxy::defaultAccountChanged);
|
||||
connect(mList.get(), &AccountList::countChanged, this, &AccountProxy::resetDefaultAccount);
|
||||
connect(mList.get(), &AccountList::defaultAccountChanged, this, &AccountProxy::resetDefaultAccount);
|
||||
connect(mList.get(), &AccountList::haveAccountChanged, this, &AccountProxy::haveAccountChanged);
|
||||
setSourceModel(mList.get());
|
||||
sort(0);
|
||||
|
|
@ -47,13 +48,20 @@ void AccountProxy::setFilterText(const QString &filter) {
|
|||
}
|
||||
}
|
||||
|
||||
AccountGui *AccountProxy::getDefaultAccount() const {
|
||||
return dynamic_cast<AccountList *>(sourceModel())->getDefaultAccount();
|
||||
AccountGui *AccountProxy::getDefaultAccount() {
|
||||
if (!mDefaultAccount) mDefaultAccount = dynamic_cast<AccountList *>(sourceModel())->getDefaultAccount();
|
||||
return mDefaultAccount;
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
bool AccountProxy::getHaveAccount() const {
|
||||
return dynamic_cast<AccountList *>(sourceModel())->getHaveAccount();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -41,8 +41,9 @@ public:
|
|||
QString getFilterText() const;
|
||||
void setFilterText(const QString &filter);
|
||||
|
||||
AccountGui *getDefaultAccount() const;
|
||||
void setDefaultAccount(AccountGui *account);
|
||||
AccountGui *getDefaultAccount(); // Get a new object from List or give the stored one.
|
||||
void setDefaultAccount(AccountGui *account); // TODO
|
||||
void resetDefaultAccount(); // Reset the default account to let UI build its new object if needed.
|
||||
|
||||
bool getHaveAccount() const;
|
||||
|
||||
|
|
@ -56,6 +57,7 @@ protected:
|
|||
virtual bool lessThan(const QModelIndex &left, const QModelIndex &right) const override;
|
||||
|
||||
QString mFilterText;
|
||||
AccountGui *mDefaultAccount = nullptr; // When null, a new UI object is build from List
|
||||
QSharedPointer<AccountList> mList;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -20,10 +20,12 @@
|
|||
|
||||
#include "AccountModel.hpp"
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
#include "core/path/Paths.hpp"
|
||||
#include "model/core/CoreModel.hpp"
|
||||
#include "tool/Utils.hpp"
|
||||
#include "tool/providers/AvatarProvider.hpp"
|
||||
#include <QDebug>
|
||||
#include <QUrl>
|
||||
|
||||
DEFINE_ABSTRACT_OBJECT(AccountModel)
|
||||
|
||||
|
|
@ -48,6 +50,15 @@ void AccountModel::setPictureUri(QString uri) {
|
|||
mustBeInLinphoneThread(log().arg(Q_FUNC_INFO));
|
||||
auto account = std::dynamic_pointer_cast<linphone::Account>(mMonitor);
|
||||
auto params = account->getParams()->clone();
|
||||
auto oldPictureUri = Utils::coreStringToAppString(params->getPictureUri());
|
||||
if (!oldPictureUri.isEmpty()) {
|
||||
QString appPrefix = QStringLiteral("image://%1/").arg(AvatarProvider::ProviderId);
|
||||
if (oldPictureUri.startsWith(appPrefix)) {
|
||||
oldPictureUri = Paths::getAvatarsDirPath() + oldPictureUri.mid(appPrefix.length());
|
||||
}
|
||||
QFile oldPicture(oldPictureUri);
|
||||
if (!oldPicture.remove()) qWarning() << log().arg("Cannot delete old avatar file at " + oldPictureUri);
|
||||
}
|
||||
params->setPictureUri(Utils::appStringToCoreString(uri));
|
||||
account->setParams(params);
|
||||
emit pictureUriChanged(uri);
|
||||
|
|
@ -61,5 +72,4 @@ void AccountModel::setDefault() {
|
|||
mustBeInLinphoneThread(log().arg(Q_FUNC_INFO));
|
||||
auto core = CoreModel::getInstance()->getCore();
|
||||
core->setDefaultAccount(mMonitor);
|
||||
emit CoreModel::getInstance()->defaultAccountChanged();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -139,6 +139,14 @@ void CoreModel::setPathAfterStart() {
|
|||
|
||||
//---------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
void CoreModel::onAccountAdded(const std::shared_ptr<linphone::Core> &core,
|
||||
const std::shared_ptr<linphone::Account> &account) {
|
||||
emit accountAdded(core, account);
|
||||
}
|
||||
void CoreModel::onAccountRemoved(const std::shared_ptr<linphone::Core> &core,
|
||||
const std::shared_ptr<linphone::Account> &account) {
|
||||
emit accountRemoved(core, account);
|
||||
}
|
||||
void CoreModel::onAccountRegistrationStateChanged(const std::shared_ptr<linphone::Core> &core,
|
||||
const std::shared_ptr<linphone::Account> &account,
|
||||
linphone::RegistrationState state,
|
||||
|
|
@ -195,6 +203,10 @@ void CoreModel::onConfiguringStatus(const std::shared_ptr<linphone::Core> &core,
|
|||
const std::string &message) {
|
||||
emit configuringStatus(core, status, message);
|
||||
}
|
||||
void CoreModel::onDefaultAccountChanged(const std::shared_ptr<linphone::Core> &core,
|
||||
const std::shared_ptr<linphone::Account> &account) {
|
||||
emit defaultAccountChanged(core, account);
|
||||
}
|
||||
void CoreModel::onDtmfReceived(const std::shared_ptr<linphone::Core> &lc,
|
||||
const std::shared_ptr<linphone::Call> &call,
|
||||
int dtmf) {
|
||||
|
|
@ -234,10 +246,7 @@ void CoreModel::onMessagesReceived(const std::shared_ptr<linphone::Core> &core,
|
|||
const std::list<std::shared_ptr<linphone::ChatMessage>> &messages) {
|
||||
emit messagesReceived(core, room, messages);
|
||||
}
|
||||
void CoreModel::onNewAccountAdded(const std::shared_ptr<linphone::Core> &core,
|
||||
const std::shared_ptr<linphone::Account> &account) {
|
||||
emit accountAdded(core, account);
|
||||
}
|
||||
|
||||
void CoreModel::onNewMessageReaction(const std::shared_ptr<linphone::Core> &core,
|
||||
const std::shared_ptr<linphone::ChatRoom> &chatRoom,
|
||||
const std::shared_ptr<linphone::ChatMessage> &message,
|
||||
|
|
|
|||
|
|
@ -56,7 +56,6 @@ public:
|
|||
|
||||
signals:
|
||||
void loggerInitialized();
|
||||
void defaultAccountChanged();
|
||||
void friendAdded();
|
||||
void friendRemoved();
|
||||
|
||||
|
|
@ -74,6 +73,10 @@ private:
|
|||
//--------------------------------------------------------------------------------
|
||||
// LINPHONE
|
||||
//--------------------------------------------------------------------------------
|
||||
virtual void onAccountAdded(const std::shared_ptr<linphone::Core> &core,
|
||||
const std::shared_ptr<linphone::Account> &account) override;
|
||||
virtual void onAccountRemoved(const std::shared_ptr<linphone::Core> &core,
|
||||
const std::shared_ptr<linphone::Account> &account) override;
|
||||
virtual void onAccountRegistrationStateChanged(const std::shared_ptr<linphone::Core> &core,
|
||||
const std::shared_ptr<linphone::Account> &account,
|
||||
linphone::RegistrationState state,
|
||||
|
|
@ -107,6 +110,8 @@ private:
|
|||
virtual void onConfiguringStatus(const std::shared_ptr<linphone::Core> &core,
|
||||
linphone::Config::ConfiguringState status,
|
||||
const std::string &message) override;
|
||||
virtual void onDefaultAccountChanged(const std::shared_ptr<linphone::Core> &core,
|
||||
const std::shared_ptr<linphone::Account> &account) override;
|
||||
virtual void onDtmfReceived(const std::shared_ptr<linphone::Core> &lc,
|
||||
const std::shared_ptr<linphone::Call> &call,
|
||||
int dtmf) override;
|
||||
|
|
@ -130,8 +135,6 @@ private:
|
|||
virtual void onMessagesReceived(const std::shared_ptr<linphone::Core> &core,
|
||||
const std::shared_ptr<linphone::ChatRoom> &room,
|
||||
const std::list<std::shared_ptr<linphone::ChatMessage>> &messages) override;
|
||||
virtual void onNewAccountAdded(const std::shared_ptr<linphone::Core> &core,
|
||||
const std::shared_ptr<linphone::Account> &account) override;
|
||||
|
||||
virtual void onNewMessageReaction(const std::shared_ptr<linphone::Core> &core,
|
||||
const std::shared_ptr<linphone::ChatRoom> &chatRoom,
|
||||
|
|
@ -159,7 +162,7 @@ private:
|
|||
|
||||
signals:
|
||||
void accountAdded(const std::shared_ptr<linphone::Core> &core, const std::shared_ptr<linphone::Account> &account);
|
||||
void accountRemoved();
|
||||
void accountRemoved(const std::shared_ptr<linphone::Core> &core, const std::shared_ptr<linphone::Account> &account);
|
||||
void accountRegistrationStateChanged(const std::shared_ptr<linphone::Core> &core,
|
||||
const std::shared_ptr<linphone::Account> &account,
|
||||
linphone::RegistrationState state,
|
||||
|
|
@ -189,6 +192,8 @@ signals:
|
|||
void configuringStatus(const std::shared_ptr<linphone::Core> &core,
|
||||
linphone::Config::ConfiguringState status,
|
||||
const std::string &message);
|
||||
void defaultAccountChanged(const std::shared_ptr<linphone::Core> &core,
|
||||
const std::shared_ptr<linphone::Account> &account);
|
||||
void dtmfReceived(const std::shared_ptr<linphone::Core> &lc, const std::shared_ptr<linphone::Call> &call, int dtmf);
|
||||
void
|
||||
ecCalibrationResult(const std::shared_ptr<linphone::Core> &core, linphone::EcCalibratorStatus status, int delayMs);
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ list(APPEND _LINPHONEAPP_SOURCES
|
|||
tool/thread/SafeSharedPointer.hpp
|
||||
tool/thread/SafeConnection.cpp
|
||||
tool/thread/Thread.cpp
|
||||
tool/providers/AvatarProvider.cpp
|
||||
tool/providers/ImageProvider.cpp
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -22,8 +22,11 @@
|
|||
|
||||
#include "core/App.hpp"
|
||||
#include "core/call/CallGui.hpp"
|
||||
#include "core/path/Paths.hpp"
|
||||
#include "model/object/VariantObject.hpp"
|
||||
#include "model/tool/ToolModel.hpp"
|
||||
#include "tool/providers/AvatarProvider.hpp"
|
||||
#include <QImageReader>
|
||||
|
||||
// =============================================================================
|
||||
|
||||
|
|
@ -78,3 +81,36 @@ VariantObject *Utils::haveAccount() {
|
|||
result->requestValue();
|
||||
return result;
|
||||
}
|
||||
QString Utils::createAvatar(const QUrl &fileUrl) {
|
||||
QString filePath = fileUrl.toLocalFile();
|
||||
QString fileId; // uuid.ext
|
||||
QString fileUri; // image://avatar/filename.ext
|
||||
QFile file;
|
||||
if (!filePath.isEmpty()) {
|
||||
if (filePath.startsWith("image:")) { // No need to copy
|
||||
fileUri = filePath;
|
||||
} else {
|
||||
file.setFileName(filePath);
|
||||
if (!file.exists()) {
|
||||
qWarning() << "[Utils] Avatar not found at " << filePath;
|
||||
return "";
|
||||
}
|
||||
if (QImageReader::imageFormat(filePath).size() == 0) {
|
||||
qWarning() << "[Utils] Avatar extension not supported by QImageReader for " << filePath;
|
||||
return "";
|
||||
}
|
||||
QFileInfo info(file);
|
||||
QString uuid = QUuid::createUuid().toString();
|
||||
fileId = QStringLiteral("%1.%2")
|
||||
.arg(uuid.mid(1, uuid.length() - 2)) // Remove `{}`.
|
||||
.arg(info.suffix());
|
||||
fileUri = QStringLiteral("image://%1/%2").arg(AvatarProvider::ProviderId).arg(fileId);
|
||||
QString dest = Paths::getAvatarsDirPath() + fileId;
|
||||
if (!file.copy(dest)) {
|
||||
qWarning() << "[Utils] Avatar couldn't be created to " << dest;
|
||||
return "";
|
||||
}
|
||||
}
|
||||
}
|
||||
return fileUri;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -53,6 +53,7 @@ public:
|
|||
const QString &prepareTransfertAddress = "",
|
||||
const QHash<QString, QString> &headers = {});
|
||||
Q_INVOKABLE static VariantObject *haveAccount();
|
||||
Q_INVOKABLE static QString createAvatar(const QUrl &fileUrl); // Return the avatar path
|
||||
|
||||
static inline QString coreStringToAppString(const std::string &str) {
|
||||
if (Constants::LinphoneLocaleEncoding == QString("UTF-8")) return QString::fromStdString(str);
|
||||
|
|
|
|||
|
|
@ -19,8 +19,6 @@
|
|||
*/
|
||||
|
||||
#include "core/path/Paths.hpp"
|
||||
#include "core/utils/Constants.hpp"
|
||||
// #include "tool/Utils.hpp"
|
||||
|
||||
#include "AvatarProvider.hpp"
|
||||
|
||||
|
|
@ -30,9 +28,7 @@ const QString AvatarProvider::ProviderId = "avatar";
|
|||
|
||||
AvatarProvider::AvatarProvider()
|
||||
: QQuickImageProvider(QQmlImageProviderBase::Image, QQmlImageProviderBase::ForceAsynchronousImageLoading) {
|
||||
const auto &str = Paths::getAvatarsDirPath().toStdString();
|
||||
if (Constants::LinphoneLocaleEncoding == QString("UTF-8")) mAvatarsPath = QString::fromStdString(str);
|
||||
else mAvatarsPath = QString::fromLocal8Bit(str.c_str(), int(str.size()));
|
||||
mAvatarsPath = Paths::getAvatarsDirPath();
|
||||
}
|
||||
|
||||
QImage AvatarProvider::requestImage(const QString &id, QSize *size, const QSize &) {
|
||||
|
|
|
|||
|
|
@ -2,11 +2,14 @@
|
|||
* Qml template used for welcome and login/register pages
|
||||
**/
|
||||
|
||||
import QtCore
|
||||
import QtQuick 2.15
|
||||
import QtQuick.Layouts 1.3
|
||||
import QtQuick.Controls 2.2 as Control
|
||||
import QtQuick.Dialogs
|
||||
|
||||
import Linphone
|
||||
import UtilsCpp
|
||||
|
||||
Item {
|
||||
id: mainItem
|
||||
|
|
@ -36,18 +39,35 @@ Item {
|
|||
placeholderText: qsTr("Rechercher un contact, appeler ou envoyer un message...")
|
||||
}
|
||||
Control.Button {
|
||||
id: avatarButton
|
||||
AccountProxy{
|
||||
id: accountProxy
|
||||
property bool haveAvatar: defaultAccount && defaultAccount.core.pictureUri || false
|
||||
}
|
||||
|
||||
Layout.preferredWidth: 30
|
||||
Layout.preferredHeight: 30
|
||||
background: Item {
|
||||
visible: false
|
||||
}
|
||||
contentItem: Image {
|
||||
//avatar
|
||||
source: AppIcons.welcomeLinphoneLogo
|
||||
// width: 30
|
||||
// height: 30
|
||||
id: avatar
|
||||
source: accountProxy.haveAvatar ? accountProxy.defaultAccount.core.pictureUri : AppIcons.welcomeLinphoneLogo
|
||||
fillMode: Image.PreserveAspectFit
|
||||
}
|
||||
onClicked: {
|
||||
fileDialog.open()
|
||||
}
|
||||
FileDialog {
|
||||
id: fileDialog
|
||||
currentFolder: StandardPaths.standardLocations(StandardPaths.PicturesLocation)[0]
|
||||
onAccepted: {
|
||||
var avatarPath = UtilsCpp.createAvatar( selectedFile )
|
||||
if(avatarPath){
|
||||
accountProxy.defaultAccount.core.pictureUri = avatarPath
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Control.Button {
|
||||
enabled: false
|
||||
|
|
|
|||
2
external/linphone-sdk
vendored
2
external/linphone-sdk
vendored
|
|
@ -1 +1 @@
|
|||
Subproject commit 477d4b7d56e256ce6581e6c31d12d3c7f49f9f9a
|
||||
Subproject commit bf9106ee57b8a32aea2693f0fc69c2397a66d570
|
||||
Loading…
Reference in a new issue