Commit 889127d4 authored by Rinigus Saar's avatar Rinigus Saar

store high-resolution icons in sqlite database

Request high resolution icons designed for touch interface
and store them in SQLite database
parent bc20afb4
include(ECMAddTests)
find_package(Qt5 ${REQUIRED_QT_VERSION} CONFIG REQUIRED Test Sql)
find_package(Qt5 ${REQUIRED_QT_VERSION} CONFIG REQUIRED Test Sql Gui Quick)
include_directories(../src)
......@@ -14,12 +14,12 @@ ecm_add_test(useragenttest.cpp ../src/useragent.cpp
LINK_LIBRARIES Qt5::Test
)
ecm_add_test(browsermanagertest.cpp ../src/browsermanager.cpp ../src/dbmanager.cpp ../src/urlutils.cpp
ecm_add_test(browsermanagertest.cpp ../src/browsermanager.cpp ../src/dbmanager.cpp ../src/iconimageprovider.cpp ../src/urlutils.cpp
TEST_NAME browsermanagertest
LINK_LIBRARIES Qt5::Test Qt5::Sql
LINK_LIBRARIES Qt5::Test Qt5::Sql Qt5::Gui Qt5::Quick
)
ecm_add_test(tabsmodeltest.cpp ../src/tabsmodel.cpp ../src/browsermanager.cpp ../src/dbmanager.cpp
ecm_add_test(tabsmodeltest.cpp ../src/tabsmodel.cpp ../src/browsermanager.cpp ../src/dbmanager.cpp ../src/iconimageprovider.cpp
TEST_NAME tabsmodeltest
LINK_LIBRARIES Qt5::Test Qt5::Sql
LINK_LIBRARIES Qt5::Test Qt5::Sql Qt5::Gui Qt5::Quick
)
set(angelfish_SRCS
main.cpp
browsermanager.cpp
dbmanager.cpp
bookmarkshistorymodel.cpp
dbmanager.cpp
iconimageprovider.cpp
sqlquerymodel.cpp
urlutils.cpp
useragent.cpp
......
......@@ -66,9 +66,9 @@ void BrowserManager::lastVisited(const QString &url)
m_dbmanager.lastVisited(url);
}
void BrowserManager::updateIcon(const QString &url, const QString &iconSource)
void BrowserManager::updateIcon(const QString &url, const QString &iconSource, const QImage &image)
{
m_dbmanager.updateIcon(url, iconSource);
m_dbmanager.updateIcon(url, iconSource, image);
}
void BrowserManager::setHomepage(const QString &homepage)
......
......@@ -74,7 +74,7 @@ public slots:
void removeFromHistory(const QString &url);
void lastVisited(const QString &url);
void updateIcon(const QString &url, const QString &iconSource);
void updateIcon(const QString &url, const QString &iconSource, const QImage &image);
void setHomepage(const QString &homepage);
void setSearchBaseUrl(const QString &searchBaseUrl);
......
......@@ -57,6 +57,25 @@ WebEngineView {
visible: false
}
Image {
id: favicon
source: webEngineView.icon
visible: false
onSourceChanged: grab()
onStatusChanged: grab()
function grab() {
if (webEngineView.privateMode || status !== Image.Ready || !source)
return;
var url = webEngineView.url;
var icon = webEngineView.icon;
favicon.grabToImage(function (result){
BrowserManager.updateIcon(url, icon, result.image);
});
}
}
Timer {
id: snaphotTimer
interval: 1000
......@@ -101,6 +120,8 @@ WebEngineView {
settings {
// Disable builtin error pages in favor of our own
errorPageEnabled: false
// Load larger touch icons
touchIconsEnabled: true
// Disable scrollbars on mobile
showScrollBars: !Kirigami.Settings.isMobile
}
......@@ -197,11 +218,6 @@ WebEngineView {
print("Settings: " + settings);
}
onIconChanged: {
if (icon && !privateMode)
BrowserManager.updateIcon(url, icon)
}
onNewViewRequested: {
if (request.userInitiated) {
tabsModel.newTab(request.requestedUrl.toString())
......
......@@ -21,9 +21,11 @@
***************************************************************************/
#include "dbmanager.h"
#include "iconimageprovider.h"
#include <QDateTime>
#include <QDebug>
#include <QImage>
#include <QStandardPaths>
#include <QSqlDatabase>
#include <QSqlError>
......@@ -51,6 +53,7 @@ DBManager::DBManager(QObject *parent) : QObject(parent)
}
trimHistory();
trimIcons();
}
int DBManager::version()
......@@ -115,10 +118,14 @@ bool DBManager::migrateTo1()
{
// Starting from empty database, let's create the tables.
QString bookmarks = "CREATE TABLE bookmarks (url TEXT UNIQUE, title TEXT, icon TEXT, lastVisited INT)";
QString history = "CREATE TABLE history (url TEXT UNIQUE, title TEXT, icon TEXT, lastVisited INT)";
QString history = "CREATE TABLE history (url TEXT UNIQUE, title TEXT, icon TEXT, lastVisited INT)";
QString icons = "CREATE TABLE icons (url TEXT UNIQUE, icon BLOB)";
QString idx_bookmarks = "CREATE UNIQUE INDEX idx_bookmarks_url ON bookmarks(url)";
QString idx_history = "CREATE UNIQUE INDEX idx_history_url ON history(url)";
if (!execute(bookmarks) || !execute(history) || !execute(idx_bookmarks) || !execute(idx_history))
QString idx_history = "CREATE UNIQUE INDEX idx_history_url ON history(url)";
QString idx_icons = "CREATE UNIQUE INDEX idx_icons_url ON icons(url)";
if (!execute(bookmarks) || !execute(idx_bookmarks) ||
!execute(history) || !execute(idx_history) ||
!execute(icons) || !execute(idx_icons) )
return false;
setVersion(1);
......@@ -128,10 +135,16 @@ bool DBManager::migrateTo1()
void DBManager::trimHistory()
{
execute(QStringLiteral("DELETE FROM history WHERE rowid NOT IN (SELECT rowid from history" \
execute(QStringLiteral("DELETE FROM history WHERE rowid NOT IN (SELECT rowid FROM history" \
" ORDER BY lastVisited DESC LIMIT %1)").arg(MAX_BROWSER_HISTORY_SIZE));
}
void DBManager::trimIcons()
{
execute(QStringLiteral("DELETE FROM icons WHERE url NOT IN " \
"(SELECT icon FROM history UNION SELECT icon FROM bookmarks)"));
}
void DBManager::addRecord(const QString &table, const QVariantMap &pagedata)
{
QString url = pagedata.value("url").toString();
......@@ -212,14 +225,15 @@ void DBManager::removeFromHistory(const QString &url)
removeRecord("history", url);
}
void DBManager::updateIcon(const QString &url, const QString &iconSource)
{
updateIconRecord("bookmarks", url, iconSource);
updateIconRecord("history", url, iconSource);
}
void DBManager::lastVisited(const QString &url)
{
lastVisitedRecord("bookmarks", url);
lastVisitedRecord("history", url);
}
void DBManager::updateIcon(const QString &url, const QString &iconSource, const QImage &image)
{
QString updatedSource = IconImageProvider::storeImage(iconSource, image);
updateIconRecord("bookmarks", url, updatedSource);
updateIconRecord("history", url, updatedSource);
}
......@@ -48,7 +48,7 @@ public:
void addToHistory(const QVariantMap &pagedata);
void removeFromHistory(const QString &url);
void updateIcon(const QString &url, const QString &iconSource);
void updateIcon(const QString &url, const QString &iconSource, const QImage &image);
void lastVisited(const QString &url);
private:
......@@ -62,6 +62,8 @@ private:
// limit the size of history table
void trimHistory();
// drop unused icons
void trimIcons();
// execute SQL statement
bool execute(const QString &command);
......
#include "iconimageprovider.h"
#include <QByteArray>
#include <QBuffer>
#include <QDebug>
#include <QImage>
#include <QSqlError>
#include <QSqlQuery>
#include <QString>
IconImageProvider::IconImageProvider() :
QQuickImageProvider(QQmlImageProviderBase::Image)
{
}
QString IconImageProvider::providerId()
{
return "angelfish";
}
QString IconImageProvider::storeImage(const QString &iconSource, const QImage &image)
{
QString prefix_favicon = "image://favicon/";
if (!iconSource.startsWith(prefix_favicon)) {
// don't know what to do with it, return as it is
return iconSource;
}
// new uri for image
QString url = QStringLiteral("image://%1/%2").arg(providerId()).arg(iconSource.mid(prefix_favicon.length()));
// check if we have that image already
QSqlQuery query_check;
query_check.prepare("SELECT 1 FROM icons WHERE url = :url LIMIT 1");
query_check.bindValue(":url", url);
if (!query_check.exec()) {
qWarning() << Q_FUNC_INFO << "Failed to execute SQL statement";
qWarning() << query_check.lastQuery();
qWarning() << query_check.lastError();
return iconSource; // as something is wrong
}
if (query_check.next()) {
// there is corresponding record in the database already
// no need to store it again
qDebug() << "Icon stored already" << url;
return url;
}
query_check.finish();
// Store new icon
QByteArray data;
QBuffer buffer(&data);
buffer.open(QIODevice::WriteOnly);
if (!image.save(&buffer, "PNG")) {
qWarning() << Q_FUNC_INFO << "Failed to save image" << url;
return iconSource; // as something is wrong
}
QSqlQuery query_write;
query_write.prepare("INSERT INTO icons(url, icon) VALUES (:url, :icon)");
query_write.bindValue(":url", url);
query_write.bindValue(":icon", data);
if (!query_write.exec()) {
qWarning() << Q_FUNC_INFO << "Failed to execute SQL statement";
qWarning() << query_write.lastQuery();
qWarning() << query_write.lastError();
return iconSource; // as something is wrong
}
return url;
}
QImage IconImageProvider::requestImage(const QString &id, QSize *size, const QSize &/*requestedSize*/)
{
QSqlQuery query;
query.prepare("SELECT icon FROM icons WHERE url LIKE :url LIMIT 1");
query.bindValue(":url", QStringLiteral("image://%1/%2%").arg(providerId()).arg(id));
if (!query.exec()) {
qWarning() << Q_FUNC_INFO << "Failed to execute SQL statement";
qWarning() << query.lastQuery();
qWarning() << query.lastError();
return QImage();
}
if (query.next()) {
QImage image = QImage::fromData( query.value(0).toByteArray() );
if (size) {
size->setHeight(image.height());
size->setWidth(image.width());
}
return image;
}
qWarning() << "Failed to find icon for" << id;
return QImage();
}
#ifndef ICONIMAGEPROVIDER_H
#define ICONIMAGEPROVIDER_H
#include <QQuickImageProvider>
class IconImageProvider : public QQuickImageProvider
{
public:
IconImageProvider();
virtual QImage requestImage(const QString &id, QSize *size, const QSize &requestedSize) override;
// store image into the database if it is missing. Return new
// image:// uri that should be used to fetch the icon
static QString storeImage(const QString &iconSource, const QImage &image);
static QString providerId();
};
#endif // ICONIMAGEPROVIDER_H
......@@ -28,6 +28,7 @@
#include "bookmarkshistorymodel.h"
#include "browsermanager.h"
#include "iconimageprovider.h"
#include "tabsmodel.h"
#include "urlutils.h"
#include "useragent.h"
......@@ -63,6 +64,8 @@ Q_DECL_EXPORT int main(int argc, char *argv[])
QQmlApplicationEngine engine;
engine.rootContext()->setContextObject(new KLocalizedContext(&engine));
engine.addImageProvider(IconImageProvider::providerId(), new IconImageProvider);
// initial url command line parameter
QString initialUrl;
if (!parser.positionalArguments().isEmpty())
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment