added initial nearby sharing app rewrite

This commit is contained in:
Lasan Mahaliyana
2026-06-26 17:00:55 +05:30
parent 7b88ebe9c2
commit da1634c501
27 changed files with 1571 additions and 10 deletions
+38
View File
@@ -0,0 +1,38 @@
import QtQuick
import QtQuick.Layouts
import QtQuick.Controls
import QtQuick.Shapes
RowLayout {
anchors.fill: parent
spacing: 0
Sidebar {}
StackLayout {
id: contentStack
Layout.fillWidth: true
Layout.fillHeight: true
currentIndex: 1
Drop {}
Rectangle {
color: "transparent"
Layout.fillWidth: true
Layout.fillHeight: true
Targets{}
}
Rectangle {
color: "transparent"
Text {
text: "User Profile Management"
anchors.centerIn: parent
font.pointSize: 22
color: "#333333"
}
}
}
}
+83
View File
@@ -0,0 +1,83 @@
load("@rules_cc//cc:cc_binary.bzl", "cc_binary")
load("@rules_cc//cc:cc_library.bzl", "cc_library")
load("@rules_cc//cc:cc_test.bzl", "cc_test")
load("@hedron_compile_commands//:refresh_compile_commands.bzl", "refresh_compile_commands")
refresh_compile_commands(
name = "refresh_compile_commands",
# Specify the targets of interest.
# For example, specify a dict of targets and any flags required to build.
targets = {
":app" : "",
},
# No need to add flags already in .bazelrc. They're automatically picked up.
# If you don't need flags, a list of targets is also okay, as is a single target string.
# Wildcard patterns, like //... for everything, *are* allowed here, just like a build.
# As are additional targets (+) and subtractions (-), like in bazel query https://docs.bazel.build/versions/main/query.html#expressions
# And if you're working on a header-only library, specify a test or binary target that compiles it.
)
# 1. Compile QML files into a C++ source file using Qt's Resource Compiler (rcc)
genrule(
name = "qrc_generation",
srcs = [
"resources.qrc",
"main.qml",
"Drop.qml",
"Sidebar.qml",
"googlesans_var.ttf",
"icons/file.svg",
"icons/up_file.svg"
],
outs = ["qrc_resources.cpp"],
cmd = "/usr/lib64/qt6/libexec/rcc $(location resources.qrc) -o $(location qrc_resources.cpp)",
)
# 2. MOC generation for backend.h
genrule(
name = "moc_backend",
srcs = ["backend.h"],
outs = ["moc_backend.cpp"],
cmd = "/usr/lib64/qt6/libexec/moc $(location backend.h) -o $(location moc_backend.cpp) ",
)
cc_binary(
name = "app",
srcs = [
"backend.cc",
"main.cc",
"backend.h",
":qrc_generation", # Include the generated QRC file
":moc_backend",
],
copts = [
# Base Qt6 directory
"-I/usr/include/qt6",
# Individual module directories required for QGuiApplication and QQmlApplicationEngine
"-I/usr/include/qt6/QtCore",
"-I/usr/include/qt6/QtGui",
"-I/usr/include/qt6/QtQml",
"-I/usr/include/qt6/QtQuick",
],
linkopts = [
"-lQt6Core",
"-lQt6Gui",
"-lQt6Qml",
"-lQt6Quick",
],
deps = [
":nearby_sharing_dbus_client",
],
)
cc_library(
name = "nearby_sharing_dbus_client",
srcs = ["nearby_sharing_dbus_client.cc"],
hdrs = ["nearby_sharing_dbus_client.h"],
deps = [
"//sharing/linux/daemon:nearby_sharing_dbus_client_glue",
"@sdbus_cpp",
],
)
+74
View File
@@ -0,0 +1,74 @@
import QtQuick
import QtQuick.Layouts
import QtQuick.Controls
import QtQuick.Shapes
Rectangle {
id: dropZone
color: "transparent"
DropArea {
anchors.fill: parent
// Visual feedback: Change color when an item is hovered over it
onEntered: drag => {
dropZone.color = "#91C8DE";
}
onExited: {
dropText.text = "Drag and drop files to share";
dropZone.color = "transparent";
}
// The action: What happens when the user releases the item here
onDropped: drop => {
dropZone.color = "#98FB98"; // Success Mint Green
dropText.text = "Dropped: " + drop.source.objectName;
// Snap the dragged item exactly to the center of the drop zone
drop.source.x = dropZone.x + (dropZone.width - drop.source.width) / 2;
drop.source.y = dropZone.y + (dropZone.height - drop.source.height) / 2;
drop.accept(); // Tell Qt the drop event was handled successfully
}
}
ColumnLayout {
anchors.centerIn: parent
Button {
icon.source: "qrc:icons/up_file.svg"
icon.height: 80
icon.width: 80
icon.color: "#195871"
Layout.fillWidth: true
background: Rectangle {
color: "transparent"
}
}
Text {
id: dropText
text: "Drag and drop files to share"
font.pointSize: 18
color: "#333333"
}
}
Canvas {
id: dashedBorderCanvas
anchors.fill: parent
onPaint: {
var ctx = getContext("2d");
ctx.clearRect(0, 0, width, height);
// Setup styling parameters
ctx.strokeStyle = "#91C8DE";
ctx.lineWidth = 3;
// Set the dash pattern array: [length of dash, length of space]
ctx.setLineDash([8, 6]);
// Draw a rectangle border (X, Y, Width, Height)
// Offset by half line-width (1px) so the line doesn't get clipped on the edges
ctx.strokeRect(80, 80, width - 160, height - 160);
}
}
}
+119
View File
@@ -0,0 +1,119 @@
import QtQuick
import QtQuick.Layouts
import QtQuick.Controls
import QtQuick.Shapes
Rectangle {
id: rootItem
// Explicit sizes so parent containers know how to space them
width: 100
height: 130
color: "transparent"
// --- CUSTOM ARGUMENTS (PROPERTIES) ---
property string deviceName: "Unknown Device"
property real progressValue: 0.0 // Value between 0.0 and 1.0
property string iconSource: "qrc:icons/smartphone.svg"
ColumnLayout {
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
spacing: 5
// --- ICON CONTAINER WITH PROGRESS RINGS ---
Item {
id: iconContainer
implicitWidth: 72
implicitHeight: 72
Layout.alignment: Qt.AlignHCenter
// Target angle calculated using the custom dynamic argument
property real targetAngle: 360 * rootItem.progressValue
Behavior on targetAngle {
NumberAnimation {
duration: 300
easing.type: Easing.OutQuad
}
}
// Background Gray Track Ring
Shape {
anchors.fill: parent
layer.enabled: true
layer.samples: 8
antialiasing: true
ShapePath {
strokeColor: "#E0E4E8"
strokeWidth: 4
fillColor: "transparent"
PathAngleArc {
centerX: iconContainer.width / 2
centerY: iconContainer.height / 2
radiusX: 32
radiusY: 32
startAngle: -90
sweepAngle: 360
}
}
}
// Active Progress Ring
Shape {
anchors.fill: parent
layer.enabled: true
layer.samples: 8
antialiasing: true
ShapePath {
strokeColor: "#00658F"
strokeWidth: 4
fillColor: "transparent"
capStyle: ShapePath.RoundCap
PathAngleArc {
centerX: iconContainer.width / 2
centerY: iconContainer.height / 2
radiusX: 32
radiusY: 32
startAngle: -90
sweepAngle: iconContainer.targetAngle
}
}
}
// Icon Circle
Rectangle {
width: 60
height: 60
anchors.centerIn: parent
color: "#6EA7B6"
radius: 30
Button {
icon.source: rootItem.iconSource
anchors.fill: parent
anchors.margins: 5
icon.color: "white"
icon.height: height
icon.width: width
background: Rectangle {
color: "transparent"
}
}
}
}
// --- TEXT COMPONENT ---
Text {
text: rootItem.deviceName
Layout.fillWidth: true
horizontalAlignment: Text.AlignHCenter
wrapMode: Text.WordWrap
color: "#1A1C1E" // Using the text charcoal color from your palette
}
}
}
+128
View File
@@ -0,0 +1,128 @@
import QtQuick
import QtQuick.Layouts
import QtQuick.Controls
import QtQuick.Shapes
Rectangle {
id: sidebar
Layout.fillHeight: true
Layout.preferredWidth: 300
color: "#CBF0FF"
ColumnLayout {
anchors.margins: 20
anchors.fill: parent
Rectangle {
Layout.preferredHeight: innerColumn.implicitHeight + 20
Layout.fillWidth: true
color: "transparent"
ColumnLayout {
id: innerColumn
Text {
text: "Device name"
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
}
Text {
text: "lasans-laptop"
font.weight: 500
font.pointSize: 17
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
}
}
}
Rectangle {
Layout.fillWidth: true
Layout.fillHeight: true
color: "transparent"
ColumnLayout {
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
//anchors.fill: parent
Text {
text: "Visible to Everyone"
font.weight: 700
color: "#377B95"
}
Text {
Layout.preferredWidth: parent.width
wrapMode: Text.WordWrap
text: "This cannot be changed due to limitations in QuickShare on Linux"
color: "gray"
}
}
}
Rectangle {
Layout.preferredHeight: 350
Layout.fillWidth: true
border.color: "#91C8DE"
border.width: 2
radius: 20
color: "#DCF5FF"
ColumnLayout {
anchors.fill: parent
anchors.margins: 15
Text {
Layout.fillWidth: true
font.pointSize: 15
font.weight: 700
color: "#377B95"
text: "Sharing"
}
Image {
source: "qrc:icons/file.svg"
Layout.fillHeight: true
Layout.fillWidth: true
fillMode: Image.PreserveAspectCrop
sourceSize.height: height
sourceSize.width: width
smooth: true
antialiasing: true
}
Text {
Layout.fillWidth: true
color: "gray"
wrapMode: Text.WordWrap
text: "/home/lasan/test/this/is/a/very/long/file/path/file.pp"
horizontalAlignment: Text.AlignHCenter
}
Button {
id: cancelbutton
Layout.fillWidth: true
text: "Cancel"
contentItem: Text {
text: cancelbutton.text
font.pointSize: 14
font.weight: 500
color: "white" // Dims text when pressed
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
}
background: Rectangle {
implicitWidth: 100
implicitHeight: 40
radius: 10
color: cancelbutton.hovered ? "#195871" : "#06384C"
border.color: "#91C8DE"
border.width: 2
}
}
}
}
}
}
+34
View File
@@ -0,0 +1,34 @@
import QtQuick
import QtQuick.Layouts
import QtQuick.Controls
Row {
id: shareTargetsRow
spacing: 10
Repeater {
model: ["Lasan's A55", "Lasan's Tab S9+", "lasan-laptop", "Somethign else"]
// Your original delegate structure
Rectangle {
// Note: You must give the Rectangle an explicit size
// so the Row knows how to position them.
width: 100
height: 130
color: "transparent"
ColumnLayout {
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
spacing: 5
ShareTarget{
progressValue: 0.7
}
}
}
}
}
+196
View File
@@ -0,0 +1,196 @@
#include "sharing/linux/app/backend.h"
#include <functional>
#include <utility>
#include <QMetaObject>
#include <QVariantMap>
#include <sdbus-c++/sdbus-c++.h>
namespace {
QString ToQString(const std::string& value) {
return QString::fromStdString(value);
}
QVariantMap ToVariantMap(
const nearby::sharing::linux::app::ShareTarget& target) {
QVariantMap map;
map.insert("id", QVariant::fromValue<qint64>(target.id));
map.insert("deviceName", ToQString(target.device_name));
map.insert("type", target.type);
map.insert("isIncoming", target.is_incoming);
map.insert("isKnown", target.is_known);
map.insert("deviceId", ToQString(target.device_id));
map.insert("forSelfShare", target.for_self_share);
map.insert("vendorId", target.vendor_id);
map.insert("receiveDisabled", target.receive_disabled);
return map;
}
} // namespace
Backend::Backend(QObject* parent) : QObject(parent) {
try {
client_ = std::make_unique<Client>(this);
SetStatusText(QStringLiteral("Connected to nearby sharing daemon"));
} catch (const sdbus::Error& error) {
SetStatusText(QStringLiteral("D-Bus connection failed: %1")
.arg(QString::fromStdString(error.getMessage())));
}
}
Backend::~Backend() = default;
QVariantList Backend::targets() const {
QVariantList list;
for (const auto& [id, target] : targets_) {
list.push_back(ToVariantMap(target));
}
return list;
}
void Backend::startReceive() {
RunCommand(QStringLiteral("Start receive"),
[this]() { return client_->StartReceive(); });
}
void Backend::stopReceive() {
RunCommand(QStringLiteral("Stop receive"),
[this]() { return client_->StopReceive(); });
}
void Backend::startDiscovery() {
RunCommand(QStringLiteral("Start discovery"),
[this]() { return client_->StartDiscovery(); });
}
void Backend::stopDiscovery() {
RunCommand(QStringLiteral("Stop discovery"),
[this]() { return client_->StopDiscovery(); });
}
void Backend::sendFile(qint64 share_target_id, const QString& path) {
RunCommand(QStringLiteral("Send file"), [this, share_target_id, path]() {
return client_->SendFile(share_target_id, path.toStdString());
});
}
void Backend::accept(qint64 share_target_id) {
RunCommand(QStringLiteral("Accept transfer"), [this, share_target_id]() {
return client_->Accept(share_target_id);
});
}
void Backend::reject(qint64 share_target_id) {
RunCommand(QStringLiteral("Reject transfer"), [this, share_target_id]() {
return client_->Reject(share_target_id);
});
}
void Backend::cancel(qint64 share_target_id) {
RunCommand(QStringLiteral("Cancel transfer"), [this, share_target_id]() {
return client_->Cancel(share_target_id);
});
}
void Backend::OnTargetDiscovered(const ShareTarget& target) {
QMetaObject::invokeMethod(
this, [this, target]() { ApplyTarget(target); }, Qt::QueuedConnection);
}
void Backend::OnTargetUpdated(const ShareTarget& target) {
QMetaObject::invokeMethod(
this, [this, target]() { ApplyTarget(target); }, Qt::QueuedConnection);
}
void Backend::OnTargetLost(const ShareTarget& target) {
QMetaObject::invokeMethod(
this, [this, target]() { RemoveTarget(target); }, Qt::QueuedConnection);
}
void Backend::OnIncomingTransfer(const std::string& direction,
const ShareTarget& target,
const Transfer& transfer) {
QMetaObject::invokeMethod(
this,
[this, target, transfer]() {
ApplyTarget(target);
emit incomingTransfer(target.id, ToQString(target.device_name),
ToQString(transfer.status));
},
Qt::QueuedConnection);
}
void Backend::OnTransferUpdate(const std::string& direction,
const ShareTarget& target,
const Transfer& transfer) {
QMetaObject::invokeMethod(
this,
[this, target, transfer]() {
ApplyTarget(target);
emit transferUpdate(target.id, ToQString(target.device_name),
ToQString(transfer.status), transfer.progress);
},
Qt::QueuedConnection);
}
void Backend::OnStatusChanged(const Status& status) {
QMetaObject::invokeMethod(
this, [this, status]() { ApplyStatus(status); }, Qt::QueuedConnection);
}
void Backend::ApplyTarget(const ShareTarget& target) {
targets_[target.id] = target;
emit targetsChanged();
}
void Backend::RemoveTarget(const ShareTarget& target) {
targets_.erase(target.id);
emit targetsChanged();
}
void Backend::ApplyStatus(const Status& status) {
status_ = status;
targets_.clear();
for (const auto& target : status.targets) {
targets_[target.id] = target;
}
emit statusChanged();
emit targetsChanged();
}
void Backend::ApplyCommandResult(const QString& command,
const std::tuple<bool, std::string>& result) {
const auto& [ok, message] = result;
const QString detail =
message.empty() ? (ok ? QStringLiteral("ok") : QStringLiteral("failed"))
: ToQString(message);
SetStatusText(QStringLiteral("%1: %2").arg(command, detail));
}
void Backend::RunCommand(
const QString& command,
const std::function<std::tuple<bool, std::string>()>& operation) {
if (!client_) {
SetStatusText(QStringLiteral("%1 failed: not connected").arg(command));
return;
}
try {
ApplyCommandResult(command, operation());
} catch (const sdbus::Error& error) {
SetStatusText(
QStringLiteral("%1 failed: %2")
.arg(command, QString::fromStdString(error.getMessage())));
}
}
void Backend::SetStatusText(const QString& text) {
if (status_text_ == text) {
return;
}
status_text_ = text;
emit statusTextChanged();
}
+89
View File
@@ -0,0 +1,89 @@
#pragma once
#include <cstdint>
#include <functional>
#include <map>
#include <memory>
#include <string>
#include <tuple>
#include <QObject>
#include <QString>
#include <QVariantList>
#include "sharing/linux/app/nearby_sharing_dbus_client.h"
class Backend
: public QObject,
public nearby::sharing::linux::app::NearbySharingDbusClient::Observer {
Q_OBJECT
Q_PROPERTY(QString statusText READ statusText NOTIFY statusTextChanged)
Q_PROPERTY(bool receiveRegistered READ receiveRegistered NOTIFY statusChanged)
Q_PROPERTY(
bool discoveryRegistered READ discoveryRegistered NOTIFY statusChanged)
Q_PROPERTY(bool scanning READ scanning NOTIFY statusChanged)
Q_PROPERTY(bool transferring READ transferring NOTIFY statusChanged)
Q_PROPERTY(QVariantList targets READ targets NOTIFY targetsChanged)
public:
explicit Backend(QObject* parent = nullptr);
~Backend() override;
QString statusText() const { return status_text_; }
bool receiveRegistered() const { return status_.receive_registered; }
bool discoveryRegistered() const { return status_.discovery_registered; }
bool scanning() const { return status_.is_scanning; }
bool transferring() const { return status_.is_transferring; }
QVariantList targets() const;
Q_INVOKABLE void startReceive();
Q_INVOKABLE void stopReceive();
Q_INVOKABLE void startDiscovery();
Q_INVOKABLE void stopDiscovery();
Q_INVOKABLE void sendFile(qint64 share_target_id, const QString& path);
Q_INVOKABLE void accept(qint64 share_target_id);
Q_INVOKABLE void reject(qint64 share_target_id);
Q_INVOKABLE void cancel(qint64 share_target_id);
signals:
void statusTextChanged();
void statusChanged();
void targetsChanged();
void incomingTransfer(qint64 share_target_id, QString device_name,
QString status);
void transferUpdate(qint64 share_target_id, QString device_name,
QString status, double progress);
private:
using Client = nearby::sharing::linux::app::NearbySharingDbusClient;
using ShareTarget = nearby::sharing::linux::app::ShareTarget;
using Status = nearby::sharing::linux::app::Status;
using Transfer = nearby::sharing::linux::app::Transfer;
void OnTargetDiscovered(const ShareTarget& target) override;
void OnTargetUpdated(const ShareTarget& target) override;
void OnTargetLost(const ShareTarget& target) override;
void OnIncomingTransfer(const std::string& direction,
const ShareTarget& target,
const Transfer& transfer) override;
void OnTransferUpdate(const std::string& direction, const ShareTarget& target,
const Transfer& transfer) override;
void OnStatusChanged(const Status& status) override;
void ApplyTarget(const ShareTarget& target);
void RemoveTarget(const ShareTarget& target);
void ApplyStatus(const Status& status);
void ApplyCommandResult(const QString& command,
const std::tuple<bool, std::string>& result);
void RunCommand(
const QString& command,
const std::function<std::tuple<bool, std::string>()>& operation);
void SetStatusText(const QString& text);
int counter_ = 0;
QString status_text_;
Status status_;
std::map<int64_t, ShareTarget> targets_;
std::unique_ptr<Client> client_;
};
Binary file not shown.
Binary file not shown.

