init commit

This commit is contained in:
2026-04-24 20:20:24 +08:00
commit 5442e32abc
34 changed files with 2684 additions and 0 deletions

151
src/app/AppController.cpp Normal file
View File

@@ -0,0 +1,151 @@
#include "AppController.h"
#include <QJsonArray>
AppController::AppController(QObject* parent)
: QObject(parent)
, m_settings(new LocalSend::Settings(this))
, m_security(new LocalSend::SecurityContext(this))
, m_discovery(new LocalSend::DiscoveryManager(this))
, m_server(new LocalSend::HttpServer(this))
, m_sessions(new LocalSend::SessionManager(this))
{
}
AppController::~AppController()
{
stopDiscovery();
}
void AppController::initialize()
{
m_security->initialize();
LocalSend::InfoDto info = buildInfoDto();
m_server->setLocalInfo(info, m_security->fingerprint());
m_discovery->setLocalInfo(info, m_security->fingerprint(), m_settings->port(),
m_settings->https() ? LocalSend::ProtocolType::Https : LocalSend::ProtocolType::Http);
if (m_server->start(m_settings->port(), m_settings->https())) {
emit serverRunningChanged();
}
connect(m_discovery, &LocalSend::DiscoveryManager::deviceDiscovered,
this, &AppController::onDeviceDiscovered);
connect(m_discovery, &LocalSend::DiscoveryManager::deviceLost,
this, &AppController::onDeviceLost);
connect(m_server, &LocalSend::HttpServer::prepareUploadRequest,
this, &AppController::onPrepareUploadRequest);
startDiscovery();
}
LocalSend::InfoDto AppController::buildInfoDto() const
{
LocalSend::InfoDto info;
info.alias = m_settings->alias();
info.version = m_settings->version();
info.deviceModel = m_settings->deviceModel();
info.deviceType = m_settings->deviceType();
info.download = false;
return info;
}
QString AppController::alias() const
{
return m_settings->alias();
}
void AppController::setAlias(const QString& alias)
{
if (m_settings->alias() != alias) {
m_settings->setAlias(alias);
emit aliasChanged();
}
}
quint16 AppController::port() const
{
return m_settings->port();
}
void AppController::setPort(quint16 port)
{
if (m_settings->port() != port) {
m_settings->setPort(port);
emit portChanged();
}
}
QVariantList AppController::devices() const
{
QVariantList result;
for (const LocalSend::Device& device : m_devices) {
QVariantMap map;
map[QStringLiteral("ip")] = device.ip;
map[QStringLiteral("port")] = device.port;
map[QStringLiteral("alias")] = device.alias;
map[QStringLiteral("fingerprint")] = device.fingerprint;
map[QStringLiteral("deviceModel")] = device.deviceModel;
map[QStringLiteral("deviceType")] = LocalSend::deviceTypeToString(device.deviceType);
result.append(map);
}
return result;
}
bool AppController::serverRunning() const
{
return m_server->isRunning();
}
void AppController::startDiscovery()
{
m_discovery->start();
}
void AppController::stopDiscovery()
{
m_discovery->stop();
}
void AppController::refreshDevices()
{
m_discovery->startScan();
}
void AppController::onDeviceDiscovered(const LocalSend::Device& device)
{
m_devices.insert(device.fingerprint, device);
emit devicesChanged();
}
void AppController::onDeviceLost(const QString& fingerprint)
{
m_devices.remove(fingerprint);
emit devicesChanged();
}
void AppController::onPrepareUploadRequest(const LocalSend::PrepareUploadRequestDto& dto,
const QHostAddress& sender)
{
LocalSend::Device device;
device.ip = sender.toString();
device.alias = dto.info.alias;
device.fingerprint = dto.info.fingerprint;
device.deviceModel = dto.info.deviceModel;
device.deviceType = dto.info.deviceType;
device.version = dto.info.version;
QString sessionId = m_sessions->createReceiveSession(device, dto.files);
QVariantList filesList;
for (auto it = dto.files.constBegin(); it != dto.files.constEnd(); ++it) {
QVariantMap file;
file[QStringLiteral("id")] = it.key();
file[QStringLiteral("fileName")] = it.value().fileName;
file[QStringLiteral("size")] = it.value().size;
file[QStringLiteral("fileType")] = it.value().fileType;
filesList.append(file);
}
emit receiveRequest(sessionId, dto.info.alias, filesList);
}

