mirror of
https://github.com/iDescriptor/iDescriptor.git
synced 2026-06-22 03:45:51 +08:00
implement upgrading wired connection to wireless after device is unplugged
This commit is contained in:
+3
-1
@@ -188,7 +188,6 @@ void AppContext::removeDevice(iDescriptor::Uniq uniq, bool ask_backend)
|
||||
<< "wasWireless:" << device->deviceInfo.isWireless;
|
||||
|
||||
emit deviceRemoved(q_udid, device->deviceInfo.wifiMacAddress,
|
||||
device->deviceInfo.ipAddress,
|
||||
device->deviceInfo.isWireless);
|
||||
emit deviceChange();
|
||||
|
||||
@@ -290,6 +289,9 @@ void AppContext::addRecoveryDevice(uint64_t ecid)
|
||||
AppContext::~AppContext()
|
||||
{
|
||||
m_devices.clear();
|
||||
for (const QString &udid : m_devices.keys()) {
|
||||
core->remove_device(udid);
|
||||
}
|
||||
|
||||
#ifdef ENABLE_RECOVERY_DEVICE_SUPPORT
|
||||
for (auto recoveryDevice : m_recoveryDevices) {
|
||||
|
||||
+1
-1
@@ -74,7 +74,7 @@ private:
|
||||
signals:
|
||||
void deviceAdded(std::shared_ptr<iDescriptorDevice> device);
|
||||
void deviceRemoved(const QString &udid, const std::string &macAddress,
|
||||
const std::string &ipAddress, bool wasWireless);
|
||||
bool wasWireless);
|
||||
void devicePaired(std::shared_ptr<iDescriptorDevice> device);
|
||||
void devicePasswordProtected(const QString &udid);
|
||||
void deviceAlreadyExists(const iDescriptor::Uniq &uniq);
|
||||
|
||||
@@ -60,12 +60,19 @@ void AvahiService::stopBrowsing()
|
||||
m_networkDevices.clear();
|
||||
}
|
||||
|
||||
QList<NetworkDevice> AvahiService::getNetworkDevices() const
|
||||
QMap<QString, NetworkDevice> AvahiService::getNetworkDevices() const
|
||||
{
|
||||
QMutexLocker locker(&m_devicesMutex);
|
||||
return m_networkDevices;
|
||||
}
|
||||
|
||||
NetworkDevice
|
||||
AvahiService::getNetworkDeviceByMac(const QString &macAddress) const
|
||||
{
|
||||
QMutexLocker locker(&m_devicesMutex);
|
||||
return m_networkDevices.value(macAddress, NetworkDevice());
|
||||
}
|
||||
|
||||
void AvahiService::pollAvahi()
|
||||
{
|
||||
if (m_simplePoll && m_running) {
|
||||
@@ -163,10 +170,7 @@ void AvahiService::browseCallback(AvahiServiceBrowser *browser,
|
||||
// Remove from our list
|
||||
{
|
||||
QMutexLocker locker(&service->m_devicesMutex);
|
||||
service->m_networkDevices.removeIf(
|
||||
[macAddress](const NetworkDevice &dev) {
|
||||
return dev.macAddress == macAddress;
|
||||
});
|
||||
service->m_networkDevices.remove(macAddress);
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -195,44 +199,27 @@ void AvahiService::resolveCallback(
|
||||
AvahiService *service = static_cast<AvahiService *>(userdata);
|
||||
|
||||
if (event == AVAHI_RESOLVER_FOUND) {
|
||||
NetworkDevice device;
|
||||
device.name = QString::fromUtf8(name);
|
||||
device.hostname = QString::fromUtf8(host_name);
|
||||
device.port = port > 0 ? port : 22; // Default to SSH port
|
||||
QString deviceName = QString::fromUtf8(name);
|
||||
|
||||
// Convert address to string
|
||||
char addr_str[AVAHI_ADDRESS_STR_MAX];
|
||||
avahi_address_snprint(addr_str, sizeof(addr_str), address);
|
||||
device.address = QString::fromUtf8(addr_str);
|
||||
|
||||
// Parse TXT records
|
||||
for (AvahiStringList *t = txt; t; t = t->next) {
|
||||
char *key = nullptr;
|
||||
char *value = nullptr;
|
||||
avahi_string_list_get_pair(t, &key, &value, nullptr);
|
||||
if (key) {
|
||||
device.txt[key] = value ? value : "";
|
||||
avahi_free(key);
|
||||
}
|
||||
if (value) {
|
||||
avahi_free(value);
|
||||
}
|
||||
}
|
||||
device.macAddress = device.name.split('@').first();
|
||||
NetworkDevice device(
|
||||
QString::fromUtf8(name), QString::fromUtf8(addr_str),
|
||||
deviceName.split('@').first(), QString::fromUtf8(host_name),
|
||||
port > 0 ? port : 22);
|
||||
|
||||
qDebug() << "Resolved Apple device:" << device.name << "at"
|
||||
<< device.address << ":" << device.port
|
||||
<< "MAC:" << device.macAddress;
|
||||
|
||||
// Add to our list if not already present
|
||||
// Add to list if not already present
|
||||
{
|
||||
QMutexLocker locker(&service->m_devicesMutex);
|
||||
bool exists = std::any_of(service->m_networkDevices.begin(),
|
||||
service->m_networkDevices.end(),
|
||||
[&device](const NetworkDevice &existing) {
|
||||
return existing == device;
|
||||
});
|
||||
bool exists = service->m_networkDevices.contains(device.macAddress);
|
||||
if (!exists) {
|
||||
service->m_networkDevices.append(device);
|
||||
service->m_networkDevices[device.macAddress] = device;
|
||||
emit service->deviceAdded(device);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,7 +44,8 @@ public:
|
||||
|
||||
void startBrowsing();
|
||||
void stopBrowsing();
|
||||
QList<NetworkDevice> getNetworkDevices() const;
|
||||
QMap<QString, NetworkDevice> getNetworkDevices() const;
|
||||
NetworkDevice getNetworkDeviceByMac(const QString &macAddress) const;
|
||||
|
||||
signals:
|
||||
void deviceAdded(const NetworkDevice &device);
|
||||
@@ -79,7 +80,7 @@ private:
|
||||
QTimer *m_pollTimer;
|
||||
|
||||
mutable QMutex m_devicesMutex;
|
||||
QList<NetworkDevice> m_networkDevices;
|
||||
QMap<QString, NetworkDevice> m_networkDevices;
|
||||
bool m_running;
|
||||
};
|
||||
|
||||
|
||||
@@ -150,10 +150,7 @@ void DNSSD_API DnssdService::browseCallback(
|
||||
|
||||
// Remove from our list
|
||||
QMutexLocker locker(&service->m_devicesMutex);
|
||||
service->m_networkDevices.removeIf(
|
||||
[macAddress](const NetworkDevice &dev) {
|
||||
return dev.macAddress == macAddress;
|
||||
});
|
||||
service->m_networkDevices.remove(macAddress);
|
||||
service->m_pendingDevices.remove(macAddress);
|
||||
}
|
||||
}
|
||||
@@ -269,32 +266,28 @@ void DNSSD_API DnssdService::addrInfoCallback(
|
||||
auto *addr_in = reinterpret_cast<const struct sockaddr_in *>(address);
|
||||
inet_ntop(AF_INET, &addr_in->sin_addr, ip, sizeof(ip));
|
||||
|
||||
NetworkDevice device;
|
||||
// Extract a better device name from hostname or use TXT records
|
||||
QString friendlyName = pending.hostname;
|
||||
if (friendlyName.endsWith(".local.")) {
|
||||
friendlyName =
|
||||
friendlyName.left(friendlyName.length() - 7); // Remove ".local."
|
||||
qDebug() << "friendly name:" << friendlyName;
|
||||
}
|
||||
|
||||
QString deviceName;
|
||||
// Try to get device name from TXT records first
|
||||
if (pending.txt.contains("DvNm")) {
|
||||
device.name = pending.txt["DvNm"];
|
||||
deviceName = pending.txt["DvNm"];
|
||||
qDebug() << "Device name from DvNm TXT record:" << device.name;
|
||||
} else if (pending.txt.contains("Name")) {
|
||||
device.name = pending.txt["Name"];
|
||||
deviceName = pending.txt["Name"];
|
||||
qDebug() << "Device name from Name TXT record:" << device.name;
|
||||
} else {
|
||||
// Use the cleaned hostname as fallback
|
||||
qDebug() << "Using hostname as device name:" << friendlyName;
|
||||
device.name = friendlyName;
|
||||
deviceName = friendlyName;
|
||||
}
|
||||
|
||||
device.hostname = pending.hostname;
|
||||
device.address = QString::fromUtf8(ip);
|
||||
device.port = pending.port > 0 ? pending.port : 22; // Default to SSH port
|
||||
device.macAddress = pending.macAddress;
|
||||
NetworkDevice device(deviceName, QString::fromUtf8(ip), pending.macAddress,
|
||||
pending.hostname,
|
||||
pending.port > 0 ? pending.port : 22);
|
||||
|
||||
qDebug() << "Resolved IP for Apple device:" << device.name << "at"
|
||||
<< device.address << ":" << device.port;
|
||||
@@ -302,11 +295,7 @@ void DNSSD_API DnssdService::addrInfoCallback(
|
||||
// Add to our list if not already present
|
||||
{
|
||||
QMutexLocker locker(&service->m_devicesMutex);
|
||||
bool exists = std::any_of(service->m_networkDevices.begin(),
|
||||
service->m_networkDevices.end(),
|
||||
[&device](const NetworkDevice &existing) {
|
||||
return existing == device;
|
||||
});
|
||||
bool exists = m_networkDevices.contains(pending.macAddress);
|
||||
if (!exists) {
|
||||
service->m_networkDevices.append(device);
|
||||
emit service->deviceAdded(device);
|
||||
|
||||
@@ -46,7 +46,8 @@ public:
|
||||
|
||||
void startBrowsing();
|
||||
void stopBrowsing();
|
||||
QList<NetworkDevice> getNetworkDevices() const;
|
||||
QMap<QString, NetworkDevice> getNetworkDevices() const;
|
||||
NetworkDevice getNetworkDeviceByMac(const QString &macAddress) const;
|
||||
|
||||
signals:
|
||||
void deviceAdded(const NetworkDevice &device);
|
||||
|
||||
+40
-28
@@ -97,34 +97,46 @@ void DeviceMenuWidget::init()
|
||||
stackedWidget->removeWidget(loadingWidget);
|
||||
loadingWidget->deleteLater();
|
||||
|
||||
// FIXME: toast really necessary here?
|
||||
// if (m_device->deviceInfo.parsedDeviceVersion.major < 13) {
|
||||
// Toast *toast = new Toast(this);
|
||||
// toast->setAttribute(Qt::WA_DeleteOnClose);
|
||||
// toast->setDuration(8000); // Hide after 8 seconds
|
||||
// toast->setTitle("Not wireless compatible");
|
||||
// toast->setText("This device is not wireless compatible.");
|
||||
// toast->setPosition(ToastPosition::BOTTOM_MIDDLE);
|
||||
// toast->show();
|
||||
// } else {
|
||||
// if (m_device->deviceInfo.isWireless)
|
||||
// return;
|
||||
// bool enabled = ServiceManager::enableWirelessConnections(m_device);
|
||||
// Toast *toast = new Toast(this);
|
||||
// toast->setAttribute(Qt::WA_DeleteOnClose);
|
||||
// toast->setDuration(8000); // Hide after 8 seconds
|
||||
// toast->setPosition(ToastPosition::BOTTOM_MIDDLE);
|
||||
// if (enabled) {
|
||||
// toast->setTitle("Wireless connections enabled");
|
||||
// toast->setText(
|
||||
// "You can now use wireless connections with this device.");
|
||||
// } else {
|
||||
// toast->setTitle("Failed to enable wireless connections");
|
||||
// toast->setText(
|
||||
// "Could not enable wireless connections for this device.");
|
||||
// }
|
||||
// toast->show();
|
||||
// }
|
||||
// ═══════════════════════════════════════════════════════════════════════
|
||||
// Enable wireless connections for iOS 14+ devices
|
||||
// (maybe supported on 13 too, untested)
|
||||
// ═══════════════════════════════════════════════════════════════════════
|
||||
if (m_device->deviceInfo.parsedDeviceVersion.major >= 14) {
|
||||
auto sm = SettingsManager::sharedInstance();
|
||||
|
||||
// Don't enable if
|
||||
// auto-enable wifi connections is disabled
|
||||
// we've already seen this device
|
||||
// device is already wireless
|
||||
if (!sm->autoEnableWifiConnections() ||
|
||||
sm->hasSeenDevice(m_device->udid) ||
|
||||
m_device->deviceInfo.isWireless)
|
||||
return;
|
||||
|
||||
connect(
|
||||
m_device->service_manager,
|
||||
&CXX::ServiceManager::enable_wifi_connections_result, this,
|
||||
[this, sm](bool success) {
|
||||
if (success) {
|
||||
QMessageBox::information(
|
||||
this, "Wireless connections enabled",
|
||||
"You can now connect to this device wirelessly.");
|
||||
} else {
|
||||
QMessageBox::warning(
|
||||
this, "Failed to enable wireless connections",
|
||||
"Could not enable wireless connections for this "
|
||||
"device.");
|
||||
}
|
||||
// FIXME: this could be a problem if
|
||||
// depend on this value elsewhere, but it should be fine for
|
||||
// now since it's only used for showing the warning about
|
||||
// auto-enabling wifi connections
|
||||
sm->setHasSeenDevice(m_device->udid, true);
|
||||
},
|
||||
Qt::SingleShotConnection);
|
||||
|
||||
m_device->service_manager->enable_wifi_connections();
|
||||
}
|
||||
}
|
||||
|
||||
void DeviceMenuWidget::switchToTab(const QString &tabName)
|
||||
|
||||
+22
-7
@@ -294,19 +294,34 @@ void fetchAppIconFromApple(
|
||||
std::function<void(const QPixmap &, const QJsonObject &)> callback);
|
||||
|
||||
struct NetworkDevice {
|
||||
QString name; // service name
|
||||
QString hostname; // e.g., iPhone-2.local
|
||||
QString address; // IPv4 or IPv6 address
|
||||
uint16_t port = 22; // SSH port
|
||||
std::map<std::string, std::string> txt; // TXT records
|
||||
QString macAddress; // MAC address if available
|
||||
QString name; // service name
|
||||
QString hostname; // e.g., iPhone-2.local
|
||||
QString address; // IPv4 or IPv6 address
|
||||
uint16_t port = 22; // SSH port
|
||||
QString macAddress; // MAC address if available
|
||||
|
||||
NetworkDevice() = default;
|
||||
NetworkDevice(const QString &name, const QString &address,
|
||||
const QString &macAddress, const QString &hostname,
|
||||
uint16_t port)
|
||||
: name(name), address(address), port(port), macAddress(macAddress),
|
||||
hostname(hostname)
|
||||
{
|
||||
}
|
||||
|
||||
bool isValid() const
|
||||
{
|
||||
return !name.isEmpty() && !address.isEmpty() && !macAddress.isEmpty() &&
|
||||
port > 0;
|
||||
}
|
||||
|
||||
bool operator==(const NetworkDevice &other) const
|
||||
{
|
||||
return name == other.name && address == other.address;
|
||||
}
|
||||
};
|
||||
|
||||
QPixmap load_heic(const QByteArray &data);
|
||||
QImage load_heic(const QByteArray &data);
|
||||
|
||||
// Helper struct for semantic version comparison
|
||||
struct AppVersion {
|
||||
|
||||
+28
-16
@@ -191,7 +191,6 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent)
|
||||
statusbar->setStyleSheet(
|
||||
"QWidget#StatusBar { background-color: transparent; }");
|
||||
statusLayout->addWidget(m_connectedDeviceCountLabel);
|
||||
// TODO: implement downloads/uploads progress stuff
|
||||
|
||||
StatusBalloon *statusBalloon = StatusBalloon::sharedInstance();
|
||||
|
||||
@@ -372,19 +371,29 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent)
|
||||
// ═══════════════════════════════════════════════════════════════════════
|
||||
// Upgrade to wireless when a "WIRED" device is removed
|
||||
// ═══════════════════════════════════════════════════════════════════════
|
||||
connect(AppContext::sharedInstance(), &AppContext::deviceRemoved, this,
|
||||
[](const QString &udid, const std::string &wifiMacAddress,
|
||||
const std::string &ipAddress, bool wasWireless) {
|
||||
if (wasWireless)
|
||||
return;
|
||||
qDebug() << "Upgrading device to wireless connection for UDID"
|
||||
<< udid;
|
||||
// FIXME: ignore iOS 15 and lower
|
||||
NetworkDevice dev;
|
||||
dev.macAddress = QString::fromStdString(wifiMacAddress);
|
||||
dev.address = QString::fromStdString(ipAddress);
|
||||
AppContext::sharedInstance()->tryToConnectToNetworkDevice(dev);
|
||||
});
|
||||
connect(
|
||||
AppContext::sharedInstance(), &AppContext::deviceRemoved, this,
|
||||
[](const QString &udid, const std::string &wifiMacAddress,
|
||||
bool wasWireless) {
|
||||
if (wasWireless)
|
||||
return;
|
||||
QString wifiMac = QString::fromStdString(wifiMacAddress);
|
||||
NetworkDevice dev =
|
||||
NetworkDeviceProvider::sharedInstance()->getNetworkDeviceByMac(
|
||||
wifiMac);
|
||||
if (!dev.isValid()) {
|
||||
qDebug() << "No valid network device found for UDID" << udid
|
||||
<< "with Wi-Fi MAC" << wifiMac
|
||||
<< "Not trying to upgrade to wireless connection.";
|
||||
return;
|
||||
}
|
||||
|
||||
qDebug()
|
||||
<< "Trying to upgrade device to wireless connection for UDID"
|
||||
<< udid;
|
||||
// FIXME: maybe ignore iOS 15 and lower?
|
||||
AppContext::sharedInstance()->tryToConnectToNetworkDevice(dev);
|
||||
});
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════════
|
||||
// Add a wireless device
|
||||
@@ -490,7 +499,7 @@ MainWindow::~MainWindow()
|
||||
|
||||
void MainWindow::handleShowSleepyDeviceWarning()
|
||||
{
|
||||
|
||||
static bool widgetAlreadyVisible = false;
|
||||
/* one minute cooldown to prevent spamming */
|
||||
static const int kMinIntervalMs = 60 * 1000;
|
||||
|
||||
@@ -504,9 +513,12 @@ void MainWindow::handleShowSleepyDeviceWarning()
|
||||
|
||||
lastShown = now;
|
||||
|
||||
if (SettingsManager::sharedInstance()->isSleepyDeviceWarningDismissed()) {
|
||||
if (SettingsManager::sharedInstance()->isSleepyDeviceWarningDismissed() ||
|
||||
widgetAlreadyVisible) {
|
||||
return;
|
||||
}
|
||||
|
||||
widgetAlreadyVisible = true;
|
||||
DeviceSleepWarningWidget(this).exec();
|
||||
widgetAlreadyVisible = false;
|
||||
}
|
||||
@@ -40,11 +40,16 @@ public:
|
||||
[this]() { m_networkProvider->startBrowsing(); });
|
||||
}
|
||||
|
||||
QList<NetworkDevice> getNetworkDevices()
|
||||
QMap<QString, NetworkDevice> getNetworkDevices()
|
||||
{
|
||||
return m_networkProvider->getNetworkDevices();
|
||||
}
|
||||
|
||||
NetworkDevice getNetworkDeviceByMac(const QString &macAddress)
|
||||
{
|
||||
return m_networkProvider->getNetworkDeviceByMac(macAddress);
|
||||
}
|
||||
|
||||
private:
|
||||
#ifdef __linux__
|
||||
AvahiService *m_networkProvider = nullptr;
|
||||
|
||||
@@ -268,7 +268,7 @@ void NetworkDevicesToConnectWidget::updateDeviceList()
|
||||
{
|
||||
clearDeviceCards();
|
||||
|
||||
QList<NetworkDevice> devices =
|
||||
QMap<QString, NetworkDevice> devices =
|
||||
NetworkDeviceProvider::sharedInstance()->getNetworkDevices();
|
||||
|
||||
if (devices.isEmpty()) {
|
||||
|
||||
@@ -182,7 +182,7 @@ void NetworkDevicesWidget::updateDeviceList()
|
||||
{
|
||||
clearDeviceCards();
|
||||
|
||||
QList<NetworkDevice> devices =
|
||||
QMap<QString, NetworkDevice> devices =
|
||||
NetworkDeviceProvider::sharedInstance()->getNetworkDevices();
|
||||
|
||||
if (devices.isEmpty()) {
|
||||
|
||||
@@ -148,7 +148,7 @@ void SSHTerminalTool::updateDeviceList()
|
||||
}
|
||||
|
||||
// Add wireless devices
|
||||
QList<NetworkDevice> wirelessDevices =
|
||||
QMap<QString, NetworkDevice> wirelessDevices =
|
||||
NetworkDeviceProvider::sharedInstance()->getNetworkDevices();
|
||||
for (const NetworkDevice &device : wirelessDevices) {
|
||||
addWirelessDevice(device);
|
||||
|
||||
Reference in New Issue
Block a user