After

Width:  |  Height:  |  Size: 85 KiB

+7
View File
@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?><!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
<svg width="800px" height="800px" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M5 5C5 3.89543 5.89543 3 7 3H11.75C11.8881 3 12 3.11193 12 3.25V8C12 9.10457 12.8954 10 14 10H18.75C18.8881 10 19 10.1119 19 10.25V19C19 20.1046 18.1046 21 17 21H7C5.89543 21 5 20.1046 5 19V5Z" fill="#2A4157" fill-opacity="0.24"/>
<path d="M13 8V3.60355C13 3.38083 13.2693 3.26929 13.4268 3.42678L18.5732 8.57322C18.7307 8.73071 18.6192 9 18.3964 9H14C13.4477 9 13 8.55228 13 8Z" fill="#222222"/>
<path d="M8.5 13.5L14.5 13.5" stroke="#222222" stroke-linecap="round"/>
<path d="M8.5 16.5L13.5 16.5" stroke="#222222" stroke-linecap="round"/>
</svg>

After

Width:  |  Height:  |  Size: 777 B

+4
View File
@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?><!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
<svg width="800px" height="800px" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M21 16V7.2C21 6.0799 21 5.51984 20.782 5.09202C20.5903 4.71569 20.2843 4.40973 19.908 4.21799C19.4802 4 18.9201 4 17.8 4H6.2C5.07989 4 4.51984 4 4.09202 4.21799C3.71569 4.40973 3.40973 4.71569 3.21799 5.09202C3 5.51984 3 6.0799 3 7.2V16M4.66667 20H19.3333C19.9533 20 20.2633 20 20.5176 19.9319C21.2078 19.7469 21.7469 19.2078 21.9319 18.5176C22 18.2633 22 17.9533 22 17.3333C22 17.0233 22 16.8683 21.9659 16.7412C21.8735 16.3961 21.6039 16.1265 21.2588 16.0341C21.1317 16 20.9767 16 20.6667 16H3.33333C3.02334 16 2.86835 16 2.74118 16.0341C2.39609 16.1265 2.12654 16.3961 2.03407 16.7412C2 16.8683 2 17.0233 2 17.3333C2 17.9533 2 18.2633 2.06815 18.5176C2.25308 19.2078 2.79218 19.7469 3.48236 19.9319C3.73669 20 4.04669 20 4.66667 20Z" stroke="#000000" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 1.0 KiB

