#include "virtuallocationwidget.h" #include "appcontext.h" #include "devdiskimagehelper.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 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(); } }); DevDiskManager::sharedInstance()->downloadCompatibleImage(m_device); unsigned int device_version = get_device_version(m_device->device); unsigned int deviceMajorVersion = (device_version >> 16) & 0xFF; if (deviceMajorVersion > 16) { QMessageBox::warning( this, "Unsupported iOS Version", "Virtual Location feature requires iOS 16 or earlier.\n" "Your device is running iOS " + QString::number(deviceMajorVersion) + ", which is not yet supported."); QTimer::singleShot(0, this, &QWidget::close); return; } } 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() { bool latOk, lonOk; double latitude = m_latitudeEdit->text().toDouble(&latOk); double longitude = m_longitudeEdit->text().toDouble(&lonOk); if (!latOk || !lonOk) { QMessageBox::warning( this, "Invalid Input", "Please enter valid latitude and longitude values."); return; } // Create and show the helper dialog auto *helper = new DevDiskImageHelper(m_device, this); connect(helper, &DevDiskImageHelper::mountingCompleted, this, [this, latitude, longitude, helper](bool success) { helper->deleteLater(); if (!success) { return; } // Apply location emit locationChanged(latitude, longitude); 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 locationSuccess = set_location( m_device->device, const_cast( m_latitudeEdit->text().toStdString().c_str()), const_cast( m_longitudeEdit->text().toStdString().c_str())); if (!locationSuccess) { QMessageBox::warning(this, "Error", "Failed to set location on device"); } else { QMessageBox::information(this, "Success", "Location applied successfully!"); } }); helper->start(); }