63
src/app/AppController.h Normal file
View File

@@ -0,0 +1,63 @@
#pragma once
#include <QObject>
#include <QVariantList>
#include "LocalSendCore/DiscoveryManager.h"
#include "LocalSendCore/HttpServer.h"
#include "LocalSendCore/HttpClient.h"
#include "LocalSendCore/SessionManager.h"
#include "LocalSendCore/SecurityContext.h"
#include "LocalSendCore/Settings.h"
class AppController : public QObject
{
Q_OBJECT
Q_PROPERTY(QString alias READ alias WRITE setAlias NOTIFY aliasChanged)
Q_PROPERTY(quint16 port READ port WRITE setPort NOTIFY portChanged)
Q_PROPERTY(QVariantList devices READ devices NOTIFY devicesChanged)
Q_PROPERTY(bool serverRunning READ serverRunning NOTIFY serverRunningChanged)
public:
explicit AppController(QObject* parent = nullptr);
~AppController() override;
void initialize();
QString alias() const;
void setAlias(const QString& alias);
quint16 port() const;
void setPort(quint16 port);
QVariantList devices() const;
bool serverRunning() const;
Q_INVOKABLE void startDiscovery();
Q_INVOKABLE void stopDiscovery();
Q_INVOKABLE void refreshDevices();
signals:
void aliasChanged();
void portChanged();
void devicesChanged();
void serverRunningChanged();
void receiveRequest(const QString& sessionId, const QString& senderAlias, const QVariantList& files);
void sendProgress(const QString& sessionId, double progress);
void sendCompleted(const QString& sessionId);
private slots:
void onDeviceDiscovered(const LocalSend::Device& device);
void onDeviceLost(const QString& fingerprint);
void onPrepareUploadRequest(const LocalSend::PrepareUploadRequestDto& dto, const QHostAddress& sender);
private:
LocalSend::Settings* m_settings = nullptr;
LocalSend::SecurityContext* m_security = nullptr;
LocalSend::DiscoveryManager* m_discovery = nullptr;
LocalSend::HttpServer* m_server = nullptr;
LocalSend::SessionManager* m_sessions = nullptr;
QMap<QString, LocalSend::Device> m_devices;
LocalSend::InfoDto buildInfoDto() const;
};

9
src/app/Application.cpp Normal file
View File

@@ -0,0 +1,9 @@
#include "Application.h"
Application::Application(int& argc, char** argv)
: QGuiApplication(argc, argv)
{
setApplicationName(QStringLiteral("LocalSendQt"));
setApplicationVersion(QStringLiteral("1.0.0"));
setOrganizationName(QStringLiteral("LocalSend"));
}

12
src/app/Application.h Normal file
View File

@@ -0,0 +1,12 @@
#pragma once
#include <QGuiApplication>
#include <QIcon>
class Application : public QGuiApplication
{
Q_OBJECT
public:
Application(int& argc, char** argv);
};

23
src/app/CMakeLists.txt Normal file
View File

@@ -0,0 +1,23 @@
qt_add_executable(LocalSendQt
main.cpp
Application.cpp
AppController.cpp
)
qt_add_qml_module(LocalSendQt
URI LocalSend
VERSION 1.0
QML_FILES
qml/main.qml
)
target_link_libraries(LocalSendQt PRIVATE
LocalSendCore
Qt6::Quick
Qt6::QuickControls2
)
install(TARGETS LocalSendQt
BUNDLE DESTINATION .
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
)

