Merge pull request #3292

e9b894d Transfer: implement 'Grab QR code from screen' functionality (xiphon)
This commit is contained in:
luigi1111 2021-01-20 22:40:14 -05:00
commit ec7bc577d6
No known key found for this signature in database
GPG key ID: F4ACA0183641E010
7 changed files with 184 additions and 2 deletions

View file

@ -93,12 +93,16 @@ Rectangle {
oaPopup.open() oaPopup.open()
} }
function updateFromQrCode(address, payment_id, amount, tx_description, recipient_name) { function fillPaymentDetails(address, payment_id, amount, tx_description, recipient_name) {
console.log("updateFromQrCode")
addressLine.text = address addressLine.text = address
setPaymentId(payment_id); setPaymentId(payment_id);
amountLine.text = amount amountLine.text = amount
setDescription((recipient_name ? recipient_name + " " : "") + tx_description); 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) 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 { MoneroComponents.InlineButton {
fontFamily: FontAwesome.fontFamily fontFamily: FontAwesome.fontFamily
text: FontAwesome.addressBook 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 "Decoder.h"
#include <limits> #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> #include <QImage>
struct quirc; struct quirc;

View file

@ -27,10 +27,16 @@
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "oshelper.h" #include "oshelper.h"
#include <unordered_set>
#include <QCoreApplication> #include <QCoreApplication>
#include <QGuiApplication>
#include <QFileDialog> #include <QFileDialog>
#include <QScreen>
#include <QStandardPaths> #include <QStandardPaths>
#include <QTemporaryFile> #include <QTemporaryFile>
#include <QWindow>
#include <QDir> #include <QDir>
#include <QDebug> #include <QDebug>
#include <QDesktopServices> #include <QDesktopServices>
@ -55,6 +61,40 @@
#include "qt/utils.h" #include "qt/utils.h"
#endif #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) #if defined(Q_OS_WIN)
bool openFolderAndSelectItem(const QString &filePath) bool openFolderAndSelectItem(const QString &filePath)
{ {
@ -99,6 +139,26 @@ QString OSHelper::downloadLocation() const
return QStandardPaths::writableLocation(QStandardPaths::DownloadLocation); 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 bool OSHelper::openContainingFolder(const QString &filePath) const
{ {
QString canonicalFilePath = QFileInfo(filePath).canonicalFilePath(); QString canonicalFilePath = QFileInfo(filePath).canonicalFilePath();

View file

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

View file

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

View file

@ -26,6 +26,8 @@
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF // 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. // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <unordered_set>
#include <QtCore> #include <QtCore>
#include <QtGui> #include <QtGui>
#include <QtMac> #include <QtMac>
@ -37,6 +39,8 @@
#include <ApplicationServices/ApplicationServices.h> #include <ApplicationServices/ApplicationServices.h>
#include <Availability.h> #include <Availability.h>
#include "ScopeGuard.h"
bool MacOSHelper::isCapsLock() bool MacOSHelper::isCapsLock()
{ {
#ifdef __MAC_10_12 #ifdef __MAC_10_12
@ -56,6 +60,41 @@ bool MacOSHelper::openFolderAndSelectItem(const QUrl &path)
return true; 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() QString MacOSHelper::bundlePath()
{ {
NSBundle *main = [NSBundle mainBundle]; NSBundle *main = [NSBundle mainBundle];