#include "virtual_location.h" #include "appcontext.h" #include "devdiskmanager.h" #include "iDescriptor.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // #include VirtualLocation::VirtualLocation(iDescriptorDevice *device, QWidget *parent) : QWidget{parent}, m_device(device) { setWindowTitle("Virtual Location - iDescriptor"); // Create the main layout QHBoxLayout *mainLayout = new QHBoxLayout(this); mainLayout->setContentsMargins(10, 10, 10, 10); mainLayout->setSpacing(10); // Create left panel for controls QWidget *rightPanel = new QWidget(); rightPanel->setFixedWidth(250); QVBoxLayout *rightLayout = new QVBoxLayout(rightPanel); rightLayout->setContentsMargins(15, 15, 15, 15); rightLayout->setSpacing(10); // Title QLabel *titleLabel = new QLabel("Virtual Location Settings"); titleLabel->setStyleSheet("margin-bottom: 10px;"); rightLayout->addWidget(titleLabel); QGroupBox *coordGroup = new QGroupBox("Coordinates"); rightLayout->addWidget(coordGroup); QVBoxLayout *coordLayout = new QVBoxLayout(coordGroup); // Latitude input QLabel *latLabel = new QLabel("Latitude:"); coordLayout->addWidget(latLabel); m_latitudeEdit = new QLineEdit(); m_latitudeEdit->setPlaceholderText("e.g., 59.9139"); m_latitudeEdit->setText("59.9139"); m_latitudeEdit->setValidator(new QDoubleValidator(-90.0, 90.0, 6, this)); coordLayout->addWidget(m_latitudeEdit); // Longitude input QLabel *lonLabel = new QLabel("Longitude:"); coordLayout->addWidget(lonLabel); m_longitudeEdit = new QLineEdit(); m_longitudeEdit->setPlaceholderText("e.g., 10.7522"); m_longitudeEdit->setText("10.7522"); m_longitudeEdit->setValidator(new QDoubleValidator(-180.0, 180.0, 6, this)); coordLayout->addWidget(m_longitudeEdit); // Add some spacing rightLayout->addItem( new QSpacerItem(20, 20, QSizePolicy::Minimum, QSizePolicy::Fixed)); // Apply button m_applyButton = new QPushButton("Apply Settings"); m_applyButton->setDefault(true); rightLayout->addWidget(m_applyButton); // Add stretch to push everything to the top rightLayout->addStretch(); // Create map widget m_quickWidget = new QQuickWidget(this); m_quickWidget->setResizeMode(QQuickWidget::SizeRootObjectToView); m_quickWidget->setSource(QUrl(QStringLiteral("qrc:/qml/MapView.qml"))); // Enable input handling m_quickWidget->setFocusPolicy(Qt::StrongFocus); m_quickWidget->setAttribute(Qt::WA_AcceptTouchEvents, true); // Add widgets to main layout mainLayout->addWidget(m_quickWidget, 1); // Give map widget stretch factor of 1 mainLayout->addWidget(rightPanel); setLayout(mainLayout); // Connect signals connect(m_latitudeEdit, &QLineEdit::textChanged, this, &VirtualLocation::onInputChanged); connect(m_longitudeEdit, &QLineEdit::textChanged, this, &VirtualLocation::onInputChanged); connect(m_applyButton, &QPushButton::clicked, this, &VirtualLocation::onApplyClicked); // Connect to QML map connect(m_quickWidget, &QQuickWidget::statusChanged, this, &VirtualLocation::onQuickWidgetStatusChanged); // Register this object with QML context so QML can call our slots m_quickWidget->rootContext()->setContextProperty("cppHandler", this); qDebug() << "QuickWidget status:" << m_quickWidget->status(); qDebug() << "QuickWidget errors:" << m_quickWidget->errors(); connect(AppContext::sharedInstance(), &AppContext::deviceRemoved, this, [this](const std::string &udid) { if (m_device->udid == udid) { this->close(); this->deleteLater(); } // qDebug() << "VirtualLocation detected device change to" // << (device ? device->udid.c_str() : "null"); }); bool res = DevDiskManager::sharedInstance()->downloadCompatibleImage(m_device); qDebug() << "Mount result:" << res; } void VirtualLocation::onQuickWidgetStatusChanged(QQuickWidget::Status status) { if (status == QQuickWidget::Ready) { qDebug() << "QuickWidget is ready"; // Set initial map position updateMapFromInputs(); } else if (status == QQuickWidget::Error) { qDebug() << "QuickWidget errors:" << m_quickWidget->errors(); } } void VirtualLocation::onInputChanged() { // Update map when input changes (with slight delay to avoid too frequent // updates) m_updateTimer.setSingleShot(true); m_updateTimer.setInterval(500); // 500ms delay disconnect(&m_updateTimer, &QTimer::timeout, this, &VirtualLocation::updateMapFromInputs); connect(&m_updateTimer, &QTimer::timeout, this, &VirtualLocation::updateMapFromInputs); m_updateTimer.start(); } void VirtualLocation::updateMapFromInputs() { bool latOk, lonOk; double latitude = m_latitudeEdit->text().toDouble(&latOk); double longitude = m_longitudeEdit->text().toDouble(&lonOk); if (latOk && lonOk && latitude >= -90 && latitude <= 90 && longitude >= -180 && longitude <= 180) { QQuickItem *rootObject = m_quickWidget->rootObject(); if (rootObject) { QQuickItem *mapItem = rootObject->findChild("map"); if (mapItem) { // Block signals to prevent feedback loop m_updatingFromInput = true; // Call QML function to update map center QMetaObject::invokeMethod(mapItem, "updateCenter", Q_ARG(QVariant, latitude), Q_ARG(QVariant, longitude)); m_updatingFromInput = false; qDebug() << "Updated map center to:" << latitude << "," << longitude; } } } } void VirtualLocation::onMapCenterChanged() { if (m_updatingFromInput) { return; // Prevent feedback loop } qDebug() << "onMapCenterChanged called!"; QQuickItem *rootObject = m_quickWidget->rootObject(); if (rootObject) { QQuickItem *mapItem = rootObject->findChild("map"); if (mapItem) { // Get map center using QMetaObject::invokeMethod for more reliable // access QVariant centerVar = mapItem->property("center"); if (centerVar.isValid()) { // Try to get the coordinate directly QGeoCoordinate coord = centerVar.value(); if (coord.isValid()) { double latitude = coord.latitude(); double longitude = coord.longitude(); // Block signals temporarily to prevent feedback m_latitudeEdit->blockSignals(true); m_longitudeEdit->blockSignals(true); // Update input fields m_latitudeEdit->setText(QString::number(latitude, 'f', 6)); m_longitudeEdit->setText( QString::number(longitude, 'f', 6)); // Restore signals m_latitudeEdit->blockSignals(false); m_longitudeEdit->blockSignals(false); qDebug() << "Updated inputs from map:" << latitude << "," << longitude; } else { qDebug() << "Invalid coordinate from map"; } } else { qDebug() << "Could not get center property from map"; } } } } // Add this new slot that QML can call directly void VirtualLocation::updateInputsFromMap(double latitude, double longitude) { if (m_updatingFromInput) { return; // Prevent feedback loop } qDebug() << "updateInputsFromMap called with:" << latitude << "," << longitude; // Block signals temporarily to prevent feedback m_latitudeEdit->blockSignals(true); m_longitudeEdit->blockSignals(true); // Update input fields m_latitudeEdit->setText(QString::number(latitude, 'f', 6)); m_longitudeEdit->setText(QString::number(longitude, 'f', 6)); // Restore signals m_latitudeEdit->blockSignals(false); m_longitudeEdit->blockSignals(false); qDebug() << "Updated inputs from map:" << latitude << "," << longitude; } void VirtualLocation::onApplyClicked() { GetMountedImageResult result = DevDiskManager::sharedInstance()->getMountedImage( m_device->udid.c_str()); if (!result.success) { QMessageBox::warning(this, "Failure", result.message.c_str()); return; } if (result.success || result.sig.empty()) { bool devImgSuccess = DevDiskManager::sharedInstance()->mountCompatibleImage(m_device); if (!devImgSuccess) { QMessageBox::warning(this, "Failure", "Failed to mount developer image on device. " "Try with a different cable."); qDebug() << "Failed to mount developer image on device. Cannot set " "location."; return; } } bool latOk, lonOk; double latitude = m_latitudeEdit->text().toDouble(&latOk); double longitude = m_longitudeEdit->text().toDouble(&lonOk); if (latOk && lonOk) { emit locationChanged(latitude, longitude); // Update map one final time updateMapFromInputs(); // Visual feedback m_applyButton->setText("Applied!"); m_applyButton->setEnabled(false); QTimer::singleShot(1000, this, [this]() { m_applyButton->setText("Apply Settings"); m_applyButton->setEnabled(true); }); bool success = set_location( m_device->device, const_cast(m_latitudeEdit->text().toStdString().c_str()), const_cast(m_longitudeEdit->text().toStdString().c_str())); if (!success) { warn("Failed to set location on device"); qDebug() << "Failed to set location on device"; } else { // todo: thread safe? QMessageBox::information(this, "Success", "Location applied successfully!"); qDebug() << "Applied location settings:" << latitude << "," << longitude; } } else { qDebug() << "Invalid coordinate values"; } }