Transfer: implement 'Grab QR code from screen' functionality

This commit is contained in:
xiphon 2021-01-06 15:40:47 +00:00
parent 9399839d96
commit e9b894da16
7 changed files with 184 additions and 2 deletions

View file

@ -93,12 +93,16 @@ Rectangle {
oaPopup.open()
}
function updateFromQrCode(address, payment_id, amount, tx_description, recipient_name) {
console.log("updateFromQrCode")
function fillPaymentDetails(address, payment_id, amount, tx_description, recipient_name) {
addressLine.text = address
setPaymentId(payment_id);
amountLine.text = amount
setDescription((recipient_name ? recipient_name + " " : "") + tx_description);
}
function updateFromQrCode(address, payment_id, amount, tx_description, recipient_name) {
console.log("updateFromQrCode")
fillPaymentDetails(address, payment_id, amount, tx_description, recipient_name);
cameraUi.qrcode_decoded.disconnect(updateFromQrCode)
}
@ -196,6 +200,23 @@ Rectangle {
}
}
MoneroComponents.InlineButton {
fontFamily: FontAwesome.fontFamily
fontPixelSize: 18
text: FontAwesome.desktop
onClicked: {
clearFields();
const codes = oshelper.grabQrCodesFromScreen();
for (var index = 0; index < codes.length; ++index) {
const parsed = walletManager.parse_uri_to_object(codes[index]);
if (!parsed.error) {
fillPaymentDetails(parsed.address, parsed.payment_id, parsed.amount, parsed.tx_description, parsed.recipient_name);
break;
}
}
}
}
MoneroComponents.InlineButton {
fontFamily: FontAwesome.fontFamily
text: FontAwesome.addressBook

View file

@ -1,3 +1,31 @@
// Copyright (c) 2020, The Monero Project
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are
// permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this list of
// conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
// of conditions and the following disclaimer in the documentation and/or other
// materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its contributors may be
// used to endorse or promote products derived from this software without specific
// prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "Decoder.h"
#include <limits>

View file

@ -1,3 +1,31 @@
// Copyright (c) 2020, The Monero Project
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are
// permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this list of
// conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
// of conditions and the following disclaimer in the documentation and/or other
// materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its contributors may be
// used to endorse or promote products derived from this software without specific
// prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <QImage>
struct quirc;

View file

@ -27,10 +27,16 @@
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "oshelper.h"
#include <unordered_set>
#include <QCoreApplication>
#include <QGuiApplication>
#include <QFileDialog>
#include <QScreen>
#include <QStandardPaths>
#include <QTemporaryFile>
#include <QWindow>
#include <QDir>
#include <QDebug>
#include <QDesktopServices>
@ -55,6 +61,40 @@
#include "qt/utils.h"
#endif
#include "QR-Code-scanner/Decoder.h"
#include "qt/ScopeGuard.h"
namespace
{
QPixmap screenshot()
{
#ifdef Q_OS_MAC
return MacOSHelper::screenshot();
#else
std::unordered_set<QWindow *> hidden;
const QWindowList windows = QGuiApplication::allWindows();
for (QWindow *window : windows)
{
if (window->isVisible())
{
hidden.emplace(window);
window->hide();
}
}
const auto unhide = sg::make_scope_guard([&hidden]() {
for (QWindow *window : hidden)
{
window->show();
}
});
return QGuiApplication::primaryScreen()->grabWindow(0);
#endif
}
} // namespace
#if defined(Q_OS_WIN)
bool openFolderAndSelectItem(const QString &filePath)
{
@ -99,6 +139,26 @@ QString OSHelper::downloadLocation() const
return QStandardPaths::writableLocation(QStandardPaths::DownloadLocation);
}
QList<QString> OSHelper::grabQrCodesFromScreen() const
{
QList<QString> codes;
try
{
const QImage image = screenshot().toImage();
const std::vector<std::string> decoded = QrDecoder().decode(image);
std::for_each(decoded.begin(), decoded.end(), [&codes](const std::string &code) {
codes.push_back(QString::fromStdString(code));
});
}
catch (const std::exception &e)
{
qWarning() << e.what();
}
return codes;
}
bool OSHelper::openContainingFolder(const QString &filePath) const
{
QString canonicalFilePath = QFileInfo(filePath).canonicalFilePath();

View file

@ -29,7 +29,9 @@
#ifndef OSHELPER_H
#define OSHELPER_H
#include <QList>
#include <QObject>
#include <QString>
/**
* @brief The OSHelper class - exports to QML some OS-related functions
*/
@ -43,6 +45,7 @@ public:
Q_INVOKABLE void createDesktopEntry() const;
Q_INVOKABLE QString downloadLocation() const;
Q_INVOKABLE QList<QString> grabQrCodesFromScreen() const;
Q_INVOKABLE bool openContainingFolder(const QString &filePath) const;
Q_INVOKABLE QString openSaveFileDialog(const QString &title, const QString &folder, const QString &filename) const;
Q_INVOKABLE QString temporaryFilename() const;

View file

@ -29,6 +29,8 @@
#ifndef MACOSHELPER_H
#define MACOSHELPER_H
#include <QPixmap>
class MacOSHelper
{
MacOSHelper() {}
@ -36,6 +38,7 @@ class MacOSHelper
public:
static bool isCapsLock();
static bool openFolderAndSelectItem(const QUrl &path);
static QPixmap screenshot();
static QString bundlePath();
};

View file

@ -26,6 +26,8 @@
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <unordered_set>
#include <QtCore>
#include <QtGui>
#include <QtMac>
@ -37,6 +39,8 @@
#include <ApplicationServices/ApplicationServices.h>
#include <Availability.h>
#include "ScopeGuard.h"
bool MacOSHelper::isCapsLock()
{
#ifdef __MAC_10_12
@ -56,6 +60,41 @@ bool MacOSHelper::openFolderAndSelectItem(const QUrl &path)
return true;
}
QPixmap MacOSHelper::screenshot()
{
std::unordered_set<uintptr_t> appWindowIds;
for (NSWindow *window in [NSApp windows])
{
appWindowIds.insert((uintptr_t)[window windowNumber]);
}
CFArrayRef onScreenWindows = CGWindowListCreate(kCGWindowListOptionOnScreenOnly, kCGNullWindowID);
const auto onScreenWindowsClenaup = sg::make_scope_guard([&onScreenWindows]() {
CFRelease(onScreenWindows);
});
CFMutableArrayRef foreignWindows = CFArrayCreateMutable(NULL, CFArrayGetCount(onScreenWindows), NULL);
const auto foreignWindowsClenaup = sg::make_scope_guard([&foreignWindows]() {
CFRelease(foreignWindows);
});
for (CFIndex index = 0, count = CFArrayGetCount(onScreenWindows); index < count; ++index)
{
const uintptr_t windowId = reinterpret_cast<const uintptr_t>(CFArrayGetValueAtIndex(onScreenWindows, index));
if (appWindowIds.find(windowId) == appWindowIds.end())
{
CFArrayAppendValue(foreignWindows, reinterpret_cast<const void *>(windowId));
}
}
CGImageRef image = CGWindowListCreateImageFromArray(CGRectInfinite, foreignWindows, kCGWindowListOptionAll);
const auto imageClenaup = sg::make_scope_guard([&image]() {
CFRelease(image);
});
return QtMac::fromCGImageRef(image);
}
QString MacOSHelper::bundlePath()
{
NSBundle *main = [NSBundle mainBundle];