Skip to content

Commit

Permalink
support V2/GET_CLIENT_ICON in socketapi, add icon to dolphin right cl…
Browse files Browse the repository at this point in the history
…ick menu (#8638)

* support V2/GET_CLIENT_ICON in socketapi, add icon to dolphin right click menu

* apply review improvements

* further improvements

* logging added, improvements

* improve OwncloudDolphinPluginHelper::slotReadyRead

* use line.mid(), add const

* changlog for #8464
  • Loading branch information
gabi18 committed May 26, 2021
1 parent eac5bcb commit 066ed56
Show file tree
Hide file tree
Showing 7 changed files with 135 additions and 15 deletions.
8 changes: 8 additions & 0 deletions changelog/unreleased/8464
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
Enhancement: Provide a socket api call to get the client icon and
add the icon to the dolphin right click menu

We added support to get the ownCloud client icon of the current theme to
the socket api.
We show the client icon in the dolphin file browser context menu.

https://github.com/owncloud/client/issues/8464
2 changes: 1 addition & 1 deletion shell_integration/dolphin/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ endif()
#---HELPER---
set(OWNCLOUDDOLPHINHELPER ${APPLICATION_EXECUTABLE}dolphinpluginhelper)
add_library(${OWNCLOUDDOLPHINHELPER} SHARED ownclouddolphinpluginhelper.cpp)
target_link_libraries(${OWNCLOUDDOLPHINHELPER} Qt5::Network)
target_link_libraries(${OWNCLOUDDOLPHINHELPER} Qt5::Network Qt5::Gui)
generate_export_header(${OWNCLOUDDOLPHINHELPER} BASE_NAME ownclouddolphinpluginhelper)
install(TARGETS ${OWNCLOUDDOLPHINHELPER} LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR})

Expand Down
5 changes: 5 additions & 0 deletions shell_integration/dolphin/ownclouddolphinactionplugin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,9 @@ class OwncloudDolphinPluginAction : public KAbstractFileItemActionPlugin
});
QTimer::singleShot(500, &loop, SLOT(quit())); // add a timeout to be sure we don't freeze dolphin
helper->sendCommand(QByteArray("GET_MENU_ITEMS:" + files + "\n"));

helper->sendGetClientIconCommand(16); // get client icon with size 16x16

loop.exec(QEventLoop::ExcludeUserInputEvents);
disconnect(con);
if (menu->actions().isEmpty()) {
Expand All @@ -95,7 +98,9 @@ class OwncloudDolphinPluginAction : public KAbstractFileItemActionPlugin

auto menuaction = new QAction(parentWidget);
menuaction->setText(helper->contextMenuTitle());
menuaction->setIcon(QIcon(helper->clientIcon()));
menuaction->setMenu(menu);

return { menuaction };
}

Expand Down
77 changes: 63 additions & 14 deletions shell_integration/dolphin/ownclouddolphinpluginhelper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,13 @@
#include <qcoreevent.h>
#include <QStandardPaths>
#include <QFile>
#include <QLoggingCategory>
#include "ownclouddolphinpluginhelper.h"
#include "config.h"
#include <QJsonObject>
#include <QJsonDocument>

Q_LOGGING_CATEGORY(lcPluginHelper, "owncloud.dolphin", QtInfoMsg)

OwncloudDolphinPluginHelper* OwncloudDolphinPluginHelper::instance()
{
Expand Down Expand Up @@ -58,6 +63,16 @@ void OwncloudDolphinPluginHelper::sendCommand(const char* data)
_socket.flush();
}

void OwncloudDolphinPluginHelper::sendGetClientIconCommand(int size)
{
const QByteArray cmd = QByteArrayLiteral("V2/GET_CLIENT_ICON:");
const QByteArray newLine = QByteArrayLiteral("\n");
const QJsonObject args { { QStringLiteral("size"), size } };
const QJsonObject obj { { QStringLiteral("id"), QString::number(_msgId++) }, { QStringLiteral("arguments"), args } };
const auto json = QJsonDocument(obj).toJson(QJsonDocument::Compact);
sendCommand(QByteArray(cmd + json + newLine));
}

