From eea982973ae78edfef68eade955071b766238319 Mon Sep 17 00:00:00 2001 From: D33r-Gee Date: Tue, 12 Mar 2024 11:05:56 -0700 Subject: [PATCH 01/11] qml: bitcoin.h and cpp, added new class to address setting custom datadir --- src/qml/bitcoin.cpp | 270 +++++++++++++++++++++++++++++++------------- src/qml/bitcoin.h | 73 +++++++++++- 2 files changed, 261 insertions(+), 82 deletions(-) diff --git a/src/qml/bitcoin.cpp b/src/qml/bitcoin.cpp index d87386cbd5..dc5597a03f 100644 --- a/src/qml/bitcoin.cpp +++ b/src/qml/bitcoin.cpp @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -31,10 +32,13 @@ #include #include #include +#include #include #include #include +#include + #include #include #include @@ -45,6 +49,7 @@ #include #include #include +#include #include #include #include @@ -140,8 +145,173 @@ void setupChainQSettings(QGuiApplication* app, QString chain) app->setApplicationName(QAPP_APP_NAME_REGTEST); } } + +// added this function to set custom data directory when starting node +bool setCustomDataDir(QString strDataDir) +{ + if(fs::exists(GUIUtil::QStringToPath(strDataDir))){ + gArgs.SoftSetArg("-datadir", fs::PathToString(GUIUtil::QStringToPath(strDataDir))); + gArgs.ClearPathCache(); + return true; + } else { + return false; + } +} } // namespace +BitcoinQmlApplication::BitcoinQmlApplication(int& argc, char* argv[]) + : QGuiApplication(argc, argv), m_gArgs(&gArgs), m_argc(argc), m_argv(argv) +{ +} + +bool BitcoinQmlApplication::createNode(QQmlApplicationEngine& engine, int& argc, char* argv[], ArgsManager& gArgs) +{ + m_engine = &engine; + + InitLogging(gArgs); + InitParameterInteraction(gArgs); + + m_init = interfaces::MakeGuiInit(argc, argv); + + m_node = m_init->makeNode(); + m_chain = m_init->makeChain(); + + if (!m_node->baseInitialize()) { + // A dialog with detailed error will have been shown by InitError(). + return EXIT_FAILURE; + } + + m_handler_message_box.disconnect(); + + m_node_model = new NodeModel{*m_node}; + m_executor = new InitExecutor{*m_node}; + QObject::connect(m_node_model, &NodeModel::requestedInitialize, m_executor, &InitExecutor::initialize); + QObject::connect(m_node_model, &NodeModel::requestedShutdown, m_executor, &InitExecutor::shutdown); + QObject::connect(m_executor, &InitExecutor::initializeResult, m_node_model, &NodeModel::initializeResult); + QObject::connect(m_executor, &InitExecutor::shutdownResult, qGuiApp, &QGuiApplication::quit, Qt::QueuedConnection); + + // Added this signal to account for NodeModel being initialized later + QObject::connect(m_onboarding_model, &OnboardingModel::requestedShutdown, m_executor, &InitExecutor::shutdown); + + m_network_traffic_tower = new NetworkTrafficTower{*m_node_model}; +#ifdef __ANDROID__ + AndroidNotifier android_notifier{m_node_model}; +#endif + + m_chain_model = new ChainModel{*m_chain}; + m_chain_model->setCurrentNetworkName(QString::fromStdString(ChainTypeToString(m_gArgs->GetChainType()))); + setupChainQSettings(this, m_chain_model->currentNetworkName()); + + QObject::connect(m_node_model, &NodeModel::setTimeRatioList, m_chain_model, &ChainModel::setTimeRatioList); + QObject::connect(m_node_model, &NodeModel::setTimeRatioListInitial, m_chain_model, &ChainModel::setTimeRatioListInitial); + + qGuiApp->setQuitOnLastWindowClosed(false); + QObject::connect(qGuiApp, &QGuiApplication::lastWindowClosed, [&] { + m_node->startShutdown(); + }); + + m_peer_model = new PeerTableModel{*m_node, nullptr}; + m_peer_model_sort_proxy = new PeerListSortProxy{nullptr}; + m_peer_model_sort_proxy->setSourceModel(m_peer_model); + + m_engine->rootContext()->setContextProperty("networkTrafficTower", m_network_traffic_tower); + m_engine->rootContext()->setContextProperty("nodeModel", m_node_model); + m_engine->rootContext()->setContextProperty("chainModel", m_chain_model); + m_engine->rootContext()->setContextProperty("peerTableModel", m_peer_model); + m_engine->rootContext()->setContextProperty("peerListModelProxy", m_peer_model_sort_proxy); + m_engine->rootContext()->setContextProperty("onboardingModel", m_onboarding_model); + + m_options_model = new OptionsQmlModel{*m_node, !m_isOnboarding}; + m_engine->rootContext()->setContextProperty("optionsModel", m_options_model); + + m_options_model->setPrune(m_onboarding_model->prune()); + m_options_model->setPruneSizeGB(m_onboarding_model->pruneSizeGB()); + m_options_model->setDbcacheSizeMiB(m_onboarding_model->dbcacheSizeMiB()); + m_options_model->setListen(m_onboarding_model->listen()); + m_options_model->setNatpmp(m_onboarding_model->natpmp()); + m_options_model->setScriptThreads(m_onboarding_model->scriptThreads()); + m_options_model->setServer(m_onboarding_model->server()); + m_options_model->setUpnp(m_onboarding_model->upnp()); + + m_node_model->startShutdownPolling(); + + return true; +} + +bool BitcoinQmlApplication::startNode(QQmlApplicationEngine& engine, int& argc, char* argv[]) +{ + m_engine = &engine; + QScopedPointer network_style{NetworkStyle::instantiate(Params().GetChainType())}; + assert(!network_style.isNull()); + m_engine->addImageProvider(QStringLiteral("images"), new ImageProvider{network_style.data()}); + + m_onboarding_model = new OnboardingModel; + m_engine->rootContext()->setContextProperty("onboardingModel", m_onboarding_model); + + createNode(*m_engine, argc, argv, gArgs); + + #ifdef __ANDROID__ + AppMode app_mode(AppMode::MOBILE); +#else + AppMode app_mode(AppMode::DESKTOP); +#endif // __ANDROID__ + + qmlRegisterSingletonInstance("org.bitcoincore.qt", 1, 0, "AppMode", &app_mode); + qmlRegisterType("org.bitcoincore.qt", 1, 0, "BlockClockDial"); + qmlRegisterType("org.bitcoincore.qt", 1, 0, "LineGraph"); + + m_engine->load(QUrl(QStringLiteral("qrc:///qml/pages/main.qml"))); + if (m_engine->rootObjects().isEmpty()) { + return EXIT_FAILURE; + } + + auto window = qobject_cast(engine.rootObjects().first()); + if (!window) { + return EXIT_FAILURE; + } + + // Install qDebug() message handler to route to debug.log + qInstallMessageHandler(DebugMessageHandler); + + qInfo() << "Graphics API in use:" << QmlUtil::GraphicsApi(window); + + return qGuiApp->exec(); +} + +bool BitcoinQmlApplication::startOnboarding(QQmlApplicationEngine& engine, ArgsManager& gArgs) +{ + m_engine = &engine; + QScopedPointer network_style{NetworkStyle::instantiate(Params().GetChainType())}; + assert(!network_style.isNull()); + m_engine->addImageProvider(QStringLiteral("images"), new ImageProvider{network_style.data()}); + + m_onboarding_model = new OnboardingModel; + m_engine->rootContext()->setContextProperty("onboardingModel", m_onboarding_model); + + QObject::connect(m_onboarding_model, &OnboardingModel::onboardingFinished, this, &BitcoinQmlApplication::startNodeAndTransitionSlot); + + #ifdef __ANDROID__ + AppMode app_mode(AppMode::MOBILE); +#else + AppMode app_mode(AppMode::DESKTOP); +#endif // __ANDROID__ + + qmlRegisterSingletonInstance("org.bitcoincore.qt", 1, 0, "AppMode", &app_mode); + qmlRegisterType("org.bitcoincore.qt", 1, 0, "BlockClockDial"); + qmlRegisterType("org.bitcoincore.qt", 1, 0, "LineGraph"); + + m_engine->load(QUrl(QStringLiteral("qrc:///qml/pages/main.qml"))); + if (m_engine->rootObjects().isEmpty()) { + return EXIT_FAILURE; + } + + auto window = qobject_cast(m_engine->rootObjects().first()); + if (!window) { + return EXIT_FAILURE; + } + + return qGuiApp->exec(); +} int QmlGuiMain(int argc, char* argv[]) { @@ -155,11 +325,10 @@ int QmlGuiMain(int argc, char* argv[]) QGuiApplication::setAttribute(Qt::AA_EnableHighDpiScaling); QGuiApplication::styleHints()->setTabFocusBehavior(Qt::TabFocusAllControls); - QGuiApplication app(argc, argv); - auto handler_message_box = ::uiInterface.ThreadSafeMessageBox_connect(InitErrorMessageBox); + BitcoinQmlApplication app(argc, argv); - std::unique_ptr init = interfaces::MakeGuiInit(argc, argv); + app.setHandlerMessageBox(::uiInterface.ThreadSafeMessageBox_connect(InitErrorMessageBox)); SetupEnvironment(); util::ThreadSetInternalName("main"); @@ -171,6 +340,10 @@ int QmlGuiMain(int argc, char* argv[]) app.setOrganizationDomain(QAPP_ORG_DOMAIN); app.setApplicationName(QAPP_APP_NAME_DEFAULT); + QSettings settings; + QString dataDir; + dataDir = settings.value("strDataDir", dataDir).toString(); + /// Parse command-line options. We do this after qt in order to show an error if there are problems parsing these. SetupServerArgs(gArgs); SetupUIArgs(gArgs); @@ -208,8 +381,10 @@ int QmlGuiMain(int argc, char* argv[]) return EXIT_FAILURE; } + // Added the or condition to check if the data directory exists QVariant need_onboarding(true); - if (gArgs.IsArgSet("-datadir") && !gArgs.GetPathArg("-datadir").empty()) { + if ((gArgs.IsArgSet("-datadir") && !gArgs.GetPathArg("-datadir").empty()) || fs::exists(GUIUtil::QStringToPath(dataDir))) { + setCustomDataDir(dataDir); need_onboarding.setValue(false); } else if (ConfigurationFileExists(gArgs)) { need_onboarding.setValue(false); @@ -222,93 +397,26 @@ int QmlGuiMain(int argc, char* argv[]) // Default printtoconsole to false for the GUI. GUI programs should not // print to the console unnecessarily. gArgs.SoftSetBoolArg("-printtoconsole", false); - InitLogging(gArgs); - InitParameterInteraction(gArgs); GUIUtil::LogQtInfo(); - - std::unique_ptr node = init->makeNode(); - std::unique_ptr chain = init->makeChain(); - if (!node->baseInitialize()) { - // A dialog with detailed error will have been shown by InitError(). - return EXIT_FAILURE; - } - - handler_message_box.disconnect(); - - NodeModel node_model{*node}; - InitExecutor init_executor{*node}; - QObject::connect(&node_model, &NodeModel::requestedInitialize, &init_executor, &InitExecutor::initialize); - QObject::connect(&node_model, &NodeModel::requestedShutdown, &init_executor, &InitExecutor::shutdown); - QObject::connect(&init_executor, &InitExecutor::initializeResult, &node_model, &NodeModel::initializeResult); - QObject::connect(&init_executor, &InitExecutor::shutdownResult, qGuiApp, &QGuiApplication::quit, Qt::QueuedConnection); - // QObject::connect(&init_executor, &InitExecutor::runawayException, &node_model, &NodeModel::handleRunawayException); - - NetworkTrafficTower network_traffic_tower{node_model}; -#ifdef __ANDROID__ - AndroidNotifier android_notifier{node_model}; -#endif - - ChainModel chain_model{*chain}; - chain_model.setCurrentNetworkName(QString::fromStdString(ChainTypeToString(gArgs.GetChainType()))); - setupChainQSettings(&app, chain_model.currentNetworkName()); - - QObject::connect(&node_model, &NodeModel::setTimeRatioList, &chain_model, &ChainModel::setTimeRatioList); - QObject::connect(&node_model, &NodeModel::setTimeRatioListInitial, &chain_model, &ChainModel::setTimeRatioListInitial); - - qGuiApp->setQuitOnLastWindowClosed(false); - QObject::connect(qGuiApp, &QGuiApplication::lastWindowClosed, [&] { - node->startShutdown(); - }); - - PeerTableModel peer_model{*node, nullptr}; - PeerListSortProxy peer_model_sort_proxy{nullptr}; - peer_model_sort_proxy.setSourceModel(&peer_model); - GUIUtil::LoadFont(":/fonts/inter/regular"); GUIUtil::LoadFont(":/fonts/inter/semibold"); QQmlApplicationEngine engine; - QScopedPointer network_style{NetworkStyle::instantiate(Params().GetChainType())}; - assert(!network_style.isNull()); - engine.addImageProvider(QStringLiteral("images"), new ImageProvider{network_style.data()}); - - engine.rootContext()->setContextProperty("networkTrafficTower", &network_traffic_tower); - engine.rootContext()->setContextProperty("nodeModel", &node_model); - engine.rootContext()->setContextProperty("chainModel", &chain_model); - engine.rootContext()->setContextProperty("peerTableModel", &peer_model); - engine.rootContext()->setContextProperty("peerListModelProxy", &peer_model_sort_proxy); - - OptionsQmlModel options_model{*node}; - engine.rootContext()->setContextProperty("optionsModel", &options_model); - engine.rootContext()->setContextProperty("needOnboarding", need_onboarding); -#ifdef __ANDROID__ - AppMode app_mode(AppMode::MOBILE); -#else - AppMode app_mode(AppMode::DESKTOP); -#endif // __ANDROID__ - qmlRegisterSingletonInstance("org.bitcoincore.qt", 1, 0, "AppMode", &app_mode); - qmlRegisterType("org.bitcoincore.qt", 1, 0, "BlockClockDial"); - qmlRegisterType("org.bitcoincore.qt", 1, 0, "LineGraph"); + OnboardingModel onboarding_model; + app.setOnboardingModel(&onboarding_model); + engine.rootContext()->setContextProperty("onboardingModel", &onboarding_model); - engine.load(QUrl(QStringLiteral("qrc:///qml/pages/main.qml"))); - if (engine.rootObjects().isEmpty()) { - return EXIT_FAILURE; - } + app.setNeedsOnboarding(need_onboarding.toBool()); - auto window = qobject_cast(engine.rootObjects().first()); - if (!window) { - return EXIT_FAILURE; + if(need_onboarding.toBool()) { + app.startOnboarding(engine, gArgs); + } else { + app.startNode(engine, argc, argv); } - // Install qDebug() message handler to route to debug.log - qInstallMessageHandler(DebugMessageHandler); - - qInfo() << "Graphics API in use:" << QmlUtil::GraphicsApi(window); - - node_model.startShutdownPolling(); - return qGuiApp->exec(); -} + return 0; +} \ No newline at end of file diff --git a/src/qml/bitcoin.h b/src/qml/bitcoin.h index 77e7102e6d..6a8d2d365e 100644 --- a/src/qml/bitcoin.h +++ b/src/qml/bitcoin.h @@ -5,6 +5,77 @@ #ifndef BITCOIN_QML_BITCOIN_H #define BITCOIN_QML_BITCOIN_H +#include +#include + +#include +#include + +#include +#include +#include + +#include + +#include +#include +#include +#include +#include + +class NodeModel; +class ChainModel; +class OptionsQmlModel; +class NetworkTrafficTower; +class PeerTableModel; +class PeerListSortProxy; +class OnboardingModel; + +class BitcoinQmlApplication: public QGuiApplication +{ + Q_OBJECT +public: + explicit BitcoinQmlApplication(int& argc, char* argv[]); + + void setHandlerMessageBox(boost::signals2::connection handler) { m_handler_message_box = handler;} + void setEngine(QQmlApplicationEngine* engine) { m_engine = engine;} + void setOnboardingModel(OnboardingModel* model) {m_onboarding_model = model;} + bool createNode(QQmlApplicationEngine& engine, int& argc, char* argv[], ArgsManager& gArgs); + bool startNode(QQmlApplicationEngine& engine, int& argc, char* argv[]); + bool startOnboarding(QQmlApplicationEngine& engine, ArgsManager& gArgs); + void setNeedsOnboarding(bool needsOnboarding) {m_isOnboarding = needsOnboarding;} + + interfaces::Node& node() const { assert(m_node); return *m_node; } + +public Q_SLOTS: + + void startNodeAndTransitionSlot() { createNode(*m_engine, m_argc, m_argv, *m_gArgs); } + +private: + QQmlApplicationEngine* m_engine; + ArgsManager* m_gArgs; + QQuickWindow* m_window; + boost::signals2::connection m_handler_message_box; + std::unique_ptr m_init; + std::unique_ptr m_node; + std::unique_ptr m_chain; + NodeModel* m_node_model{nullptr}; + InitExecutor* m_executor{nullptr}; + ChainModel* m_chain_model{nullptr}; + OptionsQmlModel* m_options_model{nullptr}; + OnboardingModel* m_onboarding_model{nullptr}; + int m_argc; + char** m_argv; + bool m_prune; + int m_prune_size_gb; + NetworkTrafficTower* m_network_traffic_tower; + PeerTableModel* m_peer_model; + PeerListSortProxy* m_peer_model_sort_proxy; + QThread* m_introThread; + bool m_isOnboarding; +}; + + int QmlGuiMain(int argc, char* argv[]); -#endif // BITCOIN_QML_BITCOIN_H +#endif // BITCOIN_QML_BITCOIN_H \ No newline at end of file From 590d0c241a3dc2cc72545c168bbf07a6618a7b02 Mon Sep 17 00:00:00 2001 From: Johnny9 Date: Tue, 12 Mar 2024 11:06:53 -0700 Subject: [PATCH 02/11] qml: integrated Johny9's Only write options after onboarding #284 --- src/qml/models/options_model.cpp | 62 +++++++++++++++++++++++++++----- src/qml/models/options_model.h | 4 ++- 2 files changed, 56 insertions(+), 10 deletions(-) diff --git a/src/qml/models/options_model.cpp b/src/qml/models/options_model.cpp index 61459dbf6c..ed8805bc95 100644 --- a/src/qml/models/options_model.cpp +++ b/src/qml/models/options_model.cpp @@ -16,8 +16,9 @@ #include -OptionsQmlModel::OptionsQmlModel(interfaces::Node& node) +OptionsQmlModel::OptionsQmlModel(interfaces::Node& node, bool is_onboarded) : m_node{node} + , m_onboarded{is_onboarded} { m_dbcache_size_mib = SettingToInt(m_node.getPersistentSetting("dbcache"), nDefaultDbCache); @@ -32,7 +33,9 @@ void OptionsQmlModel::setDbcacheSizeMiB(int new_dbcache_size_mib) { if (new_dbcache_size_mib != m_dbcache_size_mib) { m_dbcache_size_mib = new_dbcache_size_mib; - m_node.updateRwSetting("dbcache", new_dbcache_size_mib); + if (m_onboarded) { + m_node.updateRwSetting("dbcache", new_dbcache_size_mib); + } Q_EMIT dbcacheSizeMiBChanged(new_dbcache_size_mib); } } @@ -41,7 +44,9 @@ void OptionsQmlModel::setListen(bool new_listen) { if (new_listen != m_listen) { m_listen = new_listen; - m_node.updateRwSetting("listen", new_listen); + if (m_onboarded) { + m_node.updateRwSetting("listen", new_listen); + } Q_EMIT listenChanged(new_listen); } } @@ -50,7 +55,9 @@ void OptionsQmlModel::setNatpmp(bool new_natpmp) { if (new_natpmp != m_natpmp) { m_natpmp = new_natpmp; - m_node.updateRwSetting("natpmp", new_natpmp); + if (m_onboarded) { + m_node.updateRwSetting("natpmp", new_natpmp); + } Q_EMIT natpmpChanged(new_natpmp); } } @@ -59,7 +66,9 @@ void OptionsQmlModel::setPrune(bool new_prune) { if (new_prune != m_prune) { m_prune = new_prune; - m_node.updateRwSetting("prune", pruneSetting()); + if (m_onboarded) { + m_node.updateRwSetting("prune", pruneSetting()); + } Q_EMIT pruneChanged(new_prune); } } @@ -68,7 +77,9 @@ void OptionsQmlModel::setPruneSizeGB(int new_prune_size_gb) { if (new_prune_size_gb != m_prune_size_gb) { m_prune_size_gb = new_prune_size_gb; - m_node.updateRwSetting("prune", pruneSetting()); + if (m_onboarded) { + m_node.updateRwSetting("prune", pruneSetting()); + } Q_EMIT pruneSizeGBChanged(new_prune_size_gb); } } @@ -77,7 +88,9 @@ void OptionsQmlModel::setScriptThreads(int new_script_threads) { if (new_script_threads != m_script_threads) { m_script_threads = new_script_threads; - m_node.updateRwSetting("par", new_script_threads); + if (m_onboarded) { + m_node.updateRwSetting("par", new_script_threads); + } Q_EMIT scriptThreadsChanged(new_script_threads); } } @@ -86,7 +99,9 @@ void OptionsQmlModel::setServer(bool new_server) { if (new_server != m_server) { m_server = new_server; - m_node.updateRwSetting("server", new_server); + if (m_onboarded) { + m_node.updateRwSetting("server", new_server); + } Q_EMIT serverChanged(new_server); } } @@ -95,7 +110,9 @@ void OptionsQmlModel::setUpnp(bool new_upnp) { if (new_upnp != m_upnp) { m_upnp = new_upnp; - m_node.updateRwSetting("upnp", new_upnp); + if (m_onboarded) { + m_node.updateRwSetting("upnp", new_upnp); + } Q_EMIT upnpChanged(new_upnp); } } @@ -105,3 +122,30 @@ common::SettingsValue OptionsQmlModel::pruneSetting() const assert(!m_prune || m_prune_size_gb >= 1); return m_prune ? PruneGBtoMiB(m_prune_size_gb) : 0; } + +void OptionsQmlModel::onboard() +{ + m_node.resetSettings(); + if (m_dbcache_size_mib != nDefaultDbCache) { + m_node.updateRwSetting("dbcache", m_dbcache_size_mib); + } + if (m_listen) { + m_node.updateRwSetting("listen", m_listen); + } + if (m_natpmp) { + m_node.updateRwSetting("natpmp", m_natpmp); + } + if (m_prune) { + m_node.updateRwSetting("prune", pruneSetting()); + } + if (m_script_threads != DEFAULT_SCRIPTCHECK_THREADS) { + m_node.updateRwSetting("par", m_script_threads); + } + if (m_server) { + m_node.updateRwSetting("server", m_server); + } + if (m_upnp) { + m_node.updateRwSetting("upnp", m_upnp); + } + m_onboarded = true; +} diff --git a/src/qml/models/options_model.h b/src/qml/models/options_model.h index b67aa8c76c..b7b03955c0 100644 --- a/src/qml/models/options_model.h +++ b/src/qml/models/options_model.h @@ -34,7 +34,7 @@ class OptionsQmlModel : public QObject Q_PROPERTY(bool upnp READ upnp WRITE setUpnp NOTIFY upnpChanged) public: - explicit OptionsQmlModel(interfaces::Node& node); + explicit OptionsQmlModel(interfaces::Node& node, bool is_onboarded); int dbcacheSizeMiB() const { return m_dbcache_size_mib; } void setDbcacheSizeMiB(int new_dbcache_size_mib); @@ -56,6 +56,7 @@ class OptionsQmlModel : public QObject void setServer(bool new_server); bool upnp() const { return m_upnp; } void setUpnp(bool new_upnp); + Q_INVOKABLE void onboard(); Q_SIGNALS: void dbcacheSizeMiBChanged(int new_dbcache_size_mib); @@ -69,6 +70,7 @@ class OptionsQmlModel : public QObject private: interfaces::Node& m_node; + bool m_onboarded; // Properties that are exposed to QML. int m_dbcache_size_mib; From 19253787ee49981013ac00a9d51a9b60432b1981 Mon Sep 17 00:00:00 2001 From: D33r-Gee Date: Tue, 12 Mar 2024 11:07:40 -0700 Subject: [PATCH 03/11] qml: added onboardingmodel files to manage the onboarding process especially custom datadir setting and placeholders for optionsModel --- src/qml/models/onboardingmodel.cpp | 175 +++++++++++++++++++++++++++++ src/qml/models/onboardingmodel.h | 119 ++++++++++++++++++++ 2 files changed, 294 insertions(+) create mode 100644 src/qml/models/onboardingmodel.cpp create mode 100644 src/qml/models/onboardingmodel.h diff --git a/src/qml/models/onboardingmodel.cpp b/src/qml/models/onboardingmodel.cpp new file mode 100644 index 0000000000..6494ace560 --- /dev/null +++ b/src/qml/models/onboardingmodel.cpp @@ -0,0 +1,175 @@ +// Copyright (c) 2024 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +OnboardingModel::OnboardingModel() +{ + // This is to reset the data directory to the default + gArgs.LockSettings([&](common::Settings& s) { m_previous_settings = s; }); + + // Initiate prune settings + m_prune = false; +} + +void OnboardingModel::setDbcacheSizeMiB(int new_dbcache_size_mib) +{ + if (new_dbcache_size_mib != m_dbcache_size_mib) { + m_dbcache_size_mib = new_dbcache_size_mib; + // // qDebug () << "Dbcache Size Set: " << new_dbcache_size_mib; + Q_EMIT dbcacheSizeMiBChanged(new_dbcache_size_mib); + + } +} + +void OnboardingModel::setListen(bool new_listen) +{ + if (new_listen != m_listen) { + m_listen = new_listen; + // qDebug () << "Listen Set: " << new_listen; + Q_EMIT listenChanged(new_listen); + } +} + +void OnboardingModel::setNatpmp(bool new_natpmp) +{ + if (new_natpmp != m_natpmp) { + m_natpmp = new_natpmp; + // qDebug () << "Natpmp Set: " << new_natpmp; + Q_EMIT natpmpChanged(new_natpmp); + } +} + +void OnboardingModel::setPrune(bool new_prune) +{ + if (new_prune != m_prune) { + m_prune = new_prune; + // qDebug () << "Prune Set: " << new_prune; + Q_EMIT pruneChanged(new_prune); + } +} + +void OnboardingModel::setPruneSizeGB(int new_prune_size_gb) +{ + if (new_prune_size_gb != m_prune_size_gb) { + m_prune_size_gb = new_prune_size_gb; + // qDebug () << "Prune Size Set: " << new_prune_size_gb; + Q_EMIT pruneSizeGBChanged(new_prune_size_gb); + } +} + +void OnboardingModel::setScriptThreads(int new_script_threads) +{ + if (new_script_threads != m_script_threads) { + m_script_threads = new_script_threads; + // qDebug () << "Script Threads Set: " << new_script_threads; + Q_EMIT scriptThreadsChanged(new_script_threads); + } +} + +void OnboardingModel::setServer(bool new_server) +{ + if (new_server != m_server) { + m_server = new_server; + // qDebug () << "Server Set: " << new_server; + Q_EMIT serverChanged(new_server); + } +} + +void OnboardingModel::setUpnp(bool new_upnp) +{ + if (new_upnp != m_upnp) { + m_upnp = new_upnp; + // qDebug () << "Upnp Set: " << new_upnp; + Q_EMIT upnpChanged(new_upnp); + } +} + +void OnboardingModel::requestShutdown() +{ + Q_EMIT requestedShutdown(); +} + +QString PathToQString(const fs::path &path) +{ + return QString::fromStdString(path.u8string()); +} + +QString OnboardingModel::getDefaultDataDirString() +{ + return PathToQString(GetDefaultDataDir()); +} + + +QUrl OnboardingModel::getDefaultDataDirectory() +{ + QString path = getDefaultDataDirString(); + return QUrl::fromLocalFile(path); +} + +void OnboardingModel::defaultReset() +{ + QSettings settings; + // Save the strDataDir setting + QString dataDir = GUIUtil::getDefaultDataDirectory(); + + // Remove all entries from our QSettings object + settings.clear(); + + // Set strDataDir + settings.setValue("strDataDir", dataDir); + + // Set that this was reset + settings.setValue("fReset", true); + + // qDebug() << "Resetting data directory: " << dataDir; + + gArgs.LockSettings([&](common::Settings& s) { s = m_previous_settings; }); + gArgs.ClearPathCache(); + // for debug purposes checking the cached datadir + // const fs::path& cached_dataDir = gArgs.GetDataDirNet(); + // qDebug() << "Cached Data directory: " << QString::fromStdString(cached_dataDir); +} + +void OnboardingModel::setCustomDataDirArgs(QString path) +{ + if (!path.isEmpty()) { + #ifdef __ANDROID__ + QString uri = path; + QSettings settings; + QString originalPrefix = "content://com.android.externalstorage.documents/tree/primary%3A"; + QString newPrefix = "/storage/self/primary/"; + QString path = uri.replace(originalPrefix, newPrefix); + #else + QSettings settings; + path = QUrl(path).toLocalFile(); + #endif // __ANDROID__ + try{ + if (TryCreateDirectories(GUIUtil::QStringToPath(path))) { + // qDebug() << "Created data directory: " << path; + TryCreateDirectories(GUIUtil::QStringToPath(path) / "wallets"); + } + } catch (const std::exception& e) { + qDebug() << "Error creating data directory: " << e.what(); + } + settings.setValue("strDataDir", path); + if(path != GUIUtil::getDefaultDataDirectory()) { + gArgs.SoftSetArg("-datadir", fs::PathToString(GUIUtil::QStringToPath(path))); + } + gArgs.ClearPathCache(); + Q_EMIT customDataDirStringChanged(m_custom_datadir_string); + } +} diff --git a/src/qml/models/onboardingmodel.h b/src/qml/models/onboardingmodel.h new file mode 100644 index 0000000000..9c1b84d8b5 --- /dev/null +++ b/src/qml/models/onboardingmodel.h @@ -0,0 +1,119 @@ +// Copyright (c) 2024 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + + +#ifndef BITCOIN_QML_MODELS_ONBOARDINGMODEL_H +#define BITCOIN_QML_MODELS_ONBOARDINGMODEL_H + + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +class OnboardingModel : public QObject +{ + Q_OBJECT + Q_PROPERTY(QString getDefaultDataDirString READ getDefaultDataDirString CONSTANT) + Q_PROPERTY(QUrl getDefaultDataDirectory READ getDefaultDataDirectory CONSTANT) + Q_PROPERTY(bool prune READ prune WRITE setPrune NOTIFY pruneChanged) + Q_PROPERTY(int pruneSizeGB READ pruneSizeGB WRITE setPruneSizeGB NOTIFY pruneSizeGBChanged) + Q_PROPERTY(int dbcacheSizeMiB READ dbcacheSizeMiB WRITE setDbcacheSizeMiB NOTIFY dbcacheSizeMiBChanged) + Q_PROPERTY(bool listen READ listen WRITE setListen NOTIFY listenChanged) + Q_PROPERTY(int maxDbcacheSizeMiB READ maxDbcacheSizeMiB CONSTANT) + Q_PROPERTY(int minDbcacheSizeMiB READ minDbcacheSizeMiB CONSTANT) + Q_PROPERTY(int maxScriptThreads READ maxScriptThreads CONSTANT) + Q_PROPERTY(int minScriptThreads READ minScriptThreads CONSTANT) + Q_PROPERTY(bool natpmp READ natpmp WRITE setNatpmp NOTIFY natpmpChanged) + Q_PROPERTY(int scriptThreads READ scriptThreads WRITE setScriptThreads NOTIFY scriptThreadsChanged) + Q_PROPERTY(bool server READ server WRITE setServer NOTIFY serverChanged) + Q_PROPERTY(bool upnp READ upnp WRITE setUpnp NOTIFY upnpChanged) + Q_PROPERTY(quint64 assumedBlockchainSize READ assumedBlockchainSize CONSTANT) + Q_PROPERTY(quint64 assumedChainstateSize READ assumedChainstateSize CONSTANT) + Q_PROPERTY(QString fullClientVersion READ fullClientVersion CONSTANT) + + +public: + explicit OnboardingModel(); + + int dbcacheSizeMiB() const { return m_dbcache_size_mib; } + void setDbcacheSizeMiB(int new_dbcache_size_mib); + bool listen() const { return m_listen; } + void setListen(bool new_listen); + int maxDbcacheSizeMiB() const { return m_max_dbcache_size_mib; } + int minDbcacheSizeMiB() const { return m_min_dbcache_size_mib; } + int maxScriptThreads() const { return m_max_script_threads; } + int minScriptThreads() const { return m_min_script_threads; } + bool natpmp() const { return m_natpmp; } + void setNatpmp(bool new_natpmp); + bool prune() const { return m_prune; } + void setPrune(bool new_prune); + int pruneSizeGB() const { return m_prune_size_gb; } + void setPruneSizeGB(int new_prune_size); + int scriptThreads() const { return m_script_threads; } + void setScriptThreads(int new_script_threads); + bool server() const { return m_server; } + void setServer(bool new_server); + bool upnp() const { return m_upnp; } + void setUpnp(bool new_upnp); + quint64 assumedBlockchainSize() const { return m_assumed_blockchain_size; }; + quint64 assumedChainstateSize() const { return m_assumed_chainstate_size; }; + QString fullClientVersion() const { return QString::fromStdString(FormatFullVersion()); } + QString getDefaultDataDirString(); + QUrl getDefaultDataDirectory(); + Q_INVOKABLE void defaultReset(); + Q_INVOKABLE void setCustomDataDirArgs(QString path); + Q_INVOKABLE void requestShutdown(); + +public Q_SLOTS: + void setCustomDataDirString(const QString &new_custom_datadir_string) { + m_custom_datadir_string = new_custom_datadir_string; + m_signalReceived = true; + } + +Q_SIGNALS: + void customDataDirStringChanged(QString new_custom_datadir_string); + void onboardingFinished(); + void requestedShutdown(); + void dbcacheSizeMiBChanged(int new_dbcache_size_mib); + void listenChanged(bool new_listen); + void natpmpChanged(bool new_natpmp); + void pruneChanged(bool new_prune); + void pruneSizeGBChanged(int new_prune_size_gb); + void scriptThreadsChanged(int new_script_threads); + void serverChanged(bool new_server); + void upnpChanged(bool new_upnp); + +private: + QString m_custom_datadir_string; + bool m_signalReceived = false; + common::Settings m_previous_settings; + int m_dbcache_size_mib; + const int m_min_dbcache_size_mib{nMinDbCache}; + const int m_max_dbcache_size_mib{nMaxDbCache}; + bool m_listen; + const int m_max_script_threads{MAX_SCRIPTCHECK_THREADS}; + const int m_min_script_threads{-GetNumCores()}; + bool m_natpmp; + bool m_prune; + int m_prune_size_gb; + int m_script_threads; + bool m_server; + bool m_upnp; + quint64 m_assumed_blockchain_size{ Params().AssumedBlockchainSize() }; + quint64 m_assumed_chainstate_size{ Params().AssumedChainStateSize() }; + + common::SettingsValue pruneSetting() const; +}; + +#endif // BITCOIN_QML_MODELS_ONBOARDINGMODEL_H From be30a304cac08050ad39f9426d8801001f1a666c Mon Sep 17 00:00:00 2001 From: D33r-Gee Date: Tue, 12 Mar 2024 11:08:50 -0700 Subject: [PATCH 04/11] qml: StorageLocations.qml, added wiring for custom datadir and resetting to default --- src/qml/components/StorageLocations.qml | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/src/qml/components/StorageLocations.qml b/src/qml/components/StorageLocations.qml index 241477e775..ef7aa8fc42 100644 --- a/src/qml/components/StorageLocations.qml +++ b/src/qml/components/StorageLocations.qml @@ -5,6 +5,10 @@ import QtQuick 2.15 import QtQuick.Controls 2.15 import QtQuick.Layouts 1.15 +import QtQuick.Dialogs 1.3 + +import org.bitcoincore.qt 1.0 + import "../controls" ColumnLayout { @@ -19,11 +23,30 @@ ColumnLayout { description: qsTr("Your application directory.") recommended: true checked: true + onClicked: { + onboardingModel.defaultReset() + } } OptionButton { Layout.fillWidth: true ButtonGroup.group: group text: qsTr("Custom") description: qsTr("Choose the directory and storage device.") + onClicked: fileDialog.open() + } + FileDialog { + id: fileDialog + selectFolder: true + folder: onboardingModel.getDefaultDataDirectory + onAccepted: { + onboardingModel.setCustomDataDirString(fileDialog.fileUrls[0].toString()) + var customDataDir = fileDialog.fileUrl.toString(); + if (customDataDir !== "") { + onboardingModel.setCustomDataDirArgs(customDataDir); + } + } + onRejected: { + console.log("Canceled") + } } -} +} \ No newline at end of file From a61a3f696141b9058b0cb8b992a21c7fc8c4ffb5 Mon Sep 17 00:00:00 2001 From: D33r-Gee Date: Tue, 12 Mar 2024 11:09:14 -0700 Subject: [PATCH 05/11] qml: OnboardingCover.qml, commented out the settings code since we are now waiting for onboarding to finish before node init --- src/qml/pages/onboarding/OnboardingCover.qml | 25 ++------------------ 1 file changed, 2 insertions(+), 23 deletions(-) diff --git a/src/qml/pages/onboarding/OnboardingCover.qml b/src/qml/pages/onboarding/OnboardingCover.qml index 2a7dcbd237..f8585fcc3e 100644 --- a/src/qml/pages/onboarding/OnboardingCover.qml +++ b/src/qml/pages/onboarding/OnboardingCover.qml @@ -7,7 +7,6 @@ import QtQuick.Controls 2.15 import QtQuick.Layouts 1.15 import "../../controls" import "../../components" -import "../settings" Page { background: null @@ -18,19 +17,7 @@ Page { interactive: false orientation: Qt.Horizontal InformationPage { - navRightDetail: NavButton { - iconSource: "image://images/info" - iconHeight: 24 - iconWidth: 24 - iconColor: Theme.color.neutral0 - iconBackground: Rectangle { - radius: 12 - color: Theme.color.neutral9 - } - onClicked: { - introductions.incrementCurrentIndex() - } - } + // TODO: reinstate the info button bannerItem: Image { Layout.fillWidth: true Layout.alignment: Qt.AlignCenter @@ -49,14 +36,6 @@ Page { subtext: qsTr("100% open-source & open-design") buttonText: qsTr("Start") } - SettingsAbout { - navLeftDetail: NavButton { - iconSource: "image://images/caret-left" - text: qsTr("Back") - onClicked: { - introductions.decrementCurrentIndex() - } - } - } + // TODO: resintate the info button linking to settings } } From e9cd5ca840f0a88aaf0aa795e444235d2974a964 Mon Sep 17 00:00:00 2001 From: D33r-Gee Date: Tue, 12 Mar 2024 11:09:49 -0700 Subject: [PATCH 06/11] make: added onboardingmodel files to the Make process --- src/Makefile.qt.include | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Makefile.qt.include b/src/Makefile.qt.include index c9bdb051e8..f63fd17ece 100644 --- a/src/Makefile.qt.include +++ b/src/Makefile.qt.include @@ -35,11 +35,13 @@ QT_FORMS_UI = \ qt/forms/transactiondescdialog.ui QT_MOC_CPP = \ + qml/moc_bitcoin.cpp \ qml/components/moc_blockclockdial.cpp \ qml/controls/moc_linegraph.cpp \ qml/models/moc_chainmodel.cpp \ qml/models/moc_networktraffictower.cpp \ qml/models/moc_nodemodel.cpp \ + qml/models/moc_onboardingmodel.cpp \ qml/models/moc_options_model.cpp \ qml/models/moc_peerlistsortproxy.cpp \ qml/moc_appmode.cpp \ @@ -119,6 +121,7 @@ BITCOIN_QT_H = \ qml/models/chainmodel.h \ qml/models/networktraffictower.h \ qml/models/nodemodel.h \ + qml/models/onboardingmodel.h \ qml/models/options_model.h \ qml/models/peerlistsortproxy.h \ qml/appmode.h \ @@ -306,6 +309,7 @@ BITCOIN_QML_BASE_CPP = \ qml/models/chainmodel.cpp \ qml/models/networktraffictower.cpp \ qml/models/nodemodel.cpp \ + qml/models/onboardingmodel.cpp \ qml/models/options_model.cpp \ qml/models/peerlistsortproxy.cpp \ qml/imageprovider.cpp \ From be62516bb3944631875d58390dbc06792c1abf2e Mon Sep 17 00:00:00 2001 From: D33r-Gee Date: Tue, 12 Mar 2024 11:11:44 -0700 Subject: [PATCH 07/11] qml: updated these files to use onboardingModel instead of optionsModel. Also moved chainModel args to onboardingModel --- src/qml/components/ConnectionSettings.qml | 20 ++++++++------ src/qml/components/StorageOptions.qml | 26 +++++++++---------- src/qml/components/StorageSettings.qml | 8 +++--- src/qml/pages/main.qml | 14 +++++++--- .../onboarding/OnboardingStorageLocation.qml | 2 +- 5 files changed, 40 insertions(+), 30 deletions(-) diff --git a/src/qml/components/ConnectionSettings.qml b/src/qml/components/ConnectionSettings.qml index 90625a7def..63a08264ba 100644 --- a/src/qml/components/ConnectionSettings.qml +++ b/src/qml/components/ConnectionSettings.qml @@ -14,9 +14,10 @@ ColumnLayout { header: qsTr("Enable listening") description: qsTr("Allows incoming connections") actionItem: OptionSwitch { - checked: optionsModel.listen - onToggled: optionsModel.listen = checked + checked: onboardingModel.listen + onToggled: onboardingModel.listen = checked } + Component.onCompleted: onboardingModel.listen = false onClicked: { loadedItem.toggle() loadedItem.toggled() @@ -27,9 +28,10 @@ ColumnLayout { Layout.fillWidth: true header: qsTr("Map port using UPnP") actionItem: OptionSwitch { - checked: optionsModel.upnp - onToggled: optionsModel.upnp = checked + checked: onboardingModel.upnp + onToggled: onboardingModel.upnp = checked } + Component.onCompleted: onboardingModel.upnp = false onClicked: { loadedItem.toggle() loadedItem.toggled() @@ -40,9 +42,10 @@ ColumnLayout { Layout.fillWidth: true header: qsTr("Map port using NAT-PMP") actionItem: OptionSwitch { - checked: optionsModel.natpmp - onToggled: optionsModel.natpmp = checked + checked: onboardingModel.natpmp + onToggled: onboardingModel.natpmp = checked } + Component.onCompleted: onboardingModel.natpmp = false onClicked: { loadedItem.toggle() loadedItem.toggled() @@ -53,9 +56,10 @@ ColumnLayout { Layout.fillWidth: true header: qsTr("Enable RPC server") actionItem: OptionSwitch { - checked: optionsModel.server - onToggled: optionsModel.server = checked + checked: onboardingModel.server + onToggled: onboardingModel.server = checked } + Component.onCompleted: onboardingModel.server = false onClicked: { loadedItem.toggle() loadedItem.toggled() diff --git a/src/qml/components/StorageOptions.qml b/src/qml/components/StorageOptions.qml index eed962b951..e150b32ea6 100644 --- a/src/qml/components/StorageOptions.qml +++ b/src/qml/components/StorageOptions.qml @@ -22,27 +22,27 @@ ColumnLayout { Layout.fillWidth: true ButtonGroup.group: group text: qsTr("Reduce storage") - description: qsTr("Uses about %1GB. For simple wallet use.").arg(chainModel.assumedChainstateSize + 2) + description: qsTr("Uses about %1GB. For simple wallet use.").arg(onboardingModel.assumedChainstateSize + 2) recommended: true - checked: !root.customStorage && optionsModel.prune + checked: !root.customStorage && onboardingModel.prune onClicked: { - optionsModel.prune = true - optionsModel.pruneSizeGB = 2 + onboardingModel.prune = true + onboardingModel.pruneSizeGB = 2 } Component.onCompleted: { - optionsModel.prune = true - optionsModel.pruneSizeGB = 2 + onboardingModel.prune = true + onboardingModel.pruneSizeGB = 2 } } OptionButton { Layout.fillWidth: true ButtonGroup.group: group text: qsTr("Store all data") - checked: !optionsModel.prune + checked: !onboardingModel.prune description: qsTr("Uses about %1GB. Support the network.").arg( - chainModel.assumedBlockchainSize + chainModel.assumedChainstateSize) + onboardingModel.assumedBlockchainSize + onboardingModel.assumedChainstateSize) onClicked: { - optionsModel.prune = false + onboardingModel.prune = false } } Loader { @@ -51,12 +51,12 @@ ColumnLayout { visible: active sourceComponent: OptionButton { ButtonGroup.group: group - checked: root.customStorage && optionsModel.prune + checked: root.customStorage && onboardingModel.prune text: qsTr("Custom") - description: qsTr("Storing about %1GB of data.").arg(root.customStorageAmount + chainModel.assumedChainstateSize) + description: qsTr("Storing about %1GB of data.").arg(root.customStorageAmount + onboardingModel.assumedChainstateSize) onClicked: { - optionsModel.prune = true - optionsModel.pruneSizeGB = root.customStorageAmount + onboardingModel.prune = true + onboardingModel.pruneSizeGB = root.customStorageAmount } } } diff --git a/src/qml/components/StorageSettings.qml b/src/qml/components/StorageSettings.qml index 2a2951bfac..820d594830 100644 --- a/src/qml/components/StorageSettings.qml +++ b/src/qml/components/StorageSettings.qml @@ -16,8 +16,8 @@ ColumnLayout { Layout.fillWidth: true header: qsTr("Store recent blocks only") actionItem: OptionSwitch { - checked: optionsModel.prune - onToggled: optionsModel.prune = checked + checked: onboardingModel.prune + onToggled: onboardingModel.prune = checked onCheckedChanged: { if (checked == false) { pruneTargetSetting.state = "DISABLED" @@ -40,14 +40,14 @@ ColumnLayout { showErrorText: false actionItem: ValueInput { parentState: pruneTargetSetting.state - description: optionsModel.pruneSizeGB + description: onboardingModel.pruneSizeGB onEditingFinished: { if (parseInt(text) < 1) { pruneTargetSetting.showErrorText = true } else { root.customStorage = true root.customStorageAmount = parseInt(text) - optionsModel.pruneSizeGB = parseInt(text) + onboardingModel.pruneSizeGB = parseInt(text) pruneTargetSetting.forceActiveFocus() pruneTargetSetting.showErrorText = false } diff --git a/src/qml/pages/main.qml b/src/qml/pages/main.qml index 581e9b42b4..546a861427 100644 --- a/src/qml/pages/main.qml +++ b/src/qml/pages/main.qml @@ -38,14 +38,14 @@ ApplicationWindow { focus: true Keys.onReleased: { if (event.key == Qt.Key_Back) { - nodeModel.requestShutdown() + onboardingModel.requestShutdown() event.accepted = true } } } Connections { - target: nodeModel + target: onboardingModel function onRequestedShutdown() { main.clear() main.push(shutdown) @@ -66,7 +66,13 @@ ApplicationWindow { OnboardingStorageAmount {} OnboardingConnection {} - onFinishedChanged: main.push(node) + onFinishedChanged:{ + if (swipeView.finished) { + onboardingModel.onboardingFinished() + optionsModel.onboard() + main.push(node) + } + } } } @@ -93,4 +99,4 @@ ApplicationWindow { } } } -} +} \ No newline at end of file diff --git a/src/qml/pages/onboarding/OnboardingStorageLocation.qml b/src/qml/pages/onboarding/OnboardingStorageLocation.qml index b2c3aa1e00..2187aee689 100644 --- a/src/qml/pages/onboarding/OnboardingStorageLocation.qml +++ b/src/qml/pages/onboarding/OnboardingStorageLocation.qml @@ -19,7 +19,7 @@ InformationPage { bold: true headerText: qsTr("Storage location") headerMargin: 0 - description: qsTr("Where do you want to store the downloaded block data?\nYou need a minimum of %1GB of storage.").arg(chainModel.assumedChainstateSize + 1) + description: qsTr("Where do you want to store the downloaded block data?\nYou need a minimum of %1GB of storage.").arg(onboardingModel.assumedChainstateSize + 1) descriptionMargin: 20 detailActive: true detailItem: StorageLocations {} From 6b176c445aeba1c9d63eff587c9994643f75ced4 Mon Sep 17 00:00:00 2001 From: johnny9 <985648+johnny9@users.noreply.github.com> Date: Sun, 5 Mar 2023 12:13:46 -0500 Subject: [PATCH 08/11] qml: fix file location path in Controls plugin for Android --- depends/packages/qt.mk | 2 ++ .../qt/fix_android_controls_file_location.patch | 13 +++++++++++++ 2 files changed, 15 insertions(+) create mode 100644 depends/patches/qt/fix_android_controls_file_location.patch diff --git a/depends/packages/qt.mk b/depends/packages/qt.mk index dd30a60559..2afa47fece 100644 --- a/depends/packages/qt.mk +++ b/depends/packages/qt.mk @@ -24,6 +24,7 @@ $(package)_patches += fast_fixed_dtoa_no_optimize.patch $(package)_patches += guix_cross_lib_path.patch $(package)_patches += fix_android_plugin_names.patch $(package)_patches += fix_riscv_atomic.patch +$(package)_patches += fix_android_controls_file_location.patch $(package)_qtdeclarative_file_name = qtdeclarative-$($(package)_suffix) $(package)_qtdeclarative_sha256_hash = 5cc169d91efb15a1ee7f484862f872c3eaba592dacf3c0fbcb55c0f3c208254a @@ -300,6 +301,7 @@ define $(package)_preprocess_cmds patch -p1 -i $($(package)_patch_dir)/guix_cross_lib_path.patch && \ patch -p1 -i $($(package)_patch_dir)/fix_android_plugin_names.patch && \ patch -p1 -i $($(package)_patch_dir)/fix_riscv_atomic.patch && \ + patch -p1 -i $($(package)_patch_dir)/fix_android_controls_file_location.patch && \ mkdir -p qtbase/mkspecs/macx-clang-linux &&\ cp -f qtbase/mkspecs/macx-clang/qplatformdefs.h qtbase/mkspecs/macx-clang-linux/ &&\ cp -f $($(package)_patch_dir)/mac-qmake.conf qtbase/mkspecs/macx-clang-linux/qmake.conf && \ diff --git a/depends/patches/qt/fix_android_controls_file_location.patch b/depends/patches/qt/fix_android_controls_file_location.patch new file mode 100644 index 0000000000..70663aa3c6 --- /dev/null +++ b/depends/patches/qt/fix_android_controls_file_location.patch @@ -0,0 +1,13 @@ +diff --git a/qtquickcontrols/src/controls/plugin.cpp b/src/controls/plugin.cpp +index 446357aa..a08957cd 100644 +--- a/qtquickcontrols/src/controls/plugin.cpp ++++ b/qtquickcontrols/src/controls/plugin.cpp +@@ -240,7 +240,7 @@ void QtQuickControls1Plugin::initializeEngine(QQmlEngine *engine, const char *ur + QString QtQuickControls1Plugin::fileLocation() const + { + #ifdef Q_OS_ANDROID +- return "qrc:/android_rcc_bundle/qml/QtQuick/Controls"; ++ return "qrc:/qt-project.org/imports/QtQuick/Controls"; + #else + # ifndef QT_STATIC + if (isLoadedFromResource()) From 2e3f2b7e321a48c1a34cc7678b960b3b04f6069d Mon Sep 17 00:00:00 2001 From: johnny9 <985648+johnny9@users.noreply.github.com> Date: Sat, 16 Mar 2024 15:57:38 -0400 Subject: [PATCH 09/11] qml: statically link QtQuick2Dialog and FolderListModel plugins --- build-aux/m4/bitcoin_qt.m4 | 12 ++++++++++++ src/qml/bitcoin.cpp | 3 +++ 2 files changed, 15 insertions(+) diff --git a/build-aux/m4/bitcoin_qt.m4 b/build-aux/m4/bitcoin_qt.m4 index 2d56708716..95a9017c18 100644 --- a/build-aux/m4/bitcoin_qt.m4 +++ b/build-aux/m4/bitcoin_qt.m4 @@ -163,6 +163,12 @@ AC_DEFUN([BITCOIN_QT_CONFIGURE],[ if test -d "$qt_plugin_path/../qml/QtQuick/Controls"; then QT_LIBS="$QT_LIBS -L$qt_plugin_path/../qml/QtQuick/Controls" fi + if test -d "$qt_plugin_path/../qml/QtQuick/Dialogs"; then + QT_LIBS="$QT_LIBS -L$qt_plugin_path/../qml/QtQuick/Dialogs -L$qt_plugin_path/../qml/QtQuick/Dialogs/Private" + fi + if test -d "$qt_plugin_path/../qml/Qt/labs/folderlistmodel"; then + QT_LIBS="$QT_LIBS -L$qt_plugin_path/../qml/Qt/labs/folderlistmodel" + fi if test -d "$qt_plugin_path/../qml/Qt/labs/settings"; then QT_LIBS="$QT_LIBS -L$qt_plugin_path/../qml/Qt/labs/settings" fi @@ -214,6 +220,9 @@ AC_DEFUN([BITCOIN_QT_CONFIGURE],[ _BITCOIN_QT_CHECK_STATIC_PLUGIN([QtQuickLayoutsPlugin], [-lqquicklayoutsplugin]) dnl qtquickcontrols module plugins _BITCOIN_QT_CHECK_STATIC_PLUGIN([QtQuickControls1Plugin], [-lqtquickcontrolsplugin]) + _BITCOIN_QT_CHECK_STATIC_PLUGIN([QtQuick2DialogsPlugin], [-ldialogplugin]) + _BITCOIN_QT_CHECK_STATIC_PLUGIN([QtQuick2DialogsPrivatePlugin], [-ldialogsprivateplugin]) + _BITCOIN_QT_CHECK_STATIC_PLUGIN([QmlFolderListModelPlugin], [-lqmlfolderlistmodelplugin]) _BITCOIN_QT_CHECK_STATIC_PLUGIN([QmlSettingsPlugin], [-lqmlsettingsplugin]) dnl qtquickcontrols2 module plugins _BITCOIN_QT_CHECK_STATIC_PLUGIN([QtQuickControls2Plugin], [-lqtquickcontrols2plugin]) @@ -227,6 +236,9 @@ AC_DEFUN([BITCOIN_QT_CONFIGURE],[ _BITCOIN_QT_CHECK_STATIC_PLUGIN([QtQuickLayoutsPlugin], [-lqml_QtQuick_Layouts_qquicklayoutsplugin]) dnl qtquickcontrols module plugins _BITCOIN_QT_CHECK_STATIC_PLUGIN([QtQuickControls1Plugin], [-lqml_QtQuick_Controls_qtquickcontrolsplugin]) + _BITCOIN_QT_CHECK_STATIC_PLUGIN([QtQuick2DialogsPlugin], [-lqml_QtQuick_Dialogs_dialogplugin]) + _BITCOIN_QT_CHECK_STATIC_PLUGIN([QtQuick2DialogsPrivatePlugin], [-lqml_QtQuick_Dialogs_Private_dialogsprivateplugin]) + _BITCOIN_QT_CHECK_STATIC_PLUGIN([QmlFolderListModelPlugin], [-lqml_Qt_labs_folderlistmodel_qmlfolderlistmodelplugin]) _BITCOIN_QT_CHECK_STATIC_PLUGIN([QmlSettingsPlugin], [-lqml_Qt_labs_settings_qmlsettingsplugin]) dnl qtquickcontrols2 module plugins _BITCOIN_QT_CHECK_STATIC_PLUGIN([QtQuickControls2Plugin], [-lqml_QtQuick_Controls_2_qtquickcontrols2plugin]) diff --git a/src/qml/bitcoin.cpp b/src/qml/bitcoin.cpp index dc5597a03f..02f247bec7 100644 --- a/src/qml/bitcoin.cpp +++ b/src/qml/bitcoin.cpp @@ -62,9 +62,12 @@ QT_END_NAMESPACE #include Q_IMPORT_PLUGIN(QtQmlPlugin) Q_IMPORT_PLUGIN(QtQmlModelsPlugin) +Q_IMPORT_PLUGIN(QtQuick2DialogsPlugin) +Q_IMPORT_PLUGIN(QtQuick2DialogsPrivatePlugin) Q_IMPORT_PLUGIN(QtQuick2Plugin) Q_IMPORT_PLUGIN(QtQuick2WindowPlugin) Q_IMPORT_PLUGIN(QtQuickControls1Plugin) +Q_IMPORT_PLUGIN(QmlFolderListModelPlugin) Q_IMPORT_PLUGIN(QmlSettingsPlugin) Q_IMPORT_PLUGIN(QtQuickLayoutsPlugin) Q_IMPORT_PLUGIN(QtQuickControls2Plugin) From 555b21260f4c4d2471debb4a2a589cfc26cb3cbb Mon Sep 17 00:00:00 2001 From: johnny9 <985648+johnny9@users.noreply.github.com> Date: Sat, 16 Mar 2024 15:57:58 -0400 Subject: [PATCH 10/11] qml: fix AndroidNotifier construction --- src/qml/bitcoin.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/qml/bitcoin.cpp b/src/qml/bitcoin.cpp index 02f247bec7..eebf16d446 100644 --- a/src/qml/bitcoin.cpp +++ b/src/qml/bitcoin.cpp @@ -198,7 +198,7 @@ bool BitcoinQmlApplication::createNode(QQmlApplicationEngine& engine, int& argc, m_network_traffic_tower = new NetworkTrafficTower{*m_node_model}; #ifdef __ANDROID__ - AndroidNotifier android_notifier{m_node_model}; + AndroidNotifier android_notifier{*m_node_model}; #endif m_chain_model = new ChainModel{*m_chain}; From 830ae0b9e2ec3c982086836d47aadc43c3ddb0e7 Mon Sep 17 00:00:00 2001 From: D33r-Gee Date: Fri, 22 Mar 2024 11:08:14 -0700 Subject: [PATCH 11/11] android: added permissions checks to allow custom datadir read/write --- src/qt/android/AndroidManifest.xml | 2 ++ .../org/bitcoincore/qt/BitcoinQtActivity.java | 21 +++++++++++++++++++ 2 files changed, 23 insertions(+) diff --git a/src/qt/android/AndroidManifest.xml b/src/qt/android/AndroidManifest.xml index 8d2c1ac901..c9b6b2b80e 100644 --- a/src/qt/android/AndroidManifest.xml +++ b/src/qt/android/AndroidManifest.xml @@ -3,7 +3,9 @@ + + diff --git a/src/qt/android/src/org/bitcoincore/qt/BitcoinQtActivity.java b/src/qt/android/src/org/bitcoincore/qt/BitcoinQtActivity.java index 97db345d4e..16a9648366 100644 --- a/src/qt/android/src/org/bitcoincore/qt/BitcoinQtActivity.java +++ b/src/qt/android/src/org/bitcoincore/qt/BitcoinQtActivity.java @@ -8,6 +8,8 @@ import android.system.Os; import android.view.WindowManager; import android.view.View; +import android.Manifest; +import android.content.pm.PackageManager; import org.qtproject.qt5.android.bindings.QtActivity; @@ -15,6 +17,8 @@ public class BitcoinQtActivity extends QtActivity { + private static final int PERMISSIONS_REQUEST_CODE = 123; + @Override public void onCreate(Bundle savedInstanceState) { @@ -36,5 +40,22 @@ public void onCreate(Bundle savedInstanceState) getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN); super.onCreate(savedInstanceState); + + if (checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { + requestPermissions(new String[] { + Manifest.permission.WRITE_EXTERNAL_STORAGE + }, PERMISSIONS_REQUEST_CODE); + } + } + + @Override + public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { + if (requestCode == PERMISSIONS_REQUEST_CODE) { + if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { + System.out.println("Permission was granted"); + } else { + System.out.println("Permission was denied"); + } + } } }