diff --git a/components/waydroidintegrationplugin/CMakeLists.txt b/components/waydroidintegrationplugin/CMakeLists.txt index 4e42e5f9..2ae3c28d 100644 --- a/components/waydroidintegrationplugin/CMakeLists.txt +++ b/components/waydroidintegrationplugin/CMakeLists.txt @@ -9,6 +9,7 @@ target_link_libraries(waydroidintegrationplugin PRIVATE Qt::Qml Qt::Quick KF6::AuthCore + KF6::ConfigCore KF6::I18n QCoro::Core QCoro::Qml diff --git a/components/waydroidintegrationplugin/kauth/waydroidhelper.actions b/components/waydroidintegrationplugin/kauth/waydroidhelper.actions index 3b0acd2f..433ccd40 100644 --- a/components/waydroidintegrationplugin/kauth/waydroidhelper.actions +++ b/components/waydroidintegrationplugin/kauth/waydroidhelper.actions @@ -104,3 +104,10 @@ Description[zh_CN]=允许获取 Waydroid 容器的 Android ID 以激活谷歌服 Policy=yes PolicyInactive=yes Persistence=session + +[org.kde.plasma.mobileshell.waydroidhelper.reset] +Name=Reset Waydroid +Description=Allow delete waydroid folder +Policy=yes +PolicyInactive=yes +Persistence=session diff --git a/components/waydroidintegrationplugin/kauth/waydroidhelper.cpp b/components/waydroidintegrationplugin/kauth/waydroidhelper.cpp index ecaad9f9..4da57cc9 100644 --- a/components/waydroidintegrationplugin/kauth/waydroidhelper.cpp +++ b/components/waydroidintegrationplugin/kauth/waydroidhelper.cpp @@ -10,6 +10,7 @@ #include #include +#include #include #include #include @@ -20,6 +21,8 @@ using namespace Qt::StringLiterals; #define WAYDROID_COMMAND "waydroid" +#define WAYDROID_FOLDER "/var/lib/waydroid" +#define WAYDROID_USER_FOLDER "/.local/share/waydroid/" // Extract current downloaded size, total_size and speed. // Example of log: "[Downloading] 62.19 MB/1197.24 MB 96740.75 kbps(approx.)" @@ -31,6 +34,10 @@ class WaydroidHelper : public QObject public Q_SLOTS: KAuth::ActionReply initialize(const QVariantMap &args); KAuth::ActionReply getandroidid(const QVariantMap &args); + KAuth::ActionReply reset(const QVariantMap &args); + +private: + bool removeDir(const QString dirPath); }; KAuth::ActionReply WaydroidHelper::initialize(const QVariantMap &args) @@ -121,6 +128,34 @@ KAuth::ActionReply WaydroidHelper::getandroidid(const QVariantMap &args) } } +KAuth::ActionReply WaydroidHelper::reset(const QVariantMap &args) +{ + const QString homeDir = args.value("homeDir"_L1).toString(); + + if (!removeDir(WAYDROID_FOLDER)) { + qCWarning(WAYDROIDHELPER) << "Failed to remove Waydroid directory"; + return KAuth::ActionReply::HelperErrorReply(); + } + + if (!removeDir(homeDir % WAYDROID_USER_FOLDER)) { + qCWarning(WAYDROIDHELPER) << "Failed to remove user Waydroid directory"; + return KAuth::ActionReply::HelperErrorReply(); + } + + return KAuth::ActionReply::SuccessReply(); +} + +bool WaydroidHelper::removeDir(const QString dirPath) +{ + qCWarning(WAYDROIDHELPER) << "Removing " << dirPath; + QDir qDir(dirPath); + if (!qDir.exists()) { + return true; // Ignore if directory not exists + } + + return qDir.removeRecursively(); +} + KAUTH_HELPER_MAIN("org.kde.plasma.mobileshell.waydroidhelper", WaydroidHelper) -#include "waydroidhelper.moc" \ No newline at end of file +#include "waydroidhelper.moc" diff --git a/components/waydroidintegrationplugin/waydroidstate.cpp b/components/waydroidintegrationplugin/waydroidstate.cpp index fcc2abde..7425e79a 100644 --- a/components/waydroidintegrationplugin/waydroidstate.cpp +++ b/components/waydroidintegrationplugin/waydroidstate.cpp @@ -15,12 +15,16 @@ #include #include #include +#include #include #include #include #include +#include +#include #include +#include using namespace Qt::StringLiterals; @@ -328,6 +332,50 @@ void WaydroidState::copyToClipboard(const QString text) qGuiApp->clipboard()->setText(text); } +QCoro::QmlTask WaydroidState::resetWaydroidQml() +{ + return resetWaydroid(); +} + +QCoro::Task WaydroidState::resetWaydroid() +{ + if (m_status != Initialized || m_sessionStatus == SessionStarting) { + co_return; + } + + m_status = Resetting; + Q_EMIT statusChanged(); + + if (m_sessionStatus == SessionRunning) { + co_await stopSession(); + } + + const QVariantMap args = {{u"homeDir"_s, QDir::homePath()}}; + + KAuth::Action writeAction(u"org.kde.plasma.mobileshell.waydroidhelper.reset"_s); + writeAction.setHelperId(u"org.kde.plasma.mobileshell.waydroidhelper"_s); + writeAction.setArguments(args); + + KAuth::ExecuteJob *job = writeAction.execute(); + job->start(); + + co_await qCoro(job, &KAuth::ExecuteJob::finished); + + removeWaydroidApplications(); + + if (job->error() == 0) { + m_status = NotInitialized; + } else { + m_errorTitle = i18n("Failed to reset Waydroid."); + Q_EMIT errorTitleChanged(); + + m_status = Initialized; + qCWarning(WAYDROIDINTEGRATIONPLUGIN) << "KAuth returned an error code:" << job->error() << " message: " << job->errorString(); + } + + Q_EMIT statusChanged(); +} + WaydroidState::Status WaydroidState::status() const { return m_status; @@ -498,3 +546,52 @@ void WaydroidState::checkSessionStarting(const int limit, const int tried) }); } } + +QString WaydroidState::desktopFileDirectory() +{ + auto dir = []() -> QString { + if (KSandbox::isFlatpak()) { + return qEnvironmentVariable("HOME") % u"/.local/share/applications/"; + } + return QStandardPaths::writableLocation(QStandardPaths::ApplicationsLocation); + }(); + + QDir(dir).mkpath(QStringLiteral(".")); + + return dir; +} + +bool WaydroidState::removeWaydroidApplications() +{ + const QDir appsDir(desktopFileDirectory()); + const auto fileInfos = appsDir.entryInfoList(QDir::Files); + if (fileInfos.length() < 1) { + return false; + } + + bool allFileRemoved = true; + + for (const auto &fileInfo : fileInfos) { + if (fileInfo.fileName().contains(QStringView(u".desktop"))) { + const KDesktopFile desktopFile(fileInfo.filePath()); + const KConfigGroup configGroup = desktopFile.desktopGroup(); + + if (!configGroup.hasKey(u"Categories"_s)) { + continue; + } + + const auto categories = configGroup.readEntry(u"Categories"_s); + if (!categories.contains(u"X-WayDroid-App"_s)) { + continue; + } + + QFile file(fileInfo.filePath()); + if (!file.remove()) { + allFileRemoved &= false; + qCWarning(WAYDROIDINTEGRATIONPLUGIN) << "Failed to remove: " << desktopFile.name(); + } + } + } + + return allFileRemoved; +} diff --git a/components/waydroidintegrationplugin/waydroidstate.h b/components/waydroidintegrationplugin/waydroidstate.h index 5228a8af..bb37e2c5 100644 --- a/components/waydroidintegrationplugin/waydroidstate.h +++ b/components/waydroidintegrationplugin/waydroidstate.h @@ -52,7 +52,8 @@ public: NotSupported = 0, NotInitialized, Initializing, - Initialized + Initialized, + Resetting }; Q_ENUM(Status) @@ -103,7 +104,9 @@ public: QCoro::Task startSession(); Q_INVOKABLE QCoro::QmlTask stopSessionQml(); QCoro::Task stopSession(); - + Q_INVOKABLE QCoro::QmlTask resetWaydroidQml(); + QCoro::Task resetWaydroid(); + Q_INVOKABLE void copyToClipboard(const QString text); Status status() const; @@ -202,4 +205,7 @@ private: * https://github.com/waydroid/waydroid/blob/2c41162d8bfef5bf83333a6ce4834af0c3c2b535/tools/actions/session_manager.py#L31 */ void checkSessionStarting(const int limit, const int tried = 0); + + QString desktopFileDirectory(); + bool removeWaydroidApplications(); }; diff --git a/kcms/waydroidintegration/ui/WaydroidConfigurationForm.qml b/kcms/waydroidintegration/ui/WaydroidConfigurationForm.qml index af86a51c..9571a562 100644 --- a/kcms/waydroidintegration/ui/WaydroidConfigurationForm.qml +++ b/kcms/waydroidintegration/ui/WaydroidConfigurationForm.qml @@ -51,6 +51,21 @@ ColumnLayout { text: i18n("Installed applications") onClicked: kcm.push("WaydroidApplicationsPage.qml") } + + FormCard.FormButtonDelegate { + text: i18n("Reset waydroid") + onClicked: confirmDialog.open() + } + + Kirigami.PromptDialog { + id: confirmDialog + title: i18nc("@title:window", "Confirm Waydroid Reset") + subtitle: i18n("Are you sure you want to reset Waydroid ? This is a destructive action, and will wipe all user data.") + standardButtons: Kirigami.Dialog.Ok | Kirigami.Dialog.Cancel + + onAccepted: AIP.WaydroidState.resetWaydroidQml() + } + } // Some informations as IP address can take time to be set by Waydroid diff --git a/kcms/waydroidintegration/ui/main.qml b/kcms/waydroidintegration/ui/main.qml index 06600d7d..943c9dc5 100644 --- a/kcms/waydroidintegration/ui/main.qml +++ b/kcms/waydroidintegration/ui/main.qml @@ -62,6 +62,11 @@ KCM.SimpleKCM { } } + WaydroidLoader { + visible: AIP.WaydroidState.errorTitle === "" && AIP.WaydroidState.status == AIP.WaydroidState.Resetting + text: i18n("Waydroid is resetting.\nIt can take a few seconds.") + } + ColumnLayout { visible: AIP.WaydroidState.errorTitle === "" && AIP.WaydroidState.status == AIP.WaydroidState.Initialized && AIP.WaydroidState.sessionStatus == AIP.WaydroidState.SessionStopped anchors.centerIn: parent