Prepare Wowlet codebase for an Android app

pull/48/head
dsc 3 years ago
parent c024323eab
commit cfee938516

@ -30,77 +30,69 @@ AppContext::AppContext(QCommandLineParser *cmdargs) {
this->cmdargs = cmdargs; this->cmdargs = cmdargs;
AppContext::isQML = false; AppContext::isQML = false;
// OS & env
#if defined(Q_OS_MAC) #if defined(Q_OS_MAC)
this->isMac = true;
this->isTorSocks = qgetenv("DYLD_INSERT_LIBRARIES").indexOf("libtorsocks") >= 0; this->isTorSocks = qgetenv("DYLD_INSERT_LIBRARIES").indexOf("libtorsocks") >= 0;
#elif __ANDROID__
this->isAndroid = true;
#elif defined(Q_OS_LINUX) #elif defined(Q_OS_LINUX)
this->isLinux = true;
this->isTorSocks = qgetenv("LD_PRELOAD").indexOf("libtorsocks") >= 0; this->isTorSocks = qgetenv("LD_PRELOAD").indexOf("libtorsocks") >= 0;
this->isTails = TailsOS::detect();
this->isWhonix = WhonixOS::detect();
#elif defined(Q_OS_WIN) #elif defined(Q_OS_WIN)
this->isWindows = true;
this->isTorSocks = false; this->isTorSocks = false;
#endif #endif
this->androidDebug = cmdargs->isSet("android-debug");
this->isTails = TailsOS::detect(); // Paths
this->isWhonix = WhonixOS::detect(); this->pathGenericData = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation);
//Paths
this->configRoot = QDir::homePath(); this->configRoot = QDir::homePath();
if (isTails) { // #if defined(PORTABLE)
QString portablePath = []{
QString appImagePath = qgetenv("APPIMAGE");
if (appImagePath.isEmpty()) {
qDebug() << "Not an appimage, using currentPath()";
return QDir::currentPath() + "/.wowlet";
}
QFileInfo appImageDir(appImagePath);
return appImageDir.absoluteDir().path() + "/.wowlet";
}();
if (QDir().mkpath(portablePath)) {
this->configRoot = portablePath;
} else {
qCritical() << "Unable to create portable directory: " << portablePath;
}
}
this->accountName = Utils::getUnixAccountName(); this->accountName = Utils::getUnixAccountName();
this->homeDir = QDir::homePath(); this->homeDir = QDir::homePath();
this->configDirectory = QString("%1/.config/wowlet/").arg(this->configRoot);
this->configDirectoryVR = QString("%1%2").arg(this->configDirectory, "vr");
if (isTails) this->setupPathsTails();
QString walletDir = config()->get(Config::walletDirectory).toString(); QString walletDir = config()->get(Config::walletDirectory).toString();
if (walletDir.isEmpty()) { if(walletDir.isEmpty()) {
#if defined(Q_OS_LINUX) or defined(Q_OS_MAC) if (isAndroid && !androidDebug) setupPathsAndroid();
this->defaultWalletDir = QString("%1/Wownero/wallets").arg(this->configRoot); else if (isWindows) setupPathsWindows();
this->defaultWalletDirRoot = QString("%1/Wownero").arg(this->configRoot); else if (isLinux || isMac) setupPathsUnix();
#elif defined(Q_OS_WIN)
this->defaultWalletDir = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation) + "/Wownero";
this->defaultWalletDirRoot = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation);
#endif
} else { } else {
this->defaultWalletDir = walletDir; this->defaultWalletDir = walletDir;
this->defaultWalletDirRoot = walletDir; this->defaultWalletDirRoot = walletDir;
} }
#ifdef __ANDROID__
// can haz disk I/O?
QVector<QString> perms = {
"android.permission.WRITE_EXTERNAL_STORAGE",
"android.permission.READ_EXTERNAL_STORAGE"
};
Utils::androidAskPermissions(perms);
#endif
// Create wallet dirs // Create wallet dirs
qDebug() << "creating " << defaultWalletDir;
if (!QDir().mkpath(defaultWalletDir)) if (!QDir().mkpath(defaultWalletDir))
qCritical() << "Unable to create dir: " << defaultWalletDir; qCritical() << "Unable to create dir: " << defaultWalletDir;
this->configDirectory = QString("%1/.config/wowlet/").arg(this->configRoot);
#if defined(Q_OS_UNIX)
if(!this->configDirectory.endsWith('/'))
this->configDirectory = QString("%1/").arg(this->configDirectory);
#endif
this->configDirectoryVR = QString("%1%2").arg(this->configDirectory, "vr");
// Create some directories // Create some directories
createConfigDirectory(this->configDirectory); createConfigDirectory(this->configDirectory);
// if(this->cmdargs->isSet("stagenet"))
// this->networkType = NetworkType::STAGENET;
// else if(this->cmdargs->isSet("testnet"))
// this->networkType = NetworkType::TESTNET;
// else
this->networkType = NetworkType::MAINNET; this->networkType = NetworkType::MAINNET;
qDebug() << "configRoot: " << this->configRoot;
qDebug() << "homeDir: " << this->homeDir;
qDebug() << "customWalletDir: " << walletDir;
qDebug() << "defaultWalletDir: " << this->defaultWalletDir;
qDebug() << "defaultWalletDirRoot: " << this->defaultWalletDirRoot;
qDebug() << "configDirectory: " << this->configDirectory;
// auto nodeSourceUInt = config()->get(Config::nodeSource).toUInt(); // auto nodeSourceUInt = config()->get(Config::nodeSource).toUInt();
// AppContext::nodeSource = static_cast<NodeSource>(nodeSourceUInt); // AppContext::nodeSource = static_cast<NodeSource>(nodeSourceUInt);
this->nodes = new Nodes(this, this->networkClearnet); this->nodes = new Nodes(this, this->networkClearnet);
@ -558,12 +550,14 @@ void AppContext::createConfigDirectory(const QString &dir) {
} }
} }
#ifdef HAS_OPENVR
auto config_dir_vr = QString("%1%2").arg(dir, "vr"); auto config_dir_vr = QString("%1%2").arg(dir, "vr");
if(!Utils::dirExists(config_dir_vr)) { if(!Utils::dirExists(config_dir_vr)) {
qDebug() << QString("Creating directory: %1").arg(config_dir_vr); qDebug() << QString("Creating directory: %1").arg(config_dir_vr);
if (!QDir().mkpath(config_dir_vr)) if (!QDir().mkpath(config_dir_vr))
throw std::runtime_error("Could not create directory " + config_dir_vr.toStdString()); throw std::runtime_error("Could not create directory " + config_dir_vr.toStdString());
} }
#endif
} }
void AppContext::createWalletWithoutSpecifyingSeed(const QString &name, const QString &password) { void AppContext::createWalletWithoutSpecifyingSeed(const QString &name, const QString &password) {
@ -949,3 +943,38 @@ void AppContext::refreshModels() {
this->currentWallet->coins()->refresh(this->currentWallet->currentSubaddressAccount()); this->currentWallet->coins()->refresh(this->currentWallet->currentSubaddressAccount());
// Todo: set timer for refreshes // Todo: set timer for refreshes
} }
void AppContext::setupPathsUnix() {
this->defaultWalletDir = QString("%1/Wownero/wallets").arg(this->configRoot);
this->defaultWalletDirRoot = QString("%1/Wownero").arg(this->configRoot);
}
void AppContext::setupPathsWindows() {
this->defaultWalletDir = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation) + "/Wownero";
this->defaultWalletDirRoot = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation);
}
void AppContext::setupPathsAndroid() {
this->defaultWalletDir = QString("%1/Wownero/wallets").arg(this->pathGenericData);
this->defaultWalletDirRoot = QString("%1/Wownero").arg(this->pathGenericData);
}
void AppContext::setupPathsTails() {
QString portablePath = []{
QString appImagePath = qgetenv("APPIMAGE");
if (appImagePath.isEmpty()) {
qDebug() << "Not an appimage, using currentPath()";
return QDir::currentPath() + "/.wowlet";
}
QFileInfo appImageDir(appImagePath);
return appImageDir.absoluteDir().path() + "/.wowlet";
}();
if (QDir().mkpath(portablePath)) {
this->configRoot = portablePath;
} else {
qCritical() << "Unable to create portable directory: " << portablePath;
}
}

