mirror of
https://github.com/kidfromjupiter/nearby.git
synced 2026-09-14 14:46:12 -04:00
added file share qml tray app. made improvements to qml tray controller
This commit is contained in:
+159
@@ -0,0 +1,159 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
TARGET="//sharing/linux:nearby_connections_service_linux_shared"
|
||||
HEADER_SRC="sharing/linux/nearby_connections_qt_facade.h"
|
||||
BAZEL_CMD="${BAZEL:-bazel}"
|
||||
PREFIX="/usr/local"
|
||||
LIBDIR=""
|
||||
INCLUDEDIR=""
|
||||
SKIP_BUILD=0
|
||||
NEEDS_ELEVATION=0
|
||||
INSTALL_PREFIX=()
|
||||
|
||||
usage() {
|
||||
cat <<USAGE
|
||||
Usage: $0 [options]
|
||||
|
||||
Builds and installs the Nearby Connections shared library and public header.
|
||||
|
||||
Options:
|
||||
--prefix DIR Install prefix (default: /usr/local)
|
||||
--libdir DIR Library directory (default: <prefix>/lib)
|
||||
--includedir DIR Include root directory (default: <prefix>/include)
|
||||
--bazel CMD Bazel command (default: bazel or env BAZEL)
|
||||
--skip-build Skip bazel build step and only install from bazel-bin
|
||||
-h, --help Show this help
|
||||
|
||||
Examples:
|
||||
$0
|
||||
sudo $0
|
||||
sudo $0 --prefix /usr
|
||||
sudo $0 --bazel /usr/bin/bazel
|
||||
USAGE
|
||||
}
|
||||
|
||||
run_bazel() {
|
||||
# If invoked through sudo, run Bazel as the original user so Bazelisk reuses
|
||||
# that user's cache and does not re-download Bazel as root.
|
||||
if [[ "$(id -u)" -eq 0 && -n "${SUDO_USER:-}" ]]; then
|
||||
local caller_home
|
||||
caller_home="$(getent passwd "$SUDO_USER" | cut -d: -f6)"
|
||||
if [[ -z "$caller_home" ]]; then
|
||||
echo "Failed to resolve home directory for SUDO_USER=$SUDO_USER" >&2
|
||||
exit 1
|
||||
fi
|
||||
sudo -u "$SUDO_USER" -H env \
|
||||
HOME="$caller_home" \
|
||||
BAZELISK_HOME="${BAZELISK_HOME:-$caller_home/.cache/bazelisk}" \
|
||||
"$BAZEL_CMD" "$@"
|
||||
else
|
||||
"$BAZEL_CMD" "$@"
|
||||
fi
|
||||
}
|
||||
|
||||
nearest_existing_parent() {
|
||||
local p="$1"
|
||||
while [[ ! -e "$p" ]]; do
|
||||
p="$(dirname "$p")"
|
||||
done
|
||||
printf '%s\n' "$p"
|
||||
}
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--prefix)
|
||||
PREFIX="$2"
|
||||
shift 2
|
||||
;;
|
||||
--libdir)
|
||||
LIBDIR="$2"
|
||||
shift 2
|
||||
;;
|
||||
--includedir)
|
||||
INCLUDEDIR="$2"
|
||||
shift 2
|
||||
;;
|
||||
--bazel)
|
||||
BAZEL_CMD="$2"
|
||||
shift 2
|
||||
;;
|
||||
--skip-build)
|
||||
SKIP_BUILD=1
|
||||
shift
|
||||
;;
|
||||
-h|--help)
|
||||
usage
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
echo "Unknown argument: $1" >&2
|
||||
usage
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [[ -z "$LIBDIR" ]]; then
|
||||
LIBDIR="${PREFIX}/lib"
|
||||
fi
|
||||
|
||||
if [[ -z "$INCLUDEDIR" ]]; then
|
||||
INCLUDEDIR="${PREFIX}/include"
|
||||
fi
|
||||
|
||||
LIB_PARENT="$(nearest_existing_parent "$LIBDIR")"
|
||||
INCLUDE_PARENT="$(nearest_existing_parent "${INCLUDEDIR}/sharing/linux")"
|
||||
|
||||
if [[ ! -w "$LIB_PARENT" || ! -w "$INCLUDE_PARENT" ]]; then
|
||||
NEEDS_ELEVATION=1
|
||||
fi
|
||||
|
||||
if [[ "$NEEDS_ELEVATION" -eq 1 && "$(id -u)" -ne 0 ]]; then
|
||||
if ! command -v sudo >/dev/null 2>&1; then
|
||||
echo "Install requires elevated privileges, but sudo is not available." >&2
|
||||
exit 1
|
||||
fi
|
||||
INSTALL_PREFIX=(sudo)
|
||||
fi
|
||||
|
||||
if [[ ! -f "$HEADER_SRC" ]]; then
|
||||
echo "Header not found: $HEADER_SRC" >&2
|
||||
echo "Run this script from the workspace root." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ "$SKIP_BUILD" -eq 0 ]]; then
|
||||
echo "[1/4] Building $TARGET"
|
||||
run_bazel build "$TARGET"
|
||||
else
|
||||
echo "[1/4] Skipping build (--skip-build)"
|
||||
fi
|
||||
|
||||
echo "[2/4] Resolving bazel-bin path"
|
||||
BAZEL_BIN="$(run_bazel info bazel-bin)"
|
||||
LIB_SRC="${BAZEL_BIN}/sharing/linux/libnearby_connections_service_linux_shared.so"
|
||||
|
||||
if [[ ! -f "$LIB_SRC" ]]; then
|
||||
echo "Shared library not found: $LIB_SRC" >&2
|
||||
echo "Expected Bazel output for $TARGET is missing." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "[3/4] Installing library and header"
|
||||
"${INSTALL_PREFIX[@]}" install -d "$LIBDIR"
|
||||
"${INSTALL_PREFIX[@]}" install -d "${INCLUDEDIR}/sharing/linux"
|
||||
"${INSTALL_PREFIX[@]}" install -m 0755 "$LIB_SRC" "$LIBDIR/"
|
||||
"${INSTALL_PREFIX[@]}" install -m 0644 "$HEADER_SRC" "${INCLUDEDIR}/sharing/linux/"
|
||||
|
||||
if command -v ldconfig >/dev/null 2>&1; then
|
||||
echo "[4/4] Refreshing dynamic linker cache"
|
||||
"${INSTALL_PREFIX[@]}" ldconfig
|
||||
else
|
||||
echo "[4/4] ldconfig not found; skipping linker cache refresh"
|
||||
fi
|
||||
|
||||
echo "Installed:"
|
||||
echo " library: $LIBDIR/$(basename "$LIB_SRC")"
|
||||
echo " header : ${INCLUDEDIR}/sharing/linux/$(basename "$HEADER_SRC")"
|
||||
@@ -3,11 +3,13 @@
|
||||
#include <atomic>
|
||||
#include <utility>
|
||||
|
||||
#include "connections/implementation/flags/nearby_connections_feature_flags.h"
|
||||
#include "internal/base/file_path.h"
|
||||
#include "internal/flags/nearby_flags.h"
|
||||
#include "sharing/linux/nearby_connections_service_linux.h"
|
||||
#include "sharing/nearby_connections_types.h"
|
||||
|
||||
namespace nearby::sharing::linux {
|
||||
namespace nearby::sharing {
|
||||
|
||||
namespace {
|
||||
|
||||
@@ -345,7 +347,7 @@ std::unique_ptr<nearby::sharing::Payload> ToNativePayload(Facade::Payload payloa
|
||||
|
||||
class NearbyConnectionsQtFacade::Impl {
|
||||
public:
|
||||
NearbyConnectionsServiceLinux service;
|
||||
linux::NearbyConnectionsServiceLinux service;
|
||||
};
|
||||
|
||||
NearbyConnectionsQtFacade::NearbyConnectionsQtFacade()
|
||||
@@ -359,6 +361,18 @@ NearbyConnectionsQtFacade::NearbyConnectionsQtFacade(
|
||||
NearbyConnectionsQtFacade& NearbyConnectionsQtFacade::operator=(
|
||||
NearbyConnectionsQtFacade&&) noexcept = default;
|
||||
|
||||
void NearbyConnectionsQtFacade::SetBleL2capFlagOverrides(
|
||||
bool enable_ble_l2cap, bool refactor_ble_l2cap) {
|
||||
nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue(
|
||||
nearby::connections::config_package_nearby::nearby_connections_feature::
|
||||
kEnableBleL2cap,
|
||||
enable_ble_l2cap);
|
||||
nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue(
|
||||
nearby::connections::config_package_nearby::nearby_connections_feature::
|
||||
kRefactorBleL2cap,
|
||||
refactor_ble_l2cap);
|
||||
}
|
||||
|
||||
NearbyConnectionsQtFacade::Payload NearbyConnectionsQtFacade::CreateBytesPayload(
|
||||
std::vector<uint8_t> bytes) const {
|
||||
Payload payload;
|
||||
|
||||
@@ -19,10 +19,10 @@
|
||||
#undef linux
|
||||
#endif
|
||||
|
||||
namespace nearby::sharing::linux {
|
||||
|
||||
namespace nearby {
|
||||
namespace sharing {
|
||||
class NearbyConnectionsQtFacade {
|
||||
public:
|
||||
public:
|
||||
enum class Status {
|
||||
kSuccess = 0,
|
||||
kError = 1,
|
||||
@@ -141,84 +141,88 @@ class NearbyConnectionsQtFacade {
|
||||
};
|
||||
|
||||
struct ConnectionListener {
|
||||
std::function<void(const std::string&, const ConnectionInfo&)> initiated_cb;
|
||||
std::function<void(const std::string&)> accepted_cb;
|
||||
std::function<void(const std::string&, Status)> rejected_cb;
|
||||
std::function<void(const std::string&)> disconnected_cb;
|
||||
std::function<void(const std::string&, Medium)> bandwidth_changed_cb;
|
||||
std::function<void(const std::string &, const ConnectionInfo &)>
|
||||
initiated_cb;
|
||||
std::function<void(const std::string &)> accepted_cb;
|
||||
std::function<void(const std::string &, Status)> rejected_cb;
|
||||
std::function<void(const std::string &)> disconnected_cb;
|
||||
std::function<void(const std::string &, Medium)> bandwidth_changed_cb;
|
||||
};
|
||||
|
||||
struct DiscoveryListener {
|
||||
std::function<void(const std::string&, const DiscoveredEndpointInfo&)>
|
||||
std::function<void(const std::string &, const DiscoveredEndpointInfo &)>
|
||||
endpoint_found_cb;
|
||||
std::function<void(const std::string&)> endpoint_lost_cb;
|
||||
std::function<void(const std::string&, DistanceInfo)>
|
||||
std::function<void(const std::string &)> endpoint_lost_cb;
|
||||
std::function<void(const std::string &, DistanceInfo)>
|
||||
endpoint_distance_changed_cb;
|
||||
};
|
||||
|
||||
struct PayloadListener {
|
||||
std::function<void(const std::string&, Payload)> payload_cb;
|
||||
std::function<void(const std::string&, const PayloadTransferUpdate&)>
|
||||
std::function<void(const std::string &, Payload)> payload_cb;
|
||||
std::function<void(const std::string &, const PayloadTransferUpdate &)>
|
||||
payload_progress_cb;
|
||||
};
|
||||
|
||||
NearbyConnectionsQtFacade();
|
||||
~NearbyConnectionsQtFacade();
|
||||
|
||||
NearbyConnectionsQtFacade(const NearbyConnectionsQtFacade&) = delete;
|
||||
NearbyConnectionsQtFacade& operator=(const NearbyConnectionsQtFacade&) =
|
||||
delete;
|
||||
NearbyConnectionsQtFacade(NearbyConnectionsQtFacade&&) noexcept;
|
||||
NearbyConnectionsQtFacade& operator=(
|
||||
NearbyConnectionsQtFacade&&) noexcept;
|
||||
NearbyConnectionsQtFacade(const NearbyConnectionsQtFacade &) = delete;
|
||||
NearbyConnectionsQtFacade &
|
||||
operator=(const NearbyConnectionsQtFacade &) = delete;
|
||||
NearbyConnectionsQtFacade(NearbyConnectionsQtFacade &&) noexcept;
|
||||
NearbyConnectionsQtFacade &operator=(NearbyConnectionsQtFacade &&) noexcept;
|
||||
|
||||
// Sets global Nearby flag overrides for BLE L2CAP.
|
||||
static void SetBleL2capFlagOverrides(bool enable_ble_l2cap,
|
||||
bool refactor_ble_l2cap);
|
||||
|
||||
Payload CreateBytesPayload(std::vector<uint8_t> bytes) const;
|
||||
|
||||
void StartAdvertising(const std::string& service_id,
|
||||
const std::vector<uint8_t>& endpoint_info,
|
||||
const AdvertisingOptions& advertising_options,
|
||||
void StartAdvertising(const std::string &service_id,
|
||||
const std::vector<uint8_t> &endpoint_info,
|
||||
const AdvertisingOptions &advertising_options,
|
||||
ConnectionListener advertising_listener,
|
||||
std::function<void(Status)> callback);
|
||||
void StopAdvertising(const std::string& service_id,
|
||||
void StopAdvertising(const std::string &service_id,
|
||||
std::function<void(Status)> callback);
|
||||
|
||||
void StartDiscovery(const std::string& service_id,
|
||||
const DiscoveryOptions& discovery_options,
|
||||
void StartDiscovery(const std::string &service_id,
|
||||
const DiscoveryOptions &discovery_options,
|
||||
DiscoveryListener discovery_listener,
|
||||
std::function<void(Status)> callback);
|
||||
void StopDiscovery(const std::string& service_id,
|
||||
void StopDiscovery(const std::string &service_id,
|
||||
std::function<void(Status)> callback);
|
||||
|
||||
void RequestConnection(const std::string& service_id,
|
||||
const std::vector<uint8_t>& endpoint_info,
|
||||
const std::string& endpoint_id,
|
||||
const ConnectionOptions& connection_options,
|
||||
void RequestConnection(const std::string &service_id,
|
||||
const std::vector<uint8_t> &endpoint_info,
|
||||
const std::string &endpoint_id,
|
||||
const ConnectionOptions &connection_options,
|
||||
ConnectionListener connection_listener,
|
||||
std::function<void(Status)> callback);
|
||||
|
||||
void DisconnectFromEndpoint(const std::string& service_id,
|
||||
const std::string& endpoint_id,
|
||||
void DisconnectFromEndpoint(const std::string &service_id,
|
||||
const std::string &endpoint_id,
|
||||
std::function<void(Status)> callback);
|
||||
|
||||
void SendPayload(const std::string& service_id,
|
||||
const std::vector<std::string>& endpoint_ids, Payload payload,
|
||||
std::function<void(Status)> callback);
|
||||
void InitiateBandwidthUpgrade(const std::string& service_id,
|
||||
const std::string& endpoint_id,
|
||||
void SendPayload(const std::string &service_id,
|
||||
const std::vector<std::string> &endpoint_ids,
|
||||
Payload payload, std::function<void(Status)> callback);
|
||||
void InitiateBandwidthUpgrade(const std::string &service_id,
|
||||
const std::string &endpoint_id,
|
||||
std::function<void(Status)> callback);
|
||||
|
||||
void AcceptConnection(const std::string& service_id,
|
||||
const std::string& endpoint_id,
|
||||
void AcceptConnection(const std::string &service_id,
|
||||
const std::string &endpoint_id,
|
||||
PayloadListener payload_listener,
|
||||
std::function<void(Status)> callback);
|
||||
|
||||
void StopAllEndpoints(std::function<void(Status)> callback);
|
||||
|
||||
private:
|
||||
private:
|
||||
class Impl;
|
||||
std::unique_ptr<Impl> impl_;
|
||||
};
|
||||
} // namespace sharing
|
||||
} // namespace nearby
|
||||
|
||||
} // namespace nearby::sharing::linux
|
||||
|
||||
#endif // SHARING_LINUX_NEARBY_CONNECTIONS_QT_FACADE_H_
|
||||
#endif // SHARING_LINUX_NEARBY_CONNECTIONS_QT_FACADE_H_
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
#include <stdint.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdlib>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
@@ -127,6 +128,17 @@ NcStrategy ConvertStrategy(Strategy strategy) {
|
||||
return NcStrategy::kP2pPointToPoint;
|
||||
}
|
||||
|
||||
void SetUpgradeMediumEnv(const nearby::sharing::MediumSelection& mediums) {
|
||||
setenv("NEARBY_ALLOW_UPGRADE_BLUETOOTH", mediums.bluetooth ? "1" : "0", 1);
|
||||
setenv("NEARBY_ALLOW_UPGRADE_BLE", mediums.ble ? "1" : "0", 1);
|
||||
setenv("NEARBY_ALLOW_UPGRADE_WEB_RTC", mediums.web_rtc ? "1" : "0", 1);
|
||||
setenv("NEARBY_ALLOW_UPGRADE_WIFI_LAN", mediums.wifi_lan ? "1" : "0", 1);
|
||||
setenv("NEARBY_ALLOW_UPGRADE_WIFI_HOTSPOT",
|
||||
mediums.wifi_hotspot ? "1" : "0", 1);
|
||||
setenv("NEARBY_ALLOW_UPGRADE_WIFI_DIRECT", "0", 1);
|
||||
setenv("NEARBY_ALLOW_UPGRADE_AWDL", "0", 1);
|
||||
}
|
||||
|
||||
ConnectionInfo ConvertConnectionInfo(const ConnectionResponseInfo& info) {
|
||||
ConnectionInfo connection_info;
|
||||
connection_info.authentication_token = info.authentication_token;
|
||||
@@ -158,9 +170,11 @@ void NearbyConnectionsServiceLinux::StartAdvertising(
|
||||
ConnectionListener advertising_listener,
|
||||
std::function<void(Status status)> callback) {
|
||||
advertising_listener_ = std::move(advertising_listener);
|
||||
SetUpgradeMediumEnv(advertising_options.allowed_mediums);
|
||||
|
||||
NcAdvertisingOptions options{};
|
||||
options.strategy = ConvertStrategy(advertising_options.strategy);
|
||||
options.allowed.SetAll(false);
|
||||
options.allowed.ble = advertising_options.allowed_mediums.ble;
|
||||
options.allowed.bluetooth = advertising_options.allowed_mediums.bluetooth;
|
||||
options.allowed.web_rtc = advertising_options.allowed_mediums.web_rtc;
|
||||
@@ -217,6 +231,7 @@ void NearbyConnectionsServiceLinux::StartDiscovery(
|
||||
DiscoveryListener discovery_listener,
|
||||
std::function<void(Status status)> callback) {
|
||||
discovery_listener_ = std::move(discovery_listener);
|
||||
SetUpgradeMediumEnv(discovery_options.allowed_mediums);
|
||||
|
||||
NcDiscoveryOptions options{};
|
||||
options.strategy = ConvertStrategy(discovery_options.strategy);
|
||||
@@ -274,8 +289,10 @@ void NearbyConnectionsServiceLinux::RequestConnection(
|
||||
std::function<void(Status status)> callback) {
|
||||
static_cast<void>(service_id);
|
||||
connection_listener_ = std::move(connection_listener);
|
||||
SetUpgradeMediumEnv(connection_options.allowed_mediums);
|
||||
|
||||
NcConnectionOptions options{};
|
||||
options.allowed.SetAll(false);
|
||||
options.allowed.ble = connection_options.allowed_mediums.ble;
|
||||
options.allowed.bluetooth = connection_options.allowed_mediums.bluetooth;
|
||||
options.allowed.web_rtc = connection_options.allowed_mediums.web_rtc;
|
||||
|
||||
@@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.21)
|
||||
|
||||
project(nearby_qml_tray_app LANGUAGES CXX)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
set(CMAKE_CXX_STANDARD 20)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
set(CMAKE_AUTOMOC ON)
|
||||
set(CMAKE_AUTORCC ON)
|
||||
@@ -11,100 +11,43 @@ set(CMAKE_AUTOUIC OFF)
|
||||
find_package(Qt6 REQUIRED COMPONENTS Core Gui Widgets Qml Quick QuickControls2)
|
||||
include(GNUInstallDirs)
|
||||
|
||||
set(REPO_ROOT "${CMAKE_CURRENT_LIST_DIR}/../../..")
|
||||
get_filename_component(REPO_ROOT "${REPO_ROOT}" ABSOLUTE)
|
||||
set(NEARBY_INCLUDE_ROOT "${CMAKE_CURRENT_BINARY_DIR}/nearby_include_root")
|
||||
file(MAKE_DIRECTORY "${NEARBY_INCLUDE_ROOT}")
|
||||
set(NEARBY_INSTALL_PREFIX "/usr/local" CACHE PATH "Prefix where Nearby facade header and shared library are installed")
|
||||
set(NEARBY_INCLUDE_ROOT "${NEARBY_INSTALL_PREFIX}/include" CACHE PATH "Nearby include root")
|
||||
set(NEARBY_LIBRARY_DIR "${NEARBY_INSTALL_PREFIX}/lib" CACHE PATH "Nearby shared library directory")
|
||||
|
||||
# Avoid exposing the full repo root as an include directory (which can make IDEs
|
||||
# index unrelated trees like the Bazel 'external' symlink). We create a narrow
|
||||
# include root that only links directories needed by headers used in this app.
|
||||
set(NEARBY_INCLUDE_DIRS
|
||||
sharing
|
||||
)
|
||||
foreach(dir_name IN LISTS NEARBY_INCLUDE_DIRS)
|
||||
if(EXISTS "${REPO_ROOT}/${dir_name}")
|
||||
execute_process(
|
||||
COMMAND "${CMAKE_COMMAND}" -E create_symlink
|
||||
"${REPO_ROOT}/${dir_name}"
|
||||
"${NEARBY_INCLUDE_ROOT}/${dir_name}"
|
||||
RESULT_VARIABLE LINK_RESULT
|
||||
ERROR_VARIABLE LINK_ERROR
|
||||
)
|
||||
if(NOT LINK_RESULT EQUAL 0)
|
||||
message(FATAL_ERROR "Failed to create include symlink for ${dir_name}: ${LINK_ERROR}")
|
||||
endif()
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
set(BAZEL_EXECUTABLE "bazel" CACHE STRING "Path to bazel executable")
|
||||
set(
|
||||
BAZEL_NEARBY_TARGET
|
||||
"//sharing/linux:nearby_connections_service_linux_shared"
|
||||
CACHE STRING
|
||||
"Bazel target that produces a shared library for nearby_connections_service_linux"
|
||||
)
|
||||
set(BAZEL_BUILD_OPTIONS
|
||||
-s
|
||||
--check_visibility=false
|
||||
--spawn_strategy=standalone
|
||||
--verbose_failures
|
||||
--cxxopt=-std=c++20
|
||||
--host_cxxopt=-std=c++20
|
||||
)
|
||||
string(JOIN " " BAZEL_BUILD_OPTIONS_STRING ${BAZEL_BUILD_OPTIONS})
|
||||
|
||||
execute_process(
|
||||
COMMAND "${BAZEL_EXECUTABLE}" info bazel-bin
|
||||
WORKING_DIRECTORY "${REPO_ROOT}"
|
||||
RESULT_VARIABLE BAZEL_INFO_RESULT
|
||||
OUTPUT_VARIABLE BAZEL_BIN_DIR
|
||||
ERROR_VARIABLE BAZEL_INFO_ERROR
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
find_path(
|
||||
NEARBY_FACADE_INCLUDE_ROOT
|
||||
NAMES sharing/linux/nearby_connections_qt_facade.h
|
||||
HINTS "${NEARBY_INCLUDE_ROOT}"
|
||||
)
|
||||
|
||||
if(NOT BAZEL_INFO_RESULT EQUAL 0)
|
||||
message(FATAL_ERROR "Failed to query bazel-bin with '${BAZEL_EXECUTABLE} info bazel-bin': ${BAZEL_INFO_ERROR}")
|
||||
find_library(
|
||||
NEARBY_CONNECTIONS_SHARED_LIB
|
||||
NAMES nearby_connections_service_linux_shared
|
||||
HINTS "${NEARBY_LIBRARY_DIR}"
|
||||
)
|
||||
|
||||
if(NOT NEARBY_FACADE_INCLUDE_ROOT)
|
||||
message(FATAL_ERROR
|
||||
"Could not find sharing/linux/nearby_connections_qt_facade.h. "
|
||||
"Install it first (for example with sharing/linux/install_nearby_connections_service.sh) "
|
||||
"or set -DNEARBY_INCLUDE_ROOT=<prefix>/include.")
|
||||
endif()
|
||||
|
||||
set(BAZEL_NEARBY_SO "${BAZEL_BIN_DIR}/sharing/linux/libnearby_connections_service_linux_shared.so")
|
||||
set(
|
||||
BAZEL_BUILD_IF_MISSING_SCRIPT
|
||||
"${CMAKE_CURRENT_LIST_DIR}/cmake/BuildNearbySoIfMissing.cmake"
|
||||
)
|
||||
set(BAZEL_REBUILD_INPUTS
|
||||
"${REPO_ROOT}/sharing/linux/BUILD"
|
||||
"${REPO_ROOT}/sharing/linux/nearby_connections_qt_facade.h"
|
||||
"${REPO_ROOT}/sharing/linux/nearby_connections_qt_facade.cc"
|
||||
"${REPO_ROOT}/sharing/linux/nearby_connections_service_linux.h"
|
||||
"${REPO_ROOT}/sharing/linux/nearby_connections_service_linux.cc"
|
||||
"${REPO_ROOT}/internal/platform/implementation/linux/crypto.cc"
|
||||
"${REPO_ROOT}/internal/platform/uuid.cc"
|
||||
)
|
||||
if(NOT NEARBY_CONNECTIONS_SHARED_LIB)
|
||||
message(FATAL_ERROR
|
||||
"Could not find libnearby_connections_service_linux_shared.so. "
|
||||
"Install it first (for example with sharing/linux/install_nearby_connections_service.sh) "
|
||||
"or set -DNEARBY_LIBRARY_DIR=<prefix>/lib.")
|
||||
endif()
|
||||
|
||||
add_custom_target(
|
||||
bazel_nearby_connections_service_linux
|
||||
COMMAND "${CMAKE_COMMAND}"
|
||||
-DOUTPUT_SO=${BAZEL_NEARBY_SO}
|
||||
-DBAZEL_EXECUTABLE=${BAZEL_EXECUTABLE}
|
||||
-DBAZEL_TARGET=${BAZEL_NEARBY_TARGET}
|
||||
-DBAZEL_BUILD_OPTIONS=${BAZEL_BUILD_OPTIONS_STRING}
|
||||
-DREPO_ROOT=${REPO_ROOT}
|
||||
-DREBUILD_INPUTS=${BAZEL_REBUILD_INPUTS}
|
||||
-P "${BAZEL_BUILD_IF_MISSING_SCRIPT}"
|
||||
BYPRODUCTS "${BAZEL_NEARBY_SO}"
|
||||
COMMENT "Checking/building ${BAZEL_NEARBY_TARGET} with Bazel"
|
||||
VERBATIM
|
||||
)
|
||||
|
||||
add_library(nearby_connections_service_linux_bazel SHARED IMPORTED GLOBAL)
|
||||
add_library(nearby_connections_service_linux_installed SHARED IMPORTED GLOBAL)
|
||||
set_target_properties(
|
||||
nearby_connections_service_linux_bazel
|
||||
nearby_connections_service_linux_installed
|
||||
PROPERTIES
|
||||
IMPORTED_LOCATION "${BAZEL_NEARBY_SO}"
|
||||
INTERFACE_INCLUDE_DIRECTORIES "${NEARBY_INCLUDE_ROOT}"
|
||||
IMPORTED_LOCATION "${NEARBY_CONNECTIONS_SHARED_LIB}"
|
||||
INTERFACE_INCLUDE_DIRECTORIES "${NEARBY_FACADE_INCLUDE_ROOT}"
|
||||
)
|
||||
add_dependencies(nearby_connections_service_linux_bazel bazel_nearby_connections_service_linux)
|
||||
|
||||
qt_add_executable(nearby_qml_tray_app
|
||||
main.cpp
|
||||
@@ -113,8 +56,16 @@ qt_add_executable(nearby_qml_tray_app
|
||||
resources.qrc
|
||||
)
|
||||
|
||||
qt_add_executable(nearby_qml_file_tray_app
|
||||
file_share_tray_main.cpp
|
||||
file_share_tray_controller.cc
|
||||
file_share_tray_controller.h
|
||||
resources_file_share.qrc
|
||||
)
|
||||
|
||||
target_include_directories(nearby_qml_tray_app PRIVATE
|
||||
"${NEARBY_INCLUDE_ROOT}"
|
||||
"${CMAKE_CURRENT_LIST_DIR}"
|
||||
"${NEARBY_FACADE_INCLUDE_ROOT}"
|
||||
)
|
||||
target_link_libraries(nearby_qml_tray_app PRIVATE
|
||||
Qt6::Core
|
||||
@@ -123,20 +74,42 @@ target_link_libraries(nearby_qml_tray_app PRIVATE
|
||||
Qt6::Qml
|
||||
Qt6::Quick
|
||||
Qt6::QuickControls2
|
||||
nearby_connections_service_linux_bazel
|
||||
nearby_connections_service_linux_installed
|
||||
)
|
||||
add_dependencies(nearby_qml_tray_app bazel_nearby_connections_service_linux)
|
||||
|
||||
target_include_directories(nearby_qml_file_tray_app PRIVATE
|
||||
"${CMAKE_CURRENT_LIST_DIR}"
|
||||
"${NEARBY_FACADE_INCLUDE_ROOT}"
|
||||
)
|
||||
target_link_libraries(nearby_qml_file_tray_app PRIVATE
|
||||
Qt6::Core
|
||||
Qt6::Gui
|
||||
Qt6::Widgets
|
||||
Qt6::Qml
|
||||
Qt6::Quick
|
||||
Qt6::QuickControls2
|
||||
nearby_connections_service_linux_installed
|
||||
)
|
||||
|
||||
get_filename_component(NEARBY_CONNECTIONS_SHARED_LIB_DIR "${NEARBY_CONNECTIONS_SHARED_LIB}" DIRECTORY)
|
||||
|
||||
set_target_properties(nearby_qml_tray_app PROPERTIES
|
||||
BUILD_RPATH "${BAZEL_BIN_DIR}/sharing/linux"
|
||||
BUILD_RPATH "${NEARBY_CONNECTIONS_SHARED_LIB_DIR}"
|
||||
INSTALL_RPATH "$ORIGIN"
|
||||
)
|
||||
set_target_properties(nearby_qml_file_tray_app PROPERTIES
|
||||
BUILD_RPATH "${NEARBY_CONNECTIONS_SHARED_LIB_DIR}"
|
||||
INSTALL_RPATH "$ORIGIN"
|
||||
)
|
||||
|
||||
install(TARGETS nearby_qml_tray_app
|
||||
RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}"
|
||||
)
|
||||
install(TARGETS nearby_qml_file_tray_app
|
||||
RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}"
|
||||
)
|
||||
|
||||
install(FILES "${BAZEL_NEARBY_SO}"
|
||||
install(FILES "${NEARBY_CONNECTIONS_SHARED_LIB}"
|
||||
DESTINATION "${CMAKE_INSTALL_BINDIR}"
|
||||
)
|
||||
|
||||
@@ -146,6 +119,17 @@ qt_generate_deploy_qml_app_script(
|
||||
)
|
||||
install(SCRIPT "${nearby_qml_tray_app_deploy_script}")
|
||||
|
||||
qt_generate_deploy_qml_app_script(
|
||||
TARGET nearby_qml_file_tray_app
|
||||
OUTPUT_SCRIPT nearby_qml_file_tray_app_deploy_script
|
||||
)
|
||||
install(SCRIPT "${nearby_qml_file_tray_app_deploy_script}")
|
||||
|
||||
# Convenience build target for the file tray app.
|
||||
add_custom_target(build_file_tray_app
|
||||
DEPENDS nearby_qml_file_tray_app
|
||||
)
|
||||
|
||||
set(CPACK_GENERATOR "ZIP")
|
||||
set(CPACK_PACKAGE_NAME "nearby_qml_tray_app")
|
||||
set(CPACK_PACKAGE_FILE_NAME "${CPACK_PACKAGE_NAME}-${CMAKE_SYSTEM_NAME}-${CMAKE_SYSTEM_PROCESSOR}")
|
||||
|
||||
@@ -0,0 +1,441 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
|
||||
ApplicationWindow {
|
||||
id: root
|
||||
width: 980
|
||||
height: 760
|
||||
minimumWidth: 820
|
||||
minimumHeight: 620
|
||||
visible: true
|
||||
title: "Nearby File Tray"
|
||||
|
||||
readonly property color appBg: "#f5f6f8"
|
||||
readonly property color surface: "#ffffff"
|
||||
readonly property color border: "#d7dbe0"
|
||||
readonly property color textPrimary: "#1f2328"
|
||||
readonly property color textMuted: "#59636e"
|
||||
|
||||
palette.window: appBg
|
||||
palette.base: surface
|
||||
palette.button: "#f0f3f7"
|
||||
palette.text: textPrimary
|
||||
palette.windowText: textPrimary
|
||||
palette.buttonText: textPrimary
|
||||
palette.placeholderText: textMuted
|
||||
palette.highlight: "#2f6feb"
|
||||
palette.highlightedText: "#ffffff"
|
||||
|
||||
background: Rectangle {
|
||||
color: root.appBg
|
||||
}
|
||||
|
||||
function endpointLabel(endpointId) {
|
||||
var label = fileShareController.peerNameForEndpoint(endpointId)
|
||||
if (!label || label.length === 0 || label === "Unknown device") {
|
||||
return "Unknown device"
|
||||
}
|
||||
return label
|
||||
}
|
||||
|
||||
function formatBytes(bytes) {
|
||||
if (bytes === undefined || bytes === null) {
|
||||
return "0 B"
|
||||
}
|
||||
var value = Number(bytes)
|
||||
if (!isFinite(value) || value < 0) {
|
||||
return "0 B"
|
||||
}
|
||||
var units = ["B", "KB", "MB", "GB", "TB"]
|
||||
var unit = 0
|
||||
while (value >= 1024 && unit < units.length - 1) {
|
||||
value /= 1024
|
||||
unit += 1
|
||||
}
|
||||
return (value >= 10 || unit === 0 ? value.toFixed(0) : value.toFixed(1)) + " " + units[unit]
|
||||
}
|
||||
|
||||
onClosing: function(close) {
|
||||
close.accepted = false
|
||||
root.hide()
|
||||
fileShareController.hideToTray()
|
||||
}
|
||||
|
||||
header: ToolBar {
|
||||
height: 52
|
||||
|
||||
RowLayout {
|
||||
anchors.fill: parent
|
||||
anchors.leftMargin: 12
|
||||
anchors.rightMargin: 12
|
||||
spacing: 10
|
||||
|
||||
Label {
|
||||
text: "Nearby File Share"
|
||||
font.bold: true
|
||||
font.pixelSize: 18
|
||||
color: root.textPrimary
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
radius: 12
|
||||
color: fileShareController.mode === "Send" ? "#dbeafe" : "#dcfce7"
|
||||
border.color: fileShareController.mode === "Send" ? "#60a5fa" : "#4ade80"
|
||||
Layout.preferredHeight: 24
|
||||
Layout.preferredWidth: 86
|
||||
|
||||
Label {
|
||||
anchors.centerIn: parent
|
||||
text: fileShareController.mode
|
||||
font.bold: true
|
||||
color: "#1f2328"
|
||||
}
|
||||
}
|
||||
|
||||
Item { Layout.fillWidth: true }
|
||||
|
||||
Label {
|
||||
text: fileShareController.running ? "Running" : "Stopped"
|
||||
color: fileShareController.running ? "#1f7a1f" : "#a33"
|
||||
font.bold: true
|
||||
}
|
||||
|
||||
Button {
|
||||
text: fileShareController.running ? "Stop" : "Start"
|
||||
onClicked: {
|
||||
if (fileShareController.running) {
|
||||
fileShareController.stop()
|
||||
} else {
|
||||
fileShareController.start()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
anchors.fill: parent
|
||||
anchors.margins: 12
|
||||
spacing: 10
|
||||
|
||||
Frame {
|
||||
Layout.fillWidth: true
|
||||
padding: 10
|
||||
background: Rectangle {
|
||||
color: root.surface
|
||||
border.color: root.border
|
||||
radius: 6
|
||||
}
|
||||
|
||||
GridLayout {
|
||||
anchors.fill: parent
|
||||
columns: 4
|
||||
columnSpacing: 10
|
||||
rowSpacing: 8
|
||||
|
||||
Label { text: "Device name" }
|
||||
TextField {
|
||||
Layout.fillWidth: true
|
||||
text: fileShareController.deviceName
|
||||
onEditingFinished: fileShareController.deviceName = text
|
||||
}
|
||||
|
||||
Label { text: "Selected file" }
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
text: fileShareController.pendingSendFileName.length > 0
|
||||
? fileShareController.pendingSendFileName
|
||||
: "None"
|
||||
elide: Text.ElideRight
|
||||
color: root.textMuted
|
||||
}
|
||||
|
||||
Label { text: "Status" }
|
||||
Label {
|
||||
Layout.columnSpan: 3
|
||||
Layout.fillWidth: true
|
||||
text: fileShareController.statusMessage
|
||||
elide: Text.ElideRight
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
spacing: 10
|
||||
|
||||
Frame {
|
||||
Layout.preferredWidth: 420
|
||||
Layout.fillHeight: true
|
||||
padding: 10
|
||||
background: Rectangle {
|
||||
color: root.surface
|
||||
border.color: root.border
|
||||
radius: 6
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
anchors.fill: parent
|
||||
spacing: 8
|
||||
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
|
||||
Label {
|
||||
text: "Nearby Devices"
|
||||
font.bold: true
|
||||
}
|
||||
|
||||
Item { Layout.fillWidth: true }
|
||||
|
||||
Rectangle {
|
||||
radius: 10
|
||||
color: fileShareController.mode === "Send" ? "#dbeafe" : "#f1f5f9"
|
||||
border.color: fileShareController.mode === "Send" ? "#60a5fa" : "#cbd5e1"
|
||||
Layout.preferredHeight: 22
|
||||
Layout.preferredWidth: 74
|
||||
|
||||
Label {
|
||||
anchors.centerIn: parent
|
||||
text: fileShareController.mode === "Send" ? "Discovering" : "Paused"
|
||||
font.pixelSize: 11
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
text: fileShareController.mode === "Send"
|
||||
? "Select a nearby device to send your selected file."
|
||||
: "Use tray menu Send to choose a file and start discovery."
|
||||
wrapMode: Text.WordWrap
|
||||
color: root.textMuted
|
||||
}
|
||||
|
||||
ListView {
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
clip: true
|
||||
spacing: 6
|
||||
model: fileShareController.discoveredDevices
|
||||
|
||||
delegate: Rectangle {
|
||||
required property string modelData
|
||||
width: ListView.view.width
|
||||
height: 70
|
||||
color: root.surface
|
||||
border.color: root.border
|
||||
radius: 6
|
||||
|
||||
RowLayout {
|
||||
anchors.fill: parent
|
||||
anchors.leftMargin: 10
|
||||
anchors.rightMargin: 10
|
||||
spacing: 8
|
||||
|
||||
ColumnLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: 2
|
||||
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
text: root.endpointLabel(modelData)
|
||||
font.bold: true
|
||||
elide: Text.ElideRight
|
||||
}
|
||||
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
text: modelData
|
||||
color: root.textMuted
|
||||
font.pixelSize: 11
|
||||
elide: Text.ElideRight
|
||||
}
|
||||
}
|
||||
|
||||
Button {
|
||||
text: "Send"
|
||||
enabled: fileShareController.mode === "Send"
|
||||
&& fileShareController.pendingSendFilePath.length > 0
|
||||
onClicked: fileShareController.sendPendingFileToEndpoint(modelData)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
spacing: 10
|
||||
|
||||
Frame {
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: 220
|
||||
padding: 10
|
||||
background: Rectangle {
|
||||
color: root.surface
|
||||
border.color: root.border
|
||||
radius: 6
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
anchors.fill: parent
|
||||
spacing: 6
|
||||
|
||||
Label {
|
||||
text: "Connected Devices"
|
||||
font.bold: true
|
||||
}
|
||||
|
||||
ListView {
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
clip: true
|
||||
spacing: 6
|
||||
model: fileShareController.connectedDevices
|
||||
|
||||
delegate: Rectangle {
|
||||
required property string modelData
|
||||
width: ListView.view.width
|
||||
height: 46
|
||||
color: root.surface
|
||||
border.color: root.border
|
||||
radius: 6
|
||||
|
||||
RowLayout {
|
||||
anchors.fill: parent
|
||||
anchors.leftMargin: 10
|
||||
anchors.rightMargin: 10
|
||||
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
text: root.endpointLabel(modelData)
|
||||
font.bold: true
|
||||
elide: Text.ElideRight
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
radius: 10
|
||||
color: "#e2e8f0"
|
||||
border.color: "#cbd5e1"
|
||||
Layout.preferredHeight: 22
|
||||
Layout.preferredWidth: Math.max(68, mediumLabel.implicitWidth + 16)
|
||||
|
||||
Label {
|
||||
id: mediumLabel
|
||||
anchors.centerIn: parent
|
||||
text: fileShareController.mediumForEndpoint(modelData)
|
||||
font.pixelSize: 11
|
||||
color: "#334155"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Frame {
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
padding: 10
|
||||
background: Rectangle {
|
||||
color: root.surface
|
||||
border.color: root.border
|
||||
radius: 6
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
anchors.fill: parent
|
||||
spacing: 6
|
||||
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
Label {
|
||||
text: "Transfers"
|
||||
font.bold: true
|
||||
}
|
||||
Item { Layout.fillWidth: true }
|
||||
Button {
|
||||
text: "Clear"
|
||||
onClicked: fileShareController.clearTransfers()
|
||||
}
|
||||
}
|
||||
|
||||
ListView {
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
clip: true
|
||||
spacing: 6
|
||||
model: fileShareController.transfers
|
||||
|
||||
delegate: Rectangle {
|
||||
required property var modelData
|
||||
width: ListView.view.width
|
||||
height: 86
|
||||
color: root.surface
|
||||
border.color: root.border
|
||||
radius: 6
|
||||
|
||||
ColumnLayout {
|
||||
anchors.fill: parent
|
||||
anchors.leftMargin: 10
|
||||
anchors.rightMargin: 10
|
||||
spacing: 4
|
||||
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
text: modelData.direction + " | " + root.endpointLabel(modelData.endpointId)
|
||||
font.bold: true
|
||||
elide: Text.ElideRight
|
||||
}
|
||||
|
||||
Label { text: modelData.status }
|
||||
|
||||
Rectangle {
|
||||
radius: 10
|
||||
color: "#e2e8f0"
|
||||
border.color: "#cbd5e1"
|
||||
Layout.preferredHeight: 22
|
||||
Layout.preferredWidth: Math.max(68, transferMediumLabel.implicitWidth + 16)
|
||||
|
||||
Label {
|
||||
id: transferMediumLabel
|
||||
anchors.centerIn: parent
|
||||
text: modelData.medium
|
||||
font.pixelSize: 11
|
||||
color: "#334155"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ProgressBar {
|
||||
Layout.fillWidth: true
|
||||
from: 0
|
||||
to: 1
|
||||
value: modelData.progress
|
||||
}
|
||||
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
horizontalAlignment: Text.AlignRight
|
||||
text: root.formatBytes(modelData.bytesTransferred)
|
||||
+ " / " + root.formatBytes(modelData.totalBytes)
|
||||
color: root.textMuted
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -243,7 +243,14 @@ ApplicationWindow {
|
||||
id: strategyCombo
|
||||
Layout.preferredWidth: 170
|
||||
model: ["P2pCluster", "P2pStar", "P2pPointToPoint"]
|
||||
currentIndex: Math.max(0, find(nearbyController.connectionStrategy))
|
||||
currentIndex: {
|
||||
var strategy = String(nearbyController.connectionStrategy)
|
||||
if (strategy === "P2pStar")
|
||||
return 1
|
||||
if (strategy === "P2pPointToPoint")
|
||||
return 2
|
||||
return 0
|
||||
}
|
||||
onActivated: nearbyController.connectionStrategy = currentText
|
||||
}
|
||||
|
||||
|
||||
@@ -32,45 +32,24 @@ This folder contains a Qt/QML tray application backend and UI wired to:
|
||||
- Transfers are shown with endpoint, direction, status, progress, and medium.
|
||||
- Logs are appended to `/tmp/nearby_qml_tray.log`.
|
||||
|
||||
## Building (host stays clean)
|
||||
## Building
|
||||
|
||||
This app now has a Bazel target:
|
||||
This CMake app links against the installed Nearby shared library and header:
|
||||
|
||||
- `//sharing/linux/qml_tray_app:nearby_qml_tray_app`
|
||||
- `libnearby_connections_service_linux_shared.so`
|
||||
- `sharing/linux/nearby_connections_qt_facade.h`
|
||||
|
||||
Qt compiler and linker flags are resolved through `pkg-config` at build time.
|
||||
|
||||
### Recommended: build in container
|
||||
|
||||
From repo root:
|
||||
Install them first (repo root):
|
||||
|
||||
```bash
|
||||
./sharing/linux/qml_tray_app/build_in_container.sh
|
||||
./sharing/linux/install_nearby_connections_service.sh
|
||||
```
|
||||
|
||||
This command:
|
||||
|
||||
- builds an image from `sharing/linux/qml_tray_app/container/Dockerfile`
|
||||
- installs Bazel + Qt 6 dev packages inside that image
|
||||
- runs the Bazel build in the container
|
||||
- keeps Bazel cache in a Docker volume (`nearby_qml_tray_bazel_cache`)
|
||||
|
||||
Useful overrides:
|
||||
Then build the app (from `sharing/linux/qml_tray_app`):
|
||||
|
||||
```bash
|
||||
# Use podman instead of docker.
|
||||
CONTAINER_RUNTIME=podman ./sharing/linux/qml_tray_app/build_in_container.sh
|
||||
|
||||
# Pin a custom image tag.
|
||||
QML_TRAY_IMAGE_TAG=nearby-qml-tray-builder:v1 ./sharing/linux/qml_tray_app/build_in_container.sh
|
||||
```
|
||||
|
||||
### Build in an already-provisioned environment
|
||||
|
||||
If you already have Bazel + Qt 6 dev dependencies installed:
|
||||
|
||||
```bash
|
||||
./sharing/linux/qml_tray_app/build.sh
|
||||
cmake -S . -B build -DCMAKE_BUILD_TYPE=Release -DNEARBY_INSTALL_PREFIX=/usr/local
|
||||
cmake --build build -j
|
||||
```
|
||||
|
||||
## Bundle `libnearby_connections_service_linux_shared.so` with the app
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,160 @@
|
||||
#ifndef SHARING_LINUX_QML_TRAY_APP_FILE_SHARE_TRAY_CONTROLLER_H_
|
||||
#define SHARING_LINUX_QML_TRAY_APP_FILE_SHARE_TRAY_CONTROLLER_H_
|
||||
|
||||
#include <QObject>
|
||||
|
||||
#include <QFile>
|
||||
#include <QHash>
|
||||
#include <QSet>
|
||||
#include <QString>
|
||||
#include <QStringList>
|
||||
#include <QVariantList>
|
||||
#include <QVariantMap>
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "sharing/linux/nearby_connections_qt_facade.h"
|
||||
|
||||
using NearbyConnectionsQtFacade = nearby::sharing::NearbyConnectionsQtFacade;
|
||||
|
||||
class FileShareTrayController : public QObject {
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(QString mode READ mode NOTIFY modeChanged)
|
||||
Q_PROPERTY(QString deviceName READ deviceName WRITE setDeviceName NOTIFY deviceNameChanged)
|
||||
Q_PROPERTY(QString statusMessage READ statusMessage NOTIFY statusMessageChanged)
|
||||
Q_PROPERTY(bool running READ running NOTIFY runningChanged)
|
||||
Q_PROPERTY(QString pendingSendFileName READ pendingSendFileName NOTIFY pendingSendFileNameChanged)
|
||||
Q_PROPERTY(QString pendingSendFilePath READ pendingSendFilePath NOTIFY pendingSendFilePathChanged)
|
||||
Q_PROPERTY(QStringList discoveredDevices READ discoveredDevices NOTIFY discoveredDevicesChanged)
|
||||
Q_PROPERTY(QStringList connectedDevices READ connectedDevices NOTIFY connectedDevicesChanged)
|
||||
Q_PROPERTY(QVariantMap endpointMediums READ endpointMediums NOTIFY endpointMediumsChanged)
|
||||
Q_PROPERTY(QVariantList transfers READ transfers NOTIFY transfersChanged)
|
||||
|
||||
public:
|
||||
explicit FileShareTrayController(QObject* parent = nullptr);
|
||||
~FileShareTrayController() override;
|
||||
|
||||
QString mode() const { return mode_; }
|
||||
|
||||
QString deviceName() const { return device_name_; }
|
||||
void setDeviceName(const QString& device_name);
|
||||
|
||||
QString statusMessage() const { return status_message_; }
|
||||
bool running() const { return running_; }
|
||||
|
||||
QString pendingSendFileName() const { return pending_send_file_name_; }
|
||||
QString pendingSendFilePath() const { return pending_send_file_path_; }
|
||||
|
||||
QStringList discoveredDevices() const { return discovered_devices_; }
|
||||
QStringList connectedDevices() const { return connected_devices_; }
|
||||
QVariantMap endpointMediums() const { return endpoint_mediums_; }
|
||||
QVariantList transfers() const { return transfers_; }
|
||||
|
||||
Q_INVOKABLE void start();
|
||||
Q_INVOKABLE void stop();
|
||||
Q_INVOKABLE void switchToReceiveMode();
|
||||
Q_INVOKABLE void switchToSendModeWithFile(const QString& file_path);
|
||||
Q_INVOKABLE void sendPendingFileToEndpoint(const QString& endpoint_id);
|
||||
Q_INVOKABLE QString mediumForEndpoint(const QString& endpoint_id) const;
|
||||
Q_INVOKABLE QString peerNameForEndpoint(const QString& endpoint_id) const;
|
||||
Q_INVOKABLE void clearTransfers();
|
||||
Q_INVOKABLE void hideToTray();
|
||||
|
||||
signals:
|
||||
void modeChanged();
|
||||
void deviceNameChanged();
|
||||
void statusMessageChanged();
|
||||
void runningChanged();
|
||||
void pendingSendFileNameChanged();
|
||||
void pendingSendFilePathChanged();
|
||||
void discoveredDevicesChanged();
|
||||
void connectedDevicesChanged();
|
||||
void endpointMediumsChanged();
|
||||
void transfersChanged();
|
||||
|
||||
void requestTrayMessage(const QString& title, const QString& body);
|
||||
|
||||
private:
|
||||
void startSendMode();
|
||||
void startReceiveMode();
|
||||
void LoadSettings();
|
||||
void SaveSettings() const;
|
||||
|
||||
std::vector<uint8_t> BuildEndpointInfo() const;
|
||||
|
||||
NearbyConnectionsQtFacade::ConnectionListener BuildConnectionListener();
|
||||
NearbyConnectionsQtFacade::DiscoveryListener BuildDiscoveryListener();
|
||||
NearbyConnectionsQtFacade::PayloadListener BuildPayloadListener();
|
||||
|
||||
NearbyConnectionsQtFacade::AdvertisingOptions BuildAdvertisingOptions() const;
|
||||
NearbyConnectionsQtFacade::DiscoveryOptions BuildDiscoveryOptions() const;
|
||||
NearbyConnectionsQtFacade::ConnectionOptions BuildConnectionOptions() const;
|
||||
NearbyConnectionsQtFacade::MediumSelection BuildMediumSelection() const;
|
||||
|
||||
void acceptIncomingInternal(const QString& endpoint_id);
|
||||
void requestConnectionForSend(const QString& endpoint_id);
|
||||
void sendPendingFile(const QString& endpoint_id);
|
||||
void disconnectDevice(const QString& endpoint_id);
|
||||
|
||||
void AddDiscoveredDevice(const QString& endpoint_id);
|
||||
void RemoveDiscoveredDevice(const QString& endpoint_id);
|
||||
void AddConnectedDevice(const QString& endpoint_id);
|
||||
void RemoveConnectedDevice(const QString& endpoint_id);
|
||||
void SetPeerNameForEndpoint(const QString& endpoint_id,
|
||||
const QString& peer_name);
|
||||
QString PeerLabelForEndpoint(const QString& endpoint_id) const;
|
||||
|
||||
QString FinalizeReceivedFilePath(const QString& received_path,
|
||||
const QString& received_file_name,
|
||||
qlonglong payload_id) const;
|
||||
|
||||
void UpsertTransfer(const QString& endpoint_id, qlonglong payload_id,
|
||||
const QString& status, qulonglong bytes_transferred,
|
||||
qulonglong total_bytes, const QString& direction);
|
||||
void UpdateTransferMediumForEndpoint(const QString& endpoint_id,
|
||||
const QString& medium);
|
||||
|
||||
void SetStatus(const QString& status);
|
||||
bool HasActiveTransfers() const;
|
||||
void LogLine(const QString& line);
|
||||
void ReopenLogFile();
|
||||
|
||||
static QString StatusToString(NearbyConnectionsQtFacade::Status status);
|
||||
static QString PayloadStatusToString(
|
||||
NearbyConnectionsQtFacade::PayloadStatus status);
|
||||
static QString MediumToString(NearbyConnectionsQtFacade::Medium medium);
|
||||
|
||||
NearbyConnectionsQtFacade service_;
|
||||
|
||||
QString mode_ = QStringLiteral("Receive");
|
||||
QString device_name_ = QStringLiteral("NearbyQtFile");
|
||||
const std::string service_id_ = "com.nearby.qml.tray";
|
||||
QString status_message_ = QStringLiteral("Idle");
|
||||
bool running_ = false;
|
||||
|
||||
QString pending_send_file_path_;
|
||||
QString pending_send_file_name_;
|
||||
QString target_endpoint_for_send_;
|
||||
|
||||
QStringList discovered_devices_;
|
||||
QStringList connected_devices_;
|
||||
QHash<QString, QString> endpoint_peer_names_;
|
||||
QVariantMap endpoint_mediums_;
|
||||
|
||||
QVariantList transfers_;
|
||||
QHash<qlonglong, int> transfer_row_for_payload_;
|
||||
|
||||
QHash<QString, QString> pending_file_names_;
|
||||
QHash<qlonglong, QString> incoming_file_paths_;
|
||||
QHash<qlonglong, QString> incoming_file_names_;
|
||||
QHash<qlonglong, QString> incoming_file_endpoints_;
|
||||
QHash<qlonglong, QString> outgoing_file_payload_to_endpoint_;
|
||||
QHash<qlonglong, QString> outgoing_file_payload_to_name_;
|
||||
QSet<qlonglong> send_terminal_notified_;
|
||||
|
||||
QString log_path_ = QStringLiteral("/tmp/nearby_qml_file_tray.log");
|
||||
QFile log_file_;
|
||||
};
|
||||
|
||||
#endif // SHARING_LINUX_QML_TRAY_APP_FILE_SHARE_TRAY_CONTROLLER_H_
|
||||
@@ -0,0 +1,169 @@
|
||||
#include <QAction>
|
||||
#include <QApplication>
|
||||
#include <QFileDialog>
|
||||
#include <QIcon>
|
||||
#include <QMenu>
|
||||
#include <QPainter>
|
||||
#include <QPalette>
|
||||
#include <QQmlApplicationEngine>
|
||||
#include <QQmlContext>
|
||||
#include <QQuickWindow>
|
||||
#include <QStyleHints>
|
||||
#include <QSystemTrayIcon>
|
||||
|
||||
#include "file_share_tray_controller.h"
|
||||
|
||||
namespace {
|
||||
|
||||
QIcon BuildTintedSymbolicIcon(const QString& source, const QColor& color) {
|
||||
QIcon source_icon(source);
|
||||
if (source_icon.isNull()) {
|
||||
return QIcon();
|
||||
}
|
||||
|
||||
QIcon tinted_icon;
|
||||
for (int size : {16, 18, 20, 22, 24, 32}) {
|
||||
QPixmap pixmap = source_icon.pixmap(size, size);
|
||||
if (pixmap.isNull()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
QPixmap tinted(pixmap.size());
|
||||
tinted.fill(Qt::transparent);
|
||||
|
||||
QPainter painter(&tinted);
|
||||
painter.drawPixmap(0, 0, pixmap);
|
||||
painter.setCompositionMode(QPainter::CompositionMode_SourceIn);
|
||||
painter.fillRect(tinted.rect(), color);
|
||||
painter.end();
|
||||
|
||||
tinted_icon.addPixmap(tinted);
|
||||
}
|
||||
|
||||
return tinted_icon;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
nearby::sharing::NearbyConnectionsQtFacade::SetBleL2capFlagOverrides(
|
||||
true, false);
|
||||
|
||||
QApplication app(argc, argv);
|
||||
app.setQuitOnLastWindowClosed(false);
|
||||
|
||||
FileShareTrayController controller;
|
||||
|
||||
QQmlApplicationEngine engine;
|
||||
engine.rootContext()->setContextProperty("fileShareController", &controller);
|
||||
engine.load(QUrl(QStringLiteral("qrc:/qml/FileShareTray.qml")));
|
||||
if (engine.rootObjects().isEmpty()) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
auto* window = qobject_cast<QQuickWindow*>(engine.rootObjects().first());
|
||||
if (window == nullptr) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
const auto resolve_tray_icon = [&app]() {
|
||||
const QColor symbolic_color = app.palette().color(QPalette::WindowText);
|
||||
QIcon tray_icon = BuildTintedSymbolicIcon(
|
||||
QStringLiteral(":/icons/tray_icon-symbolic.svg"), symbolic_color);
|
||||
if (tray_icon.isNull()) {
|
||||
tray_icon = QIcon::fromTheme(QStringLiteral("network-wireless-symbolic"));
|
||||
}
|
||||
if (tray_icon.isNull()) {
|
||||
tray_icon = QIcon(QStringLiteral(":/icons/tray_icon.png"));
|
||||
}
|
||||
if (tray_icon.isNull()) {
|
||||
tray_icon = app.windowIcon();
|
||||
}
|
||||
return tray_icon;
|
||||
};
|
||||
|
||||
QSystemTrayIcon tray(resolve_tray_icon());
|
||||
tray.setToolTip(QStringLiteral("Nearby File Tray"));
|
||||
|
||||
QMenu tray_menu;
|
||||
QAction* send_action = tray_menu.addAction(QStringLiteral("Send"));
|
||||
QAction* receive_action = tray_menu.addAction(QStringLiteral("Receive"));
|
||||
tray_menu.addSeparator();
|
||||
QAction* show_action = tray_menu.addAction(QStringLiteral("Show"));
|
||||
QAction* hide_action = tray_menu.addAction(QStringLiteral("Hide"));
|
||||
tray_menu.addSeparator();
|
||||
QAction* quit_action = tray_menu.addAction(QStringLiteral("Quit"));
|
||||
|
||||
QObject::connect(send_action, &QAction::triggered, window,
|
||||
[&controller, window]() {
|
||||
const QString file = QFileDialog::getOpenFileName(
|
||||
nullptr, QStringLiteral("Select file to send"));
|
||||
if (file.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
controller.switchToSendModeWithFile(file);
|
||||
window->show();
|
||||
window->raise();
|
||||
window->requestActivate();
|
||||
});
|
||||
|
||||
QObject::connect(receive_action, &QAction::triggered,
|
||||
[&controller, window]() {
|
||||
controller.switchToReceiveMode();
|
||||
window->show();
|
||||
window->raise();
|
||||
window->requestActivate();
|
||||
});
|
||||
|
||||
QObject::connect(show_action, &QAction::triggered, window, [window]() {
|
||||
window->show();
|
||||
window->raise();
|
||||
window->requestActivate();
|
||||
});
|
||||
|
||||
QObject::connect(hide_action, &QAction::triggered, window, [window]() {
|
||||
window->hide();
|
||||
});
|
||||
|
||||
QObject::connect(quit_action, &QAction::triggered, &app,
|
||||
[&controller, &app]() {
|
||||
controller.stop();
|
||||
app.quit();
|
||||
});
|
||||
|
||||
QObject::connect(&tray, &QSystemTrayIcon::activated, window,
|
||||
[window](QSystemTrayIcon::ActivationReason reason) {
|
||||
if (reason != QSystemTrayIcon::Trigger &&
|
||||
reason != QSystemTrayIcon::DoubleClick) {
|
||||
return;
|
||||
}
|
||||
if (window->isVisible()) {
|
||||
window->hide();
|
||||
} else {
|
||||
window->show();
|
||||
window->raise();
|
||||
window->requestActivate();
|
||||
}
|
||||
});
|
||||
|
||||
QObject::connect(&controller, &FileShareTrayController::requestTrayMessage,
|
||||
&tray, [&tray](const QString& title, const QString& body) {
|
||||
tray.showMessage(title, body, QSystemTrayIcon::Information,
|
||||
4000);
|
||||
});
|
||||
|
||||
QObject::connect(&app, &QCoreApplication::aboutToQuit, &controller,
|
||||
[&controller]() { controller.stop(); });
|
||||
QObject::connect(app.styleHints(), &QStyleHints::colorSchemeChanged, &tray,
|
||||
[&tray, &resolve_tray_icon](Qt::ColorScheme) {
|
||||
tray.setIcon(resolve_tray_icon());
|
||||
});
|
||||
|
||||
tray.setContextMenu(&tray_menu);
|
||||
tray.show();
|
||||
|
||||
controller.switchToReceiveMode();
|
||||
|
||||
return app.exec();
|
||||
}
|
||||
@@ -2,12 +2,47 @@
|
||||
#include <QApplication>
|
||||
#include <QIcon>
|
||||
#include <QMenu>
|
||||
#include <QPainter>
|
||||
#include <QPalette>
|
||||
#include <QQmlApplicationEngine>
|
||||
#include <QQmlContext>
|
||||
#include <QQuickWindow>
|
||||
#include <QStyleHints>
|
||||
#include <QSystemTrayIcon>
|
||||
|
||||
#include "sharing/linux/qml_tray_app/nearby_tray_controller.h"
|
||||
#include "nearby_tray_controller.h"
|
||||
|
||||
namespace {
|
||||
|
||||
QIcon BuildTintedSymbolicIcon(const QString& source, const QColor& color) {
|
||||
QIcon source_icon(source);
|
||||
if (source_icon.isNull()) {
|
||||
return QIcon();
|
||||
}
|
||||
|
||||
QIcon tinted_icon;
|
||||
for (int size : {16, 18, 20, 22, 24, 32}) {
|
||||
QPixmap pixmap = source_icon.pixmap(size, size);
|
||||
if (pixmap.isNull()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
QPixmap tinted(pixmap.size());
|
||||
tinted.fill(Qt::transparent);
|
||||
|
||||
QPainter painter(&tinted);
|
||||
painter.drawPixmap(0, 0, pixmap);
|
||||
painter.setCompositionMode(QPainter::CompositionMode_SourceIn);
|
||||
painter.fillRect(tinted.rect(), color);
|
||||
painter.end();
|
||||
|
||||
tinted_icon.addPixmap(tinted);
|
||||
}
|
||||
|
||||
return tinted_icon;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
QApplication app(argc, argv);
|
||||
@@ -27,23 +62,30 @@ int main(int argc, char* argv[]) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Try custom symbolic icon first (will auto-theme if available)
|
||||
QIcon tray_icon = QIcon(QStringLiteral(":/icons/tray_icon-symbolic.svg"));
|
||||
|
||||
// Fallback to system themed icon
|
||||
if (tray_icon.isNull()) {
|
||||
tray_icon = QIcon::fromTheme(QStringLiteral("network-wireless-symbolic"));
|
||||
}
|
||||
|
||||
// Fallback to custom PNG icon
|
||||
if (tray_icon.isNull()) {
|
||||
tray_icon = QIcon(QStringLiteral(":/icons/tray_icon.png"));
|
||||
}
|
||||
|
||||
// Final fallback
|
||||
if (tray_icon.isNull()) {
|
||||
tray_icon = app.windowIcon();
|
||||
}
|
||||
const auto resolve_tray_icon = [&app]() {
|
||||
// Render the bundled symbolic icon with palette text color so it adapts to light/dark themes.
|
||||
const QColor symbolic_color = app.palette().color(QPalette::WindowText);
|
||||
QIcon tray_icon = BuildTintedSymbolicIcon(
|
||||
QStringLiteral(":/icons/tray_icon-symbolic.svg"), symbolic_color);
|
||||
|
||||
// Fallback to system themed icon.
|
||||
if (tray_icon.isNull()) {
|
||||
tray_icon = QIcon::fromTheme(QStringLiteral("network-wireless-symbolic"));
|
||||
}
|
||||
|
||||
// Fallback to custom PNG icon.
|
||||
if (tray_icon.isNull()) {
|
||||
tray_icon = QIcon(QStringLiteral(":/icons/tray_icon.png"));
|
||||
}
|
||||
|
||||
// Final fallback.
|
||||
if (tray_icon.isNull()) {
|
||||
tray_icon = app.windowIcon();
|
||||
}
|
||||
return tray_icon;
|
||||
};
|
||||
|
||||
QIcon tray_icon = resolve_tray_icon();
|
||||
QSystemTrayIcon tray(tray_icon);
|
||||
tray.setToolTip(QStringLiteral("Nearby QML Tray"));
|
||||
|
||||
@@ -89,6 +131,10 @@ int main(int argc, char* argv[]) {
|
||||
|
||||
QObject::connect(&app, &QCoreApplication::aboutToQuit, &controller,
|
||||
[&controller]() { controller.stop(); });
|
||||
QObject::connect(app.styleHints(), &QStyleHints::colorSchemeChanged, &tray,
|
||||
[&tray, &resolve_tray_icon](Qt::ColorScheme) {
|
||||
tray.setIcon(resolve_tray_icon());
|
||||
});
|
||||
|
||||
tray.setContextMenu(&tray_menu);
|
||||
tray.show();
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#include "sharing/linux/qml_tray_app/nearby_tray_controller.h"
|
||||
#include "nearby_tray_controller.h"
|
||||
|
||||
#include <QDateTime>
|
||||
#include <QDir>
|
||||
@@ -13,7 +13,7 @@
|
||||
namespace {
|
||||
|
||||
using NearbyConnectionsQtFacade =
|
||||
nearby::sharing::linux::NearbyConnectionsQtFacade;
|
||||
nearby::sharing::NearbyConnectionsQtFacade;
|
||||
|
||||
QString NormalizeMediumsMode(QString mode) {
|
||||
mode = mode.trimmed().toLower();
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
|
||||
#include "sharing/linux/nearby_connections_qt_facade.h"
|
||||
|
||||
using NearbyConnectionsQtFacade = nearby::sharing::NearbyConnectionsQtFacade;
|
||||
|
||||
class NearbyTrayController : public QObject {
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(QString mode READ mode WRITE setMode NOTIFY modeChanged)
|
||||
@@ -132,21 +134,14 @@ class NearbyTrayController : public QObject {
|
||||
|
||||
std::vector<uint8_t> BuildEndpointInfo() const;
|
||||
|
||||
nearby::sharing::linux::NearbyConnectionsQtFacade::ConnectionListener
|
||||
BuildConnectionListener();
|
||||
nearby::sharing::linux::NearbyConnectionsQtFacade::DiscoveryListener
|
||||
BuildDiscoveryListener();
|
||||
nearby::sharing::linux::NearbyConnectionsQtFacade::PayloadListener
|
||||
BuildPayloadListener();
|
||||
NearbyConnectionsQtFacade::ConnectionListener BuildConnectionListener();
|
||||
NearbyConnectionsQtFacade::DiscoveryListener BuildDiscoveryListener();
|
||||
NearbyConnectionsQtFacade::PayloadListener BuildPayloadListener();
|
||||
|
||||
nearby::sharing::linux::NearbyConnectionsQtFacade::AdvertisingOptions
|
||||
BuildAdvertisingOptions() const;
|
||||
nearby::sharing::linux::NearbyConnectionsQtFacade::DiscoveryOptions
|
||||
BuildDiscoveryOptions() const;
|
||||
nearby::sharing::linux::NearbyConnectionsQtFacade::ConnectionOptions
|
||||
BuildConnectionOptions() const;
|
||||
nearby::sharing::linux::NearbyConnectionsQtFacade::MediumSelection
|
||||
BuildMediumSelection() const;
|
||||
NearbyConnectionsQtFacade::AdvertisingOptions BuildAdvertisingOptions() const;
|
||||
NearbyConnectionsQtFacade::DiscoveryOptions BuildDiscoveryOptions() const;
|
||||
NearbyConnectionsQtFacade::ConnectionOptions BuildConnectionOptions() const;
|
||||
NearbyConnectionsQtFacade::MediumSelection BuildMediumSelection() const;
|
||||
|
||||
void AddDiscoveredDevice(const QString& endpoint_id);
|
||||
void RemoveDiscoveredDevice(const QString& endpoint_id);
|
||||
@@ -171,14 +166,12 @@ class NearbyTrayController : public QObject {
|
||||
void LogLine(const QString& line);
|
||||
void ReopenLogFile();
|
||||
|
||||
static QString StatusToString(
|
||||
nearby::sharing::linux::NearbyConnectionsQtFacade::Status status);
|
||||
static QString StatusToString(NearbyConnectionsQtFacade::Status status);
|
||||
static QString PayloadStatusToString(
|
||||
nearby::sharing::linux::NearbyConnectionsQtFacade::PayloadStatus status);
|
||||
static QString MediumToString(
|
||||
nearby::sharing::linux::NearbyConnectionsQtFacade::Medium medium);
|
||||
NearbyConnectionsQtFacade::PayloadStatus status);
|
||||
static QString MediumToString(NearbyConnectionsQtFacade::Medium medium);
|
||||
|
||||
nearby::sharing::linux::NearbyConnectionsQtFacade service_;
|
||||
NearbyConnectionsQtFacade service_;
|
||||
|
||||
QString mode_ = QStringLiteral("Receive");
|
||||
QString device_name_ = QStringLiteral("NearbyQt");
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
<RCC>
|
||||
<qresource prefix="/qml">
|
||||
<file>FileShareTray.qml</file>
|
||||
</qresource>
|
||||
<qresource prefix="/icons">
|
||||
<file>tray_icon.png</file>
|
||||
<file alias="tray_icon-symbolic.svg">tray_icon-symbolic.svg</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
@@ -0,0 +1,14 @@
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
>
|
||||
<path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" />
|
||||
<path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 359 B |
Binary file not shown.
|
After Width: | Height: | Size: 2.0 KiB |
Reference in New Issue
Block a user