From 519e85158e68a0b19127802704e6a870f0eb71db Mon Sep 17 00:00:00 2001 From: Ronan Abhamon Date: Thu, 8 Jun 2017 16:04:02 +0200 Subject: [PATCH] feat(app): download directly file chat message (without dialogs) --- linphone-desktop/assets/languages/en.ts | 11 +++--- linphone-desktop/assets/languages/fr.ts | 11 +++--- linphone-desktop/src/app/paths/Paths.cpp | 7 +++- linphone-desktop/src/app/paths/Paths.hpp | 1 + .../src/components/chat/ChatModel.cpp | 36 +++++++++++-------- .../src/components/chat/ChatModel.hpp | 2 +- .../src/components/chat/ChatProxyModel.cpp | 16 ++++----- .../src/components/chat/ChatProxyModel.hpp | 2 +- .../src/components/settings/SettingsModel.cpp | 23 ++++++++++-- .../src/components/settings/SettingsModel.hpp | 5 +++ linphone-desktop/src/utils/Utils.cpp | 31 ++++++++++++++++ linphone-desktop/src/utils/Utils.hpp | 4 +++ .../ui/modules/Linphone/Chat/FileMessage.qml | 13 +------ .../ui/views/App/Settings/SettingsUi.qml | 15 ++++++++ 14 files changed, 120 insertions(+), 57 deletions(-) diff --git a/linphone-desktop/assets/languages/en.ts b/linphone-desktop/assets/languages/en.ts index b46ff46f2..cdccd93cd 100644 --- a/linphone-desktop/assets/languages/en.ts +++ b/linphone-desktop/assets/languages/en.ts @@ -760,13 +760,6 @@ Server url not configured. It's necessary to restart the application. Do you want to restart now? - - FileMessage - - downloadFileTitle - Download file - - Home @@ -1292,6 +1285,10 @@ your friend's SIP address or username. cleanAvatarsDescription Are you sure you want to clean all avatars? + + downloadLabel + Download folder + SettingsVideo diff --git a/linphone-desktop/assets/languages/fr.ts b/linphone-desktop/assets/languages/fr.ts index f2818ba89..db00856e8 100644 --- a/linphone-desktop/assets/languages/fr.ts +++ b/linphone-desktop/assets/languages/fr.ts @@ -760,13 +760,6 @@ Url du serveur non configurée. Voulez-vous redémarrer maintenant pour prendre en compte ces modifications ? - - FileMessage - - downloadFileTitle - Télécharger le fichier - - Home @@ -1291,6 +1284,10 @@ un chat ou ajouter un contact. cleanAvatarsDescription Voulez-vous vraiment supprimer tous les avatars ? + + downloadLabel + Dossier des téléchargements + SettingsVideo diff --git a/linphone-desktop/src/app/paths/Paths.cpp b/linphone-desktop/src/app/paths/Paths.cpp index ba8968c8e..bcfd55937 100644 --- a/linphone-desktop/src/app/paths/Paths.cpp +++ b/linphone-desktop/src/app/paths/Paths.cpp @@ -28,6 +28,7 @@ #include #include "../../utils/Utils.hpp" + #include "config.h" #include "Paths.hpp" @@ -167,7 +168,7 @@ string Paths::getAvatarsDirPath () { } string Paths::getCallHistoryFilePath () { - return getWritableFilePath(getAppCallHistoryFilePath()); + return getWritableFilePath(QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation) + PATH_AVATARS); } string Paths::getCapturesDirPath () { @@ -189,6 +190,10 @@ string Paths::getFriendsListFilePath () { return getWritableFilePath(getAppFriendsFilePath()); } +std::string Paths::getDownloadDirPath () { + return getWritableDirPath(QStandardPaths::writableLocation(QStandardPaths::DownloadLocation)); +} + string Paths::getLogsDirPath () { return getWritableDirPath(QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation) + PATH_LOGS); } diff --git a/linphone-desktop/src/app/paths/Paths.hpp b/linphone-desktop/src/app/paths/Paths.hpp index e6bbda9a6..b96f30bcc 100644 --- a/linphone-desktop/src/app/paths/Paths.hpp +++ b/linphone-desktop/src/app/paths/Paths.hpp @@ -39,6 +39,7 @@ namespace Paths { std::string getConfigFilePath (const QString &configPath = QString()); std::string getFactoryConfigFilePath (); std::string getFriendsListFilePath (); + std::string getDownloadDirPath (); std::string getLogsDirPath (); std::string getMessageHistoryFilePath (); std::string getPackageDataDirPath (); diff --git a/linphone-desktop/src/components/chat/ChatModel.cpp b/linphone-desktop/src/components/chat/ChatModel.cpp index ed53c13f0..30e95fce9 100644 --- a/linphone-desktop/src/components/chat/ChatModel.cpp +++ b/linphone-desktop/src/components/chat/ChatModel.cpp @@ -106,11 +106,9 @@ public: private: QList::iterator findMessageEntry (const shared_ptr &message) { - return find_if( - mChatModel->mEntries.begin(), mChatModel->mEntries.end(), [&message](const ChatEntryData &pair) { + return find_if(mChatModel->mEntries.begin(), mChatModel->mEntries.end(), [&message](const ChatEntryData &pair) { return pair.second == message; - } - ); + }); } void signalDataChanged (const QList::iterator &it) { @@ -390,7 +388,7 @@ void ChatModel::sendFileMessage (const QString &path) { emit messageSent(message); } -void ChatModel::downloadFile (int id, const QString &downloadPath) { +void ChatModel::downloadFile (int id) { if (!mChatRoom) return; @@ -423,7 +421,20 @@ void ChatModel::downloadFile (int id, const QString &downloadPath) { return; } - message->setFileTransferFilepath(::Utils::appStringToCoreString(downloadPath)); + bool soFarSoGood; + const QString &safeFilePath = ::Utils::getSafeFilePath( + QStringLiteral("%1%2") + .arg(CoreManager::getInstance()->getSettingsModel()->getDownloadFolder()) + .arg(entry.first["fileName"].toString()), + &soFarSoGood + ); + + if (!soFarSoGood) { + qWarning() << QStringLiteral("Unable to create safe file path for: %1.").arg(id); + return; + } + + message->setFileTransferFilepath(::Utils::appStringToCoreString(safeFilePath)); message->setListener(mMessageHandlers); if (message->downloadFile() < 0) @@ -486,16 +497,14 @@ void ChatModel::removeEntry (ChatEntryData &pair) { // We are between `beginRemoveRows` and `endRemoveRows`. // A solution is to schedule a `removeEntry` call in the Qt main loop. shared_ptr linphonePtr = pair.second; - QTimer::singleShot( - 0, this, [this, linphonePtr]() { + QTimer::singleShot(0, this, [this, linphonePtr]() { auto it = find_if(mEntries.begin(), mEntries.end(), [linphonePtr](const ChatEntryData &pair) { return pair.second == linphonePtr; }); if (it != mEntries.end()) removeEntry(static_cast(distance(mEntries.begin(), it))); - } - ); + }); } CoreManager::getInstance()->getCore()->removeCallLog(static_pointer_cast(pair.second)); @@ -525,12 +534,9 @@ void ChatModel::insertCall (const shared_ptr &callLog) { const ChatEntryData &pair, const QList::iterator *start = NULL ) { - auto it = lower_bound( - start ? *start : mEntries.begin(), mEntries.end(), pair, - [](const ChatEntryData &a, const ChatEntryData &b) { + auto it = lower_bound(start ? *start : mEntries.begin(), mEntries.end(), pair, [](const ChatEntryData &a, const ChatEntryData &b) { return a.first["timestamp"] < b.first["timestamp"]; - } - ); + }); int row = static_cast(distance(mEntries.begin(), it)); diff --git a/linphone-desktop/src/components/chat/ChatModel.hpp b/linphone-desktop/src/components/chat/ChatModel.hpp index 3a6f0f998..23d46aa10 100644 --- a/linphone-desktop/src/components/chat/ChatModel.hpp +++ b/linphone-desktop/src/components/chat/ChatModel.hpp @@ -97,7 +97,7 @@ public: void sendFileMessage (const QString &path); - void downloadFile (int id, const QString &downloadPath); + void downloadFile (int id); signals: void sipAddressChanged (const QString &sipAddress); diff --git a/linphone-desktop/src/components/chat/ChatProxyModel.cpp b/linphone-desktop/src/components/chat/ChatProxyModel.cpp index 7f519a8ce..30bcd32e4 100644 --- a/linphone-desktop/src/components/chat/ChatProxyModel.cpp +++ b/linphone-desktop/src/components/chat/ChatProxyModel.cpp @@ -69,17 +69,13 @@ ChatProxyModel::ChatProxyModel (QObject *parent) : QSortFilterProxyModel(parent) ChatModel *chat = static_cast(mChatModelFilter->sourceModel()); - QObject::connect( - chat, &ChatModel::messageReceived, this, [this](const shared_ptr &) { + QObject::connect(chat, &ChatModel::messageReceived, this, [this](const shared_ptr &) { mMaxDisplayedEntries++; - } - ); + }); - QObject::connect( - chat, &ChatModel::messageSent, this, [this](const shared_ptr &) { + QObject::connect(chat, &ChatModel::messageSent, this, [this](const shared_ptr &) { mMaxDisplayedEntries++; - } - ); + }); } void ChatProxyModel::loadMoreEntries () { @@ -133,10 +129,10 @@ void ChatProxyModel::sendFileMessage (const QString &path) { static_cast(mChatModelFilter->sourceModel())->sendFileMessage(path); } -void ChatProxyModel::downloadFile (int id, const QString &downloadPath) { +void ChatProxyModel::downloadFile (int id) { QModelIndex sourceIndex = mapToSource(index(id, 0)); static_cast(mChatModelFilter->sourceModel())->downloadFile( - mChatModelFilter->mapToSource(sourceIndex).row(), downloadPath + mChatModelFilter->mapToSource(sourceIndex).row() ); } diff --git a/linphone-desktop/src/components/chat/ChatProxyModel.hpp b/linphone-desktop/src/components/chat/ChatProxyModel.hpp index a4e0cf3be..fd58c69df 100644 --- a/linphone-desktop/src/components/chat/ChatProxyModel.hpp +++ b/linphone-desktop/src/components/chat/ChatProxyModel.hpp @@ -50,7 +50,7 @@ public: Q_INVOKABLE void sendFileMessage (const QString &path); - Q_INVOKABLE void downloadFile (int id, const QString &downloadPath); + Q_INVOKABLE void downloadFile (int id); signals: void sipAddressChanged (const QString &sipAddress); diff --git a/linphone-desktop/src/components/settings/SettingsModel.cpp b/linphone-desktop/src/components/settings/SettingsModel.cpp index 2e2c39615..7bd52647a 100644 --- a/linphone-desktop/src/components/settings/SettingsModel.cpp +++ b/linphone-desktop/src/components/settings/SettingsModel.cpp @@ -640,10 +640,27 @@ QString SettingsModel::getSavedVideosFolder () const { } void SettingsModel::setSavedVideosFolder (const QString &folder) { - QString _folder = QDir::cleanPath(folder) + QDir::separator(); + QString cleanedFolder = QDir::cleanPath(folder) + QDir::separator(); - mConfig->setString(UI_SECTION, "saved_videos_folder", ::Utils::appStringToCoreString(_folder)); - emit savedVideosFolderChanged(_folder); + mConfig->setString(UI_SECTION, "saved_videos_folder", ::Utils::appStringToCoreString(cleanedFolder)); + emit savedVideosFolderChanged(cleanedFolder); +} + +// ----------------------------------------------------------------------------- + +QString SettingsModel::getDownloadFolder () const { + return QDir::cleanPath( + ::Utils::coreStringToAppString( + mConfig->getString(UI_SECTION, "download_folder", Paths::getDownloadDirPath()) + ) + ) + QDir::separator(); +} + +void SettingsModel::setDownloadFolder (const QString &folder) { + QString cleanedFolder = QDir::cleanPath(folder) + QDir::separator(); + + mConfig->setString(UI_SECTION, "download_folder", ::Utils::appStringToCoreString(cleanedFolder)); + emit downloadFolderChanged(cleanedFolder); } // ----------------------------------------------------------------------------- diff --git a/linphone-desktop/src/components/settings/SettingsModel.hpp b/linphone-desktop/src/components/settings/SettingsModel.hpp index 3b11d2340..fd57f6709 100644 --- a/linphone-desktop/src/components/settings/SettingsModel.hpp +++ b/linphone-desktop/src/components/settings/SettingsModel.hpp @@ -115,6 +115,7 @@ class SettingsModel : public QObject { Q_PROPERTY(QString savedScreenshotsFolder READ getSavedScreenshotsFolder WRITE setSavedScreenshotsFolder NOTIFY savedScreenshotsFolderChanged); Q_PROPERTY(QString savedVideosFolder READ getSavedVideosFolder WRITE setSavedVideosFolder NOTIFY savedVideosFolderChanged); + Q_PROPERTY(QString downloadFolder READ getDownloadFolder WRITE setDownloadFolder NOTIFY downloadFolderChanged); public: enum MediaEncryption { @@ -264,6 +265,9 @@ public: QString getSavedVideosFolder () const; void setSavedVideosFolder (const QString &folder); + QString getDownloadFolder () const; + void setDownloadFolder (const QString &folder); + QString getRemoteProvisioning () const; void setRemoteProvisioning (const QString &remoteProvisioning); @@ -339,6 +343,7 @@ signals: void savedScreenshotsFolderChanged (const QString &folder); void savedVideosFolderChanged (const QString &folder); + void downloadFolderChanged (const QString &folder); void remoteProvisioningChanged (const QString &remoteProvisioning); void remoteProvisioningNotChanged (const QString &remoteProvisioning); diff --git a/linphone-desktop/src/utils/Utils.cpp b/linphone-desktop/src/utils/Utils.cpp index 24ebe2496..e13747ff8 100644 --- a/linphone-desktop/src/utils/Utils.cpp +++ b/linphone-desktop/src/utils/Utils.cpp @@ -20,6 +20,8 @@ * Author: Ronan Abhamon */ +#include + #include "Utils.hpp" // ============================================================================= @@ -38,3 +40,32 @@ char *Utils::rstrstr (const char *a, const char *b) { return nullptr; } + +// ----------------------------------------------------------------------------- + +#define SAFE_FILE_PATH_LIMIT 100 + +QString Utils::getSafeFilePath (const QString &filePath, bool *soFarSoGood) { + if (soFarSoGood) + *soFarSoGood = true; + + QFileInfo info(filePath); + if (!info.exists()) + return filePath; + + const QString &prefix = QStringLiteral("%1/%2").arg(info.absolutePath()).arg(info.baseName()); + const QString &ext = info.completeSuffix(); + + for (int i = 1; i < SAFE_FILE_PATH_LIMIT; ++i) { + QString safePath = QStringLiteral("%1 (%3).%4").arg(prefix).arg(i).arg(ext); + if (!QFileInfo::exists(safePath)) + return safePath; + } + + if (soFarSoGood) + *soFarSoGood = false; + + return QStringLiteral(""); +} + +#undef SAFE_FILE_PATH_LIMIT diff --git a/linphone-desktop/src/utils/Utils.hpp b/linphone-desktop/src/utils/Utils.hpp index 1ebcd0c71..633a486a8 100644 --- a/linphone-desktop/src/utils/Utils.hpp +++ b/linphone-desktop/src/utils/Utils.hpp @@ -50,6 +50,10 @@ namespace Utils { // Reverse function of strstr. char *rstrstr (const char *a, const char *b); + + // Returns the same path given in parameter if `filePath` exists. + // Otherwise returns a safe path with a unique number before the extension. + QString getSafeFilePath (const QString &filePath, bool *soFarSoGood = nullptr); } #endif // UTILS_H_ diff --git a/linphone-desktop/ui/modules/Linphone/Chat/FileMessage.qml b/linphone-desktop/ui/modules/Linphone/Chat/FileMessage.qml index bc3b56971..7eb4aecac 100644 --- a/linphone-desktop/ui/modules/Linphone/Chat/FileMessage.qml +++ b/linphone-desktop/ui/modules/Linphone/Chat/FileMessage.qml @@ -1,6 +1,5 @@ import QtQuick 2.7 import QtQuick.Controls 2.1 -import QtQuick.Dialogs 1.2 import QtQuick.Layouts 1.3 import Common 1.0 @@ -179,23 +178,13 @@ Row { } MouseArea { - FileDialog { - id: fileDialog - - folder: shortcuts.home - selectExisting: false - title: qsTr('downloadFileTitle') - - onAccepted: proxyModel.downloadFile(index, App.convertUrlToLocalPath(fileUrl)) - } - anchors.fill: parent cursorShape: containsMouse ? Qt.PointingHandCursor : Qt.ArrowCursor hoverEnabled: true - onClicked: fileDialog.open() + onClicked: proxyModel.downloadFile(index) visible: !rectangle.isNotDelivered && !$chatEntry.isOutgoing } } diff --git a/linphone-desktop/ui/views/App/Settings/SettingsUi.qml b/linphone-desktop/ui/views/App/Settings/SettingsUi.qml index b46064464..e1352b7b2 100644 --- a/linphone-desktop/ui/views/App/Settings/SettingsUi.qml +++ b/linphone-desktop/ui/views/App/Settings/SettingsUi.qml @@ -96,7 +96,9 @@ TabContainer { onAccepted: SettingsModel.savedScreenshotsFolder = selectedFile } } + } + FormLine { FormGroup { label: qsTr('savedVideosLabel') @@ -109,6 +111,19 @@ TabContainer { } } + FormLine { + FormGroup { + label: qsTr('downloadLabel') + + FileChooserButton { + selectedFile: SettingsModel.downloadFolder + selectFolder: true + + onAccepted: SettingsModel.downloadFolder = selectedFile + } + } + } + FormEmptyLine {} }