2025-07-10 16:00:41 +00:00
|
|
|
/*
|
|
|
|
|
* SPDX-FileCopyrightText: 2025 Florian RICHER <florian.richer@protonmail.com>
|
|
|
|
|
*
|
|
|
|
|
* SPDX-License-Identifier: GPL-2.0-or-later
|
|
|
|
|
*/
|
|
|
|
|
|
2025-08-06 20:06:17 +00:00
|
|
|
#include "waydroiddbusobject.h"
|
|
|
|
|
#include "waydroidadaptor.h"
|
|
|
|
|
#include "waydroidapplicationdbusobject.h"
|
2025-07-10 16:00:41 +00:00
|
|
|
#include "waydroidintegrationplugin_debug.h"
|
2025-07-14 10:47:21 +00:00
|
|
|
#include "waydroidshared.h"
|
2025-07-10 16:00:41 +00:00
|
|
|
|
2025-12-15 02:10:54 +00:00
|
|
|
#include <QCoroProcess>
|
2025-08-06 20:06:17 +00:00
|
|
|
#include <QDBusConnection>
|
2025-07-12 12:54:24 +00:00
|
|
|
#include <QDir>
|
2025-08-06 20:06:17 +00:00
|
|
|
#include <QLoggingCategory>
|
2025-12-15 02:10:54 +00:00
|
|
|
#include <QPointer>
|
2025-07-10 16:00:41 +00:00
|
|
|
#include <QProcess>
|
|
|
|
|
#include <QRegularExpression>
|
2025-07-28 17:37:19 +00:00
|
|
|
#include <QStandardPaths>
|
2025-07-10 16:00:41 +00:00
|
|
|
#include <QTimer>
|
|
|
|
|
|
|
|
|
|
#include <KAuth/ExecuteJob>
|
2025-07-28 17:37:19 +00:00
|
|
|
#include <KConfigGroup>
|
|
|
|
|
#include <KDesktopFile>
|
2025-07-10 16:00:41 +00:00
|
|
|
#include <KLocalizedString>
|
2025-07-28 17:37:19 +00:00
|
|
|
#include <KSandbox>
|
2025-07-10 16:00:41 +00:00
|
|
|
|
|
|
|
|
using namespace Qt::StringLiterals;
|
|
|
|
|
|
|
|
|
|
#define MULTI_WINDOWS_PROP_KEY "persist.waydroid.multi_windows"
|
|
|
|
|
#define SUSPEND_PROP_KEY "persist.waydroid.suspend"
|
|
|
|
|
#define UEVENT_PROP_KEY "persist.waydroid.uevent"
|
|
|
|
|
|
|
|
|
|
static const QRegularExpression sessionRegExp(u"Session:\\s*(\\w+)"_s);
|
2025-12-15 02:10:54 +00:00
|
|
|
static const QRegularExpression ipAddressRegExp(u"IP address:\\s*(\\d+\\.\\d+\\.\\d+\\.\\d+)"_s);
|
2025-07-12 12:54:24 +00:00
|
|
|
static const QRegularExpression systemOtaRegExp(u"system_ota\\s*=\\s*(\\S+)"_s);
|
2025-07-10 16:00:41 +00:00
|
|
|
|
2025-08-06 20:06:17 +00:00
|
|
|
WaydroidDBusObject::WaydroidDBusObject(QObject *parent)
|
2025-07-10 16:00:41 +00:00
|
|
|
: QObject{parent}
|
|
|
|
|
{
|
2025-07-10 16:57:34 +00:00
|
|
|
}
|
|
|
|
|
|
2025-08-06 20:06:17 +00:00
|
|
|
void WaydroidDBusObject::registerObject()
|
2025-07-10 16:00:41 +00:00
|
|
|
{
|
2025-12-15 02:10:54 +00:00
|
|
|
if (m_dbusInitialized) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
2025-07-10 16:00:41 +00:00
|
|
|
|
2025-12-15 02:10:54 +00:00
|
|
|
new WaydroidAdaptor{this};
|
|
|
|
|
QDBusConnection::sessionBus().registerObject(u"/Waydroid"_s, this);
|
|
|
|
|
m_dbusInitialized = true;
|
2025-07-10 16:00:41 +00:00
|
|
|
|
2025-12-15 02:10:54 +00:00
|
|
|
// Connect it-self to auto-refresh when required status has changed
|
|
|
|
|
connect(this, &WaydroidDBusObject::statusChanged, this, &WaydroidDBusObject::refreshSessionInfo);
|
|
|
|
|
connect(this, &WaydroidDBusObject::statusChanged, this, &WaydroidDBusObject::refreshInstallationInfo);
|
|
|
|
|
connect(this, &WaydroidDBusObject::sessionStatusChanged, this, &WaydroidDBusObject::refreshPropsInfo);
|
|
|
|
|
connect(this, &WaydroidDBusObject::sessionStatusChanged, this, &WaydroidDBusObject::refreshApplications);
|
|
|
|
|
|
|
|
|
|
refreshSupportsInfo();
|
2025-07-10 16:00:41 +00:00
|
|
|
}
|
|
|
|
|
|
2025-08-06 20:06:17 +00:00
|
|
|
void WaydroidDBusObject::initialize(const int systemType, const int romType, const bool forced)
|
2025-07-10 16:00:41 +00:00
|
|
|
{
|
|
|
|
|
if (m_status == Initializing) {
|
2025-08-06 20:06:17 +00:00
|
|
|
return;
|
2025-07-10 16:00:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
m_status = Initializing;
|
|
|
|
|
Q_EMIT statusChanged();
|
|
|
|
|
|
|
|
|
|
QString systemTypeArg;
|
|
|
|
|
switch (systemType) {
|
2025-08-06 20:06:17 +00:00
|
|
|
case Vanilla:
|
2025-07-10 16:00:41 +00:00
|
|
|
systemTypeArg = "VANILLA";
|
|
|
|
|
break;
|
2025-08-06 20:06:17 +00:00
|
|
|
case Foss:
|
2025-07-10 16:00:41 +00:00
|
|
|
systemTypeArg = "FOSS";
|
|
|
|
|
break;
|
2025-08-06 20:06:17 +00:00
|
|
|
case Gapps:
|
2025-07-10 16:00:41 +00:00
|
|
|
systemTypeArg = "GAPPS";
|
|
|
|
|
break;
|
2025-07-12 12:54:24 +00:00
|
|
|
default:
|
|
|
|
|
systemTypeArg = "VANILLA";
|
|
|
|
|
break;
|
2025-07-10 16:00:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QString romTypeArg;
|
|
|
|
|
switch (romType) {
|
2025-08-06 20:06:17 +00:00
|
|
|
case Lineage:
|
2025-07-10 16:00:41 +00:00
|
|
|
romTypeArg = "lineage";
|
|
|
|
|
break;
|
2025-08-06 20:06:17 +00:00
|
|
|
case Bliss:
|
2025-07-10 16:00:41 +00:00
|
|
|
romTypeArg = "bliss";
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const QVariantMap args = {{u"systemType"_s, systemTypeArg}, {u"romType"_s, romTypeArg}, {u"forced"_s, forced}};
|
|
|
|
|
|
|
|
|
|
KAuth::Action writeAction(u"org.kde.plasma.mobileshell.waydroidhelper.initialize"_s);
|
|
|
|
|
writeAction.setHelperId(u"org.kde.plasma.mobileshell.waydroidhelper"_s);
|
|
|
|
|
writeAction.setArguments(args);
|
|
|
|
|
writeAction.setTimeout(3600000); // HACK: 1 hour to wait installation
|
|
|
|
|
|
|
|
|
|
KAuth::ExecuteJob *job = writeAction.execute();
|
|
|
|
|
|
2025-07-26 17:32:26 +00:00
|
|
|
connect(job, &KAuth::ExecuteJob::newData, this, [this](const QVariantMap &data) {
|
|
|
|
|
QString log = data.value("log", "").toString();
|
|
|
|
|
float downloaded = data.value("downloaded", 0.0).toFloat();
|
|
|
|
|
float total = data.value("total", 0.0).toFloat();
|
|
|
|
|
float speed = data.value("speed", 0.0).toFloat();
|
|
|
|
|
|
|
|
|
|
qCDebug(WAYDROIDINTEGRATIONPLUGIN) << "log: " << log;
|
|
|
|
|
Q_EMIT downloadStatusChanged(downloaded, total, speed);
|
|
|
|
|
});
|
|
|
|
|
|
2025-08-06 20:06:17 +00:00
|
|
|
connect(job, &KAuth::ExecuteJob::finished, this, [this](KJob *job, auto) {
|
|
|
|
|
if (job->error() == 0) {
|
|
|
|
|
m_status = Initialized;
|
|
|
|
|
} else {
|
|
|
|
|
Q_EMIT errorOccurred(i18n("Failed to initialize Waydroid."), job->errorString());
|
2025-07-28 17:20:33 +00:00
|
|
|
|
2025-08-06 20:06:17 +00:00
|
|
|
m_status = NotInitialized;
|
|
|
|
|
qCWarning(WAYDROIDINTEGRATIONPLUGIN) << "KAuth returned an error code:" << job->error() << " message: " << job->errorString();
|
|
|
|
|
}
|
2025-07-28 17:20:33 +00:00
|
|
|
|
2025-08-06 20:06:17 +00:00
|
|
|
Q_EMIT statusChanged();
|
|
|
|
|
});
|
2025-12-15 02:10:54 +00:00
|
|
|
|
|
|
|
|
job->start();
|
2025-07-10 16:00:41 +00:00
|
|
|
}
|
|
|
|
|
|
2025-08-06 20:06:17 +00:00
|
|
|
void WaydroidDBusObject::startSession()
|
2025-07-10 16:00:41 +00:00
|
|
|
{
|
|
|
|
|
if (m_sessionStatus == SessionStarting || m_sessionStatus == SessionRunning) {
|
2025-08-06 20:06:17 +00:00
|
|
|
return;
|
2025-07-10 16:00:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
m_sessionStatus = SessionStarting;
|
|
|
|
|
Q_EMIT sessionStatusChanged();
|
|
|
|
|
|
|
|
|
|
const QStringList arguments{u"session"_s, u"start"_s};
|
|
|
|
|
|
2025-08-06 20:06:17 +00:00
|
|
|
auto *process = new QProcess(this);
|
2025-07-10 16:00:41 +00:00
|
|
|
|
2025-08-06 20:06:17 +00:00
|
|
|
connect(process, &QProcess::finished, this, [this, process](int exitCode, QProcess::ExitStatus exitStatus) {
|
2025-07-10 16:00:41 +00:00
|
|
|
Q_UNUSED(exitStatus);
|
2025-12-15 02:10:54 +00:00
|
|
|
process->deleteLater();
|
2025-07-10 16:00:41 +00:00
|
|
|
|
|
|
|
|
if (exitCode == 0) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
m_sessionStatus = SessionStopped;
|
|
|
|
|
Q_EMIT sessionStatusChanged();
|
|
|
|
|
|
2025-08-06 20:06:17 +00:00
|
|
|
QByteArray errorData = process->readAllStandardError();
|
2025-07-10 16:00:41 +00:00
|
|
|
QString errorString = QString::fromUtf8(errorData);
|
|
|
|
|
|
2025-08-06 20:06:17 +00:00
|
|
|
Q_EMIT errorOccurred(i18n("Failed to start the Waydroid session."), errorString);
|
2025-07-10 16:00:41 +00:00
|
|
|
|
|
|
|
|
qCWarning(WAYDROIDINTEGRATIONPLUGIN) << "Failed to start the Waydroid session: " << errorString;
|
|
|
|
|
});
|
|
|
|
|
|
2025-12-15 02:10:54 +00:00
|
|
|
process->start(WAYDROID_COMMAND, arguments);
|
|
|
|
|
|
2025-07-10 16:00:41 +00:00
|
|
|
checkSessionStarting(10);
|
|
|
|
|
}
|
|
|
|
|
|
2025-08-06 20:06:17 +00:00
|
|
|
void WaydroidDBusObject::stopSession()
|
2025-07-10 16:00:41 +00:00
|
|
|
{
|
|
|
|
|
if (m_sessionStatus == SessionStopped) {
|
2025-08-06 20:06:17 +00:00
|
|
|
return;
|
2025-07-10 16:00:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const QStringList arguments{u"session"_s, u"stop"_s};
|
|
|
|
|
|
2025-12-15 02:10:54 +00:00
|
|
|
auto *process = new QProcess(this);
|
|
|
|
|
|
|
|
|
|
connect(process, &QProcess::finished, this, [this, process](int exitCode, QProcess::ExitStatus exitStatus) {
|
|
|
|
|
Q_UNUSED(exitStatus);
|
|
|
|
|
process->deleteLater();
|
|
|
|
|
|
|
|
|
|
if (exitCode == 0) {
|
|
|
|
|
qCWarning(WAYDROIDINTEGRATIONPLUGIN) << "Failed to stop the Waydroid session: " << process->readAllStandardError();
|
|
|
|
|
return;
|
|
|
|
|
}
|
2025-07-10 16:00:41 +00:00
|
|
|
|
|
|
|
|
m_sessionStatus = SessionStopped;
|
|
|
|
|
Q_EMIT sessionStatusChanged();
|
2025-12-15 02:10:54 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
|
|
process->start(WAYDROID_COMMAND, arguments);
|
2025-07-10 16:00:41 +00:00
|
|
|
}
|
|
|
|
|
|
2025-08-06 20:06:17 +00:00
|
|
|
void WaydroidDBusObject::resetWaydroid()
|
2025-07-28 17:37:19 +00:00
|
|
|
{
|
|
|
|
|
if (m_status != Initialized || m_sessionStatus == SessionStarting) {
|
2025-08-06 20:06:17 +00:00
|
|
|
return;
|
2025-07-28 17:37:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
m_status = Resetting;
|
|
|
|
|
Q_EMIT statusChanged();
|
|
|
|
|
|
|
|
|
|
if (m_sessionStatus == SessionRunning) {
|
2025-08-06 20:06:17 +00:00
|
|
|
stopSession();
|
2025-07-28 17:37:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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();
|
|
|
|
|
|
2025-08-06 20:06:17 +00:00
|
|
|
connect(job, &KAuth::ExecuteJob::finished, this, [this](KJob *job, auto) {
|
|
|
|
|
removeWaydroidApplications();
|
2025-07-28 17:37:19 +00:00
|
|
|
|
2025-08-06 20:06:17 +00:00
|
|
|
if (job->error() == 0) {
|
|
|
|
|
m_status = NotInitialized;
|
|
|
|
|
} else {
|
|
|
|
|
Q_EMIT errorOccurred(i18n("Failed to reset Waydroid."), "");
|
2025-07-28 17:37:19 +00:00
|
|
|
|
2025-08-06 20:06:17 +00:00
|
|
|
m_status = Initialized;
|
|
|
|
|
qCWarning(WAYDROIDINTEGRATIONPLUGIN) << "KAuth returned an error code:" << job->error() << " message: " << job->errorString();
|
|
|
|
|
}
|
2025-07-28 17:37:19 +00:00
|
|
|
|
2025-08-06 20:06:17 +00:00
|
|
|
Q_EMIT statusChanged();
|
|
|
|
|
});
|
2025-12-15 02:10:54 +00:00
|
|
|
|
|
|
|
|
job->start();
|
2025-07-28 17:37:19 +00:00
|
|
|
}
|
|
|
|
|
|
2025-08-06 20:06:17 +00:00
|
|
|
void WaydroidDBusObject::installApk(const QString apkFile)
|
2025-07-10 16:00:41 +00:00
|
|
|
{
|
2025-08-06 20:06:17 +00:00
|
|
|
const QStringList arguments{u"app"_s, u"install"_s, apkFile};
|
2025-07-10 16:00:41 +00:00
|
|
|
|
2025-08-06 20:06:17 +00:00
|
|
|
QProcess *process = new QProcess(this);
|
|
|
|
|
|
|
|
|
|
connect(process, &QProcess::finished, this, [this, apkFile, process](int exitCode, QProcess::ExitStatus exitStatus) {
|
|
|
|
|
if (exitCode == 0 && exitStatus == QProcess::NormalExit) {
|
|
|
|
|
Q_EMIT actionFinished(i18n("Application has been installed"));
|
|
|
|
|
} else {
|
|
|
|
|
Q_EMIT actionFailed(i18n("Installation Failed"));
|
|
|
|
|
qCWarning(WAYDROIDINTEGRATIONPLUGIN) << "Error occurred during installation of " << apkFile << ": " << process->readAllStandardError();
|
|
|
|
|
}
|
|
|
|
|
});
|
2025-12-15 02:10:54 +00:00
|
|
|
|
|
|
|
|
process->start(WAYDROID_COMMAND, arguments);
|
2025-07-10 16:00:41 +00:00
|
|
|
}
|
|
|
|
|
|
2025-08-06 20:06:17 +00:00
|
|
|
void WaydroidDBusObject::deleteApplication(const QString appId)
|
2025-07-12 12:54:24 +00:00
|
|
|
{
|
2025-08-06 20:06:17 +00:00
|
|
|
const QStringList arguments{u"app"_s, u"remove"_s, appId};
|
|
|
|
|
|
|
|
|
|
QProcess *process = new QProcess(this);
|
|
|
|
|
|
|
|
|
|
connect(process, &QProcess::finished, this, [this, appId, process](int exitCode, QProcess::ExitStatus exitStatus) {
|
|
|
|
|
Q_UNUSED(exitCode);
|
|
|
|
|
Q_UNUSED(exitStatus);
|
|
|
|
|
|
|
|
|
|
const QByteArray errorLog = process->readAllStandardError();
|
|
|
|
|
|
|
|
|
|
// "waydroid app remove" send log on stderr but keep exitCode to 0
|
|
|
|
|
if (errorLog.isEmpty()) {
|
|
|
|
|
Q_EMIT actionFinished(i18n("Application has been deleted"));
|
|
|
|
|
} else {
|
|
|
|
|
Q_EMIT actionFailed(i18n("Application uninstall failed"));
|
|
|
|
|
qCWarning(WAYDROIDINTEGRATIONPLUGIN) << "Error occurred during uninstallation of " << appId << ": " << errorLog;
|
|
|
|
|
}
|
|
|
|
|
});
|
2025-12-15 02:10:54 +00:00
|
|
|
|
|
|
|
|
process->start(WAYDROID_COMMAND, arguments);
|
2025-07-12 12:54:24 +00:00
|
|
|
}
|
|
|
|
|
|
2025-08-06 20:06:17 +00:00
|
|
|
int WaydroidDBusObject::status() const
|
2025-07-10 16:00:41 +00:00
|
|
|
{
|
2025-08-06 20:06:17 +00:00
|
|
|
return m_status;
|
2025-07-10 16:00:41 +00:00
|
|
|
}
|
|
|
|
|
|
2025-08-06 20:06:17 +00:00
|
|
|
int WaydroidDBusObject::sessionStatus() const
|
2025-07-10 16:00:41 +00:00
|
|
|
{
|
2025-08-06 20:06:17 +00:00
|
|
|
return m_sessionStatus;
|
2025-07-10 16:00:41 +00:00
|
|
|
}
|
|
|
|
|
|
2025-08-06 20:06:17 +00:00
|
|
|
int WaydroidDBusObject::systemType() const
|
2025-07-10 16:00:41 +00:00
|
|
|
{
|
2025-08-06 20:06:17 +00:00
|
|
|
return m_systemType;
|
2025-07-10 16:00:41 +00:00
|
|
|
}
|
|
|
|
|
|
2025-08-06 20:06:17 +00:00
|
|
|
QString WaydroidDBusObject::ipAddress() const
|
2025-07-10 16:57:34 +00:00
|
|
|
{
|
2025-08-06 20:06:17 +00:00
|
|
|
return m_ipAddress;
|
2025-07-10 16:57:34 +00:00
|
|
|
}
|
|
|
|
|
|
2025-08-06 20:06:17 +00:00
|
|
|
QString WaydroidDBusObject::androidId() const
|
2025-07-14 10:47:21 +00:00
|
|
|
{
|
2025-08-06 20:06:17 +00:00
|
|
|
return m_androidId;
|
2025-07-14 10:47:21 +00:00
|
|
|
}
|
|
|
|
|
|
2025-08-06 20:06:17 +00:00
|
|
|
bool WaydroidDBusObject::multiWindows() const
|
2025-07-10 16:00:41 +00:00
|
|
|
{
|
|
|
|
|
return m_multiWindows;
|
|
|
|
|
}
|
|
|
|
|
|
2025-08-06 20:06:17 +00:00
|
|
|
void WaydroidDBusObject::setMultiWindows(const bool multiWindows)
|
2025-07-10 16:00:41 +00:00
|
|
|
{
|
|
|
|
|
if (m_multiWindows == multiWindows) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const QString value = multiWindows ? "true" : "false";
|
|
|
|
|
|
2025-12-15 02:10:54 +00:00
|
|
|
// Run coroutine asynchronously with QPointer to safely handle 'this'
|
|
|
|
|
auto coro = [](WaydroidDBusObject *self, QString value, bool multiWindows) -> QCoro::Task<void> {
|
|
|
|
|
QPointer<WaydroidDBusObject> guard(self);
|
|
|
|
|
if (co_await self->writePropValue(MULTI_WINDOWS_PROP_KEY, value)) {
|
|
|
|
|
if (guard) {
|
|
|
|
|
self->m_multiWindows = multiWindows;
|
|
|
|
|
Q_EMIT self->multiWindowsChanged();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
coro(this, value, multiWindows);
|
2025-07-10 16:00:41 +00:00
|
|
|
}
|
|
|
|
|
|
2025-08-06 20:06:17 +00:00
|
|
|
bool WaydroidDBusObject::suspend() const
|
2025-07-10 16:00:41 +00:00
|
|
|
{
|
|
|
|
|
return m_suspend;
|
|
|
|
|
}
|
|
|
|
|
|
2025-08-06 20:06:17 +00:00
|
|
|
void WaydroidDBusObject::setSuspend(const bool suspend)
|
2025-07-10 16:00:41 +00:00
|
|
|
{
|
|
|
|
|
if (m_suspend == suspend) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const QString value = suspend ? "true" : "false";
|
|
|
|
|
|
2025-12-15 02:10:54 +00:00
|
|
|
auto coro = [](WaydroidDBusObject *self, QString value, bool suspend) -> QCoro::Task<void> {
|
|
|
|
|
QPointer<WaydroidDBusObject> guard(self);
|
|
|
|
|
if (co_await self->writePropValue(SUSPEND_PROP_KEY, value)) {
|
|
|
|
|
if (guard) {
|
|
|
|
|
self->m_suspend = suspend;
|
|
|
|
|
Q_EMIT self->suspendChanged();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
coro(this, value, suspend);
|
2025-07-10 16:00:41 +00:00
|
|
|
}
|
|
|
|
|
|
2025-08-06 20:06:17 +00:00
|
|
|
bool WaydroidDBusObject::uevent() const
|
2025-07-10 16:00:41 +00:00
|
|
|
{
|
|
|
|
|
return m_uevent;
|
|
|
|
|
}
|
|
|
|
|
|
2025-08-06 20:06:17 +00:00
|
|
|
void WaydroidDBusObject::setUevent(const bool uevent)
|
2025-07-10 16:00:41 +00:00
|
|
|
{
|
|
|
|
|
if (m_uevent == uevent) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const QString value = uevent ? "true" : "false";
|
|
|
|
|
|
2025-12-15 02:10:54 +00:00
|
|
|
auto coro = [](WaydroidDBusObject *self, QString value, bool uevent) -> QCoro::Task<void> {
|
|
|
|
|
QPointer<WaydroidDBusObject> guard(self);
|
|
|
|
|
if (co_await self->writePropValue(UEVENT_PROP_KEY, value)) {
|
|
|
|
|
if (guard) {
|
|
|
|
|
self->m_uevent = uevent;
|
|
|
|
|
Q_EMIT self->ueventChanged();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
coro(this, value, uevent);
|
2025-07-10 16:00:41 +00:00
|
|
|
}
|
|
|
|
|
|
2025-08-06 20:06:17 +00:00
|
|
|
QList<QDBusObjectPath> WaydroidDBusObject::applications() const
|
|
|
|
|
{
|
|
|
|
|
QList<QDBusObjectPath> paths;
|
|
|
|
|
for (const auto &app : m_applicationObjects) {
|
|
|
|
|
paths.push_back(app->objectPath());
|
|
|
|
|
}
|
|
|
|
|
return paths;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void WaydroidDBusObject::refreshSupportsInfo()
|
|
|
|
|
{
|
|
|
|
|
const QStringList arguments{u"-h"_s};
|
|
|
|
|
|
2025-12-15 02:10:54 +00:00
|
|
|
auto process = new QProcess(this);
|
|
|
|
|
process->start(WAYDROID_COMMAND, arguments);
|
2025-08-06 20:06:17 +00:00
|
|
|
|
2025-12-15 02:10:54 +00:00
|
|
|
connect(process, &QProcess::finished, this, [this, process](int exitCode, QProcess::ExitStatus exitStatus) {
|
|
|
|
|
Q_UNUSED(exitCode)
|
|
|
|
|
Q_UNUSED(exitStatus)
|
2025-08-06 20:06:17 +00:00
|
|
|
|
2025-12-15 02:10:54 +00:00
|
|
|
process->deleteLater();
|
|
|
|
|
|
|
|
|
|
if (process->exitCode() != 0) {
|
|
|
|
|
m_status = NotSupported;
|
|
|
|
|
Q_EMIT statusChanged();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fetchSessionInfo().then([this](const QString &output) {
|
|
|
|
|
if (!output.contains("WayDroid is not initialized")) {
|
|
|
|
|
m_status = Initialized;
|
|
|
|
|
} else {
|
|
|
|
|
m_status = NotInitialized;
|
|
|
|
|
}
|
|
|
|
|
Q_EMIT statusChanged();
|
|
|
|
|
});
|
|
|
|
|
});
|
2025-08-06 20:06:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void WaydroidDBusObject::refreshInstallationInfo()
|
|
|
|
|
{
|
|
|
|
|
if (m_status != Initialized) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QFile file("/var/lib/waydroid/waydroid.cfg");
|
|
|
|
|
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QTextStream in(&file);
|
|
|
|
|
const QString fileContent = in.readAll();
|
|
|
|
|
|
|
|
|
|
const QString systemMatch = extractRegExp(fileContent, systemOtaRegExp);
|
|
|
|
|
if (systemMatch.contains("vanilla", Qt::CaseInsensitive)) {
|
|
|
|
|
m_systemType = Vanilla;
|
|
|
|
|
} else if (systemMatch.contains("gapps", Qt::CaseInsensitive)) {
|
|
|
|
|
m_systemType = Gapps;
|
|
|
|
|
} else if (systemMatch.contains("foss", Qt::CaseInsensitive)) {
|
|
|
|
|
m_systemType = Foss;
|
|
|
|
|
} else {
|
|
|
|
|
m_systemType = UnknownSystemType;
|
|
|
|
|
}
|
|
|
|
|
Q_EMIT systemTypeChanged();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void WaydroidDBusObject::refreshSessionInfo()
|
|
|
|
|
{
|
|
|
|
|
if (m_status != Initialized) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2025-12-15 02:10:54 +00:00
|
|
|
auto coro = [](WaydroidDBusObject *self) -> QCoro::Task<void> {
|
|
|
|
|
QPointer<WaydroidDBusObject> guard(self);
|
|
|
|
|
const QString output = co_await self->fetchSessionInfo();
|
2025-08-06 20:06:17 +00:00
|
|
|
|
2025-12-15 02:10:54 +00:00
|
|
|
if (!guard) {
|
|
|
|
|
co_return;
|
|
|
|
|
}
|
2025-08-06 20:06:17 +00:00
|
|
|
|
2025-12-15 02:10:54 +00:00
|
|
|
const QString sessionMatchResult = self->extractRegExp(output, sessionRegExp);
|
|
|
|
|
SessionStatus newSessionStatus;
|
2025-08-06 20:06:17 +00:00
|
|
|
|
2025-12-15 02:10:54 +00:00
|
|
|
if (!sessionMatchResult.isEmpty()) {
|
|
|
|
|
newSessionStatus = sessionMatchResult.contains("RUNNING") ? SessionRunning : SessionStopped;
|
|
|
|
|
} else {
|
|
|
|
|
newSessionStatus = SessionStopped;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (self->m_sessionStatus != newSessionStatus) {
|
|
|
|
|
self->m_sessionStatus = newSessionStatus;
|
|
|
|
|
Q_EMIT self->sessionStatusChanged();
|
|
|
|
|
}
|
2025-08-06 20:06:17 +00:00
|
|
|
|
2025-12-15 02:10:54 +00:00
|
|
|
const QString newIpAddress = self->extractRegExp(output, ipAddressRegExp);
|
|
|
|
|
if (self->m_ipAddress != newIpAddress) {
|
|
|
|
|
self->m_ipAddress = self->extractRegExp(output, ipAddressRegExp);
|
|
|
|
|
Q_EMIT self->ipAddressChanged();
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
coro(this);
|
2025-08-06 20:06:17 +00:00
|
|
|
}
|
|
|
|
|
|
2025-12-15 02:10:54 +00:00
|
|
|
QCoro::Task<QString> WaydroidDBusObject::fetchSessionInfo()
|
2025-07-10 16:00:41 +00:00
|
|
|
{
|
|
|
|
|
const QStringList arguments{u"status"_s};
|
|
|
|
|
|
2025-12-15 02:10:54 +00:00
|
|
|
auto *basicProcess = new QProcess(this);
|
|
|
|
|
auto process = qCoro(basicProcess);
|
|
|
|
|
co_await process.start(WAYDROID_COMMAND, arguments);
|
|
|
|
|
co_await process.waitForFinished();
|
2025-07-10 16:00:41 +00:00
|
|
|
|
2025-12-15 02:10:54 +00:00
|
|
|
const QString output = basicProcess->readAllStandardOutput();
|
|
|
|
|
basicProcess->deleteLater();
|
|
|
|
|
co_return output;
|
2025-07-10 16:00:41 +00:00
|
|
|
}
|
|
|
|
|
|
2025-08-06 20:06:17 +00:00
|
|
|
void WaydroidDBusObject::refreshAndroidId()
|
|
|
|
|
{
|
|
|
|
|
if (m_status != Initialized) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
KAuth::Action writeAction(u"org.kde.plasma.mobileshell.waydroidhelper.getandroidid"_s);
|
|
|
|
|
writeAction.setHelperId(u"org.kde.plasma.mobileshell.waydroidhelper"_s);
|
|
|
|
|
|
|
|
|
|
KAuth::ExecuteJob *job = writeAction.execute();
|
|
|
|
|
|
|
|
|
|
connect(job, &KAuth::ExecuteJob::finished, this, [this](KJob *job, auto) {
|
|
|
|
|
KAuth::ExecuteJob *executeJob = dynamic_cast<KAuth::ExecuteJob *>(job);
|
|
|
|
|
if (executeJob->error() == 0) {
|
|
|
|
|
m_androidId = executeJob->data()["android_id"].toString();
|
2025-08-14 21:38:53 +00:00
|
|
|
|
|
|
|
|
if (m_androidId.isEmpty()) {
|
|
|
|
|
Q_EMIT actionFailed(i18n("Android ID not found"));
|
|
|
|
|
}
|
2025-08-06 20:06:17 +00:00
|
|
|
} else {
|
|
|
|
|
m_androidId = "";
|
|
|
|
|
qCWarning(WAYDROIDINTEGRATIONPLUGIN) << "KAuth returned an error code:" << executeJob->error();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Q_EMIT androidIdChanged();
|
|
|
|
|
});
|
2025-12-15 02:10:54 +00:00
|
|
|
|
|
|
|
|
job->start();
|
2025-08-06 20:06:17 +00:00
|
|
|
}
|
|
|
|
|
|
2025-12-15 02:10:54 +00:00
|
|
|
QCoro::Task<void> WaydroidDBusObject::refreshPropsInfo()
|
2025-08-06 20:06:17 +00:00
|
|
|
{
|
|
|
|
|
if (m_sessionStatus != SessionRunning) {
|
2025-12-15 02:10:54 +00:00
|
|
|
co_return;
|
2025-08-06 20:06:17 +00:00
|
|
|
}
|
|
|
|
|
|
2025-12-15 02:10:54 +00:00
|
|
|
const QString multiWindowsPropValue = co_await fetchPropValue(MULTI_WINDOWS_PROP_KEY, "false");
|
2025-08-06 20:06:17 +00:00
|
|
|
m_multiWindows = multiWindowsPropValue == "true";
|
|
|
|
|
Q_EMIT multiWindowsChanged();
|
|
|
|
|
|
2025-12-15 02:10:54 +00:00
|
|
|
const QString suspendPropValue = co_await fetchPropValue(SUSPEND_PROP_KEY, "true");
|
2025-08-06 20:06:17 +00:00
|
|
|
m_suspend = suspendPropValue == "true";
|
|
|
|
|
Q_EMIT suspendChanged();
|
|
|
|
|
|
2025-12-15 02:10:54 +00:00
|
|
|
const QString ueventPropValue = co_await fetchPropValue(UEVENT_PROP_KEY, "false");
|
2025-08-06 20:06:17 +00:00
|
|
|
m_uevent = ueventPropValue == "true";
|
|
|
|
|
Q_EMIT ueventChanged();
|
|
|
|
|
}
|
|
|
|
|
|
2025-12-15 02:10:54 +00:00
|
|
|
QCoro::Task<QString> WaydroidDBusObject::fetchPropValue(const QString key, const QString defaultValue)
|
2025-07-10 16:00:41 +00:00
|
|
|
{
|
|
|
|
|
const QStringList arguments{u"prop"_s, u"get"_s, key};
|
|
|
|
|
|
2025-12-15 02:10:54 +00:00
|
|
|
auto *basicProcess = new QProcess(this);
|
|
|
|
|
auto process = qCoro(basicProcess);
|
|
|
|
|
co_await process.start(WAYDROID_COMMAND, arguments);
|
|
|
|
|
co_await process.waitForFinished();
|
2025-07-10 16:00:41 +00:00
|
|
|
|
2025-12-15 02:10:54 +00:00
|
|
|
const QString commandOutput = basicProcess->readAllStandardOutput();
|
|
|
|
|
basicProcess->deleteLater();
|
2025-07-10 16:00:41 +00:00
|
|
|
const QString value = commandOutput.split("\n").first().trimmed();
|
|
|
|
|
|
|
|
|
|
if (value.isEmpty()) {
|
2025-12-15 02:10:54 +00:00
|
|
|
co_return defaultValue;
|
2025-07-10 16:00:41 +00:00
|
|
|
}
|
|
|
|
|
|
2025-12-15 02:10:54 +00:00
|
|
|
co_return value;
|
2025-07-10 16:00:41 +00:00
|
|
|
}
|
|
|
|
|
|
2025-12-15 02:10:54 +00:00
|
|
|
QCoro::Task<bool> WaydroidDBusObject::writePropValue(const QString key, const QString value)
|
2025-07-10 16:00:41 +00:00
|
|
|
{
|
|
|
|
|
const QStringList arguments{u"prop"_s, u"set"_s, key, value};
|
|
|
|
|
|
2025-12-15 02:10:54 +00:00
|
|
|
auto *basicProcess = new QProcess(this);
|
|
|
|
|
auto process = qCoro(basicProcess);
|
|
|
|
|
co_await process.start(WAYDROID_COMMAND, arguments);
|
|
|
|
|
co_await process.waitForFinished();
|
2025-07-10 16:00:41 +00:00
|
|
|
|
2025-12-15 02:10:54 +00:00
|
|
|
const bool success = basicProcess->exitCode() == 0;
|
|
|
|
|
basicProcess->deleteLater();
|
|
|
|
|
co_return success;
|
2025-07-10 16:00:41 +00:00
|
|
|
}
|
|
|
|
|
|
2025-08-06 20:06:17 +00:00
|
|
|
QString WaydroidDBusObject::extractRegExp(const QString text, const QRegularExpression regExp) const
|
2025-07-10 16:00:41 +00:00
|
|
|
{
|
|
|
|
|
const QRegularExpressionMatch match = regExp.match(text);
|
|
|
|
|
|
|
|
|
|
if (match.hasMatch() && match.lastCapturedIndex() > 0) {
|
|
|
|
|
return match.captured(match.lastCapturedIndex());
|
|
|
|
|
} else {
|
|
|
|
|
return "";
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-08-06 20:06:17 +00:00
|
|
|
void WaydroidDBusObject::checkSessionStarting(const int limit, const int tried)
|
2025-07-10 16:00:41 +00:00
|
|
|
{
|
|
|
|
|
if (m_sessionStatus != SessionStarting) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2025-12-15 02:10:54 +00:00
|
|
|
auto coro = [](WaydroidDBusObject *self, int limit, int tried) -> QCoro::Task<void> {
|
|
|
|
|
QPointer<WaydroidDBusObject> guard(self);
|
|
|
|
|
const QString output = co_await self->fetchSessionInfo();
|
2025-07-10 16:00:41 +00:00
|
|
|
|
2025-12-15 02:10:54 +00:00
|
|
|
if (!guard) {
|
|
|
|
|
co_return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const QString sessionMatchResult = self->extractRegExp(output, sessionRegExp);
|
|
|
|
|
|
|
|
|
|
if (sessionMatchResult.contains("RUNNING")) {
|
|
|
|
|
self->m_sessionStatus = SessionRunning;
|
|
|
|
|
Q_EMIT self->sessionStatusChanged();
|
|
|
|
|
} else if (tried == limit) {
|
|
|
|
|
self->m_sessionStatus = SessionStopped;
|
|
|
|
|
Q_EMIT self->sessionStatusChanged();
|
|
|
|
|
qCWarning(WAYDROIDINTEGRATIONPLUGIN) << "Failed to start the session after " << tried << " tries";
|
|
|
|
|
} else {
|
|
|
|
|
QTimer::singleShot(500, self, [self, tried, limit]() {
|
|
|
|
|
if (self) {
|
|
|
|
|
self->checkSessionStarting(limit, tried + 1);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
coro(this, limit, tried);
|
2025-07-10 16:00:41 +00:00
|
|
|
}
|
2025-07-28 17:37:19 +00:00
|
|
|
|
2025-08-06 20:06:17 +00:00
|
|
|
QString WaydroidDBusObject::desktopFileDirectory()
|
2025-07-28 17:37:19 +00:00
|
|
|
{
|
|
|
|
|
auto dir = []() -> QString {
|
|
|
|
|
if (KSandbox::isFlatpak()) {
|
|
|
|
|
return qEnvironmentVariable("HOME") % u"/.local/share/applications/";
|
|
|
|
|
}
|
|
|
|
|
return QStandardPaths::writableLocation(QStandardPaths::ApplicationsLocation);
|
|
|
|
|
}();
|
|
|
|
|
|
|
|
|
|
QDir(dir).mkpath(QStringLiteral("."));
|
|
|
|
|
|
|
|
|
|
return dir;
|
|
|
|
|
}
|
|
|
|
|
|
2025-08-06 20:06:17 +00:00
|
|
|
bool WaydroidDBusObject::removeWaydroidApplications()
|
2025-07-28 17:37:19 +00:00
|
|
|
{
|
|
|
|
|
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;
|
|
|
|
|
}
|
2025-08-06 20:06:17 +00:00
|
|
|
|
|
|
|
|
void WaydroidDBusObject::refreshApplications()
|
|
|
|
|
{
|
|
|
|
|
if (m_sessionStatus != SessionRunning) {
|
|
|
|
|
// Clear existing applications when session is not running
|
|
|
|
|
for (const auto &appObject : m_applicationObjects) {
|
|
|
|
|
appObject->unregisterObject();
|
|
|
|
|
}
|
|
|
|
|
m_applicationObjects.clear();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2025-12-15 02:10:54 +00:00
|
|
|
auto coro = [](WaydroidDBusObject *self) -> QCoro::Task<void> {
|
|
|
|
|
QPointer<WaydroidDBusObject> guard(self);
|
|
|
|
|
const QString output = co_await self->fetchApplicationsList();
|
2025-08-06 20:06:17 +00:00
|
|
|
|
2025-12-15 02:10:54 +00:00
|
|
|
if (!guard) {
|
|
|
|
|
co_return;
|
|
|
|
|
}
|
2025-08-06 20:06:17 +00:00
|
|
|
|
2025-12-15 02:10:54 +00:00
|
|
|
if (output.isEmpty()) {
|
|
|
|
|
co_return;
|
|
|
|
|
}
|
2025-08-06 20:06:17 +00:00
|
|
|
|
2025-12-15 02:10:54 +00:00
|
|
|
QTextStream inFile(const_cast<QString *>(&output), QIODevice::ReadOnly);
|
|
|
|
|
const auto newApplications = WaydroidApplicationDBusObject::parseApplicationsFromWaydroidLog(inFile);
|
2025-08-06 20:06:17 +00:00
|
|
|
|
2025-12-15 02:10:54 +00:00
|
|
|
// Create a map of existing applications by package name for efficient lookup
|
|
|
|
|
QMap<QString, int> existingAppMap;
|
|
|
|
|
for (int i = 0; i < self->m_applicationObjects.size(); ++i) {
|
|
|
|
|
const auto &application = self->m_applicationObjects[i];
|
|
|
|
|
existingAppMap.insert(application->packageName(), i);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QList<WaydroidApplicationDBusObject::Ptr> toInsert;
|
|
|
|
|
|
|
|
|
|
// Check which applications need to be added or are already present
|
|
|
|
|
for (const auto &application : newApplications) {
|
|
|
|
|
if (!application->name().isEmpty() && !application->packageName().isEmpty()) {
|
|
|
|
|
auto it = existingAppMap.find(application->packageName());
|
|
|
|
|
if (it != existingAppMap.end()) {
|
|
|
|
|
// Application already exists, remove from map to mark as kept
|
|
|
|
|
existingAppMap.erase(it);
|
|
|
|
|
} else {
|
|
|
|
|
// Application needs to be inserted
|
|
|
|
|
toInsert.append(application);
|
|
|
|
|
}
|
2025-08-06 20:06:17 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-12-15 02:10:54 +00:00
|
|
|
// Remove applications that are no longer present
|
|
|
|
|
QList<int> toRemove;
|
|
|
|
|
for (const int index : existingAppMap.values()) {
|
|
|
|
|
toRemove.append(index);
|
|
|
|
|
}
|
2025-08-06 20:06:17 +00:00
|
|
|
|
2025-12-15 02:10:54 +00:00
|
|
|
std::sort(toRemove.begin(), toRemove.end());
|
2025-08-06 20:06:17 +00:00
|
|
|
|
2025-12-15 02:10:54 +00:00
|
|
|
// Remove indices from end to start to avoid index shifting
|
|
|
|
|
for (int i = toRemove.size() - 1; i >= 0; --i) {
|
|
|
|
|
int ind = toRemove[i];
|
|
|
|
|
const auto application = self->m_applicationObjects[ind];
|
|
|
|
|
self->m_applicationObjects.removeAt(ind);
|
|
|
|
|
Q_EMIT self->applicationRemoved(application->objectPath());
|
|
|
|
|
application->unregisterObject();
|
|
|
|
|
}
|
2025-08-06 20:06:17 +00:00
|
|
|
|
2025-12-15 02:10:54 +00:00
|
|
|
// Add new applications and register them
|
|
|
|
|
for (const auto &application : toInsert) {
|
|
|
|
|
application->registerObject();
|
|
|
|
|
self->m_applicationObjects.append(application);
|
|
|
|
|
Q_EMIT self->applicationAdded(application->objectPath());
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
coro(this);
|
2025-08-06 20:06:17 +00:00
|
|
|
}
|
|
|
|
|
|
2025-12-15 02:10:54 +00:00
|
|
|
QCoro::Task<QString> WaydroidDBusObject::fetchApplicationsList()
|
2025-08-06 20:06:17 +00:00
|
|
|
{
|
|
|
|
|
const QStringList arguments{u"app"_s, u"list"_s};
|
|
|
|
|
|
2025-12-15 02:10:54 +00:00
|
|
|
auto *basicProcess = new QProcess(this);
|
|
|
|
|
auto process = qCoro(basicProcess);
|
|
|
|
|
co_await process.start(WAYDROID_COMMAND, arguments);
|
|
|
|
|
co_await process.waitForFinished();
|
2025-08-06 20:06:17 +00:00
|
|
|
|
2025-12-15 02:10:54 +00:00
|
|
|
if (basicProcess->exitCode() != 0) {
|
|
|
|
|
qCWarning(WAYDROIDINTEGRATIONPLUGIN) << "Failed to fetch applications list: " << basicProcess->readAllStandardError();
|
|
|
|
|
basicProcess->deleteLater();
|
|
|
|
|
co_return QString{};
|
2025-08-06 20:06:17 +00:00
|
|
|
}
|
|
|
|
|
|
2025-12-15 02:10:54 +00:00
|
|
|
const QString output = basicProcess->readAllStandardOutput();
|
|
|
|
|
basicProcess->deleteLater();
|
|
|
|
|
co_return output;
|
2025-08-06 20:06:17 +00:00
|
|
|
}
|