void OwncloudDolphinPluginHelper::slotConnected()
{
sendCommand("VERSION:\n");
Expand All @@ -69,7 +84,7 @@ void OwncloudDolphinPluginHelper::tryConnect()
if (_socket.state() != QLocalSocket::UnconnectedState) {
return;
}

QString socketPath = QStandardPaths::locate(QStandardPaths::RuntimeLocation,
APPLICATION_SHORTNAME,
QStandardPaths::LocateDirectory);
Expand All @@ -83,36 +98,70 @@ void OwncloudDolphinPluginHelper::slotReadyRead()
{
while (_socket.bytesAvailable()) {
_line += _socket.readLine();
if (!_line.endsWith("\n"))
if (!_line.endsWith("\n")) {
continue;
}
QByteArray line;
qSwap(line, _line);
line.chop(1);
if (line.isEmpty())
continue;
const int firstColon = line.indexOf(':');
if (firstColon == -1) {
continue;
}
// get the command (at begin of line, before first ':')
const QByteArray command = line.left(firstColon);
// rest of line contains the information
const QByteArray info = line.mid(firstColon + 1);

if (line.startsWith("REGISTER_PATH:")) {
auto col = line.indexOf(':');
QString file = QString::fromUtf8(line.constData() + col + 1, line.size() - col - 1);
if (command == QByteArrayLiteral("REGISTER_PATH")) {
const QString file = QString::fromUtf8(info);
_paths.append(file);
continue;
} else if (line.startsWith("STRING:")) {
auto args = QString::fromUtf8(line).split(QLatin1Char(':'));
if (args.size() >= 3) {
_strings[args[1]] = args.mid(2).join(QLatin1Char(':'));
} else if (command == QByteArrayLiteral("STRING")) {
auto args = QString::fromUtf8(info).split(':');
if (args.size() >= 2) {
_strings[args[0]] = args.mid(1).join(':');
}
continue;
} else if (line.startsWith("VERSION:")) {
auto args = line.split(':');
auto version = args.value(2);
_version = version;
if (!version.startsWith("1.")) {
} else if (command == QByteArrayLiteral("VERSION")) {
auto args = info.split(':');
if (args.size() >= 2) {
auto version = args.value(1);
_version = version;
}
if (!_version.startsWith("1.")) {
// Incompatible version, disconnect forever
_connectTimer.stop();
_socket.disconnectFromServer();
return;
}
} else if (command == QByteArrayLiteral("V2/GET_CLIENT_ICON_RESULT")) {
QJsonParseError error;
auto json = QJsonDocument::fromJson(info, &error).object();
if (error.error != QJsonParseError::NoError) {
qCWarning(lcPluginHelper) << "Error while parsing result: " << error.error;
continue;
}

auto jsonArgs = json.value("arguments").toObject();
if (jsonArgs.isEmpty()) {
auto jsonErr = json.value("error").toObject();
qCWarning(lcPluginHelper) << "Error getting client icon: " << jsonErr;
continue;
}

const QByteArray pngBase64 = jsonArgs.value("png").toString().toUtf8();
QByteArray png = QByteArray::fromBase64(pngBase64);

QPixmap pixmap;
bool isLoaded = pixmap.loadFromData(png, "PNG");
if (isLoaded) {
_clientIcon = pixmap;
}
}

emit commandRecieved(line);
}
}
9 changes: 9 additions & 0 deletions shell_integration/dolphin/ownclouddolphinpluginhelper.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
#include <QBasicTimer>
#include <QLocalSocket>
#include <QRegularExpression>
#include <QPixmap>
#include "ownclouddolphinpluginhelper_export.h"

class OWNCLOUDDOLPHINPLUGINHELPER_EXPORT OwncloudDolphinPluginHelper : public QObject {
Expand All @@ -31,6 +32,8 @@ class OWNCLOUDDOLPHINPLUGINHELPER_EXPORT OwncloudDolphinPluginHelper : public QO

bool isConnected() const;
void sendCommand(const char *data);
void sendGetClientIconCommand(int size);

QVector<QString> paths() const { return _paths; }

QString contextMenuTitle() const
Expand All @@ -41,6 +44,10 @@ class OWNCLOUDDOLPHINPLUGINHELPER_EXPORT OwncloudDolphinPluginHelper : public QO
{
return _strings.value("SHARE_MENU_TITLE", "Share...");
}
QPixmap clientIcon() const
{
return _clientIcon;
}

QString copyPrivateLinkTitle() const { return _strings["COPY_PRIVATE_LINK_MENU_TITLE"]; }
QString emailPrivateLinkTitle() const { return _strings["EMAIL_PRIVATE_LINK_MENU_TITLE"]; }
Expand All @@ -65,4 +72,6 @@ class OWNCLOUDDOLPHINPLUGINHELPER_EXPORT OwncloudDolphinPluginHelper : public QO

QMap<QString, QString> _strings;
QByteArray _version;
QPixmap _clientIcon;
int _msgId = 1;
};
42 changes: 42 additions & 0 deletions src/gui/socketapi/socketapi.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@
#include <QJsonDocument>
#include <QJsonObject>
#include <QWidget>
#include <QBuffer>

#include <QClipboard>

Expand Down Expand Up @@ -860,6 +861,47 @@ void SocketApi::command_V2_UPLOAD_FILES_FROM(const QSharedPointer<SocketApiJobV2
uploadJob->start();
}

void SocketApi::command_V2_GET_CLIENT_ICON(const QSharedPointer<SocketApiJobV2> &job) const
{
OC_ASSERT(job);
const auto &arguments = job->arguments();

const auto size = arguments.value("size");
if (size.isUndefined()) {
qCWarning(lcSocketApi) << "Icon size not given in " << Q_FUNC_INFO;
job->failure(QStringLiteral("cannot get client icon"));
return;
}

const Theme *theme = Theme::instance();
OC_ASSERT(theme);
const QIcon appIcon = theme->applicationIcon();
qCDebug(lcSocketApi) << Q_FUNC_INFO << " got icon from theme: " << appIcon;

// convert to pixmap (might be smaller if size is not available)
const QPixmap pixmap = appIcon.pixmap(QSize(size.toInt(), size.toInt()));

// Convert pixmap to in-memory PNG
QByteArray png;
QBuffer pngBuffer(&png);
auto success = pngBuffer.open(QIODevice::WriteOnly);
if (!success) {
qCWarning(lcSocketApi) << "Error opening buffer for png in " << Q_FUNC_INFO;
job->failure(QStringLiteral("cannot get client icon"));
return;
}

success = pixmap.save(&pngBuffer, "PNG");
if (!success) {
qCWarning(lcSocketApi) << "Error saving client icon as png in " << Q_FUNC_INFO;
job->failure(QStringLiteral("cannot get client icon"));
return;
}

const QByteArray pngAsBase64 = pngBuffer.data().toBase64();
job->success({ { QStringLiteral("png"), QString::fromUtf8(pngAsBase64) } });
}

void SocketApi::emailPrivateLink(const QString &link)
{
Utility::openEmailComposer(
Expand Down
7 changes: 7 additions & 0 deletions src/gui/socketapi/socketapi.h
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,13 @@ private slots:
Q_INVOKABLE void command_V2_LIST_ACCOUNTS(const QSharedPointer<SocketApiJobV2> &job) const;
Q_INVOKABLE void command_V2_UPLOAD_FILES_FROM(const QSharedPointer<SocketApiJobV2> &job) const;

// Sends the id and the client icon as PNG image (base64 encoded) in Json key "png"
// e.g. { "id" : "1", "arguments" : { "png" : "hswehs343dj8..." } } or an error message in key "error"
//
// Argument is a SocketApiJobV2 job which contains an id and the required icon size in Json format
// e.g. { "id" : "1", "arguments" : { "size" : 16 } }
Q_INVOKABLE void command_V2_GET_CLIENT_ICON(const QSharedPointer<SocketApiJobV2> &job) const;

// Fetch the private link and call targetFun
void fetchPrivateLinkUrlHelper(const QString &localFile, const std::function<void(const QString &url)> &targetFun);

Expand Down

0 comments on commit 066ed56

Please sign in to comment.