mirror of
https://gitlab.com/comunic/comunicmessages
synced 2024-12-04 19:24:11 +00:00
Can load remote images and cache them locally.
This commit is contained in:
parent
6e7645f17b
commit
0148f7aaa5
@ -31,7 +31,9 @@ SOURCES += \
|
|||||||
helpers/conversationhelper.cpp \
|
helpers/conversationhelper.cpp \
|
||||||
data/conversationmessage.cpp \
|
data/conversationmessage.cpp \
|
||||||
widgets/conversationmessagewidget.cpp \
|
widgets/conversationmessagewidget.cpp \
|
||||||
data/conversationmessageslist.cpp
|
data/conversationmessageslist.cpp \
|
||||||
|
helpers/imageloadhelper.cpp \
|
||||||
|
utils/filesutils.cpp
|
||||||
|
|
||||||
HEADERS += \
|
HEADERS += \
|
||||||
helpers/accounthelper.h \
|
helpers/accounthelper.h \
|
||||||
@ -64,7 +66,10 @@ HEADERS += \
|
|||||||
helpers/conversationhelper.h \
|
helpers/conversationhelper.h \
|
||||||
data/conversationmessage.h \
|
data/conversationmessage.h \
|
||||||
widgets/conversationmessagewidget.h \
|
widgets/conversationmessagewidget.h \
|
||||||
data/conversationmessageslist.h
|
data/conversationmessageslist.h \
|
||||||
|
helpers/imageloadhelper.h \
|
||||||
|
utils/filesutils.h \
|
||||||
|
data/qlabelholder.h
|
||||||
|
|
||||||
FORMS += \
|
FORMS += \
|
||||||
widgets/loginwidget.ui \
|
widgets/loginwidget.ui \
|
||||||
|
5
config.h
5
config.h
@ -35,4 +35,9 @@
|
|||||||
#define CONVERSATION_MESSAGE_MIN_LENGTH 3
|
#define CONVERSATION_MESSAGE_MIN_LENGTH 3
|
||||||
#define CONVERSATION_MESSAGES_REFRESH_INTERVAL 1000
|
#define CONVERSATION_MESSAGES_REFRESH_INTERVAL 1000
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Images load manager information
|
||||||
|
*/
|
||||||
|
#define REMOTE_IMAGES_CACHE_DIRECTORY "remote_images"
|
||||||
|
|
||||||
#endif // CONFIG_H
|
#endif // CONFIG_H
|
||||||
|
41
data/qlabelholder.h
Normal file
41
data/qlabelholder.h
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
/**
|
||||||
|
* QLabelHolder
|
||||||
|
*
|
||||||
|
* This file should be referenced by ImageLoadHelper ONLY !!!
|
||||||
|
*
|
||||||
|
* @author Pierre HUBERT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
#include <QLabel>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class is used to avoid memory leak if attempting to
|
||||||
|
* apply downloaded image to a deleted label
|
||||||
|
*/
|
||||||
|
class QLabelHolder : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
QLabelHolder(QLabel *label) {
|
||||||
|
mLabel = label;
|
||||||
|
connect(label, &QLabel::destroyed, this, &QLabelHolder::deleted);
|
||||||
|
}
|
||||||
|
|
||||||
|
QLabel *label(){
|
||||||
|
return mLabel;
|
||||||
|
}
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
|
||||||
|
void deleted(){
|
||||||
|
mLabel = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
//Private fields
|
||||||
|
QLabel *mLabel;
|
||||||
|
};
|
163
helpers/imageloadhelper.cpp
Normal file
163
helpers/imageloadhelper.cpp
Normal file
@ -0,0 +1,163 @@
|
|||||||
|
#include <QStandardPaths>
|
||||||
|
#include <QDir>
|
||||||
|
#include <QFile>
|
||||||
|
#include <QCryptographicHash>
|
||||||
|
#include <QMap>
|
||||||
|
#include <QNetworkAccessManager>
|
||||||
|
#include <QNetworkReply>
|
||||||
|
#include <QImage>
|
||||||
|
#include <QImageReader>
|
||||||
|
#include <QPixmap>
|
||||||
|
#include <QLabel>
|
||||||
|
|
||||||
|
#include "imageloadhelper.h"
|
||||||
|
#include "../config.h"
|
||||||
|
#include "../utils/filesutils.h"
|
||||||
|
|
||||||
|
|
||||||
|
//This header should be called only from this file
|
||||||
|
// (QObject subclass seems not to be declarable in CPP files)
|
||||||
|
#include "../data/qlabelholder.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The list of pending labels for an image
|
||||||
|
*/
|
||||||
|
static QMap<QString, QList<QLabelHolder*>> privateList;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Network Access Manager used to download images
|
||||||
|
*/
|
||||||
|
static QNetworkAccessManager accessManager;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The first and the last instance of this object
|
||||||
|
*/
|
||||||
|
static ImageLoadHelper *mHelper = nullptr;
|
||||||
|
|
||||||
|
ImageLoadHelper::ImageLoadHelper(QObject *parent) : QObject(parent)
|
||||||
|
{
|
||||||
|
//Nothing yet
|
||||||
|
}
|
||||||
|
|
||||||
|
void ImageLoadHelper::Load(QLabel *label, const QString &url)
|
||||||
|
{
|
||||||
|
//Construct object if required
|
||||||
|
if(mHelper == nullptr)
|
||||||
|
mHelper = new ImageLoadHelper();
|
||||||
|
|
||||||
|
//Check if the image has already been downloaded
|
||||||
|
if(IsDownloaded(url)){
|
||||||
|
ApplyImage(label, url);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Check if download is already running
|
||||||
|
if(privateList.contains(url)){
|
||||||
|
QList<QLabelHolder *> swapList = privateList.take(url);
|
||||||
|
swapList.append(new QLabelHolder(label));
|
||||||
|
privateList.insert(url, swapList);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//If we get there, we have to launch the download of the image
|
||||||
|
QList<QLabelHolder *> list;
|
||||||
|
list.append(new QLabelHolder(label));
|
||||||
|
privateList.insert(url, list);
|
||||||
|
|
||||||
|
Download(url);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ImageLoadHelper::NetworkError()
|
||||||
|
{
|
||||||
|
qWarning("Image Load Helper error : Network error while connecting to server!");
|
||||||
|
}
|
||||||
|
|
||||||
|
void ImageLoadHelper::NetworkRequestFinished()
|
||||||
|
{
|
||||||
|
//If the request is a success, we can save the image, else we can not do anything
|
||||||
|
QNetworkReply *reply = qobject_cast<QNetworkReply *>(sender());
|
||||||
|
reply->deleteLater();
|
||||||
|
|
||||||
|
if(reply->error() != QNetworkReply::NoError){
|
||||||
|
qWarning("Can not process image because we encountered an error while downloading it!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Save the image
|
||||||
|
QString url = reply->url().toString();
|
||||||
|
QImageReader reader(reply);
|
||||||
|
QImage image = reader.read();
|
||||||
|
|
||||||
|
//Check if the image could not be decode
|
||||||
|
if(image.isNull()){
|
||||||
|
qWarning("Downloaded image from %s is not valid!", url.toStdString().c_str());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Save the image
|
||||||
|
QString file_path = GetImageStoragePath(url);
|
||||||
|
if(!image.save(file_path, "PNG")){
|
||||||
|
qWarning("Could not save image from %s to %s !", url.toStdString().c_str(), file_path.toStdString().c_str());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Process the list of awaiting labels
|
||||||
|
QPixmap pixmap = QPixmap::fromImage(image);
|
||||||
|
QList<QLabelHolder *> labels = privateList.take(url);
|
||||||
|
for(QLabelHolder *currLabel : labels)
|
||||||
|
if(currLabel->label() != nullptr)
|
||||||
|
ApplyImage(currLabel->label(), pixmap);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ImageLoadHelper::SSLErrors()
|
||||||
|
{
|
||||||
|
qWarning("Image Load Helper error : SSL error while connecting to server!");
|
||||||
|
}
|
||||||
|
|
||||||
|
void ImageLoadHelper::Download(const QString &url)
|
||||||
|
{
|
||||||
|
qWarning("Download image located at URL: %s", url.toStdString().c_str());
|
||||||
|
|
||||||
|
QNetworkRequest request;
|
||||||
|
request.setUrl(QUrl(url));
|
||||||
|
request.setRawHeader("User-Agent", "ComunicMessages Images Downloader 1.0");
|
||||||
|
|
||||||
|
QNetworkReply *reply = accessManager.get(request);
|
||||||
|
connect(reply, &QNetworkReply::finished, mHelper, &ImageLoadHelper::NetworkRequestFinished);
|
||||||
|
connect(reply, SIGNAL(error(QNetworkReply::NetworkError)), mHelper, SLOT(NetworkError()));
|
||||||
|
connect(reply, &QNetworkReply::sslErrors, mHelper, &ImageLoadHelper::SSLErrors);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ImageLoadHelper::ApplyImage(QLabel *label, const QString &url)
|
||||||
|
{
|
||||||
|
ApplyImage(label, QPixmap(GetImageStoragePath(url)));
|
||||||
|
}
|
||||||
|
|
||||||
|
void ImageLoadHelper::ApplyImage(QLabel *label, const QPixmap &pixmap)
|
||||||
|
{
|
||||||
|
label->setPixmap(pixmap);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ImageLoadHelper::IsDownloaded(const QString &url)
|
||||||
|
{
|
||||||
|
return QFile(GetImageStoragePath(url)).exists();
|
||||||
|
}
|
||||||
|
|
||||||
|
QString ImageLoadHelper::GetImageStoragePath(const QString &url)
|
||||||
|
{
|
||||||
|
//Containing directory
|
||||||
|
QString path = QStandardPaths::writableLocation(QStandardPaths::CacheLocation)
|
||||||
|
+ QDir::separator() + REMOTE_IMAGES_CACHE_DIRECTORY;
|
||||||
|
|
||||||
|
//Check the directory exists. If not, create it
|
||||||
|
if(!FilesUtils::CreateDirectoryIfNotExists(path))
|
||||||
|
qFatal("Could not create images cache directory.");
|
||||||
|
|
||||||
|
//Add separator to path
|
||||||
|
path += QDir::separator();
|
||||||
|
|
||||||
|
//File name
|
||||||
|
path += QString(QCryptographicHash::hash(url.toStdString().c_str(), QCryptographicHash::Md5).toHex());
|
||||||
|
|
||||||
|
return path;
|
||||||
|
}
|
91
helpers/imageloadhelper.h
Normal file
91
helpers/imageloadhelper.h
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
/**
|
||||||
|
* ImageLoadHelper - This helper is used to
|
||||||
|
* easily load remote images and caches them
|
||||||
|
*
|
||||||
|
* @author Pierre HUBERT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef IMAGELOADHELPER_H
|
||||||
|
#define IMAGELOADHELPER_H
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
|
||||||
|
class QLabel;
|
||||||
|
|
||||||
|
class ImageLoadHelper : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load an image into a label
|
||||||
|
*
|
||||||
|
* @param label The label where the image will be applied
|
||||||
|
* @param url The URL of the target image
|
||||||
|
*/
|
||||||
|
static void Load(QLabel *label, const QString &url);
|
||||||
|
|
||||||
|
signals:
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Slot called in case of SSL error
|
||||||
|
*/
|
||||||
|
void SSLErrors();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Slot called in case of network error
|
||||||
|
*/
|
||||||
|
void NetworkError();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Slot called when a network request finished
|
||||||
|
*/
|
||||||
|
void NetworkRequestFinished();
|
||||||
|
|
||||||
|
private:
|
||||||
|
//This constructor must be private
|
||||||
|
explicit ImageLoadHelper(QObject *parent = nullptr);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Download an image
|
||||||
|
*
|
||||||
|
* @param url The URL of the image to download
|
||||||
|
*/
|
||||||
|
static void Download(const QString &url);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Apply an image to a label
|
||||||
|
*
|
||||||
|
* @param label Target label which will receive the image
|
||||||
|
* @param url The remote URL of the image
|
||||||
|
*/
|
||||||
|
static void ApplyImage(QLabel *label, const QString &url);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Apply an image to a label
|
||||||
|
*
|
||||||
|
* @param label Target label which will receive the image
|
||||||
|
* @param pixamp The pixmap to apply to the label
|
||||||
|
*/
|
||||||
|
static void ApplyImage(QLabel *label, const QPixmap &pixmap);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check out whether an image has been already downloaded
|
||||||
|
*
|
||||||
|
* @param url The URL of the target image
|
||||||
|
* @return TRUE for a success / FALSE else
|
||||||
|
*/
|
||||||
|
static bool IsDownloaded(const QString &url);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the storage path of a remote image
|
||||||
|
*
|
||||||
|
* @param url The URL of the remote image
|
||||||
|
* @return Full path to the image
|
||||||
|
*/
|
||||||
|
static QString GetImageStoragePath(const QString &url);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // IMAGELOADHELPER_H
|
BIN
res/baseline_person_black_48dp.png
Executable file
BIN
res/baseline_person_black_48dp.png
Executable file
Binary file not shown.
After Width: | Height: | Size: 544 B |
@ -2,5 +2,6 @@
|
|||||||
<qresource prefix="/">
|
<qresource prefix="/">
|
||||||
<file>baseline_people_black_48dp.png</file>
|
<file>baseline_people_black_48dp.png</file>
|
||||||
<file>baseline_access_time_black_48dp.png</file>
|
<file>baseline_access_time_black_48dp.png</file>
|
||||||
|
<file>baseline_person_black_48dp.png</file>
|
||||||
</qresource>
|
</qresource>
|
||||||
</RCC>
|
</RCC>
|
||||||
|
19
utils/filesutils.cpp
Normal file
19
utils/filesutils.cpp
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
#include <QDir>
|
||||||
|
|
||||||
|
#include "filesutils.h"
|
||||||
|
|
||||||
|
FilesUtils::FilesUtils()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FilesUtils::CreateDirectoryIfNotExists(const QString &path)
|
||||||
|
{
|
||||||
|
QDir dir(path);
|
||||||
|
|
||||||
|
//Check if the directory already exists
|
||||||
|
if(dir.exists())
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return dir.mkpath(".");
|
||||||
|
}
|
28
utils/filesutils.h
Normal file
28
utils/filesutils.h
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
/**
|
||||||
|
* Files utilities
|
||||||
|
*
|
||||||
|
* @author Pierre HUBERT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef FILESUTILS_H
|
||||||
|
#define FILESUTILS_H
|
||||||
|
|
||||||
|
#include <QString>
|
||||||
|
|
||||||
|
class FilesUtils
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
FilesUtils();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create the specified directory and all its parents if they do not
|
||||||
|
* exists
|
||||||
|
*
|
||||||
|
* @param path The path of the folder to check
|
||||||
|
* @return TRUE if the folder exists or has been successfully created /
|
||||||
|
* FALSE else
|
||||||
|
*/
|
||||||
|
static bool CreateDirectoryIfNotExists(const QString &path);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // FILESUTILS_H
|
@ -2,6 +2,7 @@
|
|||||||
#include "ui_conversationmessagewidget.h"
|
#include "ui_conversationmessagewidget.h"
|
||||||
#include "../data/user.h"
|
#include "../data/user.h"
|
||||||
#include "../data/conversationmessage.h"
|
#include "../data/conversationmessage.h"
|
||||||
|
#include "../helpers/imageloadhelper.h"
|
||||||
|
|
||||||
ConversationMessageWidget::ConversationMessageWidget(QWidget *parent) :
|
ConversationMessageWidget::ConversationMessageWidget(QWidget *parent) :
|
||||||
QWidget(parent),
|
QWidget(parent),
|
||||||
@ -19,4 +20,5 @@ void ConversationMessageWidget::setMessage(const ConversationMessage &message, c
|
|||||||
{
|
{
|
||||||
ui->nameLabel->setText(user.displayName());
|
ui->nameLabel->setText(user.displayName());
|
||||||
ui->messageLabel->setText(message.message());
|
ui->messageLabel->setText(message.message());
|
||||||
|
ImageLoadHelper::Load(ui->accountImageLabel, user.accountImage());
|
||||||
}
|
}
|
||||||
|
@ -14,6 +14,25 @@
|
|||||||
<string>Form</string>
|
<string>Form</string>
|
||||||
</property>
|
</property>
|
||||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="accountImageLabel">
|
||||||
|
<property name="maximumSize">
|
||||||
|
<size>
|
||||||
|
<width>20</width>
|
||||||
|
<height>20</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string/>
|
||||||
|
</property>
|
||||||
|
<property name="pixmap">
|
||||||
|
<pixmap resource="../res/ressources.qrc">:/baseline_person_black_48dp.png</pixmap>
|
||||||
|
</property>
|
||||||
|
<property name="scaledContents">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QLabel" name="nameLabel">
|
<widget class="QLabel" name="nameLabel">
|
||||||
<property name="sizePolicy">
|
<property name="sizePolicy">
|
||||||
@ -54,6 +73,8 @@
|
|||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
<resources/>
|
<resources>
|
||||||
|
<include location="../res/ressources.qrc"/>
|
||||||
|
</resources>
|
||||||
<connections/>
|
<connections/>
|
||||||
</ui>
|
</ui>
|
||||||
|
Loading…
Reference in New Issue
Block a user