diff --git a/src/plugins/android/CMakeLists.txt b/src/plugins/android/CMakeLists.txt index 49f5deb037b..a65936c1197 100644 --- a/src/plugins/android/CMakeLists.txt +++ b/src/plugins/android/CMakeLists.txt @@ -49,5 +49,6 @@ add_qtc_plugin(Android javaeditor.cpp javaeditor.h javaindenter.cpp javaindenter.h javaparser.cpp javaparser.h - splashiconcontainerwidget.cpp splashiconcontainerwidget.h + splashscreencontainerwidget.cpp splashscreencontainerwidget.h + splashscreenwidget.cpp splashscreenwidget.h ) diff --git a/src/plugins/android/android.pro b/src/plugins/android/android.pro index b6bb2132fd6..f5f550a9900 100644 --- a/src/plugins/android/android.pro +++ b/src/plugins/android/android.pro @@ -52,7 +52,8 @@ HEADERS += \ androidextralibrarylistmodel.h \ createandroidmanifestwizard.h \ androidsdkdownloader.h \ - splashiconcontainerwidget.h + splashscreencontainerwidget.h \ + splashscreenwidget.h SOURCES += \ androidconfigurations.cpp \ @@ -97,7 +98,8 @@ SOURCES += \ androidextralibrarylistmodel.cpp \ createandroidmanifestwizard.cpp \ androidsdkdownloader.cpp \ - splashiconcontainerwidget.cpp + splashscreencontainerwidget.cpp \ + splashscreenwidget.cpp FORMS += \ androidsettingswidget.ui \ diff --git a/src/plugins/android/android.qbs b/src/plugins/android/android.qbs index dc00c5cea7f..08a05635981 100644 --- a/src/plugins/android/android.qbs +++ b/src/plugins/android/android.qbs @@ -113,8 +113,10 @@ Project { "javaindenter.h", "javaparser.cpp", "javaparser.h", - "splashiconcontainerwidget.cpp", - "splashiconcontainerwidget.h" + "splashscreencontainerwidget.cpp", + "splashscreencontainerwidget.h", + "splashscreenwidget.cpp", + "splashscreenwidget.h" ] } } diff --git a/src/plugins/android/androidmanifesteditorwidget.cpp b/src/plugins/android/androidmanifesteditorwidget.cpp index 4bdbb9ab97f..db9b3408b2e 100644 --- a/src/plugins/android/androidmanifesteditorwidget.cpp +++ b/src/plugins/android/androidmanifesteditorwidget.cpp @@ -31,7 +31,7 @@ #include "androidmanifestdocument.h" #include "androidmanager.h" #include "androidservicewidget.h" -#include "splashiconcontainerwidget.h" +#include "splashscreencontainerwidget.h" #include #include @@ -492,7 +492,8 @@ QGroupBox *AndroidManifestEditorWidget::createAdvancedGroupBox(QWidget *parent) m_services = new AndroidServiceWidget(otherGroupBox); m_advanvedTabWidget->addTab(m_services, tr("Android services")); - m_splashButtons = new SplashIconContainerWidget(otherGroupBox, m_textEditorWidget); + m_splashButtons = new SplashScreenContainerWidget(otherGroupBox, + m_textEditorWidget); m_advanvedTabWidget->addTab(m_splashButtons, tr("Splash screen")); connect(m_services, &AndroidServiceWidget::servicesModified, this, [this]() { setDirty(); }); @@ -500,7 +501,7 @@ QGroupBox *AndroidManifestEditorWidget::createAdvancedGroupBox(QWidget *parent) this, &AndroidManifestEditorWidget::clearInvalidServiceInfo); connect(m_services, &AndroidServiceWidget::servicesInvalid, this, &AndroidManifestEditorWidget::setInvalidServiceInfo); - connect(m_splashButtons, &SplashIconContainerWidget::splashScreensModified, + connect(m_splashButtons, &SplashScreenContainerWidget::splashScreensModified, this, [this]() { setDirty(); }); connect(m_iconButtons, &AndroidManifestEditorIconContainerWidget::iconsModified, this, [this]() { setDirty(); }); @@ -892,7 +893,8 @@ void AndroidManifestEditorWidget::syncToWidgets(const QDomDocument &doc) == QLatin1String("android.app.splash_screen_sticky") && !(activityParseGuard & ActivityParseGuard::stickySplash)) { QString sticky = metadataElem.attribute(QLatin1String("android:value")); - m_splashButtons->setSticky(sticky == QLatin1String("true")); + m_currentsplashSticky = (sticky == QLatin1String("true")); + m_splashButtons->setSticky(m_currentsplashSticky); activityParseGuard |= ActivityParseGuard::stickySplash; } else if (metadataElem.attribute(QLatin1String("android:name")) .startsWith(QLatin1String("android.app.splash_screen_drawable")) @@ -902,17 +904,20 @@ void AndroidManifestEditorWidget::syncToWidgets(const QDomDocument &doc) QLatin1String drawable = QLatin1String("@drawable/"); QString splashImageValue = metadataElem.attribute(QLatin1String("android:resource")); QString splashImageName; - if (splashImageValue.startsWith(drawable)) { - splashImageName = splashImageValue.mid(drawable.size()); - } + if (!splashImageValue.startsWith(drawable)) + continue; + splashImageName = splashImageValue.mid(drawable.size()); if (attrName == QLatin1String("android.app.splash_screen_drawable")) { - m_splashButtons->setImageFileName(splashImageName); + m_splashButtons->checkSplashscreenImage(splashImageName); + m_currentsplashImageName[0] = splashImageName; splashParseGuard |= SplashImageParseGuard::splash; } else if (attrName == QLatin1String("android.app.splash_screen_drawable_portrait")) { - m_splashButtons->setPortraitImageFileName(splashImageName); - splashParseGuard |= SplashImageParseGuard::portraitSplash; + m_splashButtons->checkSplashscreenImage(splashImageName); + m_currentsplashImageName[1] = splashImageName; + splashParseGuard |= SplashImageParseGuard::portraitSplash; } else if (attrName == QLatin1String("android.app.splash_screen_drawable_landscape")) { - m_splashButtons->setLandscapeImageFileName(splashImageName); + m_splashButtons->checkSplashscreenImage(splashImageName); + m_currentsplashImageName[2] = splashImageName; splashParseGuard |= SplashImageParseGuard::landscapeSplash; } if (splashParseGuard & SplashImageParseGuard::splashDone) @@ -1203,19 +1208,36 @@ static void writeMetadataElement(const char *name, void AndroidManifestEditorWidget::parseSplashScreen(QXmlStreamWriter &writer) { - if (m_splashButtons->hasImages()) + QString splashImageName[3]; + bool splashSticky; + + if (m_splashButtons->isSplashscreenEnabled()) { + if (m_splashButtons->hasImages()) + splashImageName[0] = m_splashButtons->imageName(); + if (m_splashButtons->hasPortraitImages()) + splashImageName[1] = m_splashButtons->portraitImageName(); + if (m_splashButtons->hasLandscapeImages()) + splashImageName[2] = m_splashButtons->landscapeImageName(); + splashSticky = m_splashButtons->isSticky(); + } else { + for (int i = 0; i < 3; i++) + splashImageName[i] = m_currentsplashImageName[i]; + splashSticky = m_currentsplashSticky; + } + + if (!splashImageName[0].isEmpty()) writeMetadataElement("android.app.splash_screen_drawable", - "android:resource", QLatin1String("@drawable/") + m_splashButtons->imageFileName(), + "android:resource", QLatin1String("@drawable/%1").arg(splashImageName[0]), writer); - if (m_splashButtons->hasPortraitImages()) + if (!splashImageName[1].isEmpty()) writeMetadataElement("android.app.splash_screen_drawable_portrait", - "android:resource", QLatin1String("@drawable/") + m_splashButtons->portraitImageFileName(), + "android:resource", QLatin1String("@drawable/%1").arg(splashImageName[1]), writer); - if (m_splashButtons->hasLandscapeImages()) + if (!splashImageName[2].isEmpty()) writeMetadataElement("android.app.splash_screen_drawable_landscape", - "android:resource", QLatin1String("@drawable/") + m_splashButtons->landscapeImageFileName(), + "android:resource", QLatin1String("@drawable/%1").arg(splashImageName[2]), writer); - if (m_splashButtons->isSticky()) + if (splashSticky) writeMetadataElement("android.app.splash_screen_sticky", "android:value", "true", writer); @@ -1355,7 +1377,14 @@ void AndroidManifestEditorWidget::parseActivity(QXmlStreamReader &reader, QXmlSt QXmlStreamAttributes attributes = reader.attributes(); QStringList keys = { QLatin1String("android:label"), QLatin1String("android:screenOrientation") }; QStringList values = { m_activityNameLineEdit->text(), m_screenOrientation->currentText() }; - QXmlStreamAttributes result = modifyXmlStreamAttributes(attributes, keys, values); + QStringList removes; + if (m_splashButtons->hasImages() || m_splashButtons->hasPortraitImages() || m_splashButtons->hasLandscapeImages()) { + keys << QLatin1String("android:theme"); + values << QLatin1String("@style/splashScreenTheme"); + } else { + removes << QLatin1String("android:theme"); + } + QXmlStreamAttributes result = modifyXmlStreamAttributes(attributes, keys, values, removes); writer.writeAttributes(result); reader.readNext(); diff --git a/src/plugins/android/androidmanifesteditorwidget.h b/src/plugins/android/androidmanifesteditorwidget.h index b0ca6f15f94..23f97fd0000 100644 --- a/src/plugins/android/androidmanifesteditorwidget.h +++ b/src/plugins/android/androidmanifesteditorwidget.h @@ -58,7 +58,7 @@ class AndroidManifestEditor; class AndroidManifestEditorIconContainerWidget; class AndroidManifestEditorWidget; class AndroidServiceWidget; -class SplashIconContainerWidget; +class SplashScreenContainerWidget; class PermissionsModel: public QAbstractListModel { @@ -167,6 +167,8 @@ private: bool m_stayClean; int m_errorLine; int m_errorColumn; + QString m_currentsplashImageName[3]; + bool m_currentsplashSticky = false; QLineEdit *m_packageNameLineEdit; QLabel *m_packageNameWarningIcon; @@ -183,7 +185,7 @@ private: QComboBox *m_styleExtractMethod; QComboBox *m_screenOrientation; AndroidManifestEditorIconContainerWidget *m_iconButtons; - SplashIconContainerWidget *m_splashButtons; + SplashScreenContainerWidget *m_splashButtons; // Permissions QCheckBox *m_defaultPermissonsCheckBox; diff --git a/src/plugins/android/splashiconcontainerwidget.cpp b/src/plugins/android/splashiconcontainerwidget.cpp deleted file mode 100644 index b2d0f25e7b9..00000000000 --- a/src/plugins/android/splashiconcontainerwidget.cpp +++ /dev/null @@ -1,428 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2020 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt Creator. -** -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -****************************************************************************/ - -#include "splashiconcontainerwidget.h" -#include "androidmanifesteditoriconwidget.h" - -#include -#include -#include - -#include -#include -#include -#include -#include - -namespace Android { -namespace Internal { - -namespace { -const QString extraExtraExtraHighDpiImagePath = QLatin1String("/res/drawable-xxxhdpi/"); -const QString extraExtraHighDpiImagePath = QLatin1String("/res/drawable-xxhdpi/"); -const QString extraHighDpiImagePath = QLatin1String("/res/drawable-xhdpi/"); -const QString highDpiImagePath = QLatin1String("/res/drawable-hdpi/"); -const QString mediumDpiImagePath = QLatin1String("/res/drawable-mdpi/"); -const QString lowDpiImagePath = QLatin1String("/res/drawable-ldpi/"); -const QString extraExtraExtraHighDpiPortraitImagePath = QLatin1String("/res/drawable-port-xxxhdpi/"); -const QString extraExtraHighDpiPortraitImagePath = QLatin1String("/res/drawable-port-xxhdpi/"); -const QString extraHighDpiPortraitImagePath = QLatin1String("/res/drawable-port-xhdpi/"); -const QString highDpiPortraitImagePath = QLatin1String("/res/drawable-port-hdpi/"); -const QString mediumDpiPortraitImagePath = QLatin1String("/res/drawable-port-mdpi/"); -const QString lowDpiPortraitImagePath = QLatin1String("/res/drawable-port-ldpi/"); -const QString extraExtraExtraHighDpiLandscapeImagePath = QLatin1String("/res/drawable-land-xxxhdpi/"); -const QString extraExtraHighDpiLandscapeImagePath = QLatin1String("/res/drawable-land-xxhdpi/"); -const QString extraHighDpiLandscapeImagePath = QLatin1String("/res/drawable-land-xhdpi/"); -const QString highDpiLandscapeImagePath = QLatin1String("/res/drawable-land-hdpi/"); -const QString mediumDpiLandscapeImagePath = QLatin1String("/res/drawable-land-mdpi/"); -const QString lowDpiLandscapeImagePath = QLatin1String("/res/drawable-land-ldpi/"); -const QString imageSuffix = QLatin1String(".png"); -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}; -QString manifestDir(TextEditor::TextEditorWidget *textEditorWidget) -{ - // Get the manifest file's directory from its filepath. - return textEditorWidget->textDocument()->filePath().toFileInfo().absolutePath(); -} -} - -static AndroidManifestEditorIconWidget * addButtonToPage(QWidget *page, - const QSize &splashSize, const QSize &splashDisplaySize, - const QString &title, const QString &tooltip, - TextEditor::TextEditorWidget *textEditorWidget, - const QString &splashPath, - const QString &splashFileName, - QHBoxLayout *iconLayout, - QVector &buttonContainer, - bool scaledToOriginal, - bool scaledWithoutStretching) -{ - auto splashButton = new AndroidManifestEditorIconWidget(page, - splashSize, - splashDisplaySize, - title, - tooltip, - textEditorWidget, - splashPath, splashFileName); - splashButton->setScaledToOriginalAspectRatio(scaledToOriginal); - splashButton->setScaledWithoutStretching(scaledWithoutStretching); - iconLayout->addWidget(splashButton); - buttonContainer.push_back(splashButton); - return splashButton; -} - -static QWidget *createPage(TextEditor::TextEditorWidget *textEditorWidget, - QVector &buttonContainer, - QVector &portraitButtonContainer, - QVector &landscapeButtonContainer, - const QSize &splashSize, - const QSize &portraitSplashSize, - const QSize &landscapeSplashSize, - const QString &splashSizeTooltip, - const QString &portraitSplashSizeTooltip, - const QString &landscapeSplashSizeTooltip, - const QString &splashPath, - const QString &splashFileName, - const QString &portraitSplashPath, - const QString &portraitSplashFileName, - const QString &landscapeSplashPath, - const QString &landscapeSplashFileName) -{ - QWidget *page = new QWidget(); - auto iconLayout = new QHBoxLayout(page); - auto splashButton= addButtonToPage(page, splashSize, landscapeDisplaySize, - SplashIconContainerWidget::tr("Splash screen"), - SplashIconContainerWidget::tr("Select splash screen image") - + splashSizeTooltip, - textEditorWidget, - splashPath, splashFileName + imageSuffix, - iconLayout, - buttonContainer, - true, false); - - auto portraitButton = addButtonToPage(page, portraitSplashSize, displaySize, - SplashIconContainerWidget::tr("Portrait splash screen"), - SplashIconContainerWidget::tr("Select portrait splash screen image") - + portraitSplashSizeTooltip, - textEditorWidget, - portraitSplashPath, portraitSplashFileName + imageSuffix, - iconLayout, - portraitButtonContainer, - false, true); - - auto landscapeButton = addButtonToPage(page, landscapeSplashSize, landscapeDisplaySize, - SplashIconContainerWidget::tr("Landscape splash screen"), - SplashIconContainerWidget::tr("Select landscape splash screen image") - + landscapeSplashSizeTooltip, - textEditorWidget, - landscapeSplashPath, landscapeSplashFileName + imageSuffix, - iconLayout, - landscapeButtonContainer, - false, true); - - auto clearButton = new QToolButton(page); - clearButton->setText(SplashIconContainerWidget::tr("Clear all")); - iconLayout->addWidget(clearButton); - iconLayout->setAlignment(clearButton, Qt::AlignVCenter); - SplashIconContainerWidget::connect(clearButton, &QAbstractButton::clicked, - splashButton, &AndroidManifestEditorIconWidget::clearIcon); - SplashIconContainerWidget::connect(clearButton, &QAbstractButton::clicked, - portraitButton, &AndroidManifestEditorIconWidget::clearIcon); - SplashIconContainerWidget::connect(clearButton, &QAbstractButton::clicked, - landscapeButton, &AndroidManifestEditorIconWidget::clearIcon); - return page; -} - - -SplashIconContainerWidget::SplashIconContainerWidget( - QWidget *parent, - TextEditor::TextEditorWidget *textEditorWidget) - : QWidget(parent), m_textEditorWidget(textEditorWidget) -{ - auto layout = new QVBoxLayout(this); - m_stickyCheck = new QCheckBox(tr("Sticky splash screen"), this); - m_stickyCheck->setToolTip(tr("A non-sticky splash screen is hidden automatically when an activity is drawn.\n" - "To hide a sticky splash screen, invoke QtAndroid::hideSplashScreen().")); - QTabWidget *tab = new QTabWidget(this); - auto sizeToStr = [](const QSize &size) { return QString(" (%1x%2)").arg(size.width()).arg(size.height()); }; - - auto xxxHdpiPage = createPage(textEditorWidget, - m_imageButtons, m_portraitImageButtons, m_landscapeImageButtons, - extraExtraExtraHighDpiImageSize, - extraExtraExtraHighDpiImageSize, - extraExtraExtraHighDpiImageSize.transposed(), - sizeToStr(extraExtraExtraHighDpiImageSize), sizeToStr(extraExtraExtraHighDpiImageSize), sizeToStr(extraExtraExtraHighDpiImageSize.transposed()), - extraExtraExtraHighDpiImagePath, m_imageFileName, - extraExtraExtraHighDpiImagePath, m_portraitImageFileName, - extraExtraExtraHighDpiImagePath, m_landscapeImageFileName); - tab->addTab(xxxHdpiPage, tr("XXXHDPI splash screen")); - auto xxHdpiPage = createPage(textEditorWidget, - m_imageButtons, m_portraitImageButtons, m_landscapeImageButtons, - extraExtraHighDpiImageSize, - extraExtraHighDpiImageSize, - extraExtraHighDpiImageSize.transposed(), - sizeToStr(extraExtraHighDpiImageSize), sizeToStr(extraExtraHighDpiImageSize), sizeToStr(extraExtraHighDpiImageSize.transposed()), - extraExtraHighDpiImagePath, m_imageFileName, - extraExtraHighDpiImagePath, m_portraitImageFileName, - extraExtraHighDpiImagePath, m_landscapeImageFileName); - tab->addTab(xxHdpiPage, tr("XXHDPI splash screen")); - auto xHdpiPage = createPage(textEditorWidget, - m_imageButtons, m_portraitImageButtons, m_landscapeImageButtons, - extraHighDpiImageSize, - extraHighDpiImageSize, - extraHighDpiImageSize.transposed(), - sizeToStr(extraHighDpiImageSize), sizeToStr(extraHighDpiImageSize), sizeToStr(extraHighDpiImageSize.transposed()), - extraHighDpiImagePath, m_imageFileName, - extraHighDpiImagePath, m_portraitImageFileName, - extraHighDpiImagePath, m_landscapeImageFileName); - tab->addTab(xHdpiPage, tr("XHDPI splash screen")); - auto hdpiPage = createPage(textEditorWidget, - m_imageButtons, m_portraitImageButtons, m_landscapeImageButtons, - highDpiImageSize, - highDpiImageSize, - highDpiImageSize.transposed(), - sizeToStr(highDpiImageSize), sizeToStr(highDpiImageSize), sizeToStr(highDpiImageSize.transposed()), - highDpiImagePath, m_imageFileName, - highDpiImagePath, m_portraitImageFileName, - highDpiImagePath, m_landscapeImageFileName); - tab->addTab(hdpiPage, tr("HDPI splash screen")); - auto mdpiPage = createPage(textEditorWidget, - m_imageButtons, m_portraitImageButtons, m_landscapeImageButtons, - mediumDpiImageSize, - mediumDpiImageSize, - mediumDpiImageSize.transposed(), - sizeToStr(mediumDpiImageSize), sizeToStr(mediumDpiImageSize), sizeToStr(mediumDpiImageSize.transposed()), - mediumDpiImagePath, m_imageFileName, - mediumDpiImagePath, m_portraitImageFileName, - mediumDpiImagePath, m_landscapeImageFileName); - tab->addTab(mdpiPage, tr("MDPI splash screen")); - auto ldpiPage = createPage(textEditorWidget, - m_imageButtons, m_portraitImageButtons, m_landscapeImageButtons, - lowDpiImageSize, - lowDpiImageSize, - lowDpiImageSize.transposed(), - sizeToStr(lowDpiImageSize), sizeToStr(lowDpiImageSize), sizeToStr(lowDpiImageSize.transposed()), - lowDpiImagePath, m_imageFileName, - lowDpiImagePath, m_portraitImageFileName, - lowDpiImagePath, m_landscapeImageFileName); - tab->addTab(ldpiPage, tr("LDPI splash screen")); - layout->addWidget(m_stickyCheck); - layout->setAlignment(m_stickyCheck, Qt::AlignLeft); - layout->addWidget(tab); - for (auto &&imageButton : m_imageButtons) { - connect(imageButton, &AndroidManifestEditorIconWidget::iconSelected, - this, &SplashIconContainerWidget::splashScreensModified); - connect(imageButton, &AndroidManifestEditorIconWidget::iconSelected, - this, &SplashIconContainerWidget::imageSelected); - } - for (auto &&imageButton : m_portraitImageButtons) { - connect(imageButton, &AndroidManifestEditorIconWidget::iconSelected, - this, &SplashIconContainerWidget::splashScreensModified); - connect(imageButton, &AndroidManifestEditorIconWidget::iconRemoved, - this, &SplashIconContainerWidget::splashScreensModified); - connect(imageButton, &AndroidManifestEditorIconWidget::iconSelected, - this, &SplashIconContainerWidget::imageSelected); - } - for (auto &&imageButton : m_landscapeImageButtons) { - connect(imageButton, &AndroidManifestEditorIconWidget::iconSelected, - this, &SplashIconContainerWidget::splashScreensModified); - connect(imageButton, &AndroidManifestEditorIconWidget::iconRemoved, - this, &SplashIconContainerWidget::splashScreensModified); - connect(imageButton, &AndroidManifestEditorIconWidget::iconSelected, - this, &SplashIconContainerWidget::imageSelected); - } - connect(m_stickyCheck, &QCheckBox::stateChanged, [this](int state) { - bool old = m_splashScreenSticky; - m_splashScreenSticky = (state == Qt::Checked); - if (old != m_splashScreenSticky) - splashScreensModified(); - }); -} - -static void maybeChangeImagePath(AndroidManifestEditorIconWidget *widget, - TextEditor::TextEditorWidget *m_textEditorWidget, - const QString &extraExtraExtraHighDpiAlternativePath, - const QString &extraExtraHighDpiAlternativePath, - const QString &extraHighDpiAlternativePath, - const QString &highDpiAlternativePath, - const QString &mediumDpiAlternativePath, - const QString &lowDpiAlternativePath) -{ - QString currentPath = widget->targetIconPath(); - QString alternativePath = currentPath; - if (currentPath == extraExtraExtraHighDpiImagePath) - alternativePath = extraExtraExtraHighDpiAlternativePath; - else if (currentPath == extraExtraHighDpiImagePath) - alternativePath = extraExtraHighDpiAlternativePath; - else if (currentPath == extraHighDpiImagePath) - alternativePath = extraHighDpiAlternativePath; - else if (currentPath == highDpiImagePath) - alternativePath = highDpiAlternativePath; - else if (currentPath == mediumDpiImagePath) - alternativePath = mediumDpiAlternativePath; - else if (currentPath == lowDpiImagePath) - alternativePath = lowDpiAlternativePath; - QString baseDir = manifestDir(m_textEditorWidget); - const QString targetPath = baseDir + alternativePath + widget->targetIconFileName(); - QFileInfo image(targetPath); - if (image.exists()) - widget->setTargetIconPath(alternativePath); -} - -void SplashIconContainerWidget::loadImages() -{ - for (auto &&imageButton : m_imageButtons) { - imageButton->setTargetIconFileName(m_imageFileName + imageSuffix); - imageButton->loadIcon(); - } - for (auto &&imageButton : m_portraitImageButtons) { - imageButton->setTargetIconFileName(m_portraitImageFileName + imageSuffix); - maybeChangeImagePath(imageButton, m_textEditorWidget, - extraExtraExtraHighDpiPortraitImagePath, extraExtraHighDpiPortraitImagePath, extraHighDpiPortraitImagePath, - highDpiPortraitImagePath, mediumDpiPortraitImagePath, lowDpiPortraitImagePath); - imageButton->loadIcon(); - } - for (auto &&imageButton : m_landscapeImageButtons) { - imageButton->setTargetIconFileName(m_landscapeImageFileName + imageSuffix); - maybeChangeImagePath(imageButton, m_textEditorWidget, - extraExtraExtraHighDpiLandscapeImagePath, extraExtraHighDpiLandscapeImagePath, extraHighDpiLandscapeImagePath, - highDpiLandscapeImagePath, mediumDpiLandscapeImagePath, lowDpiLandscapeImagePath); - imageButton->loadIcon(); - } -} - -bool SplashIconContainerWidget::hasImages() const -{ - for (auto &&iconButton : m_imageButtons) { - if (iconButton->hasIcon()) - return true; - } - return false; -} - -bool SplashIconContainerWidget::hasPortraitImages() const -{ - for (auto &&iconButton : m_portraitImageButtons) { - if (iconButton->hasIcon()) - return true; - } - return false; -} - -bool SplashIconContainerWidget::hasLandscapeImages() const -{ - for (auto &&iconButton : m_landscapeImageButtons) { - if (iconButton->hasIcon()) - return true; - } - return false; -} - -bool SplashIconContainerWidget::isSticky() const -{ - return m_splashScreenSticky; -} - -void SplashIconContainerWidget::setSticky(bool sticky) -{ - m_splashScreenSticky = sticky; - m_stickyCheck->setCheckState(m_splashScreenSticky ? Qt::Checked : Qt::Unchecked); -} - -QString SplashIconContainerWidget::imageFileName() const -{ - return m_imageFileName; -} - -QString SplashIconContainerWidget::landscapeImageFileName() const -{ - return m_landscapeImageFileName; -} - -QString SplashIconContainerWidget::portraitImageFileName() const -{ - return m_portraitImageFileName; -} - -void SplashIconContainerWidget::setImageFileName(const QString &name) -{ - m_imageFileName = name; -} - -void SplashIconContainerWidget::setLandscapeImageFileName(const QString &name) -{ - m_landscapeImageFileName = name; -} - -void SplashIconContainerWidget::setPortraitImageFileName(const QString &name) -{ - m_portraitImageFileName = name; -} - -static void reflectImage(const QString &path, - AndroidManifestEditorIconWidget *iconWidget, - const QVector &source, - QVector *firstTarget, - QVector *secondTarget = nullptr, - Qt::Orientation *imageOrientation = nullptr) -{ - for (int i = 0; i < source.size(); ++i) - if (source[i] == iconWidget) { - if (firstTarget && !(*firstTarget)[i]->hasIcon() - && (!imageOrientation || *imageOrientation == Qt::Horizontal)) - (*firstTarget)[i]->setIconFromPath(path); - if (secondTarget && !(*secondTarget)[i]->hasIcon() - && (!imageOrientation || *imageOrientation == Qt::Vertical)) - (*secondTarget)[i]->setIconFromPath(path); - break; - } - -} - -void SplashIconContainerWidget::imageSelected(const QString &path, AndroidManifestEditorIconWidget *iconWidget) -{ - QImage original(path); - Qt::Orientation orientation = Qt::Horizontal; - if (!original.isNull()) { - if (original.width() > original.height()) { - orientation = Qt::Horizontal; - } else { - orientation = Qt::Vertical; - } - } - reflectImage(path, iconWidget, - m_imageButtons, &m_portraitImageButtons, &m_landscapeImageButtons, &orientation); - reflectImage(path, iconWidget, m_portraitImageButtons, &m_landscapeImageButtons); - reflectImage(path, iconWidget, m_landscapeImageButtons, &m_portraitImageButtons); -} - -} // namespace Internal -} // namespace Android diff --git a/src/plugins/android/splashscreencontainerwidget.cpp b/src/plugins/android/splashscreencontainerwidget.cpp new file mode 100644 index 00000000000..bce9a13f4f1 --- /dev/null +++ b/src/plugins/android/splashscreencontainerwidget.cpp @@ -0,0 +1,636 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#include "splashscreencontainerwidget.h" +#include "splashscreenwidget.h" + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Android { +namespace Internal { + +namespace { +const QString extraExtraExtraHighDpiImagePath = QLatin1String("/res/drawable-xxxhdpi/"); +const QString extraExtraHighDpiImagePath = QLatin1String("/res/drawable-xxhdpi/"); +const QString extraHighDpiImagePath = QLatin1String("/res/drawable-xhdpi/"); +const QString highDpiImagePath = QLatin1String("/res/drawable-hdpi/"); +const QString mediumDpiImagePath = QLatin1String("/res/drawable-mdpi/"); +const QString lowDpiImagePath = QLatin1String("/res/drawable-ldpi/"); +const QString splashscreenName = QLatin1String("splashscreen"); +const QString splashscreenPortraitName = QLatin1String("splashscreen_port"); +const QString splashscreenLandscapeName = QLatin1String("splashscreen_land"); +const QString splashscreenFileName = QLatin1String("logo"); +const QString splashscreenPortraitFileName = QLatin1String("logo_port"); +const QString splashscreenLandscapeFileName = QLatin1String("logo_land"); +const QString imageSuffix = QLatin1String(".png"); +const QString fileDialogImageFiles = QString(QWidget::tr("Images (*.png *.jpg)")); +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; + +QString manifestDir(TextEditor::TextEditorWidget *textEditorWidget) +{ + // Get the manifest file's directory from its filepath. + return textEditorWidget->textDocument()->filePath().toFileInfo().absolutePath(); +} + +} + +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, + QVector &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, + QVector &widgetContainer, + QVector &portraitWidgetContainer, + QVector &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, + SplashScreenContainerWidget::tr("Splash screen"), + SplashScreenContainerWidget::tr("Select splash screen image") + + sizeToStr(size), + textEditorWidget, + path, + scalingRatio, extraExtraExtraHighDpiScalingRatio, + pageLayout, + widgetContainer); + + auto portraitWidget = addWidgetToPage(page, + displaySize, portraitSize, + SplashScreenContainerWidget::tr("Portrait splash screen"), + SplashScreenContainerWidget::tr("Select portrait splash screen image") + + sizeToStr(portraitSize), + textEditorWidget, + path, + scalingRatio, extraExtraExtraHighDpiScalingRatio, + pageLayout, + portraitWidgetContainer); + + auto landscapeWidget = addWidgetToPage(page, + landscapeDisplaySize, landscapeSize, + SplashScreenContainerWidget::tr("Landscape splash screen"), + SplashScreenContainerWidget::tr("Select landscape splash screen image") + + sizeToStr(landscapeSize), + textEditorWidget, + path, + scalingRatio, extraExtraExtraHighDpiScalingRatio, + pageLayout, + landscapeWidgetContainer); + + auto clearButton = new QToolButton(page); + clearButton->setText(SplashScreenContainerWidget::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("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("Sticky splash screen:"), m_stickyCheck); + + m_imageShowMode = new QComboBox(this); + formLayout->addRow(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("Background color of the splash screen.")); + formLayout->addRow(tr("Background color:"), m_backgroundColor); + + m_masterImage = new QToolButton(this); + m_masterImage->setToolTip(tr("Select master image to use.")); + m_masterImage->setIcon(QIcon::fromTheme(QLatin1String("document-open"), Utils::Icons::OPENFILE.icon())); + formLayout->addRow(tr("Master image:"), m_masterImage); + + m_portraitMasterImage = new QToolButton(this); + m_portraitMasterImage->setToolTip(tr("Select portrait master image to use.")); + m_portraitMasterImage->setIcon(QIcon::fromTheme(QLatin1String("document-open"), Utils::Icons::OPENFILE.icon())); + formLayout->addRow(tr("Portrait master image:"), m_portraitMasterImage); + + m_landscapeMasterImage = new QToolButton(this); + m_landscapeMasterImage->setToolTip(tr("Select landscape master image to use.")); + m_landscapeMasterImage->setIcon(QIcon::fromTheme(QLatin1String("document-open"), Utils::Icons::OPENFILE.icon())); + formLayout->addRow(tr("Landscape master image:"), m_landscapeMasterImage); + + auto clearAllButton = new QToolButton(this); + clearAllButton->setText(SplashScreenContainerWidget::tr("Clear All")); + + auto ldpiPage = createPage(textEditorWidget, + m_imageWidgets, m_portraitImageWidgets, m_landscapeImageWidgets, + lowDpiScalingRatio, + lowDpiImageSize, + lowDpiImageSize, + lowDpiImageSize.transposed(), + lowDpiImagePath); + tab->addTab(ldpiPage, tr("LDPI")); + auto mdpiPage = createPage(textEditorWidget, + m_imageWidgets, m_portraitImageWidgets, m_landscapeImageWidgets, + mediumDpiScalingRatio, + mediumDpiImageSize, + mediumDpiImageSize, + mediumDpiImageSize.transposed(), + mediumDpiImagePath); + tab->addTab(mdpiPage, tr("MDPI")); + auto hdpiPage = createPage(textEditorWidget, + m_imageWidgets, m_portraitImageWidgets, m_landscapeImageWidgets, + highDpiScalingRatio, + highDpiImageSize, + highDpiImageSize, + highDpiImageSize.transposed(), + highDpiImagePath); + tab->addTab(hdpiPage, tr("HDPI")); + auto xHdpiPage = createPage(textEditorWidget, + m_imageWidgets, m_portraitImageWidgets, m_landscapeImageWidgets, + extraHighDpiScalingRatio, + extraHighDpiImageSize, + extraHighDpiImageSize, + extraHighDpiImageSize.transposed(), + extraHighDpiImagePath); + tab->addTab(xHdpiPage, tr("XHDPI")); + auto xxHdpiPage = createPage(textEditorWidget, + m_imageWidgets, m_portraitImageWidgets, m_landscapeImageWidgets, + extraExtraHighDpiScalingRatio, + extraExtraHighDpiImageSize, + extraExtraHighDpiImageSize, + extraExtraHighDpiImageSize.transposed(), + extraExtraHighDpiImagePath); + tab->addTab(xxHdpiPage, tr("XXHDPI")); + auto xxxHdpiPage = createPage(textEditorWidget, + m_imageWidgets, m_portraitImageWidgets, m_landscapeImageWidgets, + extraExtraExtraHighDpiScalingRatio, + extraExtraExtraHighDpiImageSize, + extraExtraExtraHighDpiImageSize, + extraExtraExtraHighDpiImageSize.transposed(), + extraExtraExtraHighDpiImagePath); + tab->addTab(xxxHdpiPage, 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("An image is used for the splashscreen. Qt Creator manages\n" + "splashscreen by using a different method which requires changing\n" + "the manifest file by overriding your settings. Allow override?")); + m_convertSplashscreen = new QToolButton(this); + m_convertSplashscreen->setText(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); + + for (auto &&imageWidget : m_imageWidgets) + imageWidget->setImageFileName(splashscreenFileName + imageSuffix); + for (auto &&imageWidget : m_portraitImageWidgets) + imageWidget->setImageFileName(splashscreenPortraitFileName + imageSuffix); + for (auto &&imageWidget : m_landscapeImageWidgets) + imageWidget->setImageFileName(splashscreenLandscapeFileName + imageSuffix); + + for (auto &&imageWidget : m_imageWidgets) { + connect(imageWidget, &SplashScreenWidget::imageChanged, [this]() { + createSplashscreenThemes(); + emit splashScreensModified(); + }); + } + for (auto &&imageWidget : m_portraitImageWidgets) { + connect(imageWidget, &SplashScreenWidget::imageChanged, [this]() { + createSplashscreenThemes(); + emit splashScreensModified(); + }); + } + for (auto &&imageWidget : m_landscapeImageWidgets) { + connect(imageWidget, &SplashScreenWidget::imageChanged, [this]() { + createSplashscreenThemes(); + emit splashScreensModified(); + }); + } + connect(m_stickyCheck, &QCheckBox::stateChanged, [this](int state) { + bool old = m_splashScreenSticky; + m_splashScreenSticky = (state == Qt::Checked); + if (old != m_splashScreenSticky) + emit splashScreensModified(); + }); + connect(m_backgroundColor, &QToolButton::clicked, [this]() { + const QColor color = QColorDialog::getColor(m_splashScreenBackgroundColor, + this, + tr("Select background color")); + if (color.isValid()) { + setBackgroundColor(color); + createSplashscreenThemes(); + emit splashScreensModified(); + } + }); + connect(m_masterImage, &QToolButton::clicked, [this]() { + const QString file = QFileDialog::getOpenFileName(this, tr("Select master image"), + QDir::homePath(), fileDialogImageFiles); + if (!file.isEmpty()) { + for (auto &&imageWidget : m_imageWidgets) + imageWidget->setImageFromPath(file); + createSplashscreenThemes(); + emit splashScreensModified(); + } + }); + connect(m_portraitMasterImage, &QToolButton::clicked, [this]() { + const QString file = QFileDialog::getOpenFileName(this, tr("Select portrait master image"), + QDir::homePath(), fileDialogImageFiles); + if (!file.isEmpty()) { + for (auto &&imageWidget : m_portraitImageWidgets) + imageWidget->setImageFromPath(file); + createSplashscreenThemes(); + emit splashScreensModified(); + } + }); + connect(m_landscapeMasterImage, &QToolButton::clicked, [this]() { + const QString file = QFileDialog::getOpenFileName(this, tr("Select landscape master image"), + QDir::homePath(), fileDialogImageFiles); + if (!file.isEmpty()) { + for (auto &&imageWidget : m_landscapeImageWidgets) + imageWidget->setImageFromPath(file); + createSplashscreenThemes(); + emit splashScreensModified(); + } + }); + connect(m_imageShowMode, &QComboBox::currentTextChanged, [this](const QString &mode) { + setImageShowMode(mode); + createSplashscreenThemes(); + emit splashScreensModified(); + }); + connect(clearAllButton, &QToolButton::clicked, [this]() { + clearAll(); + emit splashScreensModified(); + }); + connect(m_convertSplashscreen, &QToolButton::clicked, [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) +{ + QFile file(QLatin1String("%1/res/drawable/%2.xml").arg(manifestDir(m_textEditorWidget)).arg(name)); + 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 QStringRef color = attributes.value(QLatin1String("android:color")); + if (!color.isEmpty()) + setBackgroundColor(QColor(color)); + } + else if (reader.name() == QLatin1String("bitmap")) { + const QXmlStreamAttributes attributes = reader.attributes(); + const QStringRef 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_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); + + m_splashScreenBackgroundColor = 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 QString 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++) { + if (!splashscreens[i].isEmpty()) { + QDir dir; + QFile themeFile(baseDir + splashscreenThemeFiles[i]); + 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(baseDir + splashscreenDrawableFiles[i]); + 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(baseDir + splashscreenThemeFiles[i]); + QFile::remove(baseDir + splashscreenDrawableFiles[i]); + } + } +} + +void SplashScreenContainerWidget::checkSplashscreenImage(const QString &name) +{ + if (isSplashscreenEnabled()) { + const QString baseDir = manifestDir(m_textEditorWidget); + const QStringList paths = {extraExtraExtraHighDpiImagePath, + extraExtraHighDpiImagePath, + extraHighDpiImagePath, + highDpiImagePath, + mediumDpiImagePath, + lowDpiImagePath}; + + for (const QString &path : paths) { + const QString filePath(baseDir + path + name); + if (QFile::exists(filePath + ".png") + || QFile::exists(filePath + ".jpg")) { + 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 Internal +} // namespace Android diff --git a/src/plugins/android/splashiconcontainerwidget.h b/src/plugins/android/splashscreencontainerwidget.h similarity index 58% rename from src/plugins/android/splashiconcontainerwidget.h rename to src/plugins/android/splashscreencontainerwidget.h index 9c0d6389b6d..0593a5ecbde 100644 --- a/src/plugins/android/splashiconcontainerwidget.h +++ b/src/plugins/android/splashscreencontainerwidget.h @@ -26,53 +26,62 @@ #pragma once #include -#include +#include QT_BEGIN_NAMESPACE class QCheckBox; +class QColor; +class QComboBox; +class QToolButton; QT_END_NAMESPACE -namespace TextEditor { - class TextEditorWidget; -} +namespace TextEditor { class TextEditorWidget; } namespace Android { namespace Internal { -class AndroidManifestEditorIconWidget; +class SplashScreenWidget; -class SplashIconContainerWidget : public QWidget +class SplashScreenContainerWidget : public QStackedWidget { Q_OBJECT public: - explicit SplashIconContainerWidget(QWidget *parent, - TextEditor::TextEditorWidget *textEditorWidget); + explicit SplashScreenContainerWidget(QWidget *parent, + TextEditor::TextEditorWidget *textEditorWidget); void loadImages(); bool hasImages() const; bool hasPortraitImages() const; bool hasLandscapeImages() const; bool isSticky() const; void setSticky(bool sticky); - QString imageFileName() const; - QString landscapeImageFileName() const; - QString portraitImageFileName() const; - void setImageFileName(const QString &name); - void setLandscapeImageFileName(const QString &name); - void setPortraitImageFileName(const QString &name); + QString imageName() const; + QString portraitImageName() const; + QString landscapeImageName() const; + void checkSplashscreenImage(const QString &name); + bool isSplashscreenEnabled(); signals: void splashScreensModified(); + private: - void imageSelected(const QString &path, AndroidManifestEditorIconWidget *iconWidget); -private: + void loadSplashscreenDrawParams(const QString &name); + void setBackgroundColor(const QColor &color); + void setImageShowMode(const QString &mode); + void createSplashscreenThemes(); + void clearAll(); + TextEditor::TextEditorWidget *m_textEditorWidget = nullptr; - QVector m_imageButtons; - QVector m_portraitImageButtons; - QVector m_landscapeImageButtons; + QVector m_imageWidgets; + QVector m_portraitImageWidgets; + QVector m_landscapeImageWidgets; bool m_splashScreenSticky = false; + QColor m_splashScreenBackgroundColor; QCheckBox *m_stickyCheck = nullptr; - QString m_imageFileName = QLatin1String("logo"); - QString m_landscapeImageFileName = QLatin1String("logo_landscape"); - QString m_portraitImageFileName = QLatin1String("logo_portrait"); + QComboBox *m_imageShowMode = nullptr; + QToolButton *m_backgroundColor = nullptr; + QToolButton *m_masterImage = nullptr; + QToolButton *m_portraitMasterImage = nullptr; + QToolButton *m_landscapeMasterImage = nullptr; + QToolButton *m_convertSplashscreen = nullptr; }; } // namespace Internal diff --git a/src/plugins/android/splashscreenwidget.cpp b/src/plugins/android/splashscreenwidget.cpp new file mode 100644 index 00000000000..6e6b2f376ab --- /dev/null +++ b/src/plugins/android/splashscreenwidget.cpp @@ -0,0 +1,267 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#include "splashscreenwidget.h" + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Android { +namespace Internal { + +namespace { +static Q_LOGGING_CATEGORY(androidManifestEditorLog, "qtc.android.splashScreenWidget", QtWarningMsg) +const auto fileDialogImageFiles = QStringLiteral("%1 (*.png *.jpg)").arg(QWidget::tr("Images")); +QString manifestDir(TextEditor::TextEditorWidget *textEditorWidget) +{ + // Get the manifest file's directory from its filepath. + return textEditorWidget->textDocument()->filePath().toFileInfo().absolutePath(); +} +} + +SplashScreenWidget::SplashScreenWidget(QWidget *parent) : QWidget(parent) +{ +} + +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("Icon scaled up.")); + m_scaleWarningLabel->setVisible(false); + } + auto label = new QLabel(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; +} + +SplashScreenWidget::SplashScreenButton::SplashScreenButton(SplashScreenWidget *parent) + : QToolButton(parent), + m_parentWidget(parent) +{ +} + +void SplashScreenWidget::SplashScreenButton::paintEvent(QPaintEvent *event) +{ + 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); +} + +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 QString &imagePath, bool resize) +{ + if (!m_textEditorWidget) + return; + const QString baseDir = manifestDir(m_textEditorWidget); + const QString targetPath = baseDir + m_imagePath + m_imageFileName; + if (targetPath.isEmpty()) { + qCDebug(androidManifestEditorLog) << "Image target path is empty, cannot set image."; + return; + } + QImage image = QImage(imagePath); + if (image.isNull()) { + qCDebug(androidManifestEditorLog) << "Image '" << imagePath << "' not found or invalid format."; + return; + } + QDir dir; + if (!dir.mkpath(QFileInfo(targetPath).absolutePath())) { + 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); + if (file.open(QIODevice::WriteOnly | QIODevice::Truncate)) { + image.save(&file, "PNG"); + file.close(); + loadImage(); + emit imageChanged(); + } + else { + qCDebug(androidManifestEditorLog) << "Cannot save image."; + } +} + +void SplashScreenWidget::selectImage() +{ + const QString file = QFileDialog::getOpenFileName(this, m_imageSelectionText, + QDir::homePath(), fileDialogImageFiles); + if (file.isEmpty()) + return; + setImageFromPath(file, false); + emit imageChanged(); +} + +void SplashScreenWidget::removeImage() +{ + const QString baseDir = manifestDir(m_textEditorWidget); + const QString targetPath = baseDir + m_imagePath + m_imageFileName; + if (targetPath.isEmpty()) { + qCDebug(androidManifestEditorLog) << "Image target path empty, cannot remove image."; + return; + } + QFile::remove(targetPath); + 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 QString baseDir = manifestDir(m_textEditorWidget); + const QString targetPath = baseDir + m_imagePath + m_imageFileName; + if (targetPath.isEmpty()) { + qCDebug(androidManifestEditorLog) << "Image target path empty, cannot load image."; + return; + } + QImage image = QImage(targetPath); + if (image.isNull()) { + qCDebug(androidManifestEditorLog) << "Cannot load image."; + 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; +} + +} // namespace Internal +} // namespace Android diff --git a/src/plugins/android/splashscreenwidget.h b/src/plugins/android/splashscreenwidget.h new file mode 100644 index 00000000000..ef91f1b0c37 --- /dev/null +++ b/src/plugins/android/splashscreenwidget.h @@ -0,0 +1,97 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#pragma once + +#include + +namespace TextEditor { class TextEditorWidget; } + +QT_BEGIN_NAMESPACE +class QLabel; +class QColor; +class QImage; +QT_END_NAMESPACE + +namespace Android { +namespace Internal { + +class SplashScreenWidget : public QWidget +{ + Q_OBJECT + + class SplashScreenButton : public QToolButton + { + public: + explicit SplashScreenButton(SplashScreenWidget *parent); + private: + void paintEvent(QPaintEvent *event) override; + SplashScreenWidget *m_parentWidget; + }; + +public: + explicit SplashScreenWidget(QWidget *parent); + 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 QString &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; +}; + +} // namespace Internal +} // namespace Android