diff options
Diffstat (limited to 'src/plugins/android/androidmanifesteditorwidget.cpp')
-rw-r--r-- | src/plugins/android/androidmanifesteditorwidget.cpp | 694 |
1 files changed, 331 insertions, 363 deletions
diff --git a/src/plugins/android/androidmanifesteditorwidget.cpp b/src/plugins/android/androidmanifesteditorwidget.cpp index 87c2218cfe..6efa02e98e 100644 --- a/src/plugins/android/androidmanifesteditorwidget.cpp +++ b/src/plugins/android/androidmanifesteditorwidget.cpp @@ -24,14 +24,16 @@ ****************************************************************************/ #include "androidmanifesteditorwidget.h" +#include "androidmanifesteditoriconcontainerwidget.h" #include "androidmanifesteditor.h" #include "androidconfigurations.h" #include "androidconstants.h" #include "androidmanifestdocument.h" #include "androidmanager.h" +#include "androidservicewidget.h" +#include "splashiconcontainerwidget.h" #include <coreplugin/icore.h> -#include <coreplugin/infobar.h> #include <coreplugin/editormanager/ieditor.h> #include <qtsupport/qtkitinformation.h> @@ -50,6 +52,7 @@ #include <utils/algorithm.h> #include <utils/fileutils.h> +#include <utils/infobar.h> #include <utils/stylehelper.h> #include <utils/utilsicons.h> @@ -67,8 +70,9 @@ #include <QLabel> #include <QLineEdit> #include <QListView> -#include <QLoggingCategory> +#include <QMessageBox> #include <QPushButton> +#include <QRegularExpression> #include <QScrollArea> #include <QSpinBox> #include <QTimer> @@ -77,22 +81,17 @@ #include <algorithm> #include <limits> - -namespace { -static Q_LOGGING_CATEGORY(androidManifestEditorLog, "qtc.android.manifestEditor", QtWarningMsg) -} - using namespace ProjectExplorer; using namespace Android; using namespace Android::Internal; namespace { -const QLatin1String packageNameRegExp("^([a-z]{1}[a-z0-9_]+(\\.[a-zA-Z]{1}[a-zA-Z0-9_]*)*)$"); const char infoBarId[] = "Android.AndroidManifestEditor.InfoBar"; bool checkPackageName(const QString &packageName) { - return QRegExp(packageNameRegExp).exactMatch(packageName); + const QLatin1String packageNameRegExp("^([a-z]{1}[a-z0-9_]+(\\.[a-zA-Z]{1}[a-zA-Z0-9_]*)*)$"); + return QRegularExpression(packageNameRegExp).match(packageName).hasMatch(); } Target *androidTarget(const Utils::FilePath &fileName) @@ -252,54 +251,16 @@ void AndroidManifestEditorWidget::initializePage() m_styleExtractMethod->setItemData(i, styleMethodsMap.at(i).at(1), Qt::ToolTipRole); } - auto iconLayout = new QHBoxLayout(); - - createDPIButton(iconLayout, - applicationGroupBox, - m_masterIconButton, iconSize(LowDPI), - tr("Master icon"), tr("Select master icon.")); - - m_masterIconButton->setIcon(QIcon::fromTheme(QLatin1String("document-open"), Utils::Icons::OPENFILE.icon())); - - iconLayout->addStretch(1); - - QFrame* line = new QFrame(); - line->setFrameShape(QFrame::VLine); - line->setFrameShadow(QFrame::Sunken); - iconLayout->addWidget(line); - - iconLayout->addStretch(1); - - createDPIButton(iconLayout, - applicationGroupBox, - m_lIconButton, iconSize(LowDPI), - tr("Low DPI icon"), tr("Select low DPI icon."), - &m_lIconClearButton, - &m_lIconScaleWarningLabel); - - iconLayout->addStretch(1); - - createDPIButton(iconLayout, - applicationGroupBox, - m_mIconButton, iconSize(MediumDPI), - tr("Medium DPI icon"), tr("Select medium DPI icon."), - &m_mIconClearButton, - &m_mIconScaleWarningLabel); - - iconLayout->addStretch(1); - - createDPIButton(iconLayout, - applicationGroupBox, - m_hIconButton, iconSize(HighDPI), - tr("High DPI icon"), tr("Select high DPI icon."), - &m_hIconClearButton, - &m_hIconScaleWarningLabel); - - iconLayout->addStretch(6); - + m_iconButtons = new AndroidManifestEditorIconContainerWidget(applicationGroupBox, m_textEditorWidget); formLayout->addRow(tr("Application icon:"), new QLabel()); + formLayout->addRow(QString(), m_iconButtons); + + m_splashButtons = new SplashIconContainerWidget(applicationGroupBox, m_textEditorWidget); + formLayout->addRow(tr("Splash screen:"), new QLabel()); + formLayout->addRow(QString(), m_splashButtons); - formLayout->addRow(QString(), iconLayout); + m_services = new AndroidServiceWidget(this); + formLayout->addRow(tr("Android services:"), m_services); applicationGroupBox->setLayout(formLayout); @@ -312,21 +273,14 @@ void AndroidManifestEditorWidget::initializePage() connect(m_styleExtractMethod, QOverload<int>::of(&QComboBox::currentIndexChanged), this, setDirtyFunc); - - connect(m_masterIconButton, &QAbstractButton::clicked, - this, &AndroidManifestEditorWidget::setMasterIcon); - connect(m_lIconButton, &QAbstractButton::clicked, - this, &AndroidManifestEditorWidget::setLDPIIcon); - connect(m_mIconButton, &QAbstractButton::clicked, - this, &AndroidManifestEditorWidget::setMDPIIcon); - connect(m_hIconButton, &QAbstractButton::clicked, - this, &AndroidManifestEditorWidget::setHDPIIcon); - connect(m_lIconClearButton, &QAbstractButton::clicked, - this, &AndroidManifestEditorWidget::clearLDPIIcon); - connect(m_mIconClearButton, &QAbstractButton::clicked, - this, &AndroidManifestEditorWidget::clearMDPIIcon); - connect(m_hIconClearButton, &QAbstractButton::clicked, - this, &AndroidManifestEditorWidget::clearHDPIIcon); + connect(m_services, &AndroidServiceWidget::servicesModified, + this, setDirtyFunc); + connect(m_splashButtons, &SplashIconContainerWidget::splashScreensModified, + this, setDirtyFunc); + connect(m_services, &AndroidServiceWidget::servicesModified, + this, &AndroidManifestEditorWidget::clearInvalidServiceInfo); + connect(m_services, &AndroidServiceWidget::servicesInvalid, + this, &AndroidManifestEditorWidget::setInvalidServiceInfo); } @@ -602,6 +556,14 @@ AndroidManifestEditorWidget::EditorPage AndroidManifestEditorWidget::activePage( return AndroidManifestEditorWidget::EditorPage(currentIndex()); } +bool servicesValid(const QList<AndroidServiceData> &services) +{ + for (auto &&x : services) + if (!x.isValid()) + return false; + return true; +} + bool AndroidManifestEditorWidget::setActivePage(EditorPage page) { EditorPage prevPage = activePage(); @@ -610,6 +572,11 @@ bool AndroidManifestEditorWidget::setActivePage(EditorPage page) return true; if (page == Source) { + if (!servicesValid(m_services->services())) { + QMessageBox::critical(nullptr, tr("Service Definition Invalid"), + tr("Cannot switch to source when there are invalid services.")); + return false; + } syncToEditor(); } else { if (!syncToWidgets()) @@ -630,16 +597,16 @@ bool AndroidManifestEditorWidget::setActivePage(EditorPage page) void AndroidManifestEditorWidget::preSave() { - if (activePage() != Source) + if (activePage() != Source) { + if (!servicesValid(m_services->services())) { + QMessageBox::critical(nullptr, tr("Service Definition Invalid"), + tr("Cannot save when there are invalid services.")); + return; + } syncToEditor(); - - QString baseDir = m_textEditorWidget->textDocument()->filePath().toFileInfo().absolutePath(); - copyIcon(LowDPI, baseDir, m_lIconPath); - copyIcon(MediumDPI, baseDir, m_mIconPath); - copyIcon(HighDPI, baseDir, m_hIconPath); + } // no need to emit changed() since this is called as part of saving - updateInfoBar(); } @@ -754,13 +721,13 @@ void AndroidManifestEditorWidget::updateSdkVersions() void AndroidManifestEditorWidget::updateInfoBar(const QString &errorMessage, int line, int column) { - Core::InfoBar *infoBar = m_textEditorWidget->textDocument()->infoBar(); + Utils::InfoBar *infoBar = m_textEditorWidget->textDocument()->infoBar(); QString text; if (line < 0) text = tr("Could not parse file: \"%1\".").arg(errorMessage); else text = tr("%2: Could not parse file: \"%1\".").arg(errorMessage).arg(line); - Core::InfoBarEntry infoBarEntry(infoBarId, text); + Utils::InfoBarEntry infoBarEntry(infoBarId, text); infoBarEntry.setCustomButtonInfo(tr("Goto error"), [this]() { m_textEditorWidget->gotoLine(m_errorLine, m_errorColumn); }); @@ -774,9 +741,28 @@ void AndroidManifestEditorWidget::updateInfoBar(const QString &errorMessage, int void AndroidManifestEditorWidget::hideInfoBar() { - Core::InfoBar *infoBar = m_textEditorWidget->textDocument()->infoBar(); + Utils::InfoBar *infoBar = m_textEditorWidget->textDocument()->infoBar(); infoBar->removeInfo(infoBarId); - m_timerParseCheck.stop(); + m_timerParseCheck.stop(); +} + +static const char kServicesInvalid[] = "AndroidServiceDefinitionInvalid"; + +void AndroidManifestEditorWidget::setInvalidServiceInfo() +{ + Utils::Id id(kServicesInvalid); + if (m_textEditorWidget->textDocument()->infoBar()->containsInfo(id)) + return; + Utils::InfoBarEntry info(id, + tr("Services invalid. " + "Manifest cannot be saved. Correct the service definitions before saving.")); + m_textEditorWidget->textDocument()->infoBar()->addInfo(info); + +} + +void AndroidManifestEditorWidget::clearInvalidServiceInfo() +{ + m_textEditorWidget->textDocument()->infoBar()->removeInfo(Utils::Id(kServicesInvalid)); } void setApiLevel(QComboBox *box, const QDomElement &element, const QString &attribute) @@ -820,33 +806,66 @@ void AndroidManifestEditorWidget::syncToWidgets(const QDomDocument &doc) QDomElement activityElem = applicationElement.firstChildElement(QLatin1String("activity")); m_activityNameLineEdit->setText(activityElem.attribute(QLatin1String("android:label"))); - QDomElement metadataElem = activityElem.firstChildElement(QLatin1String("meta-data")); + QString appIconValue = applicationElement.attribute(QLatin1String("android:icon")); + if (!appIconValue.isEmpty()) { + QLatin1String drawable = QLatin1String("@drawable/"); + if (appIconValue.startsWith(drawable)) { + QString appIconName = appIconValue.mid(drawable.size()); + m_iconButtons->setIconFileName(appIconName); + } + } - const int parseItemsCount = 2; - int counter = 0; + QDomElement metadataElem = activityElem.firstChildElement(QLatin1String("meta-data")); + enum ActivityParseGuard {none = 0, libName = 1, styleExtract = 2, stickySplash = 4, splashImages = 8, done = 16}; + int activityParseGuard = ActivityParseGuard::none; + enum SplashImageParseGuard {splashNone = 0, splash = 1, portraitSplash = 2, landscapeSplash = 4, splashDone = 8}; + int splashParseGuard = SplashImageParseGuard::splashNone; while (!metadataElem.isNull()) { - if (metadataElem.attribute(QLatin1String("android:name")) == QLatin1String("android.app.lib_name")) { + if (metadataElem.attribute(QLatin1String("android:name")) == QLatin1String("android.app.lib_name") + && !(activityParseGuard & ActivityParseGuard::libName)) { m_targetLineEdit->setEditText(metadataElem.attribute(QLatin1String("android:value"))); - ++counter; + activityParseGuard |= ActivityParseGuard::libName; } else if (metadataElem.attribute(QLatin1String("android:name")) - == QLatin1String("android.app.extract_android_style")) { + == QLatin1String("android.app.extract_android_style") + && !(activityParseGuard & ActivityParseGuard::styleExtract)) { m_styleExtractMethod->setCurrentText( metadataElem.attribute(QLatin1String("android:value"))); - ++counter; + activityParseGuard |= ActivityParseGuard::styleExtract; + } else if (metadataElem.attribute(QLatin1String("android:name")) + == QLatin1String("android.app.splash_screen_sticky") + && !(activityParseGuard & ActivityParseGuard::stickySplash)) { + QString sticky = metadataElem.attribute(QLatin1String("android:value")); + m_splashButtons->setSticky(sticky == QLatin1String("true")); + activityParseGuard |= ActivityParseGuard::stickySplash; + } else if (metadataElem.attribute(QLatin1String("android:name")) + .startsWith(QLatin1String("android.app.splash_screen_drawable")) + && !(activityParseGuard & ActivityParseGuard::splashImages) + && !(splashParseGuard & SplashImageParseGuard::splashDone)) { + QString attrName = metadataElem.attribute(QLatin1String("android:name")); + QLatin1String drawable = QLatin1String("@drawable/"); + QString splashImageValue = metadataElem.attribute(QLatin1String("android:resource")); + QString splashImageName; + if (splashImageValue.startsWith(drawable)) { + splashImageName = splashImageValue.mid(drawable.size()); + } + if (attrName == QLatin1String("android.app.splash_screen_drawable")) { + m_splashButtons->setImageFileName(splashImageName); + splashParseGuard |= SplashImageParseGuard::splash; + } else if (attrName == QLatin1String("android.app.splash_screen_drawable_portrait")) { + m_splashButtons->setPortraitImageFileName(splashImageName); + splashParseGuard |= SplashImageParseGuard::portraitSplash; + } else if (attrName == QLatin1String("android.app.splash_screen_drawable_landscape")) { + m_splashButtons->setLandscapeImageFileName(splashImageName); + splashParseGuard |= SplashImageParseGuard::landscapeSplash; + } + if (splashParseGuard & SplashImageParseGuard::splashDone) + activityParseGuard |= ActivityParseGuard::splashImages; } - - if (counter == parseItemsCount) + if (activityParseGuard == ActivityParseGuard::done) break; metadataElem = metadataElem.nextSiblingElement(QLatin1String("meta-data")); } - copyIcon(LowDPI, baseDir, baseDir + iconPath(LowDPI)); - copyIcon(MediumDPI, baseDir, baseDir + iconPath(MediumDPI)); - copyIcon(HighDPI, baseDir, baseDir + iconPath(HighDPI)); - m_lIconButton->setIcon(icon(baseDir, LowDPI)); - m_mIconButton->setIcon(icon(baseDir, MediumDPI)); - m_hIconButton->setIcon(icon(baseDir, HighDPI)); - disconnect(m_defaultPermissonsCheckBox, &QCheckBox::stateChanged, this, &AndroidManifestEditorWidget::defaultPermissionOrFeatureCheckBoxClicked); disconnect(m_defaultFeaturesCheckBox, &QCheckBox::stateChanged, @@ -886,6 +905,36 @@ void AndroidManifestEditorWidget::syncToWidgets(const QDomDocument &doc) m_permissionsModel->setPermissions(permissions); updateAddRemovePermissionButtons(); + QList<AndroidServiceData> services; + QDomElement serviceElem = applicationElement.firstChildElement(QLatin1String("service")); + while (!serviceElem.isNull()) { + AndroidServiceData service; + service.setClassName(serviceElem.attribute(QLatin1String("android:name"))); + QString process = serviceElem.attribute(QLatin1String("android:process")); + service.setRunInExternalProcess(!process.isEmpty()); + service.setExternalProcessName(process); + QDomElement serviceMetadataElem = serviceElem.firstChildElement(QLatin1String("meta-data")); + while (!serviceMetadataElem.isNull()) { + QString metadataName = serviceMetadataElem.attribute(QLatin1String("android:name")); + if (metadataName == QLatin1String("android.app.lib_name")) { + QString metadataValue = serviceMetadataElem.attribute(QLatin1String("android:value")); + service.setRunInExternalLibrary(metadataValue != QLatin1String("-- %%INSERT_APP_LIB_NAME%% --")); + service.setExternalLibraryName(metadataValue); + } + else if (metadataName == QLatin1String("android.app.arguments")) { + QString metadataValue = serviceMetadataElem.attribute(QLatin1String("android:value")); + service.setServiceArguments(metadataValue); + } + serviceMetadataElem = serviceMetadataElem.nextSiblingElement(QLatin1String("meta-data")); + } + services << service; + serviceElem = serviceElem.nextSiblingElement(QLatin1String("service")); + } + m_services->setServices(services); + + m_iconButtons->loadIcons(); + m_splashButtons->loadImages(); + m_stayClean = false; m_dirty = false; } @@ -1048,12 +1097,10 @@ void AndroidManifestEditorWidget::parseApplication(QXmlStreamReader &reader, QXm QStringList keys = {QLatin1String("android:label")}; QStringList values = {m_appNameLineEdit->text()}; QStringList remove; - bool ensureIconAttribute = !m_lIconPath.isEmpty() - || !m_mIconPath.isEmpty() - || !m_hIconPath.isEmpty(); + bool ensureIconAttribute = m_iconButtons->hasIcons(); if (ensureIconAttribute) { keys << QLatin1String("android:icon"); - values << QLatin1String("@drawable/icon"); + values << (QLatin1String("@drawable/") + m_iconButtons->iconFileName()); } else remove << QLatin1String("android:icon"); @@ -1064,13 +1111,19 @@ void AndroidManifestEditorWidget::parseApplication(QXmlStreamReader &reader, QXm while (!reader.atEnd()) { if (reader.isEndElement()) { + parseNewServices(writer); writer.writeCurrentToken(reader); + m_services->servicesSaved(); return; } else if (reader.isStartElement()) { if (reader.name() == QLatin1String("activity")) parseActivity(reader, writer); + else if (reader.name() == QLatin1String("service")) + parseService(reader, writer); else parseUnknownElement(reader, writer); + } else if (reader.isWhitespace()) { + /* no copying of whitespace */ } else { writer.writeCurrentToken(reader); } @@ -1079,6 +1132,164 @@ void AndroidManifestEditorWidget::parseApplication(QXmlStreamReader &reader, QXm } } +static void writeMetadataElement(const char *name, + const char *attributeName, + const QString &value, + QXmlStreamWriter &writer) +{ + writer.writeStartElement(QLatin1String("meta-data")); + writer.writeAttribute(QLatin1String("android:name"), QLatin1String(name)); + writer.writeAttribute(QLatin1String(attributeName), value); + writer.writeEndElement(); + +} + +void AndroidManifestEditorWidget::parseSplashScreen(QXmlStreamWriter &writer) +{ + if (m_splashButtons->hasImages()) + writeMetadataElement("android.app.splash_screen_drawable", + "android:resource", QLatin1String("@drawable/") + m_splashButtons->imageFileName(), + writer); + if (m_splashButtons->hasPortraitImages()) + writeMetadataElement("android.app.splash_screen_drawable_portrait", + "android:resource", QLatin1String("@drawable/") + m_splashButtons->portraitImageFileName(), + writer); + if (m_splashButtons->hasLandscapeImages()) + writeMetadataElement("android.app.splash_screen_drawable_landscape", + "android:resource", QLatin1String("@drawable/") + m_splashButtons->landscapeImageFileName(), + writer); + if (m_splashButtons->isSticky()) + writeMetadataElement("android.app.splash_screen_sticky", + "android:value", "true", + writer); +} + +static int findService(const QString &name, const QList<AndroidServiceData> &data) +{ + for (int i = 0; i < data.size(); ++i) { + if (data[i].className() == name) + return i; + } + return -1; +} + +static void writeMetadataElement(const char *name, + const char *attributeName, + const char *value, + QXmlStreamWriter &writer) +{ + writer.writeStartElement(QLatin1String("meta-data")); + writer.writeAttribute(QLatin1String("android:name"), QLatin1String(name)); + writer.writeAttribute(QLatin1String(attributeName), QLatin1String(value)); + writer.writeEndElement(); + +} + +static void addServiceArgumentsAndLibName(const AndroidServiceData &service, QXmlStreamWriter &writer) +{ + if (!service.isRunInExternalLibrary() && !service.serviceArguments().isEmpty()) + writeMetadataElement("android.app.arguments", "android:value", service.serviceArguments(), writer); + if (service.isRunInExternalLibrary() && !service.externalLibraryName().isEmpty()) + writeMetadataElement("android.app.lib_name", "android:value", service.externalLibraryName(), writer); + else + writeMetadataElement("android.app.lib_name", "android:value", "-- %%INSERT_APP_LIB_NAME%% --", writer); +} + +static void addServiceMetadata(QXmlStreamWriter &writer) +{ + writeMetadataElement("android.app.qt_sources_resource_id", "android:resource", "@array/qt_sources", writer); + writeMetadataElement("android.app.repository", "android:value", "default", writer); + writeMetadataElement("android.app.qt_libs_resource_id", "android:resource", "@array/qt_libs", writer); + writeMetadataElement("android.app.bundled_libs_resource_id", "android:resource", "@array/bundled_libs", writer); + writeMetadataElement("android.app.bundle_local_qt_libs", "android:value", "-- %%BUNDLE_LOCAL_QT_LIBS%% --", writer); + writeMetadataElement("android.app.use_local_qt_libs", "android:value", "-- %%USE_LOCAL_QT_LIBS%% --", writer); + writeMetadataElement("android.app.libs_prefix", "android:value", "/data/local/tmp/qt/", writer); + writeMetadataElement("android.app.load_local_libs_resource_id", "android:resource", "@array/load_local_libs", writer); + writeMetadataElement("android.app.load_local_jars", "android:value", "-- %%INSERT_LOCAL_JARS%% --", writer); + writeMetadataElement("android.app.static_init_classes", "android:value", "-- %%INSERT_INIT_CLASSES%% --", writer); +} + +void AndroidManifestEditorWidget::parseService(QXmlStreamReader &reader, QXmlStreamWriter &writer) +{ + Q_ASSERT(reader.isStartElement()); + const auto &services = m_services->services(); + QString serviceName = reader.attributes().value(QLatin1String("android:name")).toString(); + int serviceIndex = findService(serviceName, services); + const AndroidServiceData* serviceFound = (serviceIndex >= 0) ? &services[serviceIndex] : nullptr; + if (serviceFound && serviceFound->isValid()) { + writer.writeStartElement(reader.name().toString()); + writer.writeAttribute(QLatin1String("android:name"), serviceFound->className()); + if (serviceFound->isRunInExternalProcess()) + writer.writeAttribute(QLatin1String("android:process"), serviceFound->externalProcessName()); + } + + reader.readNext(); + + bool bundleTagFound = false; + + while (!reader.atEnd()) { + if (reader.isEndElement()) { + if (serviceFound && serviceFound->isValid()) { + addServiceArgumentsAndLibName(*serviceFound, writer); + if (serviceFound->isRunInExternalProcess() && !bundleTagFound) + addServiceMetadata(writer); + writer.writeCurrentToken(reader); + } + return; + } else if (reader.isStartElement()) { + if (serviceFound && !serviceFound->isValid()) + parseUnknownElement(reader, writer, true); + else if (reader.name() == QLatin1String("meta-data")) { + QString metaTagName = reader.attributes().value(QLatin1String("android:name")).toString(); + if (serviceFound) { + if (metaTagName == QLatin1String("android.app.bundle_local_qt_libs")) + bundleTagFound = true; + if (metaTagName == QLatin1String("android.app.arguments")) + parseUnknownElement(reader, writer, true); + else if (metaTagName == QLatin1String("android.app.lib_name")) + parseUnknownElement(reader, writer, true); + else if (serviceFound->isRunInExternalProcess() + || metaTagName == QLatin1String("android.app.background_running")) + parseUnknownElement(reader, writer); + else + parseUnknownElement(reader, writer, true); + } else + parseUnknownElement(reader, writer, true); + } else + parseUnknownElement(reader, writer, true); + } else if (reader.isWhitespace()) { + /* no copying of whitespace */ + } else { + if (serviceFound) + writer.writeCurrentToken(reader); + } + reader.readNext(); + } +} + +void AndroidManifestEditorWidget::parseNewServices(QXmlStreamWriter &writer) +{ + const auto &services = m_services->services(); + for (const auto &x : services) { + if (x.isNewService() && x.isValid()) { + writer.writeStartElement(QLatin1String("service")); + writer.writeAttribute(QLatin1String("android:name"), x.className()); + if (x.isRunInExternalProcess()) { + writer.writeAttribute(QLatin1String("android:process"), + x.externalProcessName()); + } + addServiceArgumentsAndLibName(x, writer); + if (x.isRunInExternalProcess()) + addServiceMetadata(writer); + writer.writeStartElement(QLatin1String("meta-data")); + writer.writeAttribute(QLatin1String("android:name"), QLatin1String("android.app.background_running")); + writer.writeAttribute(QLatin1String("android:value"), QLatin1String("true")); + writer.writeEndElement(); + writer.writeEndElement(); + } + } +} + void AndroidManifestEditorWidget::parseActivity(QXmlStreamReader &reader, QXmlStreamWriter &writer) { Q_ASSERT(reader.isStartElement()); @@ -1096,6 +1307,7 @@ void AndroidManifestEditorWidget::parseActivity(QXmlStreamReader &reader, QXmlSt while (!reader.atEnd()) { if (reader.isEndElement()) { + parseSplashScreen(writer); if (!found) { writer.writeEmptyElement(QLatin1String("meta-data")); writer.writeAttribute(QLatin1String("android:name"), @@ -1106,10 +1318,16 @@ void AndroidManifestEditorWidget::parseActivity(QXmlStreamReader &reader, QXmlSt writer.writeCurrentToken(reader); return; } else if (reader.isStartElement()) { - if (reader.name() == QLatin1String("meta-data")) - found = parseMetaData(reader, writer) || found; // ORDER MATTERS - else + if (reader.name() == QLatin1String("meta-data")) { + QString metaTagName = reader.attributes().value(QLatin1String("android:name")).toString(); + if (metaTagName.startsWith(QLatin1String("android.app.splash_screen"))) + parseUnknownElement(reader, writer, true); + else + found = parseMetaData(reader, writer) || found; // ORDER MATTERS + } else parseUnknownElement(reader, writer); + } else if (reader.isWhitespace()) { + /* no copying of whitespace */ } else { writer.writeCurrentToken(reader); } @@ -1256,273 +1474,29 @@ QString AndroidManifestEditorWidget::parseComment(QXmlStreamReader &reader, QXml return commentText; } -void AndroidManifestEditorWidget::parseUnknownElement(QXmlStreamReader &reader, QXmlStreamWriter &writer) +void AndroidManifestEditorWidget::parseUnknownElement(QXmlStreamReader &reader, QXmlStreamWriter &writer, + bool ignore) { Q_ASSERT(reader.isStartElement()); - writer.writeCurrentToken(reader); + if (!ignore) + writer.writeCurrentToken(reader); reader.readNext(); while (!reader.atEnd()) { if (reader.isEndElement()) { - writer.writeCurrentToken(reader); + if (!ignore) + writer.writeCurrentToken(reader); return; } else if (reader.isStartElement()) { - parseUnknownElement(reader, writer); + parseUnknownElement(reader, writer, ignore); } else { - writer.writeCurrentToken(reader); + if (!ignore) + writer.writeCurrentToken(reader); } reader.readNext(); } } -QString AndroidManifestEditorWidget::iconPath(IconDPI dpi) -{ - switch (dpi) { - case HighDPI: - return QString("/res/drawable-hdpi/icon.png"); - case MediumDPI: - return QString("/res/drawable-mdpi/icon.png"); - case LowDPI: - return QString("/res/drawable-ldpi/icon.png"); - } - return {}; -} - -QSize AndroidManifestEditorWidget::iconSize(IconDPI dpi) -{ - switch (dpi) { - case HighDPI: - return QSize(72, 72); - case MediumDPI: - return QSize(48, 48); - case LowDPI: - return QSize(32, 32); - } - return QSize(72, 72); -} - -void AndroidManifestEditorWidget::updateIconPath(const QString &newPath, IconDPI dpi) -{ - switch (dpi) { - case HighDPI: - m_hIconPath = newPath; - break; - case MediumDPI: - m_mIconPath = newPath; - break; - case LowDPI: - m_lIconPath = newPath; - break; - } -} - -QIcon AndroidManifestEditorWidget::icon(const QString &baseDir, IconDPI dpi) -{ - - if (dpi == HighDPI && !m_hIconPath.isEmpty()) - return QIcon(m_hIconPath); - - if (dpi == MediumDPI && !m_mIconPath.isEmpty()) - return QIcon(m_mIconPath); - - if (dpi == LowDPI && !m_lIconPath.isEmpty()) - return QIcon(m_lIconPath); - - QString fileName = baseDir + iconPath(dpi); - if (fileName.isEmpty()) - return QIcon(); - return QIcon(fileName); -} - -void AndroidManifestEditorWidget::copyIcon(IconDPI dpi, const QString &baseDir, const QString &filePath) -{ - const QString targetPath = baseDir + iconPath(dpi); - if (targetPath.isEmpty()) { - qCDebug(androidManifestEditorLog) << "Icon target path empty, cannot copy icon."; - return; - } - QFileInfo targetFile(targetPath); - if (filePath != targetPath) - removeIcon(dpi, baseDir); - QImage original(filePath); - if (!targetPath.isEmpty() && !original.isNull()) { - if (filePath != targetPath) { - QDir dir; - dir.mkpath(QFileInfo(targetPath).absolutePath()); - QSize targetSize = iconSize(dpi); - QImage scaled = original.scaled(targetSize.width(), targetSize.height(), - Qt::KeepAspectRatio, Qt::SmoothTransformation); - toggleIconScaleWarning(dpi, scaled.width() > original.width() || scaled.height() > original.height()); - scaled.save(targetPath); - } - updateIconPath(targetPath, dpi); - } -} - -void AndroidManifestEditorWidget::removeIcon(IconDPI dpi, const QString &baseDir) -{ - const QString targetPath = baseDir + iconPath(dpi); - if (targetPath.isEmpty()) { - qCDebug(androidManifestEditorLog) << "Icon target path empty, cannot remove icon."; - return; - } - QFileInfo targetFile(targetPath); - if (targetFile.exists()) { - QDir rmRf(targetFile.absoluteDir()); - rmRf.removeRecursively(); - } - toggleIconScaleWarning(dpi, false); -} - -void AndroidManifestEditorWidget::toggleIconScaleWarning(IconDPI dpi, bool visible) -{ - switch (dpi) { - case HighDPI: - m_hIconScaleWarningLabel->setVisible(visible); - break; - case MediumDPI: - m_mIconScaleWarningLabel->setVisible(visible); - break; - case LowDPI: - m_lIconScaleWarningLabel->setVisible(visible); - break; - } -} - -const auto fileDialogIconFiles = QWidget::tr("Images (*.png *.jpg *.webp *.svg)"); - -void AndroidManifestEditorWidget::setMasterIcon() -{ - QString file = QFileDialog::getOpenFileName(this, tr("Choose Master Icon"), QDir::homePath(), fileDialogIconFiles); - if (file.isEmpty()) - return; - QString baseDir = m_textEditorWidget->textDocument()->filePath().toFileInfo().absolutePath(); - copyIcon(LowDPI, baseDir, file); - copyIcon(MediumDPI, baseDir, file); - copyIcon(HighDPI, baseDir, file); - m_lIconButton->setIcon(icon(baseDir, LowDPI)); - m_mIconButton->setIcon(icon(baseDir, MediumDPI)); - m_hIconButton->setIcon(icon(baseDir, HighDPI)); - syncToEditor(); -} - -void AndroidManifestEditorWidget::setLDPIIcon() -{ - QString file = QFileDialog::getOpenFileName(this, tr("Choose Low DPI Icon"), QDir::homePath(), fileDialogIconFiles); - if (file.isEmpty()) - return; - m_lIconPath = file; - QString baseDir = m_textEditorWidget->textDocument()->filePath().toFileInfo().absolutePath(); - copyIcon(LowDPI, baseDir, m_lIconPath); - m_lIconButton->setIcon(icon(baseDir, LowDPI)); - syncToEditor(); -} - -void AndroidManifestEditorWidget::setMDPIIcon() -{ - QString file = QFileDialog::getOpenFileName(this, tr("Choose Medium DPI Icon"), QDir::homePath(), fileDialogIconFiles); - if (file.isEmpty()) - return; - m_mIconPath = file; - QString baseDir = m_textEditorWidget->textDocument()->filePath().toFileInfo().absolutePath(); - copyIcon(MediumDPI, baseDir, m_mIconPath); - m_mIconButton->setIcon(icon(baseDir, MediumDPI)); - syncToEditor(); -} - -void AndroidManifestEditorWidget::setHDPIIcon() -{ - QString file = QFileDialog::getOpenFileName(this, tr("Choose High DPI Icon"), QDir::homePath(), fileDialogIconFiles); - if (file.isEmpty()) - return; - m_hIconPath = file; - QString baseDir = m_textEditorWidget->textDocument()->filePath().toFileInfo().absolutePath(); - copyIcon(HighDPI, baseDir, m_hIconPath); - m_hIconButton->setIcon(icon(baseDir, HighDPI)); - syncToEditor(); -} - -void AndroidManifestEditorWidget::clearLDPIIcon() -{ - m_lIconPath.clear(); - m_lIconButton->setIcon(QIcon()); - QString baseDir = m_textEditorWidget->textDocument()->filePath().toFileInfo().absolutePath(); - removeIcon(LowDPI, baseDir); - syncToEditor(); -} - -void AndroidManifestEditorWidget::clearMDPIIcon() -{ - m_mIconPath.clear(); - m_mIconButton->setIcon(QIcon()); - QString baseDir = m_textEditorWidget->textDocument()->filePath().toFileInfo().absolutePath(); - removeIcon(MediumDPI, baseDir); - syncToEditor(); -} - -void AndroidManifestEditorWidget::clearHDPIIcon() -{ - m_hIconPath.clear(); - m_hIconButton->setIcon(QIcon()); - QString baseDir = m_textEditorWidget->textDocument()->filePath().toFileInfo().absolutePath(); - removeIcon(HighDPI, baseDir); - syncToEditor(); -} - -void AndroidManifestEditorWidget::createDPIButton(QHBoxLayout *layout, - QWidget *parent, - QToolButton *&button, - const QSize &buttonSize, - const QString &title, - const QString &tooltip, - QToolButton **clearButton, - QLabel **scaleWarningLabel) -{ - auto iconLayout = new QVBoxLayout(); - auto iconTitle = new QLabel(title, parent); - auto iconButtonLayout = new QGridLayout(); - button = new QToolButton(parent); - button->setMinimumSize(buttonSize); - button->setMaximumSize(buttonSize); - button->setToolTip(tooltip); - button->setIconSize(buttonSize); - QSize clearAndWarningSize(16, 16); - if (clearButton) { - *clearButton = new QToolButton(parent); - (*clearButton)->setMinimumSize(clearAndWarningSize); - (*clearButton)->setMaximumSize(clearAndWarningSize); - (*clearButton)->setIcon(Utils::Icons::CLOSE_FOREGROUND.icon()); - } - if (scaleWarningLabel) { - *scaleWarningLabel = new QLabel(parent); - (*scaleWarningLabel)->setMinimumSize(clearAndWarningSize); - (*scaleWarningLabel)->setMaximumSize(clearAndWarningSize); - (*scaleWarningLabel)->setPixmap(Utils::Icons::WARNING.icon().pixmap(clearAndWarningSize)); - (*scaleWarningLabel)->setToolTip(tr("Icon scaled up")); - (*scaleWarningLabel)->setVisible(false); - } - auto label = new QLabel(tr("Click to select"), parent); - iconLayout->addWidget(iconTitle); - iconLayout->setAlignment(iconTitle, Qt::AlignHCenter); - iconButtonLayout->setColumnMinimumWidth(0, 16); - iconButtonLayout->addWidget(button, 0, 1, 1, 3); - iconButtonLayout->setAlignment(button, Qt::AlignVCenter); - if (clearButton) { - iconButtonLayout->addWidget(*clearButton, 0, 4, 1, 1); - iconButtonLayout->setAlignment(*clearButton, Qt::AlignTop); - } - if (scaleWarningLabel) { - iconButtonLayout->addWidget(*scaleWarningLabel, 0, 0, 1, 1); - iconButtonLayout->setAlignment(*scaleWarningLabel, Qt::AlignTop); - } - iconLayout->addLayout(iconButtonLayout); - iconLayout->setAlignment(iconButtonLayout, Qt::AlignHCenter); - iconLayout->addWidget(label); - iconLayout->setAlignment(label, Qt::AlignHCenter); - - layout->addLayout(iconLayout); -} - void AndroidManifestEditorWidget::defaultPermissionOrFeatureCheckBoxClicked() { setDirty(true); @@ -1660,9 +1634,3 @@ AndroidManifestTextEditorWidget::AndroidManifestTextEditorWidget(AndroidManifest m_context->setContext(Core::Context(Constants::ANDROID_MANIFEST_EDITOR_CONTEXT)); Core::ICore::addContextObject(m_context); } - -AndroidManifestTextEditorWidget::~AndroidManifestTextEditorWidget() -{ - Core::ICore::removeContextObject(m_context); -} - |