@ -39,7 +39,12 @@ public:
~AppContext() override; ~AppContext() override;
bool isTails = false; bool isTails = false;
bool isWhonix = false; bool isWhonix = false;
bool isAndroid = false;
bool isLinux = false;
bool isMac = false;
bool isWindows = false;
bool isDebug = false; bool isDebug = false;
bool androidDebug = false;
// Donation config // Donation config
const QString donationAddress = "Wo3MWeKwtA918DU4c69hVSNgejdWFCRCuWjShRY66mJkU2Hv58eygJWDJS1MNa2Ge5M1WjUkGHuLqHkweDxwZZU42d16v94mP"; const QString donationAddress = "Wo3MWeKwtA918DU4c69hVSNgejdWFCRCuWjShRY66mJkU2Hv58eygJWDJS1MNa2Ge5M1WjUkGHuLqHkweDxwZZU42d16v94mP";
@ -50,6 +55,7 @@ public:
QString coinName = "wownero"; QString coinName = "wownero";
bool isTorSocks = false; bool isTorSocks = false;
QString pathGenericData;
QString homeDir; QString homeDir;
QString accountName; QString accountName;
QString configRoot; QString configRoot;
@ -215,6 +221,11 @@ private:
WalletKeysFilesModel *m_walletKeysFilesModel; WalletKeysFilesModel *m_walletKeysFilesModel;
const int m_donationBoundary = 15; const int m_donationBoundary = 15;
QTimer m_storeTimer; QTimer m_storeTimer;
void setupPathsUnix();
void setupPathsWindows();
void setupPathsAndroid();
void setupPathsTails();
}; };
#endif //WOWLET_APPCONTEXT_H #endif //WOWLET_APPCONTEXT_H

