mirror of
https://gitlab.com/comunic/comunicmessages
synced 2024-12-04 11:14:09 +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 \
|
||||
data/conversationmessage.cpp \
|
||||
widgets/conversationmessagewidget.cpp \
|
||||
data/conversationmessageslist.cpp
|
||||
data/conversationmessageslist.cpp \
|
||||
helpers/imageloadhelper.cpp \
|
||||
utils/filesutils.cpp
|
||||
|
||||
HEADERS += \
|
||||
helpers/accounthelper.h \
|
||||
@ -64,7 +66,10 @@ HEADERS += \
|
||||
helpers/conversationhelper.h \
|
||||
data/conversationmessage.h \
|
||||
widgets/conversationmessagewidget.h \
|
||||
data/conversationmessageslist.h
|
||||
data/conversationmessageslist.h \
|
||||
helpers/imageloadhelper.h \
|
||||
utils/filesutils.h \
|
||||
data/qlabelholder.h
|
||||
|
||||
FORMS += \
|
||||
widgets/loginwidget.ui \
|
||||
|
5
config.h
5
config.h
@ -35,4 +35,9 @@
|
||||
#define CONVERSATION_MESSAGE_MIN_LENGTH 3
|
||||
#define CONVERSATION_MESSAGES_REFRESH_INTERVAL 1000
|
||||
|
||||
/**
|
||||
* Images load manager information
|
||||
*/
|
||||
#define REMOTE_IMAGES_CACHE_DIRECTORY "remote_images"
|
||||
|
||||
#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="/">
|
||||
<file>baseline_people_black_48dp.png</file>
|
||||
<file>baseline_access_time_black_48dp.png</file>
|
||||
<file>baseline_person_black_48dp.png</file>
|
||||
</qresource>
|
||||
</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 "../data/user.h"
|
||||
#include "../data/conversationmessage.h"
|
||||
#include "../helpers/imageloadhelper.h"
|
||||
|
||||
ConversationMessageWidget::ConversationMessageWidget(QWidget *parent) :
|
||||
QWidget(parent),
|
||||
@ -19,4 +20,5 @@ void ConversationMessageWidget::setMessage(const ConversationMessage &message, c
|
||||
{
|
||||
ui->nameLabel->setText(user.displayName());
|
||||
ui->messageLabel->setText(message.message());
|
||||
ImageLoadHelper::Load(ui->accountImageLabel, user.accountImage());
|
||||
}
|
||||
|
@ -14,6 +14,25 @@
|
||||
<string>Form</string>
|
||||
</property>
|
||||
<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>
|
||||
<widget class="QLabel" name="nameLabel">
|
||||
<property name="sizePolicy">
|
||||
@ -54,6 +73,8 @@
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<resources>
|
||||
<include location="../res/ressources.qrc"/>
|
||||
</resources>
|
||||
<connections/>
|
||||
</ui>
|
||||
|
Loading…
Reference in New Issue
Block a user