forked from qt-creator/qt-creator
DeviceShare: Add Android app connector and device manager
Change-Id: I30cb3dc8b71e87fc27482aa503cb53ce98c6bde0 Reviewed-by: Henning Gründl <henning.gruendl@qt.io>
This commit is contained in:
@@ -34,6 +34,8 @@ add_feature_info("Meta info tracing" ${ENABLE_METAINFO_TRACING} "")
|
|||||||
|
|
||||||
add_subdirectory(libs)
|
add_subdirectory(libs)
|
||||||
|
|
||||||
|
find_package(Qt6 REQUIRED COMPONENTS WebSockets)
|
||||||
|
|
||||||
add_qtc_plugin(QmlDesigner
|
add_qtc_plugin(QmlDesigner
|
||||||
PLUGIN_RECOMMENDS QmlPreview
|
PLUGIN_RECOMMENDS QmlPreview
|
||||||
CONDITION TARGET QmlDesignerCore AND TARGET Qt::QuickWidgets AND TARGET Qt::Svg
|
CONDITION TARGET QmlDesignerCore AND TARGET Qt::QuickWidgets AND TARGET Qt::Svg
|
||||||
@@ -728,6 +730,17 @@ extend_qtc_plugin(QmlDesigner
|
|||||||
messagemodel.h
|
messagemodel.h
|
||||||
)
|
)
|
||||||
|
|
||||||
|
extend_qtc_plugin(QmlDesigner
|
||||||
|
SOURCES_PREFIX components/devicesharing
|
||||||
|
DEPENDS
|
||||||
|
Qt::WebSockets
|
||||||
|
SOURCES
|
||||||
|
device.cpp device.h
|
||||||
|
deviceinfo.cpp deviceinfo.h
|
||||||
|
devicemanager.cpp devicemanager.h
|
||||||
|
devicemanagermodel.cpp devicemanagermodel.h
|
||||||
|
)
|
||||||
|
|
||||||
add_qtc_plugin(assetexporterplugin
|
add_qtc_plugin(assetexporterplugin
|
||||||
PLUGIN_CLASS AssetExporterPlugin
|
PLUGIN_CLASS AssetExporterPlugin
|
||||||
CONDITION TARGET QmlDesigner
|
CONDITION TARGET QmlDesigner
|
||||||
|
198
src/plugins/qmldesigner/components/devicesharing/device.cpp
Normal file
198
src/plugins/qmldesigner/components/devicesharing/device.cpp
Normal file
@@ -0,0 +1,198 @@
|
|||||||
|
// Copyright (C) 2024 The Qt Company Ltd.
|
||||||
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||||
|
|
||||||
|
#include "device.h"
|
||||||
|
|
||||||
|
#include <QJsonDocument>
|
||||||
|
#include <QLatin1String>
|
||||||
|
|
||||||
|
namespace QmlDesigner::DeviceShare {
|
||||||
|
|
||||||
|
// Below are the constants that are used in the communication between the Design Studio and the device.
|
||||||
|
namespace PackageToDevice {
|
||||||
|
using namespace Qt::Literals;
|
||||||
|
constexpr auto designStudioReady = "designStudioReady"_L1;
|
||||||
|
constexpr auto projectData = "projectData"_L1;
|
||||||
|
constexpr auto stopRunningProject = "stopRunningProject"_L1;
|
||||||
|
}; // namespace PackageToDevice
|
||||||
|
|
||||||
|
namespace PackageFromDevice {
|
||||||
|
using namespace Qt::Literals;
|
||||||
|
constexpr auto deviceInfo = "deviceInfo"_L1;
|
||||||
|
constexpr auto projectRunning = "projectRunning"_L1;
|
||||||
|
constexpr auto projectStopped = "projectStopped"_L1;
|
||||||
|
constexpr auto projectLogs = "projectLogs"_L1;
|
||||||
|
}; // namespace PackageFromDevice
|
||||||
|
|
||||||
|
Device::Device(const DeviceInfo &deviceInfo, const DeviceSettings &deviceSettings, QObject *parent)
|
||||||
|
: QObject(parent)
|
||||||
|
, m_deviceInfo(deviceInfo)
|
||||||
|
, m_deviceSettings(deviceSettings)
|
||||||
|
, m_socket(nullptr)
|
||||||
|
, m_socketWasConnected(false)
|
||||||
|
{
|
||||||
|
qCDebug(deviceSharePluginLog) << "initial device info:" << m_deviceInfo;
|
||||||
|
|
||||||
|
m_socket.reset(new QWebSocket());
|
||||||
|
connect(m_socket.data(), &QWebSocket::textMessageReceived, this, &Device::processTextMessage);
|
||||||
|
connect(m_socket.data(), &QWebSocket::disconnected, this, [this]() {
|
||||||
|
m_reconnectTimer.start();
|
||||||
|
if (!m_socketWasConnected)
|
||||||
|
return;
|
||||||
|
|
||||||
|
m_socketWasConnected = false;
|
||||||
|
m_pingTimer.stop();
|
||||||
|
m_pongTimer.stop();
|
||||||
|
emit disconnected(m_deviceInfo.deviceId());
|
||||||
|
});
|
||||||
|
connect(m_socket.data(), &QWebSocket::connected, this, [this]() {
|
||||||
|
m_socketWasConnected = true;
|
||||||
|
m_reconnectTimer.stop();
|
||||||
|
m_pingTimer.start(15000);
|
||||||
|
sendDesignStudioReady(m_deviceInfo.deviceId());
|
||||||
|
emit connected(m_deviceInfo.deviceId());
|
||||||
|
});
|
||||||
|
|
||||||
|
m_reconnectTimer.setSingleShot(true);
|
||||||
|
m_reconnectTimer.setInterval(5000);
|
||||||
|
connect(&m_reconnectTimer, &QTimer::timeout, this, &Device::reconnect);
|
||||||
|
|
||||||
|
initPingPong();
|
||||||
|
reconnect();
|
||||||
|
}
|
||||||
|
|
||||||
|
Device::~Device()
|
||||||
|
{
|
||||||
|
m_socket->close();
|
||||||
|
m_socket.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Device::initPingPong()
|
||||||
|
{
|
||||||
|
connect(&m_pingTimer, &QTimer::timeout, this, [this]() {
|
||||||
|
m_socket->ping();
|
||||||
|
m_pongTimer.start(15000);
|
||||||
|
});
|
||||||
|
|
||||||
|
connect(m_socket.data(),
|
||||||
|
&QWebSocket::pong,
|
||||||
|
this,
|
||||||
|
[this](quint64 elapsedTime, [[maybe_unused]] const QByteArray &payload) {
|
||||||
|
qCDebug(deviceSharePluginLog)
|
||||||
|
<< "Pong received from Device" << m_deviceInfo.deviceId() << "in" << elapsedTime
|
||||||
|
<< "ms";
|
||||||
|
m_pongTimer.stop();
|
||||||
|
});
|
||||||
|
|
||||||
|
connect(&m_pongTimer, &QTimer::timeout, this, [this]() {
|
||||||
|
qCDebug(deviceSharePluginLog)
|
||||||
|
<< "Device" << m_deviceInfo.deviceId() << "is not responding. Closing connection.";
|
||||||
|
m_socket->close();
|
||||||
|
});
|
||||||
|
|
||||||
|
m_pongTimer.setSingleShot(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Device::reconnect()
|
||||||
|
{
|
||||||
|
if (m_socket->state() == QAbstractSocket::ConnectedState)
|
||||||
|
m_socket->close();
|
||||||
|
|
||||||
|
QUrl url(QStringLiteral("ws://%1:%2").arg(m_deviceSettings.ipAddress()).arg(40000));
|
||||||
|
m_socket->open(url);
|
||||||
|
}
|
||||||
|
|
||||||
|
DeviceInfo Device::deviceInfo() const
|
||||||
|
{
|
||||||
|
return m_deviceInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Device::setDeviceInfo(const DeviceInfo &deviceInfo)
|
||||||
|
{
|
||||||
|
m_deviceInfo = deviceInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
DeviceSettings Device::deviceSettings() const
|
||||||
|
{
|
||||||
|
return m_deviceSettings;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Device::setDeviceSettings(const DeviceSettings &deviceSettings)
|
||||||
|
{
|
||||||
|
m_deviceSettings = deviceSettings;
|
||||||
|
reconnect();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Device::sendDesignStudioReady(const QString &uuid) {
|
||||||
|
sendTextMessage(PackageToDevice::designStudioReady, uuid);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Device::sendProjectNotification()
|
||||||
|
{
|
||||||
|
sendTextMessage(PackageToDevice::projectData);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Device::sendProjectData(const QByteArray &data)
|
||||||
|
{
|
||||||
|
sendBinaryMessage(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Device::sendProjectStopped()
|
||||||
|
{
|
||||||
|
sendTextMessage(PackageToDevice::stopRunningProject);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Device::isConnected() const
|
||||||
|
{
|
||||||
|
return m_socket ? m_socket->state() == QAbstractSocket::ConnectedState : false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Device::sendTextMessage(const QLatin1String &dataType, const QJsonValue &data)
|
||||||
|
{
|
||||||
|
if (!isConnected())
|
||||||
|
return;
|
||||||
|
|
||||||
|
QJsonObject message;
|
||||||
|
message["dataType"] = dataType;
|
||||||
|
message["data"] = data;
|
||||||
|
const QString jsonMessage = QString::fromLatin1(
|
||||||
|
QJsonDocument(message).toJson(QJsonDocument::Compact));
|
||||||
|
m_socket->sendTextMessage(jsonMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Device::sendBinaryMessage(const QByteArray &data)
|
||||||
|
{
|
||||||
|
if (!isConnected())
|
||||||
|
return;
|
||||||
|
|
||||||
|
m_socket->sendBinaryMessage(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Device::processTextMessage(const QString &data)
|
||||||
|
{
|
||||||
|
QJsonParseError jsonError;
|
||||||
|
const QJsonDocument jsonDoc = QJsonDocument::fromJson(data.toLatin1(), &jsonError);
|
||||||
|
if (jsonError.error != QJsonParseError::NoError) {
|
||||||
|
qCDebug(deviceSharePluginLog)
|
||||||
|
<< "Failed to parse JSON message:" << jsonError.errorString() << data;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const QJsonObject jsonObj = jsonDoc.object();
|
||||||
|
const QString dataType = jsonObj.value("dataType").toString();
|
||||||
|
if (dataType == PackageFromDevice::deviceInfo) {
|
||||||
|
QJsonObject deviceInfo = jsonObj.value("data").toObject();
|
||||||
|
m_deviceInfo.setJsonObject(deviceInfo);
|
||||||
|
emit deviceInfoReady(m_deviceInfo.deviceId(), m_deviceInfo);
|
||||||
|
} else if (dataType == PackageFromDevice::projectRunning) {
|
||||||
|
emit projectStarted(m_deviceInfo.deviceId());
|
||||||
|
} else if (dataType == PackageFromDevice::projectStopped) {
|
||||||
|
emit projectStopped(m_deviceInfo.deviceId());
|
||||||
|
} else if (dataType == PackageFromDevice::projectLogs) {
|
||||||
|
emit projectLogsReceived(m_deviceInfo.deviceId(), jsonObj.value("data").toString());
|
||||||
|
} else {
|
||||||
|
qCDebug(deviceSharePluginLog) << "Invalid JSON message:" << jsonObj;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace QmlDesigner::DeviceShare
|
64
src/plugins/qmldesigner/components/devicesharing/device.h
Normal file
64
src/plugins/qmldesigner/components/devicesharing/device.h
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
// Copyright (C) 2024 The Qt Company Ltd.
|
||||||
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QTimer>
|
||||||
|
#include <QWebSocket>
|
||||||
|
|
||||||
|
#include "deviceinfo.h"
|
||||||
|
|
||||||
|
namespace QmlDesigner::DeviceShare {
|
||||||
|
|
||||||
|
class Device : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
Device(const DeviceInfo &deviceInfo = {},
|
||||||
|
const DeviceSettings &deviceSettings = {},
|
||||||
|
QObject *parent = nullptr);
|
||||||
|
~Device();
|
||||||
|
|
||||||
|
// device management
|
||||||
|
DeviceInfo deviceInfo() const;
|
||||||
|
DeviceSettings deviceSettings() const;
|
||||||
|
void setDeviceInfo(const DeviceInfo &deviceInfo);
|
||||||
|
void setDeviceSettings(const DeviceSettings &deviceSettings);
|
||||||
|
|
||||||
|
// device communication
|
||||||
|
void sendDesignStudioReady(const QString &uuid);
|
||||||
|
void sendProjectNotification();
|
||||||
|
void sendProjectData(const QByteArray &data);
|
||||||
|
void sendProjectStopped();
|
||||||
|
|
||||||
|
// socket
|
||||||
|
bool isConnected() const;
|
||||||
|
void reconnect();
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void processTextMessage(const QString &data);
|
||||||
|
|
||||||
|
private:
|
||||||
|
DeviceInfo m_deviceInfo;
|
||||||
|
DeviceSettings m_deviceSettings;
|
||||||
|
|
||||||
|
QScopedPointer<QWebSocket> m_socket;
|
||||||
|
bool m_socketWasConnected;
|
||||||
|
|
||||||
|
QTimer m_reconnectTimer;
|
||||||
|
QTimer m_pingTimer;
|
||||||
|
QTimer m_pongTimer;
|
||||||
|
|
||||||
|
void initPingPong();
|
||||||
|
void sendTextMessage(const QLatin1String &dataType, const QJsonValue &data = QJsonValue());
|
||||||
|
void sendBinaryMessage(const QByteArray &data);
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void connected(const QString &deviceId);
|
||||||
|
void disconnected(const QString &deviceId);
|
||||||
|
void deviceInfoReady(const QString &deviceId, const DeviceInfo &deviceInfo);
|
||||||
|
void projectStarted(const QString &deviceId);
|
||||||
|
void projectStopped(const QString &deviceId);
|
||||||
|
void projectLogsReceived(const QString &deviceId, const QString &logs);
|
||||||
|
};
|
||||||
|
} // namespace QmlDesigner::DeviceShare
|
130
src/plugins/qmldesigner/components/devicesharing/deviceinfo.cpp
Normal file
130
src/plugins/qmldesigner/components/devicesharing/deviceinfo.cpp
Normal file
@@ -0,0 +1,130 @@
|
|||||||
|
// Copyright (C) 2024 The Qt Company Ltd.
|
||||||
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||||
|
|
||||||
|
#include "deviceinfo.h"
|
||||||
|
|
||||||
|
#include <QJsonDocument>
|
||||||
|
|
||||||
|
namespace QmlDesigner::DeviceShare {
|
||||||
|
Q_LOGGING_CATEGORY(deviceSharePluginLog, "qtc.designer.deviceSharePluginLog")
|
||||||
|
|
||||||
|
IDeviceData::IDeviceData(const QJsonObject &data)
|
||||||
|
: m_data(data)
|
||||||
|
{}
|
||||||
|
|
||||||
|
QJsonObject IDeviceData::jsonObject() const
|
||||||
|
{
|
||||||
|
return m_data;
|
||||||
|
}
|
||||||
|
|
||||||
|
void IDeviceData::setJsonObject(const QJsonObject &data)
|
||||||
|
{
|
||||||
|
m_data = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
IDeviceData::operator QString() const
|
||||||
|
{
|
||||||
|
return QString::fromLatin1(QJsonDocument(m_data).toJson(QJsonDocument::Compact));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DeviceSettings::active() const
|
||||||
|
{
|
||||||
|
return m_data.value(keyActive).toBool();
|
||||||
|
}
|
||||||
|
|
||||||
|
QString DeviceSettings::alias() const
|
||||||
|
{
|
||||||
|
return m_data.value(keyAlias).toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
QString DeviceSettings::ipAddress() const
|
||||||
|
{
|
||||||
|
return m_data.value(keyIpAddress).toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
void DeviceSettings::setActive(const bool &active)
|
||||||
|
{
|
||||||
|
m_data[keyActive] = active;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DeviceSettings::setAlias(const QString &alias)
|
||||||
|
{
|
||||||
|
m_data[keyAlias] = alias;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DeviceSettings::setIpAddress(const QString &ipAddress)
|
||||||
|
{
|
||||||
|
m_data[keyIpAddress] = ipAddress;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString DeviceInfo::os() const
|
||||||
|
{
|
||||||
|
return m_data.value(keyOs).toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
QString DeviceInfo::osVersion() const
|
||||||
|
{
|
||||||
|
return m_data.value(keyOsVersion).toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
QString DeviceInfo::architecture() const
|
||||||
|
{
|
||||||
|
return m_data.value(keyArchitecture).toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
int DeviceInfo::screenWidth() const
|
||||||
|
{
|
||||||
|
return m_data.value(keyScreenWidth).toInt();
|
||||||
|
}
|
||||||
|
|
||||||
|
int DeviceInfo::screenHeight() const
|
||||||
|
{
|
||||||
|
return m_data.value(keyScreenHeight).toInt();
|
||||||
|
}
|
||||||
|
|
||||||
|
QString DeviceInfo::deviceId() const
|
||||||
|
{
|
||||||
|
return m_data.value(keyDeviceId).toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
QString DeviceInfo::appVersion() const
|
||||||
|
{
|
||||||
|
return m_data.value(keyAppVersion).toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
void DeviceInfo::setOs(const QString &os)
|
||||||
|
{
|
||||||
|
m_data[keyOs] = os;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DeviceInfo::setOsVersion(const QString &osVersion)
|
||||||
|
{
|
||||||
|
m_data[keyOsVersion] = osVersion;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DeviceInfo::setArchitecture(const QString &architecture)
|
||||||
|
{
|
||||||
|
m_data[keyArchitecture] = architecture;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DeviceInfo::setScreenWidth(const int &screenWidth)
|
||||||
|
{
|
||||||
|
m_data[keyScreenWidth] = screenWidth;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DeviceInfo::setScreenHeight(const int &screenHeight)
|
||||||
|
{
|
||||||
|
m_data[keyScreenHeight] = screenHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DeviceInfo::setDeviceId(const QString &deviceId)
|
||||||
|
{
|
||||||
|
m_data[keyDeviceId] = deviceId;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DeviceInfo::setAppVersion(const QString &appVersion)
|
||||||
|
{
|
||||||
|
m_data[keyAppVersion] = appVersion;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace QmlDesigner::DeviceShare
|
@@ -0,0 +1,81 @@
|
|||||||
|
// Copyright (C) 2024 The Qt Company Ltd.
|
||||||
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QJsonObject>
|
||||||
|
#include <QLoggingCategory>
|
||||||
|
|
||||||
|
namespace QmlDesigner::DeviceShare {
|
||||||
|
Q_DECLARE_LOGGING_CATEGORY(deviceSharePluginLog);
|
||||||
|
|
||||||
|
class IDeviceData
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
IDeviceData(const QJsonObject &data = QJsonObject());
|
||||||
|
virtual ~IDeviceData() = default;
|
||||||
|
|
||||||
|
// converters
|
||||||
|
QJsonObject jsonObject() const;
|
||||||
|
void setJsonObject(const QJsonObject &data);
|
||||||
|
operator QString() const;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
QJsonObject m_data;
|
||||||
|
};
|
||||||
|
|
||||||
|
class DeviceSettings : public IDeviceData
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
DeviceSettings() = default;
|
||||||
|
|
||||||
|
// Getters
|
||||||
|
bool active() const;
|
||||||
|
QString alias() const;
|
||||||
|
QString ipAddress() const;
|
||||||
|
|
||||||
|
// Setters
|
||||||
|
void setActive(const bool &active);
|
||||||
|
void setAlias(const QString &alias);
|
||||||
|
void setIpAddress(const QString &ipAddress);
|
||||||
|
|
||||||
|
private:
|
||||||
|
static constexpr char keyActive[] = "deviceActive";
|
||||||
|
static constexpr char keyAlias[] = "deviceAlias";
|
||||||
|
static constexpr char keyIpAddress[] = "ipAddress";
|
||||||
|
};
|
||||||
|
|
||||||
|
class DeviceInfo : public IDeviceData
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
DeviceInfo() = default;
|
||||||
|
|
||||||
|
// Getters
|
||||||
|
QString os() const;
|
||||||
|
QString osVersion() const;
|
||||||
|
QString architecture() const;
|
||||||
|
int screenWidth() const;
|
||||||
|
int screenHeight() const;
|
||||||
|
QString deviceId() const;
|
||||||
|
QString appVersion() const;
|
||||||
|
|
||||||
|
// Setters
|
||||||
|
void setOs(const QString &os);
|
||||||
|
void setOsVersion(const QString &osVersion);
|
||||||
|
void setArchitecture(const QString &architecture);
|
||||||
|
void setScreenWidth(const int &screenWidth);
|
||||||
|
void setScreenHeight(const int &screenHeight);
|
||||||
|
void setDeviceId(const QString &deviceId);
|
||||||
|
void setAppVersion(const QString &appVersion);
|
||||||
|
|
||||||
|
private:
|
||||||
|
static constexpr char keyOs[] = "os";
|
||||||
|
static constexpr char keyOsVersion[] = "osVersion";
|
||||||
|
static constexpr char keyScreenWidth[] = "screenWidth";
|
||||||
|
static constexpr char keyScreenHeight[] = "screenHeight";
|
||||||
|
static constexpr char keyDeviceId[] = "deviceId";
|
||||||
|
static constexpr char keyArchitecture[] = "architecture";
|
||||||
|
static constexpr char keyAppVersion[] = "appVersion";
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace QmlDesigner::DeviceShare
|
@@ -0,0 +1,260 @@
|
|||||||
|
// Copyright (C) 2024 The Qt Company Ltd.
|
||||||
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||||
|
|
||||||
|
#include "devicemanager.h"
|
||||||
|
|
||||||
|
#include <QFile>
|
||||||
|
#include <QJsonArray>
|
||||||
|
#include <QJsonDocument>
|
||||||
|
#include <QJsonObject>
|
||||||
|
#include <QRegularExpression>
|
||||||
|
#include <QUdpSocket>
|
||||||
|
|
||||||
|
namespace QmlDesigner::DeviceShare {
|
||||||
|
|
||||||
|
DeviceManager::DeviceManager(QObject *parent, const QString &settingsPath)
|
||||||
|
: QObject(parent)
|
||||||
|
, m_settingsPath(settingsPath)
|
||||||
|
{
|
||||||
|
readSettings();
|
||||||
|
if (m_uuid.isEmpty()) {
|
||||||
|
m_uuid = QUuid::createUuid().toString(QUuid::WithoutBraces);
|
||||||
|
writeSettings();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DeviceManager::writeSettings()
|
||||||
|
{
|
||||||
|
QJsonObject root;
|
||||||
|
QJsonArray devices;
|
||||||
|
for (const auto &device : m_devices) {
|
||||||
|
QJsonObject deviceInfo;
|
||||||
|
deviceInfo.insert("deviceInfo", device->deviceInfo().jsonObject());
|
||||||
|
deviceInfo.insert("deviceSettings", device->deviceSettings().jsonObject());
|
||||||
|
devices.append(deviceInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
root.insert("devices", devices);
|
||||||
|
root.insert("uuid", m_uuid);
|
||||||
|
|
||||||
|
QJsonDocument doc(root);
|
||||||
|
QFile file(m_settingsPath);
|
||||||
|
if (!file.open(QIODevice::WriteOnly)) {
|
||||||
|
qCWarning(deviceSharePluginLog) << "Failed to open settings file" << file.fileName();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
file.write(doc.toJson());
|
||||||
|
}
|
||||||
|
|
||||||
|
void DeviceManager::readSettings()
|
||||||
|
{
|
||||||
|
QFile file(m_settingsPath);
|
||||||
|
qCDebug(deviceSharePluginLog) << "Reading settings from" << file.fileName();
|
||||||
|
if (!file.open(QIODevice::ReadOnly)) {
|
||||||
|
qCWarning(deviceSharePluginLog) << "Failed to open settings file" << file.fileName();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QJsonDocument doc = QJsonDocument::fromJson(file.readAll());
|
||||||
|
m_uuid = doc.object()["uuid"].toString();
|
||||||
|
QJsonArray devices = doc.object()["devices"].toArray();
|
||||||
|
for (const QJsonValue &deviceInfoJson : devices) {
|
||||||
|
DeviceInfo deviceInfo;
|
||||||
|
DeviceSettings deviceSettings;
|
||||||
|
deviceInfo.setJsonObject(deviceInfoJson.toObject()["deviceInfo"].toObject());
|
||||||
|
deviceSettings.setJsonObject(deviceInfoJson.toObject()["deviceSettings"].toObject());
|
||||||
|
auto device = initDevice(deviceInfo, deviceSettings);
|
||||||
|
m_devices.append(device);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QList<QSharedPointer<Device>> DeviceManager::devices() const
|
||||||
|
{
|
||||||
|
return m_devices;
|
||||||
|
}
|
||||||
|
|
||||||
|
QSharedPointer<Device> DeviceManager::findDevice(const QString &deviceId) const
|
||||||
|
{
|
||||||
|
auto it = std::find_if(m_devices.begin(), m_devices.end(), [deviceId](const auto &device) {
|
||||||
|
return device->deviceInfo().deviceId() == deviceId;
|
||||||
|
});
|
||||||
|
|
||||||
|
return it != m_devices.end() ? *it : nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<DeviceInfo> DeviceManager::deviceInfo(const QString &deviceId) const
|
||||||
|
{
|
||||||
|
auto device = findDevice(deviceId);
|
||||||
|
if (!device)
|
||||||
|
return {};
|
||||||
|
|
||||||
|
return device->deviceInfo();
|
||||||
|
}
|
||||||
|
|
||||||
|
void DeviceManager::setDeviceAlias(const QString &deviceId, const QString &alias)
|
||||||
|
{
|
||||||
|
auto device = findDevice(deviceId);
|
||||||
|
if (!device)
|
||||||
|
return;
|
||||||
|
|
||||||
|
auto deviceSettings = device->deviceSettings();
|
||||||
|
deviceSettings.setAlias(alias);
|
||||||
|
device->setDeviceSettings(deviceSettings);
|
||||||
|
writeSettings();
|
||||||
|
}
|
||||||
|
|
||||||
|
void DeviceManager::setDeviceActive(const QString &deviceId, const bool active)
|
||||||
|
{
|
||||||
|
auto device = findDevice(deviceId);
|
||||||
|
if (!device)
|
||||||
|
return;
|
||||||
|
|
||||||
|
auto deviceSettings = device->deviceSettings();
|
||||||
|
deviceSettings.setActive(active);
|
||||||
|
device->setDeviceSettings(deviceSettings);
|
||||||
|
writeSettings();
|
||||||
|
}
|
||||||
|
|
||||||
|
void DeviceManager::setDeviceIP(const QString &deviceId, const QString &ip)
|
||||||
|
{
|
||||||
|
auto device = findDevice(deviceId);
|
||||||
|
if (!device)
|
||||||
|
return;
|
||||||
|
|
||||||
|
auto deviceSettings = device->deviceSettings();
|
||||||
|
deviceSettings.setIpAddress(ip);
|
||||||
|
device->setDeviceSettings(deviceSettings);
|
||||||
|
writeSettings();
|
||||||
|
}
|
||||||
|
|
||||||
|
void DeviceManager::addDevice(const QString &ip)
|
||||||
|
{
|
||||||
|
if (ip.isEmpty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
const auto trimmedIp = ip.trimmed();
|
||||||
|
|
||||||
|
// check regex for xxx.xxx.xxx.xxx
|
||||||
|
QRegularExpression ipRegex(R"(^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$)");
|
||||||
|
if (!ipRegex.match(trimmedIp).hasMatch()) {
|
||||||
|
qCWarning(deviceSharePluginLog) << "Invalid IP address" << ip;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const auto &device : m_devices) {
|
||||||
|
if (device->deviceSettings().ipAddress() == trimmedIp) {
|
||||||
|
qCWarning(deviceSharePluginLog) << "Device" << trimmedIp << "already exists";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DeviceSettings deviceSettings;
|
||||||
|
deviceSettings.setIpAddress(trimmedIp);
|
||||||
|
auto device = initDevice({}, deviceSettings);
|
||||||
|
m_devices.append(device);
|
||||||
|
writeSettings();
|
||||||
|
emit deviceAdded(device->deviceInfo());
|
||||||
|
}
|
||||||
|
|
||||||
|
QSharedPointer<Device> DeviceManager::initDevice(const DeviceInfo &deviceInfo,
|
||||||
|
const DeviceSettings &deviceSettings)
|
||||||
|
{
|
||||||
|
QSharedPointer<Device> device = QSharedPointer<Device>(new Device{deviceInfo, deviceSettings},
|
||||||
|
&QObject::deleteLater);
|
||||||
|
connect(device.data(), &Device::deviceInfoReady, this, &DeviceManager::deviceInfoReceived);
|
||||||
|
connect(device.data(), &Device::disconnected, this, &DeviceManager::deviceDisconnected);
|
||||||
|
connect(device.data(), &Device::projectStarted, this, [this](const QString deviceId) {
|
||||||
|
auto device = findDevice(deviceId);
|
||||||
|
qCDebug(deviceSharePluginLog) << "Project started on device" << deviceId;
|
||||||
|
emit projectStarted(device->deviceInfo());
|
||||||
|
});
|
||||||
|
connect(device.data(), &Device::projectStopped, this, [this](const QString deviceId) {
|
||||||
|
auto device = findDevice(deviceId);
|
||||||
|
qCDebug(deviceSharePluginLog) << "Project stopped on device" << deviceId;
|
||||||
|
emit projectStopped(device->deviceInfo());
|
||||||
|
});
|
||||||
|
connect(device.data(),
|
||||||
|
&Device::projectLogsReceived,
|
||||||
|
this,
|
||||||
|
[this](const QString deviceId, const QString &logs) {
|
||||||
|
auto device = findDevice(deviceId);
|
||||||
|
qCDebug(deviceSharePluginLog) << "Log:" << deviceId << logs;
|
||||||
|
emit projectLogsReceived(device->deviceInfo(), logs);
|
||||||
|
});
|
||||||
|
|
||||||
|
return device;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DeviceManager::deviceInfoReceived(const QString &deviceId, const DeviceInfo &deviceInfo)
|
||||||
|
{
|
||||||
|
writeSettings();
|
||||||
|
qCDebug(deviceSharePluginLog) << "Device" << deviceId << "is online";
|
||||||
|
emit deviceOnline(deviceInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DeviceManager::deviceDisconnected(const QString &deviceId)
|
||||||
|
{
|
||||||
|
auto device = findDevice(deviceId);
|
||||||
|
if (!device)
|
||||||
|
return;
|
||||||
|
|
||||||
|
qCDebug(deviceSharePluginLog) << "Device" << deviceId << "disconnected";
|
||||||
|
emit deviceOffline(device->deviceInfo());
|
||||||
|
}
|
||||||
|
|
||||||
|
void DeviceManager::removeDevice(const QString &deviceId)
|
||||||
|
{
|
||||||
|
auto device = findDevice(deviceId);
|
||||||
|
if (!device)
|
||||||
|
return;
|
||||||
|
|
||||||
|
const auto deviceInfo = device->deviceInfo();
|
||||||
|
m_devices.removeOne(device);
|
||||||
|
writeSettings();
|
||||||
|
emit deviceRemoved(deviceInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DeviceManager::removeDeviceAt(int index)
|
||||||
|
{
|
||||||
|
if (index < 0 || index >= m_devices.size())
|
||||||
|
return;
|
||||||
|
|
||||||
|
auto deviceInfo = m_devices[index]->deviceInfo();
|
||||||
|
m_devices.removeAt(index);
|
||||||
|
writeSettings();
|
||||||
|
emit deviceRemoved(deviceInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DeviceManager::sendProjectFile(const QString &deviceId, const QString &projectFile)
|
||||||
|
{
|
||||||
|
auto device = findDevice(deviceId);
|
||||||
|
if (!device)
|
||||||
|
return;
|
||||||
|
|
||||||
|
device->sendProjectNotification();
|
||||||
|
|
||||||
|
QFile file(projectFile);
|
||||||
|
if (!file.open(QIODevice::ReadOnly)) {
|
||||||
|
qCWarning(deviceSharePluginLog) << "Failed to open project file" << projectFile;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
qCDebug(deviceSharePluginLog) << "Sending project file to device" << deviceId;
|
||||||
|
|
||||||
|
QByteArray projectData = file.readAll();
|
||||||
|
device->sendProjectData(projectData);
|
||||||
|
|
||||||
|
qCDebug(deviceSharePluginLog) << "Project file sent to device" << deviceId;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DeviceManager::stopRunningProject(const QString &deviceId)
|
||||||
|
{
|
||||||
|
auto device = findDevice(deviceId);
|
||||||
|
if (!device)
|
||||||
|
return;
|
||||||
|
|
||||||
|
device->sendProjectStopped();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace QmlDesigner::DeviceShare
|
@@ -0,0 +1,69 @@
|
|||||||
|
// Copyright (C) 2024 The Qt Company Ltd.
|
||||||
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
#include <QTimer>
|
||||||
|
#include <QWebSocketServer>
|
||||||
|
|
||||||
|
#include "device.h"
|
||||||
|
|
||||||
|
namespace QmlDesigner::DeviceShare {
|
||||||
|
|
||||||
|
class DeviceManager : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
explicit DeviceManager(QObject *parent = nullptr, const QString &settingsPath = "settings.json");
|
||||||
|
|
||||||
|
// internal init functions
|
||||||
|
void addDevice(const QString &ip);
|
||||||
|
|
||||||
|
// Getters
|
||||||
|
QList<QSharedPointer<Device>> devices() const;
|
||||||
|
std::optional<DeviceInfo> deviceInfo(const QString &deviceId) const;
|
||||||
|
|
||||||
|
// Device management functions
|
||||||
|
void setDeviceAlias(const QString &deviceId, const QString &alias);
|
||||||
|
void setDeviceActive(const QString &deviceId, const bool active);
|
||||||
|
void setDeviceIP(const QString &deviceId, const QString &ip);
|
||||||
|
|
||||||
|
void removeDevice(const QString &deviceId);
|
||||||
|
void removeDeviceAt(int index);
|
||||||
|
void sendProjectFile(const QString &deviceId, const QString &projectFile);
|
||||||
|
void stopRunningProject(const QString &deviceId);
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Devices management
|
||||||
|
QList<QSharedPointer<Device>> m_devices;
|
||||||
|
|
||||||
|
// settings
|
||||||
|
const QString m_settingsPath;
|
||||||
|
QString m_uuid;
|
||||||
|
|
||||||
|
private:
|
||||||
|
// internal slots
|
||||||
|
void incomingConnection();
|
||||||
|
void readSettings();
|
||||||
|
void writeSettings();
|
||||||
|
QSharedPointer<Device> initDevice(const DeviceInfo &deviceInfo = DeviceInfo(),
|
||||||
|
const DeviceSettings &deviceSettings = DeviceSettings());
|
||||||
|
|
||||||
|
// device signals
|
||||||
|
void deviceInfoReceived(const QString &deviceId, const DeviceInfo &deviceInfo);
|
||||||
|
void deviceDisconnected(const QString &deviceId);
|
||||||
|
|
||||||
|
QSharedPointer<Device> findDevice(const QString &deviceId) const;
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void deviceAdded(const DeviceInfo &deviceInfo);
|
||||||
|
void deviceRemoved(const DeviceInfo &deviceInfo);
|
||||||
|
void deviceOnline(const DeviceInfo &deviceInfo);
|
||||||
|
void deviceOffline(const DeviceInfo &deviceInfo);
|
||||||
|
void projectStarted(const DeviceInfo &deviceInfo);
|
||||||
|
void projectStopped(const DeviceInfo &deviceInfo);
|
||||||
|
void projectLogsReceived(const DeviceInfo &deviceInfo, const QString &logs);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace QmlDesigner::DeviceShare
|
@@ -0,0 +1,162 @@
|
|||||||
|
// Copyright (C) 2024 The Qt Company Ltd.
|
||||||
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||||
|
|
||||||
|
#include "devicemanagermodel.h"
|
||||||
|
#include "devicemanager.h"
|
||||||
|
|
||||||
|
namespace QmlDesigner::DeviceShare {
|
||||||
|
|
||||||
|
DeviceManagerModel::DeviceManagerModel(DeviceManager &deviceManager, QObject *parent)
|
||||||
|
: QAbstractTableModel(parent)
|
||||||
|
, m_deviceManager(deviceManager)
|
||||||
|
{
|
||||||
|
connect(&m_deviceManager, &DeviceManager::deviceAdded, this, [this](const DeviceInfo &) {
|
||||||
|
endResetModel();
|
||||||
|
});
|
||||||
|
connect(&m_deviceManager, &DeviceManager::deviceRemoved, this, [this](const DeviceInfo &) {
|
||||||
|
endResetModel();
|
||||||
|
});
|
||||||
|
|
||||||
|
connect(&m_deviceManager, &DeviceManager::deviceOnline, this, [this](const DeviceInfo &) {
|
||||||
|
endResetModel();
|
||||||
|
});
|
||||||
|
|
||||||
|
connect(&m_deviceManager, &DeviceManager::deviceOffline, this, [this](const DeviceInfo &) {
|
||||||
|
endResetModel();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
int DeviceManagerModel::rowCount(const QModelIndex &parent) const
|
||||||
|
{
|
||||||
|
if (parent.isValid())
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
return m_deviceManager.devices().size();
|
||||||
|
}
|
||||||
|
|
||||||
|
int DeviceManagerModel::columnCount(const QModelIndex &parent) const
|
||||||
|
{
|
||||||
|
if (parent.isValid())
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
return DeviceColumns::COLUMN_COUNT;
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariant DeviceManagerModel::data(const QModelIndex &index, int role) const
|
||||||
|
{
|
||||||
|
if (!index.isValid())
|
||||||
|
return QVariant();
|
||||||
|
|
||||||
|
if (role == Qt::DisplayRole) {
|
||||||
|
const auto deviceInfo = m_deviceManager.devices()[index.row()]->deviceInfo();
|
||||||
|
const auto deviceSettings = m_deviceManager.devices()[index.row()]->deviceSettings();
|
||||||
|
bool isConnected = m_deviceManager.devices()[index.row()]->isConnected();
|
||||||
|
switch (index.column()) {
|
||||||
|
case DeviceColumns::Active:
|
||||||
|
return deviceSettings.active();
|
||||||
|
case DeviceColumns::Status:
|
||||||
|
return static_cast<DeviceStatus>(isConnected);
|
||||||
|
case DeviceColumns::Alias:
|
||||||
|
return deviceSettings.alias();
|
||||||
|
case DeviceColumns::IPv4Addr:
|
||||||
|
return deviceSettings.ipAddress();
|
||||||
|
case DeviceColumns::OS:
|
||||||
|
return deviceInfo.os();
|
||||||
|
case DeviceColumns::OSVersion:
|
||||||
|
return deviceInfo.osVersion();
|
||||||
|
case DeviceColumns::Architecture:
|
||||||
|
return deviceInfo.architecture();
|
||||||
|
case DeviceColumns::ScreenSize:
|
||||||
|
return QString("%1x%2").arg(deviceInfo.screenWidth()).arg(deviceInfo.screenHeight());
|
||||||
|
case DeviceColumns::AppVersion:
|
||||||
|
return deviceInfo.appVersion();
|
||||||
|
case DeviceColumns::DeviceId:
|
||||||
|
return deviceInfo.deviceId();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (role == Qt::EditRole) {
|
||||||
|
const auto deviceSettings = m_deviceManager.devices()[index.row()]->deviceSettings();
|
||||||
|
switch (index.column()) {
|
||||||
|
case DeviceColumns::Alias:
|
||||||
|
return deviceSettings.alias();
|
||||||
|
case DeviceColumns::Active:
|
||||||
|
return deviceSettings.active();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return QVariant();
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariant DeviceManagerModel::headerData(int section, Qt::Orientation orientation, int role) const
|
||||||
|
{
|
||||||
|
if (role != Qt::DisplayRole)
|
||||||
|
return QVariant();
|
||||||
|
|
||||||
|
if (orientation == Qt::Horizontal) {
|
||||||
|
switch (section) {
|
||||||
|
case DeviceColumns::Active:
|
||||||
|
return "Active";
|
||||||
|
case DeviceColumns::Status:
|
||||||
|
return "Status";
|
||||||
|
case DeviceColumns::Alias:
|
||||||
|
return "Alias";
|
||||||
|
case DeviceColumns::IPv4Addr:
|
||||||
|
return "IPv4 Address";
|
||||||
|
case DeviceColumns::OS:
|
||||||
|
return "OS";
|
||||||
|
case DeviceColumns::OSVersion:
|
||||||
|
return "OS Version";
|
||||||
|
case DeviceColumns::Architecture:
|
||||||
|
return "Architecture";
|
||||||
|
case DeviceColumns::ScreenSize:
|
||||||
|
return "Screen Size";
|
||||||
|
case DeviceColumns::AppVersion:
|
||||||
|
return "App Version";
|
||||||
|
case DeviceColumns::DeviceId:
|
||||||
|
return "Device ID";
|
||||||
|
case DeviceColumns::Remove:
|
||||||
|
return "Remove";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return QVariant();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DeviceManagerModel::setData(const QModelIndex &index, const QVariant &value, int role)
|
||||||
|
{
|
||||||
|
if (!index.isValid() || role != Qt::EditRole) {
|
||||||
|
qCWarning(deviceSharePluginLog) << "Invalid index or role";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto deviceInfo = m_deviceManager.devices()[index.row()]->deviceInfo();
|
||||||
|
switch (index.column()) {
|
||||||
|
case DeviceColumns::Alias:
|
||||||
|
m_deviceManager.setDeviceAlias(deviceInfo.deviceId(), value.toString());
|
||||||
|
break;
|
||||||
|
case DeviceColumns::Active:
|
||||||
|
m_deviceManager.setDeviceActive(deviceInfo.deviceId(), value.toBool());
|
||||||
|
break;
|
||||||
|
case DeviceColumns::IPv4Addr:
|
||||||
|
m_deviceManager.setDeviceIP(deviceInfo.deviceId(), value.toString());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
emit dataChanged(index, index, {role});
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Qt::ItemFlags DeviceManagerModel::flags(const QModelIndex &index) const
|
||||||
|
{
|
||||||
|
if (!index.isValid())
|
||||||
|
return Qt::NoItemFlags;
|
||||||
|
|
||||||
|
Qt::ItemFlags flags = Qt::ItemIsEnabled | Qt::ItemIsSelectable;
|
||||||
|
|
||||||
|
if (index.column() == DeviceColumns::Active || index.column() == DeviceColumns::Alias)
|
||||||
|
flags |= Qt::ItemIsEditable;
|
||||||
|
|
||||||
|
return flags;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace QmlDesigner::DeviceShare
|
@@ -0,0 +1,47 @@
|
|||||||
|
// Copyright (C) 2024 The Qt Company Ltd.
|
||||||
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QAbstractTableModel>
|
||||||
|
|
||||||
|
namespace QmlDesigner::DeviceShare {
|
||||||
|
|
||||||
|
class DeviceManager;
|
||||||
|
class DeviceManagerModel : public QAbstractTableModel
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
explicit DeviceManagerModel(DeviceManager &deviceManager, QObject *parent = nullptr);
|
||||||
|
|
||||||
|
enum DeviceStatus { Offline, Online };
|
||||||
|
Q_ENUM(DeviceStatus)
|
||||||
|
|
||||||
|
enum DeviceColumns {
|
||||||
|
Active,
|
||||||
|
Status,
|
||||||
|
Alias,
|
||||||
|
IPv4Addr,
|
||||||
|
OS,
|
||||||
|
OSVersion,
|
||||||
|
Architecture,
|
||||||
|
ScreenSize,
|
||||||
|
AppVersion,
|
||||||
|
DeviceId,
|
||||||
|
Remove,
|
||||||
|
COLUMN_COUNT
|
||||||
|
};
|
||||||
|
Q_ENUM(DeviceColumns)
|
||||||
|
|
||||||
|
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
|
||||||
|
int columnCount(const QModelIndex &parent = QModelIndex()) const override;
|
||||||
|
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
|
||||||
|
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;
|
||||||
|
bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override;
|
||||||
|
Qt::ItemFlags flags(const QModelIndex &index) const override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
DeviceManager &m_deviceManager;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace QmlDesigner::DeviceShare
|
Reference in New Issue
Block a user