@ -1,6 +1,8 @@
// SPDX-License-Identifier: BSD-3-Clause // SPDX-License-Identifier: BSD-3-Clause
// Copyright (c) 2014-2021, The Monero Project. // Copyright (c) 2014-2021, The Monero Project.
#include <thread>
#include "Wallet.h" #include "Wallet.h"
#include "TransactionHistory.h" #include "TransactionHistory.h"

@ -14,6 +14,12 @@
#include "vr/main.h" #include "vr/main.h"
#endif #endif
#ifdef HAS_ANDROID_DEBUG
#include "mobile/main.h"
#elif HAS_ANDROID
#include "mobile/main.h"
#endif
#if defined(Q_OS_WIN) #if defined(Q_OS_WIN)
#include <windows.h> #include <windows.h>
#endif #endif
@ -44,6 +50,10 @@ if (AttachConsole(ATTACH_PARENT_PROCESS)) {
argv_ << QString::fromStdString(argv[i]); argv_ << QString::fromStdString(argv[i]);
} }
QCoreApplication::setApplicationName("wowlet");
QCoreApplication::setOrganizationDomain("wownero.org");
QCoreApplication::setOrganizationName("wownero.org");
QCommandLineParser parser; QCommandLineParser parser;
parser.setApplicationDescription("wowlet"); parser.setApplicationDescription("wowlet");
parser.addHelpOption(); parser.addHelpOption();
@ -91,9 +101,12 @@ if (AttachConsole(ATTACH_PARENT_PROCESS)) {
QCommandLineOption openVROption(QStringList() << "openvr", "Start Wowlet OpenVR"); QCommandLineOption openVROption(QStringList() << "openvr", "Start Wowlet OpenVR");
parser.addOption(openVROption); parser.addOption(openVROption);
QCommandLineOption openVRDebugOption(QStringList() << "openvr-debug", "Start the Wowlet VR interface without initializing OpenVR - for debugging purposes."); QCommandLineOption openVRDebugOption(QStringList() << "openvr-debug", "Start the Wowlet VR interface without initializing OpenVR - for debugging purposes. Requires -DOPENVR=ON CMake definition.");
parser.addOption(openVRDebugOption); parser.addOption(openVRDebugOption);
QCommandLineOption androidDebugOption(QStringList() << "android-debug", "Start the Android interface without actually running on Android - for debugging purposes. Requires -DANDROID_DEBUG=ON CMake definition.");
parser.addOption(androidDebugOption);
auto parsed = parser.parse(argv_); auto parsed = parser.parse(argv_);
if(!parsed) { if(!parsed) {
qCritical() << parser.errorText(); qCritical() << parser.errorText();
@ -111,13 +124,29 @@ if (AttachConsole(ATTACH_PARENT_PROCESS)) {
bool backgroundAddressEnabled = parser.isSet(backgroundOption); bool backgroundAddressEnabled = parser.isSet(backgroundOption);
bool openVREnabled = parser.isSet(openVROption); bool openVREnabled = parser.isSet(openVROption);
bool cliMode = exportContacts || exportTxHistory || backgroundAddressEnabled; bool cliMode = exportContacts || exportTxHistory || backgroundAddressEnabled;
bool androidDebug = parser.isSet(androidDebugOption);
bool android = false;
#ifdef __ANDROID__
android = true;
#endif
qRegisterMetaType<QVector<QString>>(); qRegisterMetaType<QVector<QString>>();
#ifdef HAS_QML #ifdef HAS_QML
qputenv("QML_DISABLE_DISK_CACHE", "1"); qputenv("QML_DISABLE_DISK_CACHE", "1");
#endif #endif
if(android || androidDebug) {
#ifndef HAS_QML
qCritical() << "Wowlet compiled without QML support. Try -DQML=ON";
return 1;
#endif
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication mobile_app(argc, argv);
auto *ctx = new AppContext(&parser);
auto *mobile = new mobile::Mobile(ctx, &parser, &mobile_app);
return mobile_app.exec();
}
if(openVREnabled) { if(openVREnabled) {
#ifdef HAS_OPENVR #ifdef HAS_OPENVR
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
@ -138,9 +167,6 @@ if (AttachConsole(ATTACH_PARENT_PROCESS)) {
if(cliMode) { if(cliMode) {
auto *ctx = new AppContext(&parser); auto *ctx = new AppContext(&parser);
QCoreApplication cli_app(argc, argv); QCoreApplication cli_app(argc, argv);
QCoreApplication::setApplicationName("wowlet");
QCoreApplication::setOrganizationDomain("wownero.org");
QCoreApplication::setOrganizationName("wownero.org");
ctx->applicationPath = QString(argv[0]); ctx->applicationPath = QString(argv[0]);
ctx->isDebug = debugMode; ctx->isDebug = debugMode;
@ -191,10 +217,6 @@ if (AttachConsole(ATTACH_PARENT_PROCESS)) {
QApplication app(argc, argv); QApplication app(argc, argv);
QApplication::setApplicationName("wowlet");
QApplication::setOrganizationDomain("wownero.org");
QApplication::setOrganizationName("wownero.org");
parser.process(app); // Parse again for --help and --version parser.process(app); // Parse again for --help and --version
if(!quiet) { if(!quiet) {
@ -204,8 +226,10 @@ if (AttachConsole(ATTACH_PARENT_PROCESS)) {
if (stagenet) info["Mode"] = "Stagenet"; if (stagenet) info["Mode"] = "Stagenet";
else if (testnet) info["Mode"] = "Testnet"; else if (testnet) info["Mode"] = "Testnet";
else info["Mode"] = "Mainnet"; else info["Mode"] = "Mainnet";
#ifndef QT_NO_SSL
info["SSL"] = QSslSocket::sslLibraryVersionString(); info["SSL"] = QSslSocket::sslLibraryVersionString();
info["SSL build"] = QSslSocket::sslLibraryBuildVersionString(); info["SSL build"] = QSslSocket::sslLibraryBuildVersionString();
#endif
for (const auto &k: info.keys()) for (const auto &k: info.keys())
qWarning().nospace().noquote() << QString("%1: %2").arg(k).arg(info[k]); qWarning().nospace().noquote() << QString("%1: %2").arg(k).arg(info[k]);
} }

@ -256,11 +256,14 @@ QStandardItem *Utils::qStandardItem(const QString& text, QFont &font) {
} }
QString Utils::getUnixAccountName() { QString Utils::getUnixAccountName() {
#ifdef __ANDROID__
return "";
#endif
QString accountName = qgetenv("USER"); // mac/linux QString accountName = qgetenv("USER"); // mac/linux
if (accountName.isEmpty()) if (accountName.isEmpty())
accountName = qgetenv("USERNAME"); // Windows accountName = qgetenv("USERNAME"); // Windows
if (accountName.isEmpty()) if (accountName.isEmpty())
throw std::runtime_error("Could derive system account name from env vars: USER or USERNAME"); throw std::runtime_error("Could not derive system account name from env vars: USER or USERNAME");
return accountName; return accountName;
} }
@ -454,4 +457,27 @@ QTextCharFormat Utils::addressTextFormat(const SubaddressIndex &index) {
return rec; return rec;
} }
return QTextCharFormat(); return QTextCharFormat();
} }
#ifdef __ANDROID__
bool Utils::androidAskPermissions(const QVector<QString> &permissions) {
bool rtn = true;
if(QtAndroid::androidSdkVersion() >= 23) {
for(const QString &permission : permissions) {
auto result = QtAndroid::checkPermission(permission);
if(result != QtAndroid::PermissionResult::Granted) {
auto resultHash = QtAndroid::requestPermissionsSync(QStringList({permission}));
if(resultHash[permission] != QtAndroid::PermissionResult::Granted) {
qDebug() << "Fail to get permission" << permission;
rtn = false;
} else {
qDebug() << "Permission" << permission << "granted!";
}
} else {
qDebug() << "Permission" << permission << "already granted!";
}
}
}
return rtn;
}
#endif

@ -8,6 +8,9 @@
#include <QStandardItemModel> #include <QStandardItemModel>
#include <QApplication> #include <QApplication>
#include <QTextCharFormat> #include <QTextCharFormat>
#ifdef __ANDROID__
#include <QtAndroid>
#endif
#include <monero_seed/monero_seed.hpp> #include <monero_seed/monero_seed.hpp>
@ -80,10 +83,13 @@ public:
static QTextCharFormat addressTextFormat(const SubaddressIndex &index); static QTextCharFormat addressTextFormat(const SubaddressIndex &index);
template<typename QEnum> template<typename QEnum>
static QString QtEnumToString (const QEnum value) static QString QtEnumToString (const QEnum value) {
{
return QString::fromStdString(std::string(QMetaEnum::fromType<QEnum>().valueToKey(value))); return QString::fromStdString(std::string(QMetaEnum::fromType<QEnum>().valueToKey(value)));
} }
#ifdef __ANDROID__
static bool androidAskPermissions(const QVector<QString> &permissions);
#endif
}; };
class AppContext; // forward declaration class AppContext; // forward declaration

Loading…
Cancel
Save