Files
iDescriptor/src/virtuallocationwidget.cpp
T
uncor3 c783123b8d implement WelcomeWidget , fix bugs , add tutorial videos
- Introduced WelcomeWidget to display a welcome message when no devices are connected, replacing the previous "No devices detected" page in MainWindow.
- Replaced ClickableLabel with ZLabel in ifusewidget.h for improved UI consistency.
- Removed PCFileExplorerWidget and its associated header file, streamlining the codebase.
- Updated PhotoImportDialog to improve server start process and UI elements, including renaming buttons and adjusting labels.
- Modified RealtimeScreenWidget to increase delay before initializing screenshot service for better reliability.
- Enhanced SimpleHttpServer to include a method for retrieving the JSON file name.
- Updated ToolboxWidget to integrate WirelessPhotoImportWidget, allowing for wireless photo imports.
- Added WirelessPhotoImportWidget to facilitate the selection and import of photos, including a tutorial video feature.
- Created a new WelcomeWidget to guide users on connecting their iOS devices.
2025-10-18 22:16:15 +00:00

328 lines
11 KiB
C++

#include "virtuallocationwidget.h"
#include "appcontext.h"
#include "devdiskimagehelper.h"
#include "devdiskmanager.h"
#include "iDescriptor.h"
#include <QDebug>
#include <QDoubleValidator>
#include <QGeoCoordinate>
#include <QGridLayout>
#include <QGroupBox>
#include <QHBoxLayout>
#include <QLabel>
#include <QLineEdit>
#include <QMessageBox>
#include <QPushButton>
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include <QQuickItem>
#include <QQuickView>
#include <QQuickWidget>
#include <QUrl>
#include <QVBoxLayout>
#include <QWidget>
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 = idevice_get_device_version(m_device->device);
unsigned int deviceMajorVersion = (device_version >> 16) & 0xFF;
if (deviceMajorVersion > 16) {
QMessageBox::warning(
this, "Unsupported iOS Version",
"Real-time Screen 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<QQuickItem *>("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<QQuickItem *>("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<QGeoCoordinate>();
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<char *>(
m_latitudeEdit->text().toStdString().c_str()),
const_cast<char *>(
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();
}