+4
View File
@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?><!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
<svg width="800px" height="800px" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M12 18.01V18M8 3H16C17.1046 3 18 3.89543 18 5V19C18 20.1046 17.1046 21 16 21H8C6.89543 21 6 20.1046 6 19V5C6 3.89543 6.89543 3 8 3Z" stroke="#000000" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 454 B

+12
View File
@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="800px" height="800px" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
<title>ic_fluent_tablet_24_regular</title>
<desc>Created with Sketch.</desc>
<g id="🔍-Product-Icons" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="ic_fluent_tablet_24_regular" fill="#212121" fill-rule="nonzero">
<path d="M19.748832,3.99956021 C20.9914727,3.99956021 21.998832,5.00691952 21.998832,6.24956021 L21.998832,17.7518356 C21.998832,18.9944763 20.9914727,20.0018356 19.748832,20.0018356 L4.25,20.0018356 C3.00735931,20.0018356 2,18.9944763 2,17.7518356 L2,6.24956021 C2,5.00691952 3.00735931,3.99956021 4.25,3.99956021 L19.748832,3.99956021 Z M19.748832,5.49956021 L4.25,5.49956021 C3.83578644,5.49956021 3.5,5.83534665 3.5,6.24956021 L3.5,17.7518356 C3.5,18.1660492 3.83578644,18.5018356 4.25,18.5018356 L19.748832,18.5018356 C20.1630456,18.5018356 20.498832,18.1660492 20.498832,17.7518356 L20.498832,6.24956021 C20.498832,5.83534665 20.1630456,5.49956021 19.748832,5.49956021 Z M10.25,15.5 L13.75,15.5 C14.1642136,15.5 14.5,15.8357864 14.5,16.25 C14.5,16.6296958 14.2178461,16.943491 13.8517706,16.9931534 L13.75,17 L10.25,17 C9.83578644,17 9.5,16.6642136 9.5,16.25 C9.5,15.8703042 9.78215388,15.556509 10.1482294,15.5068466 L10.25,15.5 L13.75,15.5 L10.25,15.5 Z" id="🎨-Color">
</path>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