33
src/app/main.cpp Normal file
View File

@@ -0,0 +1,33 @@
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include <QQuickStyle>
#include <QDir>
#include "Application.h"
#include "AppController.h"
int main(int argc, char *argv[])
{
Application app(argc, argv);
QQuickStyle::setStyle("Basic");
AppController controller;
controller.initialize();
QQmlApplicationEngine engine;
engine.addImportPath(QStringLiteral("qrc:/"));
engine.rootContext()->setContextProperty(QStringLiteral("appController"), &controller);
const QUrl url(u"qrc:/LocalSend/qml/main.qml"_qs);
QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,
&app, [url](QObject *obj, const QUrl &objUrl) {
if (!obj && url == objUrl) QCoreApplication::exit(-1);
}, Qt::QueuedConnection);
engine.load(url);
return app.exec();
}

178
src/app/qml/main.qml Normal file
View File

@@ -0,0 +1,178 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
ApplicationWindow {
id: root
visible: true
width: 800
height: 600
title: qsTr("LocalSend")
StackView {
id: stackView
anchors.fill: parent
Component.onCompleted: {
push(homePageComponent)
}
}
Component {
id: homePageComponent
Page {
id: homePage
signal openSettings()
header: ToolBar {
RowLayout {
anchors.fill: parent
Label {
text: qsTr("LocalSend")
font.bold: true
font.pixelSize: 20
Layout.leftMargin: 16
}
Item { Layout.fillWidth: true }
ToolButton {
text: qsTr("Settings")
onClicked: homePage.openSettings()
}
}
}
ColumnLayout {
anchors.fill: parent
anchors.margins: 16
spacing: 16
Label {
text: qsTr("Nearby Devices")
font.bold: true
font.pixelSize: 16
}
ListView {
id: deviceListView
Layout.fillWidth: true
Layout.fillHeight: true
property var devices: appController.devices
model: devices
spacing: 8
delegate: Pane {
width: ListView.view.width
padding: 12
background: Rectangle {
color: Qt.lighter("gray", 1.8)
radius: 8
}
RowLayout {
anchors.fill: parent
Column {
Layout.fillWidth: true
Label {
text: modelData.alias || modelData.ip
font.bold: true
}
Label {
text: "%1:%2".arg(modelData.ip).arg(modelData.port)
color: "gray"
font.pixelSize: 12
}
}
Button {
text: qsTr("Send")
onClicked: {
// send to this device
}
}
}
}
}
RowLayout {
Layout.fillWidth: true
Button {
text: qsTr("Refresh")
onClicked: appController.refreshDevices()
}
Item { Layout.fillWidth: true }
Label {
text: qsTr("Alias: %1").arg(appController.alias)
color: "gray"
}
}
}
onOpenSettings: stackView.push(settingsPageComponent)
}
}
Component {
id: settingsPageComponent
Page {
id: settingsPage
signal back()
header: ToolBar {
RowLayout {
anchors.fill: parent
ToolButton {
text: qsTr("Back")
onClicked: settingsPage.back()
}
Label {
text: qsTr("Settings")
font.bold: true
Layout.fillWidth: true
Layout.leftMargin: 16
}
}
}
ColumnLayout {
anchors.fill: parent
anchors.margins: 16
spacing: 16
GridLayout {
columns: 2
Layout.fillWidth: true
Label { text: qsTr("Device Alias:") }
TextField {
id: aliasField
text: appController.alias
onEditingFinished: appController.alias = text
Layout.fillWidth: true
}
Label { text: qsTr("Port:") }
SpinBox {
id: portField
value: appController.port
from: 1
to: 65535
onValueChanged: appController.port = value
}
}
Item { Layout.fillHeight: true }
Label {
text: qsTr("Server Status: %1").arg(appController.serverRunning ? qsTr("Running") : qsTr("Stopped"))
color: appController.serverRunning ? "green" : "red"
}
}
onBack: stackView.pop()
}
}
}