From 9a344f35821ca0566e0b6fa35fe70ab0d2934349 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Burak=20Han=C3=A7erli?= <burak.hancerli@qt.io> Date: Wed, 15 Nov 2023 11:11:11 +0000 Subject: [PATCH] QDS-11236 Simple caching method for the demos --- android/AndroidManifest.xml | 2 +- src/backend.cpp | 56 ++++++++++++++++------- src/backend.h | 3 +- src/main.cpp | 13 +++--- src/projectManager.cpp | 89 ++++++++++++++++++++++++++++++++++--- src/projectManager.h | 17 +++++-- ui/ExamplesPage.qml | 13 ++++-- 7 files changed, 156 insertions(+), 37 deletions(-) diff --git a/android/AndroidManifest.xml b/android/AndroidManifest.xml index 151df22..b3d4af6 100644 --- a/android/AndroidManifest.xml +++ b/android/AndroidManifest.xml @@ -1,6 +1,6 @@ <?xml version="1.0"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="io.qt.qtdesignviewer" - android:installLocation="auto" android:versionCode="13" android:versionName="1.2"> + android:installLocation="auto" android:versionCode="14" android:versionName="1.2"> <!-- %%INSERT_PERMISSIONS --> <!-- %%INSERT_FEATURES --> <supports-screens android:anyDensity="true" android:largeScreens="true" diff --git a/src/backend.cpp b/src/backend.cpp index 0fd27c4..15fe089 100644 --- a/src/backend.cpp +++ b/src/backend.cpp @@ -86,34 +86,56 @@ void Backend::showWarning(const QString &message) { QMessageBox msg(QMessageBox::Warning, "Warning", message, QMessageBox::Ok); msg.exec(); + qWarning() << message; } -void Backend::downloadAndRun(const QString &url) +void Backend::updatePopup(const QString &text, bool indeterminate) { - updatePopup("Downloading project...", false); + emit popupTextChanged(text); + emit popupProgressIndeterminateChanged(indeterminate); + QEventLoop().processEvents(QEventLoop::AllEvents, 100); +} + +void Backend::runDemoProject(const QString &projectName) +{ + qDebug() << "Checking if demo project is cached for " << projectName; emit popupOpen(); - qDebug() << "Fetching project from " << url << "..."; - QByteArray project = m_serviceConnector.fetchProject(url); + const bool cached = m_projectManager.isDemoProjectCached(projectName); - updatePopup("Unpacking project..."); + if (!cached) { + updatePopup("Downloading demo project...", false); - const QString projectPath = m_projectManager.unpackProject(project); + const QString url = "https://designviewer.qt.io/qmlprojects/" + projectName + ".qmlrc"; + QByteArray project = m_serviceConnector.fetchProject(url); + qDebug() << "Demo project is not cached. Trying to download from " << url << " ..."; + + updatePopup("Caching demo project..."); + if (!m_projectManager.cacheDemoProject(project, projectName)) { + showWarning( + "Could not cache demo project. Please check the logs for more information."); + emit popupClose(); + return; + } + } else { + qDebug() << "Demo project is cached. Running cached project..."; + } - updatePopup("Running project..."); - if (!m_projectManager.runProject(projectPath, QFileInfo(url).baseName())) - qCritical("Could not run project"); + updatePopup("Running demo project..."); + if (!m_projectManager.runDemoProject(projectName)) + showWarning("Could not run demo project. Please check the logs for more information."); else m_projectManager.showAppWindow(); emit popupClose(); } -void Backend::updatePopup(const QString &text, bool indeterminate) +void Backend::clearDemoCaches() { - emit popupTextChanged(text); - emit popupProgressIndeterminateChanged(indeterminate); - QEventLoop().processEvents(QEventLoop::AllEvents, 100); + emit popupOpen(); + updatePopup("Clearing demo caches..."); + m_projectManager.clearDemoCaches(); + emit popupClose(); } void Backend::runUserProject(const QString &url) @@ -142,14 +164,18 @@ void Backend::runUserProject(const QString &url) QByteArray projectData = m_serviceConnector.fetchProject(url); updatePopup("Caching user project..."); - m_projectManager.cacheProject(projectData, projectInfo); + if (!m_projectManager.cacheProject(projectData, projectInfo)) { + showWarning("Could not cache project. Please check the logs for more information."); + emit popupClose(); + return; + } } else { qDebug("Project is cached. Running cached project..."); } updatePopup("Running cached project..."); if (!m_projectManager.runCachedProject(projectInfo)) - qCritical("Could not run project"); + showWarning("Could not run project. Please check the logs for more information."); else m_projectManager.showAppWindow(); diff --git a/src/backend.h b/src/backend.h index af0e181..c17d6d6 100644 --- a/src/backend.h +++ b/src/backend.h @@ -77,7 +77,8 @@ signals: public slots: void runUserProject(const QString &url); - void downloadAndRun(const QString &url); + void runDemoProject(const QString &projectName); + void clearDemoCaches(); void registerUser(const QUrl &url); }; diff --git a/src/main.cpp b/src/main.cpp index 823dc0a..d10669e 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -29,14 +29,15 @@ #include "backend.h" -Backend *backend; +static Backend *backend; +static QString appLogs; void messageHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg) { QByteArray localMsg = msg.toLocal8Bit(); const char *file = context.file ? context.file : ""; const char *function = context.function ? context.function : ""; - QString logPrefix, logSuffix, applicationLogs; + QString logPrefix, logSuffix, newLog; switch (type) { @@ -57,11 +58,11 @@ void messageHandler(QtMsgType type, const QMessageLogContext &context, const QSt logSuffix = QStringLiteral(" (%1:%2, %3)").arg(file).arg(context.line).arg(function); break; } - applicationLogs += logPrefix + localMsg + logSuffix + "\n"; - __android_log_print(ANDROID_LOG_DEBUG, "Qt_Design_Viewer", "%s", qPrintable(applicationLogs)); + newLog += logPrefix + localMsg + logSuffix + "\n"; + __android_log_print(ANDROID_LOG_DEBUG, "Qt_Design_Viewer", "%s", qPrintable(newLog)); if (backend) - backend->setLogs(applicationLogs); + backend->setLogs(appLogs += newLog); } int main(int argc, char *argv[]) @@ -81,7 +82,5 @@ int main(int argc, char *argv[]) view.showMaximized(); backend->initialize(); - backend->registerUser(QUrl("17e8907b3b84b8206d45be4f551f4e25")); - return app.exec(); } diff --git a/src/projectManager.cpp b/src/projectManager.cpp index 37f3654..fc0249d 100644 --- a/src/projectManager.cpp +++ b/src/projectManager.cpp @@ -46,9 +46,22 @@ ProjectManager::ProjectManager(QObject *parent) : QObject(parent) + , m_projectCachePath(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation)) + , m_demoProjectCachePath(m_projectCachePath + "/demoProjects") { qDebug() << "ProjectManager created."; - m_projectCachePath = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation); + qDebug() << "Project cache path: " << m_projectCachePath; + qDebug() << "Demo project cache path: " << m_demoProjectCachePath; + + if (!QDir(m_projectCachePath).exists()) { + qDebug() << "Creating project cache path: " << m_projectCachePath; + QDir().mkpath(m_projectCachePath); + } + + if (!QDir(m_demoProjectCachePath).exists()) { + qDebug() << "Creating demo project cache path: " << m_demoProjectCachePath; + QDir().mkpath(m_demoProjectCachePath); + } } QString ProjectManager::unpackProject(const QByteArray &project, bool extractZip) @@ -215,13 +228,17 @@ bool ProjectManager::runProject(const QString &projectPath, const QString &proje qputenv("QT_QUICK_CONTROLS_CONF", qtquickcontrols2File.toLatin1()); } + qDebug() << "Initializing the qmlEngine"; + m_qmlEngine.reset(new QQmlEngine); + m_qmlEngine->clearComponentCache(); + qDebug("Adding import paths"); for (const QString &importPath : importPaths) { qDebug() << "-- Import path: " << importPath; - m_qmlEngine.addImportPath(importPath); + m_qmlEngine->addImportPath(importPath); } - QObject::connect(&m_qmlEngine, + QObject::connect(m_qmlEngine.data(), &QQmlEngine::warnings, this, [&](const QList<QQmlError> &warnings) { @@ -234,7 +251,7 @@ bool ProjectManager::runProject(const QString &projectPath, const QString &proje QEventLoop().processEvents(QEventLoop::AllEvents, 1000); qDebug() << "Loading mainQmlUrl: " << mainQmlUrl.toString(); - m_qmlComponent.reset(new QQmlComponent(&m_qmlEngine)); + m_qmlComponent.reset(new QQmlComponent(m_qmlEngine.data())); m_qmlComponent->loadUrl(mainQmlUrl); qDebug() << "Waiting for qmlComponent to load"; @@ -260,7 +277,7 @@ bool ProjectManager::runProject(const QString &projectPath, const QString &proje m_quickWindow.reset(qobject_cast<QQuickWindow *>(topLevel)); if (m_quickWindow) { qDebug() << "Running with incubator controller"; - m_qmlEngine.setIncubationController(m_quickWindow->incubationController()); + m_qmlEngine->setIncubationController(m_quickWindow->incubationController()); } else { qWarning() << "Top level object is not a QQuickWindow. Trying QQuickView..."; @@ -271,7 +288,7 @@ bool ProjectManager::runProject(const QString &projectPath, const QString &proje } qDebug() << "Initializing QQuickView"; - QQuickView *view = new QQuickView(&m_qmlEngine, nullptr); + QQuickView *view = new QQuickView(m_qmlEngine.data(), nullptr); m_quickWindow.reset(view); view->setContent(mainQmlUrl, m_qmlComponent.data(), contentItem); view->setResizeMode(QQuickView::SizeViewToRootObject); @@ -386,6 +403,66 @@ bool ProjectManager::runCachedProject(const QJsonObject &projectInfo) return runProject(projectPath, projectName); } +bool ProjectManager::cacheDemoProject(const QByteArray &projectData, const QString &projectName) +{ + const QString demoProjectPath = m_demoProjectCachePath + "/" + projectName; + qDebug() << "Caching demo project " << projectName << " to " << demoProjectPath; + + // remove old cache + if (QDir(demoProjectPath).exists()) { + qDebug() << "Removing old cache for demo project " << projectName; + if (!QDir(demoProjectPath).removeRecursively()) { + qCritical() << "Could not remove old cache for demo project " << projectName; + return false; + } + } + + // create new cache + if (!QDir().mkpath(demoProjectPath)) { + qCritical() << "Could not create new cache for demo project " << projectName; + return false; + } + + const QString tempProjectPath = unpackProject(projectData); + + // copy all files from tempProjectPath to demoProjectPath + QDirIterator it(tempProjectPath, QDirIterator::Subdirectories); + while (it.hasNext()) { + it.next(); + const QString filePath = it.filePath(); + const QString relativeFilePath = filePath.mid(tempProjectPath.length()); + const QString newFilePath = demoProjectPath + relativeFilePath; + qDebug() << "Copying " << filePath << " to " << newFilePath; + if (QFileInfo(filePath).isDir()) { + QDir().mkpath(newFilePath); + } else if (!QFile::copy(filePath, newFilePath)) { + qCritical() << "Could not copy " << filePath << " to " << newFilePath; + return false; + } + } + return true; +} + +void ProjectManager::clearDemoCaches() +{ + qDebug() << "Clearing demo caches"; + QDir(m_demoProjectCachePath).removeRecursively(); +} + +bool ProjectManager::isDemoProjectCached(const QString &projectName) +{ + const QString demoProjectPath = m_demoProjectCachePath + "/" + projectName; + qDebug() << "Checking if demo project " << projectName << " is cached"; + return QDir(demoProjectPath).exists(); +} + +bool ProjectManager::runDemoProject(const QString &projectName) +{ + const QString demoProjectPath = m_demoProjectCachePath + "/" + projectName; + qDebug() << "Running demo project " << projectName << " from " << demoProjectPath; + return runProject(demoProjectPath, projectName); +} + void ProjectManager::orientateWindow(Qt::ScreenOrientation orientation) { QQuickItem *contentItem = m_quickWindow->contentItem(); diff --git a/src/projectManager.h b/src/projectManager.h index e036e72..3fc0759 100644 --- a/src/projectManager.h +++ b/src/projectManager.h @@ -38,11 +38,19 @@ class ProjectManager : public QObject Q_OBJECT public: explicit ProjectManager(QObject *parent = nullptr); + QString unpackProject(const QByteArray &project, bool extractZip = false); bool runProject(const QString &projectPath, const QString &projectName); + bool cacheProject(const QByteArray &projectData, const QJsonObject &projectInfo); bool isProjectCached(const QJsonObject &projectInfo); bool runCachedProject(const QJsonObject &projectInfo); + + bool cacheDemoProject(const QByteArray &projectData, const QString &projectName); + bool isDemoProjectCached(const QString &projectName); + bool runDemoProject(const QString &projectName); + void clearDemoCaches(); + void showAppWindow(); void hideAppWindow(); @@ -53,12 +61,13 @@ private: // Member variables QByteArray m_projectData; QString m_projectPath; - QString m_projectCachePath; + const QString m_projectCachePath; + const QString m_demoProjectCachePath; // Qml related members - QQmlEngine m_qmlEngine; - QSharedPointer<QQmlComponent> m_qmlComponent; - QSharedPointer<QQuickWindow> m_quickWindow; + QScopedPointer<QQmlEngine> m_qmlEngine; + QScopedPointer<QQmlComponent> m_qmlComponent; + QScopedPointer<QQuickWindow> m_quickWindow; // Member functions QString findFile(const QString &dir, const QString &filter); diff --git a/ui/ExamplesPage.qml b/ui/ExamplesPage.qml index c673676..fb8b1f6 100644 --- a/ui/ExamplesPage.qml +++ b/ui/ExamplesPage.qml @@ -13,7 +13,7 @@ Item { id: button3 text: qsTr("Cluster Tutorial") Layout.fillWidth: true - onClicked: backend.downloadAndRun("https://designviewer.qt.io/#ClusterTutorial.qmlrc") + onClicked: backend.runDemoProject("ClusterTutorial") } @@ -21,14 +21,14 @@ Item { id: button text: qsTr("E-Bike Design") Layout.fillWidth: true - onClicked: backend.downloadAndRun("https://designviewer.qt.io/#EBikeDesign.qmlrc") + onClicked: backend.runDemoProject("EBikeDesign") } Button { id: button4 text: qsTr("Material Bundle") Layout.fillWidth: true - onClicked: backend.downloadAndRun("https://designviewer.qt.io/#MaterialBundle.qmlrc") + onClicked: backend.runDemoProject("MaterialBundle") } Item { @@ -38,5 +38,12 @@ Item { Layout.fillHeight: true Layout.fillWidth: true } + + Button { + id: button5 + text: qsTr("Clear demo caches") + Layout.fillWidth: true + onClicked: backend.clearDemoCaches() + } } } -- GitLab