+4
View File
@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?><!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
<svg width="800px" height="800px" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M19.002 21V15M21.0303 17L19.0303 15L17.0303 17M13 3H8.2C7.0799 3 6.51984 3 6.09202 3.21799C5.71569 3.40973 5.40973 3.71569 5.21799 4.09202C5 4.51984 5 5.0799 5 6.2V17.8C5 18.9201 5 19.4802 5.21799 19.908C5.40973 20.2843 5.71569 20.5903 6.09202 20.782C6.51984 21 7.0799 21 8.2 21H15M13 3L19 9M13 3V7.4C13 7.96005 13 8.24008 13.109 8.45399C13.2049 8.64215 13.3578 8.79513 13.546 8.89101C13.7599 9 14.0399 9 14.6 9H19M19 9V11" stroke="#000000" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 743 B

+148
View File
@@ -0,0 +1,148 @@
#include <QDir>
#include <QFileInfo>
#include <QFileSystemWatcher>
#include <QFontDatabase>
#include <QGuiApplication>
#include <QMetaObject>
#include <QObject>
#include <QTimer>
#include <QQmlApplicationEngine>
namespace {
constexpr char kQmlHotReloadEnv[] = "NEARBY_QML_HOT_RELOAD";
constexpr char kQmlSourceDirEnv[] = "NEARBY_QML_SOURCE_DIR";
constexpr char kDefaultQmlSourceDir[] =
"/home/lasan/Dev/nearby_latest/sharing/linux/app";
bool IsHotReloadEnabled() {
const QByteArray value = qgetenv(kQmlHotReloadEnv);
return value == "1" || value.toLower() == "true";
}
QDir QmlSourceDir() {
const QByteArray configured_dir = qgetenv(kQmlSourceDirEnv);
if (!configured_dir.isEmpty()) {
return QDir(QString::fromLocal8Bit(configured_dir));
}
return QDir(QString::fromUtf8(kDefaultQmlSourceDir));
}
void WatchQmlFiles(QFileSystemWatcher& watcher, const QDir& source_dir) {
const QStringList existing_files = watcher.files();
if (!existing_files.isEmpty()) {
watcher.removePaths(existing_files);
}
const QStringList files =
source_dir.entryList(QStringList() << "*.qml", QDir::Files, QDir::Name);
for (const QString& file : files) {
watcher.addPath(source_dir.absoluteFilePath(file));
}
}
void DestroyRootObjects(QQmlApplicationEngine& engine) {
const QList<QObject*> root_objects = engine.rootObjects();
for (QObject* object : root_objects) {
object->deleteLater();
}
}
bool ReloadInnerContent(QQmlApplicationEngine& engine) {
const QList<QObject*> root_objects = engine.rootObjects();
if (root_objects.isEmpty()) {
return false;
}
QObject* root = root_objects.constFirst();
return QMetaObject::invokeMethod(root, "reloadInnerContent");
}
bool CanReloadInnerContent(const QFileInfo& changed_file) {
return changed_file.fileName() == QStringLiteral("AppContent.qml");
}
} // namespace
int main(int argc, char* argv[]) {
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
const int fontId = QFontDatabase::addApplicationFont(":/googlesans_var.ttf");
if (fontId != -1) {
const QStringList fontFamilies =
QFontDatabase::applicationFontFamilies(fontId);
if (!fontFamilies.isEmpty()) {
const QFont defaultFont(fontFamilies.at(0));
app.setFont(defaultFont);
}
}
if (IsHotReloadEnabled()) {
const QDir qml_source_dir = QmlSourceDir();
const QUrl source_url =
QUrl::fromLocalFile(qml_source_dir.absoluteFilePath("main.qml"));
QFileSystemWatcher watcher;
QTimer reload_timer;
reload_timer.setSingleShot(true);
reload_timer.setInterval(75);
QString changed_path;
const auto fullReload = [&engine, &watcher, qml_source_dir, source_url]() {
WatchQmlFiles(watcher, qml_source_dir);
DestroyRootObjects(engine);
engine.clearComponentCache();
engine.load(source_url);
};
const auto reload = [&engine, &watcher, qml_source_dir, source_url,
&changed_path, &fullReload]() {
WatchQmlFiles(watcher, qml_source_dir);
const QFileInfo changed_file(changed_path);
if (changed_file.fileName() == QStringLiteral("main.qml") ||
!CanReloadInnerContent(changed_file) ||
engine.rootObjects().isEmpty()) {
fullReload();
return;
}
engine.clearComponentCache();
if (!ReloadInnerContent(engine)) {
fullReload();
}
};
QObject::connect(&reload_timer, &QTimer::timeout, &app, reload);
QObject::connect(&watcher, &QFileSystemWatcher::fileChanged, &app,
[&reload_timer, &changed_path](const QString& path) {
changed_path = path;
reload_timer.start();
});
QObject::connect(&watcher, &QFileSystemWatcher::directoryChanged, &app,
[&reload_timer, &changed_path](const QString& path) {
changed_path = path;
reload_timer.start();
});
watcher.addPath(qml_source_dir.absolutePath());
fullReload();
return app.exec();
}
// Loaded via the qrc scheme since it's compiled into the binary
const QUrl url(QStringLiteral("qrc:/main.qml"));
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();
}
+30
View File
@@ -0,0 +1,30 @@
import QtQuick
import QtQuick.Window
Window {
id: root
width: 640
height: 480
visible: true
title: qsTr("QuickShare")
color: "#DCF5FF"
FontLoader {
id: googlesans
source: "qrc:/googlesans_var.ttf"
}
Loader {
id: contentLoader
anchors.fill: parent
source: "AppContent.qml"
}
function reloadInnerContent() {
const nextSource = "AppContent.qml?rev=" + Date.now()
contentLoader.active = false
contentLoader.source = ""
contentLoader.active = true
contentLoader.source = nextSource
}
}
@@ -0,0 +1,214 @@
#include "sharing/linux/app/nearby_sharing_dbus_client.h"
#include <utility>
namespace nearby::sharing::linux::app {
namespace {
template <typename T>
T GetField(const DbusDictionary& map, const std::string& key,
T default_value = T{}) {
auto it = map.find(key);
if (it == map.end()) {
return default_value;
}
try {
return it->second.get<T>();
} catch (const sdbus::Error&) {
return default_value;
}
}
template <typename T>
std::optional<T> GetOptionalField(const DbusDictionary& map,
const std::string& key) {
auto it = map.find(key);
if (it == map.end()) {
return std::nullopt;
}
try {
return it->second.get<T>();
} catch (const sdbus::Error&) {
return std::nullopt;
}
}
double GetProgress(const DbusDictionary& map) {
if (auto progress = GetOptionalField<double>(map, "progress")) {
return *progress;
}
if (auto progress = GetOptionalField<int32_t>(map, "progress")) {
return *progress;
}
if (auto progress = GetOptionalField<int64_t>(map, "progress")) {
return *progress;
}
return 0;
}
} // namespace
NearbySharingDbusClient::NearbySharingDbusClient(Observer* observer)
: sdbus::ProxyInterfaces<com::google::nearby::sharing_proxy>(
sdbus::ServiceName("com.google.nearby.sharing"),
sdbus::ObjectPath("/com/google/nearby/sharing")),
observer_(observer) {
registerProxy();
}
NearbySharingDbusClient::~NearbySharingDbusClient() {
unregisterProxy();
}
std::tuple<bool, std::string> NearbySharingDbusClient::StartReceive() {
return com::google::nearby::sharing_proxy::StartReceive();
}
std::tuple<bool, std::string> NearbySharingDbusClient::StopReceive() {
return com::google::nearby::sharing_proxy::StopReceive();
}
std::tuple<bool, std::string> NearbySharingDbusClient::StartDiscovery() {
return com::google::nearby::sharing_proxy::StartDiscovery();
}
std::tuple<bool, std::string> NearbySharingDbusClient::StopDiscovery() {
return com::google::nearby::sharing_proxy::StopDiscovery();
}
std::tuple<bool, std::string> NearbySharingDbusClient::SendFile(
int64_t share_target_id, const std::string& path) {
return com::google::nearby::sharing_proxy::SendFile(share_target_id, path);
}
std::tuple<bool, std::string> NearbySharingDbusClient::Accept(
int64_t share_target_id) {
return com::google::nearby::sharing_proxy::Accept(share_target_id);
}
std::tuple<bool, std::string> NearbySharingDbusClient::Reject(
int64_t share_target_id) {
return com::google::nearby::sharing_proxy::Reject(share_target_id);
}
std::tuple<bool, std::string> NearbySharingDbusClient::Cancel(
int64_t share_target_id) {
return com::google::nearby::sharing_proxy::Cancel(share_target_id);
}
ShareTarget NearbySharingDbusClient::ConvertToShareTarget(
const DbusDictionary& map) {
ShareTarget target;
target.id = GetField<int64_t>(map, "id");
target.device_name = GetField<std::string>(map, "device_name");
target.type = GetField<int32_t>(map, "type");
target.is_incoming = GetField<bool>(map, "is_incoming");
target.is_known = GetField<bool>(map, "is_known");
target.device_id = GetField<std::string>(map, "device_id");
target.for_self_share = GetField<bool>(map, "for_self_share");
target.vendor_id = GetField<int32_t>(map, "vendor_id");
target.receive_disabled = GetField<bool>(map, "receive_disabled");
return target;
}
Transfer NearbySharingDbusClient::ConvertToTransfer(const DbusDictionary& map) {
Transfer transfer;
transfer.status = GetField<std::string>(map, "status");
transfer.progress = GetProgress(map);
transfer.transferred_bytes = GetField<int64_t>(map, "transferred_bytes");
transfer.total_bytes = GetField<int64_t>(map, "total_bytes");
transfer.transfer_speed = GetField<int64_t>(map, "transfer_speed");
transfer.estimated_time_remaining =
GetField<int64_t>(map, "estimated_time_remaining");
transfer.total_attachments_count =
GetField<int32_t>(map, "total_attachments_count");
transfer.transferred_attachments_count =
GetField<int32_t>(map, "transferred_attachments_count");
transfer.is_final_status = GetField<bool>(map, "is_final_status");
transfer.is_self_share = GetField<bool>(map, "is_self_share");
transfer.binding_id = GetField<std::string>(map, "binding_id");
transfer.token = GetOptionalField<std::string>(map, "token");
transfer.in_progress_attachment_id =
GetOptionalField<int64_t>(map, "in_progress_attachment_id");
transfer.in_progress_attachment_transferred_bytes = GetOptionalField<int64_t>(
map, "in_progress_attachment_transferred_bytes");
transfer.in_progress_attachment_total_bytes =
GetOptionalField<int64_t>(map, "in_progress_attachment_total_bytes");
return transfer;
}
Status NearbySharingDbusClient::ConvertToStatus(const DbusDictionary& map) {
Status status;
status.receive_registered = GetField<bool>(map, "receive_registered");
status.discovery_registered = GetField<bool>(map, "discovery_registered");
status.is_transferring = GetField<bool>(map, "is_transferring");
status.is_scanning = GetField<bool>(map, "is_scanning");
status.bluetooth_present = GetField<bool>(map, "bluetooth_present");
status.bluetooth_powered = GetField<bool>(map, "bluetooth_powered");
status.lan_connected = GetField<bool>(map, "lan_connected");
auto raw_targets =
GetOptionalField<std::vector<DbusDictionary>>(map, "targets");
if (raw_targets.has_value()) {
status.targets.reserve(raw_targets->size());
for (const auto& target : *raw_targets) {
status.targets.push_back(ConvertToShareTarget(target));
}
}
return status;
}
void NearbySharingDbusClient::onTargetDiscovered(
const DbusDictionary& share_target) {
if (observer_ == nullptr) {
return;
}
observer_->OnTargetDiscovered(ConvertToShareTarget(share_target));
}
void NearbySharingDbusClient::onTargetUpdated(
const DbusDictionary& share_target) {
if (observer_ == nullptr) {
return;
}
observer_->OnTargetUpdated(ConvertToShareTarget(share_target));
}
void NearbySharingDbusClient::onTargetLost(const DbusDictionary& share_target) {
if (observer_ == nullptr) {
return;
}
observer_->OnTargetLost(ConvertToShareTarget(share_target));
}
void NearbySharingDbusClient::onIncomingTransfer(
const std::string& direction, const DbusDictionary& share_target,
const DbusDictionary& transfer) {
if (observer_ == nullptr) {
return;
}
observer_->OnIncomingTransfer(direction, ConvertToShareTarget(share_target),
ConvertToTransfer(transfer));
}
void NearbySharingDbusClient::onTransferUpdate(
const std::string& direction, const DbusDictionary& share_target,
const DbusDictionary& transfer) {
if (observer_ == nullptr) {
return;
}
observer_->OnTransferUpdate(direction, ConvertToShareTarget(share_target),
ConvertToTransfer(transfer));
}
void NearbySharingDbusClient::onStatusChanged(const DbusDictionary& status) {
if (observer_ == nullptr) {
return;
}
observer_->OnStatusChanged(ConvertToStatus(status));
}
} // namespace nearby::sharing::linux::app
@@ -0,0 +1,111 @@
#pragma once
#include <cstdint>
#include <map>
#include <optional>
#include <string>
#include <tuple>
#include <vector>
#include <sdbus-c++/sdbus-c++.h>
#include "sharing/linux/daemon/nearby_sharing_client.h"
namespace nearby::sharing::linux::app {
using DbusDictionary = std::map<std::string, sdbus::Variant>;
struct ShareTarget {
int64_t id = 0;
std::string device_name;
int32_t type = 0;
bool is_incoming = false;
bool is_known = false;
std::string device_id;
bool for_self_share = false;
int32_t vendor_id = 0;
bool receive_disabled = false;
};
struct Transfer {
std::string status;
double progress = 0;
int64_t transferred_bytes = 0;
int64_t total_bytes = 0;
int64_t transfer_speed = 0;
int64_t estimated_time_remaining = 0;
int32_t total_attachments_count = 0;
int32_t transferred_attachments_count = 0;
bool is_final_status = false;
bool is_self_share = false;
std::string binding_id;
std::optional<std::string> token;
std::optional<int64_t> in_progress_attachment_id;
std::optional<int64_t> in_progress_attachment_transferred_bytes;
std::optional<int64_t> in_progress_attachment_total_bytes;
};
struct Status {
bool receive_registered = false;
bool discovery_registered = false;
bool is_transferring = false;
bool is_scanning = false;
bool bluetooth_present = false;
bool bluetooth_powered = false;
bool lan_connected = false;
std::vector<ShareTarget> targets;
};
class NearbySharingDbusClient
: public sdbus::ProxyInterfaces<com::google::nearby::sharing_proxy> {
public:
struct Observer {
virtual ~Observer() = default;
virtual void OnTargetDiscovered(const ShareTarget& target) {}
virtual void OnTargetUpdated(const ShareTarget& target) {}
virtual void OnTargetLost(const ShareTarget& target) {}
virtual void OnIncomingTransfer(const std::string& direction,
const ShareTarget& target,
const Transfer& transfer) {}
virtual void OnTransferUpdate(const std::string& direction,
const ShareTarget& target,
const Transfer& transfer) {}
virtual void OnStatusChanged(const Status& status) {}
};
explicit NearbySharingDbusClient(Observer* observer);
~NearbySharingDbusClient();
NearbySharingDbusClient(const NearbySharingDbusClient&) = delete;
NearbySharingDbusClient& operator=(const NearbySharingDbusClient&) = delete;
std::tuple<bool, std::string> StartReceive();
std::tuple<bool, std::string> StopReceive();
std::tuple<bool, std::string> StartDiscovery();
std::tuple<bool, std::string> StopDiscovery();
std::tuple<bool, std::string> SendFile(int64_t share_target_id,
const std::string& path);
std::tuple<bool, std::string> Accept(int64_t share_target_id);
std::tuple<bool, std::string> Reject(int64_t share_target_id);
std::tuple<bool, std::string> Cancel(int64_t share_target_id);
static ShareTarget ConvertToShareTarget(const DbusDictionary& map);
static Transfer ConvertToTransfer(const DbusDictionary& map);
static Status ConvertToStatus(const DbusDictionary& map);
private:
void onTargetDiscovered(const DbusDictionary& share_target) override;
void onTargetUpdated(const DbusDictionary& share_target) override;
void onTargetLost(const DbusDictionary& share_target) override;
void onIncomingTransfer(const std::string& direction,
const DbusDictionary& share_target,
const DbusDictionary& transfer) override;
void onTransferUpdate(const std::string& direction,
const DbusDictionary& share_target,
const DbusDictionary& transfer) override;
void onStatusChanged(const DbusDictionary& status) override;
Observer* observer_ = nullptr;
};
} // namespace nearby::sharing::linux::app
+17
View File
@@ -0,0 +1,17 @@
<!DOCTYPE RCC><RCC version="1.0">
<qresource prefix="/">
<file>main.qml</file>
<file>AppContent.qml</file>
<file>Sidebar.qml</file>
<file>Drop.qml</file>
<file>ShareTarget.qml</file>
<file>Targets.qml</file>
<file>googlesans_var.ttf</file>
<file>icons/file.svg</file>
<file>icons/up_file.svg</file>
<file>icons/tablet.svg</file>
<file>icons/laptop.svg</file>
<file>icons/smartphone.svg</file>
</qresource>
</RCC>
+8
View File
@@ -18,12 +18,20 @@ cc_binary(
],
)
cc_library(
name = "nearby_sharing_dbus_client_glue",
hdrs = ["nearby_sharing_client.h"],
visibility = ["//visibility:public"],
deps = ["@sdbus_cpp"],
)
cc_library(
name = "nearby_sharing_dbus_service",
srcs = ["nearby_sharing_dbus_service.cc"],
hdrs = [
"nearby_sharing_dbus_service.h",
"nearby_sharing_server.h",
"nearby_sharing_client.h",
],
deps = [
"//internal/base:file_path",
+2 -2
View File
@@ -3,8 +3,8 @@
* This file was automatically generated by sdbus-c++-xml2cpp; DO NOT EDIT!
*/
#ifndef __sdbuscpp___sharing_linux_daemon_nearby_sharing_client_h__proxy__H__
#define __sdbuscpp___sharing_linux_daemon_nearby_sharing_client_h__proxy__H__
#ifndef __sdbuscpp__sharing_linux_daemon_nearby_sharing_client_h__proxy__H__
#define __sdbuscpp__sharing_linux_daemon_nearby_sharing_client_h__proxy__H__
#include <sdbus-c++/sdbus-c++.h>
#include <string>
+16 -1
View File
@@ -25,7 +25,22 @@ cc_binary(
"main.cc",
],
deps = [
":app",
#":app",
"nearby_sharing_dbus_client"
]
)
cc_library(
name ="nearby_sharing_dbus_client",
hdrs = [
'nearby_sharing_dbus_client.h'
],
srcs = [
'nearby_sharing_dbus_client.cc'
],
deps = [
"//sharing/linux/daemon:nearby_sharing_daemon",
"@sdbus_cpp",
]
)
-4
View File
@@ -17,15 +17,11 @@ class TuiApp {
int Run();
private:
bool HandleEvent(Event event);
ScreenInteractive screen_;
ZenityFilePicker file_picker_;
Page current_page_ = Page::FilePicker;
std::string hostname_;
std::string selected_file_;
std::string incoming_share_device_name_ = "Lasan's A55";
ShareTargetType incoming_share_device_type_ = ShareTargetType::kPhone;
};
} // namespace nearby::sharing::linux_tui
+12 -3
View File
@@ -1,6 +1,15 @@
#include "sharing/linux/tui/app.h"
#include "nearby_sharing_dbus_client.h"
#include <sdbus-c++/IConnection.h>
int main() {
nearby::sharing::linux_tui::TuiApp app;
return app.Run();
auto sharing =
NearbySharingService(sdbus::ServiceName("com.google.nearby.sharing"),
sdbus::ObjectPath("/com/google/nearby/sharing"));
sharing.StartReceive();
sleep(10);
sharing.StopReceive();
// nearby::sharing::linux_tui::TuiApp app;
// return app.Run();
return 0;
}
@@ -0,0 +1,143 @@
#include "sharing/linux/tui/nearby_sharing_dbus_client.h"
// Helper to extract a value from the map with a fallback if missing or type
// mismatches
template <typename T>
T get_field(const std::map<std::string, sdbus::Variant>& map,
const std::string& key, T default_value = T{}) {
auto it = map.find(key);
if (it != map.end()) {
try {
return it->second.get<T>();
} catch (const sdbus::Error& e) {
// Log type mismatch error if necessary
}
}
return default_value;
}
// Helper for optional fields
template <typename T>
std::optional<T> get_optional_field(
const std::map<std::string, sdbus::Variant>& map, const std::string& key) {
auto it = map.find(key);
if (it != map.end()) {
try {
return it->second.get<T>();
} catch (const sdbus::Error& e) {
// Log type mismatch error if necessary
}
}
return std::nullopt;
}
// Converter: Map -> ShareTarget
ShareTarget convertToShareTarget(
const std::map<std::string, sdbus::Variant>& map) {
ShareTarget target;
target.id = get_field<int64_t>(map, "id");
target.device_name = get_field<std::string>(map, "device_name");
target.type = get_field<int32_t>(map, "type");
target.is_incoming = get_field<bool>(map, "is_incoming");
target.is_known = get_field<bool>(map, "is_known");
target.device_id = get_field<std::string>(map, "device_id");
target.for_self_share = get_field<bool>(map, "for_self_share");
target.vendor_id = get_field<int32_t>(map, "vendor_id");
target.receive_disabled = get_field<bool>(map, "receive_disabled");
return target;
}
// Converter: Map -> Transfer
Transfer convertToTransfer(const std::map<std::string, sdbus::Variant>& map) {
Transfer transfer;
transfer.status = get_field<std::string>(map, "status");
transfer.progress = get_field<int32_t>(map, "progress");
transfer.transferred_bytes = get_field<int64_t>(map, "transferred_bytes");
transfer.total_bytes = get_field<int64_t>(map, "total_bytes");
transfer.transfer_speed = get_field<int64_t>(map, "transfer_speed");
transfer.estimated_time_remaining =
get_field<int64_t>(map, "estimated_time_remaining");
transfer.total_attachments_count =
get_field<int32_t>(map, "total_attachments_count");
transfer.transferred_attachments_count =
get_field<int32_t>(map, "transferred_attachments_count");
transfer.is_final_status = get_field<bool>(map, "is_final_status");
transfer.is_self_share = get_field<bool>(map, "is_self_share");
transfer.binding_id = get_field<std::string>(map, "binding_id");
// Optional fields
transfer.token = get_optional_field<std::string>(map, "token");
transfer.in_progress_attachment_id =
get_optional_field<int64_t>(map, "in_progress_attachment_id");
transfer.in_progress_attachment_transferred_bytes =
get_optional_field<int64_t>(map,
"in_progress_attachment_transferred_bytes");
transfer.in_progress_attachment_total_bytes =
get_optional_field<int64_t>(map, "in_progress_attachment_total_bytes");
return transfer;
}
// Converter: Map -> Status
Status convertToStatus(const std::map<std::string, sdbus::Variant>& map) {
Status status;
status.receive_registered = get_field<bool>(map, "receive_registered");
status.discovery_registered = get_field<bool>(map, "discovery_registered");
status.is_transferring = get_field<bool>(map, "is_transferring");
status.is_scanning = get_field<bool>(map, "is_scanning");
status.bluetooth_present = get_field<bool>(map, "bluetooth_present");
status.bluetooth_powered = get_field<bool>(map, "bluetooth_powered");
status.lan_connected = get_field<bool>(map, "lan_connected");
// Unpack aa{sv} (vector of maps) into vector of ShareTarget structs
auto it = map.find("targets");
if (it != map.end()) {
try {
auto raw_targets =
it->second.get<std::vector<std::map<std::string, sdbus::Variant>>>();
for (const auto& target_map : raw_targets) {
status.targets.push_back(convertToShareTarget(target_map));
}
} catch (const sdbus::Error& e) {
// Handle type mismatch for targets array
}
}
return status;
}
void NearbySharingService::onTargetDiscovered(
const std::map<std::string, sdbus::Variant>& share_target) {
auto target = convertToShareTarget(share_target);
targets_[target.id] = target;
};
void NearbySharingService::onTargetUpdated(
const std::map<std::string, sdbus::Variant>& share_target) {
auto target = convertToShareTarget(share_target);
targets_[target.id] = target;
};
void NearbySharingService::onTargetLost(
const std::map<std::string, sdbus::Variant>& share_target) {
auto target = convertToShareTarget(share_target);
if (targets_.find(target.id) != targets_.end()) {
targets_.erase(target.id);
}
};
void NearbySharingService::onIncomingTransfer(
const std::string& direction,
const std::map<std::string, sdbus::Variant>& share_target,
const std::map<std::string, sdbus::Variant>& transfer) {
auto target = convertToShareTarget(share_target);
auto incoming_transfer = convertToTransfer(transfer);
incoming_transfer_ = incoming_transfer;
};
void NearbySharingService::onTransferUpdate(
const std::string& direction,
const std::map<std::string, sdbus::Variant>& share_target,
const std::map<std::string, sdbus::Variant>& transfer) {};
void NearbySharingService::onStatusChanged(
const std::map<std::string, sdbus::Variant>& share_target) {};
@@ -0,0 +1,78 @@
#include "sharing/linux/daemon/nearby_sharing_client.h"
struct ShareTarget {
int64_t id;
std::string device_name;
int32_t type;
bool is_incoming;
bool is_known;
std::string device_id;
bool for_self_share;
int32_t vendor_id;
bool receive_disabled;
};
struct Transfer {
std::string status;
int32_t progress;
int64_t transferred_bytes;
int64_t total_bytes;
int64_t transfer_speed;
int64_t estimated_time_remaining;
int32_t total_attachments_count;
int32_t transferred_attachments_count;
bool is_final_status;
bool is_self_share;
std::string binding_id;
std::optional<std::string> token;
std::optional<int64_t> in_progress_attachment_id;
std::optional<int64_t> in_progress_attachment_transferred_bytes;
std::optional<int64_t> in_progress_attachment_total_bytes;
};
struct Status {
bool receive_registered;
bool discovery_registered;
bool is_transferring;
bool is_scanning;
bool bluetooth_present;
bool bluetooth_powered;
bool lan_connected;
// aa{sv} maps to a vector of ShareTarget structs
std::vector<ShareTarget> targets;
};
class NearbySharingService
: public sdbus::ProxyInterfaces<com::google::nearby::sharing_proxy> {
public:
NearbySharingService(sdbus::ServiceName dest, sdbus::ObjectPath objectPath)
: ProxyInterfaces(std::move(dest), std::move(objectPath)) {
registerProxy();
}
~NearbySharingService() { unregisterProxy(); }
void onTargetDiscovered(
const std::map<std::string, sdbus::Variant>& share_target) override;
void onTargetUpdated(
const std::map<std::string, sdbus::Variant>& share_target) override;
void onTargetLost(
const std::map<std::string, sdbus::Variant>& share_target) override;
void onIncomingTransfer(
const std::string& direction,
const std::map<std::string, sdbus::Variant>& share_target,
const std::map<std::string, sdbus::Variant>& transfer) override;
void onTransferUpdate(
const std::string& direction,
const std::map<std::string, sdbus::Variant>& share_target,
const std::map<std::string, sdbus::Variant>& transfer) override;
void onStatusChanged(
const std::map<std::string, sdbus::Variant>& status) override;
private:
Status status_;
std::map<int, ShareTarget> targets_;
Transfer outgoing_transfer_;
Transfer incoming_transfer_;
};