// Copyright (C) 2020 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 #include "splashscreencontainerwidget.h" #include "androidtr.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace Utils; namespace Android::Internal { const char extraExtraExtraHighDpiImagePath[] = "/res/drawable-xxxhdpi/"; const char extraExtraHighDpiImagePath[] = "/res/drawable-xxhdpi/"; const char extraHighDpiImagePath[] = "/res/drawable-xhdpi/"; const char highDpiImagePath[] = "/res/drawable-hdpi/"; const char mediumDpiImagePath[] = "/res/drawable-mdpi/"; const char lowDpiImagePath[] = "/res/drawable-ldpi/"; const char splashscreenName[] = "splashscreen"; const char splashscreenPortraitName[] = "splashscreen_port"; const char splashscreenLandscapeName[] = "splashscreen_land"; const char splashscreenFileName[] = "logo"; const char splashscreenPortraitFileName[] = "logo_port"; const char splashscreenLandscapeFileName[] = "logo_land"; const char imageSuffix[] = ".png"; const QString fileDialogImageFiles = Tr::tr("Images (*.png *.jpg *.jpeg)"); // TODO: Implement a centralized images filter string const QSize lowDpiImageSize{200, 320}; const QSize mediumDpiImageSize{320, 480}; const QSize highDpiImageSize{480, 800}; const QSize extraHighDpiImageSize{720, 1280}; const QSize extraExtraHighDpiImageSize{960, 1600}; const QSize extraExtraExtraHighDpiImageSize{1280, 1920}; const QSize displaySize{48, 72}; const QSize landscapeDisplaySize{72, 48}; // https://developer.android.com/training/multiscreen/screendensities#TaskProvideAltBmp const int extraExtraExtraHighDpiScalingRatio = 16; const int extraExtraHighDpiScalingRatio = 12; const int extraHighDpiScalingRatio = 8; const int highDpiScalingRatio = 6; const int mediumDpiScalingRatio = 4; const int lowDpiScalingRatio = 3; static FilePath manifestDir(TextEditor::TextEditorWidget *textEditorWidget) { // Get the manifest file's directory from its filepath. return textEditorWidget->textDocument()->filePath().absolutePath(); } static Q_LOGGING_CATEGORY(androidManifestEditorLog, "qtc.android.splashScreenWidget", QtWarningMsg); class SplashScreenWidget : public QWidget { Q_OBJECT class SplashScreenButton : public QToolButton { public: explicit SplashScreenButton(SplashScreenWidget *parent) : QToolButton(parent), m_parentWidget(parent) {} private: void paintEvent(QPaintEvent *event) override { Q_UNUSED(event) QPainter painter(this); painter.setPen(QPen(Qt::gray, 1)); painter.setBrush(QBrush(m_parentWidget->m_backgroundColor)); painter.drawRect(0, 0, width()-1, height()-1); if (!m_parentWidget->m_image.isNull()) painter.drawImage(m_parentWidget->m_imagePosition, m_parentWidget->m_image); } SplashScreenWidget *m_parentWidget; }; public: SplashScreenWidget(QWidget *parent, const QSize &size, const QSize &screenSize, const QString &title, const QString &tooltip, const QString &imagePath, int scalingRatio, int maxScalingRatio, TextEditor::TextEditorWidget *textEditorWidget = nullptr); bool hasImage() const; void clearImage(); void setBackgroundColor(const QColor &backgroundColor); void showImageFullScreen(bool fullScreen); void setImageFromPath(const FilePath &imagePath, bool resize = true); void setImageFileName(const QString &imageFileName); void loadImage(); signals: void imageChanged(); private: void selectImage(); void removeImage(); void setScaleWarningLabelVisible(bool visible); private: TextEditor::TextEditorWidget *m_textEditorWidget = nullptr; QLabel *m_scaleWarningLabel = nullptr; SplashScreenButton *m_splashScreenButton = nullptr; int m_scalingRatio, m_maxScalingRatio; QColor m_backgroundColor = Qt::white; QImage m_image; QPoint m_imagePosition; QString m_imagePath; QString m_imageFileName; QString m_imageSelectionText; QSize m_screenSize; bool m_showImageFullScreen = false; }; SplashScreenWidget::SplashScreenWidget( QWidget *parent, const QSize &size, const QSize &screenSize, const QString &title, const QString &tooltip, const QString &imagePath, int scalingRatio, int maxScalingRatio, TextEditor::TextEditorWidget *textEditorWidget) : QWidget(parent), m_textEditorWidget(textEditorWidget), m_scalingRatio(scalingRatio), m_maxScalingRatio(maxScalingRatio), m_imagePath(imagePath), m_screenSize(screenSize) { auto splashLayout = new QVBoxLayout(this); auto splashTitle = new QLabel(title, this); auto splashButtonLayout = new QGridLayout(); m_splashScreenButton = new SplashScreenButton(this); m_splashScreenButton->setMinimumSize(size); m_splashScreenButton->setMaximumSize(size); m_splashScreenButton->setToolTip(tooltip); QSize clearAndWarningSize(16, 16); QToolButton *clearButton = nullptr; if (textEditorWidget) { clearButton = new QToolButton(this); clearButton->setMinimumSize(clearAndWarningSize); clearButton->setMaximumSize(clearAndWarningSize); clearButton->setIcon(Utils::Icons::CLOSE_FOREGROUND.icon()); } if (textEditorWidget) { m_scaleWarningLabel = new QLabel(this); m_scaleWarningLabel->setMinimumSize(clearAndWarningSize); m_scaleWarningLabel->setMaximumSize(clearAndWarningSize); m_scaleWarningLabel->setPixmap(Utils::Icons::WARNING.icon().pixmap(clearAndWarningSize)); m_scaleWarningLabel->setToolTip(Tr::tr("Icon scaled up.")); m_scaleWarningLabel->setVisible(false); } auto label = new QLabel(Tr::tr("Click to select..."), parent); splashLayout->addWidget(splashTitle); splashLayout->setAlignment(splashTitle, Qt::AlignHCenter); splashButtonLayout->setColumnMinimumWidth(0, 16); splashButtonLayout->addWidget(m_splashScreenButton, 0, 1, 1, 3); splashButtonLayout->setAlignment(m_splashScreenButton, Qt::AlignVCenter); if (textEditorWidget) { splashButtonLayout->addWidget(clearButton, 0, 4, 1, 1); splashButtonLayout->setAlignment(clearButton, Qt::AlignTop); } if (textEditorWidget) { splashButtonLayout->addWidget(m_scaleWarningLabel, 0, 0, 1, 1); splashButtonLayout->setAlignment(m_scaleWarningLabel, Qt::AlignTop); } splashLayout->addLayout(splashButtonLayout); splashLayout->setAlignment(splashButtonLayout, Qt::AlignHCenter); splashLayout->addWidget(label); splashLayout->setAlignment(label, Qt::AlignHCenter); this->setLayout(splashLayout); connect(m_splashScreenButton, &QAbstractButton::clicked, this, &SplashScreenWidget::selectImage); if (clearButton) connect(clearButton, &QAbstractButton::clicked, this, &SplashScreenWidget::removeImage); m_imageSelectionText = tooltip; } void SplashScreenWidget::setBackgroundColor(const QColor &backgroundColor) { m_backgroundColor = backgroundColor; m_splashScreenButton->update(); emit imageChanged(); } void SplashScreenWidget::showImageFullScreen(bool fullScreen) { m_showImageFullScreen = fullScreen; loadImage(); } void SplashScreenWidget::setImageFromPath(const FilePath &imagePath, bool resize) { if (!m_textEditorWidget) return; const FilePath baseDir = manifestDir(m_textEditorWidget); const FilePath targetPath = baseDir / m_imagePath / m_imageFileName; if (targetPath.isEmpty()) { qCDebug(androidManifestEditorLog) << "Image target path is empty, cannot set image."; return; } QImage image = QImage(imagePath.toFSPathString()); if (image.isNull()) { qCDebug(androidManifestEditorLog) << "Image '" << imagePath << "' not found or invalid format."; return; } if (!targetPath.absolutePath().ensureWritableDir()) { qCDebug(androidManifestEditorLog) << "Cannot create image target path."; return; } if (resize == true && m_scalingRatio < m_maxScalingRatio) { const QSize size = QSize((float(image.width()) / float(m_maxScalingRatio)) * float(m_scalingRatio), (float(image.height()) / float(m_maxScalingRatio)) * float(m_scalingRatio)); image = image.scaled(size); } QFile file(targetPath.toFSPathString()); if (file.open(QIODevice::WriteOnly | QIODevice::Truncate)) { image.save(&file, "PNG"); file.close(); loadImage(); emit imageChanged(); } else { qCDebug(androidManifestEditorLog).noquote() << "Cannot save image." << targetPath.toUserOutput(); } } void SplashScreenWidget::selectImage() { const FilePath file = FileUtils::getOpenFilePath(m_imageSelectionText, FileUtils::homePath(), QStringLiteral("%1 (*.png *.jpg *.jpeg)") .arg(Tr::tr("Images"))); if (file.isEmpty()) return; setImageFromPath(file, false); emit imageChanged(); } void SplashScreenWidget::removeImage() { const FilePath baseDir = manifestDir(m_textEditorWidget); const FilePath targetPath = baseDir / m_imagePath / m_imageFileName; if (targetPath.isEmpty()) { qCDebug(androidManifestEditorLog) << "Image target path empty, cannot remove image."; return; } targetPath.removeFile(); m_image = QImage(); m_splashScreenButton->update(); setScaleWarningLabelVisible(false); } void SplashScreenWidget::clearImage() { removeImage(); emit imageChanged(); } void SplashScreenWidget::loadImage() { if (m_imageFileName.isEmpty()) { qCDebug(androidManifestEditorLog) << "Image name not set, cannot load image."; return; } const FilePath baseDir = manifestDir(m_textEditorWidget); const FilePath targetPath = baseDir / m_imagePath / m_imageFileName; if (targetPath.isEmpty()) { qCDebug(androidManifestEditorLog) << "Image target path empty, cannot load image."; return; } QImage image = QImage(targetPath.toFSPathString()); if (image.isNull()) { qCDebug(androidManifestEditorLog).noquote() << "Cannot load image." << targetPath.toUserOutput(); return; } if (m_showImageFullScreen) { m_image = image.scaled(m_splashScreenButton->size()); m_imagePosition = QPoint(0,0); } else { QSize scaledSize = QSize((m_splashScreenButton->width() * image.width()) / m_screenSize.width(), (m_splashScreenButton->height() * image.height()) / m_screenSize.height()); m_image = image.scaled(scaledSize, Qt::KeepAspectRatio); m_imagePosition = QPoint((m_splashScreenButton->width() - m_image.width()) / 2, (m_splashScreenButton->height() - m_image.height()) / 2); } m_splashScreenButton->update(); } bool SplashScreenWidget::hasImage() const { return !m_image.isNull(); } void SplashScreenWidget::setScaleWarningLabelVisible(bool visible) { if (m_scaleWarningLabel) m_scaleWarningLabel->setVisible(visible); } void SplashScreenWidget::setImageFileName(const QString &imageFileName) { m_imageFileName = imageFileName; } static SplashScreenWidget *addWidgetToPage(QWidget *page, const QSize &size, const QSize &screenSize, const QString &title, const QString &tooltip, TextEditor::TextEditorWidget *textEditorWidget, const QString &splashScreenPath, int scalingRatio, int maxScalingRatio, QHBoxLayout *pageLayout, QList &widgetContainer) { auto splashScreenWidget = new SplashScreenWidget(page, size, screenSize, title, tooltip, splashScreenPath, scalingRatio, maxScalingRatio, textEditorWidget); pageLayout->addWidget(splashScreenWidget); widgetContainer.push_back(splashScreenWidget); return splashScreenWidget; } static QWidget *createPage(TextEditor::TextEditorWidget *textEditorWidget, QList &widgetContainer, QList &portraitWidgetContainer, QList &landscapeWidgetContainer, int scalingRatio, const QSize &size, const QSize &portraitSize, const QSize &landscapeSize, const QString &path) { auto sizeToStr = [](const QSize &size) { return QString(" (%1x%2)").arg(size.width()).arg(size.height()); }; QWidget *page = new QWidget(); auto pageLayout = new QHBoxLayout(page); auto genericWidget= addWidgetToPage(page, displaySize, size, Tr::tr("Splash screen"), Tr::tr("Select splash screen image") + sizeToStr(size), textEditorWidget, path, scalingRatio, extraExtraExtraHighDpiScalingRatio, pageLayout, widgetContainer); auto portraitWidget = addWidgetToPage(page, displaySize, portraitSize, Tr::tr("Portrait splash screen"), Tr::tr("Select portrait splash screen image") + sizeToStr(portraitSize), textEditorWidget, path, scalingRatio, extraExtraExtraHighDpiScalingRatio, pageLayout, portraitWidgetContainer); auto landscapeWidget = addWidgetToPage(page, landscapeDisplaySize, landscapeSize, Tr::tr("Landscape splash screen"), Tr::tr("Select landscape splash screen image") + sizeToStr(landscapeSize), textEditorWidget, path, scalingRatio, extraExtraExtraHighDpiScalingRatio, pageLayout, landscapeWidgetContainer); auto clearButton = new QToolButton(page); clearButton->setText(Tr::tr("Clear All")); pageLayout->addWidget(clearButton); pageLayout->setAlignment(clearButton, Qt::AlignVCenter); SplashScreenContainerWidget::connect(clearButton, &QAbstractButton::clicked, genericWidget, &SplashScreenWidget::clearImage); SplashScreenContainerWidget::connect(clearButton, &QAbstractButton::clicked, portraitWidget, &SplashScreenWidget::clearImage); SplashScreenContainerWidget::connect(clearButton, &QAbstractButton::clicked, landscapeWidget, &SplashScreenWidget::clearImage); return page; } SplashScreenContainerWidget::SplashScreenContainerWidget( QWidget *parent, TextEditor::TextEditorWidget *textEditorWidget) : QStackedWidget(parent), m_textEditorWidget(textEditorWidget) { auto noSplashscreenWidget = new QWidget(this); auto splashscreenWidget = new QWidget(this); auto layout = new QHBoxLayout(this); auto settingsLayout = new QVBoxLayout(this); auto noSplashscreenLayout = new QVBoxLayout(this); auto formLayout = new QFormLayout(this); QTabWidget *tab = new QTabWidget(this); m_stickyCheck = new QCheckBox(this); m_stickyCheck->setToolTip(Tr::tr("A non-sticky splash screen is hidden automatically when an activity is drawn.\n" "To hide a sticky splash screen, invoke QtAndroid::hideSplashScreen().")); formLayout->addRow(Tr::tr("Sticky splash screen:"), m_stickyCheck); m_imageShowMode = new QComboBox(this); formLayout->addRow(Tr::tr("Image show mode:"), m_imageShowMode); const QList imageShowModeMethodsMap = { {"center", "Place the object in the center of the screen in both the vertical and horizontal axis,\n" "not changing its size."}, {"fill", "Grow the horizontal and vertical size of the image if needed so it completely fills its screen."}}; for (int i = 0; i < imageShowModeMethodsMap.size(); ++i) { m_imageShowMode->addItem(imageShowModeMethodsMap.at(i).first()); m_imageShowMode->setItemData(i, imageShowModeMethodsMap.at(i).at(1), Qt::ToolTipRole); } m_backgroundColor = new QToolButton(this); m_backgroundColor->setToolTip(Tr::tr("Background color of the splash screen.")); formLayout->addRow(Tr::tr("Background color:"), m_backgroundColor); m_masterImage = new QToolButton(this); m_masterImage->setToolTip(Tr::tr("Select master image to use.")); m_masterImage->setIcon(Icon::fromTheme("document-open")); formLayout->addRow(Tr::tr("Master image:"), m_masterImage); m_portraitMasterImage = new QToolButton(this); m_portraitMasterImage->setToolTip(Tr::tr("Select portrait master image to use.")); m_portraitMasterImage->setIcon(Icon::fromTheme("document-open")); formLayout->addRow(Tr::tr("Portrait master image:"), m_portraitMasterImage); m_landscapeMasterImage = new QToolButton(this); m_landscapeMasterImage->setToolTip(Tr::tr("Select landscape master image to use.")); m_landscapeMasterImage->setIcon(Icon::fromTheme("document-open")); formLayout->addRow(Tr::tr("Landscape master image:"), m_landscapeMasterImage); auto clearAllButton = new QToolButton(this); clearAllButton->setText(Tr::tr("Clear All")); auto ldpiPage = createPage(textEditorWidget, m_imageWidgets, m_portraitImageWidgets, m_landscapeImageWidgets, lowDpiScalingRatio, lowDpiImageSize, lowDpiImageSize, lowDpiImageSize.transposed(), lowDpiImagePath); tab->addTab(ldpiPage, Tr::tr("LDPI")); auto mdpiPage = createPage(textEditorWidget, m_imageWidgets, m_portraitImageWidgets, m_landscapeImageWidgets, mediumDpiScalingRatio, mediumDpiImageSize, mediumDpiImageSize, mediumDpiImageSize.transposed(), mediumDpiImagePath); tab->addTab(mdpiPage, Tr::tr("MDPI")); auto hdpiPage = createPage(textEditorWidget, m_imageWidgets, m_portraitImageWidgets, m_landscapeImageWidgets, highDpiScalingRatio, highDpiImageSize, highDpiImageSize, highDpiImageSize.transposed(), highDpiImagePath); tab->addTab(hdpiPage, Tr::tr("HDPI")); auto xHdpiPage = createPage(textEditorWidget, m_imageWidgets, m_portraitImageWidgets, m_landscapeImageWidgets, extraHighDpiScalingRatio, extraHighDpiImageSize, extraHighDpiImageSize, extraHighDpiImageSize.transposed(), extraHighDpiImagePath); tab->addTab(xHdpiPage, Tr::tr("XHDPI")); auto xxHdpiPage = createPage(textEditorWidget, m_imageWidgets, m_portraitImageWidgets, m_landscapeImageWidgets, extraExtraHighDpiScalingRatio, extraExtraHighDpiImageSize, extraExtraHighDpiImageSize, extraExtraHighDpiImageSize.transposed(), extraExtraHighDpiImagePath); tab->addTab(xxHdpiPage, Tr::tr("XXHDPI")); auto xxxHdpiPage = createPage(textEditorWidget, m_imageWidgets, m_portraitImageWidgets, m_landscapeImageWidgets, extraExtraExtraHighDpiScalingRatio, extraExtraExtraHighDpiImageSize, extraExtraExtraHighDpiImageSize, extraExtraExtraHighDpiImageSize.transposed(), extraExtraExtraHighDpiImagePath); tab->addTab(xxxHdpiPage, Tr::tr("XXXHDPI")); formLayout->setContentsMargins(10, 10, 10, 10); formLayout->setSpacing(10); settingsLayout->addLayout(formLayout); settingsLayout->addWidget(clearAllButton); settingsLayout->setAlignment(clearAllButton, Qt::AlignHCenter); layout->addLayout(settingsLayout); layout->addWidget(tab); splashscreenWidget->setLayout(layout); addWidget(splashscreenWidget); setBackgroundColor(Qt::white); auto warningLabel = new QLabel(this); warningLabel->setAlignment(Qt::AlignHCenter); warningLabel->setText( Tr::tr( "An image is used for the splashscreen. Qt Creator manages " "splashscreen by using a different method which requires changing " "the manifest file by overriding your settings. Allow override?")); warningLabel->setWordWrap(true); m_convertSplashscreen = new QToolButton(this); m_convertSplashscreen->setText(Tr::tr("Convert")); noSplashscreenLayout->addStretch(); noSplashscreenLayout->addWidget(warningLabel); noSplashscreenLayout->addWidget(m_convertSplashscreen); noSplashscreenLayout->addStretch(); noSplashscreenLayout->setSpacing(10); noSplashscreenLayout->setAlignment(warningLabel, Qt::AlignHCenter); noSplashscreenLayout->setAlignment(m_convertSplashscreen, Qt::AlignHCenter); noSplashscreenWidget->setLayout(noSplashscreenLayout); addWidget(noSplashscreenWidget); const auto splashFileName = QString(splashscreenFileName).append(imageSuffix); const auto portraitSplashFileName = QString(splashscreenPortraitFileName).append(imageSuffix); const auto landscapeSplashFileName = QString(splashscreenLandscapeFileName).append(imageSuffix); for (auto &&imageWidget : m_imageWidgets) imageWidget->setImageFileName(splashFileName); for (auto &&imageWidget : m_portraitImageWidgets) imageWidget->setImageFileName(portraitSplashFileName); for (auto &&imageWidget : m_landscapeImageWidgets) imageWidget->setImageFileName(landscapeSplashFileName); for (auto &&imageWidget : m_imageWidgets) { connect(imageWidget, &SplashScreenWidget::imageChanged, this, [this] { createSplashscreenThemes(); emit splashScreensModified(); }); } for (auto &&imageWidget : m_portraitImageWidgets) { connect(imageWidget, &SplashScreenWidget::imageChanged, this, [this] { createSplashscreenThemes(); emit splashScreensModified(); }); } for (auto &&imageWidget : m_landscapeImageWidgets) { connect(imageWidget, &SplashScreenWidget::imageChanged, this, [this] { createSplashscreenThemes(); emit splashScreensModified(); }); } connect(m_stickyCheck, &QCheckBox::stateChanged, this, [this](int state) { bool old = m_splashScreenSticky; m_splashScreenSticky = (state == Qt::Checked); if (old != m_splashScreenSticky) emit splashScreensModified(); }); connect(m_backgroundColor, &QToolButton::clicked, this, [this] { const QColor color = QColorDialog::getColor(m_splashScreenBackgroundColor, this, Tr::tr("Select background color")); if (color.isValid()) { setBackgroundColor(color); createSplashscreenThemes(); emit splashScreensModified(); } }); connect(m_masterImage, &QToolButton::clicked, this, [this] { const FilePath file = FileUtils::getOpenFilePath(Tr::tr("Select master image"), FileUtils::homePath(), fileDialogImageFiles); if (!file.isEmpty()) { for (auto &&imageWidget : m_imageWidgets) imageWidget->setImageFromPath(file); createSplashscreenThemes(); emit splashScreensModified(); } }); connect(m_portraitMasterImage, &QToolButton::clicked, this, [this] { const FilePath file = FileUtils::getOpenFilePath(Tr::tr("Select portrait master image"), FileUtils::homePath(), fileDialogImageFiles); if (!file.isEmpty()) { for (auto &&imageWidget : m_portraitImageWidgets) imageWidget->setImageFromPath(file); createSplashscreenThemes(); emit splashScreensModified(); } }); connect(m_landscapeMasterImage, &QToolButton::clicked, this, [this] { const FilePath file = FileUtils::getOpenFilePath(Tr::tr("Select landscape master image"), FileUtils::homePath(), fileDialogImageFiles); if (!file.isEmpty()) { for (auto &&imageWidget : m_landscapeImageWidgets) imageWidget->setImageFromPath(file); createSplashscreenThemes(); emit splashScreensModified(); } }); connect(m_imageShowMode, &QComboBox::currentTextChanged, this, [this](const QString &mode) { setImageShowMode(mode); createSplashscreenThemes(); emit splashScreensModified(); }); connect(clearAllButton, &QToolButton::clicked, this, [this] { clearAll(); emit splashScreensModified(); }); connect(m_convertSplashscreen, &QToolButton::clicked, this, [this] { clearAll(); setCurrentIndex(0); emit splashScreensModified(); }); } void SplashScreenContainerWidget::loadImages() { if (isSplashscreenEnabled()) { for (auto &&imageWidget : m_imageWidgets) { imageWidget->loadImage(); } loadSplashscreenDrawParams(splashscreenName); for (auto &&imageWidget : m_portraitImageWidgets) { imageWidget->loadImage(); } loadSplashscreenDrawParams(splashscreenPortraitName); for (auto &&imageWidget : m_landscapeImageWidgets) { imageWidget->loadImage(); } loadSplashscreenDrawParams(splashscreenLandscapeName); } } void SplashScreenContainerWidget::loadSplashscreenDrawParams(const QString &name) { const FilePath filePath = manifestDir(m_textEditorWidget).pathAppended("res/drawable/" + name + ".xml"); QFile file(filePath.toFSPathString()); if (file.open(QIODevice::ReadOnly | QIODevice::Text)) { QXmlStreamReader reader(&file); reader.setNamespaceProcessing(false); while (!reader.atEnd()) { reader.readNext(); if (reader.hasError()) { // This should not happen return; } else { if (reader.name() == QLatin1String("solid")) { const QXmlStreamAttributes attributes = reader.attributes(); const auto color = attributes.value(QLatin1String("android:color")); if (!color.isEmpty()) setBackgroundColor(QColor(color)); } else if (reader.name() == QLatin1String("bitmap")) { const QXmlStreamAttributes attributes = reader.attributes(); const auto showMode = attributes.value(QLatin1String("android:gravity")); if (!showMode.isEmpty()) setImageShowMode(showMode.toString()); } } } } } void SplashScreenContainerWidget::clearAll() { for (auto &&imageWidget : m_imageWidgets) { imageWidget->clearImage(); } for (auto &&imageWidget : m_portraitImageWidgets) { imageWidget->clearImage(); } for (auto &&imageWidget : m_landscapeImageWidgets) { imageWidget->clearImage(); } setBackgroundColor(Qt::white); createSplashscreenThemes(); } bool SplashScreenContainerWidget::hasImages() const { for (auto &&imageWidget : m_imageWidgets) { if (imageWidget->hasImage()) return true; } return false; } bool SplashScreenContainerWidget::hasPortraitImages() const { for (auto &&imageWidget : m_portraitImageWidgets) { if (imageWidget->hasImage()) return true; } return false; } bool SplashScreenContainerWidget::hasLandscapeImages() const { for (auto &&imageWidget : m_landscapeImageWidgets) { if (imageWidget->hasImage()) return true; } return false; } bool SplashScreenContainerWidget::isSticky() const { return m_splashScreenSticky; } void SplashScreenContainerWidget::setSticky(bool sticky) { m_splashScreenSticky = sticky; m_stickyCheck->setCheckState(m_splashScreenSticky ? Qt::Checked : Qt::Unchecked); } void SplashScreenContainerWidget::setBackgroundColor(const QColor &color) { if (color != m_splashScreenBackgroundColor) { m_splashScreenBackgroundColor = color; m_backgroundColor->setStyleSheet(QString("QToolButton {background-color: %1; border: 1px solid gray;}").arg(color.name())); for (auto &&imageWidget : m_imageWidgets) imageWidget->setBackgroundColor(color); for (auto &&imageWidget : m_portraitImageWidgets) imageWidget->setBackgroundColor(color); for (auto &&imageWidget : m_landscapeImageWidgets) imageWidget->setBackgroundColor(color); } } void SplashScreenContainerWidget::setImageShowMode(const QString &mode) { bool imageFullScreen; if (mode == "center") imageFullScreen = false; else if (mode == "fill") imageFullScreen = true; else return; for (auto &&imageWidget : m_imageWidgets) imageWidget->showImageFullScreen(imageFullScreen); for (auto &&imageWidget : m_portraitImageWidgets) imageWidget->showImageFullScreen(imageFullScreen); for (auto &&imageWidget : m_landscapeImageWidgets) imageWidget->showImageFullScreen(imageFullScreen); m_imageShowMode->setCurrentText(mode); } void SplashScreenContainerWidget::createSplashscreenThemes() { const FilePath baseDir = manifestDir(m_textEditorWidget); const QStringList splashscreenThemeFiles = {"res/values/splashscreentheme.xml", "res/values-port/splashscreentheme.xml", "res/values-land/splashscreentheme.xml"}; const QStringList splashscreenDrawableFiles = {QString("res/drawable/%1.xml").arg(splashscreenName), QString("res/drawable/%1.xml").arg(splashscreenPortraitName), QString("res/drawable/%1.xml").arg(splashscreenLandscapeName)}; QStringList splashscreens[3]; if (hasImages()) splashscreens[0] << splashscreenName << splashscreenFileName; if (hasPortraitImages()) splashscreens[1] << splashscreenPortraitName << splashscreenPortraitFileName; if (hasLandscapeImages()) splashscreens[2] << splashscreenLandscapeName << splashscreenLandscapeFileName; for (int i = 0; i < 3; i++) { const FilePath splashscreenThemeFile = baseDir.pathAppended(splashscreenThemeFiles[i]); const FilePath splashscreenDrawableFile = baseDir.pathAppended(splashscreenDrawableFiles[i]); if (!splashscreens[i].isEmpty()) { QDir dir; QFile themeFile(splashscreenThemeFile.toFSPathString()); dir.mkpath(QFileInfo(themeFile).absolutePath()); if (themeFile.open(QFile::WriteOnly | QFile::Truncate)) { QXmlStreamWriter stream(&themeFile); stream.setAutoFormatting(true); stream.writeStartDocument(); stream.writeStartElement("resources"); stream.writeStartElement("style"); stream.writeAttribute("name", "splashScreenTheme"); stream.writeStartElement("item"); stream.writeAttribute("name", "android:windowBackground"); stream.writeCharacters(QLatin1String("@drawable/%1").arg(splashscreens[i].at(0))); stream.writeEndElement(); // item stream.writeEndElement(); // style stream.writeEndElement(); // resources stream.writeEndDocument(); themeFile.close(); } QFile drawableFile(splashscreenDrawableFile.toFSPathString()); dir.mkpath(QFileInfo(drawableFile).absolutePath()); if (drawableFile.open(QFile::WriteOnly | QFile::Truncate)) { QXmlStreamWriter stream(&drawableFile); stream.setAutoFormatting(true); stream.writeStartDocument(); stream.writeStartElement("layer-list"); stream.writeAttribute("xmlns:android", "http://schemas.android.com/apk/res/android"); stream.writeStartElement("item"); stream.writeStartElement("shape"); stream.writeAttribute("android:shape", "rectangle"); stream.writeEmptyElement("solid"); stream.writeAttribute("android:color", m_splashScreenBackgroundColor.name()); stream.writeEndElement(); // shape stream.writeEndElement(); // item stream.writeStartElement("item"); stream.writeEmptyElement("bitmap"); stream.writeAttribute("android:src", QLatin1String("@drawable/%1").arg(splashscreens[i].at(1))); stream.writeAttribute("android:gravity", m_imageShowMode->currentText()); stream.writeEndElement(); // item stream.writeEndElement(); // layer-list stream.writeEndDocument(); drawableFile.close(); } } else { QFile::remove(splashscreenThemeFile.toFSPathString()); QFile::remove(splashscreenDrawableFile.toFSPathString()); } } } void SplashScreenContainerWidget::checkSplashscreenImage(const QString &name) { if (isSplashscreenEnabled()) { const FilePath baseDir = manifestDir(m_textEditorWidget); const QString paths[] = {extraExtraExtraHighDpiImagePath, extraExtraHighDpiImagePath, extraHighDpiImagePath, highDpiImagePath, mediumDpiImagePath, lowDpiImagePath}; for (const QString &path : paths) { const FilePath filePath = baseDir.pathAppended(path + name); if (filePath.stringAppended(".png").exists() || filePath.stringAppended(".jpg").exists() || filePath.stringAppended(".jpeg").exists()) { setCurrentIndex(1); break; } } } } bool SplashScreenContainerWidget::isSplashscreenEnabled() { return (currentIndex() == 0); } QString SplashScreenContainerWidget::imageName() const { return splashscreenName; } QString SplashScreenContainerWidget::portraitImageName() const { return splashscreenPortraitName; } QString SplashScreenContainerWidget::landscapeImageName() const { return splashscreenLandscapeName; } } // namespace Android::Internal #include "splashscreencontainerwidget.moc"