diff --git a/src/accountadapter.cpp b/src/accountadapter.cpp index ebf0de238b17f4fa3e33be40edf117ccf9f8ce06..7ac854291a7d14e83489523e5a32c1dcfd2f519b 100644 --- a/src/accountadapter.cpp +++ b/src/accountadapter.cpp @@ -25,6 +25,8 @@ #include "qtutils.h" +#include <QtConcurrent/QtConcurrent> + #undef REGISTERED #include "../daemon/src/dring/account_const.h" @@ -89,13 +91,12 @@ AccountAdapter::connectFailure() void AccountAdapter::createJamiAccount(QString registeredName, const QVariantMap& settings, - QString photoBoothImgBase64, bool isCreating) { Utils::oneShotConnect( &LRCInstance::accountModel(), &lrc::api::NewAccountModel::accountAdded, - [this, registeredName, settings, isCreating, photoBoothImgBase64](const QString& accountId) { + [this, registeredName, settings, isCreating](const QString& accountId) { auto showBackup = isCreating && !AppSettingsManager::getValue(Settings::Key::NeverShowMeAgain) .toBool(); @@ -124,16 +125,6 @@ AccountAdapter::createJamiAccount(QString registeredName, showBackup, LRCInstance::accountModel().getAccountList().indexOf(accountId)); } - - // set up avatar pixmap from photobooth - QImage avatarImg; - const bool ret = avatarImg.loadFromData( - QByteArray::fromBase64(photoBoothImgBase64.toLatin1())); - if (!ret) { - qDebug() << "No image provided for JAMI account creation"; - } else { - LRCInstance::setAvatarForAccount(QPixmap::fromImage(avatarImg), accountId); - } }); connectFailure(); @@ -156,11 +147,11 @@ AccountAdapter::createJamiAccount(QString registeredName, } void -AccountAdapter::createSIPAccount(const QVariantMap& settings, QString photoBoothImgBase64) +AccountAdapter::createSIPAccount(const QVariantMap& settings) { Utils::oneShotConnect(&LRCInstance::accountModel(), &lrc::api::NewAccountModel::accountAdded, - [this, settings, photoBoothImgBase64](const QString& accountId) { + [this, settings](const QString& accountId) { auto confProps = LRCInstance::accountModel().getAccountConfig( accountId); // set SIP details @@ -170,17 +161,6 @@ AccountAdapter::createSIPAccount(const QVariantMap& settings, QString photoBooth confProps.routeset = settings["proxy"].toString(); LRCInstance::accountModel().setAccountConfig(accountId, confProps); - // set up photobooth avatar to SIP avatar - QImage avatarImg; - const bool ret = avatarImg.loadFromData( - QByteArray::fromBase64(photoBoothImgBase64.toLatin1())); - if (!ret) { - qDebug() << "SIP account creation BASE64 image loading failed"; - } else { - LRCInstance::setAvatarForAccount(QPixmap::fromImage(avatarImg), - accountId); - } - emit LRCInstance::instance().accountListChanged(); emit accountAdded(accountId, false, @@ -294,6 +274,22 @@ AccountAdapter::getCurrentAccountType() return LRCInstance::getCurrentAccountInfo().profileInfo.type; } +void +AccountAdapter::setCurrAccAvatar(bool fromFile, const QString& source) +{ + QtConcurrent::run([fromFile, source]() { + QPixmap image; + bool success; + if (fromFile) + success = image.load(source); + else + success = image.loadFromData(Utils::base64StringToByteArray(source)); + + if (success) + LRCInstance::setCurrAccAvatar(image); + }); +} + void AccountAdapter::onCurrentAccountChanged() { diff --git a/src/accountadapter.h b/src/accountadapter.h index 4405e345e3e3e4f142b7bd7552b606cd1ee0ca19..cdcc7561709e3bfdf7b1b61ccb1799c24550636b 100644 --- a/src/accountadapter.h +++ b/src/accountadapter.h @@ -66,9 +66,8 @@ public: */ Q_INVOKABLE void createJamiAccount(QString registeredName, const QVariantMap& settings, - QString photoBoothImgBase64, bool isCreating); - Q_INVOKABLE void createSIPAccount(const QVariantMap& settings, QString photoBoothImgBase64); + Q_INVOKABLE void createSIPAccount(const QVariantMap& settings); Q_INVOKABLE void createJAMSAccount(const QVariantMap& settings); /* * Delete current account @@ -102,6 +101,8 @@ public: Q_INVOKABLE void setSelectedConvId(const QString& convId = {}); Q_INVOKABLE lrc::api::profile::Type getCurrentAccountType(); + Q_INVOKABLE void setCurrAccAvatar(bool fromFile, const QString& source); + signals: /* * Trigger other components to reconnect account related signals. diff --git a/src/avadapter.cpp b/src/avadapter.cpp index 91cd9e2a4835b78455e2ae46b032dae9be0ebfdf..b1cfb98838668f1ee966910d641b478fc66dffa2 100644 --- a/src/avadapter.cpp +++ b/src/avadapter.cpp @@ -99,7 +99,7 @@ AvAdapter::captureScreen(int screenNumber) QBuffer buffer; buffer.open(QIODevice::WriteOnly); pixmap.save(&buffer, "PNG"); - return QString::fromLatin1(buffer.data().toBase64().data()); + return Utils::byteArrayToBase64String(buffer.data()); } void diff --git a/src/avatarimageprovider.h b/src/avatarimageprovider.h index 336a22bb35ccccce5cb7eaa0e18c907c6cefb59e..29d074f0c22b27aee246ba5133ed5b1eda006565 100644 --- a/src/avatarimageprovider.h +++ b/src/avatarimageprovider.h @@ -38,6 +38,7 @@ public: * 2. file_ + file path * 3. contact_+ contact uri * 4. conversation_+ conversation uid + * 5. base64_ + base64 string */ QImage requestImage(const QString& id, QSize* size, const QSize& requestedSize) override { @@ -65,11 +66,13 @@ public: return Utils::fallbackAvatar(QString(), idContent, requestedSize); } else if (idType == "default") { return Utils::fallbackAvatar(QString(), QString(), requestedSize); + } else if (idType == "base64") { + return Utils::cropImage(QImage::fromData(Utils::base64StringToByteArray(idContent))) + .scaled(requestedSize, Qt::KeepAspectRatio, Qt::SmoothTransformation); } else { - auto image = Utils::cropImage(QImage(idContent)); - return image.scaled(requestedSize, - Qt::KeepAspectRatioByExpanding, - Qt::SmoothTransformation); + QImage image = QImage(idContent); + return Utils::getCirclePhoto(image, image.size().width()) + .scaled(requestedSize, Qt::KeepAspectRatio, Qt::SmoothTransformation); } } }; diff --git a/src/commoncomponents/AvatarImage.qml b/src/commoncomponents/AvatarImage.qml index 61b68a74a01e79163235d6ef9538be4a3db88324..bfd74876939d934de177d978020578bcb5a7cf4b 100644 --- a/src/commoncomponents/AvatarImage.qml +++ b/src/commoncomponents/AvatarImage.qml @@ -24,19 +24,19 @@ import net.jami.Constants 1.0 Item { id: root - // FromUrl here is for grabToImage image url enum Mode { FromAccount = 0, FromFile, FromContactUri, FromConvUid, - FromUrl, + FromBase64, FromTemporaryName, Default } property alias fillMode: rootImage.fillMode property alias sourceSize: rootImage.sourceSize + property bool saveToConfig: false property int mode: AvatarImage.Mode.FromAccount property string imageProviderIdPrefix: { switch(mode) { @@ -50,6 +50,8 @@ Item { return "conversation_" case AvatarImage.Mode.FromTemporaryName: return "fallback_" + case AvatarImage.Mode.FromBase64: + return "base64_" case AvatarImage.Mode.Default: return "default_" default: @@ -69,6 +71,19 @@ Item { signal imageIsReady + function saveAvatarToConfig() { + switch(mode) { + case AvatarImage.Mode.FromFile: + AccountAdapter.setCurrAccAvatar(true, imageId) + break + case AvatarImage.Mode.FromBase64: + AccountAdapter.setCurrAccAvatar(false, imageId) + break + default: + return + } + } + function updateImage(updatedId, oneTimeForceUpdateUrl) { imageId = updatedId if (oneTimeForceUpdateUrl === undefined) @@ -76,10 +91,10 @@ Item { else forceUpdateUrl = oneTimeForceUpdateUrl - if (mode === AvatarImage.Mode.FromUrl) - rootImage.source = imageId - else - rootImage.source = imageProviderUrl + imageId + rootImage.source = imageProviderUrl + imageId + + if (saveToConfig) + saveAvatarToConfig() } Image { @@ -192,5 +207,4 @@ Item { radius: 30 color: JamiTheme.notificationRed } - } diff --git a/src/commoncomponents/PhotoboothView.qml b/src/commoncomponents/PhotoboothView.qml index f88ff3ea425b0e33d5db0c1e7ab4fe5ca1b1bb89..78b3d9c1fcf8eca80e7a950b7829cd589e0f71f5 100644 --- a/src/commoncomponents/PhotoboothView.qml +++ b/src/commoncomponents/PhotoboothView.qml @@ -12,9 +12,8 @@ ColumnLayout { property int photoState: PhotoboothView.PhotoState.Default property bool avatarSet: false // saveToConfig is to specify whether the image should be saved to account config - property bool saveToConfig: false + property alias saveToConfig: avatarImg.saveToConfig property string fileName: "" - property var boothImg: "" property int boothWidth: 224 @@ -50,7 +49,7 @@ ColumnLayout { function setAvatarImage(mode = AvatarImage.Mode.FromAccount, imageId = AccountAdapter.currentAccountId){ - if (mode !== AvatarImage.Mode.FromUrl) + if (mode !== AvatarImage.Mode.FromBase64) avatarImg.enableAnimation = true else avatarImg.enableAnimation = false @@ -58,7 +57,6 @@ ColumnLayout { avatarImg.mode = mode if (mode === AvatarImage.Mode.Default) { - boothImg = "" avatarImg.updateImage(imageId) return } @@ -67,6 +65,10 @@ ColumnLayout { avatarImg.updateImage(imageId) } + function manualSaveToConfig() { + avatarImg.saveAvatarToConfig() + } + onVisibleChanged: { if(!visible){ stopBooth() @@ -141,21 +143,12 @@ ColumnLayout { } onImageIsReady: { - if (mode === AvatarImage.Mode.FromUrl) + if (mode === AvatarImage.Mode.FromBase64) photoState = PhotoboothView.PhotoState.Taken if (photoState === PhotoboothView.PhotoState.Taken) { avatarImg.state = "" avatarImg.state = "flashIn" - } else { - // Once image is loaded (updated), save to boothImg (choose from file) - avatarImg.grabToImage(function(result) { - if (mode !== AvatarImage.Mode.Default) - boothImg = result.image - - if (saveToConfig) - SettingsAdapter.setCurrAccAvatar(result.image) - }) } } @@ -255,17 +248,11 @@ ColumnLayout { startBooth() return } else { - previewWidget.grabToImage(function(result) { - boothImg = result.image - - if (saveToConfig) - SettingsAdapter.setCurrAccAvatar(result.image) - - setAvatarImage(AvatarImage.Mode.FromUrl, result.url) + setAvatarImage(AvatarImage.Mode.FromBase64, + previewWidget.takePhoto(boothWidth)) - avatarSet = true - stopBooth() - }) + avatarSet = true + stopBooth() } } } diff --git a/src/messagesadapter.cpp b/src/messagesadapter.cpp index da4186f99748ff87264862c877e02440b6572d9d..819285bd3ce0560ad9d2d864c84d2e1e315917ab 100644 --- a/src/messagesadapter.cpp +++ b/src/messagesadapter.cpp @@ -396,7 +396,7 @@ MessagesAdapter::pasteKeyDetected() QBuffer bu(&ba); bu.open(QIODevice::WriteOnly); pixmap.save(&bu, "PNG"); - auto str = QString::fromLatin1(ba.toBase64().data()); + auto str = Utils::byteArrayToBase64String(ba); setMessagesImageContent(str, true); } else if (mimeData->hasUrls()) { diff --git a/src/previewrenderer.cpp b/src/previewrenderer.cpp index 92cd0f0cb46576f66cae971b795a6419fa6c64c3..e966a20eeea4578f87874a84741218360f27222d 100644 --- a/src/previewrenderer.cpp +++ b/src/previewrenderer.cpp @@ -112,6 +112,16 @@ PhotoboothPreviewRender::PhotoboothPreviewRender(QQuickItem* parent) PhotoboothPreviewRender::~PhotoboothPreviewRender() {} +QString +PhotoboothPreviewRender::takePhoto(int size) +{ + if (auto previewImage = LRCInstance::renderer()->getPreviewFrame()) { + return Utils::byteArrayToBase64String(Utils::QImageToByteArray( + previewImage->copy() + .scaled(size, size, Qt::KeepAspectRatioByExpanding, Qt::SmoothTransformation))); + } +} + void PhotoboothPreviewRender::paint(QPainter* painter) { diff --git a/src/previewrenderer.h b/src/previewrenderer.h index 55249b1248a8498bb6326f9f6ca20a7b60d47030..715121c3b1e308c7b95981241c9327c70993f2ea 100644 --- a/src/previewrenderer.h +++ b/src/previewrenderer.h @@ -63,6 +63,8 @@ public: explicit PhotoboothPreviewRender(QQuickItem* parent = 0); virtual ~PhotoboothPreviewRender(); + Q_INVOKABLE QString takePhoto(int size); + signals: void hideBooth(); diff --git a/src/settingsadapter.cpp b/src/settingsadapter.cpp index e81c45f3684d8a91c186330897f5432a6e9f439d..a4022bac481de333d3f778a74378e02fd983ed67 100644 --- a/src/settingsadapter.cpp +++ b/src/settingsadapter.cpp @@ -263,26 +263,6 @@ SettingsAdapter::getAccountBestName() return LRCInstance::accountModel().bestNameForAccount(LRCInstance::getCurrAccId()); } -bool -SettingsAdapter::getIsDefaultAvatar() -{ - auto& accountInfo = LRCInstance::getCurrentAccountInfo(); - - return accountInfo.profileInfo.avatar.isEmpty(); -} - -void -SettingsAdapter::setCurrAccAvatar(QVariant avatarImg) -{ - LRCInstance::setCurrAccAvatar(QPixmap::fromImage(avatarImg.value<QImage>())); -} - -void -SettingsAdapter::clearCurrentAvatar() -{ - LRCInstance::setCurrAccAvatar(QPixmap()); -} - lrc::api::account::ConfProperties_t SettingsAdapter::getAccountConfig() { diff --git a/src/settingsadapter.h b/src/settingsadapter.h index 405b7385407293b0f203bb454c8a3369fcd69347..53e1f1569b5419905edbd3ce1cc05ae6d7042192 100644 --- a/src/settingsadapter.h +++ b/src/settingsadapter.h @@ -93,11 +93,6 @@ public: Q_INVOKABLE int getCurrentAccount_Profile_Info_Type(); Q_INVOKABLE QString getAccountBestName(); - // getters and setters of avatar image - Q_INVOKABLE bool getIsDefaultAvatar(); - Q_INVOKABLE void setCurrAccAvatar(QVariant avatarImg); - Q_INVOKABLE void clearCurrentAvatar(); - /* * getters and setters of ConfProperties_t */ diff --git a/src/utils.cpp b/src/utils.cpp index b38484f7ea951b661749b4afd5ca6377191a1d72..327cdc679302d4e3206bf42acccb84ef8e0f02e2 100644 --- a/src/utils.cpp +++ b/src/utils.cpp @@ -265,7 +265,7 @@ Utils::contactPhoto(const QString& contactUri, const QSize& size) && contactInfo.profileInfo.uri.isEmpty()) { photo = Utils::fallbackAvatar(QString(), QString()); } else if (!contactPhoto.isEmpty()) { - QByteArray byteArray = contactPhoto.toLocal8Bit(); + QByteArray byteArray = Utils::base64StringToByteArray(contactPhoto); photo = contactPhotoFromBase64(byteArray, nullptr); if (photo.isNull()) { auto avatarName = contactInfo.profileInfo.uri == bestName ? QString() : bestName; @@ -284,7 +284,7 @@ QImage Utils::contactPhotoFromBase64(const QByteArray& data, const QString& type) { QImage avatar; - const bool ret = avatar.loadFromData(QByteArray::fromBase64(data), type.toLatin1()); + const bool ret = avatar.loadFromData(data, type.toLatin1()); if (!ret) { qDebug() << "Utils: vCard image loading failed"; return QImage(); @@ -587,6 +587,18 @@ Utils::QImageToByteArray(QImage image) return ba; } +QString +Utils::byteArrayToBase64String(QByteArray byteArray) +{ + return QString::fromLatin1(byteArray.toBase64().data()); +} + +QByteArray +Utils::base64StringToByteArray(QString base64) +{ + return QByteArray::fromBase64(base64.toLatin1()); +} + QImage Utils::cropImage(const QImage& img) { @@ -718,7 +730,7 @@ Utils::accountPhoto(const lrc::api::account::Info& accountInfo, const QSize& siz { QImage photo; if (!accountInfo.profileInfo.avatar.isEmpty()) { - QByteArray ba = accountInfo.profileInfo.avatar.toLocal8Bit(); + QByteArray ba = Utils::base64StringToByteArray(accountInfo.profileInfo.avatar); photo = contactPhotoFromBase64(ba, nullptr); } else { auto bestId = LRCInstance::accountModel().bestIdForAccount(accountInfo.id); diff --git a/src/utils.h b/src/utils.h index 2a7781fb3e16a9610ddb46ddf74f8cc52bdfaa69..f21958ea3857c25a5a1d27b6795d52459d9fc99f 100644 --- a/src/utils.h +++ b/src/utils.h @@ -101,6 +101,8 @@ QImage fallbackAvatar(const std::string& alias, const std::string& uri, const QSize& size = defaultAvatarSize); QByteArray QImageToByteArray(QImage image); +QString byteArrayToBase64String(QByteArray byteArray); +QByteArray base64StringToByteArray(QString base64); QByteArray QByteArrayFromFile(const QString& filename); QPixmap generateTintedPixmap(const QString& filename, QColor color); QPixmap generateTintedPixmap(const QPixmap& pix, QColor color); diff --git a/src/wizardview/WizardView.qml b/src/wizardview/WizardView.qml index 47c9947372aec5aa0fbfa1a1c6102527beb571b4..b3d80c587dd026a5fb04a2d6b9bdf9d50ef150f2 100644 --- a/src/wizardview/WizardView.qml +++ b/src/wizardview/WizardView.qml @@ -238,7 +238,6 @@ Rectangle { AccountAdapter.createJamiAccount( createAccountPage.text_usernameEditAlias, inputParaObject, - createAccountPage.boothImgBase64, true) showBackUp = !isRdv showBottom = true @@ -391,8 +390,7 @@ Rectangle { } onSaveProfile: { - if (profilePage.profileImg) - SettingsAdapter.setCurrAccAvatar(profilePage.profileImg) + avatarBooth.manualSaveToConfig() AccountAdapter.setCurrAccDisplayName(profilePage.displayName) leave() } diff --git a/src/wizardview/components/ProfilePage.qml b/src/wizardview/components/ProfilePage.qml index 91e0acbe2cc8ebd1133473d3f11ad21d1bded535..cd7e72f2d089bd6e873acdd8c077627e61c0ec4b 100644 --- a/src/wizardview/components/ProfilePage.qml +++ b/src/wizardview/components/ProfilePage.qml @@ -27,11 +27,11 @@ Rectangle { id: root property string createdAccountId: "" - property alias profileImg: setAvatarWidget.boothImg property int preferredHeight: profilePageColumnLayout.implicitHeight property var showBottom: false property alias displayName: aliasEdit.text property bool isRdv: false + property alias avatarBooth: setAvatarWidget signal leavePage signal saveProfile