Files
nearby/sharing/linux/nearby_sharing_app.cc
T

551 lines
25 KiB
C++

// Copyright 2025 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "connections/implementation/flags/nearby_connections_feature_flags.h"
#include <chrono>
#include <iostream>
#include <memory>
#include <string>
#include <thread>
#include <vector>
#include "sharing/linux/nearby_sharing_service_linux.h"
#include "sharing/attachment_container.h"
#include "sharing/file_attachment.h"
#include "sharing/share_target.h"
#include "sharing/text_attachment.h"
#include "sharing/transfer_metadata.h"
#include "sharing/share_target_discovered_callback.h"
#include "sharing/transfer_update_callback.h"
#include "internal/base/file_path.h"
#include "internal/base/files.h"
#include "internal/flags/nearby_flags.h"
using namespace nearby::sharing;
using namespace nearby::sharing::linux;
class MyTransferUpdateCallback : public TransferUpdateCallback {
public:
void OnTransferUpdate(const ShareTarget& share_target,
const AttachmentContainer& attachment_container,
const TransferMetadata& transfer_metadata) override {
std::cout << "\n┌─────────────────────────────────────┐" << std::endl;
std::cout << "│ TRANSFER UPDATE │" << std::endl;
std::cout << "├─────────────────────────────────────┤" << std::endl;
std::cout << "│ Device: " << share_target.device_name << std::endl;
std::cout << "│ Target ID: " << share_target.id << " ← USE THIS ID" << std::endl;
std::cout << "│ Status: " << TransferMetadata::StatusToString(transfer_metadata.status()) << std::endl;
std::cout << "│ Progress: " << (transfer_metadata.progress() * 100) << "%" << std::endl;
std::cout << "│ Transferred: " << transfer_metadata.transferred_bytes() << " bytes" << std::endl;
std::cout << "│ Total attachments: " << transfer_metadata.total_attachments_count() << std::endl;
// Track incoming transfer requests
if (transfer_metadata.status() == TransferMetadata::Status::kAwaitingLocalConfirmation) {
std::cout << "├─────────────────────────────────────┤" << std::endl;
std::cout << "│ ⚠️ INCOMING TRANSFER REQUEST │" << std::endl;
std::cout << "│ From: " << share_target.device_name << std::endl;
std::cout << "│ Target ID: " << share_target.id << std::endl;
std::cout << "│ → Press 6 to ACCEPT │" << std::endl;
std::cout << "│ → Press 7 to REJECT │" << std::endl;
pending_target_id_ = share_target.id;
has_pending_request_ = true;
}
// Show completed transfers
if (transfer_metadata.status() == TransferMetadata::Status::kComplete) {
std::cout << "├─────────────────────────────────────┤" << std::endl;
std::cout << "│ ✓ TRANSFER COMPLETE │" << std::endl;
for (const auto& file : attachment_container.GetFileAttachments()) {
std::cout << "│ Received file: " << file.file_name() << std::endl;
}
for (const auto& text : attachment_container.GetTextAttachments()) {
std::cout << "│ Received text: " << text.text_body() << std::endl;
}
has_pending_request_ = false;
}
std::cout << "└─────────────────────────────────────┘" << std::endl;
PrintMenuAtBottom();
}
bool HasPendingRequest() const { return has_pending_request_; }
int64_t GetPendingTargetId() const { return pending_target_id_; }
private:
bool has_pending_request_ = false;
int64_t pending_target_id_ = -1;
void PrintMenuAtBottom() {
std::cout << "\n╔═════════════════════════════════════════════════════════╗" << std::endl;
std::cout << "║ NEARBY SHARING - MENU ║" << std::endl;
std::cout << "╠═════════════════════════════════════════════════════════╣" << std::endl;
std::cout << "║ 1. Start as Receiver │ 6. Accept pending request ║" << std::endl;
std::cout << "║ 2. Start as Sender │ 7. Reject pending request ║" << std::endl;
std::cout << "║ 3. List devices │ 8. Cancel transfer ║" << std::endl;
std::cout << "║ 4. Send file │ 9. Print status ║" << std::endl;
std::cout << "║ 5. Send text │ 0. Exit ║" << std::endl;
std::cout << "╚═════════════════════════════════════════════════════════╝" << std::endl;
std::cout << "Choice: ";
}
};
class MyShareTargetDiscoveredCallback : public ShareTargetDiscoveredCallback {
public:
void OnShareTargetDiscovered(const ShareTarget& share_target) override {
std::cout << "\n┌─────────────────────────────────────┐" << std::endl;
std::cout << "│ 📱 DEVICE DISCOVERED │" << std::endl;
std::cout << "├─────────────────────────────────────┤" << std::endl;
std::cout << "│ ID: " << share_target.id << std::endl;
std::cout << "│ Name: " << share_target.device_name << std::endl;
std::cout << "│ Vendor ID: " << static_cast<int>(share_target.vendor_id) << std::endl;
std::cout << "└─────────────────────────────────────┘" << std::endl;
discovered_targets_.push_back(share_target);
PrintMenuAtBottom();
}
void OnShareTargetLost(const ShareTarget& share_target) override {
std::cout << "\n┌─────────────────────────────────────┐" << std::endl;
std::cout << "│ ❌ DEVICE LOST │" << std::endl;
std::cout << "├─────────────────────────────────────┤" << std::endl;
std::cout << "│ Name: " << share_target.device_name << std::endl;
std::cout << "└─────────────────────────────────────┘" << std::endl;
for (auto it = discovered_targets_.begin(); it != discovered_targets_.end(); ++it) {
if (it->id == share_target.id) {
discovered_targets_.erase(it);
break;
}
}
PrintMenuAtBottom();
}
void OnShareTargetUpdated(const ShareTarget& share_target) override {
std::cout << "\n┌─────────────────────────────────────┐" << std::endl;
std::cout << "│ 🔄 DEVICE UPDATED │" << std::endl;
std::cout << "├─────────────────────────────────────┤" << std::endl;
std::cout << "│ Name: " << share_target.device_name << std::endl;
std::cout << "└─────────────────────────────────────┘" << std::endl;
PrintMenuAtBottom();
}
const std::vector<ShareTarget>& GetDiscoveredTargets() const {
return discovered_targets_;
}
private:
std::vector<ShareTarget> discovered_targets_;
void PrintMenuAtBottom() {
std::cout << "\n╔═════════════════════════════════════════════════════════╗" << std::endl;
std::cout << "║ NEARBY SHARING - MENU ║" << std::endl;
std::cout << "╠═════════════════════════════════════════════════════════╣" << std::endl;
std::cout << "║ 1. Start as Receiver │ 6. Accept pending request ║" << std::endl;
std::cout << "║ 2. Start as Sender │ 7. Reject pending request ║" << std::endl;
std::cout << "║ 3. List devices │ 8. Cancel transfer ║" << std::endl;
std::cout << "║ 4. Send file │ 9. Print status ║" << std::endl;
std::cout << "║ 5. Send text │ 0. Exit ║" << std::endl;
std::cout << "╚═════════════════════════════════════════════════════════╝" << std::endl;
std::cout << "Choice: ";
}
};
class NearbySharingApp {
public:
NearbySharingApp(const std::string& device_name)
: service_(std::make_unique<NearbySharingServiceLinux>(device_name)),
transfer_callback_(std::make_unique<MyTransferUpdateCallback>()),
discovery_callback_(std::make_unique<MyShareTargetDiscoveredCallback>()) {
std::cout << "Nearby Sharing Application initialized with device name: "
<< device_name << std::endl;
}
~NearbySharingApp() {
Shutdown();
}
void StartAsReceiver() {
std::cout << "\n=== Starting as Receiver (Foreground) ===" << std::endl;
service_->RegisterReceiveSurface(
transfer_callback_.get(),
NearbySharingService::ReceiveSurfaceState::kForeground,
Advertisement::BlockedVendorId::kNone,
[this](NearbySharingService::StatusCodes status) {
if (status == NearbySharingService::StatusCodes::kOk) {
std::cout << "Successfully registered as receiver!" << std::endl;
// Display QR code URL
std::string qr_url = service_->GetQrCodeUrl();
if (!qr_url.empty()) {
std::cout << "\n┌──────────────────────────────────────────────────────────────┐" << std::endl;
std::cout << "│ QR CODE URL: │" << std::endl;
std::cout << "│ " << qr_url;
// Add padding if needed
if (qr_url.length() < 61) {
std::cout << std::string(61 - qr_url.length(), ' ');
}
std::cout << "│" << std::endl;
std::cout << "│ │" << std::endl;
std::cout << "│ Scan this with your phone to connect! │" << std::endl;
std::cout << "└──────────────────────────────────────────────────────────────┘" << std::endl;
}
} else {
std::cout << "Failed to register as receiver: "
<< NearbySharingService::StatusCodeToString(status) << std::endl;
}
});
std::cout << "Advertising enabled. Waiting for incoming connections..." << std::endl;
}
void StartAsSender() {
std::cout << "\n=== Starting as Sender (Foreground) ===" << std::endl;
service_->RegisterSendSurface(
transfer_callback_.get(),
discovery_callback_.get(),
NearbySharingService::SendSurfaceState::kForeground,
Advertisement::BlockedVendorId::kNone,
false, // disable_wifi_hotspot
[this](NearbySharingService::StatusCodes status) {
if (status == NearbySharingService::StatusCodes::kOk) {
std::cout << "Successfully registered as sender!" << std::endl;
// Display QR code URL
std::string qr_url = service_->GetQrCodeUrl();
if (!qr_url.empty()) {
std::cout << "\n┌──────────────────────────────────────────────────────────────┐" << std::endl;
std::cout << "│ QR CODE URL: │" << std::endl;
std::cout << "│ " << qr_url;
// Add padding if needed
if (qr_url.length() < 61) {
std::cout << std::string(61 - qr_url.length(), ' ');
}
std::cout << "│" << std::endl;
std::cout << "│ │" << std::endl;
std::cout << "│ Scan this with your phone to connect! │" << std::endl;
std::cout << "└──────────────────────────────────────────────────────────────┘" << std::endl;
}
} else {
std::cout << "Failed to register as sender: "
<< NearbySharingService::StatusCodeToString(status) << std::endl;
}
});
std::cout << "Scanning for nearby devices..." << std::endl;
}
void SendFile(int64_t target_id, const std::string& file_path) {
std::cout << "\n=== Sending File ===" << std::endl;
std::cout << "Target ID: " << target_id << std::endl;
std::cout << "File: " << file_path << std::endl;
nearby::FilePath path(file_path);
auto file_size = nearby::Files::GetFileSize(path);
if (!file_size.has_value()) {
std::cout << "Error: Could not get file size for " << file_path << std::endl;
return;
}
// Use the constructor that accepts size
std::string file_name = path.GetFileName().ToString();
std::string mime_type = ""; // Will be auto-detected from extension
// Create file attachment with proper size
FileAttachment file_attachment(
/*id=*/0, // ID will be auto-generated
/*size=*/static_cast<int64_t>(*file_size),
/*file_name=*/file_name,
/*mime_type=*/mime_type,
/*type=*/service::proto::FileMetadata::UNKNOWN,
/*parent_folder=*/"",
/*batch_id=*/0
);
// Set the file path after construction
file_attachment.set_file_path(path);
auto attachment_container = AttachmentContainer::Builder()
.AddFileAttachment(
std::move(file_attachment))
.Build();
service_->SendAttachments(
target_id,
std::move(attachment_container),
[](NearbySharingService::StatusCodes status) {
if (status == NearbySharingService::StatusCodes::kOk) {
std::cout << "File send initiated successfully!" << std::endl;
} else {
std::cout << "Failed to send file: "
<< NearbySharingService::StatusCodeToString(status) << std::endl;
}
});
}
void SendText(int64_t target_id, const std::string& text) {
std::cout << "\n=== Sending Text ===" << std::endl;
std::cout << "Target ID: " << target_id << std::endl;
std::cout << "Text: " << text << std::endl;
auto attachment_container = AttachmentContainer::Builder()
.AddTextAttachment(TextAttachment(
nearby::sharing::service::proto::
TextMetadata::TEXT,
text,
std::nullopt, // text_title
std::nullopt // mime_type
))
.Build();
service_->SendAttachments(
target_id,
std::move(attachment_container),
[](NearbySharingService::StatusCodes status) {
if (status == NearbySharingService::StatusCodes::kOk) {
std::cout << "Text send initiated successfully!" << std::endl;
} else {
std::cout << "Failed to send text: "
<< NearbySharingService::StatusCodeToString(status) << std::endl;
}
});
}
void AcceptIncomingShare(int64_t target_id) {
std::cout << "\n=== Accepting Incoming Share ===" << std::endl;
std::cout << "Target ID: " << target_id << std::endl;
service_->Accept(
target_id,
[](NearbySharingService::StatusCodes status) {
if (status == NearbySharingService::StatusCodes::kOk) {
std::cout << "Share accepted!" << std::endl;
} else {
std::cout << "Failed to accept share: "
<< NearbySharingService::StatusCodeToString(status) << std::endl;
}
});
}
void AcceptPendingShare() {
if (!transfer_callback_->HasPendingRequest()) {
std::cout << "No pending incoming transfer request!" << std::endl;
return;
}
AcceptIncomingShare(transfer_callback_->GetPendingTargetId());
}
void RejectIncomingShare(int64_t target_id) {
std::cout << "\n=== Rejecting Incoming Share ===" << std::endl;
std::cout << "Target ID: " << target_id << std::endl;
service_->Reject(
target_id,
[](NearbySharingService::StatusCodes status) {
if (status == NearbySharingService::StatusCodes::kOk) {
std::cout << "Share rejected!" << std::endl;
} else {
std::cout << "Failed to reject share: "
<< NearbySharingService::StatusCodeToString(status) << std::endl;
}
});
}
void RejectPendingShare() {
if (!transfer_callback_->HasPendingRequest()) {
std::cout << "No pending incoming transfer request!" << std::endl;
return;
}
RejectIncomingShare(transfer_callback_->GetPendingTargetId());
}
void CancelTransfer(int64_t target_id) {
std::cout << "\n=== Canceling Transfer ===" << std::endl;
std::cout << "Target ID: " << target_id << std::endl;
service_->Cancel(
target_id,
[](NearbySharingService::StatusCodes status) {
if (status == NearbySharingService::StatusCodes::kOk) {
std::cout << "Transfer cancelled!" << std::endl;
} else {
std::cout << "Failed to cancel transfer: "
<< NearbySharingService::StatusCodeToString(status) << std::endl;
}
});
}
void ListDiscoveredDevices() {
std::cout << "\n=== Discovered Devices ===" << std::endl;
const auto& targets = discovery_callback_->GetDiscoveredTargets();
if (targets.empty()) {
std::cout << "No devices found." << std::endl;
} else {
for (const auto& target : targets) {
std::cout << "ID: " << target.id
<< " | Name: " << target.device_name
<< " | Vendor: " << static_cast<int>(target.vendor_id) << std::endl;
}
}
std::cout << "==========================" << std::endl;
}
void PrintStatus() {
std::cout << "\n=== Service Status ===" << std::endl;
std::cout << "Bluetooth Present: " << (service_->IsBluetoothPresent() ? "Yes" : "No") << std::endl;
std::cout << "Bluetooth Powered: " << (service_->IsBluetoothPowered() ? "Yes" : "No") << std::endl;
std::cout << "Is Scanning: " << (service_->IsScanning() ? "Yes" : "No") << std::endl;
std::cout << "Is Transferring: " << (service_->IsTransferring() ? "Yes" : "No") << std::endl;
std::cout << "======================" << std::endl;
}
void Shutdown() {
std::cout << "\n=== Shutting Down ===" << std::endl;
service_->Shutdown([](NearbySharingService::StatusCodes status) {
std::cout << "Shutdown complete: "
<< NearbySharingService::StatusCodeToString(status) << std::endl;
});
}
private:
std::unique_ptr<NearbySharingServiceLinux> service_;
std::unique_ptr<MyTransferUpdateCallback> transfer_callback_;
std::unique_ptr<MyShareTargetDiscoveredCallback> discovery_callback_;
};
void PrintMenu() {
std::cout << "\n╔═════════════════════════════════════════════════════════╗" << std::endl;
std::cout << "║ NEARBY SHARING - MENU ║" << std::endl;
std::cout << "╠═════════════════════════════════════════════════════════╣" << std::endl;
std::cout << "║ 1. Start as Receiver │ 6. Accept pending request ║" << std::endl;
std::cout << "║ 2. Start as Sender │ 7. Reject pending request ║" << std::endl;
std::cout << "║ 3. List devices │ 8. Cancel transfer ║" << std::endl;
std::cout << "║ 4. Send file │ 9. Print status ║" << std::endl;
std::cout << "║ 5. Send text │ 0. Exit ║" << std::endl;
std::cout << "╚═════════════════════════════════════════════════════════╝" << std::endl;
std::cout << "Choice: ";
}
int main(int argc, char* argv[]) {
nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue(
nearby::connections::config_package_nearby::nearby_connections_feature::
kEnableBleL2cap,
true);
nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue(
nearby::connections::config_package_nearby::nearby_connections_feature::
kRefactorBleL2cap,
true);
std::string device_name = "MyLinuxDevice";
if (argc > 1) {
device_name = argv[1];
}
// Clear screen and show header
std::cout << "\033[2J\033[1;1H"; // Clear screen
std::cout << "╔═════════════════════════════════════════════════════════╗" << std::endl;
std::cout << "║ ║" << std::endl;
std::cout << "║ NEARBY SHARING - LINUX APPLICATION ║" << std::endl;
std::cout << "║ Device: " << device_name << std::string(32 - device_name.length(), ' ') << "║" << std::endl;
std::cout << "║ ║" << std::endl;
std::cout << "╚═════════════════════════════════════════════════════════╝" << std::endl;
std::cout << "\n[LOG AREA - Updates will appear above the menu]\n" << std::endl;
NearbySharingApp app(device_name);
bool running = true;
while (running) {
PrintMenu();
int choice;
std::cin >> choice;
switch (choice) {
case 1:
app.StartAsReceiver();
break;
case 2:
app.StartAsSender();
std::this_thread::sleep_for(std::chrono::seconds(2));
app.ListDiscoveredDevices();
break;
case 3:
app.ListDiscoveredDevices();
break;
case 4: {
int64_t target_id;
std::string file_path;
std::cout << "Enter target ID: ";
std::cin >> target_id;
std::cout << "Enter file path: ";
std::cin.ignore();
std::getline(std::cin, file_path);
app.SendFile(target_id, file_path);
break;
}
case 5: {
int64_t target_id;
std::string text;
std::cout << "Enter target ID: ";
std::cin >> target_id;
std::cout << "Enter text to send: ";
std::cin.ignore();
std::getline(std::cin, text);
app.SendText(target_id, text);
break;
}
case 6:
app.AcceptPendingShare();
break;
case 7:
app.RejectPendingShare();
break;
case 8: {
int64_t target_id;
std::cout << "Enter target ID to cancel: ";
std::cin >> target_id;
app.CancelTransfer(target_id);
break;
}
case 9:
app.PrintStatus();
break;
case 0:
running = false;
break;
default:
std::cout << "Invalid choice. Please try again." << std::endl;
break;
}
}
std::cout << "\nGoodbye!" << std::endl;
return 0;
}