Android: add service editor to manifest editor

Task-number: QTCREATORBUG-23937
Change-Id: Iec0435721504df744ec985bd3e5cefcc0700e852
Reviewed-by: Assam Boudjelthia <assam.boudjelthia@qt.io>
This commit is contained in:
Ville Voutilainen
2020-05-07 09:07:39 +03:00
parent 14666c801a
commit 52188918c0
8 changed files with 805 additions and 8 deletions

View File

@@ -40,6 +40,7 @@ add_qtc_plugin(Android
androidsdkmanagerwidget.cpp androidsdkmanagerwidget.h androidsdkmanagerwidget.ui androidsdkmanagerwidget.cpp androidsdkmanagerwidget.h androidsdkmanagerwidget.ui
androidsdkmodel.cpp androidsdkmodel.h androidsdkmodel.cpp androidsdkmodel.h
androidsdkpackage.cpp androidsdkpackage.h androidsdkpackage.cpp androidsdkpackage.h
androidservicewidget.cpp androidservicewidget.h androidservicewidget_p.h
androidsettingswidget.cpp androidsettingswidget.h androidsettingswidget.ui androidsettingswidget.cpp androidsettingswidget.h androidsettingswidget.ui
androidsignaloperation.cpp androidsignaloperation.h androidsignaloperation.cpp androidsignaloperation.h
androidtoolchain.cpp androidtoolchain.h androidtoolchain.cpp androidtoolchain.h

View File

@@ -13,6 +13,8 @@ HEADERS += \
androidmanifesteditoriconwidget.h \ androidmanifesteditoriconwidget.h \
androidrunconfiguration.h \ androidrunconfiguration.h \
androidruncontrol.h \ androidruncontrol.h \
androidservicewidget.h \
androidservicewidget_p.h \
androidsettingswidget.h \ androidsettingswidget.h \
androidtoolchain.h \ androidtoolchain.h \
androiderrormessage.h \ androiderrormessage.h \
@@ -60,6 +62,7 @@ SOURCES += \
androidmanifesteditoriconwidget.cpp \ androidmanifesteditoriconwidget.cpp \
androidrunconfiguration.cpp \ androidrunconfiguration.cpp \
androidruncontrol.cpp \ androidruncontrol.cpp \
androidservicewidget.cpp \
androidsettingswidget.cpp \ androidsettingswidget.cpp \
androidtoolchain.cpp \ androidtoolchain.cpp \
androiderrormessage.cpp \ androiderrormessage.cpp \

View File

@@ -93,6 +93,9 @@ Project {
"androidsdkmodel.h", "androidsdkmodel.h",
"androidsdkpackage.cpp", "androidsdkpackage.cpp",
"androidsdkpackage.h", "androidsdkpackage.h",
"androidservicewidget.cpp",
"androidservicewidget.h",
"androidservicewidget_p.h",
"androidsettingswidget.cpp", "androidsettingswidget.cpp",
"androidsettingswidget.h", "androidsettingswidget.h",
"androidsettingswidget.ui", "androidsettingswidget.ui",

View File

@@ -30,6 +30,7 @@
#include "androidconstants.h" #include "androidconstants.h"
#include "androidmanifestdocument.h" #include "androidmanifestdocument.h"
#include "androidmanager.h" #include "androidmanager.h"
#include "androidservicewidget.h"
#include <coreplugin/icore.h> #include <coreplugin/icore.h>
#include <coreplugin/infobar.h> #include <coreplugin/infobar.h>
@@ -68,6 +69,7 @@
#include <QLabel> #include <QLabel>
#include <QLineEdit> #include <QLineEdit>
#include <QListView> #include <QListView>
#include <QMessageBox>
#include <QPushButton> #include <QPushButton>
#include <QScrollArea> #include <QScrollArea>
#include <QSpinBox> #include <QSpinBox>
@@ -253,6 +255,9 @@ void AndroidManifestEditorWidget::initializePage()
formLayout->addRow(QString(), m_iconButtons); formLayout->addRow(QString(), m_iconButtons);
m_services = new AndroidServiceWidget(this);
formLayout->addRow(tr("Android services:"), m_services);
applicationGroupBox->setLayout(formLayout); applicationGroupBox->setLayout(formLayout);
connect(m_appNameLineEdit, &QLineEdit::textEdited, connect(m_appNameLineEdit, &QLineEdit::textEdited,
@@ -264,6 +269,12 @@ void AndroidManifestEditorWidget::initializePage()
connect(m_styleExtractMethod, connect(m_styleExtractMethod,
QOverload<int>::of(&QComboBox::currentIndexChanged), QOverload<int>::of(&QComboBox::currentIndexChanged),
this, setDirtyFunc); this, setDirtyFunc);
connect(m_services, &AndroidServiceWidget::servicesModified,
this, setDirtyFunc);
connect(m_services, &AndroidServiceWidget::servicesModified,
this, &AndroidManifestEditorWidget::clearInvalidServiceInfo);
connect(m_services, &AndroidServiceWidget::servicesInvalid,
this, &AndroidManifestEditorWidget::setInvalidServiceInfo);
} }
@@ -539,6 +550,14 @@ AndroidManifestEditorWidget::EditorPage AndroidManifestEditorWidget::activePage(
return AndroidManifestEditorWidget::EditorPage(currentIndex()); return AndroidManifestEditorWidget::EditorPage(currentIndex());
} }
bool servicesValid(const QList<AndroidServiceData> &services)
{
for (auto &&x : services)
if (!x.isValid())
return false;
return true;
}
bool AndroidManifestEditorWidget::setActivePage(EditorPage page) bool AndroidManifestEditorWidget::setActivePage(EditorPage page)
{ {
EditorPage prevPage = activePage(); EditorPage prevPage = activePage();
@@ -547,6 +566,11 @@ bool AndroidManifestEditorWidget::setActivePage(EditorPage page)
return true; return true;
if (page == Source) { if (page == Source) {
if (!servicesValid(m_services->services())) {
QMessageBox::critical(nullptr, tr("Service Definition Invalid"),
tr("Cannot switch to source when there are invalid services."));
return false;
}
syncToEditor(); syncToEditor();
} else { } else {
if (!syncToWidgets()) if (!syncToWidgets())
@@ -567,8 +591,14 @@ bool AndroidManifestEditorWidget::setActivePage(EditorPage page)
void AndroidManifestEditorWidget::preSave() void AndroidManifestEditorWidget::preSave()
{ {
if (activePage() != Source) if (activePage() != Source) {
if (!servicesValid(m_services->services())) {
QMessageBox::critical(nullptr, tr("Service Definition Invalid"),
tr("Cannot save when there are invalid services."));
return;
}
syncToEditor(); syncToEditor();
}
// no need to emit changed() since this is called as part of saving // no need to emit changed() since this is called as part of saving
updateInfoBar(); updateInfoBar();
@@ -707,7 +737,26 @@ void AndroidManifestEditorWidget::hideInfoBar()
{ {
Core::InfoBar *infoBar = m_textEditorWidget->textDocument()->infoBar(); Core::InfoBar *infoBar = m_textEditorWidget->textDocument()->infoBar();
infoBar->removeInfo(infoBarId); infoBar->removeInfo(infoBarId);
m_timerParseCheck.stop(); m_timerParseCheck.stop();
}
static const char kServicesInvalid[] = "AndroidServiceDefinitionInvalid";
void AndroidManifestEditorWidget::setInvalidServiceInfo()
{
Core::Id id(kServicesInvalid);
if (m_textEditorWidget->textDocument()->infoBar()->containsInfo(id))
return;
Core::InfoBarEntry info(id,
tr("Services invalid. "
"Manifest cannot be saved. Correct the service definitions before saving."));
m_textEditorWidget->textDocument()->infoBar()->addInfo(info);
}
void AndroidManifestEditorWidget::clearInvalidServiceInfo()
{
m_textEditorWidget->textDocument()->infoBar()->removeInfo(Core::Id(kServicesInvalid));
} }
void setApiLevel(QComboBox *box, const QDomElement &element, const QString &attribute) void setApiLevel(QComboBox *box, const QDomElement &element, const QString &attribute)
@@ -810,6 +859,33 @@ void AndroidManifestEditorWidget::syncToWidgets(const QDomDocument &doc)
m_permissionsModel->setPermissions(permissions); m_permissionsModel->setPermissions(permissions);
updateAddRemovePermissionButtons(); updateAddRemovePermissionButtons();
QList<AndroidServiceData> services;
QDomElement serviceElem = applicationElement.firstChildElement(QLatin1String("service"));
while (!serviceElem.isNull()) {
AndroidServiceData service;
service.setClassName(serviceElem.attribute(QLatin1String("android:name")));
QString process = serviceElem.attribute(QLatin1String("android:process"));
service.setRunInExternalProcess(!process.isEmpty());
service.setExternalProcessName(process);
QDomElement serviceMetadataElem = serviceElem.firstChildElement(QLatin1String("meta-data"));
while (!serviceMetadataElem.isNull()) {
QString metadataName = serviceMetadataElem.attribute(QLatin1String("android:name"));
if (metadataName == QLatin1String("android.app.lib_name")) {
QString metadataValue = serviceMetadataElem.attribute(QLatin1String("android:value"));
service.setRunInExternalLibrary(metadataValue != QLatin1String("-- %%INSERT_APP_LIB_NAME%% --"));
service.setExternalLibraryName(metadataValue);
}
else if (metadataName == QLatin1String("android.app.arguments")) {
QString metadataValue = serviceMetadataElem.attribute(QLatin1String("android:value"));
service.setServiceArguments(metadataValue);
}
serviceMetadataElem = serviceMetadataElem.nextSiblingElement(QLatin1String("meta-data"));
}
services << service;
serviceElem = serviceElem.nextSiblingElement(QLatin1String("service"));
}
m_services->setServices(services);
m_iconButtons->loadIcons(); m_iconButtons->loadIcons();
m_stayClean = false; m_stayClean = false;
@@ -988,13 +1064,19 @@ void AndroidManifestEditorWidget::parseApplication(QXmlStreamReader &reader, QXm
while (!reader.atEnd()) { while (!reader.atEnd()) {
if (reader.isEndElement()) { if (reader.isEndElement()) {
parseNewServices(writer);
writer.writeCurrentToken(reader); writer.writeCurrentToken(reader);
m_services->servicesSaved();
return; return;
} else if (reader.isStartElement()) { } else if (reader.isStartElement()) {
if (reader.name() == QLatin1String("activity")) if (reader.name() == QLatin1String("activity"))
parseActivity(reader, writer); parseActivity(reader, writer);
else if (reader.name() == QLatin1String("service"))
parseService(reader, writer);
else else
parseUnknownElement(reader, writer); parseUnknownElement(reader, writer);
} else if (reader.isWhitespace()) {
/* no copying of whitespace */
} else { } else {
writer.writeCurrentToken(reader); writer.writeCurrentToken(reader);
} }
@@ -1003,6 +1085,144 @@ void AndroidManifestEditorWidget::parseApplication(QXmlStreamReader &reader, QXm
} }
} }
static int findService(const QString &name, const QList<AndroidServiceData> &data)
{
for (int i = 0; i < data.size(); ++i) {
if (data[i].className() == name)
return i;
}
return -1;
}
static void writeServiceMetadataElement(const char *name,
const char *attributeName,
const char *value,
QXmlStreamWriter &writer)
{
writer.writeStartElement(QLatin1String("meta-data"));
writer.writeAttribute(QLatin1String("android:name"), QLatin1String(name));
writer.writeAttribute(QLatin1String(attributeName), QLatin1String(value));
writer.writeEndElement();
}
static void writeServiceMetadataElement(const char *name,
const char *attributeName,
const QString &value,
QXmlStreamWriter &writer)
{
writer.writeStartElement(QLatin1String("meta-data"));
writer.writeAttribute(QLatin1String("android:name"), QLatin1String(name));
writer.writeAttribute(QLatin1String(attributeName), value);
writer.writeEndElement();
}
static void addServiceArgumentsAndLibName(const AndroidServiceData &service, QXmlStreamWriter &writer)
{
if (!service.isRunInExternalLibrary() && !service.serviceArguments().isEmpty())
writeServiceMetadataElement("android.app.arguments", "android:value", service.serviceArguments(), writer);
if (service.isRunInExternalLibrary() && !service.externalLibraryName().isEmpty())
writeServiceMetadataElement("android.app.lib_name", "android:value", service.externalLibraryName(), writer);
else
writeServiceMetadataElement("android.app.lib_name", "android:value", "-- %%INSERT_APP_LIB_NAME%% --", writer);
}
static void addServiceMetadata(QXmlStreamWriter &writer)
{
writeServiceMetadataElement("android.app.qt_sources_resource_id", "android:resource", "@array/qt_sources", writer);
writeServiceMetadataElement("android.app.repository", "android:value", "default", writer);
writeServiceMetadataElement("android.app.qt_libs_resource_id", "android:resource", "@array/qt_libs", writer);
writeServiceMetadataElement("android.app.bundled_libs_resource_id", "android:resource", "@array/bundled_libs", writer);
writeServiceMetadataElement("android.app.bundle_local_qt_libs", "android:value", "-- %%BUNDLE_LOCAL_QT_LIBS%% --", writer);
writeServiceMetadataElement("android.app.use_local_qt_libs", "android:value", "-- %%USE_LOCAL_QT_LIBS%% --", writer);
writeServiceMetadataElement("android.app.libs_prefix", "android:value", "/data/local/tmp/qt/", writer);
writeServiceMetadataElement("android.app.load_local_libs_resource_id", "android:resource", "@array/load_local_libs", writer);
writeServiceMetadataElement("android.app.load_local_jars", "android:value", "-- %%INSERT_LOCAL_JARS%% --", writer);
writeServiceMetadataElement("android.app.static_init_classes", "android:value", "-- %%INSERT_INIT_CLASSES%% --", writer);
}
void AndroidManifestEditorWidget::parseService(QXmlStreamReader &reader, QXmlStreamWriter &writer)
{
Q_ASSERT(reader.isStartElement());
const auto &services = m_services->services();
QString serviceName = reader.attributes().value(QLatin1String("android:name")).toString();
int serviceIndex = findService(serviceName, services);
const AndroidServiceData* serviceFound = (serviceIndex >= 0) ? &services[serviceIndex] : nullptr;
if (serviceFound && serviceFound->isValid()) {
writer.writeStartElement(reader.name().toString());
writer.writeAttribute(QLatin1String("android:name"), serviceFound->className());
if (serviceFound->isRunInExternalProcess())
writer.writeAttribute(QLatin1String("android:process"), serviceFound->externalProcessName());
}
reader.readNext();
bool bundleTagFound = false;
while (!reader.atEnd()) {
if (reader.isEndElement()) {
if (serviceFound && serviceFound->isValid()) {
addServiceArgumentsAndLibName(*serviceFound, writer);
if (serviceFound->isRunInExternalProcess() && !bundleTagFound)
addServiceMetadata(writer);
writer.writeCurrentToken(reader);
}
return;
} else if (reader.isStartElement()) {
if (serviceFound && !serviceFound->isValid())
parseUnknownElement(reader, writer, true);
else if (reader.name() == QLatin1String("meta-data")) {
QString metaTagName = reader.attributes().value(QLatin1String("android:name")).toString();
if (serviceFound) {
if (metaTagName == QLatin1String("android.app.bundle_local_qt_libs"))
bundleTagFound = true;
if (metaTagName == QLatin1String("android.app.arguments"))
parseUnknownElement(reader, writer, true);
else if (metaTagName == QLatin1String("android.app.lib_name"))
parseUnknownElement(reader, writer, true);
else if (serviceFound->isRunInExternalProcess()
|| metaTagName == QLatin1String("android.app.background_running"))
parseUnknownElement(reader, writer);
else
parseUnknownElement(reader, writer, true);
} else
parseUnknownElement(reader, writer, true);
} else
parseUnknownElement(reader, writer, true);
} else if (reader.isWhitespace()) {
/* no copying of whitespace */
} else {
if (serviceFound)
writer.writeCurrentToken(reader);
}
reader.readNext();
}
}
void AndroidManifestEditorWidget::parseNewServices(QXmlStreamWriter &writer)
{
const auto &services = m_services->services();
for (const auto &x : services) {
if (x.isNewService() && x.isValid()) {
writer.writeStartElement(QLatin1String("service"));
writer.writeAttribute(QLatin1String("android:name"), x.className());
if (x.isRunInExternalProcess()) {
writer.writeAttribute(QLatin1String("android:process"),
x.externalProcessName());
}
addServiceArgumentsAndLibName(x, writer);
if (x.isRunInExternalProcess())
addServiceMetadata(writer);
writer.writeStartElement(QLatin1String("meta-data"));
writer.writeAttribute(QLatin1String("android:name"), QLatin1String("android.app.background_running"));
writer.writeAttribute(QLatin1String("android:value"), QLatin1String("true"));
writer.writeEndElement();
writer.writeEndElement();
}
}
}
void AndroidManifestEditorWidget::parseActivity(QXmlStreamReader &reader, QXmlStreamWriter &writer) void AndroidManifestEditorWidget::parseActivity(QXmlStreamReader &reader, QXmlStreamWriter &writer)
{ {
Q_ASSERT(reader.isStartElement()); Q_ASSERT(reader.isStartElement());
@@ -1180,20 +1400,24 @@ QString AndroidManifestEditorWidget::parseComment(QXmlStreamReader &reader, QXml
return commentText; return commentText;
} }
void AndroidManifestEditorWidget::parseUnknownElement(QXmlStreamReader &reader, QXmlStreamWriter &writer) void AndroidManifestEditorWidget::parseUnknownElement(QXmlStreamReader &reader, QXmlStreamWriter &writer,
bool ignore)
{ {
Q_ASSERT(reader.isStartElement()); Q_ASSERT(reader.isStartElement());
writer.writeCurrentToken(reader); if (!ignore)
writer.writeCurrentToken(reader);
reader.readNext(); reader.readNext();
while (!reader.atEnd()) { while (!reader.atEnd()) {
if (reader.isEndElement()) { if (reader.isEndElement()) {
writer.writeCurrentToken(reader); if (!ignore)
writer.writeCurrentToken(reader);
return; return;
} else if (reader.isStartElement()) { } else if (reader.isStartElement()) {
parseUnknownElement(reader, writer); parseUnknownElement(reader, writer, ignore);
} else { } else {
writer.writeCurrentToken(reader); if (!ignore)
writer.writeCurrentToken(reader);
} }
reader.readNext(); reader.readNext();
} }

View File

@@ -54,6 +54,7 @@ namespace Internal {
class AndroidManifestEditor; class AndroidManifestEditor;
class AndroidManifestEditorIconContainerWidget; class AndroidManifestEditorIconContainerWidget;
class AndroidManifestEditorWidget; class AndroidManifestEditorWidget;
class AndroidServiceWidget;
class PermissionsModel: public QAbstractListModel class PermissionsModel: public QAbstractListModel
{ {
@@ -136,10 +137,15 @@ private:
void updateInfoBar(const QString &errorMessage, int line, int column); void updateInfoBar(const QString &errorMessage, int line, int column);
void hideInfoBar(); void hideInfoBar();
void setInvalidServiceInfo();
void clearInvalidServiceInfo();
void updateTargetComboBox(); void updateTargetComboBox();
void parseManifest(QXmlStreamReader &reader, QXmlStreamWriter &writer); void parseManifest(QXmlStreamReader &reader, QXmlStreamWriter &writer);
void parseApplication(QXmlStreamReader &reader, QXmlStreamWriter &writer); void parseApplication(QXmlStreamReader &reader, QXmlStreamWriter &writer);
void parseService(QXmlStreamReader &reader, QXmlStreamWriter &writer);
void parseNewServices(QXmlStreamWriter &writer);
void parseActivity(QXmlStreamReader &reader, QXmlStreamWriter &writer); void parseActivity(QXmlStreamReader &reader, QXmlStreamWriter &writer);
bool parseMetaData(QXmlStreamReader &reader, QXmlStreamWriter &writer); bool parseMetaData(QXmlStreamReader &reader, QXmlStreamWriter &writer);
void parseUsesSdk(QXmlStreamReader &reader, QXmlStreamWriter &writer); void parseUsesSdk(QXmlStreamReader &reader, QXmlStreamWriter &writer);
@@ -147,7 +153,7 @@ private:
QXmlStreamWriter &writer, QXmlStreamWriter &writer,
const QSet<QString> &permissions); const QSet<QString> &permissions);
QString parseComment(QXmlStreamReader &reader, QXmlStreamWriter &writer); QString parseComment(QXmlStreamReader &reader, QXmlStreamWriter &writer);
void parseUnknownElement(QXmlStreamReader &reader, QXmlStreamWriter &writer); void parseUnknownElement(QXmlStreamReader &reader, QXmlStreamWriter &writer, bool ignore=false);
bool m_dirty; // indicates that we need to call syncToEditor() bool m_dirty; // indicates that we need to call syncToEditor()
bool m_stayClean; bool m_stayClean;
@@ -178,6 +184,8 @@ private:
QPushButton *m_removePermissionButton; QPushButton *m_removePermissionButton;
QComboBox *m_permissionsComboBox; QComboBox *m_permissionsComboBox;
// Services
AndroidServiceWidget *m_services;
QTimer m_timerParseCheck; QTimer m_timerParseCheck;
TextEditor::TextEditorWidget *m_textEditorWidget; TextEditor::TextEditorWidget *m_textEditorWidget;
AndroidManifestEditor *m_editor; AndroidManifestEditor *m_editor;

View File

@@ -0,0 +1,410 @@
/****************************************************************************
**
** 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 "androidservicewidget.h"
#include "androidservicewidget_p.h"
#include <utils/utilsicons.h>
#include <QAbstractTableModel>
#include <QGridLayout>
#include <QHBoxLayout>
#include <QHeaderView>
#include <QPushButton>
#include <QTableView>
namespace Android {
namespace Internal {
bool AndroidServiceData::isValid() const
{
return !m_className.isEmpty()
&& (!m_isRunInExternalProcess || !m_externalProcessName.isEmpty())
&& (!m_isRunInExternalLibrary || !m_externalLibName.isEmpty())
&& (m_isRunInExternalLibrary || !m_serviceArguments.isEmpty());
}
void AndroidServiceData::setClassName(const QString &className)
{
m_className = className;
}
QString AndroidServiceData::className() const
{
return m_className;
}
void AndroidServiceData::setRunInExternalProcess(bool isRunInExternalProcess)
{
m_isRunInExternalProcess = isRunInExternalProcess;
if (!m_isRunInExternalProcess) {
m_isRunInExternalLibrary = false;
m_externalProcessName.clear();
m_externalLibName.clear();
}
}
bool AndroidServiceData::isRunInExternalProcess() const
{
return m_isRunInExternalProcess;
}
void AndroidServiceData::setExternalProcessName(const QString &externalProcessName)
{
if (m_isRunInExternalProcess)
m_externalProcessName = externalProcessName;
}
QString AndroidServiceData::externalProcessName() const
{
return m_externalProcessName;
}
void AndroidServiceData::setRunInExternalLibrary(bool isRunInExternalLibrary)
{
if (m_isRunInExternalProcess)
m_isRunInExternalLibrary = isRunInExternalLibrary;
if (!m_isRunInExternalLibrary)
m_externalLibName.clear();
else
m_serviceArguments.clear();
}
bool AndroidServiceData::isRunInExternalLibrary() const
{
return m_isRunInExternalLibrary;
}
void AndroidServiceData::setExternalLibraryName(const QString &externalLibraryName)
{
if (m_isRunInExternalLibrary)
m_externalLibName = externalLibraryName;
}
QString AndroidServiceData::externalLibraryName() const
{
return m_externalLibName;
}
void AndroidServiceData::setServiceArguments(const QString &serviceArguments)
{
if (!m_isRunInExternalLibrary)
m_serviceArguments = serviceArguments;
}
QString AndroidServiceData::serviceArguments() const
{
return m_serviceArguments;
}
void AndroidServiceData::setNewService(bool isNewService)
{
m_isNewService = isNewService;
}
bool AndroidServiceData::isNewService() const
{
return m_isNewService;
}
void AndroidServiceWidget::AndroidServiceModel::setServices(const QList<AndroidServiceData> &androidServices)
{
beginResetModel();
m_services = androidServices;
endResetModel();
}
const QList<AndroidServiceData> &AndroidServiceWidget::AndroidServiceModel::services()
{
return m_services;
}
void AndroidServiceWidget::AndroidServiceModel::addService()
{
int rowIndex = m_services.size();
beginInsertRows(QModelIndex(), rowIndex, rowIndex);
AndroidServiceData service;
service.setNewService(true);
m_services.push_back(service);
endInsertRows();
invalidDataChanged();
}
void AndroidServiceWidget::AndroidServiceModel::removeService(int row)
{
beginRemoveRows(QModelIndex(), row, row);
m_services.removeAt(row);
endRemoveRows();
}
void AndroidServiceWidget::AndroidServiceModel::servicesSaved()
{
for (auto && x : m_services)
x.setNewService(false);
}
int AndroidServiceWidget::AndroidServiceModel::rowCount(const QModelIndex &/*parent*/) const
{
return m_services.count();
}
int AndroidServiceWidget::AndroidServiceModel::columnCount(const QModelIndex &/*parent*/) const
{
return 6;
}
Qt::ItemFlags AndroidServiceWidget::AndroidServiceModel::flags(const QModelIndex &index) const
{
if (index.column() == 0)
return Qt::ItemIsEnabled | Qt::ItemIsEditable | Qt::ItemIsSelectable;
else if (index.column() == 1)
return Qt::ItemIsEnabled | Qt::ItemIsUserCheckable | Qt::ItemIsSelectable;
else if (index.column() == 2 && index.row() <= m_services.count()) {
if (m_services[index.row()].isRunInExternalProcess())
return Qt::ItemIsEnabled | Qt::ItemIsEditable | Qt::ItemIsSelectable;
return Qt::ItemIsSelectable;
} else if (index.column() == 3 && index.row() <= m_services.count()) {
if (m_services[index.row()].isRunInExternalProcess())
return Qt::ItemIsEnabled | Qt::ItemIsUserCheckable | Qt::ItemIsSelectable;
return Qt::ItemIsUserCheckable | Qt::ItemIsSelectable;
} else if (index.column() == 4 && index.row() <= m_services.count()) {
if (m_services[index.row()].isRunInExternalLibrary())
return Qt::ItemIsEnabled | Qt::ItemIsEditable | Qt::ItemIsSelectable;
return Qt::ItemIsSelectable;
} else if (index.column() == 5 && index.row() <= m_services.count()) {
if (!m_services[index.row()].isRunInExternalLibrary())
return Qt::ItemIsEnabled | Qt::ItemIsEditable | Qt::ItemIsSelectable;
return Qt::ItemIsSelectable;
}
return Qt::ItemIsSelectable;
}
QVariant AndroidServiceWidget::AndroidServiceModel::headerData(int section, Qt::Orientation orientation, int role) const
{
if (role == Qt::ToolTipRole && orientation == Qt::Horizontal) {
if (section == 0)
return tr("The name of the class implementing the service");
else if (section == 1)
return tr("Checked if the service is run in an external process");
else if (section == 2)
return tr("The name of the external process.\n"
"Prefix with : if the process is private, use a lowercase name if the process is global.");
else if (section == 3)
return tr("Checked if the service is in a separate dynamic library");
else if (section == 4)
return tr("The name of the separate dynamic library");
else if (section == 5)
return tr("The arguments for telling the app to run the service instead of the main activity");
} else if (role == Qt::DisplayRole && orientation == Qt::Horizontal) {
if (section == 0)
return tr("Service class name");
else if (section == 1)
return tr("Run in external process");
else if (section == 2)
return tr("Process name");
else if (section == 3)
return tr("Run in external library");
else if (section == 4)
return tr("Library name");
else if (section == 5)
return tr("Service arguments");
}
return {};
}
QVariant AndroidServiceWidget::AndroidServiceModel::data(const QModelIndex &index, int role) const
{
if (!(index.row() >= 0 && index.row() < m_services.count()))
return {};
if (role == Qt::CheckStateRole) {
if (index.column() == 3)
return m_services[index.row()].isRunInExternalLibrary() ? Qt::Checked : Qt::Unchecked;
else if (index.column() == 1 && index.row() <= m_services.count())
return m_services[index.row()].isRunInExternalProcess() ? Qt::Checked : Qt::Unchecked;
return QVariant();
} else if (role == Qt::DisplayRole) {
if (index.column() == 0)
return m_services[index.row()].className();
else if (index.column() == 1)
return tr("Run in external process");
else if (index.column() == 2)
return m_services[index.row()].externalProcessName();
else if (index.column() == 3)
return tr("Run in external library");
else if (index.column() == 4)
return m_services[index.row()].externalLibraryName();
else if (index.column() == 5)
return m_services[index.row()].serviceArguments();
} else if (role == Qt::ToolTipRole) {
if (index.column() == 0 && m_services[index.row()].className().isEmpty())
return tr("The class name must be set");
else if (index.column() == 2 && m_services[index.row()].isRunInExternalProcess())
return tr("The process name must be set for a service run in an external process");
else if (index.column() == 4 && m_services[index.row()].isRunInExternalLibrary())
return tr("The library name must be set for a service run in an external library");
else if (index.column() == 5 && !m_services[index.row()].isRunInExternalLibrary())
return tr("The service arguments must be set for a service not run in an external library");
} else if (role == Qt::EditRole) {
if (index.column() == 0)
return m_services[index.row()].className();
else if (index.column() == 2)
return m_services[index.row()].externalProcessName();
else if (index.column() == 4)
return m_services[index.row()].externalLibraryName();
else if (index.column() == 5)
return m_services[index.row()].serviceArguments();
} else if (role == Qt::DecorationRole) {
if (index.column() == 0) {
if (m_services[index.row()].className().isEmpty())
return Utils::Icons::WARNING.icon();
} else if (index.column() == 2) {
if (m_services[index.row()].isRunInExternalProcess()
&& m_services[index.row()].externalProcessName().isEmpty())
return Utils::Icons::WARNING.icon();
} else if (index.column() == 4) {
if (m_services[index.row()].isRunInExternalLibrary()
&& m_services[index.row()].externalLibraryName().isEmpty())
return Utils::Icons::WARNING.icon();
} else if (index.column() == 5) {
if (!m_services[index.row()].isRunInExternalLibrary()
&& m_services[index.row()].serviceArguments().isEmpty())
return Utils::Icons::WARNING.icon();
}
}
return {};
}
bool AndroidServiceWidget::AndroidServiceModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
if (!(index.row() >= 0 && index.row() < m_services.count()))
return {};
if (role == Qt::CheckStateRole) {
if (index.column() == 1)
m_services[index.row()].setRunInExternalProcess((value == Qt::Checked) ? true : false);
else if (index.column() == 3)
m_services[index.row()].setRunInExternalLibrary((value == Qt::Checked) ? true : false);
dataChanged(createIndex(index.row(), 0), createIndex(index.row(), 5));
if (m_services[index.row()].isValid())
validDataChanged();
else
invalidDataChanged();
} else if (role == Qt::EditRole) {
if (index.column() == 0) {
QString className = value.toString();
if (!className.isEmpty() && className[0] != QChar('.'))
className.push_front(QChar('.'));
m_services[index.row()].setClassName(className);
m_services[index.row()].setNewService(true);
} else if (index.column() == 2) {
m_services[index.row()].setExternalProcessName(value.toString());
} else if (index.column() == 4) {
m_services[index.row()].setExternalLibraryName(value.toString());
} else if (index.column() == 5) {
m_services[index.row()].setServiceArguments(value.toString());
}
dataChanged(index, index);
if (m_services[index.row()].isValid())
validDataChanged();
else
invalidDataChanged();
}
return true;
}
AndroidServiceWidget::AndroidServiceWidget(QWidget *parent) : QWidget(parent),
m_model(new AndroidServiceModel), m_tableView(new QTableView(this))
{
m_tableView->setModel(m_model.data());
m_tableView->setSelectionBehavior(QAbstractItemView::SelectRows);
QSizePolicy sizePolicy;
sizePolicy.setHorizontalPolicy(QSizePolicy::Expanding);
m_tableView->setSizePolicy(sizePolicy);
m_tableView->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch);
auto layout = new QHBoxLayout(this);
layout->addWidget(m_tableView, 1);
auto buttonLayout = new QGridLayout();
auto addButton = new QPushButton(this);
addButton->setText(tr("Add"));
buttonLayout->addWidget(addButton, 0, 0);
m_removeButton = new QPushButton(this);
m_removeButton->setText(tr("Remove"));
m_removeButton->setEnabled(false);
buttonLayout->addWidget(m_removeButton, 1, 0);
layout->addLayout(buttonLayout);
layout->setAlignment(buttonLayout, Qt::AlignTop);
connect(addButton, &QAbstractButton::clicked,
this, &AndroidServiceWidget::addService);
connect(m_removeButton, &QAbstractButton::clicked,
this, &AndroidServiceWidget::removeService);
connect(m_tableView->selectionModel(), &QItemSelectionModel::selectionChanged,
[this](const QItemSelection &selected, const QItemSelection &/*deselected*/) {
if (!selected.isEmpty())
m_removeButton->setEnabled(true);
});
connect(m_model.data(), &AndroidServiceWidget::AndroidServiceModel::validDataChanged,
[this] {servicesModified();});
connect(m_model.data(), &AndroidServiceWidget::AndroidServiceModel::invalidDataChanged,
[this] {servicesInvalid();});
}
AndroidServiceWidget::~AndroidServiceWidget()
{
}
void AndroidServiceWidget::setServices(const QList<AndroidServiceData> &androidServices)
{
m_removeButton->setEnabled(false);
m_model->setServices(androidServices);
}
const QList<AndroidServiceData> &AndroidServiceWidget::services()
{
return m_model->services();
}
void AndroidServiceWidget::servicesSaved()
{
m_model->servicesSaved();
}
void AndroidServiceWidget::addService()
{
m_model->addService();
}
void AndroidServiceWidget::removeService()
{
auto selections = m_tableView->selectionModel()->selectedRows();
for (const auto &x : selections) {
m_model->removeService(x.row());
m_removeButton->setEnabled(false);
servicesModified();
break;
}
}
} // namespace Internal
} // namespace Android

View File

@@ -0,0 +1,89 @@
/****************************************************************************
**
** 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 <QList>
#include <QString>
#include <QWidget>
class QPushButton;
class QTableView;
namespace Android {
namespace Internal {
struct AndroidServiceData
{
public:
bool isValid() const;
void setClassName(const QString &className);
QString className() const;
void setRunInExternalProcess(bool isRunInExternalProcess);
bool isRunInExternalProcess() const;
void setExternalProcessName(const QString &externalProcessName);
QString externalProcessName() const;
void setRunInExternalLibrary(bool isRunInExternalLibrary);
bool isRunInExternalLibrary() const ;
void setExternalLibraryName(const QString &externalLibraryName);
QString externalLibraryName() const;
void setServiceArguments(const QString &serviceArguments);
QString serviceArguments() const;
void setNewService(bool isNewService);
bool isNewService() const;
private:
QString m_className;
bool m_isRunInExternalProcess = false;
QString m_externalProcessName;
bool m_isRunInExternalLibrary = false;
QString m_externalLibName;
QString m_serviceArguments;
bool m_isNewService = false;
};
class AndroidServiceWidget : public QWidget
{
Q_OBJECT
public:
explicit AndroidServiceWidget(QWidget *parent = nullptr);
~AndroidServiceWidget();
void setServices(const QList<AndroidServiceData> &androidServices);
const QList<AndroidServiceData> &services();
void servicesSaved();
private:
void addService();
void removeService();
signals:
void servicesModified();
void servicesInvalid();
private:
class AndroidServiceModel;
QScopedPointer<AndroidServiceModel> m_model;
QTableView *m_tableView;
QPushButton *m_removeButton;
};
} // namespace Internal
} // namespace Android

View File

@@ -0,0 +1,59 @@
/****************************************************************************
**
** 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 "androidservicewidget.h"
#include <QAbstractTableModel>
namespace Android {
namespace Internal {
class AndroidServiceWidget::AndroidServiceModel : public QAbstractTableModel
{
Q_OBJECT
public:
void setServices(const QList<AndroidServiceData> &androidServices);
const QList<AndroidServiceData> &services();
void addService();
void removeService(int row);
void servicesSaved();
signals:
void validDataChanged();
void invalidDataChanged();
private:
int rowCount(const QModelIndex &/*parent*/ = QModelIndex()) const override;
int columnCount(const QModelIndex &/*parent*/ = QModelIndex()) const override;
Qt::ItemFlags flags(const QModelIndex &index) const override;
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override;
private:
QList<AndroidServiceData> m_services;
};
} // namespace Internal
} // namespace Android