diff options
author | BogDan Vatra <[email protected]> | 2012-04-18 20:30:57 +0300 |
---|---|---|
committer | Daniel Teske <[email protected]> | 2012-04-24 12:25:40 +0200 |
commit | 50e8f7bb7be503a922986a74e386f5ad4df22736 (patch) | |
tree | 80c94d097c7c5efde2fe78b22c8d1370d81fbe73 /src/plugins/android | |
parent | cf4ecad4bc20f246e1c67047b158ac2fe799183b (diff) |
Android plugin
Change-Id: Iad58914f067a6ef6d3412ccd42f757d821e39e45
Reviewed-by: Alessandro Portale <[email protected]>
Diffstat (limited to 'src/plugins/android')
63 files changed, 11589 insertions, 0 deletions
diff --git a/src/plugins/android/Android.pluginspec.in b/src/plugins/android/Android.pluginspec.in new file mode 100644 index 00000000000..94083f57664 --- /dev/null +++ b/src/plugins/android/Android.pluginspec.in @@ -0,0 +1,22 @@ +<plugin name=\"Android\" version=\"$$QTCREATOR_VERSION\" compatVersion=\"$$QTCREATOR_VERSION\"> + <vendor>KDE Necessitas</vendor> + <copyright>(C) 2010-2012 BogDan Vatra</copyright> + <license> +Commercial Usage + +Licensees holding valid Qt Commercial licenses may use this plugin in accordance with the Qt Commercial License Agreement provided with the Software or, alternatively, in accordance with the terms contained in a written agreement between you and Nokia. + +GNU Lesser General Public License Usage + +Alternatively, this plugin may be used under the terms of the GNU Lesser General Public License version 2.1 as published by the Free Software Foundation. Please review the following information to ensure the GNU Lesser General Public License version 2.1 requirements will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. + </license> + <description>Support for deployment to and execution on Android Devices</description> + <category>Device Support</category> + <url>http://community.kde.org/Necessitas</url> + <dependencyList> + <dependency name=\"Core\" version=\"$$QTCREATOR_VERSION\"/> + <dependency name=\"Debugger\" version=\"$$QTCREATOR_VERSION\"/> + <dependency name=\"ProjectExplorer\" version=\"$$QTCREATOR_VERSION\"/> + <dependency name=\"Qt4ProjectManager\" version=\"$$QTCREATOR_VERSION\"/> + </dependencyList> +</plugin> diff --git a/src/plugins/android/addnewavddialog.ui b/src/plugins/android/addnewavddialog.ui new file mode 100644 index 00000000000..149f79dfcf9 --- /dev/null +++ b/src/plugins/android/addnewavddialog.ui @@ -0,0 +1,133 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>AddNewAVDDialog</class> + <widget class="QDialog" name="AddNewAVDDialog"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>250</width> + <height>149</height> + </rect> + </property> + <property name="windowTitle"> + <string>Create new AVD</string> + </property> + <layout class="QVBoxLayout" name="verticalLayout"> + <item> + <layout class="QGridLayout" name="gridLayout"> + <item row="0" column="0"> + <widget class="QLabel" name="label"> + <property name="text"> + <string>Name:</string> + </property> + <property name="alignment"> + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + </widget> + </item> + <item row="0" column="1"> + <widget class="QLineEdit" name="nameLineEdit"/> + </item> + <item row="1" column="0"> + <widget class="QLabel" name="label_2"> + <property name="text"> + <string>Target:</string> + </property> + <property name="alignment"> + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + </widget> + </item> + <item row="1" column="1"> + <widget class="QComboBox" name="targetComboBox"/> + </item> + <item row="2" column="0"> + <widget class="QLabel" name="label_3"> + <property name="text"> + <string>SD card size:</string> + </property> + <property name="alignment"> + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + </widget> + </item> + <item row="2" column="1"> + <widget class="QSpinBox" name="sizeSpinBox"> + <property name="alignment"> + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + <property name="suffix"> + <string> MiB</string> + </property> + <property name="minimum"> + <number>50</number> + </property> + <property name="maximum"> + <number>9999999</number> + </property> + </widget> + </item> + </layout> + </item> + <item> + <spacer name="verticalSpacer"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="QDialogButtonBox" name="buttonBox"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="standardButtons"> + <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set> + </property> + </widget> + </item> + </layout> + </widget> + <resources/> + <connections> + <connection> + <sender>buttonBox</sender> + <signal>accepted()</signal> + <receiver>AddNewAVDDialog</receiver> + <slot>accept()</slot> + <hints> + <hint type="sourcelabel"> + <x>222</x> + <y>134</y> + </hint> + <hint type="destinationlabel"> + <x>157</x> + <y>148</y> + </hint> + </hints> + </connection> + <connection> + <sender>buttonBox</sender> + <signal>rejected()</signal> + <receiver>AddNewAVDDialog</receiver> + <slot>reject()</slot> + <hints> + <hint type="sourcelabel"> + <x>245</x> + <y>140</y> + </hint> + <hint type="destinationlabel"> + <x>249</x> + <y>148</y> + </hint> + </hints> + </connection> + </connections> +</ui> diff --git a/src/plugins/android/android.pri b/src/plugins/android/android.pri new file mode 100644 index 00000000000..ca008aba34b --- /dev/null +++ b/src/plugins/android/android.pri @@ -0,0 +1,3 @@ +include(android_dependencies.pri) + +LIBS *= -l$$qtLibraryName(Android) diff --git a/src/plugins/android/android.pro b/src/plugins/android/android.pro new file mode 100644 index 00000000000..d7168d8746d --- /dev/null +++ b/src/plugins/android/android.pro @@ -0,0 +1,77 @@ +TEMPLATE = lib +TARGET = Android + +include(../../qtcreatorplugin.pri) +include(android_dependencies.pri) + +QT += xml network + +HEADERS += \ + androidconstants.h \ + androidconfigurations.h \ + androidrunconfiguration.h \ + androidruncontrol.h \ + androidrunfactories.h \ + androidsettingspage.h \ + androidsettingswidget.h \ + androidtoolchain.h \ + androidpackageinstallationstep.h \ + androidpackageinstallationfactory.h \ + androidpackagecreationstep.h \ + androidpackagecreationfactory.h \ + androidpackagecreationwidget.h \ + androiddeploystep.h \ + androiddeploystepwidget.h \ + androiddeploystepfactory.h \ + androidglobal.h \ + androidrunner.h \ + androiddebugsupport.h \ + androidqtversionfactory.h \ + androidqtversion.h \ + androiddeployconfiguration.h \ + androidtarget.h \ + androidtargetfactory.h \ + androidcreatekeystorecertificate.h \ + javaparser.h \ + androidplugin.h + +SOURCES += \ + androidconfigurations.cpp \ + androidrunconfiguration.cpp \ + androidruncontrol.cpp \ + androidrunfactories.cpp \ + androidsettingspage.cpp \ + androidsettingswidget.cpp \ + androidtoolchain.cpp \ + androidpackageinstallationstep.cpp \ + androidpackageinstallationfactory.cpp \ + androidpackagecreationstep.cpp \ + androidpackagecreationfactory.cpp \ + androidpackagecreationwidget.cpp \ + androiddeploystep.cpp \ + androiddeploystepwidget.cpp \ + androiddeploystepfactory.cpp \ + androidrunner.cpp \ + androiddebugsupport.cpp \ + androidqtversionfactory.cpp \ + androidqtversion.cpp \ + androiddeployconfiguration.cpp \ + androidtarget.cpp \ + androidtargetfactory.cpp \ + androidcreatekeystorecertificate.cpp \ + javaparser.cpp \ + androidplugin.cpp + + +FORMS += \ + androidsettingswidget.ui \ + androidpackagecreationwidget.ui \ + androiddeploystepwidget.ui \ + addnewavddialog.ui \ + androidcreatekeystorecertificate.ui + +RESOURCES = android.qrc +DEFINES += QT_NO_CAST_TO_ASCII +DEFINES += ANDROID_LIBRARY + + diff --git a/src/plugins/android/android.qrc b/src/plugins/android/android.qrc new file mode 100644 index 00000000000..94570ee5f5d --- /dev/null +++ b/src/plugins/android/android.qrc @@ -0,0 +1,5 @@ +<RCC> + <qresource prefix="/android"> + <file>images/QtAndroid.png</file> + </qresource> +</RCC> diff --git a/src/plugins/android/android_dependencies.pri b/src/plugins/android/android_dependencies.pri new file mode 100644 index 00000000000..c2940f93623 --- /dev/null +++ b/src/plugins/android/android_dependencies.pri @@ -0,0 +1,4 @@ +include(../../plugins/coreplugin/coreplugin.pri) +include(../../plugins/debugger/debugger.pri) +include(../../plugins/projectexplorer/projectexplorer.pri) +include(../../plugins/qt4projectmanager/qt4projectmanager.pri) diff --git a/src/plugins/android/androidconfigurations.cpp b/src/plugins/android/androidconfigurations.cpp new file mode 100644 index 00000000000..1da6fd6c994 --- /dev/null +++ b/src/plugins/android/androidconfigurations.cpp @@ -0,0 +1,632 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2012 BogDan Vatra <[email protected]> +** +** Contact: Nokia Corporation ([email protected]) +** +** +** GNU Lesser General Public License Usage +** +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this file. +** Please review the following information to ensure the GNU Lesser General +** Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** Other Usage +** +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** If you have questions regarding the use of this file, please contact +** Nokia at [email protected]. +** +**************************************************************************/ + +#include "androidconfigurations.h" +#include "androidconstants.h" +#include "ui_addnewavddialog.h" + +#include <coreplugin/icore.h> +#include <utils/persistentsettings.h> + +#include <QDateTime> +#include <QSettings> +#include <QStringList> +#include <QProcess> +#include <QFileInfo> +#include <QDirIterator> +#include <QMetaObject> + +#include <QStringListModel> +#include <QMessageBox> + +#if defined(_WIN32) +#include <iostream> +#include <windows.h> +#define sleep(_n) Sleep(1000 * (_n)) +#endif + +using namespace Utils; + +namespace Android { +namespace Internal { + +namespace { + const QLatin1String SettingsGroup("AndroidConfigurations"); + const QLatin1String SDKLocationKey("SDKLocation"); + const QLatin1String NDKLocationKey("NDKLocation"); + const QLatin1String NDKToolchainVersionKey("NDKToolchainVersion"); + const QLatin1String AntLocationKey("AntLocation"); + const QLatin1String ArmGdbLocationKey("GdbLocation"); + const QLatin1String ArmGdbserverLocationKey("GdbserverLocation"); + const QLatin1String X86GdbLocationKey("X86GdbLocation"); + const QLatin1String X86GdbserverLocationKey("X86GdbserverLocation"); + const QLatin1String OpenJDKLocationKey("OpenJDKLocation"); + const QLatin1String KeystoreLocationKey("KeystoreLocation"); + const QLatin1String PartitionSizeKey("PartitionSize"); + const QLatin1String NDKGccVersionRegExp("\\d\\.\\d\\.\\d"); + const QLatin1String ArmToolchainPrefix("arm-linux-androideabi"); + const QLatin1String X86ToolchainPrefix("x86"); + const QLatin1String ArmToolsPrefix("arm-linux-androideabi"); + const QLatin1String X86ToolsPrefix("i686-android-linux"); + const QLatin1String Unknown("unknown"); + const QLatin1String keytoolName("keytool"); + const QLatin1String jarsignerName("jarsigner"); + const QLatin1String androidFilename("/android.xml"); + const QLatin1String changeTimeStamp("ChangeTimeStamp"); + + static QString settingsFileName() + { + return Core::ICore::instance()->resourcePath() + + QLatin1String("/Nokia") + androidFilename; + } + + bool androidDevicesLessThan(const AndroidDevice &dev1, const AndroidDevice &dev2) + { + return dev1.sdk < dev2.sdk; + } +} + +QLatin1String AndroidConfigurations::toolchainPrefix(ProjectExplorer::Abi::Architecture architecture) +{ + switch (architecture) { + case ProjectExplorer::Abi::ArmArchitecture: + return ArmToolchainPrefix; + case ProjectExplorer::Abi::X86Architecture: + return X86ToolchainPrefix; + default: + return Unknown; + } +} + + +QLatin1String AndroidConfigurations::toolsPrefix(ProjectExplorer::Abi::Architecture architecture) +{ + switch (architecture) { + case ProjectExplorer::Abi::ArmArchitecture: + return ArmToolsPrefix; + case ProjectExplorer::Abi::X86Architecture: + return X86ToolsPrefix; + default: + return Unknown; + } +} + +AndroidConfig::AndroidConfig(const QSettings &settings) +{ + // user settings + armGdbLocation = settings.value(ArmGdbLocationKey).toString(); + armGdbserverLocation = settings.value(ArmGdbserverLocationKey).toString(); + x86GdbLocation = settings.value(X86GdbLocationKey).toString(); + x86GdbserverLocation = settings.value(X86GdbserverLocationKey).toString(); + partitionSize = settings.value(PartitionSizeKey, 1024).toInt(); + sdkLocation = settings.value(SDKLocationKey).toString(); + ndkLocation = settings.value(NDKLocationKey).toString(); + antLocation = settings.value(AntLocationKey).toString(); + openJDKLocation = settings.value(OpenJDKLocationKey).toString(); + keystoreLocation = settings.value(KeystoreLocationKey).toString(); + + QRegExp versionRegExp(NDKGccVersionRegExp); + const QString &value = settings.value(NDKToolchainVersionKey).toString(); + if (versionRegExp.exactMatch(value)) + ndkToolchainVersion = value; + else + ndkToolchainVersion = value.mid(versionRegExp.indexIn(value)); + // user settings + + PersistentSettingsReader reader; + if (reader.load(settingsFileName()) + && settings.value(changeTimeStamp).toInt() != QFileInfo(settingsFileName()).lastModified().toMSecsSinceEpoch() / 1000) { + // persisten settings + sdkLocation = reader.restoreValue(SDKLocationKey).toString(); + ndkLocation = reader.restoreValue(NDKLocationKey).toString(); + antLocation = reader.restoreValue(AntLocationKey).toString(); + openJDKLocation = reader.restoreValue(OpenJDKLocationKey).toString(); + keystoreLocation = reader.restoreValue(KeystoreLocationKey).toString(); + + QRegExp versionRegExp(NDKGccVersionRegExp); + const QString &value = reader.restoreValue(NDKToolchainVersionKey).toString(); + if (versionRegExp.exactMatch(value)) + ndkToolchainVersion = value; + else + ndkToolchainVersion = value.mid(versionRegExp.indexIn(value)); + + if (!armGdbLocation.length()) + armGdbLocation = reader.restoreValue(ArmGdbLocationKey).toString(); + + if (!armGdbserverLocation.length()) + armGdbserverLocation = reader.restoreValue(ArmGdbserverLocationKey).toString(); + + if (!x86GdbLocation.length()) + x86GdbLocation = reader.restoreValue(X86GdbLocationKey).toString(); + + if (!x86GdbserverLocation.length()) + x86GdbserverLocation = reader.restoreValue(X86GdbserverLocationKey).toString(); + // persistent settings + } + +} + +AndroidConfig::AndroidConfig() +{ + partitionSize = 1024; +} + +void AndroidConfig::save(QSettings &settings) const +{ + QFileInfo fileInfo(settingsFileName()); + if (fileInfo.exists()) + settings.setValue(changeTimeStamp, fileInfo.lastModified().toMSecsSinceEpoch() / 1000); + + // user settings + settings.setValue(SDKLocationKey, sdkLocation); + settings.setValue(NDKLocationKey, ndkLocation); + settings.setValue(NDKToolchainVersionKey, ndkToolchainVersion); + settings.setValue(AntLocationKey, antLocation); + settings.setValue(OpenJDKLocationKey, openJDKLocation); + settings.setValue(KeystoreLocationKey, keystoreLocation); + settings.setValue(ArmGdbLocationKey, armGdbLocation); + settings.setValue(ArmGdbserverLocationKey, armGdbserverLocation); + settings.setValue(X86GdbLocationKey, x86GdbLocation); + settings.setValue(X86GdbserverLocationKey, x86GdbserverLocation); + settings.setValue(PartitionSizeKey, partitionSize); + // user settings + +} + +void AndroidConfigurations::setConfig(const AndroidConfig &devConfigs) +{ + m_config = devConfigs; + save(); + updateAvailablePlatforms(); + emit updated(); +} + +void AndroidConfigurations::updateAvailablePlatforms() +{ + m_availablePlatforms.clear(); + QDirIterator it(m_config.ndkLocation + QLatin1String("/platforms"), QStringList() << QLatin1String("android-*"), QDir::Dirs); + while (it.hasNext()) { + const QString &fileName = it.next(); + m_availablePlatforms.push_back(fileName.mid(fileName.lastIndexOf(QLatin1Char('-')) + 1).toInt()); + } + qSort(m_availablePlatforms.begin(), m_availablePlatforms.end(), qGreater<int>()); +} + +QStringList AndroidConfigurations::sdkTargets(int minApiLevel) const +{ + QStringList targets; + QProcess proc; + proc.start(androidToolPath(), QStringList() << QLatin1String("list") << QLatin1String("target")); // list avaialbe AVDs + if (!proc.waitForFinished(-1)) { + proc.terminate(); + return targets; + } + QList<QByteArray> avds = proc.readAll().trimmed().split('\n'); + for (int i = 0; i < avds.size(); i++) { + QString line = QLatin1String(avds[i]); + int index = line.indexOf(QLatin1String("\"android-")); + if (index == -1) + continue; + QString apiLevel = line.mid(index + 1, line.length() - index - 2); + if (apiLevel.mid(apiLevel.lastIndexOf(QLatin1Char('-')) + 1).toInt() >= minApiLevel) + targets.push_back(apiLevel); + } + return targets; +} + +QStringList AndroidConfigurations::ndkToolchainVersions() const +{ + QRegExp versionRegExp(NDKGccVersionRegExp); + QStringList result; + QDirIterator it(m_config.ndkLocation + QLatin1String("/toolchains"), + QStringList() << QLatin1String("*"), QDir::Dirs); + while (it.hasNext()) { + const QString &fileName = it.next(); + int idx = versionRegExp.indexIn(fileName); + if (idx == -1) + continue; + QString version = fileName.mid(idx); + if (!result.contains(version)) + result.append(version); + } + return result; +} + +QString AndroidConfigurations::adbToolPath() const +{ + return m_config.sdkLocation + QLatin1String("/platform-tools/adb"ANDROID_EXE_SUFFIX); +} + +QString AndroidConfigurations::androidToolPath() const +{ +#ifdef Q_OS_WIN32 + // I want to switch from using android.bat to using an executable. All it really does is call + // Java and I've made some progress on it. So if android.exe exists, return that instead. + QFileInfo fi(m_config.SDKLocation + QLatin1String("/tools/android"ANDROID_EXE_SUFFIX)); + if (fi.exists()) + return m_config.SDKLocation + QString("/tools/android"ANDROID_EXE_SUFFIX); + else + return m_config.SDKLocation + QLatin1String("/tools/android"ANDROID_BAT_SUFFIX); +#else + return m_config.sdkLocation + QLatin1String("/tools/android"ANDROID_EXE_SUFFIX); +#endif +} + +QString AndroidConfigurations::antToolPath() const +{ + if (m_config.antLocation.length()) + return m_config.antLocation; + else + return QLatin1String("ant"); +} + +QString AndroidConfigurations::emulatorToolPath() const +{ + return m_config.sdkLocation + QLatin1String("/tools/emulator"ANDROID_EXE_SUFFIX); +} + +QString AndroidConfigurations::toolPath(ProjectExplorer::Abi::Architecture architecture) const +{ + return m_config.ndkLocation + QString::fromLatin1("/toolchains/%1-%2/prebuilt/%3/bin/%4") + .arg(toolchainPrefix(architecture)) + .arg(m_config.ndkToolchainVersion) + .arg(ToolchainHost) + .arg(toolsPrefix(architecture)); +} + +QString AndroidConfigurations::stripPath(ProjectExplorer::Abi::Architecture architecture) const +{ + return toolPath(architecture) + QLatin1String("-strip"ANDROID_EXE_SUFFIX); +} + +QString AndroidConfigurations::readelfPath(ProjectExplorer::Abi::Architecture architecture) const +{ + return toolPath(architecture) + QLatin1String("-readelf"ANDROID_EXE_SUFFIX); +} + +QString AndroidConfigurations::gccPath(ProjectExplorer::Abi::Architecture architecture) const +{ + return toolPath(architecture) + QLatin1String("-gcc"ANDROID_EXE_SUFFIX); +} + +QString AndroidConfigurations::gdbServerPath(ProjectExplorer::Abi::Architecture architecture) const +{ + QString gdbServerPath; + switch (architecture) { + case ProjectExplorer::Abi::ArmArchitecture: + gdbServerPath = m_config.armGdbserverLocation; + break; + case ProjectExplorer::Abi::X86Architecture: + gdbServerPath = m_config.x86GdbserverLocation; + break; + default: + gdbServerPath = Unknown; + break; + } + + if (gdbServerPath.length()) + return gdbServerPath; + return m_config.ndkLocation + QString::fromLatin1("/toolchains/%1-%2/prebuilt/gdbserver") + .arg(toolchainPrefix(architecture)) + .arg(m_config.ndkToolchainVersion); +} + +QString AndroidConfigurations::gdbPath(ProjectExplorer::Abi::Architecture architecture) const +{ + QString gdbPath; + switch (architecture) { + case ProjectExplorer::Abi::ArmArchitecture: + gdbPath = m_config.armGdbLocation; + break; + case ProjectExplorer::Abi::X86Architecture: + gdbPath = m_config.x86GdbLocation; + break; + default: + gdbPath = Unknown; + break; + } + if (!gdbPath.isEmpty()) + return gdbPath; + return toolPath(architecture) + QLatin1String("-gdb"ANDROID_EXE_SUFFIX); +} + +QString AndroidConfigurations::openJDKPath() const +{ + return m_config.openJDKLocation; +} + +QString AndroidConfigurations::openJDKBinPath() const +{ + if (m_config.openJDKLocation.length()) + return m_config.openJDKLocation + QLatin1String("/bin/"); + return QString(); +} + +QString AndroidConfigurations::keytoolPath() const +{ + return openJDKBinPath() + keytoolName; +} + +QString AndroidConfigurations::jarsignerPath() const +{ + return openJDKBinPath() + jarsignerName; +} + +QString AndroidConfigurations::getDeployDeviceSerialNumber(int *apiLevel) const +{ + QVector<AndroidDevice> devices = connectedDevices(); + + foreach (AndroidDevice device, devices) + if (device.sdk >= *apiLevel) { + *apiLevel = device.sdk; + return device.serialNumber; + } + return startAVD(apiLevel); +} + +QVector<AndroidDevice> AndroidConfigurations::connectedDevices(int apiLevel) const +{ + QVector<AndroidDevice> devices; + QProcess adbProc; + adbProc.start(adbToolPath(), QStringList() << QLatin1String("devices")); + if (!adbProc.waitForFinished(-1)) { + adbProc.terminate(); + return devices; + } + QList<QByteArray> adbDevs = adbProc.readAll().trimmed().split('\n'); + adbDevs.removeFirst(); + AndroidDevice dev; + foreach (const QByteArray &device, adbDevs) { + dev.serialNumber = QString::fromLatin1(device.left(device.indexOf('\t')).trimmed()); + dev.sdk = getSDKVersion(dev.serialNumber); + if (apiLevel != -1 && dev.sdk != apiLevel) + continue; + devices.push_back(dev); + } + qSort(devices.begin(), devices.end(), androidDevicesLessThan); + return devices; +} + +bool AndroidConfigurations::createAVD(int minApiLevel) const +{ + QDialog d; + Ui::AddNewAVDDialog avdDialog; + avdDialog.setupUi(&d); + QStringListModel model(sdkTargets(minApiLevel)); + avdDialog.targetComboBox->setModel(&model); + if (!model.rowCount()) { + QMessageBox::critical(0, tr("Create AVD error"), + tr("Can't create a new AVD, not enough android SDKs available\n" + "Please install one SDK with api version >=%1").arg(minApiLevel)); + return false; + } + + QRegExp rx(QLatin1String("\\S+")); + QRegExpValidator v(rx, 0); + avdDialog.nameLineEdit->setValidator(&v); + if (d.exec() != QDialog::Accepted) + return false; + return createAVD(avdDialog.targetComboBox->currentText(), avdDialog.nameLineEdit->text(), avdDialog.sizeSpinBox->value()); +} + +bool AndroidConfigurations::createAVD(const QString &target, const QString &name, int sdcardSize ) const +{ + QProcess proc; + proc.start(androidToolPath(), + QStringList() << QLatin1String("create") << QLatin1String("avd") + << QLatin1String("-a") << QLatin1String("-t") << target + << QLatin1String("-n") << name + << QLatin1String("-c") << QString::fromLatin1("%1M").arg(sdcardSize)); + if (!proc.waitForStarted()) + return false; + proc.write(QByteArray("no\n")); + if (!proc.waitForFinished(-1)) { + proc.terminate(); + return false; + } + return !proc.exitCode(); +} + +bool AndroidConfigurations::removeAVD(const QString &name) const +{ + QProcess proc; + proc.start(androidToolPath(), + QStringList() << QLatin1String("delete") << QLatin1String("avd") + << QLatin1String("-n") << name); + if (!proc.waitForFinished(-1)) { + proc.terminate(); + return false; + } + return !proc.exitCode(); +} + +QVector<AndroidDevice> AndroidConfigurations::androidVirtualDevices() const +{ + QVector<AndroidDevice> devices; + QProcess proc; + proc.start(androidToolPath(), + QStringList() << QLatin1String("list") << QLatin1String("avd")); // list available AVDs + if (!proc.waitForFinished(-1)) { + proc.terminate(); + return devices; + } + QList<QByteArray> avds = proc.readAll().trimmed().split('\n'); + avds.removeFirst(); + AndroidDevice dev; + for (int i = 0; i < avds.size(); i++) { + QString line = QLatin1String(avds[i]); + if (!line.contains(QLatin1String("Name:"))) + continue; + + dev.serialNumber = line.mid(line.indexOf(QLatin1Char(':')) + 2).trimmed(); + ++i; + for (; i < avds.size(); ++i) { + line = QLatin1String(avds[i]); + if (line.contains(QLatin1String("---------"))) + break; + if (line.contains(QLatin1String("Target:"))) + dev.sdk = line.mid(line.lastIndexOf(QLatin1Char(' '))).remove(QLatin1Char(')')).toInt(); + if (line.contains(QLatin1String("ABI:"))) + dev.cpuABI = line.mid(line.lastIndexOf(QLatin1Char(' '))).trimmed(); + } + devices.push_back(dev); + } + qSort(devices.begin(), devices.end(), androidDevicesLessThan); + + return devices; +} + +QString AndroidConfigurations::startAVD(int *apiLevel, const QString &name) const +{ + QProcess *m_avdProcess = new QProcess(); + connect(this, SIGNAL(destroyed()), m_avdProcess, SLOT(deleteLater())); + connect(m_avdProcess, SIGNAL(finished(int)), m_avdProcess, SLOT(deleteLater())); + + QString avdName = name; + QVector<AndroidDevice> devices; + bool createAVDOnce = false; + while (true) { + if (avdName.isEmpty()) { + devices = androidVirtualDevices(); + foreach (AndroidDevice device, devices) + if (device.sdk >= *apiLevel) { // take first emulator how supports this package + *apiLevel = device.sdk; + avdName = device.serialNumber; + break; + } + } + // if no emulators found try to create one once + if (avdName.isEmpty() && !createAVDOnce) { + createAVDOnce = true; + QMetaObject::invokeMethod(const_cast<QObject*>(static_cast<const QObject*>(this)), "createAVD", Qt::AutoConnection, + Q_ARG(int, *apiLevel)); + } else { + break; + } + } + + if (avdName.isEmpty())// stop here if no emulators found + return avdName; + + // start the emulator + m_avdProcess->start(emulatorToolPath(), + QStringList() << QLatin1String("-partition-size") << QString::number(config().partitionSize) + << QLatin1String("-avd") << avdName); + if (!m_avdProcess->waitForStarted(-1)) { + delete m_avdProcess; + return QString(); + } + + // wait until the emulator is online + QProcess proc; + proc.start(adbToolPath(), QStringList() << QLatin1String("-e") << QLatin1String("wait-for-device")); + if (!proc.waitForFinished(-1)) { + proc.terminate(); + return QString(); + } + sleep(5);// wait for pm to start + + // workaround for stupid adb bug + proc.start(adbToolPath(), QStringList() << QLatin1String("devices")); + if (!proc.waitForFinished(-1)) { + proc.terminate(); + return QString(); + } + + // get connected devices + devices = connectedDevices(*apiLevel); + foreach (AndroidDevice device, devices) + if (device.sdk == *apiLevel) + return device.serialNumber; + // this should not happen, but ... + return QString(); +} + +int AndroidConfigurations::getSDKVersion(const QString &device) const +{ + + QProcess adbProc; + adbProc.start(adbToolPath(), + QStringList() << QLatin1String("-s") << device + << QLatin1String("shell") << QLatin1String("getprop") + << QLatin1String("ro.build.version.sdk")); + if (!adbProc.waitForFinished(-1)) { + adbProc.terminate(); + return -1; + } + return adbProc.readAll().trimmed().toInt(); +} + +QString AndroidConfigurations::bestMatch(const QString &targetAPI) const +{ + int target = targetAPI.mid(targetAPI.lastIndexOf(QLatin1Char('-')) + 1).toInt(); + foreach (int apiLevel, m_availablePlatforms) { + if (apiLevel <= target) + return QString::fromLatin1("android-%1").arg(apiLevel); + } + return QLatin1String("android-8"); +} + +AndroidConfigurations &AndroidConfigurations::instance(QObject *parent) +{ + if (m_instance == 0) + m_instance = new AndroidConfigurations(parent); + return *m_instance; +} + +void AndroidConfigurations::save() +{ + QSettings *settings = Core::ICore::instance()->settings(); + settings->beginGroup(SettingsGroup); + m_config.save(*settings); + settings->endGroup(); +} + +AndroidConfigurations::AndroidConfigurations(QObject *parent) + : QObject(parent) +{ + load(); + updateAvailablePlatforms(); +} + +void AndroidConfigurations::load() +{ + QSettings *settings = Core::ICore::instance()->settings(); + settings->beginGroup(SettingsGroup); + m_config = AndroidConfig(*settings); + settings->endGroup(); +} + +AndroidConfigurations *AndroidConfigurations::m_instance = 0; + +} // namespace Internal +} // namespace Android diff --git a/src/plugins/android/androidconfigurations.h b/src/plugins/android/androidconfigurations.h new file mode 100644 index 00000000000..6fc01f0796a --- /dev/null +++ b/src/plugins/android/androidconfigurations.h @@ -0,0 +1,149 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2012 BogDan Vatra <[email protected]> +** +** Contact: Nokia Corporation ([email protected]) +** +** +** GNU Lesser General Public License Usage +** +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this file. +** Please review the following information to ensure the GNU Lesser General +** Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** Other Usage +** +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** If you have questions regarding the use of this file, please contact +** Nokia at [email protected]. +** +**************************************************************************/ + +#ifndef ANDROIDCONFIGURATIONS_H +#define ANDROIDCONFIGURATIONS_H + +#include <QObject> +#include <QString> +#include <QVector> +#include <projectexplorer/abi.h> + +QT_BEGIN_NAMESPACE +class QSettings; +QT_END_NAMESPACE + +namespace Android { +namespace Internal { + +#ifdef Q_OS_LINUX + const QLatin1String ToolchainHost("linux-x86"); +#else +# ifdef Q_OS_DARWIN + const QLatin1String ToolchainHost("darwin-x86"); +# else +# ifdef Q_OS_WIN32 + const QLatin1String ToolchainHost("windows"); +# else +# warning No Android supported OSs found + const QLatin1String ToolchainHost("linux-x86"); +# endif +# endif +#endif + +class AndroidConfig +{ +public: + AndroidConfig(); + AndroidConfig(const QSettings &settings); + void save(QSettings &settings) const; + + QString sdkLocation; + QString ndkLocation; + QString ndkToolchainVersion; + QString antLocation; + QString armGdbLocation; + QString armGdbserverLocation; + QString x86GdbLocation; + QString x86GdbserverLocation; + QString openJDKLocation; + QString keystoreLocation; + unsigned partitionSize; +}; + +struct AndroidDevice { + QString serialNumber; + QString cpuABI; + int sdk; +}; + +class AndroidConfigurations : public QObject +{ + Q_OBJECT + Q_DISABLE_COPY(AndroidConfigurations) +public: + + static AndroidConfigurations &instance(QObject *parent = 0); + AndroidConfig config() const { return m_config; } + void setConfig(const AndroidConfig &config); + QStringList sdkTargets(int minApiLevel = 0) const; + QStringList ndkToolchainVersions() const; + QString adbToolPath() const; + QString androidToolPath() const; + QString antToolPath() const; + QString emulatorToolPath() const; + QString gccPath(ProjectExplorer::Abi::Architecture architecture) const; + QString gdbServerPath(ProjectExplorer::Abi::Architecture architecture) const; + QString gdbPath(ProjectExplorer::Abi::Architecture architecture) const; + QString openJDKPath() const; + QString keytoolPath() const; + QString jarsignerPath() const; + QString stripPath(ProjectExplorer::Abi::Architecture architecture) const; + QString readelfPath(ProjectExplorer::Abi::Architecture architecture) const; + QString getDeployDeviceSerialNumber(int *apiLevel) const; + bool createAVD(const QString &target, const QString &name, int sdcardSize) const; + bool removeAVD(const QString &name) const; + QVector<AndroidDevice> connectedDevices(int apiLevel = -1) const; + QVector<AndroidDevice> androidVirtualDevices() const; + QString startAVD(int *apiLevel, const QString &name = QString()) const; + QString bestMatch(const QString &targetAPI) const; + + static QLatin1String toolchainPrefix(ProjectExplorer::Abi::Architecture architecture); + static QLatin1String toolsPrefix(ProjectExplorer::Abi::Architecture architecture); + +signals: + void updated(); + +public slots: + bool createAVD(int minApiLevel = 0) const; + +private: + QString toolPath(ProjectExplorer::Abi::Architecture architecture) const; + QString openJDKBinPath() const; + + AndroidConfigurations(QObject *parent); + void load(); + void save(); + + int getSDKVersion(const QString &device) const; + void updateAvailablePlatforms(); + + + static AndroidConfigurations *m_instance; + AndroidConfig m_config; + QVector<int> m_availablePlatforms; +}; + +} // namespace Internal +} // namespace Android + +#endif // ANDROIDCONFIGURATIONS_H diff --git a/src/plugins/android/androidconstants.h b/src/plugins/android/androidconstants.h new file mode 100644 index 00000000000..22798b17e7e --- /dev/null +++ b/src/plugins/android/androidconstants.h @@ -0,0 +1,86 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2012 BogDan Vatra <[email protected]> +** +** Contact: Nokia Corporation ([email protected]) +** +** +** GNU Lesser General Public License Usage +** +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this file. +** Please review the following information to ensure the GNU Lesser General +** Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** Other Usage +** +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** If you have questions regarding the use of this file, please contact +** Nokia at [email protected]. +** +**************************************************************************/ + +#ifndef ANDROIDCONSTANTS_H +#define ANDROIDCONSTANTS_H + +#include <QLatin1String> + +namespace Android { +namespace Internal { + +enum AndroidQemuStatus { + AndroidQemuStarting, + AndroidQemuFailedToStart, + AndroidQemuFinished, + AndroidQemuCrashed, + AndroidQemuUserReason +}; + +#define ANDROID_PREFIX "Qt4ProjectManager.AndroidRunConfiguration" + +#ifdef Q_OS_WIN32 +#define ANDROID_EXE_SUFFIX ".exe" +#define ANDROID_BAT_SUFFIX ".bat" +#else +#define ANDROID_EXE_SUFFIX "" +#define ANDROID_BAT_SUFFIX "" +#endif + +static const QLatin1String ANDROID_RC_ID(ANDROID_PREFIX); +static const QLatin1String ANDROID_RC_ID_PREFIX(ANDROID_PREFIX "."); + +static const QLatin1String AndroidArgumentsKey(ANDROID_PREFIX ".Arguments"); +static const QLatin1String AndroidSimulatorPathKey(ANDROID_PREFIX ".Simulator"); +static const QLatin1String AndroidDeviceIdKey(ANDROID_PREFIX ".DeviceId"); +static const QLatin1String AndroidProFileKey(ANDROID_PREFIX ".ProFile"); +static const QLatin1String AndroidExportedLocalDirsKey(ANDROID_PREFIX ".ExportedLocalDirs"); +static const QLatin1String AndroidBaseEnvironmentBaseKey(ANDROID_PREFIX ".BaseEnvironmentBase"); +static const QLatin1String AndroidUserEnvironmentChangesKey(ANDROID_PREFIX ".UserEnvironmentChanges"); +static const QLatin1String AndroidUseRemoteGdbKey(ANDROID_PREFIX ".UseRemoteGdb"); + +} // namespace Internal + +namespace Constants { +const char ANDROID_SETTINGS_ID[] = "ZZ.Android Configurations"; +const char ANDROID_SETTINGS_CATEGORY[] = "X.Android"; +const char ANDROID_SETTINGS_TR_CATEGORY[] = QT_TRANSLATE_NOOP("Android", "Android"); +const char ANDROID_SETTINGS_CATEGORY_ICON[] = ":/android/images/QtAndroid.png"; +const char ANDROID_TOOLCHAIN_ID[] = "Qt4ProjectManager.ToolChain.Android"; +const char ANDROIDQT[] = "Qt4ProjectManager.QtVersion.Android"; +const char ANDROID_PLATFORM[] = "Android"; +const char ANDROID_PLATFORM_TR[] = QT_TRANSLATE_NOOP("QtSupport", "Android"); + +} +} // namespace Android + +#endif // ANDROIDCONSTANTS_H diff --git a/src/plugins/android/androidcreatekeystorecertificate.cpp b/src/plugins/android/androidcreatekeystorecertificate.cpp new file mode 100644 index 00000000000..8a326c3b59c --- /dev/null +++ b/src/plugins/android/androidcreatekeystorecertificate.cpp @@ -0,0 +1,198 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2012 BogDan Vatra <[email protected]> +** +** Contact: Nokia Corporation ([email protected]) +** +** +** GNU Lesser General Public License Usage +** +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this file. +** Please review the following information to ensure the GNU Lesser General +** Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** Other Usage +** +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** If you have questions regarding the use of this file, please contact +** Nokia at [email protected]. +** +**************************************************************************/ + +#include "androidcreatekeystorecertificate.h" +#include "androidconfigurations.h" +#include "ui_androidcreatekeystorecertificate.h" + +#include <QFileDialog> +#include <QProcess> +#include <QMessageBox> + +using namespace Android::Internal; + +AndroidCreateKeystoreCertificate::AndroidCreateKeystoreCertificate(QWidget *parent) : + QDialog(parent), + ui(new Ui::AndroidCreateKeystoreCertificate) +{ + ui->setupUi(this); + connect(ui->keystorePassLineEdit, SIGNAL(textChanged(QString)), this, SLOT(checkKeystorePassword())); + connect(ui->keystoreRetypePassLineEdit, SIGNAL(textChanged(QString)), this, SLOT(checkKeystorePassword())); + connect(ui->certificatePassLineEdit, SIGNAL(textChanged(QString)), this, SLOT(checkCertificatePassword())); + connect(ui->certificateRetypePassLineEdit, SIGNAL(textChanged(QString)), this, SLOT(checkCertificatePassword())); +} + +AndroidCreateKeystoreCertificate::~AndroidCreateKeystoreCertificate() +{ + delete ui; +} + +QString AndroidCreateKeystoreCertificate::keystoreFilePath() +{ + return m_keystoreFilePath; +} + +QString AndroidCreateKeystoreCertificate::keystorePassword() +{ + return ui->keystorePassLineEdit->text(); +} + +QString AndroidCreateKeystoreCertificate::certificateAlias() +{ + return ui->aliasNameLineEdit->text(); +} + +QString AndroidCreateKeystoreCertificate::certificatePassword() +{ + return ui->certificatePassLineEdit->text(); +} + +AndroidCreateKeystoreCertificate::PasswordStatus AndroidCreateKeystoreCertificate::checkKeystorePassword() +{ + if (ui->keystorePassLineEdit->text().length() < 6) { + ui->keystorePassInfoLabel->setText(tr("<span style=\" color:#ff0000;\">Password is too short</span>")); + return Invalid; + } + if (ui->keystorePassLineEdit->text() != ui->keystoreRetypePassLineEdit->text()) { + ui->keystorePassInfoLabel->setText(tr("<span style=\" color:#ff0000;\">Passwords don't match</span>")); + return NoMatch; + } + ui->keystorePassInfoLabel->setText(tr("<span style=\" color:#00ff00;\">Password is ok</span>")); + return Match; +} + +AndroidCreateKeystoreCertificate::PasswordStatus AndroidCreateKeystoreCertificate::checkCertificatePassword() +{ + if (ui->certificatePassLineEdit->text().length() < 6) { + ui->certificatePassInfoLabel->setText(tr("<span style=\" color:#ff0000;\">Password is too short</span>")); + return Invalid; + } + if (ui->certificatePassLineEdit->text() != ui->certificateRetypePassLineEdit->text()) { + ui->certificatePassInfoLabel->setText(tr("<span style=\" color:#ff0000;\">Passwords don't match</span>")); + return NoMatch; + } + ui->certificatePassInfoLabel->setText(tr("<span style=\" color:#00ff00;\">Password is ok</span>")); + return Match; +} + +void AndroidCreateKeystoreCertificate::on_keystoreShowPassCheckBox_stateChanged(int state) +{ + ui->keystorePassLineEdit->setEchoMode(state == Qt::Checked ? QLineEdit::Normal : QLineEdit::Password); + ui->keystoreRetypePassLineEdit->setEchoMode(ui->keystorePassLineEdit->echoMode()); +} + +void AndroidCreateKeystoreCertificate::on_certificateShowPassCheckBox_stateChanged(int state) +{ + ui->certificatePassLineEdit->setEchoMode(state == Qt::Checked ? QLineEdit::Normal : QLineEdit::Password); + ui->certificateRetypePassLineEdit->setEchoMode(ui->certificatePassLineEdit->echoMode()); +} + +void AndroidCreateKeystoreCertificate::on_buttonBox_accepted() +{ + switch (checkKeystorePassword()) { + case Invalid: + ui->keystorePassLineEdit->setFocus(); + return; + case NoMatch: + ui->keystoreRetypePassLineEdit->setFocus(); + return; + default: + break; + } + + switch (checkCertificatePassword()) { + case Invalid: + ui->certificatePassLineEdit->setFocus(); + return; + case NoMatch: + ui->certificateRetypePassLineEdit->setFocus(); + return; + default: + break; + } + + if (!ui->aliasNameLineEdit->text().length()) + ui->aliasNameLineEdit->setFocus(); + + if (!ui->commonNameLineEdit->text().length()) + ui->commonNameLineEdit->setFocus(); + + if (!ui->organizationNameLineEdit->text().length()) + ui->organizationNameLineEdit->setFocus(); + + if (!ui->localityNameLineEdit->text().length()) + ui->localityNameLineEdit->setFocus(); + + if (!ui->countryLineEdit->text().length()) + ui->countryLineEdit->setFocus(); + + m_keystoreFilePath = QFileDialog::getSaveFileName(this, tr("Keystore file name"), + QDir::homePath() + QLatin1String("/android_release.keystore"), + tr("Keystore files (*.keystore *.jks)")); + if (!m_keystoreFilePath.length()) + return; + QString distinguishedNames(QString::fromLatin1("CN=%1, O=%2, L=%3, C=%4") + .arg(ui->commonNameLineEdit->text().replace(QLatin1Char(','), QLatin1String("\\,"))) + .arg(ui->organizationNameLineEdit->text().replace(QLatin1Char(','), QLatin1String("\\,"))) + .arg(ui->localityNameLineEdit->text().replace(QLatin1Char(','), QLatin1String("\\,"))) + .arg(ui->countryLineEdit->text().replace(QLatin1Char(','), QLatin1String("\\,")))); + + if (ui->organizationUnitLineEdit->text().length()) + distinguishedNames += QLatin1String(", OU=") + ui->organizationUnitLineEdit->text().replace(QLatin1Char(','), QLatin1String("\\,")); + + if (ui->stateNameLineEdit->text().length()) + distinguishedNames += QLatin1String(", S=") + ui->stateNameLineEdit->text().replace(QLatin1Char(','), QLatin1String("\\,")); + + QStringList params; + params << QLatin1String("-genkey") << QLatin1String("-keyalg") << QLatin1String("RSA") + << QLatin1String("-keystore") << m_keystoreFilePath + << QLatin1String("-storepass") << ui->keystorePassLineEdit->text() + << QLatin1String("-alias") << ui->aliasNameLineEdit->text() + << QLatin1String("-keysize") << ui->keySizeSpinBox->text() + << QLatin1String("-validity") << ui->validitySpinBox->text() + << QLatin1String("-keypass") << ui->certificatePassLineEdit->text() + << QLatin1String("-dname") << distinguishedNames; + + QProcess genKeyCertProc; + genKeyCertProc.start(AndroidConfigurations::instance().keytoolPath(), params ); + + if (!genKeyCertProc.waitForStarted() || !genKeyCertProc.waitForFinished()) + return; + + if (genKeyCertProc.exitCode()) { + QMessageBox::critical(this, tr("Error") + , QString::fromLatin1(genKeyCertProc.readAllStandardOutput()) + + QString::fromLatin1(genKeyCertProc.readAllStandardError())); + return; + } + accept(); +} diff --git a/src/plugins/android/androidcreatekeystorecertificate.h b/src/plugins/android/androidcreatekeystorecertificate.h new file mode 100644 index 00000000000..bcdc68750e2 --- /dev/null +++ b/src/plugins/android/androidcreatekeystorecertificate.h @@ -0,0 +1,76 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2012 BogDan Vatra <[email protected]> +** +** Contact: Nokia Corporation ([email protected]) +** +** +** GNU Lesser General Public License Usage +** +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this file. +** Please review the following information to ensure the GNU Lesser General +** Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** Other Usage +** +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** If you have questions regarding the use of this file, please contact +** Nokia at [email protected]. +** +**************************************************************************/ + +#ifndef ANDROIDCREATEKEYSTORECERTIFICATE_H +#define ANDROIDCREATEKEYSTORECERTIFICATE_H + +#include <QDialog> + +namespace Ui { + class AndroidCreateKeystoreCertificate; +} +namespace Android { +namespace Internal { +class AndroidCreateKeystoreCertificate : public QDialog +{ + Q_OBJECT + enum PasswordStatus + { + Invalid, + NoMatch, + Match + }; + +public: + explicit AndroidCreateKeystoreCertificate(QWidget *parent = 0); + ~AndroidCreateKeystoreCertificate(); + QString keystoreFilePath(); + QString keystorePassword(); + QString certificateAlias(); + QString certificatePassword(); + +private slots: + PasswordStatus checkKeystorePassword(); + PasswordStatus checkCertificatePassword(); + void on_keystoreShowPassCheckBox_stateChanged(int state); + void on_certificateShowPassCheckBox_stateChanged(int state); + void on_buttonBox_accepted(); + +private: + Ui::AndroidCreateKeystoreCertificate *ui; + QString m_keystoreFilePath; +}; + +} // namespace Internal +} // namespace Android + +#endif // ANDROIDCREATEKEYSTORECERTIFICATE_H diff --git a/src/plugins/android/androidcreatekeystorecertificate.ui b/src/plugins/android/androidcreatekeystorecertificate.ui new file mode 100644 index 00000000000..9a0cb0a02d9 --- /dev/null +++ b/src/plugins/android/androidcreatekeystorecertificate.ui @@ -0,0 +1,362 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>AndroidCreateKeystoreCertificate</class> + <widget class="QDialog" name="AndroidCreateKeystoreCertificate"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>638</width> + <height>429</height> + </rect> + </property> + <property name="windowTitle"> + <string>Create a keystore and a cetificate</string> + </property> + <layout class="QGridLayout" name="gridLayout_4"> + <item row="0" column="0"> + <widget class="QGroupBox" name="groupBox"> + <property name="title"> + <string>Keystore</string> + </property> + <layout class="QGridLayout" name="gridLayout"> + <item row="0" column="0"> + <widget class="QLabel" name="label"> + <property name="text"> + <string>Password:</string> + </property> + <property name="alignment"> + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + </widget> + </item> + <item row="0" column="1"> + <widget class="QLineEdit" name="keystorePassLineEdit"> + <property name="echoMode"> + <enum>QLineEdit::Password</enum> + </property> + </widget> + </item> + <item row="1" column="0"> + <widget class="QLabel" name="label_2"> + <property name="text"> + <string>Retype password:</string> + </property> + <property name="alignment"> + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + </widget> + </item> + <item row="1" column="1"> + <widget class="QLineEdit" name="keystoreRetypePassLineEdit"> + <property name="echoMode"> + <enum>QLineEdit::Password</enum> + </property> + </widget> + </item> + <item row="2" column="0"> + <widget class="QCheckBox" name="keystoreShowPassCheckBox"> + <property name="text"> + <string>Show password</string> + </property> + </widget> + </item> + <item row="2" column="1"> + <widget class="QLabel" name="keystorePassInfoLabel"> + <property name="text"> + <string><span style=" color:#ff0000;">Password is too short</span></string> + </property> + <property name="textFormat"> + <enum>Qt::RichText</enum> + </property> + <property name="alignment"> + <set>Qt::AlignCenter</set> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item row="0" column="1"> + <widget class="QGroupBox" name="groupBox_3"> + <property name="title"> + <string>Certificate</string> + </property> + <layout class="QGridLayout" name="gridLayout_2"> + <item row="0" column="0"> + <widget class="QLabel" name="label_11"> + <property name="text"> + <string>Alias name:</string> + </property> + <property name="alignment"> + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + </widget> + </item> + <item row="0" column="1"> + <widget class="QLineEdit" name="aliasNameLineEdit"> + <property name="inputMask"> + <string>Aaaaaaaa; </string> + </property> + <property name="maxLength"> + <number>8</number> + </property> + </widget> + </item> + <item row="1" column="0"> + <widget class="QLabel" name="label_12"> + <property name="text"> + <string>Keysize:</string> + </property> + <property name="alignment"> + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + </widget> + </item> + <item row="1" column="1"> + <widget class="QSpinBox" name="keySizeSpinBox"> + <property name="minimum"> + <number>2048</number> + </property> + <property name="maximum"> + <number>2097152</number> + </property> + </widget> + </item> + <item row="2" column="0"> + <widget class="QLabel" name="label_13"> + <property name="text"> + <string>Validity (days):</string> + </property> + <property name="alignment"> + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + </widget> + </item> + <item row="2" column="1"> + <widget class="QSpinBox" name="validitySpinBox"> + <property name="minimum"> + <number>10000</number> + </property> + <property name="maximum"> + <number>100000</number> + </property> + </widget> + </item> + <item row="3" column="0"> + <widget class="QLabel" name="label_17"> + <property name="text"> + <string>Password:</string> + </property> + <property name="alignment"> + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + </widget> + </item> + <item row="3" column="1"> + <widget class="QLineEdit" name="certificatePassLineEdit"> + <property name="echoMode"> + <enum>QLineEdit::Password</enum> + </property> + </widget> + </item> + <item row="4" column="0"> + <widget class="QLabel" name="label_18"> + <property name="text"> + <string>Retype password:</string> + </property> + <property name="alignment"> + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + </widget> + </item> + <item row="4" column="1"> + <widget class="QLineEdit" name="certificateRetypePassLineEdit"> + <property name="echoMode"> + <enum>QLineEdit::Password</enum> + </property> + </widget> + </item> + <item row="5" column="0"> + <widget class="QCheckBox" name="certificateShowPassCheckBox"> + <property name="text"> + <string>Show password</string> + </property> + </widget> + </item> + <item row="5" column="1"> + <widget class="QLabel" name="certificatePassInfoLabel"> + <property name="text"> + <string><span style=" color:#ff0000;">Password is too short</span></string> + </property> + <property name="textFormat"> + <enum>Qt::RichText</enum> + </property> + <property name="alignment"> + <set>Qt::AlignCenter</set> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item row="1" column="0" colspan="2"> + <widget class="QGroupBox" name="groupBox_2"> + <property name="title"> + <string>Certificate Distinguished Names</string> + </property> + <layout class="QGridLayout" name="gridLayout_3"> + <item row="0" column="0"> + <widget class="QLabel" name="label_5"> + <property name="text"> + <string>First and last name:</string> + </property> + <property name="alignment"> + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + </widget> + </item> + <item row="0" column="1"> + <widget class="QLineEdit" name="commonNameLineEdit"/> + </item> + <item row="1" column="0"> + <widget class="QLabel" name="label_6"> + <property name="text"> + <string>Organizational unit (e.g. Necessitas):</string> + </property> + <property name="alignment"> + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + </widget> + </item> + <item row="1" column="1"> + <widget class="QLineEdit" name="organizationUnitLineEdit"/> + </item> + <item row="2" column="0"> + <widget class="QLabel" name="label_7"> + <property name="text"> + <string>Organization (e.g. KDE):</string> + </property> + <property name="alignment"> + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + </widget> + </item> + <item row="2" column="1"> + <widget class="QLineEdit" name="organizationNameLineEdit"/> + </item> + <item row="3" column="0"> + <widget class="QLabel" name="label_8"> + <property name="text"> + <string>City or Locality:</string> + </property> + <property name="alignment"> + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + </widget> + </item> + <item row="3" column="1"> + <widget class="QLineEdit" name="localityNameLineEdit"/> + </item> + <item row="4" column="0"> + <widget class="QLabel" name="label_9"> + <property name="text"> + <string>State or Province:</string> + </property> + <property name="alignment"> + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + </widget> + </item> + <item row="4" column="1"> + <widget class="QLineEdit" name="stateNameLineEdit"/> + </item> + <item row="5" column="0"> + <widget class="QLabel" name="label_10"> + <property name="text"> + <string>Two-letter country code for this unit (e.g. RO):</string> + </property> + <property name="alignment"> + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + </widget> + </item> + <item row="5" column="1"> + <widget class="QLineEdit" name="countryLineEdit"> + <property name="inputMask"> + <string>>AA; </string> + </property> + <property name="maxLength"> + <number>2</number> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item row="2" column="0" colspan="2"> + <widget class="QDialogButtonBox" name="buttonBox"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="standardButtons"> + <set>QDialogButtonBox::Close|QDialogButtonBox::Save</set> + </property> + </widget> + </item> + </layout> + </widget> + <tabstops> + <tabstop>keystorePassLineEdit</tabstop> + <tabstop>keystoreRetypePassLineEdit</tabstop> + <tabstop>aliasNameLineEdit</tabstop> + <tabstop>keySizeSpinBox</tabstop> + <tabstop>validitySpinBox</tabstop> + <tabstop>certificatePassLineEdit</tabstop> + <tabstop>certificateRetypePassLineEdit</tabstop> + <tabstop>commonNameLineEdit</tabstop> + <tabstop>organizationUnitLineEdit</tabstop> + <tabstop>organizationNameLineEdit</tabstop> + <tabstop>localityNameLineEdit</tabstop> + <tabstop>stateNameLineEdit</tabstop> + <tabstop>countryLineEdit</tabstop> + <tabstop>keystoreShowPassCheckBox</tabstop> + <tabstop>certificateShowPassCheckBox</tabstop> + <tabstop>buttonBox</tabstop> + </tabstops> + <resources/> + <connections> + <connection> + <sender>buttonBox</sender> + <signal>rejected()</signal> + <receiver>AndroidCreateKeystoreCertificate</receiver> + <slot>reject()</slot> + <hints> + <hint type="sourcelabel"> + <x>320</x> + <y>422</y> + </hint> + <hint type="destinationlabel"> + <x>286</x> + <y>274</y> + </hint> + </hints> + </connection> + <connection> + <sender>keystorePassLineEdit</sender> + <signal>editingFinished()</signal> + <receiver>keystoreRetypePassLineEdit</receiver> + <slot>setFocus()</slot> + <hints> + <hint type="sourcelabel"> + <x>201</x> + <y>48</y> + </hint> + <hint type="destinationlabel"> + <x>213</x> + <y>75</y> + </hint> + </hints> + </connection> + </connections> +</ui> diff --git a/src/plugins/android/androiddebugsupport.cpp b/src/plugins/android/androiddebugsupport.cpp new file mode 100644 index 00000000000..a6a863c3d1c --- /dev/null +++ b/src/plugins/android/androiddebugsupport.cpp @@ -0,0 +1,165 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2012 BogDan Vatra <[email protected]> +** +** Contact: Nokia Corporation ([email protected]) +** +** +** GNU Lesser General Public License Usage +** +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this file. +** Please review the following information to ensure the GNU Lesser General +** Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** Other Usage +** +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** If you have questions regarding the use of this file, please contact +** Nokia at [email protected]. +** +**************************************************************************/ + +#include "androiddebugsupport.h" + +#include "androiddeploystep.h" +#include "androidglobal.h" +#include "androidrunner.h" +#include "androidtarget.h" + +#include <debugger/debuggerplugin.h> +#include <debugger/debuggerrunner.h> +#include <debugger/debuggerengine.h> +#include <debugger/debuggerstartparameters.h> + +#include <qt4projectmanager/qt4buildconfiguration.h> +#include <qt4projectmanager/qt4target.h> +#include <qt4projectmanager/qt4project.h> +#include <qt4projectmanager/qt4nodes.h> + +#include <QDir> + +using namespace Debugger; +using namespace ProjectExplorer; +using namespace Qt4ProjectManager; + +namespace Android { +namespace Internal { + +static const char * const qMakeVariables[] = { + "QT_INSTALL_LIBS", + "QT_INSTALL_PLUGINS", + "QT_INSTALL_IMPORTS" +}; + + +RunControl *AndroidDebugSupport::createDebugRunControl(AndroidRunConfiguration *runConfig) +{ + DebuggerStartParameters params; + params.toolChainAbi = runConfig->abi(); + params.dumperLibrary = runConfig->dumperLib(); + params.startMode = AttachToRemoteServer; + params.executable = runConfig->androidTarget()->qt4Project()->rootQt4ProjectNode()->buildDir() + QLatin1String("/app_process"); + params.debuggerCommand = runConfig->gdbCmd(); + params.remoteChannel = runConfig->remoteChannel(); + params.displayName = runConfig->androidTarget()->packageName(); + + params.solibSearchPath.clear(); + + QList<Qt4ProFileNode *> nodes = runConfig->androidTarget()->qt4Project()->allProFiles(); + foreach (Qt4ProFileNode *node, nodes) + if (node->projectType() == ApplicationTemplate) + params.solibSearchPath.append(node->targetInformation().buildDir); + + params.solibSearchPath.append(qtSoPaths(runConfig->activeQt4BuildConfiguration()->qtVersion())); + + params.useServerStartScript = true; + params.requestRemoteSetup = true; + params.remoteArchitecture = QLatin1String("arm"); + + DebuggerRunControl * const debuggerRunControl + = DebuggerPlugin::createDebugger(params, runConfig); + new AndroidDebugSupport(runConfig, debuggerRunControl); + return debuggerRunControl; +} + +AndroidDebugSupport::AndroidDebugSupport(AndroidRunConfiguration *runConfig, + DebuggerRunControl *runControl) + : QObject(runControl), m_runControl(runControl), + m_runner(new AndroidRunner(this, runConfig, true)), + m_debuggingType(runConfig->debuggingType()), + m_gdbServerPort(5039), m_qmlPort(-1) +{ + connect(m_runControl->engine(), SIGNAL(requestRemoteSetup()), + m_runner, SLOT(start())); + connect(m_runControl, SIGNAL(finished()), + m_runner, SLOT(stop())); + + connect(m_runner, SIGNAL(remoteProcessStarted(int,int)), + SLOT(handleRemoteProcessStarted(int,int))); + connect(m_runner, SIGNAL(remoteProcessFinished(const QString &)), + SLOT(handleRemoteProcessFinished(const QString &))); + + connect(m_runner, SIGNAL(remoteErrorOutput(QByteArray)), + SLOT(handleRemoteErrorOutput(QByteArray))); + connect(m_runner, SIGNAL(remoteOutput(QByteArray)), + SLOT(handleRemoteOutput(QByteArray))); +} + +AndroidDebugSupport::~AndroidDebugSupport() +{ +} + +void AndroidDebugSupport::handleRemoteProcessStarted(int gdbServerPort, int qmlPort) +{ + disconnect(m_runner, SIGNAL(remoteProcessStarted(int,int)), + this, SLOT(handleRemoteProcessStarted(int,int))); + m_runControl->engine()->handleRemoteSetupDone(gdbServerPort, qmlPort); +} + +void AndroidDebugSupport::handleRemoteProcessFinished(const QString &errorMsg) +{ + disconnect(m_runner, SIGNAL(remoteProcessFinished(const QString &)), + this,SLOT(handleRemoteProcessFinished(const QString &))); + m_runControl->engine()->handleRemoteSetupFailed(errorMsg); +} + +void AndroidDebugSupport::handleRemoteOutput(const QByteArray &output) +{ + if (m_runControl) + m_runControl->showMessage(QString::fromUtf8(output), AppOutput); +} + +void AndroidDebugSupport::handleRemoteErrorOutput(const QByteArray &output) +{ + if (m_runControl) + m_runControl->showMessage(QString::fromUtf8(output), AppError); +} + +QStringList AndroidDebugSupport::qtSoPaths(QtSupport::BaseQtVersion *qtVersion) +{ + QSet<QString> paths; + for (uint i = 0; i < sizeof qMakeVariables / sizeof qMakeVariables[0]; ++i) { + if (!qtVersion->versionInfo().contains(QLatin1String(qMakeVariables[i]))) + continue; + QDirIterator it(qtVersion->versionInfo()[QLatin1String(qMakeVariables[i])], QStringList() << QLatin1String("*.so"), QDir::Files, QDirIterator::Subdirectories); + while (it.hasNext()) { + it.next(); + paths.insert(it.fileInfo().absolutePath()); + } + } + return paths.toList(); +} + +} // namespace Internal +} // namespace Qt4ProjectManager diff --git a/src/plugins/android/androiddebugsupport.h b/src/plugins/android/androiddebugsupport.h new file mode 100644 index 00000000000..d9cdfae34f9 --- /dev/null +++ b/src/plugins/android/androiddebugsupport.h @@ -0,0 +1,87 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2012 BogDan Vatra <[email protected]> +** +** Contact: Nokia Corporation ([email protected]) +** +** +** GNU Lesser General Public License Usage +** +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this file. +** Please review the following information to ensure the GNU Lesser General +** Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** Other Usage +** +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** If you have questions regarding the use of this file, please contact +** Nokia at [email protected]. +** +**************************************************************************/ + +#ifndef ANDROIDDEBUGSUPPORT_H +#define ANDROIDDEBUGSUPPORT_H + +#include "androidrunconfiguration.h" + +#include <QObject> +#include <QPointer> + +namespace Debugger { +class DebuggerRunControl; +} +namespace QtSupport {class BaseQtVersion; } +namespace ProjectExplorer { class RunControl; } + +namespace Android { + +namespace Internal { + +class AndroidRunConfiguration; +class AndroidRunner; + +class AndroidDebugSupport : public QObject +{ + Q_OBJECT +public: + static ProjectExplorer::RunControl *createDebugRunControl(AndroidRunConfiguration *runConfig); + + AndroidDebugSupport(AndroidRunConfiguration *runConfig, + Debugger::DebuggerRunControl *runControl); + ~AndroidDebugSupport(); + +private slots: + void handleRemoteProcessStarted(int gdbServerPort = -1, int qmlPort = -1); + void handleRemoteProcessFinished(const QString &errorMsg); + + void handleRemoteOutput(const QByteArray &output); + void handleRemoteErrorOutput(const QByteArray &output); + +private: + static QStringList qtSoPaths(QtSupport::BaseQtVersion *qtVersion); + +private: + Debugger::DebuggerRunControl* m_runControl; + AndroidRunner * const m_runner; + const AndroidRunConfiguration::DebuggingType m_debuggingType; + const QString m_dumperLib; + + int m_gdbServerPort; + int m_qmlPort; +}; + +} // namespace Internal +} // namespace Android + +#endif // ANDROIDDEBUGSUPPORT_H diff --git a/src/plugins/android/androiddeployconfiguration.cpp b/src/plugins/android/androiddeployconfiguration.cpp new file mode 100644 index 00000000000..78c3f1ee5a8 --- /dev/null +++ b/src/plugins/android/androiddeployconfiguration.cpp @@ -0,0 +1,135 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2012 BogDan Vatra <[email protected]> +** +** Contact: Nokia Corporation ([email protected]) +** +** +** GNU Lesser General Public License Usage +** +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this file. +** Please review the following information to ensure the GNU Lesser General +** Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** Other Usage +** +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** If you have questions regarding the use of this file, please contact +** Nokia at [email protected]. +** +**************************************************************************/ + +#include "androiddeploystep.h" +#include "androidpackageinstallationstep.h" +#include "androidpackagecreationstep.h" +#include "androiddeployconfiguration.h" +#include "androidtarget.h" + +#include <projectexplorer/buildsteplist.h> +#include <projectexplorer/target.h> + +#include <qt4projectmanager/qt4projectmanagerconstants.h> +#include <qt4projectmanager/qt4project.h> + +using namespace Android::Internal; + +AndroidDeployConfiguration::AndroidDeployConfiguration(ProjectExplorer::Target *parent) + :DeployConfiguration(parent, QLatin1String(ANDROID_DEPLOYCONFIGURATION_ID)) +{ + setDisplayName(tr("Deploy to Android device")); + setDefaultDisplayName(displayName()); +} + +AndroidDeployConfiguration::~AndroidDeployConfiguration() +{ + +} + +AndroidDeployConfiguration::AndroidDeployConfiguration(ProjectExplorer::Target *parent, ProjectExplorer::DeployConfiguration *source) + :DeployConfiguration(parent, source) +{ + +} + +AndroidDeployConfigurationFactory::AndroidDeployConfigurationFactory(QObject *parent) : + ProjectExplorer::DeployConfigurationFactory(parent) +{ } + +bool AndroidDeployConfigurationFactory::canCreate(ProjectExplorer::Target *parent, const QString &id) const +{ + AndroidTarget *t = qobject_cast<AndroidTarget *>(parent); + if (!t || t->id() != QLatin1String(Qt4ProjectManager::Constants::ANDROID_DEVICE_TARGET_ID) + || !id.startsWith(QLatin1String(ANDROID_DEPLOYCONFIGURATION_ID))) + return false; + return true; +} + +ProjectExplorer::DeployConfiguration *AndroidDeployConfigurationFactory::create(ProjectExplorer::Target *parent, const QString &/*id*/) +{ + AndroidDeployConfiguration *dc = new AndroidDeployConfiguration(parent); + if (!dc) + return 0; + dc->stepList()->insertStep(0, new AndroidPackageInstallationStep(dc->stepList())); + dc->stepList()->insertStep(1, new AndroidPackageCreationStep(dc->stepList())); + dc->stepList()->insertStep(2, new AndroidDeployStep(dc->stepList())); + return dc; +} + +bool AndroidDeployConfigurationFactory::canRestore(ProjectExplorer::Target *parent, const QVariantMap &map) const +{ + return canCreate(parent, ProjectExplorer::idFromMap(map)); +} + +ProjectExplorer::DeployConfiguration *AndroidDeployConfigurationFactory::restore(ProjectExplorer::Target *parent, const QVariantMap &map) +{ + if (!canRestore(parent, map)) + return 0; + AndroidTarget *t = static_cast<AndroidTarget *>(parent); + AndroidDeployConfiguration *dc = new AndroidDeployConfiguration(t); + if (dc->fromMap(map)) + return dc; + + delete dc; + return 0; +} + +bool AndroidDeployConfigurationFactory::canClone(ProjectExplorer::Target *parent, ProjectExplorer::DeployConfiguration *source) const +{ + if (!qobject_cast<AndroidTarget *>(parent)) + return false; + return source->id() == QLatin1String(ANDROID_DEPLOYCONFIGURATION_ID); +} + +ProjectExplorer::DeployConfiguration *AndroidDeployConfigurationFactory::clone(ProjectExplorer::Target *parent, ProjectExplorer::DeployConfiguration *source) +{ + if (!canClone(parent, source)) + return 0; + AndroidTarget *t = static_cast<AndroidTarget *>(parent); + return new AndroidDeployConfiguration(t, source); +} + +QStringList AndroidDeployConfigurationFactory::availableCreationIds(ProjectExplorer::Target *parent) const +{ + AndroidTarget *target = qobject_cast<AndroidTarget *>(parent); + if (!target || + target->id() != QLatin1String(Qt4ProjectManager::Constants::ANDROID_DEVICE_TARGET_ID)) + return QStringList(); + + return target->qt4Project()->applicationProFilePathes(QLatin1String(ANDROID_DC_PREFIX)); +} + +QString AndroidDeployConfigurationFactory::displayNameForId(const QString &/*id*/) const +{ + return tr("Deploy on Android"); +} diff --git a/src/plugins/android/androiddeployconfiguration.h b/src/plugins/android/androiddeployconfiguration.h new file mode 100644 index 00000000000..e6550e421f4 --- /dev/null +++ b/src/plugins/android/androiddeployconfiguration.h @@ -0,0 +1,81 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2012 BogDan Vatra <[email protected]> +** +** Contact: Nokia Corporation ([email protected]) +** +** +** GNU Lesser General Public License Usage +** +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this file. +** Please review the following information to ensure the GNU Lesser General +** Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** Other Usage +** +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** If you have questions regarding the use of this file, please contact +** Nokia at [email protected]. +** +**************************************************************************/ + +#ifndef QT4PROJECTMANAGER_QT4ANDROIDDEPLOYCONFIGURATION_H +#define QT4PROJECTMANAGER_QT4ANDROIDDEPLOYCONFIGURATION_H + +#include <projectexplorer/deployconfiguration.h> + +namespace Android { +namespace Internal { + +class Target; +const char ANDROID_DEPLOYCONFIGURATION_ID[] = "Qt4ProjectManager.AndroidDeployConfiguration"; +const char ANDROID_DC_PREFIX[] = "Qt4ProjectManager.AndroidDeployConfiguration."; + +class AndroidDeployConfiguration : public ProjectExplorer::DeployConfiguration +{ + Q_OBJECT + friend class AndroidDeployConfigurationFactory; + +public: + AndroidDeployConfiguration(ProjectExplorer::Target *parent); + virtual ~AndroidDeployConfiguration(); +protected: + AndroidDeployConfiguration(ProjectExplorer::Target *parent, ProjectExplorer::DeployConfiguration *source); + +}; + +class AndroidDeployConfigurationFactory : public ProjectExplorer::DeployConfigurationFactory +{ + Q_OBJECT + +public: + explicit AndroidDeployConfigurationFactory(QObject *parent = 0); + + bool canCreate(ProjectExplorer::Target *parent, const QString &id) const; + ProjectExplorer::DeployConfiguration *create(ProjectExplorer::Target *parent, const QString &id); + bool canRestore(ProjectExplorer::Target *parent, const QVariantMap &map) const; + ProjectExplorer::DeployConfiguration *restore(ProjectExplorer::Target *parent, const QVariantMap &map); + bool canClone(ProjectExplorer::Target *parent, ProjectExplorer::DeployConfiguration *source) const; + ProjectExplorer::DeployConfiguration *clone(ProjectExplorer::Target *parent, ProjectExplorer::DeployConfiguration *source); + + QStringList availableCreationIds(ProjectExplorer::Target *parent) const; + // used to translate the ids to names to display to the user + QString displayNameForId(const QString &id) const; + +}; + +} // namespace Internal +} // namespace Android + +#endif // QT4PROJECTMANAGER_QT4ANDROIDDEPLOYCONFIGURATION_H diff --git a/src/plugins/android/androiddeploystep.cpp b/src/plugins/android/androiddeploystep.cpp new file mode 100644 index 00000000000..64c024add3c --- /dev/null +++ b/src/plugins/android/androiddeploystep.cpp @@ -0,0 +1,324 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2012 BogDan Vatra <[email protected]> +** +** Contact: Nokia Corporation ([email protected]) +** +** +** GNU Lesser General Public License Usage +** +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this file. +** Please review the following information to ensure the GNU Lesser General +** Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** Other Usage +** +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** If you have questions regarding the use of this file, please contact +** Nokia at [email protected]. +** +**************************************************************************/ + +#include "androiddeploystep.h" + +#include "androidconstants.h" +#include "androiddeploystepwidget.h" +#include "androidglobal.h" +#include "androidpackagecreationstep.h" +#include "androidrunconfiguration.h" +#include "androidtarget.h" + +#include <projectexplorer/buildconfiguration.h> +#include <projectexplorer/projectexplorerconstants.h> +#include <projectexplorer/target.h> +#include <qt4projectmanager/qt4project.h> +#include <qt4projectmanager/qt4target.h> +#include <qt4projectmanager/qt4nodes.h> + +#include <qt4projectmanager/qt4buildconfiguration.h> + +#include <QDir> + +#define ASSERT_STATE(state) ASSERT_STATE_GENERIC(State, state, m_state) + +using namespace ProjectExplorer; +using namespace Qt4ProjectManager; + +namespace Android { +namespace Internal { + +const QLatin1String AndroidDeployStep::Id("Qt4ProjectManager.AndroidDeployStep"); + +AndroidDeployStep::AndroidDeployStep(ProjectExplorer::BuildStepList *parent) + : BuildStep(parent, Id) +{ + ctor(); +} + +AndroidDeployStep::AndroidDeployStep(ProjectExplorer::BuildStepList *parent, + AndroidDeployStep *other) + : BuildStep(parent, other) +{ + ctor(); +} + +AndroidDeployStep::~AndroidDeployStep() { } + +void AndroidDeployStep::ctor() +{ + //: AndroidDeployStep default display name + setDefaultDisplayName(tr("Deploy to Android device")); + m_deployAction = NoDeploy; + m_useLocalQtLibs = false; +} + +bool AndroidDeployStep::init() +{ + AndroidTarget *androidTarget = qobject_cast<AndroidTarget *>(target()); + if (!androidTarget) { + raiseError(tr("Cannot deploy: current target is not android.")); + return false; + } + const Qt4BuildConfiguration *const bc = androidTarget->activeQt4BuildConfiguration(); + m_packageName = androidTarget->packageName(); + const QString targetSDK = androidTarget->targetSDK(); + + writeOutput(tr("Please wait, searching for a suitable device for target:%1.").arg(targetSDK)); + m_deviceAPILevel = targetSDK.mid(targetSDK.indexOf(QLatin1Char('-')) + 1).toInt(); + m_deviceSerialNumber = AndroidConfigurations::instance().getDeployDeviceSerialNumber(&m_deviceAPILevel); + if (!m_deviceSerialNumber.length()) { + m_deviceSerialNumber.clear(); + raiseError(tr("Cannot deploy: no devices or emulators found for your package.")); + return false; + } + + if (!bc->qtVersion()) + return false; + m_qtVersionSourcePath = bc->qtVersion()->sourcePath().toString(); + m_qtVersionQMakeBuildConfig = bc->qtVersion()->defaultBuildConfig(); + m_androidDirPath = androidTarget->androidDirPath(); + m_apkPathDebug = androidTarget->apkPath(AndroidTarget::DebugBuild); + m_apkPathRelease = androidTarget->apkPath(AndroidTarget::ReleaseBuildSigned); + m_buildDirectory = androidTarget->qt4Project()->rootQt4ProjectNode()->buildDir(); + m_runQASIPackagePath = m_QASIPackagePath; + m_runDeployAction = m_deployAction; + return true; +} + +void AndroidDeployStep::run(QFutureInterface<bool> &fi) +{ + fi.reportResult(deployPackage()); +} + +BuildStepConfigWidget *AndroidDeployStep::createConfigWidget() +{ + return new AndroidDeployStepWidget(this); +} + +AndroidDeployStep::AndroidDeployAction AndroidDeployStep::deployAction() +{ + return m_deployAction; +} + +bool AndroidDeployStep::useLocalQtLibs() +{ + return m_useLocalQtLibs; +} + +void AndroidDeployStep::setDeployAction(AndroidDeployStep::AndroidDeployAction deploy) +{ + m_deployAction = deploy; +} + +void AndroidDeployStep::setDeployQASIPackagePath(const QString &package) +{ + m_QASIPackagePath = package; + m_deployAction = InstallQASI; +} + +void AndroidDeployStep::setUseLocalQtLibs(bool useLocal) +{ + m_useLocalQtLibs = useLocal; +} + +bool AndroidDeployStep::runCommand(QProcess *buildProc, + const QString &program, const QStringList &arguments) +{ + writeOutput(tr("Package deploy: Running command '%1 %2'.").arg(program).arg(arguments.join(QLatin1String(" "))), BuildStep::MessageOutput); + buildProc->start(program, arguments); + if (!buildProc->waitForStarted()) { + writeOutput(tr("Packaging error: Could not start command '%1 %2'. Reason: %3") + .arg(program).arg(arguments.join(QLatin1String(" "))).arg(buildProc->errorString()), BuildStep::ErrorMessageOutput); + return false; + } + buildProc->waitForFinished(-1); + if (buildProc->error() != QProcess::UnknownError + || buildProc->exitCode() != 0) { + QString mainMessage = tr("Packaging Error: Command '%1 %2' failed.") + .arg(program).arg(arguments.join(QLatin1String(" "))); + if (buildProc->error() != QProcess::UnknownError) + mainMessage += tr(" Reason: %1").arg(buildProc->errorString()); + else + mainMessage += tr("Exit code: %1").arg(buildProc->exitCode()); + writeOutput(mainMessage, BuildStep::ErrorMessageOutput); + return false; + } + return true; +} + +void AndroidDeployStep::handleBuildOutput() +{ + QProcess *const buildProc = qobject_cast<QProcess *>(sender()); + if (!buildProc) + return; + emit addOutput(QString::fromLocal8Bit(buildProc->readAllStandardOutput()) + , BuildStep::NormalOutput); +} + +void AndroidDeployStep::handleBuildError() +{ + QProcess *const buildProc = qobject_cast<QProcess *>(sender()); + if (!buildProc) + return; + emit addOutput(QString::fromLocal8Bit(buildProc->readAllStandardError()) + , BuildStep::ErrorOutput); +} + +QString AndroidDeployStep::deviceSerialNumber() +{ + return m_deviceSerialNumber; +} + +int AndroidDeployStep::deviceAPILevel() +{ + return m_deviceAPILevel; +} + +QString AndroidDeployStep::localLibsRulesFilePath() +{ + AndroidTarget *androidTarget = qobject_cast<AndroidTarget *>(target()); + if (!androidTarget) + return QString(); + return androidTarget->localLibsRulesFilePath(); +} + +void AndroidDeployStep::copyLibs(const QString &srcPath, const QString &destPath, QStringList &copiedLibs, const QStringList &filter) +{ + QDir dir; + dir.mkpath(destPath); + QDirIterator libsIt(srcPath, filter, QDir::NoFilter, QDirIterator::Subdirectories); + int pos = srcPath.size(); + while (libsIt.hasNext()) { + libsIt.next(); + const QString destFile(destPath + libsIt.filePath().mid(pos)); + if (libsIt.fileInfo().isDir()) { + dir.mkpath(destFile); + } else { + QFile::copy(libsIt.filePath(), destFile); + copiedLibs.append(destFile); + } + } +} + +bool AndroidDeployStep::deployPackage() +{ + QProcess *const deployProc = new QProcess; + connect(deployProc, SIGNAL(readyReadStandardOutput()), this, + SLOT(handleBuildOutput())); + connect(deployProc, SIGNAL(readyReadStandardError()), this, + SLOT(handleBuildError())); + + if (m_runDeployAction == DeployLocal) { + writeOutput(tr("Clean old qt libs")); + runCommand(deployProc, AndroidConfigurations::instance().adbToolPath(), + QStringList() << QLatin1String("-s") << m_deviceSerialNumber + << QLatin1String("shell") << QLatin1String("rm") << QLatin1String("-r") << QLatin1String("/data/local/qt")); + + writeOutput(tr("Deploy qt libs ... this may take some time, please wait")); + const QString tempPath = QDir::tempPath() + QLatin1String("/android_qt_libs_") + m_packageName; + AndroidPackageCreationStep::removeDirectory(tempPath); + QStringList stripFiles; + copyLibs(m_qtVersionSourcePath + QLatin1String("/lib"), + tempPath + QLatin1String("/lib"), stripFiles, QStringList() << QLatin1String("*.so")); + copyLibs(m_qtVersionSourcePath + QLatin1String("/plugins"), + tempPath + QLatin1String("/plugins"), stripFiles); + copyLibs(m_qtVersionSourcePath + QLatin1String("/imports"), + tempPath + QLatin1String("/imports"), stripFiles); + copyLibs(m_qtVersionSourcePath + QLatin1String("/jar"), + tempPath + QLatin1String("/jar"), stripFiles); + AndroidPackageCreationStep::stripAndroidLibs(stripFiles, target()->activeRunConfiguration()->abi().architecture()); + runCommand(deployProc, AndroidConfigurations::instance().adbToolPath(), + QStringList() << QLatin1String("-s") << m_deviceSerialNumber + << QLatin1String("push") << tempPath << QLatin1String("/data/local/qt")); + AndroidPackageCreationStep::removeDirectory(tempPath); + emit (resetDelopyAction()); + } + + if (m_runDeployAction == InstallQASI) { + if (!runCommand(deployProc, AndroidConfigurations::instance().adbToolPath(), + QStringList() << QLatin1String("-s") << m_deviceSerialNumber + << QLatin1String("install") << QLatin1String("-r ") << m_runQASIPackagePath)) { + raiseError(tr("Qt Android smart installer instalation failed")); + disconnect(deployProc, 0, this, 0); + deployProc->deleteLater(); + return false; + } + emit resetDelopyAction(); + } + deployProc->setWorkingDirectory(m_androidDirPath); + + writeOutput(tr("Installing package onto %1.").arg(m_deviceSerialNumber)); + runCommand(deployProc, AndroidConfigurations::instance().adbToolPath(), + QStringList() << QLatin1String("-s") << m_deviceSerialNumber << QLatin1String("uninstall") << m_packageName); + QString package = m_apkPathDebug; + + if (!(m_qtVersionQMakeBuildConfig & QtSupport::BaseQtVersion::DebugBuild) + && QFile::exists(m_apkPathRelease)) + package = m_apkPathRelease; + + if (!runCommand(deployProc, AndroidConfigurations::instance().adbToolPath(), + QStringList() << QLatin1String("-s") << m_deviceSerialNumber << QLatin1String("install") << package)) { + raiseError(tr("Package instalation failed")); + disconnect(deployProc, 0, this, 0); + deployProc->deleteLater(); + return false; + } + + writeOutput(tr("Pulling files necessary for debugging")); + runCommand(deployProc, AndroidConfigurations::instance().adbToolPath(), + QStringList() << QLatin1String("-s") << m_deviceSerialNumber + << QLatin1String("pull") << QLatin1String("/system/bin/app_process") + << QString::fromLatin1("%1/app_process").arg(m_buildDirectory)); + runCommand(deployProc, AndroidConfigurations::instance().adbToolPath(), + QStringList() << QLatin1String("-s") << m_deviceSerialNumber << QLatin1String("pull") + << QLatin1String("/system/lib/libc.so") + << QString::fromLatin1("%1/libc.so").arg(m_buildDirectory)); + disconnect(deployProc, 0, this, 0); + deployProc->deleteLater(); + return true; +} + +void AndroidDeployStep::raiseError(const QString &errorString) +{ + emit addTask(Task(Task::Error, errorString, Utils::FileName::fromString(QString()), -1, + ProjectExplorer::Constants::TASK_CATEGORY_BUILDSYSTEM)); +} + +void AndroidDeployStep::writeOutput(const QString &text, OutputFormat format) +{ + emit addOutput(text, format); +} + +} // namespace Internal +} // namespace Qt4ProjectManager diff --git a/src/plugins/android/androiddeploystep.h b/src/plugins/android/androiddeploystep.h new file mode 100644 index 00000000000..a7627e927b0 --- /dev/null +++ b/src/plugins/android/androiddeploystep.h @@ -0,0 +1,132 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2012 BogDan Vatra <[email protected]> +** +** Contact: Nokia Corporation ([email protected]) +** +** +** GNU Lesser General Public License Usage +** +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this file. +** Please review the following information to ensure the GNU Lesser General +** Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** Other Usage +** +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** If you have questions regarding the use of this file, please contact +** Nokia at [email protected]. +** +**************************************************************************/ + +#ifndef ANDROIDDEPLOYSTEP_H +#define ANDROIDDEPLOYSTEP_H + +#include "androidconfigurations.h" + +#include <projectexplorer/buildstep.h> +#include <qtsupport/baseqtversion.h> + +#include <QProcess> + +QT_BEGIN_NAMESPACE +class QEventLoop; +class QTimer; +QT_END_NAMESPACE + +namespace Android { +namespace Internal { +class AndroidDeviceConfigListModel; +class AndroidPackageCreationStep; + +class AndroidDeployStep : public ProjectExplorer::BuildStep +{ + Q_OBJECT + friend class AndroidDeployStepFactory; + +public: + enum AndroidDeployAction + { + NoDeploy, + DeployLocal, + InstallQASI + }; + +public: + AndroidDeployStep(ProjectExplorer::BuildStepList *bc); + + virtual ~AndroidDeployStep(); + + QString deviceSerialNumber(); + int deviceAPILevel(); + QString localLibsRulesFilePath(); + + AndroidDeployAction deployAction(); + bool useLocalQtLibs(); + +public slots: + void setDeployAction(AndroidDeployAction deploy); + void setDeployQASIPackagePath(const QString &package); + void setUseLocalQtLibs(bool useLocal); + +signals: + void done(); + void error(); + void resetDelopyAction(); + +private slots: + bool deployPackage(); + void handleBuildOutput(); + void handleBuildError(); + +private: + AndroidDeployStep(ProjectExplorer::BuildStepList *bc, + AndroidDeployStep *other); + virtual bool init(); + virtual void run(QFutureInterface<bool> &fi); + virtual ProjectExplorer::BuildStepConfigWidget *createConfigWidget(); + virtual bool immutable() const { return true; } + + void copyLibs(const QString &srcPath, const QString &destPath, QStringList &copiedLibs, const QStringList &filter = QStringList()); + void ctor(); + void raiseError(const QString &error); + void writeOutput(const QString &text, OutputFormat = MessageOutput); + bool runCommand(QProcess *buildProc, const QString &program, const QStringList &arguments); + +private: + QString m_deviceSerialNumber; + int m_deviceAPILevel; + + QString m_QASIPackagePath; + AndroidDeployAction m_deployAction; + bool m_useLocalQtLibs; + + // members to transfer data from init() to run + QString m_packageName; + QString m_qtVersionSourcePath; + QtSupport::BaseQtVersion::QmakeBuildConfigs m_qtVersionQMakeBuildConfig; + QString m_androidDirPath; + QString m_apkPathDebug; + QString m_apkPathRelease; + QString m_buildDirectory; + QString m_runQASIPackagePath; + AndroidDeployAction m_runDeployAction; + + static const QLatin1String Id; +}; + +} // namespace Internal +} // namespace Android + +#endif // ANDROIDDEPLOYSTEP_H diff --git a/src/plugins/android/androiddeploystepfactory.cpp b/src/plugins/android/androiddeploystepfactory.cpp new file mode 100644 index 00000000000..289b4f18b19 --- /dev/null +++ b/src/plugins/android/androiddeploystepfactory.cpp @@ -0,0 +1,113 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2012 BogDan Vatra <[email protected]> +** +** Contact: Nokia Corporation ([email protected]) +** +** +** GNU Lesser General Public License Usage +** +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this file. +** Please review the following information to ensure the GNU Lesser General +** Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** Other Usage +** +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** If you have questions regarding the use of this file, please contact +** Nokia at [email protected]. +** +**************************************************************************/ + +#include "androiddeploystepfactory.h" + +#include "androiddeploystep.h" + +#include <projectexplorer/buildsteplist.h> +#include <projectexplorer/projectexplorerconstants.h> +#include <projectexplorer/target.h> +#include <qt4projectmanager/qt4projectmanagerconstants.h> + +#include <QCoreApplication> + +using namespace ProjectExplorer; + +namespace Android { +namespace Internal { + +AndroidDeployStepFactory::AndroidDeployStepFactory(QObject *parent) + : IBuildStepFactory(parent) +{ +} + +QStringList AndroidDeployStepFactory::availableCreationIds(BuildStepList *parent) const +{ + if (parent->id() == QLatin1String(ProjectExplorer::Constants::BUILDSTEPS_DEPLOY) + && parent->target()->id() == QLatin1String(Qt4ProjectManager::Constants::ANDROID_DEVICE_TARGET_ID) + && !parent->contains(AndroidDeployStep::Id)) + return QStringList() << AndroidDeployStep::Id; + return QStringList(); +} + +QString AndroidDeployStepFactory::displayNameForId(const QString &id) const +{ + if (id == AndroidDeployStep::Id) + return QCoreApplication::translate("Qt4ProjectManager::Internal::AndroidDeployStepFactory", + "Deploy to Android device/emulator"); + return QString(); +} + +bool AndroidDeployStepFactory::canCreate(BuildStepList *parent, const QString &id) const +{ + return parent->id() == QLatin1String(ProjectExplorer::Constants::BUILDSTEPS_DEPLOY) + && id == QLatin1String(AndroidDeployStep::Id) + && parent->target()->id() == QLatin1String(Qt4ProjectManager::Constants::ANDROID_DEVICE_TARGET_ID) + && !parent->contains(AndroidDeployStep::Id); +} + +BuildStep *AndroidDeployStepFactory::create(BuildStepList *parent, const QString &id) +{ + Q_ASSERT(canCreate(parent, id)); + return new AndroidDeployStep(parent); +} + +bool AndroidDeployStepFactory::canRestore(BuildStepList *parent, const QVariantMap &map) const +{ + return canCreate(parent, idFromMap(map)); +} + +BuildStep *AndroidDeployStepFactory::restore(BuildStepList *parent, const QVariantMap &map) +{ + Q_ASSERT(canRestore(parent, map)); + AndroidDeployStep * const step = new AndroidDeployStep(parent); + if (!step->fromMap(map)) { + delete step; + return 0; + } + return step; +} + +bool AndroidDeployStepFactory::canClone(BuildStepList *parent, BuildStep *product) const +{ + return canCreate(parent, product->id()); +} + +BuildStep *AndroidDeployStepFactory::clone(BuildStepList *parent, BuildStep *product) +{ + Q_ASSERT(canClone(parent, product)); + return new AndroidDeployStep(parent, static_cast<AndroidDeployStep *>(product)); +} + +} // namespace Internal +} // namespace Android diff --git a/src/plugins/android/androiddeploystepfactory.h b/src/plugins/android/androiddeploystepfactory.h new file mode 100644 index 00000000000..c2361acb3f7 --- /dev/null +++ b/src/plugins/android/androiddeploystepfactory.h @@ -0,0 +1,70 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2012 BogDan Vatra <[email protected]> +** +** Contact: Nokia Corporation ([email protected]) +** +** +** GNU Lesser General Public License Usage +** +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this file. +** Please review the following information to ensure the GNU Lesser General +** Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** Other Usage +** +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** If you have questions regarding the use of this file, please contact +** Nokia at [email protected]. +** +**************************************************************************/ + +#ifndef ANDROIDDEPLOYSTEPFACTORY_H +#define ANDROIDDEPLOYSTEPFACTORY_H + +#include <projectexplorer/buildstep.h> + +namespace Android { +namespace Internal { + +class AndroidDeployStepFactory : public ProjectExplorer::IBuildStepFactory +{ + Q_OBJECT +public: + explicit AndroidDeployStepFactory(QObject *parent = 0); + + virtual QStringList availableCreationIds(ProjectExplorer::BuildStepList *parent) const; + virtual QString displayNameForId(const QString &id) const; + + virtual bool canCreate(ProjectExplorer::BuildStepList *parent, + const QString &id) const; + virtual ProjectExplorer::BuildStep * + create(ProjectExplorer::BuildStepList *parent, const QString &id); + + virtual bool canRestore(ProjectExplorer::BuildStepList *parent, + const QVariantMap &map) const; + virtual ProjectExplorer::BuildStep * + restore(ProjectExplorer::BuildStepList *parent, const QVariantMap &map); + + virtual bool canClone(ProjectExplorer::BuildStepList *parent, + ProjectExplorer::BuildStep *product) const; + virtual ProjectExplorer::BuildStep * + clone(ProjectExplorer::BuildStepList *parent, + ProjectExplorer::BuildStep *product); +}; + +} // namespace Internal +} // namespace Android + +#endif // ANDROIDDEPLOYSTEPFACTORY_H diff --git a/src/plugins/android/androiddeploystepwidget.cpp b/src/plugins/android/androiddeploystepwidget.cpp new file mode 100644 index 00000000000..3eafae6e540 --- /dev/null +++ b/src/plugins/android/androiddeploystepwidget.cpp @@ -0,0 +1,115 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2012 BogDan Vatra <[email protected]> +** +** Contact: Nokia Corporation ([email protected]) +** +** +** GNU Lesser General Public License Usage +** +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this file. +** Please review the following information to ensure the GNU Lesser General +** Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** Other Usage +** +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** If you have questions regarding the use of this file, please contact +** Nokia at [email protected]. +** +**************************************************************************/ + +#include "androiddeploystepwidget.h" +#include "ui_androiddeploystepwidget.h" + +#include "androiddeploystep.h" +#include "androidrunconfiguration.h" + +#include <coreplugin/icore.h> + +#include <QFileDialog> + +namespace Android { +namespace Internal { + +AndroidDeployStepWidget::AndroidDeployStepWidget(AndroidDeployStep *step) : + ProjectExplorer::BuildStepConfigWidget(), + ui(new Ui::AndroidDeployStepWidget), + m_step(step) +{ + ui->setupUi(this); + + ui->useLocalQtLibs->setChecked(m_step->useLocalQtLibs()); + switch (m_step->deployAction()) { + case AndroidDeployStep::DeployLocal: + ui->deployQtLibs->setChecked(true); + break; + default: + ui->devicesQtLibs->setChecked(true); + break; + } + + connect(m_step, SIGNAL(resetDelopyAction()), SLOT(resetAction())); + connect(ui->devicesQtLibs, SIGNAL(clicked()), SLOT(resetAction())); + connect(ui->deployQtLibs, SIGNAL(clicked()), SLOT(setDeployLocalQtLibs())); + connect(ui->chooseButton, SIGNAL(clicked()), SLOT(setQASIPackagePath())); + connect(ui->useLocalQtLibs, SIGNAL(stateChanged(int)), SLOT(useLocalQtLibsStateChanged(int))); + connect(ui->editRulesFilePushButton, SIGNAL(clicked()), SLOT(editRulesFile())); +} + +AndroidDeployStepWidget::~AndroidDeployStepWidget() +{ + delete ui; +} + +QString AndroidDeployStepWidget::displayName() const +{ + return tr("<b>Deploy configurations</b>"); +} + +QString AndroidDeployStepWidget::summaryText() const +{ + return displayName(); +} + +void AndroidDeployStepWidget::resetAction() +{ + ui->devicesQtLibs->setChecked(true); + m_step->setDeployAction(AndroidDeployStep::NoDeploy); +} + +void AndroidDeployStepWidget::setDeployLocalQtLibs() +{ + m_step->setDeployAction(AndroidDeployStep::DeployLocal); +} + +void AndroidDeployStepWidget::setQASIPackagePath() +{ + QString packagePath = QFileDialog::getOpenFileName(this, tr("Qt Android smart installer"), QDir::homePath(), tr("Android package (*.apk)")); + if (packagePath.length()) + m_step->setDeployQASIPackagePath(packagePath); +} + +void AndroidDeployStepWidget::useLocalQtLibsStateChanged(int state) +{ + m_step->setUseLocalQtLibs(state == Qt::Checked); +} + +void AndroidDeployStepWidget::editRulesFile() +{ + Core::ICore::instance()->openFiles(QStringList() << m_step->localLibsRulesFilePath(), Core::ICore::SwitchMode); +} + +} // namespace Internal +} // namespace Qt4ProjectManager diff --git a/src/plugins/android/androiddeploystepwidget.h b/src/plugins/android/androiddeploystepwidget.h new file mode 100644 index 00000000000..3623662b75f --- /dev/null +++ b/src/plugins/android/androiddeploystepwidget.h @@ -0,0 +1,74 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2012 BogDan Vatra <[email protected]> +** +** Contact: Nokia Corporation ([email protected]) +** +** +** GNU Lesser General Public License Usage +** +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this file. +** Please review the following information to ensure the GNU Lesser General +** Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** Other Usage +** +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** If you have questions regarding the use of this file, please contact +** Nokia at [email protected]. +** +**************************************************************************/ + +#ifndef ANDROIDDEPLOYSTEPWIDGET_H +#define ANDROIDDEPLOYSTEPWIDGET_H + +#include <projectexplorer/buildstep.h> + +QT_BEGIN_NAMESPACE +namespace Ui { + class AndroidDeployStepWidget; +} +QT_END_NAMESPACE + +namespace Android { +namespace Internal { +class AndroidDeployStep; + +class AndroidDeployStepWidget : public ProjectExplorer::BuildStepConfigWidget +{ + Q_OBJECT + +public: + AndroidDeployStepWidget(AndroidDeployStep *step); + ~AndroidDeployStepWidget(); + +private slots: + void resetAction(); + void setDeployLocalQtLibs(); + void setQASIPackagePath(); + void useLocalQtLibsStateChanged(int); + void editRulesFile(); + +private: + virtual QString summaryText() const; + virtual QString displayName() const; + + Ui::AndroidDeployStepWidget *ui; + AndroidDeployStep *m_step; +}; + +} // namespace Internal +} // namespace Android + +#endif // ANDROIDDEPLOYSTEPWIDGET_H diff --git a/src/plugins/android/androiddeploystepwidget.ui b/src/plugins/android/androiddeploystepwidget.ui new file mode 100644 index 00000000000..5a348df0d6e --- /dev/null +++ b/src/plugins/android/androiddeploystepwidget.ui @@ -0,0 +1,156 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>AndroidDeployStepWidget</class> + <widget class="QWidget" name="AndroidDeployStepWidget"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>560</width> + <height>136</height> + </rect> + </property> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Minimum"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="windowTitle"> + <string>Form</string> + </property> + <layout class="QGridLayout" name="gridLayout"> + <item row="0" column="0"> + <widget class="QRadioButton" name="devicesQtLibs"> + <property name="toolTip"> + <string>Use devices qt libs</string> + </property> + <property name="text"> + <string>Use devices qt libs</string> + </property> + <property name="checked"> + <bool>true</bool> + </property> + </widget> + </item> + <item row="1" column="0"> + <layout class="QHBoxLayout" name="horizontalLayout_2"> + <item> + <widget class="QRadioButton" name="deployQtLibs"> + <property name="toolTip"> + <string>Push local qt libs to device. +You must have Qt libs compiled for that platform</string> + </property> + <property name="text"> + <string>Deploy local qt libs</string> + </property> + </widget> + </item> + <item> + <widget class="QCheckBox" name="useLocalQtLibs"> + <property name="toolTip"> + <string>Check this option to force the application to use local qt libs instead of system libs.</string> + </property> + <property name="text"> + <string>Use local qt libs</string> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="editRulesFilePushButton"> + <property name="text"> + <string>Edit rules file</string> + </property> + </widget> + </item> + </layout> + </item> + <item row="2" column="0"> + <layout class="QHBoxLayout" name="horizontalLayout"> + <item> + <widget class="QRadioButton" name="installQASI"> + <property name="toolTip"> + <string>Choose and install Ministro system wide qt shared libraries. +This option is useful when you want to try your application on devices which don't have Android Market (e.g. Android Emulator).</string> + </property> + <property name="text"> + <string>Install Ministro system wide qt shared libraries installer</string> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="chooseButton"> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="text"> + <string>Choose apk</string> + </property> + </widget> + </item> + <item> + <spacer name="horizontalSpacer"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + </layout> + </item> + <item row="3" column="0"> + <spacer name="verticalSpacer"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>41</height> + </size> + </property> + </spacer> + </item> + </layout> + </widget> + <resources/> + <connections> + <connection> + <sender>installQASI</sender> + <signal>toggled(bool)</signal> + <receiver>chooseButton</receiver> + <slot>setEnabled(bool)</slot> + <hints> + <hint type="sourcelabel"> + <x>119</x> + <y>68</y> + </hint> + <hint type="destinationlabel"> + <x>272</x> + <y>64</y> + </hint> + </hints> + </connection> + <connection> + <sender>deployQtLibs</sender> + <signal>clicked(bool)</signal> + <receiver>useLocalQtLibs</receiver> + <slot>setChecked(bool)</slot> + <hints> + <hint type="sourcelabel"> + <x>138</x> + <y>43</y> + </hint> + <hint type="destinationlabel"> + <x>343</x> + <y>44</y> + </hint> + </hints> + </connection> + </connections> +</ui> diff --git a/src/plugins/android/androidglobal.h b/src/plugins/android/androidglobal.h new file mode 100644 index 00000000000..836546d91d5 --- /dev/null +++ b/src/plugins/android/androidglobal.h @@ -0,0 +1,87 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2012 BogDan Vatra <[email protected]> +** +** Contact: Nokia Corporation ([email protected]) +** +** +** GNU Lesser General Public License Usage +** +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this file. +** Please review the following information to ensure the GNU Lesser General +** Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** Other Usage +** +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** If you have questions regarding the use of this file, please contact +** Nokia at [email protected]. +** +**************************************************************************/ + +#ifndef ANDROIDGLOBAL_H +#define ANDROIDGLOBAL_H + +#include <utils/environment.h> + +#include <projectexplorer/deployconfiguration.h> +#include <projectexplorer/buildsteplist.h> + +#define ASSERT_STATE_GENERIC(State, expected, actual) \ + AndroidGlobal::assertState<State>(expected, actual, Q_FUNC_INFO) + +QT_BEGIN_NAMESPACE +class QString; +QT_END_NAMESPACE + +namespace Android { +namespace Internal { + +class AndroidGlobal +{ +public: + + template<class T> static T *buildStep(const ProjectExplorer::DeployConfiguration *dc) + { + ProjectExplorer::BuildStepList *bsl = dc->stepList(); + if (!bsl) + return 0; + const QList<ProjectExplorer::BuildStep *> &buildSteps = bsl->steps(); + for (int i = buildSteps.count() - 1; i >= 0; --i) { + if (T * const step = qobject_cast<T *>(buildSteps.at(i))) + return step; + } + return 0; + } + + template<typename State> static void assertState(State expected, + State actual, const char *func) + { + assertState(QList<State>() << expected, actual, func); + } + + template<typename State> static void assertState(const QList<State> &expected, + State actual, const char *func) + { + if (!expected.contains(actual)) { + qWarning("Warning: Unexpected state %d in function %s.", + actual, func); + } + } +}; + +} // namespace Internal +} // namespace Android + +#endif // ANDROIDGLOBAL_H diff --git a/src/plugins/android/androidpackagecreationfactory.cpp b/src/plugins/android/androidpackagecreationfactory.cpp new file mode 100644 index 00000000000..b33c16c012d --- /dev/null +++ b/src/plugins/android/androidpackagecreationfactory.cpp @@ -0,0 +1,119 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2012 BogDan Vatra <[email protected]> +** +** Contact: Nokia Corporation ([email protected]) +** +** +** GNU Lesser General Public License Usage +** +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this file. +** Please review the following information to ensure the GNU Lesser General +** Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** Other Usage +** +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** If you have questions regarding the use of this file, please contact +** Nokia at [email protected]. +** +**************************************************************************/ + +#include "androidpackagecreationfactory.h" + +#include "androidpackagecreationstep.h" + +#include <projectexplorer/buildsteplist.h> +#include <projectexplorer/projectexplorerconstants.h> +#include <projectexplorer/target.h> +#include <qt4projectmanager/qt4projectmanagerconstants.h> + +#include <QCoreApplication> + +using ProjectExplorer::BuildStepList; +using ProjectExplorer::BuildStep; + +namespace Android { +namespace Internal { + +AndroidPackageCreationFactory::AndroidPackageCreationFactory(QObject *parent) + : ProjectExplorer::IBuildStepFactory(parent) +{ +} + +QStringList AndroidPackageCreationFactory::availableCreationIds(ProjectExplorer::BuildStepList *parent) const +{ + if (parent->id() == QLatin1String(ProjectExplorer::Constants::BUILDSTEPS_DEPLOY) + && parent->target()->id() == QLatin1String(Qt4ProjectManager::Constants::ANDROID_DEVICE_TARGET_ID) + && !parent->contains(AndroidPackageCreationStep::CreatePackageId)) + return QStringList() << AndroidPackageCreationStep::CreatePackageId; + return QStringList(); +} + +QString AndroidPackageCreationFactory::displayNameForId(const QString &id) const +{ + if (id == AndroidPackageCreationStep::CreatePackageId) + return QCoreApplication::translate("Qt4ProjectManager::Internal::AndroidPackageCreationFactory", + "Create Android (.apk) Package"); + return QString(); +} + +bool AndroidPackageCreationFactory::canCreate(ProjectExplorer::BuildStepList *parent, const QString &id) const +{ + return parent->id() == QLatin1String(ProjectExplorer::Constants::BUILDSTEPS_DEPLOY) + && id == QLatin1String(AndroidPackageCreationStep::CreatePackageId) + && parent->target()->id() == QLatin1String(Qt4ProjectManager::Constants::ANDROID_DEVICE_TARGET_ID) + && !parent->contains(AndroidPackageCreationStep::CreatePackageId); +} + +BuildStep *AndroidPackageCreationFactory::create(ProjectExplorer::BuildStepList *parent, const QString &id) +{ + Q_ASSERT(canCreate(parent, id)); + return new AndroidPackageCreationStep(parent); +} + +bool AndroidPackageCreationFactory::canRestore(ProjectExplorer::BuildStepList *parent, + const QVariantMap &map) const +{ + return canCreate(parent, ProjectExplorer::idFromMap(map)); +} + +BuildStep *AndroidPackageCreationFactory::restore(ProjectExplorer::BuildStepList *parent, + const QVariantMap &map) +{ + Q_ASSERT(canRestore(parent, map)); + AndroidPackageCreationStep *const step + = new AndroidPackageCreationStep(parent); + if (!step->fromMap(map)) { + delete step; + return 0; + } + return step; +} + +bool AndroidPackageCreationFactory::canClone(ProjectExplorer::BuildStepList *parent, + ProjectExplorer::BuildStep *product) const +{ + return canCreate(parent, product->id()); +} + +BuildStep *AndroidPackageCreationFactory::clone(ProjectExplorer::BuildStepList *parent, + ProjectExplorer::BuildStep *product) +{ + Q_ASSERT(canClone(parent, product)); + return new AndroidPackageCreationStep(parent, static_cast<AndroidPackageCreationStep *>(product)); +} + +} // namespace Internal +} // namespace Android diff --git a/src/plugins/android/androidpackagecreationfactory.h b/src/plugins/android/androidpackagecreationfactory.h new file mode 100644 index 00000000000..81f1bd9d487 --- /dev/null +++ b/src/plugins/android/androidpackagecreationfactory.h @@ -0,0 +1,69 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2012 BogDan Vatra <[email protected]> +** +** Contact: Nokia Corporation ([email protected]) +** +** +** GNU Lesser General Public License Usage +** +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this file. +** Please review the following information to ensure the GNU Lesser General +** Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** Other Usage +** +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** If you have questions regarding the use of this file, please contact +** Nokia at [email protected]. +** +**************************************************************************/ + +#ifndef ANDROIDPACKAGECREATIONFACTORY_H +#define ANDROIDPACKAGECREATIONFACTORY_H + +#include <projectexplorer/buildstep.h> + +namespace Android { +namespace Internal { + +class AndroidPackageCreationFactory : public ProjectExplorer::IBuildStepFactory +{ +public: + explicit AndroidPackageCreationFactory(QObject *parent = 0); + + virtual QStringList availableCreationIds(ProjectExplorer::BuildStepList *parent) const; + virtual QString displayNameForId(const QString &id) const; + + virtual bool canCreate(ProjectExplorer::BuildStepList *parent, + const QString &id) const; + virtual ProjectExplorer::BuildStep * + create(ProjectExplorer::BuildStepList *parent, const QString &id); + + virtual bool canRestore(ProjectExplorer::BuildStepList *parent, + const QVariantMap &map) const; + virtual ProjectExplorer::BuildStep * + restore(ProjectExplorer::BuildStepList *parent, const QVariantMap &map); + + virtual bool canClone(ProjectExplorer::BuildStepList *parent, + ProjectExplorer::BuildStep *product) const; + virtual ProjectExplorer::BuildStep * + clone(ProjectExplorer::BuildStepList *parent, + ProjectExplorer::BuildStep *product); +}; + +} // namespace Internal +} // namespace Android + +#endif // ANDROIDPACKAGECREATIONFACTORY_H diff --git a/src/plugins/android/androidpackagecreationstep.cpp b/src/plugins/android/androidpackagecreationstep.cpp new file mode 100644 index 00000000000..df4ed403b5b --- /dev/null +++ b/src/plugins/android/androidpackagecreationstep.cpp @@ -0,0 +1,589 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2012 BogDan Vatra <[email protected]> +** +** Contact: Nokia Corporation ([email protected]) +** +** +** GNU Lesser General Public License Usage +** +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this file. +** Please review the following information to ensure the GNU Lesser General +** Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** Other Usage +** +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** If you have questions regarding the use of this file, please contact +** Nokia at [email protected]. +** +**************************************************************************/ + +#include "androidpackagecreationstep.h" + +#include "androidconstants.h" +#include "androiddeploystep.h" +#include "androidglobal.h" +#include "androidpackagecreationwidget.h" +#include "androidtarget.h" + +#include <projectexplorer/buildsteplist.h> +#include <projectexplorer/projectexplorerconstants.h> +#include <projectexplorer/runconfiguration.h> +#include <qt4projectmanager/qt4buildconfiguration.h> +#include <qt4projectmanager/qt4project.h> +#include <qt4projectmanager/qt4nodes.h> +#include <qt4projectmanager/qt4target.h> + +#include <coreplugin/icore.h> +#include <coreplugin/fileutils.h> + +#include <QAbstractListModel> +#include <QProcess> +#include <QVector> +#include <QPair> +#include <QWidget> +#include <QMessageBox> +#include <QInputDialog> +#include <QMainWindow> + +using namespace ProjectExplorer; +using namespace ProjectExplorer::Constants; +using ProjectExplorer::BuildStepList; +using ProjectExplorer::BuildStepConfigWidget; +using ProjectExplorer::Task; + +namespace Android { +namespace Internal { + +namespace { + const QLatin1String KeystoreLocationKey("KeystoreLocation"); + const QLatin1String AliasString("Alias name:"); + const QLatin1String CertificateSeparator("*******************************************"); +} + +using namespace Qt4ProjectManager; + +class CertificatesModel: public QAbstractListModel +{ +public: + CertificatesModel(const QString &rowCertificates, QObject *parent) + : QAbstractListModel(parent) + { + int from = rowCertificates.indexOf(AliasString); + QPair<QString, QString> item; + while (from > -1) { + from += 11;// strlen(AliasString); + const int eol = rowCertificates.indexOf(QLatin1String("\n"), from); + item.first = rowCertificates.mid(from, eol - from).trimmed(); + const int eoc = rowCertificates.indexOf(CertificateSeparator, eol); + item.second = rowCertificates.mid(eol + 1, eoc - eol - 2).trimmed(); + from = rowCertificates.indexOf(AliasString, eoc); + m_certs.push_back(item); + } + } + +protected: + int rowCount(const QModelIndex &parent = QModelIndex()) const + { + if (parent.isValid()) + return 0; + return m_certs.size(); + } + + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const + { + if (!index.isValid() || (role != Qt::DisplayRole && role != Qt::ToolTipRole)) + return QVariant(); + if (role == Qt::DisplayRole) + return m_certs[index.row()].first; + return m_certs[index.row()].second; + } + +private: + QVector<QPair<QString, QString> > m_certs; +}; + + +AndroidPackageCreationStep::AndroidPackageCreationStep(BuildStepList *bsl) + : BuildStep(bsl, CreatePackageId) +{ + ctor(); +} + +AndroidPackageCreationStep::AndroidPackageCreationStep(BuildStepList *bsl, + AndroidPackageCreationStep *other) + : BuildStep(bsl, other) +{ + ctor(); +} + +AndroidPackageCreationStep::~AndroidPackageCreationStep() +{ +} + +void AndroidPackageCreationStep::ctor() +{ + setDefaultDisplayName(tr("Packaging for Android")); + m_openPackageLocation = true; + connect(&m_outputParser, SIGNAL(addTask(ProjectExplorer::Task)), this, SIGNAL(addTask(ProjectExplorer::Task))); +} + +bool AndroidPackageCreationStep::init() +{ + AndroidTarget *target = androidTarget(); + const Qt4BuildConfiguration *bc = target->activeQt4BuildConfiguration(); + if (!target) { + raiseError(tr("Cannot create android package: current target is not android.")); + return false; + } + m_outputParser.setProjectFileList(target->qt4Project()->files(Project::AllFiles)); + + // Copying + m_androidDir = target->androidDirPath(); + QString androidLibPath; + if (target->qt4Project()->rootQt4ProjectNode()->variableValue(Qt4ProjectManager::ConfigVar).contains(QLatin1String("x86"))) + androidLibPath = m_androidDir + QLatin1String("/libs/x86"); + else if (target->qt4Project()->rootQt4ProjectNode() + ->variableValue(Qt4ProjectManager::ConfigVar).contains(QLatin1String("armeabi-v7a"))) + androidLibPath = m_androidDir + QLatin1String("/libs/armeabi-v7a"); + else + androidLibPath = m_androidDir + QLatin1String("/libs/armeabi"); + m_gdbServerDestination = androidLibPath + QLatin1String("/gdbserver"); + m_gdbServerSource = AndroidConfigurations::instance().gdbServerPath(target->activeRunConfiguration()->abi().architecture()); + m_debugBuild = bc->qmakeBuildConfiguration() & QtSupport::BaseQtVersion::DebugBuild; + + if (!target->createAndroidTemplatesIfNecessary()) + return false; + + target->updateProject(target->targetSDK(), target->applicationName()); + m_antToolPath = AndroidConfigurations::instance().antToolPath(); + m_apkPathUnsigned = target->apkPath(AndroidTarget::ReleaseBuildUnsigned); + m_apkPathSigned = target->apkPath(AndroidTarget::ReleaseBuildSigned); + m_keystorePathForRun = m_keystorePath; + m_certificatePasswdForRun = m_certificatePasswd; + m_jarSigner = AndroidConfigurations::instance().jarsignerPath(); + initCheckRequiredLibrariesForRun(); + return true; +} + +void AndroidPackageCreationStep::run(QFutureInterface<bool> &fi) +{ + fi.reportResult(createPackage()); +} + +BuildStepConfigWidget *AndroidPackageCreationStep::createConfigWidget() +{ + return new AndroidPackageCreationWidget(this); +} + +AndroidTarget *AndroidPackageCreationStep::androidTarget() const +{ + return qobject_cast<AndroidTarget *>(target()); +} + +void AndroidPackageCreationStep::checkRequiredLibraries() +{ + QProcess readelfProc; + QString appPath = androidTarget()->targetApplicationPath(); + if (!QFile::exists(appPath)) { + raiseError(tr("Can't find read elf information"), + tr("Can't find '%1'.\n" + "Please make sure your application is " + "built successfully and is selected in Application tab ('Run option') ").arg(appPath)); + return; + } + readelfProc.start(AndroidConfigurations::instance().readelfPath(androidTarget()->activeRunConfiguration()->abi().architecture()), + QStringList() << QLatin1String("-d") << QLatin1String("-W") << appPath); + if (!readelfProc.waitForFinished(-1)) { + readelfProc.terminate(); + return; + } + QStringList libs; + QList<QByteArray> lines = readelfProc.readAll().trimmed().split('\n'); + foreach (const QByteArray &line, lines) { + if (line.contains("(NEEDED)") && line.contains("Shared library:") ) { + const int pos = line.lastIndexOf('[') + 1; + libs << QString::fromLatin1(line.mid(pos, line.length() - pos - 1)); + } + } + QStringList checkedLibs = androidTarget()->qtLibs(); + QStringList requiredLibraries; + foreach (const QString &qtLib, androidTarget()->availableQtLibs()) { + if (libs.contains(QLatin1String("lib") + qtLib + QLatin1String(".so")) || checkedLibs.contains(qtLib)) + requiredLibraries << qtLib; + } + androidTarget()->setQtLibs(requiredLibraries); + + checkedLibs = androidTarget()->prebundledLibs(); + requiredLibraries.clear(); + foreach (const QString &qtLib, androidTarget()->availableQtLibs()) { + if (libs.contains(qtLib) || checkedLibs.contains(qtLib)) + requiredLibraries << qtLib; + } + androidTarget()->setPrebundledLibs(requiredLibraries); + emit updateRequiredLibrariesModels(); +} + +void AndroidPackageCreationStep::initCheckRequiredLibrariesForRun() +{ + m_appPath = androidTarget()->targetApplicationPath(); + m_readElf = AndroidConfigurations::instance().readelfPath(androidTarget()->activeRunConfiguration()->abi().architecture()); + m_qtLibs = androidTarget()->qtLibs(); + m_availableQtLibs = androidTarget()->availableQtLibs(); + m_prebundledLibs = androidTarget()->prebundledLibs(); +} + +void AndroidPackageCreationStep::checkRequiredLibrariesForRun() +{ + QProcess readelfProc; + if (!QFile::exists(m_appPath)) { + raiseError(tr("Can't find read elf information"), + tr("Can't find '%1'.\n" + "Please make sure your application is " + "built successfully and is selected in Application tab ('Run option') ").arg(m_appPath)); + return; + } + readelfProc.start(m_readElf, QStringList() << QLatin1String("-d") << QLatin1String("-W") << m_appPath); + if (!readelfProc.waitForFinished(-1)) { + readelfProc.terminate(); + return; + } + QStringList libs; + QList<QByteArray> lines = readelfProc.readAll().trimmed().split('\n'); + foreach (const QByteArray &line, lines) { + if (line.contains("(NEEDED)") && line.contains("Shared library:") ) { + const int pos = line.lastIndexOf('[') + 1; + libs << QString::fromLatin1(line.mid(pos, line.length() - pos - 1)); + } + } + + QStringList requiredLibraries; + foreach (const QString &qtLib, m_availableQtLibs) { + if (libs.contains(QLatin1String("lib") + qtLib + QLatin1String(".so")) || m_qtLibs.contains(qtLib)) + requiredLibraries << qtLib; + } + QMetaObject::invokeMethod(this, "setQtLibs",Qt::BlockingQueuedConnection, + Q_ARG(QStringList, requiredLibraries)); + + requiredLibraries.clear(); + foreach (const QString &qtLib, m_availableQtLibs) { + if (libs.contains(qtLib) || m_prebundledLibs.contains(qtLib)) + requiredLibraries << qtLib; + } + + QMetaObject::invokeMethod(this, "setPrebundledLibs", Qt::BlockingQueuedConnection, + Q_ARG(QStringList, requiredLibraries)); + emit updateRequiredLibrariesModels(); +} + +void AndroidPackageCreationStep::setQtLibs(const QStringList &qtLibs) +{ + androidTarget()->setQtLibs(qtLibs); +} + +void AndroidPackageCreationStep::setPrebundledLibs(const QStringList &prebundledLibs) +{ + androidTarget()->setPrebundledLibs(prebundledLibs); +} + +QString AndroidPackageCreationStep::keystorePath() +{ + return m_keystorePath; +} + +void AndroidPackageCreationStep::setKeystorePath(const QString &path) +{ + m_keystorePath = path; + m_certificatePasswd.clear(); + m_keystorePasswd.clear(); +} + +void AndroidPackageCreationStep::setKeystorePassword(const QString &pwd) +{ + m_keystorePasswd = pwd; +} + +void AndroidPackageCreationStep::setCertificateAlias(const QString &alias) +{ + m_certificateAlias = alias; +} + +void AndroidPackageCreationStep::setCertificatePassword(const QString &pwd) +{ + m_certificatePasswd = pwd; +} + +void AndroidPackageCreationStep::setOpenPackageLocation(bool open) +{ + m_openPackageLocation = open; +} + +QAbstractItemModel *AndroidPackageCreationStep::keystoreCertificates() +{ + QString rawCerts; + QProcess keytoolProc; + while (!rawCerts.length() || !m_keystorePasswd.length()) { + QStringList params; + params << QLatin1String("-list") << QLatin1String("-v") << QLatin1String("-keystore") << m_keystorePathForRun << QLatin1String("-storepass"); + if (!m_keystorePasswd.length()) + keystorePassword(); + if (!m_keystorePasswd.length()) + return 0; + params << m_keystorePasswd; + keytoolProc.start(AndroidConfigurations::instance().keytoolPath(), params); + if (!keytoolProc.waitForStarted() || !keytoolProc.waitForFinished()) { + QMessageBox::critical(0, tr("Error"), + tr("Failed to run keytool")); + return 0; + } + + if (keytoolProc.exitCode()) { + QMessageBox::critical(0, tr("Error"), + tr("Invalid password")); + m_keystorePasswd.clear(); + } + rawCerts = QString::fromLatin1(keytoolProc.readAllStandardOutput()); + } + return new CertificatesModel(rawCerts, this); +} + +bool AndroidPackageCreationStep::fromMap(const QVariantMap &map) +{ + if (!BuildStep::fromMap(map)) + return false; + m_keystorePath = map.value(KeystoreLocationKey).toString(); + return true; +} + +QVariantMap AndroidPackageCreationStep::toMap() const +{ + QVariantMap map(BuildStep::toMap()); + map.insert(KeystoreLocationKey, m_keystorePath); + return map; +} + +bool AndroidPackageCreationStep::createPackage() +{ + checkRequiredLibrariesForRun(); + + emit addOutput(tr("Copy Qt app & libs to Android package ..."), MessageOutput); + + QStringList build; + build << QLatin1String("clean"); + QFile::remove(m_gdbServerDestination); + if (m_debugBuild || !m_certificateAlias.length()) { + build << QLatin1String("debug"); + if (!QFile::copy(m_gdbServerSource, m_gdbServerDestination)) { + raiseError(tr("Can't copy gdbserver from '%1' to '%2'").arg(m_gdbServerSource) + .arg(m_gdbServerDestination)); + return false; + } + } else { + build << QLatin1String("release"); + } + + emit addOutput(tr("Creating package file ..."), MessageOutput); + + QProcess *const buildProc = new QProcess; + + connect(buildProc, SIGNAL(readyReadStandardOutput()), this, + SLOT(handleBuildStdOutOutput())); + connect(buildProc, SIGNAL(readyReadStandardError()), this, + SLOT(handleBuildStdErrOutput())); + + buildProc->setWorkingDirectory(m_androidDir); + + if (!runCommand(buildProc, m_antToolPath, build)) { + disconnect(buildProc, 0, this, 0); + buildProc->deleteLater(); + return false; + } + + if (!(m_debugBuild) && m_certificateAlias.length()) { + emit addOutput(tr("Signing package ..."), MessageOutput); + while (true) { + if (m_certificatePasswdForRun.isEmpty()) + QMetaObject::invokeMethod(this, "certificatePassword", Qt::BlockingQueuedConnection); + + if (m_certificatePasswdForRun.isEmpty()) { + disconnect(buildProc, 0, this, 0); + buildProc->deleteLater(); + return false; + } + + QByteArray keyPass = m_certificatePasswdForRun.toUtf8(); + build.clear(); + build << QLatin1String("-verbose") << QLatin1String("-keystore") << m_keystorePathForRun + << QLatin1String("-storepass") << m_keystorePasswd + << m_apkPathUnsigned + << m_certificateAlias; + buildProc->start(m_jarSigner, build); //TODO + if (!buildProc->waitForStarted()) { + disconnect(buildProc, 0, this, 0); + buildProc->deleteLater(); + return false; + } + + keyPass += "\n"; + buildProc->write(keyPass); + buildProc->waitForBytesWritten(); + buildProc->waitForFinished(); + + if (!buildProc->exitCode()) + break; + emit addOutput(tr("Failed, try again"), ErrorMessageOutput); + m_certificatePasswdForRun.clear(); + } + if (QFile::rename(m_apkPathUnsigned, m_apkPathSigned)) { + emit addOutput(tr("Release signed package created to %1") + .arg(m_apkPathSigned) + , MessageOutput); + + if (m_openPackageLocation) + QMetaObject::invokeMethod(this, "showInGraphicalShell", Qt::QueuedConnection); + } + } + emit addOutput(tr("Package created."), BuildStep::MessageOutput); + disconnect(buildProc, 0, this, 0); + buildProc->deleteLater(); + return true; +} + +void AndroidPackageCreationStep::stripAndroidLibs(const QStringList & files, Abi::Architecture architecture) +{ + QProcess stripProcess; + foreach (const QString &file, files) { + stripProcess.start(AndroidConfigurations::instance().stripPath(architecture) + QLatin1String(" --strip-unneeded ") + file); + if (!stripProcess.waitForFinished(-1)) + stripProcess.terminate(); + } +} + +bool AndroidPackageCreationStep::removeDirectory(const QString &dirPath) +{ + QDir dir(dirPath); + if (!dir.exists()) + return true; + + const QStringList &files + = dir.entryList(QDir::Files | QDir::Hidden | QDir::System); + foreach (const QString &fileName, files) { + if (!dir.remove(fileName)) + return false; + } + + const QStringList &subDirs + = dir.entryList(QDir::Dirs | QDir::NoDotAndDotDot); + foreach (const QString &subDirName, subDirs) { + if (!removeDirectory(dirPath + QLatin1Char('/') + subDirName)) + return false; + } + + return dir.rmdir(dirPath); +} + +bool AndroidPackageCreationStep::runCommand(QProcess *buildProc + , const QString &program, const QStringList &arguments) +{ + emit addOutput(tr("Package deploy: Running command '%1 %2'.").arg(program).arg(arguments.join(QLatin1String(" "))), BuildStep::MessageOutput); + buildProc->start(program, arguments); + if (!buildProc->waitForStarted()) { + raiseError(tr("Packaging failed."), + tr("Packaging error: Could not start command '%1 %2'. Reason: %3") + .arg(program).arg(arguments.join(QLatin1String(" "))).arg(buildProc->errorString())); + return false; + } + buildProc->waitForFinished(-1); + if (buildProc->error() != QProcess::UnknownError + || buildProc->exitCode() != 0) { + QString mainMessage = tr("Packaging Error: Command '%1 %2' failed.") + .arg(program).arg(arguments.join(QLatin1String(" "))); + if (buildProc->error() != QProcess::UnknownError) + mainMessage += tr(" Reason: %1").arg(buildProc->errorString()); + else + mainMessage += tr("Exit code: %1").arg(buildProc->exitCode()); + raiseError(mainMessage); + return false; + } + return true; +} + +void AndroidPackageCreationStep::handleBuildStdOutOutput() +{ + QProcess *const process = qobject_cast<QProcess *>(sender()); + if (!process) + return; + + process->setReadChannel(QProcess::StandardOutput); + while (process->canReadLine()) { + QString line = QString::fromLocal8Bit(process->readLine()); + m_outputParser.stdOutput(line); + emit addOutput(line, BuildStep::NormalOutput, BuildStep::DontAppendNewline); + } +} + +void AndroidPackageCreationStep::handleBuildStdErrOutput() +{ + QProcess *const process = qobject_cast<QProcess *>(sender()); + if (!process) + return; + + process->setReadChannel(QProcess::StandardError); + while (process->canReadLine()) { + QString line = QString::fromLocal8Bit(process->readLine()); + m_outputParser.stdError(line); + emit addOutput(line, BuildStep::ErrorOutput, BuildStep::DontAppendNewline); + } +} + +void AndroidPackageCreationStep::keystorePassword() +{ + m_keystorePasswd.clear(); + bool ok; + QString text = QInputDialog::getText(0, tr("Keystore"), + tr("Keystore password:"), QLineEdit::Password, + QString(), &ok); + if (ok && !text.isEmpty()) + m_keystorePasswd = text; +} + +void AndroidPackageCreationStep::certificatePassword() +{ + m_certificatePasswdForRun.clear(); + bool ok; + QString text = QInputDialog::getText(0, tr("Certificate"), + tr("Certificate password (%1):").arg(m_certificateAlias), QLineEdit::Password, + QString(), &ok); + if (ok && !text.isEmpty()) + m_certificatePasswdForRun = text; +} + +void AndroidPackageCreationStep::showInGraphicalShell() +{ + Core::FileUtils::showInGraphicalShell(Core::ICore::instance()->mainWindow(), m_apkPathSigned); +} + +void AndroidPackageCreationStep::raiseError(const QString &shortMsg, + const QString &detailedMsg) +{ + emit addOutput(detailedMsg.isNull() ? shortMsg : detailedMsg, BuildStep::ErrorOutput); + emit addTask(Task(Task::Error, shortMsg, Utils::FileName::fromString(QString()), -1, + TASK_CATEGORY_BUILDSYSTEM)); +} + +const QLatin1String AndroidPackageCreationStep::CreatePackageId("Qt4ProjectManager.AndroidPackageCreationStep"); + +} // namespace Internal +} // namespace Qt4ProjectManager diff --git a/src/plugins/android/androidpackagecreationstep.h b/src/plugins/android/androidpackagecreationstep.h new file mode 100644 index 00000000000..85ed43414b4 --- /dev/null +++ b/src/plugins/android/androidpackagecreationstep.h @@ -0,0 +1,144 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2012 BogDan Vatra <[email protected]> +** +** Contact: Nokia Corporation ([email protected]) +** +** +** GNU Lesser General Public License Usage +** +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this file. +** Please review the following information to ensure the GNU Lesser General +** Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** Other Usage +** +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** If you have questions regarding the use of this file, please contact +** Nokia at [email protected]. +** +**************************************************************************/ + +#ifndef ANDROIDPACKAGECREATIONSTEP_H +#define ANDROIDPACKAGECREATIONSTEP_H + +#include <projectexplorer/abi.h> +#include <projectexplorer/buildstep.h> +#include <QAbstractItemModel> +#include "javaparser.h" + +QT_BEGIN_NAMESPACE +class QDateTime; +class QFile; +class QProcess; +QT_END_NAMESPACE + +namespace Qt4ProjectManager { +class Qt4BuildConfiguration; +} + +namespace Android { +namespace Internal { +class AndroidTarget; + +class AndroidPackageCreationStep : public ProjectExplorer::BuildStep +{ + Q_OBJECT + friend class AndroidPackageCreationFactory; +public: + AndroidPackageCreationStep(ProjectExplorer::BuildStepList *bsl); + ~AndroidPackageCreationStep(); + + static bool removeDirectory(const QString &dirPath); + + static void stripAndroidLibs(const QStringList &files, ProjectExplorer::Abi::Architecture architecture); + + static const QLatin1String DefaultVersionNumber; + + AndroidTarget *androidTarget() const; + + void checkRequiredLibraries(); + void initCheckRequiredLibrariesForRun(); + void checkRequiredLibrariesForRun(); + + QString keystorePath(); + void setKeystorePath(const QString &path); + void setKeystorePassword(const QString &pwd); + void setCertificateAlias(const QString &alias); + void setCertificatePassword(const QString &pwd); + void setOpenPackageLocation(bool open); + QAbstractItemModel *keystoreCertificates(); + +protected: + virtual bool fromMap(const QVariantMap &map); + virtual QVariantMap toMap() const; + +private slots: + void handleBuildStdOutOutput(); + void handleBuildStdErrOutput(); + void keystorePassword(); + void certificatePassword(); + void showInGraphicalShell(); + void setQtLibs(const QStringList &qtLibs); + void setPrebundledLibs(const QStringList &prebundledLibs); + +private: + AndroidPackageCreationStep(ProjectExplorer::BuildStepList *buildConfig, + AndroidPackageCreationStep *other); + + void ctor(); + virtual bool init(); + virtual void run(QFutureInterface<bool> &fi); + virtual ProjectExplorer::BuildStepConfigWidget *createConfigWidget(); + virtual bool immutable() const { return true; } + bool createPackage(); + bool runCommand(QProcess *buildProc, const QString &program, const QStringList &arguments); + void raiseError(const QString &shortMsg, + const QString &detailedMsg = QString()); + + static const QLatin1String CreatePackageId; + +private: + QString m_keystorePath; + QString m_keystorePasswd; + QString m_certificateAlias; + QString m_certificatePasswd; + bool m_openPackageLocation; + JavaParser m_outputParser; + + // members to pass data from init() to run() + QString m_androidDir; + QString m_gdbServerSource; + QString m_gdbServerDestination; + bool m_debugBuild; + QString m_antToolPath; + QString m_apkPathUnsigned; + QString m_apkPathSigned; + QString m_keystorePathForRun; + QString m_certificatePasswdForRun; + QString m_jarSigner; + // more for checkLibraries + QString m_appPath; + QString m_readElf; + QStringList m_qtLibs; + QStringList m_availableQtLibs; + QStringList m_prebundledLibs; +signals: + void updateRequiredLibrariesModels(); +}; + +} // namespace Internal +} // namespace Android + +#endif // ANDROIDPACKAGECREATIONSTEP_H diff --git a/src/plugins/android/androidpackagecreationwidget.cpp b/src/plugins/android/androidpackagecreationwidget.cpp new file mode 100644 index 00000000000..3ccc08c8d2f --- /dev/null +++ b/src/plugins/android/androidpackagecreationwidget.cpp @@ -0,0 +1,556 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2012 BogDan Vatra <[email protected]> +** +** Contact: Nokia Corporation ([email protected]) +** +** +** GNU Lesser General Public License Usage +** +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this file. +** Please review the following information to ensure the GNU Lesser General +** Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** Other Usage +** +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** If you have questions regarding the use of this file, please contact +** Nokia at [email protected]. +** +**************************************************************************/ + +#include "androidpackagecreationwidget.h" +#include "androidpackagecreationstep.h" +#include "androidconfigurations.h" +#include "androidcreatekeystorecertificate.h" +#include "androidtarget.h" +#include "ui_androidpackagecreationwidget.h" + +#include <projectexplorer/project.h> +#include <projectexplorer/target.h> +#include <projectexplorer/projectexplorer.h> +#include <projectexplorer/buildmanager.h> +#include <projectexplorer/projectexplorerconstants.h> +#include <projectexplorer/projectexplorer.h> +#include <qt4projectmanager/qt4buildconfiguration.h> +#include <qt4projectmanager/qmakestep.h> + +#include <QTimer> + +#include <QFileDialog> +#include <QMessageBox> + +namespace Android { +namespace Internal { + +using namespace Qt4ProjectManager; + +const QLatin1String packageNameRegExp("^([a-z_]{1}[a-z0-9_]+(\\.[a-zA-Z_]{1}[a-zA-Z0-9_]*)*)$"); + +bool checkPackageName(const QString &packageName) +{ + return QRegExp(packageNameRegExp).exactMatch(packageName); +} + +///////////////////////////// CheckModel ///////////////////////////// +CheckModel::CheckModel(QObject *parent) : QAbstractListModel(parent) +{ + +} + +void CheckModel::setAvailableItems(const QStringList &items) +{ + beginResetModel(); + m_availableItems = items; + endResetModel(); +} + +void CheckModel::setCheckedItems(const QStringList &items) +{ + m_checkedItems = items; + if (rowCount()) + emit dataChanged(index(0), index(rowCount()-1)); +} + +const QStringList &CheckModel::checkedItems() +{ + return m_checkedItems; +} + +QVariant CheckModel::data(const QModelIndex &index, int role) const +{ + if (!index.isValid()) + return QVariant(); + switch (role) { + case Qt::CheckStateRole: + return m_checkedItems.contains(m_availableItems.at(index.row())) ? Qt::Checked : Qt::Unchecked; + case Qt::DisplayRole: + return m_availableItems.at(index.row()); + } + return QVariant(); +} + +void CheckModel::moveUp(int index) +{ + beginMoveRows(QModelIndex(), index, index, QModelIndex(), index - 1); + const QString &item1 = m_availableItems[index]; + const QString &item2 = m_availableItems[index - 1]; + m_availableItems.swap(index, index - 1); + index = m_checkedItems.indexOf(item1); + int index2 = m_checkedItems.indexOf(item2); + if (index > -1 && index2 > -1) + m_checkedItems.swap(index, index2); + endMoveRows(); +} + +bool CheckModel::setData(const QModelIndex &index, const QVariant &value, int role) +{ + if (role != Qt::CheckStateRole || !index.isValid()) + return false; + if (value.toInt() == Qt::Checked) + m_checkedItems.append(m_availableItems.at(index.row())); + else + m_checkedItems.removeAll(m_availableItems.at(index.row())); + emit dataChanged(index, index); + return true; +} + +int CheckModel::rowCount(const QModelIndex &parent) const +{ + if (parent.isValid()) + return 0; + return m_availableItems.count(); +} + +Qt::ItemFlags CheckModel::flags(const QModelIndex &/*index*/) const +{ + return Qt::ItemIsSelectable|Qt::ItemIsUserCheckable|Qt::ItemIsEnabled; +} +///////////////////////////// CheckModel ///////////////////////////// + + +///////////////////////////// PermissionsModel ///////////////////////////// +PermissionsModel::PermissionsModel(QObject *parent):QAbstractListModel(parent) +{ +} + +void PermissionsModel::setPermissions(const QStringList &permissions) +{ + m_permissions = permissions; + reset(); +} +const QStringList &PermissionsModel::permissions() +{ + return m_permissions; +} + +QModelIndex PermissionsModel::addPermission(const QString &permission) +{ + const int idx = m_permissions.count(); + beginInsertRows(QModelIndex(), idx, idx); + m_permissions.push_back(permission); + endInsertRows(); + return index(idx); +} + +bool PermissionsModel::updatePermission(QModelIndex index, const QString &permission) +{ + if (!index.isValid()) + return false; + if (m_permissions[index.row()] == permission) + return false; + m_permissions[index.row()] = permission; + emit dataChanged(index, index); + return true; +} + +void PermissionsModel::removePermission(int index) +{ + if (index >= m_permissions.size()) + return; + beginRemoveRows(QModelIndex(), index, index); + m_permissions.removeAt(index); + endRemoveRows(); +} + +QVariant PermissionsModel::data(const QModelIndex &index, int role) const +{ + if (role != Qt::DisplayRole || !index.isValid()) + return QVariant(); + return m_permissions[index.row()]; +} + +int PermissionsModel::rowCount(const QModelIndex &parent) const +{ + Q_UNUSED(parent) + return m_permissions.count(); +} +///////////////////////////// PermissionsModel ///////////////////////////// + + +///////////////////////////// AndroidPackageCreationWidget ///////////////////////////// +AndroidPackageCreationWidget::AndroidPackageCreationWidget(AndroidPackageCreationStep *step) + : ProjectExplorer::BuildStepConfigWidget(), + m_step(step), + m_ui(new Ui::AndroidPackageCreationWidget) +{ + m_qtLibsModel = new CheckModel(this); + m_prebundledLibs = new CheckModel(this); + m_permissionsModel = new PermissionsModel(this); + + m_ui->setupUi(this); + setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); + QTimer::singleShot(50, this, SLOT(initGui())); + connect(m_step, SIGNAL(updateRequiredLibrariesModels()), SLOT(updateRequiredLibrariesModels())); +} + + +void AndroidPackageCreationWidget::initGui() +{ + updateAndroidProjectInfo(); + AndroidTarget *target = m_step->androidTarget(); + connect(target, + SIGNAL(androidDirContentsChanged()), + this, SLOT(updateAndroidProjectInfo())); + m_ui->packageNameLineEdit->setValidator(new QRegExpValidator(QRegExp(packageNameRegExp), this)); + connect(m_ui->packageNameLineEdit, SIGNAL(editingFinished()), SLOT(setPackageName())); + connect(m_ui->appNameLineEdit, SIGNAL(editingFinished()), SLOT(setApplicationName())); + connect(m_ui->versionCode, SIGNAL(editingFinished()), SLOT(setVersionCode())); + connect(m_ui->versionNameLinedit, SIGNAL(editingFinished()), SLOT(setVersionName())); + connect(m_ui->targetSDKComboBox, SIGNAL(activated(QString)), SLOT(setTargetSDK(QString))); + connect(m_ui->permissionsListView, SIGNAL(activated(QModelIndex)), SLOT(permissionActivated(QModelIndex))); + connect(m_ui->addPermissionButton, SIGNAL(clicked()), SLOT(addPermission())); + connect(m_ui->removePermissionButton, SIGNAL(clicked()), SLOT(removePermission())); + connect(m_ui->permissionsComboBox->lineEdit(), SIGNAL(editingFinished()), SLOT(updatePermission())); + connect(m_ui->savePermissionsButton, SIGNAL(clicked()), SLOT(savePermissionsButton())); + connect(m_ui->discardPermissionsButton, SIGNAL(clicked()), SLOT(discardPermissionsButton())); + connect(m_ui->targetComboBox, SIGNAL(activated(QString)), SLOT(setTarget(QString))); + connect(m_qtLibsModel, SIGNAL(dataChanged(QModelIndex,QModelIndex)), SLOT(setQtLibs(QModelIndex,QModelIndex))); + connect(m_prebundledLibs, SIGNAL(dataChanged(QModelIndex,QModelIndex)), SLOT(setPrebundledLibs(QModelIndex,QModelIndex))); + connect(m_ui->prebundledLibsListView, SIGNAL(activated(QModelIndex)), SLOT(prebundledLibSelected(QModelIndex))); + connect(m_ui->prebundledLibsListView, SIGNAL(clicked(QModelIndex)), SLOT(prebundledLibSelected(QModelIndex))); + connect(m_ui->upPushButton, SIGNAL(clicked()), SLOT(prebundledLibMoveUp())); + connect(m_ui->downPushButton, SIGNAL(clicked()), SLOT(prebundledLibMoveDown())); + connect(m_ui->readInfoPushButton, SIGNAL(clicked()), SLOT(readElfInfo())); + + connect(m_ui->hIconButton, SIGNAL(clicked()), SLOT(setHDPIIcon())); + connect(m_ui->mIconButton, SIGNAL(clicked()), SLOT(setMDPIIcon())); + connect(m_ui->lIconButton, SIGNAL(clicked()), SLOT(setLDPIIcon())); + + m_ui->qtLibsListView->setModel(m_qtLibsModel); + m_ui->prebundledLibsListView->setModel(m_prebundledLibs); + m_ui->permissionsListView->setModel(m_permissionsModel); + m_ui->KeystoreLocationLineEdit->setText(m_step->keystorePath()); +} + +void AndroidPackageCreationWidget::updateAndroidProjectInfo() +{ + AndroidTarget *target = m_step->androidTarget(); + m_ui->targetSDKComboBox->clear(); + QStringList targets = AndroidConfigurations::instance().sdkTargets(); + m_ui->targetSDKComboBox->addItems(targets); + m_ui->targetSDKComboBox->setCurrentIndex(targets.indexOf(target->targetSDK())); + m_ui->packageNameLineEdit->setText(target->packageName()); + m_ui->appNameLineEdit->setText(target->applicationName()); + if (!m_ui->appNameLineEdit->text().length()) { + QString applicationName = target->project()->displayName(); + target->setPackageName(target->packageName() + QLatin1Char('.') + applicationName); + m_ui->packageNameLineEdit->setText(target->packageName()); + if (applicationName.length()) + applicationName[0] = applicationName[0].toUpper(); + m_ui->appNameLineEdit->setText(applicationName); + target->setApplicationName(applicationName); + } + m_ui->versionCode->setValue(target->versionCode()); + m_ui->versionNameLinedit->setText(target->versionName()); + + m_qtLibsModel->setAvailableItems(target->availableQtLibs()); + m_qtLibsModel->setCheckedItems(target->qtLibs()); + m_prebundledLibs->setAvailableItems(target->availablePrebundledLibs()); + m_prebundledLibs->setCheckedItems(target->prebundledLibs()); + + m_permissionsModel->setPermissions(target->permissions()); + m_ui->removePermissionButton->setEnabled(m_permissionsModel->permissions().size()); + + targets = target->availableTargetApplications(); + m_ui->targetComboBox->clear(); + m_ui->targetComboBox->addItems(targets); + m_ui->targetComboBox->setCurrentIndex(targets.indexOf(target->targetApplication())); + if (m_ui->targetComboBox->currentIndex() == -1 && targets.count()) { + m_ui->targetComboBox->setCurrentIndex(0); + target->setTargetApplication(m_ui->targetComboBox->currentText()); + } + m_ui->hIconButton->setIcon(target->highDpiIcon()); + m_ui->mIconButton->setIcon(target->mediumDpiIcon()); + m_ui->lIconButton->setIcon(target->lowDpiIcon()); +} + +void AndroidPackageCreationWidget::setPackageName() +{ + const QString packageName= m_ui->packageNameLineEdit->text(); + if (!checkPackageName(packageName)) { + QMessageBox::critical(this, tr("Invalid package name") , + tr("The package name '%1' is not valid.\n" + "Please choose a valid package name for your application (e.g. \"org.example.myapplication\").") + .arg(packageName)); + m_ui->packageNameLineEdit->selectAll(); + m_ui->packageNameLineEdit->setFocus(); + return; + } + m_step->androidTarget()->setPackageName(packageName); +} + +void AndroidPackageCreationWidget::setApplicationName() +{ + m_step->androidTarget()->setApplicationName(m_ui->appNameLineEdit->text()); +} + +void AndroidPackageCreationWidget::setTargetSDK(const QString &target) +{ + m_step->androidTarget()->setTargetSDK(target); + Qt4BuildConfiguration *bc = m_step->androidTarget()->activeQt4BuildConfiguration(); + ProjectExplorer::BuildManager *bm = ProjectExplorer::ProjectExplorerPlugin::instance()->buildManager(); + QMakeStep *qs = bc->qmakeStep(); + + if (!qs) + return; + + qs->setForced(true); + + bm->buildList(bc->stepList(QLatin1String(ProjectExplorer::Constants::BUILDSTEPS_CLEAN)), + ProjectExplorer::ProjectExplorerPlugin::displayNameForStepId(QLatin1String(ProjectExplorer::Constants::BUILDSTEPS_CLEAN))); + bm->appendStep(qs, ProjectExplorer::ProjectExplorerPlugin::displayNameForStepId(QLatin1String(ProjectExplorer::Constants::BUILDSTEPS_CLEAN))); + bc->setSubNodeBuild(0); + // Make the buildconfiguration emit a evironmentChanged() signal + // TODO find a better way + bool use = bc->useSystemEnvironment(); + bc->setUseSystemEnvironment(!use); + bc->setUseSystemEnvironment(use); +} + +void AndroidPackageCreationWidget::setVersionCode() +{ + m_step->androidTarget()->setVersionCode(m_ui->versionCode->value()); +} + +void AndroidPackageCreationWidget::setVersionName() +{ + m_step->androidTarget()->setVersionName(m_ui->versionNameLinedit->text()); +} + +void AndroidPackageCreationWidget::setTarget(const QString &target) +{ + m_step->androidTarget()->setTargetApplication(target); +} + +void AndroidPackageCreationWidget::setQtLibs(QModelIndex, QModelIndex) +{ + m_step->androidTarget()->setQtLibs(m_qtLibsModel->checkedItems()); +} + +void AndroidPackageCreationWidget::setPrebundledLibs(QModelIndex, QModelIndex) +{ + m_step->androidTarget()->setPrebundledLibs(m_prebundledLibs->checkedItems()); +} + +void AndroidPackageCreationWidget::prebundledLibSelected(const QModelIndex &index) +{ + m_ui->upPushButton->setEnabled(false); + m_ui->downPushButton->setEnabled(false); + if (!index.isValid()) + return; + if (index.row() > 0) + m_ui->upPushButton->setEnabled(true); + if (index.row() < m_prebundledLibs->rowCount(QModelIndex()) - 1) + m_ui->downPushButton->setEnabled(true); +} + +void AndroidPackageCreationWidget::prebundledLibMoveUp() +{ + const QModelIndex &index = m_ui->prebundledLibsListView->currentIndex(); + if (index.isValid()) + m_prebundledLibs->moveUp(index.row()); +} + +void AndroidPackageCreationWidget::prebundledLibMoveDown() +{ + const QModelIndex &index = m_ui->prebundledLibsListView->currentIndex(); + if (index.isValid()) + m_prebundledLibs->moveUp(index.row() + 1); +} + +void AndroidPackageCreationWidget::setHDPIIcon() +{ + QString file = QFileDialog::getOpenFileName(this, tr("Choose High DPI Icon"), QDir::homePath(), tr("png images (*.png)")); + if (!file.length()) + return; + m_step->androidTarget()->setHighDpiIcon(file); + m_ui->hIconButton->setIcon(m_step->androidTarget()->highDpiIcon()); +} + +void AndroidPackageCreationWidget::setMDPIIcon() +{ + QString file = QFileDialog::getOpenFileName(this, tr("Choose Medium DPI Icon"), QDir::homePath(), tr("png images (*.png)")); + if (!file.length()) + return; + m_step->androidTarget()->setMediumDpiIcon(file); + m_ui->mIconButton->setIcon(m_step->androidTarget()->mediumDpiIcon()); +} + +void AndroidPackageCreationWidget::setLDPIIcon() +{ + QString file = QFileDialog::getOpenFileName(this, tr("Choose Low DPI Icon"), QDir::homePath(), tr("png images (*.png)")); + if (!file.length()) + return; + m_step->androidTarget()->setLowDpiIcon(file); + m_ui->lIconButton->setIcon(m_step->androidTarget()->lowDpiIcon()); +} + +void AndroidPackageCreationWidget::permissionActivated(QModelIndex index) +{ + m_ui->permissionsComboBox->setCurrentIndex( + m_ui->permissionsComboBox->findText(m_permissionsModel->data(index, Qt::DisplayRole).toString())); + m_ui->permissionsComboBox->lineEdit()->setText(m_permissionsModel->data(index, Qt::DisplayRole).toString()); +} + +void AndroidPackageCreationWidget::addPermission() +{ + setEnabledSaveDiscardButtons(true); + m_ui->permissionsListView->setCurrentIndex(m_permissionsModel->addPermission(tr("< type or choose a permission >"))); + m_ui->permissionsComboBox->lineEdit()->setText(tr("< type or choose a permission >")); + m_ui->permissionsComboBox->setFocus(); + m_ui->removePermissionButton->setEnabled(m_permissionsModel->permissions().size()); +} + +void AndroidPackageCreationWidget::updatePermission() +{ + if (m_permissionsModel->updatePermission(m_ui->permissionsListView->currentIndex(), + m_ui->permissionsComboBox->lineEdit()->text())) + setEnabledSaveDiscardButtons(true); +} + +void AndroidPackageCreationWidget::removePermission() +{ + setEnabledSaveDiscardButtons(true); + if (m_ui->permissionsListView->currentIndex().isValid()) + m_permissionsModel->removePermission(m_ui->permissionsListView->currentIndex().row()); + m_ui->removePermissionButton->setEnabled(m_permissionsModel->permissions().size()); +} + +void AndroidPackageCreationWidget::savePermissionsButton() +{ + setEnabledSaveDiscardButtons(false); + m_step->androidTarget()->setPermissions(m_permissionsModel->permissions()); +} + +void AndroidPackageCreationWidget::discardPermissionsButton() +{ + setEnabledSaveDiscardButtons(false); + m_permissionsModel->setPermissions(m_step->androidTarget()->permissions()); + m_ui->permissionsComboBox->setCurrentIndex(-1); + m_ui->removePermissionButton->setEnabled(m_permissionsModel->permissions().size()); +} + +void AndroidPackageCreationWidget::updateRequiredLibrariesModels() +{ + m_qtLibsModel->setCheckedItems(m_step->androidTarget()->qtLibs()); + m_prebundledLibs->setCheckedItems(m_step->androidTarget()->prebundledLibs()); +} + +void AndroidPackageCreationWidget::readElfInfo() +{ + m_step->checkRequiredLibraries(); +} + +void AndroidPackageCreationWidget::setEnabledSaveDiscardButtons(bool enabled) +{ + if (!enabled) + m_ui->permissionsListView->setFocus(); + m_ui->savePermissionsButton->setEnabled(enabled); + m_ui->discardPermissionsButton->setEnabled(enabled); +} + +QString AndroidPackageCreationWidget::summaryText() const +{ + return tr("<b>Package configurations</b>"); +} + +QString AndroidPackageCreationWidget::displayName() const +{ + return m_step->displayName(); +} + +void AndroidPackageCreationWidget::setCertificates() +{ + QAbstractItemModel *certificates = m_step->keystoreCertificates(); + m_ui->signPackageCheckBox->setChecked(certificates); + m_ui->certificatesAliasComboBox->setModel(certificates); +} + +void AndroidPackageCreationWidget::on_signPackageCheckBox_toggled(bool checked) +{ + if (!checked) + return; + if (m_step->keystorePath().length()) + setCertificates(); +} + +void AndroidPackageCreationWidget::on_KeystoreCreatePushButton_clicked() +{ + AndroidCreateKeystoreCertificate d; + if (d.exec() != QDialog::Accepted) + return; + m_ui->KeystoreLocationLineEdit->setText(d.keystoreFilePath()); + m_step->setKeystorePath(d.keystoreFilePath()); + m_step->setKeystorePassword(d.keystorePassword()); + m_step->setCertificateAlias(d.certificateAlias()); + m_step->setCertificatePassword(d.certificatePassword()); + setCertificates(); +} + +void AndroidPackageCreationWidget::on_KeystoreLocationPushButton_clicked() +{ + QString keystorePath = m_step->keystorePath(); + if (!keystorePath.length()) + keystorePath = QDir::homePath(); + QString file = QFileDialog::getOpenFileName(this, tr("Select keystore file"), keystorePath, tr("Keystore files (*.keystore *.jks)")); + if (!file.length()) + return; + m_ui->KeystoreLocationLineEdit->setText(file); + m_step->setKeystorePath(file); + m_ui->signPackageCheckBox->setChecked(false); +} + +void AndroidPackageCreationWidget::on_certificatesAliasComboBox_activated(const QString &alias) +{ + if (alias.length()) + m_step->setCertificateAlias(alias); +} + +void AndroidPackageCreationWidget::on_certificatesAliasComboBox_currentIndexChanged(const QString &alias) +{ + if (alias.length()) + m_step->setCertificateAlias(alias); +} + +void AndroidPackageCreationWidget::on_openPackageLocationCheckBox_toggled(bool checked) +{ + m_step->setOpenPackageLocation(checked); +} + +} // namespace Internal +} // namespace Android + + + diff --git a/src/plugins/android/androidpackagecreationwidget.h b/src/plugins/android/androidpackagecreationwidget.h new file mode 100644 index 00000000000..59ca55ce86d --- /dev/null +++ b/src/plugins/android/androidpackagecreationwidget.h @@ -0,0 +1,150 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2012 BogDan Vatra <[email protected]> +** +** Contact: Nokia Corporation ([email protected]) +** +** +** GNU Lesser General Public License Usage +** +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this file. +** Please review the following information to ensure the GNU Lesser General +** Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** Other Usage +** +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** If you have questions regarding the use of this file, please contact +** Nokia at [email protected]. +** +**************************************************************************/ + +#ifndef ANDROIDPACKAGECREATIONWIDGET_H +#define ANDROIDPACKAGECREATIONWIDGET_H + +#include <projectexplorer/buildstep.h> +#include <QAbstractListModel> +#include <QStringList> + +QT_BEGIN_NAMESPACE +namespace Ui { class AndroidPackageCreationWidget; } +QT_END_NAMESPACE + +namespace Android { +namespace Internal { +class AndroidPackageCreationStep; + +class CheckModel: public QAbstractListModel +{ + Q_OBJECT +public: + CheckModel(QObject *parent = 0); + void setAvailableItems(const QStringList &items); + void setCheckedItems(const QStringList &items); + const QStringList &checkedItems(); + QVariant data(const QModelIndex &index, int role) const; + void moveUp(int index); + int rowCount(const QModelIndex &parent = QModelIndex()) const; + +protected: + bool setData(const QModelIndex &index, const QVariant &value, int role); + Qt::ItemFlags flags(const QModelIndex &index) const; +private: + QStringList m_availableItems; + QStringList m_checkedItems; +}; + +class PermissionsModel: public QAbstractListModel +{ + Q_OBJECT +public: + PermissionsModel(QObject *parent = 0 ); + void setPermissions(const QStringList &permissions); + const QStringList &permissions(); + QModelIndex addPermission(const QString &permission); + bool updatePermission(QModelIndex index, const QString &permission); + void removePermission(int index); + QVariant data(const QModelIndex &index, int role) const; + +protected: + int rowCount(const QModelIndex &parent) const; + +private: + QStringList m_permissions; +}; + +class AndroidPackageCreationWidget : public ProjectExplorer::BuildStepConfigWidget +{ + Q_OBJECT +public: + AndroidPackageCreationWidget(AndroidPackageCreationStep *step); + + virtual QString summaryText() const; + virtual QString displayName() const; + +public slots: + void readElfInfo(); + +private: + void setEnabledSaveDiscardButtons(bool enabled); + void setCertificates(); + +private slots: + void initGui(); + void updateAndroidProjectInfo(); + + void setPackageName(); + void setApplicationName(); + void setTargetSDK(const QString &target); + void setVersionCode(); + void setVersionName(); + void setTarget(const QString &target); + + void setQtLibs(QModelIndex, QModelIndex); + void setPrebundledLibs(QModelIndex, QModelIndex); + void prebundledLibSelected(const QModelIndex &index); + void prebundledLibMoveUp(); + void prebundledLibMoveDown(); + + void setHDPIIcon(); + void setMDPIIcon(); + void setLDPIIcon(); + + void permissionActivated(QModelIndex index); + void addPermission(); + void updatePermission(); + void removePermission(); + void savePermissionsButton(); + void discardPermissionsButton(); + void updateRequiredLibrariesModels(); + void on_signPackageCheckBox_toggled(bool checked); + void on_KeystoreCreatePushButton_clicked(); + void on_KeystoreLocationPushButton_clicked(); + void on_certificatesAliasComboBox_activated(const QString &alias); + void on_certificatesAliasComboBox_currentIndexChanged(const QString &alias); + + void on_openPackageLocationCheckBox_toggled(bool checked); + +private: + AndroidPackageCreationStep *const m_step; + Ui::AndroidPackageCreationWidget *const m_ui; + CheckModel *m_qtLibsModel; + CheckModel *m_prebundledLibs; + PermissionsModel *m_permissionsModel; +}; + +} // namespace Internal +} // namespace Android + +#endif // ANDROIDPACKAGECREATIONWIDGET_H diff --git a/src/plugins/android/androidpackagecreationwidget.ui b/src/plugins/android/androidpackagecreationwidget.ui new file mode 100644 index 00000000000..ef0f66b79db --- /dev/null +++ b/src/plugins/android/androidpackagecreationwidget.ui @@ -0,0 +1,1262 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>AndroidPackageCreationWidget</class> + <widget class="QWidget" name="AndroidPackageCreationWidget"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>649</width> + <height>384</height> + </rect> + </property> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> + <horstretch>1</horstretch> + <verstretch>1</verstretch> + </sizepolicy> + </property> + <layout class="QVBoxLayout" name="verticalLayout"> + <item> + <widget class="QTabWidget" name="tabWidget"> + <property name="tabPosition"> + <enum>QTabWidget::South</enum> + </property> + <property name="currentIndex"> + <number>0</number> + </property> + <widget class="QWidget" name="tab"> + <attribute name="title"> + <string>Manifest</string> + </attribute> + <layout class="QFormLayout" name="formLayout"> + <property name="fieldGrowthPolicy"> + <enum>QFormLayout::ExpandingFieldsGrow</enum> + </property> + <item row="0" column="0"> + <widget class="QLabel" name="editDebianFileLabel_4"> + <property name="text"> + <string><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'DejaVu Sans'; font-size:9pt; font-weight:400; font-style:normal;"> +<table border="0" style="-qt-table-type: root; margin-top:4px; margin-bottom:4px; margin-left:4px; margin-right:4px;"> +<tr> +<td style="border: none;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">Android target SDK:</span></p></td></tr></table></body></html></string> + </property> + </widget> + </item> + <item row="0" column="1"> + <widget class="QComboBox" name="targetSDKComboBox"/> + </item> + <item row="1" column="0"> + <widget class="QLabel" name="editDebianFileLabel_2"> + <property name="text"> + <string><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'DejaVu Sans'; font-size:9pt; font-weight:400; font-style:normal;"> +<table style="-qt-table-type: root; margin-top:4px; margin-bottom:4px; margin-left:4px; margin-right:4px;"> +<tr> +<td style="border: none;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">Package name:</span></p></td></tr></table></body></html></string> + </property> + </widget> + </item> + <item row="1" column="1"> + <widget class="QLineEdit" name="packageNameLineEdit"> + <property name="toolTip"> + <string><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'DejaVu Sans'; font-size:9pt; font-weight:400; font-style:normal;"> +<table style="-qt-table-type: root; margin-top:4px; margin-bottom:4px; margin-left:4px; margin-right:4px;"> +<tr> +<td style="border: none;"> +<p align="justify" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Please choose a valid package name for your application (e.g. &quot;org.example.myapplication&quot;). </p> +<p align="justify" style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> +<p align="justify" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Packages are usually defined using a hierarchical naming pattern, with levels in the hierarchy separated by periods (.) (pronounced &quot;dot&quot;).</p> +<p align="justify" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">In general, a package name begins with the top level domain name of the organization and then the organization's domain and then any subdomains listed in reverse order. The organization can then choose a specific name for their package. Package names should be all lowercase characters whenever possible.</p> +<p align="justify" style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> +<p align="justify" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Complete conventions for disambiguating package names and rules for naming packages when the Internet domain name cannot be directly used as a package name are described in section 7.7 of the Java Language Specification.</p> +<p align="justify" style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p></td></tr></table></body></html></string> + </property> + </widget> + </item> + <item row="2" column="0"> + <widget class="QLabel" name="label"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="font"> + <font> + <weight>75</weight> + <bold>true</bold> + </font> + </property> + <property name="text"> + <string><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'DejaVu Sans'; font-size:9pt; font-weight:600; font-style:normal;"> +<table style="-qt-table-type: root; margin-top:4px; margin-bottom:4px; margin-left:4px; margin-right:4px;"> +<tr> +<td style="border: none;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Version code:</p></td></tr></table></body></html></string> + </property> + </widget> + </item> + <item row="2" column="1"> + <widget class="QSpinBox" name="versionCode"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Maximum" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="maximum"> + <number>99</number> + </property> + <property name="value"> + <number>1</number> + </property> + </widget> + </item> + <item row="3" column="0"> + <widget class="QLabel" name="label_2"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="font"> + <font> + <weight>75</weight> + <bold>true</bold> + </font> + </property> + <property name="text"> + <string><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'DejaVu Sans'; font-size:9pt; font-weight:600; font-style:normal;"> +<table border="0" style="-qt-table-type: root; margin-top:4px; margin-bottom:4px; margin-left:4px; margin-right:4px;"> +<tr> +<td style="border: none;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Version name:</p></td></tr></table></body></html></string> + </property> + </widget> + </item> + <item row="3" column="1"> + <widget class="QLineEdit" name="versionNameLinedit"> + <property name="text"> + <string>1.0.0</string> + </property> + </widget> + </item> + </layout> + </widget> + <widget class="QWidget" name="tab_3"> + <attribute name="title"> + <string>Application</string> + </attribute> + <layout class="QFormLayout" name="formLayout_2"> + <item row="0" column="0"> + <widget class="QLabel" name="editDebianFileLabel_3"> + <property name="text"> + <string><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'DejaVu Sans'; font-size:9pt; font-weight:400; font-style:normal;"> +<table border="0" style="-qt-table-type: root; margin-top:4px; margin-bottom:4px; margin-left:4px; margin-right:4px;"> +<tr> +<td style="border: none;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">Application name:</span></p></td></tr></table></body></html></string> + </property> + </widget> + </item> + <item row="0" column="1"> + <widget class="QLineEdit" name="appNameLineEdit"/> + </item> + <item row="1" column="0"> + <widget class="QLabel" name="editDebianFileLabel_5"> + <property name="text"> + <string><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'DejaVu Sans'; font-size:9pt; font-weight:400; font-style:normal;"> +<table border="0" style="-qt-table-type: root; margin-top:4px; margin-bottom:4px; margin-left:4px; margin-right:4px;"> +<tr> +<td style="border: none;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">Run:</span></p></td></tr></table></body></html></string> + </property> + <property name="textFormat"> + <enum>Qt::AutoText</enum> + </property> + </widget> + </item> + <item row="1" column="1"> + <widget class="QComboBox" name="targetComboBox"/> + </item> + <item row="2" column="0"> + <widget class="QLabel" name="packageManagerIconLabel_2"> + <property name="minimumSize"> + <size> + <width>0</width> + <height>48</height> + </size> + </property> + <property name="text"> + <string><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'DejaVu Sans'; font-size:9pt; font-weight:400; font-style:normal;"> +<table style="-qt-table-type: root; margin-top:4px; margin-bottom:4px; margin-left:4px; margin-right:4px;"> +<tr> +<td style="border: none;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">Application icon:</span></p></td></tr></table></body></html></string> + </property> + </widget> + </item> + <item row="2" column="1"> + <layout class="QHBoxLayout" name="horizontalLayout"> + <item> + <widget class="QToolButton" name="lIconButton"> + <property name="minimumSize"> + <size> + <width>48</width> + <height>48</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>48</width> + <height>48</height> + </size> + </property> + <property name="toolTip"> + <string>Select low dpi icon</string> + </property> + <property name="text"> + <string/> + </property> + </widget> + </item> + <item> + <spacer name="horizontalSpacer_4"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>28</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="QToolButton" name="mIconButton"> + <property name="minimumSize"> + <size> + <width>48</width> + <height>48</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>48</width> + <height>48</height> + </size> + </property> + <property name="toolTip"> + <string>Select medium dpi icon</string> + </property> + <property name="text"> + <string/> + </property> + </widget> + </item> + <item> + <spacer name="horizontalSpacer_5"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>28</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="QToolButton" name="hIconButton"> + <property name="minimumSize"> + <size> + <width>48</width> + <height>48</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>48</width> + <height>48</height> + </size> + </property> + <property name="toolTip"> + <string>Select high dpi icon</string> + </property> + <property name="text"> + <string/> + </property> + </widget> + </item> + </layout> + </item> + </layout> + </widget> + <widget class="QWidget" name="tab_2"> + <attribute name="title"> + <string>Permissions</string> + </attribute> + <layout class="QVBoxLayout" name="verticalLayout_3"> + <item> + <layout class="QHBoxLayout" name="horizontalLayout_3"> + <item> + <widget class="QListView" name="permissionsListView"> + <property name="minimumSize"> + <size> + <width>0</width> + <height>200</height> + </size> + </property> + </widget> + </item> + <item> + <layout class="QVBoxLayout" name="verticalLayout_2"> + <item> + <widget class="QPushButton" name="addPermissionButton"> + <property name="text"> + <string>Add</string> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="removePermissionButton"> + <property name="text"> + <string>Remove</string> + </property> + </widget> + </item> + <item> + <spacer name="verticalSpacer"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>40</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="QPushButton" name="savePermissionsButton"> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="text"> + <string>Save</string> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="discardPermissionsButton"> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="text"> + <string>Discard</string> + </property> + </widget> + </item> + </layout> + </item> + </layout> + </item> + <item> + <layout class="QHBoxLayout" name="horizontalLayout_2"> + <item> + <widget class="QLabel" name="label_3"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Minimum" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>Name:</string> + </property> + <property name="alignment"> + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + </widget> + </item> + <item> + <widget class="QComboBox" name="permissionsComboBox"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="editable"> + <bool>true</bool> + </property> + <property name="currentIndex"> + <number>0</number> + </property> + <item> + <property name="text"> + <string>android.permission.ACCESS_CHECKIN_PROPERTIES</string> + </property> + </item> + <item> + <property name="text"> + <string>android.permission.ACCESS_COARSE_LOCATION</string> + </property> + </item> + <item> + <property name="text"> + <string>android.permission.ACCESS_FINE_LOCATION</string> + </property> + </item> + <item> + <property name="text"> + <string>android.permission.ACCESS_LOCATION_EXTRA_COMMANDS</string> + </property> + </item> + <item> + <property name="text"> + <string>android.permission.ACCESS_MOCK_LOCATION</string> + </property> + </item> + <item> + <property name="text"> + <string>android.permission.ACCESS_NETWORK_STATE</string> + </property> + </item> + <item> + <property name="text"> + <string>android.permission.ACCESS_SURFACE_FLINGER</string> + </property> + </item> + <item> + <property name="text"> + <string>android.permission.ACCESS_WIFI_STATE</string> + </property> + </item> + <item> + <property name="text"> + <string>android.permission.ACCOUNT_MANAGER</string> + </property> + </item> + <item> + <property name="text"> + <string>android.permission.AUTHENTICATE_ACCOUNTS</string> + </property> + </item> + <item> + <property name="text"> + <string>android.permission.BATTERY_STATS</string> + </property> + </item> + <item> + <property name="text"> + <string>android.permission.BIND_APPWIDGET</string> + </property> + </item> + <item> + <property name="text"> + <string>android.permission.BIND_DEVICE_ADMIN</string> + </property> + </item> + <item> + <property name="text"> + <string>android.permission.BIND_INPUT_METHOD</string> + </property> + </item> + <item> + <property name="text"> + <string>android.permission.BIND_REMOTEVIEWS</string> + </property> + </item> + <item> + <property name="text"> + <string>android.permission.BIND_WALLPAPER</string> + </property> + </item> + <item> + <property name="text"> + <string>android.permission.BLUETOOTH</string> + </property> + </item> + <item> + <property name="text"> + <string>android.permission.BLUETOOTH_ADMIN</string> + </property> + </item> + <item> + <property name="text"> + <string>android.permission.BRICK</string> + </property> + </item> + <item> + <property name="text"> + <string>android.permission.BROADCAST_PACKAGE_REMOVED</string> + </property> + </item> + <item> + <property name="text"> + <string>android.permission.BROADCAST_SMS</string> + </property> + </item> + <item> + <property name="text"> + <string>android.permission.BROADCAST_STICKY</string> + </property> + </item> + <item> + <property name="text"> + <string>android.permission.BROADCAST_WAP_PUSH</string> + </property> + </item> + <item> + <property name="text"> + <string>android.permission.CALL_PHONE</string> + </property> + </item> + <item> + <property name="text"> + <string>android.permission.CALL_PRIVILEGED</string> + </property> + </item> + <item> + <property name="text"> + <string>android.permission.CAMERA</string> + </property> + </item> + <item> + <property name="text"> + <string>android.permission.CHANGE_COMPONENT_ENABLED_STATE</string> + </property> + </item> + <item> + <property name="text"> + <string>android.permission.CHANGE_CONFIGURATION</string> + </property> + </item> + <item> + <property name="text"> + <string>android.permission.CHANGE_NETWORK_STATE</string> + </property> + </item> + <item> + <property name="text"> + <string>android.permission.CHANGE_WIFI_MULTICAST_STATE</string> + </property> + </item> + <item> + <property name="text"> + <string>android.permission.CHANGE_WIFI_STATE</string> + </property> + </item> + <item> + <property name="text"> + <string>android.permission.CLEAR_APP_CACHE</string> + </property> + </item> + <item> + <property name="text"> + <string>android.permission.CLEAR_APP_USER_DATA</string> + </property> + </item> + <item> + <property name="text"> + <string>android.permission.CONTROL_LOCATION_UPDATES</string> + </property> + </item> + <item> + <property name="text"> + <string>android.permission.DELETE_CACHE_FILES</string> + </property> + </item> + <item> + <property name="text"> + <string>android.permission.DELETE_PACKAGES</string> + </property> + </item> + <item> + <property name="text"> + <string>android.permission.DEVICE_POWER</string> + </property> + </item> + <item> + <property name="text"> + <string>android.permission.DIAGNOSTIC</string> + </property> + </item> + <item> + <property name="text"> + <string>android.permission.DISABLE_KEYGUARD</string> + </property> + </item> + <item> + <property name="text"> + <string>android.permission.DUMP</string> + </property> + </item> + <item> + <property name="text"> + <string>android.permission.EXPAND_STATUS_BAR</string> + </property> + </item> + <item> + <property name="text"> + <string>android.permission.FACTORY_TEST</string> + </property> + </item> + <item> + <property name="text"> + <string>android.permission.FLASHLIGHT</string> + </property> + </item> + <item> + <property name="text"> + <string>android.permission.FORCE_BACK</string> + </property> + </item> + <item> + <property name="text"> + <string>android.permission.GET_ACCOUNTS</string> + </property> + </item> + <item> + <property name="text"> + <string>android.permission.GET_PACKAGE_SIZE</string> + </property> + </item> + <item> + <property name="text"> + <string>android.permission.GET_TASKS</string> + </property> + </item> + <item> + <property name="text"> + <string>android.permission.GLOBAL_SEARCH</string> + </property> + </item> + <item> + <property name="text"> + <string>android.permission.HARDWARE_TEST</string> + </property> + </item> + <item> + <property name="text"> + <string>android.permission.INJECT_EVENTS</string> + </property> + </item> + <item> + <property name="text"> + <string>android.permission.INSTALL_LOCATION_PROVIDER</string> + </property> + </item> + <item> + <property name="text"> + <string>android.permission.INSTALL_PACKAGES</string> + </property> + </item> + <item> + <property name="text"> + <string>android.permission.INTERNAL_SYSTEM_WINDOW</string> + </property> + </item> + <item> + <property name="text"> + <string>android.permission.INTERNET</string> + </property> + </item> + <item> + <property name="text"> + <string>android.permission.KILL_BACKGROUND_PROCESSES</string> + </property> + </item> + <item> + <property name="text"> + <string>android.permission.MANAGE_ACCOUNTS</string> + </property> + </item> + <item> + <property name="text"> + <string>android.permission.MANAGE_APP_TOKENS</string> + </property> + </item> + <item> + <property name="text"> + <string>android.permission.MASTER_CLEAR</string> + </property> + </item> + <item> + <property name="text"> + <string>android.permission.MODIFY_AUDIO_SETTINGS</string> + </property> + </item> + <item> + <property name="text"> + <string>android.permission.MODIFY_PHONE_STATE</string> + </property> + </item> + <item> + <property name="text"> + <string>android.permission.MOUNT_FORMAT_FILESYSTEMS</string> + </property> + </item> + <item> + <property name="text"> + <string>android.permission.MOUNT_UNMOUNT_FILESYSTEMS</string> + </property> + </item> + <item> + <property name="text"> + <string>android.permission.NFC</string> + </property> + </item> + <item> + <property name="text"> + <string>android.permission.PERSISTENT_ACTIVITY</string> + </property> + </item> + <item> + <property name="text"> + <string>android.permission.PROCESS_OUTGOING_CALLS</string> + </property> + </item> + <item> + <property name="text"> + <string>android.permission.READ_CALENDAR</string> + </property> + </item> + <item> + <property name="text"> + <string>android.permission.READ_CONTACTS</string> + </property> + </item> + <item> + <property name="text"> + <string>android.permission.READ_FRAME_BUFFER</string> + </property> + </item> + <item> + <property name="text"> + <string>com.android.browser.permission.READ_HISTORY_BOOKMARKS</string> + </property> + </item> + <item> + <property name="text"> + <string>android.permission.READ_INPUT_STATE</string> + </property> + </item> + <item> + <property name="text"> + <string>android.permission.READ_LOGS</string> + </property> + </item> + <item> + <property name="text"> + <string>android.permission.READ_OWNER_DATA</string> + </property> + </item> + <item> + <property name="text"> + <string>android.permission.READ_PHONE_STATE</string> + </property> + </item> + <item> + <property name="text"> + <string>android.permission.READ_SMS</string> + </property> + </item> + <item> + <property name="text"> + <string>android.permission.READ_SYNC_SETTINGS</string> + </property> + </item> + <item> + <property name="text"> + <string>android.permission.READ_SYNC_STATS</string> + </property> + </item> + <item> + <property name="text"> + <string>android.permission.REBOOT</string> + </property> + </item> + <item> + <property name="text"> + <string>android.permission.RECEIVE_BOOT_COMPLETED</string> + </property> + </item> + <item> + <property name="text"> + <string>android.permission.RECEIVE_MMS</string> + </property> + </item> + <item> + <property name="text"> + <string>android.permission.RECEIVE_SMS</string> + </property> + </item> + <item> + <property name="text"> + <string>android.permission.RECEIVE_WAP_PUSH</string> + </property> + </item> + <item> + <property name="text"> + <string>android.permission.RECORD_AUDIO</string> + </property> + </item> + <item> + <property name="text"> + <string>android.permission.REORDER_TASKS</string> + </property> + </item> + <item> + <property name="text"> + <string>android.permission.RESTART_PACKAGES</string> + </property> + </item> + <item> + <property name="text"> + <string>android.permission.SEND_SMS</string> + </property> + </item> + <item> + <property name="text"> + <string>android.permission.SET_ACTIVITY_WATCHER</string> + </property> + </item> + <item> + <property name="text"> + <string>com.android.alarm.permission.SET_ALARM</string> + </property> + </item> + <item> + <property name="text"> + <string>android.permission.SET_ALWAYS_FINISH</string> + </property> + </item> + <item> + <property name="text"> + <string>android.permission.SET_ANIMATION_SCALE</string> + </property> + </item> + <item> + <property name="text"> + <string>android.permission.SET_DEBUG_APP</string> + </property> + </item> + <item> + <property name="text"> + <string>android.permission.SET_ORIENTATION</string> + </property> + </item> + <item> + <property name="text"> + <string>android.permission.SET_PREFERRED_APPLICATIONS</string> + </property> + </item> + <item> + <property name="text"> + <string>android.permission.SET_PROCESS_LIMIT</string> + </property> + </item> + <item> + <property name="text"> + <string>android.permission.SET_TIME</string> + </property> + </item> + <item> + <property name="text"> + <string>android.permission.SET_TIME_ZONE</string> + </property> + </item> + <item> + <property name="text"> + <string>android.permission.SET_WALLPAPER</string> + </property> + </item> + <item> + <property name="text"> + <string>android.permission.SET_WALLPAPER_HINTS</string> + </property> + </item> + <item> + <property name="text"> + <string>android.permission.SIGNAL_PERSISTENT_PROCESSES</string> + </property> + </item> + <item> + <property name="text"> + <string>android.permission.STATUS_BAR</string> + </property> + </item> + <item> + <property name="text"> + <string>android.permission.SUBSCRIBED_FEEDS_READ</string> + </property> + </item> + <item> + <property name="text"> + <string>android.permission.SUBSCRIBED_FEEDS_WRITE</string> + </property> + </item> + <item> + <property name="text"> + <string>android.permission.SYSTEM_ALERT_WINDOW</string> + </property> + </item> + <item> + <property name="text"> + <string>android.permission.UPDATE_DEVICE_STATS</string> + </property> + </item> + <item> + <property name="text"> + <string>android.permission.USE_CREDENTIALS</string> + </property> + </item> + <item> + <property name="text"> + <string>android.permission.USE_SIP</string> + </property> + </item> + <item> + <property name="text"> + <string>android.permission.VIBRATE</string> + </property> + </item> + <item> + <property name="text"> + <string>android.permission.WAKE_LOCK</string> + </property> + </item> + <item> + <property name="text"> + <string>android.permission.WRITE_APN_SETTINGS</string> + </property> + </item> + <item> + <property name="text"> + <string>android.permission.WRITE_CALENDAR</string> + </property> + </item> + <item> + <property name="text"> + <string>android.permission.WRITE_CONTACTS</string> + </property> + </item> + <item> + <property name="text"> + <string>android.permission.WRITE_EXTERNAL_STORAGE</string> + </property> + </item> + <item> + <property name="text"> + <string>android.permission.WRITE_GSERVICES</string> + </property> + </item> + <item> + <property name="text"> + <string>com.android.browser.permission.WRITE_HISTORY_BOOKMARKS</string> + </property> + </item> + <item> + <property name="text"> + <string>android.permission.WRITE_OWNER_DATA</string> + </property> + </item> + <item> + <property name="text"> + <string>android.permission.WRITE_SECURE_SETTINGS</string> + </property> + </item> + <item> + <property name="text"> + <string>android.permission.WRITE_SETTINGS</string> + </property> + </item> + <item> + <property name="text"> + <string>android.permission.WRITE_SMS</string> + </property> + </item> + <item> + <property name="text"> + <string>android.permission.WRITE_SYNC_SETTINGS</string> + </property> + </item> + </widget> + </item> + </layout> + </item> + </layout> + </widget> + <widget class="QWidget" name="tab_4"> + <attribute name="title"> + <string>Libraries</string> + </attribute> + <layout class="QGridLayout" name="gridLayout_2"> + <item row="0" column="0" colspan="2"> + <widget class="QPushButton" name="readInfoPushButton"> + <property name="toolTip"> + <string>Automatically check required Qt libraries from compiled application</string> + </property> + <property name="text"> + <string>Read information from application (must be compiled)</string> + </property> + </widget> + </item> + <item row="1" column="0"> + <widget class="QFrame" name="frame"> + <property name="frameShape"> + <enum>QFrame::StyledPanel</enum> + </property> + <property name="frameShadow"> + <enum>QFrame::Raised</enum> + </property> + <layout class="QVBoxLayout" name="verticalLayout_4"> + <item> + <widget class="QLabel" name="label_4"> + <property name="text"> + <string>Required Qt libraries</string> + </property> + <property name="alignment"> + <set>Qt::AlignCenter</set> + </property> + </widget> + </item> + <item> + <widget class="QListView" name="qtLibsListView"/> + </item> + </layout> + </widget> + </item> + <item row="1" column="1"> + <widget class="QFrame" name="frame_2"> + <property name="frameShape"> + <enum>QFrame::StyledPanel</enum> + </property> + <property name="frameShadow"> + <enum>QFrame::Raised</enum> + </property> + <layout class="QGridLayout" name="gridLayout"> + <item row="0" column="0" colspan="2"> + <widget class="QLabel" name="label_5"> + <property name="text"> + <string><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'DejaVu Sans'; font-size:9pt; font-weight:400; font-style:normal;"> +<table border="0" style="-qt-table-type: root; margin-top:4px; margin-bottom:4px; margin-left:4px; margin-right:4px;"> +<tr> +<td style="border: none;"> +<p align="center" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Prebundled libraries</p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Be aware, the order is very important: if library A depends on library B, B MUST be come before A!</p></td></tr></table></body></html></string> + </property> + <property name="wordWrap"> + <bool>true</bool> + </property> + </widget> + </item> + <item row="1" column="0"> + <widget class="QListView" name="prebundledLibsListView"/> + </item> + <item row="1" column="1"> + <layout class="QVBoxLayout" name="verticalLayout_5"> + <item> + <spacer name="verticalSpacer_2"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>40</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="QPushButton" name="upPushButton"> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="text"> + <string>Up</string> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="downPushButton"> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="text"> + <string>Down</string> + </property> + </widget> + </item> + <item> + <spacer name="verticalSpacer_3"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>40</height> + </size> + </property> + </spacer> + </item> + </layout> + </item> + </layout> + </widget> + </item> + </layout> + </widget> + <widget class="QWidget" name="tab_5"> + <attribute name="title"> + <string>Sign package</string> + </attribute> + <layout class="QFormLayout" name="formLayout_3"> + <item row="0" column="0" colspan="2"> + <layout class="QGridLayout" name="gridLayout_3"> + <item row="0" column="0" colspan="2"> + <layout class="QHBoxLayout" name="horizontalLayout_4"> + <item> + <widget class="QLabel" name="KeystoreLocationLabel"> + <property name="text"> + <string>Keystore:</string> + </property> + <property name="alignment"> + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + </widget> + </item> + <item> + <widget class="QLineEdit" name="KeystoreLocationLineEdit"> + <property name="readOnly"> + <bool>true</bool> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="KeystoreCreatePushButton"> + <property name="text"> + <string>Create</string> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="KeystoreLocationPushButton"> + <property name="text"> + <string>Browse</string> + </property> + </widget> + </item> + </layout> + </item> + <item row="1" column="0"> + <widget class="QCheckBox" name="signPackageCheckBox"> + <property name="text"> + <string>Sign package</string> + </property> + </widget> + </item> + <item row="1" column="1"> + <widget class="QCheckBox" name="openPackageLocationCheckBox"> + <property name="text"> + <string>Open package location after is complete</string> + </property> + <property name="checked"> + <bool>true</bool> + </property> + </widget> + </item> + <item row="2" column="0" colspan="2"> + <layout class="QHBoxLayout" name="horizontalLayout_5"> + <item> + <widget class="QLabel" name="KeystoreLocationLabel_2"> + <property name="text"> + <string>Certificate alias:</string> + </property> + <property name="alignment"> + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + </widget> + </item> + <item> + <widget class="QComboBox" name="certificatesAliasComboBox"> + <property name="enabled"> + <bool>false</bool> + </property> + </widget> + </item> + </layout> + </item> + </layout> + </item> + </layout> + </widget> + </widget> + </item> + </layout> + </widget> + <resources/> + <connections> + <connection> + <sender>signPackageCheckBox</sender> + <signal>toggled(bool)</signal> + <receiver>certificatesAliasComboBox</receiver> + <slot>setEnabled(bool)</slot> + <hints> + <hint type="sourcelabel"> + <x>138</x> + <y>93</y> + </hint> + <hint type="destinationlabel"> + <x>184</x> + <y>139</y> + </hint> + </hints> + </connection> + </connections> + <slots> + <slot>addFile()</slot> + <slot>removeFile()</slot> + <slot>handleSkipButtonToggled(bool)</slot> + <slot>versionInfoChanged()</slot> + <slot>editDebianFile()</slot> + <slot>setPackageManagerIcon()</slot> + </slots> +</ui> diff --git a/src/plugins/android/androidpackageinstallationfactory.cpp b/src/plugins/android/androidpackageinstallationfactory.cpp new file mode 100644 index 00000000000..f6b9ff9fc71 --- /dev/null +++ b/src/plugins/android/androidpackageinstallationfactory.cpp @@ -0,0 +1,113 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2012 BogDan Vatra <[email protected]> +** +** Contact: Nokia Corporation ([email protected]) +** +** +** GNU Lesser General Public License Usage +** +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this file. +** Please review the following information to ensure the GNU Lesser General +** Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** Other Usage +** +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** If you have questions regarding the use of this file, please contact +** Nokia at [email protected]. +** +**************************************************************************/ + +#include "androidpackageinstallationfactory.h" + +#include "androidpackageinstallationstep.h" + +#include <projectexplorer/buildsteplist.h> +#include <projectexplorer/projectexplorerconstants.h> +#include <projectexplorer/target.h> +#include <qt4projectmanager/qt4projectmanagerconstants.h> + +#include <QCoreApplication> + +using namespace ProjectExplorer; + +namespace Android { +namespace Internal { + +AndroidPackageInstallationFactory::AndroidPackageInstallationFactory(QObject *parent) + : IBuildStepFactory(parent) +{ +} + +QStringList AndroidPackageInstallationFactory::availableCreationIds(BuildStepList *parent) const +{ + if (parent->id() == QLatin1String(ProjectExplorer::Constants::BUILDSTEPS_DEPLOY) + && parent->target()->id() == QLatin1String(Qt4ProjectManager::Constants::ANDROID_DEVICE_TARGET_ID) + && !parent->contains(AndroidPackageInstallationStep::Id)) + return QStringList() << AndroidPackageInstallationStep::Id; + return QStringList(); +} + +QString AndroidPackageInstallationFactory::displayNameForId(const QString &id) const +{ + if (id == AndroidPackageInstallationStep::Id) + return QCoreApplication::translate("Qt4ProjectManager::Internal::AndroidPackageInstallationFactory", + "Deploy to device"); + return QString(); +} + +bool AndroidPackageInstallationFactory::canCreate(BuildStepList *parent, const QString &id) const +{ + return parent->id() == QLatin1String(ProjectExplorer::Constants::BUILDSTEPS_DEPLOY) + && id == QLatin1String(AndroidPackageInstallationStep::Id) + && parent->target()->id() == QLatin1String(Qt4ProjectManager::Constants::ANDROID_DEVICE_TARGET_ID) + && !parent->contains(AndroidPackageInstallationStep::Id); +} + +BuildStep *AndroidPackageInstallationFactory::create(BuildStepList *parent, const QString &id) +{ + Q_ASSERT(canCreate(parent, id)); + return new AndroidPackageInstallationStep(parent); +} + +bool AndroidPackageInstallationFactory::canRestore(BuildStepList *parent, const QVariantMap &map) const +{ + return canCreate(parent, idFromMap(map)); +} + +BuildStep *AndroidPackageInstallationFactory::restore(BuildStepList *parent, const QVariantMap &map) +{ + Q_ASSERT(canRestore(parent, map)); + AndroidPackageInstallationStep * const step = new AndroidPackageInstallationStep(parent); + if (!step->fromMap(map)) { + delete step; + return 0; + } + return step; +} + +bool AndroidPackageInstallationFactory::canClone(BuildStepList *parent, BuildStep *product) const +{ + return canCreate(parent, product->id()); +} + +BuildStep *AndroidPackageInstallationFactory::clone(BuildStepList *parent, BuildStep *product) +{ + Q_ASSERT(canClone(parent, product)); + return new AndroidPackageInstallationStep(parent, static_cast<AndroidPackageInstallationStep*>(product)); +} + +} // namespace Internal +} // namespace Android diff --git a/src/plugins/android/androidpackageinstallationfactory.h b/src/plugins/android/androidpackageinstallationfactory.h new file mode 100644 index 00000000000..b6619f99e8a --- /dev/null +++ b/src/plugins/android/androidpackageinstallationfactory.h @@ -0,0 +1,69 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2012 BogDan Vatra <[email protected]> +** +** Contact: Nokia Corporation ([email protected]) +** +** +** GNU Lesser General Public License Usage +** +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this file. +** Please review the following information to ensure the GNU Lesser General +** Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** Other Usage +** +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** If you have questions regarding the use of this file, please contact +** Nokia at [email protected]. +** +**************************************************************************/ + +#ifndef ANDROIDPACKAGEINSTALLATIONFACTORY_H +#define ANDROIDPACKAGEINSTALLATIONFACTORY_H +#include <projectexplorer/buildstep.h> + +namespace Android { +namespace Internal { + +class AndroidPackageInstallationFactory: public ProjectExplorer::IBuildStepFactory +{ + Q_OBJECT +public: + explicit AndroidPackageInstallationFactory(QObject *parent = 0); + + virtual QStringList availableCreationIds(ProjectExplorer::BuildStepList *parent) const; + virtual QString displayNameForId(const QString &id) const; + + virtual bool canCreate(ProjectExplorer::BuildStepList *parent, + const QString &id) const; + virtual ProjectExplorer::BuildStep * + create(ProjectExplorer::BuildStepList *parent, const QString &id); + + virtual bool canRestore(ProjectExplorer::BuildStepList *parent, + const QVariantMap &map) const; + virtual ProjectExplorer::BuildStep * + restore(ProjectExplorer::BuildStepList *parent, const QVariantMap &map); + + virtual bool canClone(ProjectExplorer::BuildStepList *parent, + ProjectExplorer::BuildStep *product) const; + virtual ProjectExplorer::BuildStep * + clone(ProjectExplorer::BuildStepList *parent, + ProjectExplorer::BuildStep *product); +}; + +} // namespace Internal +} // namespace Android + +#endif // ANDROIDPACKAGEINSTALLATIONFACTORY_H diff --git a/src/plugins/android/androidpackageinstallationstep.cpp b/src/plugins/android/androidpackageinstallationstep.cpp new file mode 100644 index 00000000000..d5b0cf19ee1 --- /dev/null +++ b/src/plugins/android/androidpackageinstallationstep.cpp @@ -0,0 +1,71 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2012 BogDan Vatra <[email protected]> +** +** Contact: Nokia Corporation ([email protected]) +** +** +** GNU Lesser General Public License Usage +** +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this file. +** Please review the following information to ensure the GNU Lesser General +** Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** Other Usage +** +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** If you have questions regarding the use of this file, please contact +** Nokia at [email protected]. +** +**************************************************************************/ + +#include "androidpackageinstallationstep.h" +#include "androidtarget.h" + +#include <QFileInfo> +#include <QDir> +#include <projectexplorer/buildsteplist.h> + +using namespace Android::Internal; + +const QLatin1String AndroidPackageInstallationStep::Id("Qt4ProjectManager.AndroidPackageInstallationStep"); + +AndroidPackageInstallationStep::AndroidPackageInstallationStep(ProjectExplorer::BuildStepList *bsl) : MakeStep(bsl, Id) +{ + setDefaultDisplayName(tr("Copy application data")); + setDisplayName(tr("Copy application data")); +} + +AndroidPackageInstallationStep::AndroidPackageInstallationStep(ProjectExplorer::BuildStepList *bc, AndroidPackageInstallationStep *other): MakeStep(bc, other) +{ + setDefaultDisplayName(tr("Copy application data")); + setDisplayName(tr("Copy application data")); +} + +AndroidPackageInstallationStep::~AndroidPackageInstallationStep() +{ +} + +bool AndroidPackageInstallationStep::init() +{ + AndroidTarget *androidTarget = qobject_cast<AndroidTarget *>(target()); + if (!androidTarget) { + emit addOutput(tr("Current target is not an android target"), BuildStep::MessageOutput); + return false; + } + + setUserArguments(QString::fromLatin1("INSTALL_ROOT=\"%1\" install").arg(QDir::toNativeSeparators(androidTarget->androidDirPath()))); + + return MakeStep::init(); +} diff --git a/src/plugins/android/androidpackageinstallationstep.h b/src/plugins/android/androidpackageinstallationstep.h new file mode 100644 index 00000000000..1fdf1486780 --- /dev/null +++ b/src/plugins/android/androidpackageinstallationstep.h @@ -0,0 +1,59 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2012 BogDan Vatra <[email protected]> +** +** Contact: Nokia Corporation ([email protected]) +** +** +** GNU Lesser General Public License Usage +** +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this file. +** Please review the following information to ensure the GNU Lesser General +** Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** Other Usage +** +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** If you have questions regarding the use of this file, please contact +** Nokia at [email protected]. +** +**************************************************************************/ + +#ifndef ANDROIDPACKAGEINSTALLATIONSTEP_H +#define ANDROIDPACKAGEINSTALLATIONSTEP_H + +#include <qt4projectmanager/makestep.h> + +namespace Android { +namespace Internal { + +class AndroidPackageInstallationStep : public Qt4ProjectManager::MakeStep +{ + friend class AndroidPackageInstallationFactory; +public: + explicit AndroidPackageInstallationStep(ProjectExplorer::BuildStepList *bsl); + virtual ~AndroidPackageInstallationStep(); + virtual bool init(); +private: + AndroidPackageInstallationStep(ProjectExplorer::BuildStepList *bc, + AndroidPackageInstallationStep *other); + +private: + static const QLatin1String Id; +}; + +} // namespace Internal +} // namespace Android + +#endif // ANDROIDPACKAGEINSTALLATIONSTEP_H diff --git a/src/plugins/android/androidplugin.cpp b/src/plugins/android/androidplugin.cpp new file mode 100644 index 00000000000..6f27c8c8cff --- /dev/null +++ b/src/plugins/android/androidplugin.cpp @@ -0,0 +1,86 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2012 BogDan Vatra <[email protected]> +** +** Contact: Nokia Corporation ([email protected]) +** +** +** GNU Lesser General Public License Usage +** +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this file. +** Please review the following information to ensure the GNU Lesser General +** Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** Other Usage +** +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** If you have questions regarding the use of this file, please contact +** Nokia at [email protected]. +** +**************************************************************************/ + +#include "androidplugin.h" + +#include "androidconstants.h" +#include "androidconfigurations.h" +#include "androiddeploystepfactory.h" +#include "androidconfigurations.h" +#include "androidpackagecreationfactory.h" +#include "androidpackageinstallationfactory.h" +#include "androidrunfactories.h" +#include "androidsettingspage.h" +#include "androidtoolchain.h" +#include "androidqtversionfactory.h" +#include "androidtargetfactory.h" +#include "androiddeployconfiguration.h" + +#include <QtPlugin> + +using namespace Android; +using namespace Android::Internal; + +AndroidPlugin::AndroidPlugin() +{ +} + +AndroidPlugin::~AndroidPlugin() +{ +} + +bool AndroidPlugin::initialize(const QStringList &arguments, + QString *error_message) +{ + Q_UNUSED(arguments) + Q_UNUSED(error_message) + + AndroidConfigurations::instance(this); + + addAutoReleasedObject(new AndroidRunControlFactory); + addAutoReleasedObject(new AndroidRunConfigurationFactory); + addAutoReleasedObject(new AndroidPackageInstallationFactory); + addAutoReleasedObject(new AndroidPackageCreationFactory); + addAutoReleasedObject(new AndroidDeployStepFactory); + addAutoReleasedObject(new AndroidSettingsPage); + addAutoReleasedObject(new AndroidTargetFactory); + addAutoReleasedObject(new AndroidQtVersionFactory); + addAutoReleasedObject(new AndroidToolChainFactory); + addAutoReleasedObject(new AndroidDeployConfigurationFactory); + return true; +} + +void AndroidPlugin::extensionsInitialized() +{ +} + +Q_EXPORT_PLUGIN(Android::AndroidPlugin) diff --git a/src/plugins/android/androidplugin.h b/src/plugins/android/androidplugin.h new file mode 100644 index 00000000000..c8ad7c4a9cf --- /dev/null +++ b/src/plugins/android/androidplugin.h @@ -0,0 +1,54 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2012 BogDan Vatra <[email protected]> +** +** Contact: Nokia Corporation ([email protected]) +** +** +** GNU Lesser General Public License Usage +** +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this file. +** Please review the following information to ensure the GNU Lesser General +** Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** Other Usage +** +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** If you have questions regarding the use of this file, please contact +** Nokia at [email protected]. +** +**************************************************************************/ + +#ifndef ANDROIDMANAGER_H +#define ANDROIDMANAGER_H + +#include <extensionsystem/iplugin.h> + +namespace Android { + +class AndroidPlugin : public ExtensionSystem::IPlugin +{ + Q_OBJECT + +public: + AndroidPlugin(); + ~AndroidPlugin(); + + bool initialize(const QStringList &arguments, QString *error_message); + void extensionsInitialized(); + +}; +} // namespace Qt4ProjectManager + +#endif // ANDROIDMANAGER_H diff --git a/src/plugins/android/androidqtversion.cpp b/src/plugins/android/androidqtversion.cpp new file mode 100644 index 00000000000..aeae9afeee8 --- /dev/null +++ b/src/plugins/android/androidqtversion.cpp @@ -0,0 +1,123 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2012 BogDan Vatra <[email protected]> +** +** Contact: Nokia Corporation ([email protected]) +** +** +** GNU Lesser General Public License Usage +** +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this file. +** Please review the following information to ensure the GNU Lesser General +** Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** Other Usage +** +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** If you have questions regarding the use of this file, please contact +** Nokia at [email protected]. +** +**************************************************************************/ + +#include "androidqtversion.h" +#include "androidconstants.h" +#include "qt4projectmanager/qt4projectmanagerconstants.h" + +#include <qtsupport/qtsupportconstants.h> + +#include <QCoreApplication> + +using namespace Android::Internal; + +AndroidQtVersion::AndroidQtVersion() + : QtSupport::BaseQtVersion() +{ + +} + +AndroidQtVersion::AndroidQtVersion(const Utils::FileName &path, bool isAutodetected, const QString &autodetectionSource) + : QtSupport::BaseQtVersion(path, isAutodetected, autodetectionSource) +{ +} + +AndroidQtVersion::~AndroidQtVersion() +{ + +} + +AndroidQtVersion *AndroidQtVersion::clone() const +{ + return new AndroidQtVersion(*this); +} + +QString AndroidQtVersion::type() const +{ + return QLatin1String(Constants::ANDROIDQT); +} + +bool AndroidQtVersion::isValid() const +{ + if (!BaseQtVersion::isValid()) + return false; + if (qtAbis().isEmpty()) + return false; + return true; +} + +QString AndroidQtVersion::invalidReason() const +{ + QString tmp = BaseQtVersion::invalidReason(); + if (tmp.isEmpty() && qtAbis().isEmpty()) + return QCoreApplication::translate("QtVersion", "Failed to detect the ABI(s) used by the Qt version."); + return tmp; +} + +QList<ProjectExplorer::Abi> AndroidQtVersion::detectQtAbis() const +{ + return QList<ProjectExplorer::Abi>() << ProjectExplorer::Abi(ProjectExplorer::Abi::ArmArchitecture, ProjectExplorer::Abi::LinuxOS, + ProjectExplorer::Abi::AndroidLinuxFlavor, ProjectExplorer::Abi::ElfFormat, + 32); +} + +bool AndroidQtVersion::supportsTargetId(const QString &id) const +{ + return id == QLatin1String(Qt4ProjectManager::Constants::ANDROID_DEVICE_TARGET_ID); +} + +QSet<QString> AndroidQtVersion::supportedTargetIds() const +{ + return QSet<QString>() << QLatin1String(Qt4ProjectManager::Constants::ANDROID_DEVICE_TARGET_ID); +} + +QString AndroidQtVersion::description() const +{ + return QCoreApplication::translate("QtVersion", "Android", "Qt Version is meant for Android"); +} + +Core::FeatureSet AndroidQtVersion::availableFeatures() const +{ + Core::FeatureSet features = QtSupport::BaseQtVersion::availableFeatures(); + features |= Core::FeatureSet(QtSupport::Constants::FEATURE_MOBILE); + return features; +} + +QString AndroidQtVersion::platformName() const +{ + return QLatin1String(Constants::ANDROID_PLATFORM); +} + +QString AndroidQtVersion::platformDisplayName() const +{ + return QLatin1String(Constants::ANDROID_PLATFORM_TR); +} diff --git a/src/plugins/android/androidqtversion.h b/src/plugins/android/androidqtversion.h new file mode 100644 index 00000000000..b57432cb0bd --- /dev/null +++ b/src/plugins/android/androidqtversion.h @@ -0,0 +1,68 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2012 BogDan Vatra <[email protected]> +** +** Contact: Nokia Corporation ([email protected]) +** +** +** GNU Lesser General Public License Usage +** +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this file. +** Please review the following information to ensure the GNU Lesser General +** Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** Other Usage +** +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** If you have questions regarding the use of this file, please contact +** Nokia at [email protected]. +** +**************************************************************************/ + +#ifndef ANDROIDQTVERSION_H +#define ANDROIDQTVERSION_H +#include <qtsupport/baseqtversion.h> + +namespace Android { +namespace Internal { + +class AndroidQtVersion : public QtSupport::BaseQtVersion +{ +public: + AndroidQtVersion(); + AndroidQtVersion(const Utils::FileName &path, bool isAutodetected = false, const QString &autodetectionSource = QString()); + ~AndroidQtVersion (); + AndroidQtVersion *clone() const; + + virtual QString type() const; + + virtual bool isValid() const; + virtual QString invalidReason() const; + + virtual QList<ProjectExplorer::Abi> detectQtAbis() const; + + virtual bool supportsTargetId(const QString &id) const; + virtual QSet<QString> supportedTargetIds() const; + + virtual Core::FeatureSet availableFeatures() const; + virtual QString platformName() const; + virtual QString platformDisplayName() const; + + QString description() const; +}; + +} // namespace Internal +} // namespace Android + +#endif // ANDROIDQTVERSION_H diff --git a/src/plugins/android/androidqtversionfactory.cpp b/src/plugins/android/androidqtversionfactory.cpp new file mode 100644 index 00000000000..2f133c03041 --- /dev/null +++ b/src/plugins/android/androidqtversionfactory.cpp @@ -0,0 +1,86 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2012 BogDan Vatra <[email protected]> +** +** Contact: Nokia Corporation ([email protected]) +** +** +** GNU Lesser General Public License Usage +** +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this file. +** Please review the following information to ensure the GNU Lesser General +** Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** Other Usage +** +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** If you have questions regarding the use of this file, please contact +** Nokia at [email protected]. +** +**************************************************************************/ + +#include "androidqtversionfactory.h" +#include "androidqtversion.h" +#include "androidconstants.h" +#include <qtsupport/qtsupportconstants.h> +#include <utils/qtcassert.h> +#include <proparser/profileevaluator.h> + +#include <QFileInfo> + +namespace Android { +namespace Internal { + +AndroidQtVersionFactory::AndroidQtVersionFactory(QObject *parent) + : QtSupport::QtVersionFactory(parent) +{ + +} + +AndroidQtVersionFactory::~AndroidQtVersionFactory() +{ + +} + +bool AndroidQtVersionFactory::canRestore(const QString &type) +{ + return type == QLatin1String(Constants::ANDROIDQT); +} + +QtSupport::BaseQtVersion *AndroidQtVersionFactory::restore(const QString &type, + const QVariantMap &data) +{ + QTC_ASSERT(canRestore(type), return 0); + AndroidQtVersion *v = new AndroidQtVersion; + v->fromMap(data); + return v; +} + +int AndroidQtVersionFactory::priority() const +{ + return 90; +} + +QtSupport::BaseQtVersion *AndroidQtVersionFactory::create(const Utils::FileName &qmakePath, ProFileEvaluator *evaluator, bool isAutoDetected, const QString &autoDetectionSource) +{ + QFileInfo fi(qmakePath.toString()); + if (!fi.exists() || !fi.isExecutable() || !fi.isFile()) + return 0; + if (!evaluator->values(QLatin1String("CONFIG")).contains(QLatin1String("android"))) + return 0; + return new AndroidQtVersion(qmakePath, isAutoDetected, autoDetectionSource); +} + +} // Internal +} // Android diff --git a/src/plugins/android/androidqtversionfactory.h b/src/plugins/android/androidqtversionfactory.h new file mode 100644 index 00000000000..b1aa94b1e9f --- /dev/null +++ b/src/plugins/android/androidqtversionfactory.h @@ -0,0 +1,57 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2012 BogDan Vatra <[email protected]> +** +** Contact: Nokia Corporation ([email protected]) +** +** +** GNU Lesser General Public License Usage +** +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this file. +** Please review the following information to ensure the GNU Lesser General +** Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** Other Usage +** +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** If you have questions regarding the use of this file, please contact +** Nokia at [email protected]. +** +**************************************************************************/ + +#ifndef ANDROIDQTVERSIONFACTORY_H +#define ANDROIDQTVERSIONFACTORY_H +#include <qtsupport/qtversionfactory.h> + +namespace Android { +namespace Internal { + +class AndroidQtVersionFactory : public QtSupport::QtVersionFactory +{ +public: + explicit AndroidQtVersionFactory(QObject *parent = 0); + ~AndroidQtVersionFactory(); + + virtual bool canRestore(const QString &type); + virtual QtSupport::BaseQtVersion *restore(const QString &type, const QVariantMap &data); + + virtual int priority() const; + virtual QtSupport::BaseQtVersion *create(const Utils::FileName &qmakePath, ProFileEvaluator *evaluator, + bool isAutoDetected = false, const QString &autoDetectionSource = QString()); +}; + +} // namespace Internal +} // namespace Android + +#endif // ANDROIDQTVERSIONFACTORY_H diff --git a/src/plugins/android/androidrunconfiguration.cpp b/src/plugins/android/androidrunconfiguration.cpp new file mode 100644 index 00000000000..370725381ba --- /dev/null +++ b/src/plugins/android/androidrunconfiguration.cpp @@ -0,0 +1,147 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2012 BogDan Vatra <[email protected]> +** +** Contact: Nokia Corporation ([email protected]) +** +** +** GNU Lesser General Public License Usage +** +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this file. +** Please review the following information to ensure the GNU Lesser General +** Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** Other Usage +** +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** If you have questions regarding the use of this file, please contact +** Nokia at [email protected]. +** +**************************************************************************/ + +#include "androidrunconfiguration.h" +#include "androiddeploystep.h" +#include "androidglobal.h" +#include "androidtoolchain.h" +#include "androidtarget.h" + +#include <qt4projectmanager/qt4buildconfiguration.h> +#include <qt4projectmanager/qt4project.h> + +#include <utils/qtcassert.h> + +#include <qtsupport/qtoutputformatter.h> + +using namespace Qt4ProjectManager; + +namespace Android { +namespace Internal { + +using namespace ProjectExplorer; + +AndroidRunConfiguration::AndroidRunConfiguration(AndroidTarget *parent, + const QString &proFilePath) + : RunConfiguration(parent, QLatin1String(ANDROID_RC_ID)) + , m_proFilePath(proFilePath) +{ + init(); +} + +AndroidRunConfiguration::AndroidRunConfiguration(AndroidTarget *parent, + AndroidRunConfiguration *source) + : RunConfiguration(parent, source) + , m_proFilePath(source->m_proFilePath) +{ + init(); +} + +void AndroidRunConfiguration::init() +{ + setDefaultDisplayName(defaultDisplayName()); +} + +AndroidRunConfiguration::~AndroidRunConfiguration() +{ +} + +AndroidTarget *AndroidRunConfiguration::androidTarget() const +{ + return static_cast<AndroidTarget *>(target()); +} + +Qt4BuildConfiguration *AndroidRunConfiguration::activeQt4BuildConfiguration() const +{ + return static_cast<Qt4BuildConfiguration *>(activeBuildConfiguration()); +} + +QWidget *AndroidRunConfiguration::createConfigurationWidget() +{ + return 0;// no special running configurations +} + +Utils::OutputFormatter *AndroidRunConfiguration::createOutputFormatter() const +{ + return new QtSupport::QtOutputFormatter(androidTarget()->qt4Project()); +} + +QString AndroidRunConfiguration::defaultDisplayName() +{ + return tr("Run on Android device"); +} + +AndroidConfig AndroidRunConfiguration::config() const +{ + return AndroidConfigurations::instance().config(); +} + +const QString AndroidRunConfiguration::gdbCmd() const +{ + return AndroidConfigurations::instance().gdbPath(activeQt4BuildConfiguration()->toolChain()->targetAbi().architecture()); +} + +AndroidDeployStep *AndroidRunConfiguration::deployStep() const +{ + AndroidDeployStep * const step + = AndroidGlobal::buildStep<AndroidDeployStep>(target()->activeDeployConfiguration()); + Q_ASSERT_X(step, Q_FUNC_INFO, + "Impossible: Android build configuration without deploy step."); + return step; +} + + +const QString AndroidRunConfiguration::remoteChannel() const +{ + return QLatin1String(":5039"); +} + +const QString AndroidRunConfiguration::dumperLib() const +{ + Qt4BuildConfiguration *qt4bc(activeQt4BuildConfiguration()); + return qt4bc->qtVersion()->gdbDebuggingHelperLibrary(); +} + +QString AndroidRunConfiguration::proFilePath() const +{ + return m_proFilePath; +} + +AndroidRunConfiguration::DebuggingType AndroidRunConfiguration::debuggingType() const +{ + return DebugCppAndQml; +} + + + +} // namespace Internal +} // namespace Android diff --git a/src/plugins/android/androidrunconfiguration.h b/src/plugins/android/androidrunconfiguration.h new file mode 100644 index 00000000000..1e34fac4577 --- /dev/null +++ b/src/plugins/android/androidrunconfiguration.h @@ -0,0 +1,108 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2012 BogDan Vatra <[email protected]> +** +** Contact: Nokia Corporation ([email protected]) +** +** +** GNU Lesser General Public License Usage +** +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this file. +** Please review the following information to ensure the GNU Lesser General +** Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** Other Usage +** +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** If you have questions regarding the use of this file, please contact +** Nokia at [email protected]. +** +**************************************************************************/ + +#ifndef ANDROIDRUNCONFIGURATION_H +#define ANDROIDRUNCONFIGURATION_H + +#include "androidconstants.h" +#include "androidconfigurations.h" + +#include <utils/environment.h> + +#include <projectexplorer/runconfiguration.h> + +QT_FORWARD_DECLARE_CLASS(QWidget) + +namespace Qt4ProjectManager { + +class Qt4BuildConfiguration; +class Qt4Project; +class Qt4ProFileNode; +} + +namespace Android { +namespace Internal { + +class AndroidDeviceConfigListModel; +class AndroidDeployStep; +class AndroidRunConfigurationFactory; +class AndroidToolChain; +class AndroidTarget; + +class AndroidRunConfiguration : public ProjectExplorer::RunConfiguration +{ + Q_OBJECT + friend class AndroidRunConfigurationFactory; + +public: + enum BaseEnvironmentBase { + CleanEnvironmentBase = 0, + SystemEnvironmentBase = 1 + }; + + enum DebuggingType { DebugCppOnly, DebugQmlOnly, DebugCppAndQml }; + + AndroidRunConfiguration(AndroidTarget *parent, const QString &proFilePath); + virtual ~AndroidRunConfiguration(); + + QWidget *createConfigurationWidget(); + Utils::OutputFormatter *createOutputFormatter() const; + AndroidTarget *androidTarget() const; + Qt4ProjectManager::Qt4BuildConfiguration *activeQt4BuildConfiguration() const; + + AndroidDeployStep *deployStep() const; + + void setArguments(const QString &args); + AndroidConfig config() const; + QString proFilePath() const; + + DebuggingType debuggingType() const; + + const QString gdbCmd() const; + const QString remoteChannel() const; + const QString dumperLib() const; + +protected: + AndroidRunConfiguration(AndroidTarget *parent, AndroidRunConfiguration *source); + QString defaultDisplayName(); + +private: + void init(); + + QString m_proFilePath; + +}; + +} // namespace Internal +} // namespace Android + +#endif // ANDROIDRUNCONFIGURATION_H diff --git a/src/plugins/android/androidruncontrol.cpp b/src/plugins/android/androidruncontrol.cpp new file mode 100644 index 00000000000..ad8dbe5d7bb --- /dev/null +++ b/src/plugins/android/androidruncontrol.cpp @@ -0,0 +1,119 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2012 BogDan Vatra <[email protected]> +** +** Contact: Nokia Corporation ([email protected]) +** +** +** GNU Lesser General Public License Usage +** +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this file. +** Please review the following information to ensure the GNU Lesser General +** Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** Other Usage +** +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** If you have questions regarding the use of this file, please contact +** Nokia at [email protected]. +** +**************************************************************************/ + +#include "androidruncontrol.h" + +#include "androiddeploystep.h" +#include "androidglobal.h" +#include "androidrunconfiguration.h" +#include "androidrunner.h" + +#include <projectexplorer/projectexplorerconstants.h> +#include <utils/qtcassert.h> + +#include <QIcon> + +namespace Android { +namespace Internal { + +using ProjectExplorer::RunConfiguration; +using namespace ProjectExplorer; + +AndroidRunControl::AndroidRunControl(AndroidRunConfiguration *rc) + : RunControl(rc, ProjectExplorer::NormalRunMode) + , m_runner(new AndroidRunner(this, rc, false)) + , m_running(false) +{ +} + +AndroidRunControl::~AndroidRunControl() +{ + stop(); +} + +void AndroidRunControl::start() +{ + m_running = true; + emit started(); + disconnect(m_runner, 0, this, 0); + + connect(m_runner, SIGNAL(remoteErrorOutput(QByteArray)), + SLOT(handleRemoteErrorOutput(QByteArray))); + connect(m_runner, SIGNAL(remoteOutput(QByteArray)), + SLOT(handleRemoteOutput(QByteArray))); + connect(m_runner, SIGNAL(remoteProcessFinished(const QString &)), + SLOT(handleRemoteProcessFinished(const QString &))); + appendMessage(tr("Starting remote process ..."), Utils::NormalMessageFormat); + m_runner->start(); +} + +ProjectExplorer::RunControl::StopResult AndroidRunControl::stop() +{ + m_runner->stop(); + return StoppedSynchronously; +} + +void AndroidRunControl::handleRemoteProcessFinished(const QString &error) +{ + appendMessage(error, Utils::ErrorMessageFormat); + disconnect(m_runner, 0, this, 0); + m_running = false; + emit finished(); +} + +void AndroidRunControl::handleRemoteOutput(const QByteArray &output) +{ + appendMessage(QString::fromUtf8(output), Utils::StdOutFormatSameLine); +} + +void AndroidRunControl::handleRemoteErrorOutput(const QByteArray &output) +{ + appendMessage(QString::fromUtf8(output), Utils::StdErrFormatSameLine); +} + +bool AndroidRunControl::isRunning() const +{ + return m_running; +} + +QString AndroidRunControl::displayName() const +{ + return m_runner->displayName(); +} + +QIcon AndroidRunControl::icon() const +{ + return QIcon(QLatin1String(ProjectExplorer::Constants::ICON_DEBUG_SMALL)); +} + +} // namespace Internal +} // namespace Qt4ProjectManager diff --git a/src/plugins/android/androidruncontrol.h b/src/plugins/android/androidruncontrol.h new file mode 100644 index 00000000000..7e5df82a9c7 --- /dev/null +++ b/src/plugins/android/androidruncontrol.h @@ -0,0 +1,72 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2012 BogDan Vatra <[email protected]> +** +** Contact: Nokia Corporation ([email protected]) +** +** +** GNU Lesser General Public License Usage +** +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this file. +** Please review the following information to ensure the GNU Lesser General +** Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** Other Usage +** +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** If you have questions regarding the use of this file, please contact +** Nokia at [email protected]. +** +**************************************************************************/ + +#ifndef ANDROIDRUNCONTROL_H +#define ANDROIDRUNCONTROL_H + +#include <projectexplorer/runconfiguration.h> + +#include <QString> + +namespace Android { +namespace Internal { +class AndroidRunConfiguration; +class AndroidRunner; + +class AndroidRunControl : public ProjectExplorer::RunControl +{ + Q_OBJECT +public: + explicit AndroidRunControl(AndroidRunConfiguration *runConfig); + virtual ~AndroidRunControl(); + + virtual void start(); + virtual StopResult stop(); + virtual bool isRunning() const; + virtual QString displayName() const; + virtual QIcon icon() const; + +private slots: + void handleRemoteProcessFinished(const QString &error); + void handleRemoteOutput(const QByteArray &output); + void handleRemoteErrorOutput(const QByteArray &output); + +private: + + AndroidRunner * const m_runner; + bool m_running; +}; + +} // namespace Internal +} // namespace Android + +#endif // ANDROIDRUNCONTROL_H diff --git a/src/plugins/android/androidrunfactories.cpp b/src/plugins/android/androidrunfactories.cpp new file mode 100644 index 00000000000..253e6559b88 --- /dev/null +++ b/src/plugins/android/androidrunfactories.cpp @@ -0,0 +1,198 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2012 BogDan Vatra <[email protected]> +** +** Contact: Nokia Corporation ([email protected]) +** +** +** GNU Lesser General Public License Usage +** +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this file. +** Please review the following information to ensure the GNU Lesser General +** Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** Other Usage +** +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** If you have questions regarding the use of this file, please contact +** Nokia at [email protected]. +** +**************************************************************************/ + +#include "androidrunfactories.h" + +#include "androidconstants.h" +#include "androiddebugsupport.h" +#include "androidrunconfiguration.h" +#include "androidruncontrol.h" +#include "androidtarget.h" + +#include <projectexplorer/projectexplorerconstants.h> +#include <debugger/debuggerconstants.h> +#include <qt4projectmanager/qt4project.h> +#include <qt4projectmanager/qt4nodes.h> +#include <qt4projectmanager/qt4projectmanagerconstants.h> + + +namespace Android { +namespace Internal { + +using namespace ProjectExplorer; +using namespace Qt4ProjectManager; + +namespace { + +QString pathFromId(const QString &id) +{ + if (!id.startsWith(ANDROID_RC_ID_PREFIX)) + return QString(); + return id.mid(QString(ANDROID_RC_ID_PREFIX).size()); +} + +} // namespace + +AndroidRunConfigurationFactory::AndroidRunConfigurationFactory(QObject *parent) + : IRunConfigurationFactory(parent) +{ +} + +AndroidRunConfigurationFactory::~AndroidRunConfigurationFactory() +{ +} + +bool AndroidRunConfigurationFactory::canCreate(Target *parent, + const QString &/*id*/) const +{ + AndroidTarget *target = qobject_cast<AndroidTarget *>(parent); + if (!target + || target->id() != QLatin1String(Qt4ProjectManager::Constants::ANDROID_DEVICE_TARGET_ID)) { + return false; + } + return true; +} + +bool AndroidRunConfigurationFactory::canRestore(Target *parent, + const QVariantMap &map) const +{ + Q_UNUSED(parent) + Q_UNUSED(map) + if (!qobject_cast<AndroidTarget *>(parent)) + return false; + return ProjectExplorer::idFromMap(map) + .startsWith(QLatin1String(ANDROID_RC_ID)); +} + +bool AndroidRunConfigurationFactory::canClone(Target *parent, + RunConfiguration *source) const +{ + return canCreate(parent, source->id()); +} + +QStringList AndroidRunConfigurationFactory::availableCreationIds(Target *parent) const +{ + QStringList ids; + if (AndroidTarget *t = qobject_cast<AndroidTarget *>(parent)) { + if (t->id() == QLatin1String(Qt4ProjectManager::Constants::ANDROID_DEVICE_TARGET_ID)) { + QList<Qt4ProFileNode *> nodes = t->qt4Project()->allProFiles(); + foreach (Qt4ProFileNode *node, nodes) + if (node->projectType() == ApplicationTemplate || node->projectType() == LibraryTemplate) + ids << node->targetInformation().target; + } + } + return ids; +} + +QString AndroidRunConfigurationFactory::displayNameForId(const QString &id) const +{ + return QFileInfo(pathFromId(id)).completeBaseName(); +} + +RunConfiguration *AndroidRunConfigurationFactory::create(Target *parent, + const QString &id) +{ + if (!canCreate(parent, id)) + return 0; + AndroidTarget *pqt4parent = static_cast<AndroidTarget *>(parent); + return new AndroidRunConfiguration(pqt4parent, pathFromId(id)); + +} + +RunConfiguration *AndroidRunConfigurationFactory::restore(Target *parent, + const QVariantMap &map) +{ + if (!canRestore(parent, map)) + return 0; + AndroidTarget *target = static_cast<AndroidTarget *>(parent); + AndroidRunConfiguration *rc = new AndroidRunConfiguration(target, QString()); + if (rc->fromMap(map)) + return rc; + + delete rc; + return 0; +} + +RunConfiguration *AndroidRunConfigurationFactory::clone(Target *parent, + RunConfiguration *source) +{ + if (!canClone(parent, source)) + return 0; + + AndroidRunConfiguration *old = static_cast<AndroidRunConfiguration *>(source); + return new AndroidRunConfiguration(static_cast<AndroidTarget *>(parent), old); +} + +// #pragma mark -- AndroidRunControlFactory + +AndroidRunControlFactory::AndroidRunControlFactory(QObject *parent) + : IRunControlFactory(parent) +{ +} + +AndroidRunControlFactory::~AndroidRunControlFactory() +{ +} + +bool AndroidRunControlFactory::canRun(RunConfiguration *runConfiguration, + ProjectExplorer::RunMode mode) const +{ + if (mode != NormalRunMode && mode != DebugRunMode) + return false; + return qobject_cast<AndroidRunConfiguration *>(runConfiguration); +} + +RunControl *AndroidRunControlFactory::create(RunConfiguration *runConfig, + ProjectExplorer::RunMode mode) +{ + Q_ASSERT(canRun(runConfig, mode)); + AndroidRunConfiguration *rc = qobject_cast<AndroidRunConfiguration *>(runConfig); + Q_ASSERT(rc); + if (mode == NormalRunMode) + return new AndroidRunControl(rc); + else + return AndroidDebugSupport::createDebugRunControl(rc); +} + +QString AndroidRunControlFactory::displayName() const +{ + return tr("Run on Android device/emulator"); +} + +RunConfigWidget *AndroidRunControlFactory::createConfigurationWidget(RunConfiguration *config) +{ + Q_UNUSED(config) + return 0; +} + +} // namespace Internal +} // namespace Qt4ProjectManager diff --git a/src/plugins/android/androidrunfactories.h b/src/plugins/android/androidrunfactories.h new file mode 100644 index 00000000000..d9d0f1c6b42 --- /dev/null +++ b/src/plugins/android/androidrunfactories.h @@ -0,0 +1,94 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2012 BogDan Vatra <[email protected]> +** +** Contact: Nokia Corporation ([email protected]) +** +** +** GNU Lesser General Public License Usage +** +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this file. +** Please review the following information to ensure the GNU Lesser General +** Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** Other Usage +** +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** If you have questions regarding the use of this file, please contact +** Nokia at [email protected]. +** +**************************************************************************/ + +#ifndef ANDROIDRUNFACTORIES_H +#define ANDROIDRUNFACTORIES_H + +#include <projectexplorer/runconfiguration.h> + +namespace ProjectExplorer { + class RunConfiguration; + class RunControl; + class RunConfigWidget; + class Target; +} +using ProjectExplorer::IRunConfigurationFactory; +using ProjectExplorer::IRunControlFactory; +using ProjectExplorer::RunConfiguration; +using ProjectExplorer::RunControl; +using ProjectExplorer::RunConfigWidget; +using ProjectExplorer::Target; + +namespace Android { +namespace Internal { + +class AndroidRunConfigurationFactory : public IRunConfigurationFactory +{ + Q_OBJECT + +public: + explicit AndroidRunConfigurationFactory(QObject *parent = 0); + ~AndroidRunConfigurationFactory(); + + QString displayNameForId(const QString &id) const; + QStringList availableCreationIds(Target *parent) const; + + bool canCreate(Target *parent, const QString &id) const; + RunConfiguration *create(Target *parent, const QString &id); + + bool canRestore(Target *parent, const QVariantMap &map) const; + RunConfiguration *restore(Target *parent, const QVariantMap &map); + + bool canClone(Target *parent, RunConfiguration *source) const; + RunConfiguration *clone(Target *parent, RunConfiguration *source); +}; + +class AndroidRunControlFactory : public IRunControlFactory +{ + Q_OBJECT +public: + explicit AndroidRunControlFactory(QObject *parent = 0); + ~AndroidRunControlFactory(); + + QString displayName() const; + RunConfigWidget *createConfigurationWidget(RunConfiguration *runConfiguration); + + bool canRun(RunConfiguration *runConfiguration, + ProjectExplorer::RunMode mode) const; + RunControl *create(RunConfiguration *runConfiguration, + ProjectExplorer::RunMode mode); +}; + +} // namespace Internal +} // namespace Android + +#endif // ANDROIDRUNFACTORIES_H diff --git a/src/plugins/android/androidrunner.cpp b/src/plugins/android/androidrunner.cpp new file mode 100644 index 00000000000..4b30bee1783 --- /dev/null +++ b/src/plugins/android/androidrunner.cpp @@ -0,0 +1,281 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2012 BogDan Vatra <[email protected]> +** +** Contact: Nokia Corporation ([email protected]) +** +** +** GNU Lesser General Public License Usage +** +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this file. +** Please review the following information to ensure the GNU Lesser General +** Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** Other Usage +** +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** If you have questions regarding the use of this file, please contact +** Nokia at [email protected]. +** +**************************************************************************/ + +#include "androidrunner.h" + +#include "androiddeploystep.h" +#include "androidconfigurations.h" +#include "androidglobal.h" +#include "androidrunconfiguration.h" +#include "androidtarget.h" + +#include <QTime> +#include <QtConcurrentRun> + +namespace Android { +namespace Internal { + +AndroidRunner::AndroidRunner(QObject *parent, + AndroidRunConfiguration *runConfig, bool debugging) + : QThread(parent) +{ + m_remoteChannel = runConfig->remoteChannel(); + AndroidTarget * at = runConfig->androidTarget(); + AndroidDeployStep * ds = runConfig->deployStep(); + if ((m_useLocalQtLibs = ds->useLocalQtLibs())) { + m_localLibs = at->loadLocalLibs(ds->deviceAPILevel()); + m_localJars = at->loadLocalJars(ds->deviceAPILevel()); + } + m_intentName = at->intentName(); + m_debugingMode = debugging; + m_packageName = m_intentName.left(m_intentName.indexOf(QLatin1Char('/'))); + m_deviceSerialNumber = ds->deviceSerialNumber(); + m_processPID = -1; + m_gdbserverPID = -1; + connect(&m_checkPIDTimer, SIGNAL(timeout()), SLOT(checkPID())); + connect(&m_adbLogcatProcess, SIGNAL(readyReadStandardOutput()), SLOT(logcatReadStandardOutput())); + connect(&m_adbLogcatProcess, SIGNAL(readyReadStandardError()) , SLOT(logcatReadStandardError())); +} + +AndroidRunner::~AndroidRunner() +{ + stop(); +} + +void AndroidRunner::checkPID() +{ + QProcess psProc; + psProc.start(AndroidConfigurations::instance().adbToolPath(), + QStringList() << QLatin1String("-s") << m_deviceSerialNumber + << QLatin1String("shell") << QLatin1String("ps")); + if (!psProc.waitForFinished(-1)) { + psProc.terminate(); + return; + } + qint64 pid = -1; + QList<QByteArray> procs = psProc.readAll().split('\n'); + foreach (const QByteArray &proc, procs) { + if (proc.trimmed().endsWith(m_packageName.toAscii())) { + QRegExp rx(QLatin1String("(\\d+)")); + if (rx.indexIn(QLatin1String(proc), proc.indexOf(' ')) > 0) { + pid = rx.cap(1).toLongLong(); + break; + } + } + } + + if (-1 != m_processPID && pid == -1) { + m_processPID = -1; + emit remoteProcessFinished(tr("\n\n'%1' died").arg(m_packageName)); + return; + } + m_processPID = pid; + if (!m_debugingMode) + return; + + m_gdbserverPID = -1; + foreach (const QByteArray &proc, procs) { + if (proc.trimmed().endsWith("gdbserver")) { + QRegExp rx(QLatin1String("(\\d+)")); + if (rx.indexIn(QLatin1String(proc), proc.indexOf(' ')) > 0) { + m_gdbserverPID = rx.cap(1).toLongLong(); + break; + } + } + } +} + +void AndroidRunner::killPID() +{ + checkPID(); //updates m_processPID and m_gdbserverPID + for (int tries = 0; tries < 10 && (m_processPID != -1 || m_gdbserverPID != -1); ++tries) { + if (m_processPID != -1) { + adbKill(m_processPID, m_deviceSerialNumber, 2000); + adbKill(m_processPID, m_deviceSerialNumber, 2000, m_packageName); + } + + if (m_gdbserverPID != -1) { + adbKill(m_gdbserverPID, m_deviceSerialNumber, 2000); + adbKill(m_gdbserverPID, m_deviceSerialNumber, 2000, m_packageName); + } + checkPID(); + } +} + +void AndroidRunner::start() +{ + QtConcurrent::run(this,&AndroidRunner::asyncStart); +} + +void AndroidRunner::asyncStart() +{ + QMutexLocker locker(&m_mutex); + m_processPID = -1; + killPID(); // kill any process with this name + QString extraParams; + QProcess adbStarProc; + if (m_debugingMode) { + QStringList arguments; + arguments << QLatin1String("-s") << m_deviceSerialNumber + << QLatin1String("forward") << QString::fromLatin1("tcp%1").arg(m_remoteChannel) + << QString::fromLatin1("localfilesystem:/data/data/%1/debug-socket").arg(m_packageName); + adbStarProc.start(AndroidConfigurations::instance().adbToolPath(), arguments); + if (!adbStarProc.waitForStarted()) { + emit remoteProcessFinished(tr("Failed to forward debugging ports. Reason: $1").arg(adbStarProc.errorString())); + return; + } + if (!adbStarProc.waitForFinished(-1)) { + emit remoteProcessFinished(tr("Failed to forward debugging ports")); + return; + } + extraParams = QLatin1String("-e native_debug true -e gdbserver_socket +debug-socket"); + } + + if (m_useLocalQtLibs) { + extraParams += QLatin1String(" -e use_local_qt_libs true"); + extraParams += QLatin1String(" -e libs_prefix /data/local/qt/"); + extraParams += QLatin1String(" -e load_local_libs ") + m_localLibs; + extraParams += QLatin1String(" -e load_local_jars ") + m_localJars; + } + + extraParams = extraParams.trimmed(); + QStringList arguments; + arguments << QLatin1String("-s") << m_deviceSerialNumber + << QLatin1String("shell") << QLatin1String("am") + << QLatin1String("start") << QLatin1String("-n") << m_intentName; + + if (extraParams.length()) + arguments << extraParams.split(QLatin1Char(' ')); + + adbStarProc.start(AndroidConfigurations::instance().adbToolPath(), arguments); + if (!adbStarProc.waitForStarted()) { + emit remoteProcessFinished(tr("Failed to start the activity. Reason: $1").arg(adbStarProc.errorString())); + return; + } + if (!adbStarProc.waitForFinished(-1)) { + adbStarProc.terminate(); + emit remoteProcessFinished(tr("Unable to start '%1'").arg(m_packageName)); + return; + } + QTime startTime = QTime::currentTime(); + while (m_processPID == -1 && startTime.secsTo(QTime::currentTime()) < 10) { // wait up to 10 seconds for application to start + checkPID(); + } + if (m_processPID == -1) { + emit remoteProcessFinished(tr("Can't find %1 process").arg(m_packageName)); + return; + } + + if (m_debugingMode) { + startTime = QTime::currentTime(); + while (m_gdbserverPID == -1 && startTime.secsTo(QTime::currentTime()) < 25) { // wait up to 25 seconds to connect + checkPID(); + } + msleep(200); // give gdbserver more time to start + } + + QMetaObject::invokeMethod(this, "startLogcat", Qt::QueuedConnection); +} + +void AndroidRunner::startLogcat() +{ + m_checkPIDTimer.start(1000); // check if the application is alive every 1 seconds + m_adbLogcatProcess.start(AndroidConfigurations::instance().adbToolPath(), + QStringList() << QLatin1String("-s") << m_deviceSerialNumber + << QLatin1String("logcat")); + emit remoteProcessStarted(5039); +} + +void AndroidRunner::stop() +{ + QMutexLocker locker(&m_mutex); + m_adbLogcatProcess.terminate(); + m_adbLogcatProcess.waitForFinished(-1); + m_checkPIDTimer.stop(); + if (m_processPID == -1) + return; // don't emit another signal + QtConcurrent::run(this, &AndroidRunner::asyncStop); +} +void AndroidRunner::asyncStop() +{ + killPID(); + emit remoteProcessFinished(tr("\n\n'%1' killed").arg(m_packageName)); +} + +void AndroidRunner::logcatReadStandardError() +{ + emit remoteErrorOutput(m_adbLogcatProcess.readAllStandardError()); +} + +void AndroidRunner::logcatReadStandardOutput() +{ + m_logcat += m_adbLogcatProcess.readAllStandardOutput(); + bool keepLastLine = m_logcat.endsWith('\n'); + QByteArray line; + QByteArray pid(QString::fromLatin1("%1):").arg(m_processPID).toAscii()); + foreach (line, m_logcat.split('\n')) { + if (!line.contains(pid)) + continue; + if (line.startsWith("E/")) + emit remoteErrorOutput(line); + else + emit remoteOutput(line); + + } + if (keepLastLine) + m_logcat = line; +} + +void AndroidRunner::adbKill(qint64 pid, const QString &device, int timeout, const QString &runAsPackageName) +{ + QProcess process; + QStringList arguments; + + arguments << QLatin1String("-s") << device; + arguments << QLatin1String("shell"); + if (runAsPackageName.size()) + arguments << QLatin1String("run-as") << runAsPackageName; + arguments << QLatin1String("kill") << QLatin1String("-9"); + arguments << QString::number(pid); + + process.start(AndroidConfigurations::instance().adbToolPath(), arguments); + if (!process.waitForFinished(timeout)) + process.terminate(); +} + +QString AndroidRunner::displayName() const +{ + return m_packageName; +} + +} // namespace Internal +} // namespace Qt4ProjectManager diff --git a/src/plugins/android/androidrunner.h b/src/plugins/android/androidrunner.h new file mode 100644 index 00000000000..0e2cd4b9f9f --- /dev/null +++ b/src/plugins/android/androidrunner.h @@ -0,0 +1,104 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2012 BogDan Vatra <[email protected]> +** +** Contact: Nokia Corporation ([email protected]) +** +** +** GNU Lesser General Public License Usage +** +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this file. +** Please review the following information to ensure the GNU Lesser General +** Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** Other Usage +** +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** If you have questions regarding the use of this file, please contact +** Nokia at [email protected]. +** +**************************************************************************/ + +#ifndef ANDROIDRUNNER_H +#define ANDROIDRUNNER_H + +#include "androidconfigurations.h" + +#include <QObject> +#include <QStringList> +#include <QTimer> +#include <QThread> +#include <QProcess> +#include <QMutex> + + +namespace Android { +namespace Internal { +class AndroidRunConfiguration; + +class AndroidRunner : public QThread +{ + Q_OBJECT + +public: + AndroidRunner(QObject *parent, AndroidRunConfiguration *runConfig, + bool debugging); + ~AndroidRunner(); + + QString displayName() const; + +public slots: + void start(); + void stop(); + +signals: + void remoteProcessStarted(int gdbServerPort = -1, int qmlPort = -1); + void remoteProcessFinished(const QString &errString = QString()); + + void remoteOutput(const QByteArray &output); + void remoteErrorOutput(const QByteArray &output); + +private slots: + void killPID(); + void checkPID(); + void logcatReadStandardError(); + void logcatReadStandardOutput(); + void startLogcat(); + void asyncStart(); + void asyncStop(); + +private: + void adbKill(qint64 pid, const QString &device, int timeout = 2000, const QString &runAsPackageName = QString()); + +private: + bool m_debugingMode; + QProcess m_adbLogcatProcess; + QByteArray m_logcat; + QString m_intentName; + QString m_packageName; + QString m_deviceSerialNumber; + qint64 m_processPID; + qint64 m_gdbserverPID; + QTimer m_checkPIDTimer; + QString m_remoteChannel; + bool m_useLocalQtLibs; + QString m_localLibs; + QString m_localJars; + QMutex m_mutex; +}; + +} // namespace Internal +} // namespace Android + +#endif // ANDROIDRUNNER_H diff --git a/src/plugins/android/androidsettingspage.cpp b/src/plugins/android/androidsettingspage.cpp new file mode 100644 index 00000000000..d65846f6a9d --- /dev/null +++ b/src/plugins/android/androidsettingspage.cpp @@ -0,0 +1,102 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2012 BogDan Vatra <[email protected]> +** +** Contact: Nokia Corporation ([email protected]) +** +** +** GNU Lesser General Public License Usage +** +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this file. +** Please review the following information to ensure the GNU Lesser General +** Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** Other Usage +** +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** If you have questions regarding the use of this file, please contact +** Nokia at [email protected]. +** +**************************************************************************/ + +#include "androidsettingspage.h" + +#include "androidsettingswidget.h" + +#include "androidconstants.h" + +#include <QCoreApplication> +#include <QIcon> + +namespace Android { +namespace Internal { + +AndroidSettingsPage::AndroidSettingsPage(QObject *parent) + : Core::IOptionsPage(parent) +{ +} + +AndroidSettingsPage::~AndroidSettingsPage() +{ +} + +QString AndroidSettingsPage::id() const +{ + return QLatin1String(Constants::ANDROID_SETTINGS_ID); +} + +QString AndroidSettingsPage::displayName() const +{ + return tr("Android Configurations"); +} + +QString AndroidSettingsPage::category() const +{ + return QLatin1String(Constants::ANDROID_SETTINGS_CATEGORY); +} + +QString AndroidSettingsPage::displayCategory() const +{ + return QCoreApplication::translate("Android", Constants::ANDROID_SETTINGS_TR_CATEGORY); +} + +QIcon AndroidSettingsPage::categoryIcon() const +{ + return QIcon(QLatin1String(Constants::ANDROID_SETTINGS_CATEGORY_ICON)); +} + +bool AndroidSettingsPage::matches(const QString &searchKeyWord) const +{ + return m_keywords.contains(searchKeyWord, Qt::CaseInsensitive); +} + +QWidget *AndroidSettingsPage::createPage(QWidget *parent) +{ + m_widget = new AndroidSettingsWidget(parent); + if (m_keywords.isEmpty()) + m_keywords = m_widget->searchKeywords(); + return m_widget; +} + +void AndroidSettingsPage::apply() +{ + m_widget->saveSettings(); +} + +void AndroidSettingsPage::finish() +{ +} + +} // namespace Internal +} // namespace Android diff --git a/src/plugins/android/androidsettingspage.h b/src/plugins/android/androidsettingspage.h new file mode 100644 index 00000000000..6586cd9d931 --- /dev/null +++ b/src/plugins/android/androidsettingspage.h @@ -0,0 +1,68 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2012 BogDan Vatra <[email protected]> +** +** Contact: Nokia Corporation ([email protected]) +** +** +** GNU Lesser General Public License Usage +** +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this file. +** Please review the following information to ensure the GNU Lesser General +** Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** Other Usage +** +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** If you have questions regarding the use of this file, please contact +** Nokia at [email protected]. +** +**************************************************************************/ + +#ifndef ANDROIDSETTINGSPAGE_H +#define ANDROIDSETTINGSPAGE_H + +#include <coreplugin/dialogs/ioptionspage.h> + +namespace Android { +namespace Internal { + +class AndroidSettingsWidget; + +class AndroidSettingsPage : public Core::IOptionsPage +{ + Q_OBJECT +public: + explicit AndroidSettingsPage(QObject *parent = 0); + ~AndroidSettingsPage(); + + virtual QString id() const; + virtual QString displayName() const; + virtual QString category() const; + virtual QString displayCategory() const; + virtual QIcon categoryIcon() const; + virtual bool matches(const QString &searchKeyWord) const; + virtual QWidget *createPage(QWidget *parent); + virtual void apply(); + virtual void finish(); + +private: + QString m_keywords; + AndroidSettingsWidget *m_widget; +}; + +} // namespace Internal +} // namespace Android + +#endif // ANDROIDSETTINGSPAGE_H diff --git a/src/plugins/android/androidsettingswidget.cpp b/src/plugins/android/androidsettingswidget.cpp new file mode 100644 index 00000000000..73e259f3749 --- /dev/null +++ b/src/plugins/android/androidsettingswidget.cpp @@ -0,0 +1,439 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2012 BogDan Vatra <[email protected]> +** +** Contact: Nokia Corporation ([email protected]) +** +** +** GNU Lesser General Public License Usage +** +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this file. +** Please review the following information to ensure the GNU Lesser General +** Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** Other Usage +** +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** If you have questions regarding the use of this file, please contact +** Nokia at [email protected]. +** +**************************************************************************/ + +#include "androidsettingswidget.h" + +#include "ui_androidsettingswidget.h" + +#include "androidconfigurations.h" + +#include "androidconstants.h" + +#include <QFile> +#include <QTextStream> +#include <QProcess> + +#include <QFileDialog> +#include <QMessageBox> +#include <QModelIndex> + +namespace Android { +namespace Internal { + +void AVDModel::setAvdList(QVector<AndroidDevice> list) +{ + m_list = list; + reset(); +} + +QString AVDModel::avdName(const QModelIndex &index) +{ + return m_list[index.row()].serialNumber; +} + +QVariant AVDModel::data(const QModelIndex &index, int role) const +{ + if (role != Qt::DisplayRole || !index.isValid()) + return QVariant(); + switch (index.column()) { + case 0: + return m_list[index.row()].serialNumber; + case 1: + return QString::fromLatin1("API %1").arg(m_list[index.row()].sdk); + case 2: + return m_list[index.row()].cpuABI; + } + return QVariant(); +} + +QVariant AVDModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + if (orientation == Qt::Horizontal && role == Qt::DisplayRole) { + switch (section) { + case 0: + return tr("AVD Name"); + case 1: + return tr("AVD Target"); + case 2: + return tr("CPU/ABI"); + } + } + return QAbstractItemModel::headerData(section, orientation, role ); +} + +int AVDModel::rowCount(const QModelIndex &/*parent*/) const +{ + return m_list.size(); +} + +int AVDModel::columnCount(const QModelIndex &/*parent*/) const +{ + return 3; +} + + +AndroidSettingsWidget::AndroidSettingsWidget(QWidget *parent) + : QWidget(parent), + m_ui(new Ui_AndroidSettingsWidget), + m_androidConfig(AndroidConfigurations::instance().config()), + m_saveSettingsRequested(false) +{ + initGui(); +} + +AndroidSettingsWidget::~AndroidSettingsWidget() +{ + if (m_saveSettingsRequested) + AndroidConfigurations::instance().setConfig(m_androidConfig); + delete m_ui; +} + +QString AndroidSettingsWidget::searchKeywords() const +{ + QString rc; + QTextStream(&rc) << m_ui->SDKLocationLabel->text() + << ' ' << m_ui->SDKLocationLineEdit->text() + << ' ' << m_ui->NDKLocationLabel->text() + << ' ' << m_ui->NDKLocationLineEdit->text() + << ' ' << m_ui->NDKToolchainVersionLabel->text() + << ' ' << m_ui->AntLocationLabel->text() + << ' ' << m_ui->AntLocationLineEdit->text() + << ' ' << m_ui->GdbLocationLabel->text() + << ' ' << m_ui->GdbLocationLineEdit->text() + << ' ' << m_ui->GdbserverLocationLabel->text() + << ' ' << m_ui->GdbserverLocationLineEdit->text() + << ' ' << m_ui->GdbLocationLabelx86->text() + << ' ' << m_ui->GdbLocationLineEditx86->text() + << ' ' << m_ui->GdbserverLocationLabelx86->text() + << ' ' << m_ui->GdbserverLocationLineEditx86->text() + << ' ' << m_ui->OpenJDKLocationLabel->text() + << ' ' << m_ui->OpenJDKLocationLineEdit->text() + << ' ' << m_ui->AVDManagerLabel->text() + << ' ' << m_ui->DataPartitionSizeLable->text() + << ' ' << m_ui->DataPartitionSizeSpinBox->text(); + rc.remove(QLatin1Char('&')); + return rc; +} + +void AndroidSettingsWidget::initGui() +{ + m_ui->setupUi(this); + m_ui->toolchainVersionComboBox->clear(); + if (checkSDK(m_androidConfig.sdkLocation)) + m_ui->SDKLocationLineEdit->setText(m_androidConfig.sdkLocation); + else + m_androidConfig.sdkLocation.clear(); + if (checkNDK(m_androidConfig.ndkLocation)) + m_ui->NDKLocationLineEdit->setText(m_androidConfig.ndkLocation); + else + m_androidConfig.ndkLocation.clear(); + m_ui->AntLocationLineEdit->setText(m_androidConfig.antLocation); + m_ui->GdbLocationLineEdit->setText(m_androidConfig.armGdbLocation); + m_ui->GdbserverLocationLineEdit->setText(m_androidConfig.armGdbserverLocation); + m_ui->GdbLocationLineEditx86->setText(m_androidConfig.x86GdbLocation); + m_ui->GdbserverLocationLineEditx86->setText(m_androidConfig.x86GdbserverLocation); + m_ui->OpenJDKLocationLineEdit->setText(m_androidConfig.openJDKLocation); + m_ui->DataPartitionSizeSpinBox->setValue(m_androidConfig.partitionSize); + m_ui->AVDTableView->setModel(&m_AVDModel); + m_AVDModel.setAvdList(AndroidConfigurations::instance().androidVirtualDevices()); + m_ui->AVDTableView->horizontalHeader()->setResizeMode(QHeaderView::Stretch); + m_ui->AVDTableView->horizontalHeader()->setResizeMode(1, QHeaderView::ResizeToContents); +} + +void AndroidSettingsWidget::saveSettings(bool saveNow) +{ + // We must defer this step because of a stupid bug on MacOS. See QTCREATORBUG-1675. + if (saveNow) { + AndroidConfigurations::instance().setConfig(m_androidConfig); + m_saveSettingsRequested = false; + } else { + m_saveSettingsRequested = true; + } +} + + +bool AndroidSettingsWidget::checkSDK(const QString &location) +{ + if (!location.length()) + return false; + if (!QFile::exists(location + QLatin1String("/platform-tools/adb" ANDROID_EXE_SUFFIX)) + || (!QFile::exists(location + QLatin1String("/tools/android" ANDROID_EXE_SUFFIX)) + && !QFile::exists(location + QLatin1String("/tools/android" ANDROID_BAT_SUFFIX))) + || !QFile::exists(location + QLatin1String("/tools/emulator" ANDROID_EXE_SUFFIX))) { + QMessageBox::critical(this, tr("Android SDK Folder"), tr("\"%1\" doesn't seem to be an Android SDK top folder").arg(location)); + return false; + } + return true; +} + +bool AndroidSettingsWidget::checkNDK(const QString &location) +{ + m_ui->toolchainVersionComboBox->setEnabled(false); + m_ui->GdbLocationLineEdit->setEnabled(false); + m_ui->GdbLocationPushButton->setEnabled(false); + m_ui->GdbserverLocationLineEdit->setEnabled(false); + m_ui->GdbserverLocationPushButton->setEnabled(false); + if (!location.length()) + return false; + if (!QFile::exists(location + QLatin1String("/platforms")) + || !QFile::exists(location + QLatin1String("/toolchains")) + || !QFile::exists(location + QLatin1String("/sources/cxx-stl"))) { + QMessageBox::critical(this, tr("Android SDK Folder"), tr("\"%1\" doesn't seem to be an Android NDK top folder").arg(location)); + return false; + } + m_ui->toolchainVersionComboBox->setEnabled(true); + m_ui->GdbLocationLineEdit->setEnabled(true); + m_ui->GdbLocationPushButton->setEnabled(true); + m_ui->GdbserverLocationLineEdit->setEnabled(true); + m_ui->GdbserverLocationPushButton->setEnabled(true); + fillToolchainVersions(); + return true; + +} + +void AndroidSettingsWidget::sdkLocationEditingFinished() +{ + QString location = m_ui->SDKLocationLineEdit->text(); + if (!checkSDK(location)) { + m_ui->AVDManagerFrame->setEnabled(false); + return; + } + m_androidConfig.sdkLocation = location; + saveSettings(true); + m_AVDModel.setAvdList(AndroidConfigurations::instance().androidVirtualDevices()); + m_ui->AVDManagerFrame->setEnabled(true); +} + +void AndroidSettingsWidget::ndkLocationEditingFinished() +{ + QString location = m_ui->NDKLocationLineEdit->text(); + if (!checkNDK(location)) + return; + m_androidConfig.ndkLocation = location; + saveSettings(true); +} + +void AndroidSettingsWidget::fillToolchainVersions() +{ + m_ui->toolchainVersionComboBox->clear(); + QStringList toolchainVersions = AndroidConfigurations::instance().ndkToolchainVersions(); + QString toolchain = m_androidConfig.ndkToolchainVersion; + foreach (const QString &item, toolchainVersions) + m_ui->toolchainVersionComboBox->addItem(item); + if (!toolchain.isEmpty()) + m_ui->toolchainVersionComboBox->setCurrentIndex(toolchainVersions.indexOf(toolchain)); + else + m_ui->toolchainVersionComboBox->setCurrentIndex(0); +} + +void AndroidSettingsWidget::toolchainVersionIndexChanged(QString version) +{ + m_androidConfig.ndkToolchainVersion = version; + saveSettings(true); +} + + +void AndroidSettingsWidget::antLocationEditingFinished() +{ + QString location = m_ui->AntLocationLineEdit->text(); + if (!location.length() || !QFile::exists(location)) + return; + m_androidConfig.antLocation = location; +} + +void AndroidSettingsWidget::gdbLocationEditingFinished() +{ + QString location = m_ui->GdbLocationLineEdit->text(); + if (!location.length() || !QFile::exists(location)) + return; + m_androidConfig.armGdbLocation = location; +} + +void AndroidSettingsWidget::gdbserverLocationEditingFinished() +{ + QString location = m_ui->GdbserverLocationLineEdit->text(); + if (!location.length() || !QFile::exists(location)) + return; + m_androidConfig.armGdbserverLocation = location; +} + +void AndroidSettingsWidget::gdbLocationX86EditingFinished() +{ + QString location = m_ui->GdbLocationLineEditx86->text(); + if (!location.length() || !QFile::exists(location)) + return; + m_androidConfig.x86GdbLocation = location; +} + +void AndroidSettingsWidget::gdbserverLocationX86EditingFinished() +{ + QString location = m_ui->GdbserverLocationLineEditx86->text(); + if (!location.length() || !QFile::exists(location)) + return; + m_androidConfig.x86GdbserverLocation = location; +} + +void AndroidSettingsWidget::openJDKLocationEditingFinished() +{ + QString location = m_ui->OpenJDKLocationLineEdit->text(); + if (!location.length() || !QFile::exists(location)) + return; + m_androidConfig.openJDKLocation = location; +} + +void AndroidSettingsWidget::browseSDKLocation() +{ + QString dir = QFileDialog::getExistingDirectory(this, tr("Select Android SDK folder")); + if (!checkSDK(dir)) + return; + m_ui->SDKLocationLineEdit->setText(dir); + sdkLocationEditingFinished(); +} + +void AndroidSettingsWidget::browseNDKLocation() +{ + QString dir = QFileDialog::getExistingDirectory(this, tr("Select Android NDK folder")); + if (!checkNDK(dir)) + return; + m_ui->NDKLocationLineEdit->setText(dir); + ndkLocationEditingFinished(); +} + +void AndroidSettingsWidget::browseAntLocation() +{ + QString dir = QDir::homePath(); +#if defined(Q_OS_LINUX) || defined(Q_OS_MAC) + dir = QLatin1String("/usr/bin/ant"); + QLatin1String antApp("ant"); +#elif defined(Q_OS_WIN) + QLatin1String antApp("ant.bat"); +#elif defined(Q_OS_DARWIN) + dir = QLatin1String("/opt/local/bin/ant"); + QLatin1String antApp("ant"); +#endif + QString file = QFileDialog::getOpenFileName(this, tr("Select ant script"),dir,antApp); + if (!file.length()) + return; + m_ui->AntLocationLineEdit->setText(file); + antLocationEditingFinished(); +} + +void AndroidSettingsWidget::browseGdbLocation() +{ + QString gdbPath = AndroidConfigurations::instance().gdbPath(ProjectExplorer::Abi::ArmArchitecture); + QString file = QFileDialog::getOpenFileName(this, tr("Select gdb executable"),gdbPath); + if (!file.length()) + return; + m_ui->GdbLocationLineEdit->setText(file); + gdbLocationEditingFinished(); +} + +void AndroidSettingsWidget::browseGdbserverLocation() +{ + QString gdbserverPath = AndroidConfigurations::instance().gdbServerPath(ProjectExplorer::Abi::ArmArchitecture); + QString file = QFileDialog::getOpenFileName(this, tr("Select gdbserver android executable"),gdbserverPath); + if (!file.length()) + return; + m_ui->GdbserverLocationLineEdit->setText(file); + gdbserverLocationEditingFinished(); +} + +void AndroidSettingsWidget::browseGdbLocationX86() +{ + QString gdbPath = AndroidConfigurations::instance().gdbPath(ProjectExplorer::Abi::X86Architecture); + QString file = QFileDialog::getOpenFileName(this, tr("Select gdb executable"),gdbPath); + if (!file.length()) + return; + m_ui->GdbLocationLineEditx86->setText(file); + gdbLocationX86EditingFinished(); +} + +void AndroidSettingsWidget::browseGdbserverLocationX86() +{ + QString gdbserverPath = AndroidConfigurations::instance().gdbServerPath(ProjectExplorer::Abi::X86Architecture); + QString file = QFileDialog::getOpenFileName(this, tr("Select gdbserver android executable"),gdbserverPath); + if (!file.length()) + return; + m_ui->GdbserverLocationLineEditx86->setText(file); + gdbserverLocationX86EditingFinished(); +} + +void AndroidSettingsWidget::browseOpenJDKLocation() +{ + QString openJDKPath = AndroidConfigurations::instance().openJDKPath(); + QString file = QFileDialog::getOpenFileName(this, tr("Select OpenJDK path"),openJDKPath); + if (!file.length()) + return; + m_ui->OpenJDKLocationLineEdit->setText(file); + openJDKLocationEditingFinished(); +} + +void AndroidSettingsWidget::addAVD() +{ + AndroidConfigurations::instance().createAVD(); + m_AVDModel.setAvdList(AndroidConfigurations::instance().androidVirtualDevices()); +} + +void AndroidSettingsWidget::removeAVD() +{ + AndroidConfigurations::instance().removeAVD(m_AVDModel.avdName(m_ui->AVDTableView->currentIndex())); + m_AVDModel.setAvdList(AndroidConfigurations::instance().androidVirtualDevices()); +} + +void AndroidSettingsWidget::startAVD() +{ + int tempApiLevel = -1; + AndroidConfigurations::instance().startAVD(&tempApiLevel, m_AVDModel.avdName(m_ui->AVDTableView->currentIndex())); +} + +void AndroidSettingsWidget::avdActivated(QModelIndex index) +{ + m_ui->AVDRemovePushButton->setEnabled(index.isValid()); + m_ui->AVDStartPushButton->setEnabled(index.isValid()); +} + +void AndroidSettingsWidget::dataPartitionSizeEditingFinished() +{ + m_androidConfig.partitionSize = m_ui->DataPartitionSizeSpinBox->value(); +} + +void AndroidSettingsWidget::manageAVD() +{ + QProcess *avdProcess = new QProcess(); + connect(this, SIGNAL(destroyed()), avdProcess, SLOT(deleteLater())); + connect(avdProcess, SIGNAL(finished(int)), avdProcess, SLOT(deleteLater())); + avdProcess->start(AndroidConfigurations::instance().androidToolPath(), QStringList() << QLatin1String("avd")); +} + + +} // namespace Internal +} // namespace Android diff --git a/src/plugins/android/androidsettingswidget.h b/src/plugins/android/androidsettingswidget.h new file mode 100644 index 00000000000..fa985a6cb74 --- /dev/null +++ b/src/plugins/android/androidsettingswidget.h @@ -0,0 +1,121 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2012 BogDan Vatra <[email protected]> +** +** Contact: Nokia Corporation ([email protected]) +** +** +** GNU Lesser General Public License Usage +** +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this file. +** Please review the following information to ensure the GNU Lesser General +** Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** Other Usage +** +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** If you have questions regarding the use of this file, please contact +** Nokia at [email protected]. +** +**************************************************************************/ + +#ifndef ANDROIDSETTINGSWIDGET_H +#define ANDROIDSETTINGSWIDGET_H + +#include "androidconfigurations.h" + +#include <QList> +#include <QSharedPointer> +#include <QString> +#include <QWidget> +#include <QAbstractTableModel> + +QT_BEGIN_NAMESPACE +class QLineEdit; +class QModelIndex; + +class Ui_AndroidSettingsWidget; +QT_END_NAMESPACE + +namespace Android { +namespace Internal { + +class AVDModel: public QAbstractTableModel +{ + Q_OBJECT +public: + void setAvdList(QVector<AndroidDevice> list); + QString avdName(const QModelIndex &index); + +protected: + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; + QVariant headerData ( int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; + int rowCount(const QModelIndex &parent = QModelIndex()) const; + int columnCount(const QModelIndex &parent = QModelIndex()) const; + +private: + QVector<AndroidDevice> m_list; +}; + +class AndroidSettingsWidget : public QWidget +{ + Q_OBJECT +public: + AndroidSettingsWidget(QWidget *parent); + ~AndroidSettingsWidget(); + + void saveSettings(bool saveNow = false); + QString searchKeywords() const; + +private slots: + void sdkLocationEditingFinished(); + void ndkLocationEditingFinished(); + void antLocationEditingFinished(); + void gdbLocationEditingFinished(); + void gdbserverLocationEditingFinished(); + void gdbLocationX86EditingFinished(); + void gdbserverLocationX86EditingFinished(); + void openJDKLocationEditingFinished(); + void browseSDKLocation(); + void browseNDKLocation(); + void browseAntLocation(); + void browseGdbLocation(); + void browseGdbserverLocation(); + void browseGdbLocationX86(); + void browseGdbserverLocationX86(); + void browseOpenJDKLocation(); + void toolchainVersionIndexChanged(QString); + void addAVD(); + void removeAVD(); + void startAVD(); + void avdActivated(QModelIndex); + void dataPartitionSizeEditingFinished(); + void manageAVD(); + +private: + void initGui(); + bool checkSDK(const QString &location); + bool checkNDK(const QString &location); + void fillToolchainVersions(); + + Ui_AndroidSettingsWidget *m_ui; + AndroidConfig m_androidConfig; + AVDModel m_AVDModel; + bool m_saveSettingsRequested; +}; + +} // namespace Internal +} // namespace Android + +#endif // ANDROIDSETTINGSWIDGET_H diff --git a/src/plugins/android/androidsettingswidget.ui b/src/plugins/android/androidsettingswidget.ui new file mode 100644 index 00000000000..1e7cba16af9 --- /dev/null +++ b/src/plugins/android/androidsettingswidget.ui @@ -0,0 +1,730 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>AndroidSettingsWidget</class> + <widget class="QWidget" name="AndroidSettingsWidget"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>679</width> + <height>441</height> + </rect> + </property> + <property name="windowTitle"> + <string>Android Configuration</string> + </property> + <layout class="QVBoxLayout" name="verticalLayout"> + <item> + <layout class="QGridLayout" name="gridLayout"> + <item row="0" column="0"> + <widget class="QLabel" name="SDKLocationLabel"> + <property name="text"> + <string>Android SDK location:</string> + </property> + <property name="alignment"> + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + </widget> + </item> + <item row="0" column="1"> + <widget class="QLineEdit" name="SDKLocationLineEdit"/> + </item> + <item row="0" column="2"> + <widget class="QPushButton" name="SDKLocationPushButton"> + <property name="text"> + <string>Browse</string> + </property> + </widget> + </item> + <item row="1" column="0"> + <widget class="QLabel" name="NDKLocationLabel"> + <property name="text"> + <string>Android NDK location:</string> + </property> + <property name="alignment"> + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + </widget> + </item> + <item row="1" column="1"> + <widget class="QLineEdit" name="NDKLocationLineEdit"/> + </item> + <item row="1" column="2"> + <widget class="QPushButton" name="NDKLocationPushButton"> + <property name="text"> + <string>Browse</string> + </property> + </widget> + </item> + <item row="2" column="0"> + <widget class="QLabel" name="NDKToolchainVersionLabel"> + <property name="text"> + <string>Android NDK toolchain version:</string> + </property> + <property name="alignment"> + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + </widget> + </item> + <item row="2" column="1"> + <widget class="QComboBox" name="toolchainVersionComboBox"/> + </item> + <item row="3" column="0"> + <widget class="QLabel" name="AntLocationLabel"> + <property name="text"> + <string>Ant location:</string> + </property> + <property name="alignment"> + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + </widget> + </item> + <item row="3" column="1"> + <widget class="QLineEdit" name="AntLocationLineEdit"/> + </item> + <item row="3" column="2"> + <widget class="QPushButton" name="AntLocationPushButton"> + <property name="text"> + <string>Browse</string> + </property> + </widget> + </item> + <item row="4" column="0"> + <widget class="QLabel" name="GdbLocationLabel"> + <property name="text"> + <string>arm GDB location:</string> + </property> + <property name="alignment"> + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + </widget> + </item> + <item row="4" column="1"> + <widget class="QLineEdit" name="GdbLocationLineEdit"/> + </item> + <item row="4" column="2"> + <widget class="QPushButton" name="GdbLocationPushButton"> + <property name="text"> + <string>Browse</string> + </property> + </widget> + </item> + <item row="5" column="0"> + <widget class="QLabel" name="GdbserverLocationLabel"> + <property name="text"> + <string>arm GDBserver location:</string> + </property> + <property name="alignment"> + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + </widget> + </item> + <item row="5" column="1"> + <widget class="QLineEdit" name="GdbserverLocationLineEdit"/> + </item> + <item row="5" column="2"> + <widget class="QPushButton" name="GdbserverLocationPushButton"> + <property name="text"> + <string>Browse</string> + </property> + </widget> + </item> + <item row="6" column="0"> + <widget class="QLabel" name="GdbLocationLabelx86"> + <property name="text"> + <string>x86 GDB location:</string> + </property> + <property name="alignment"> + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + </widget> + </item> + <item row="6" column="1"> + <widget class="QLineEdit" name="GdbLocationLineEditx86"/> + </item> + <item row="6" column="2"> + <widget class="QPushButton" name="GdbLocationPushButtonx86"> + <property name="text"> + <string>Browse</string> + </property> + </widget> + </item> + <item row="7" column="0"> + <widget class="QLabel" name="GdbserverLocationLabelx86"> + <property name="text"> + <string>x86 GDBserver location:</string> + </property> + <property name="alignment"> + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + </widget> + </item> + <item row="7" column="1"> + <widget class="QLineEdit" name="GdbserverLocationLineEditx86"/> + </item> + <item row="7" column="2"> + <widget class="QPushButton" name="GdbserverLocationPushButtonx86"> + <property name="text"> + <string>Browse</string> + </property> + </widget> + </item> + <item row="8" column="0"> + <widget class="QLabel" name="OpenJDKLocationLabel"> + <property name="text"> + <string>OpenJDK location:</string> + </property> + <property name="alignment"> + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + </widget> + </item> + <item row="8" column="1"> + <widget class="QLineEdit" name="OpenJDKLocationLineEdit"/> + </item> + <item row="8" column="2"> + <widget class="QPushButton" name="OpenJDKLocationPushButton"> + <property name="text"> + <string>Browse</string> + </property> + </widget> + </item> + </layout> + </item> + <item> + <widget class="QFrame" name="AVDManagerFrame"> + <property name="frameShape"> + <enum>QFrame::StyledPanel</enum> + </property> + <property name="frameShadow"> + <enum>QFrame::Raised</enum> + </property> + <layout class="QGridLayout" name="gridLayout_2"> + <item row="1" column="0" rowspan="4"> + <widget class="QTableView" name="AVDTableView"> + <property name="selectionMode"> + <enum>QAbstractItemView::SingleSelection</enum> + </property> + <property name="selectionBehavior"> + <enum>QAbstractItemView::SelectRows</enum> + </property> + <property name="textElideMode"> + <enum>Qt::ElideMiddle</enum> + </property> + <attribute name="verticalHeaderVisible"> + <bool>false</bool> + </attribute> + </widget> + </item> + <item row="3" column="1"> + <widget class="QPushButton" name="AVDStartPushButton"> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="text"> + <string>Start</string> + </property> + </widget> + </item> + <item row="4" column="1"> + <spacer name="verticalSpacer"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>129</height> + </size> + </property> + </spacer> + </item> + <item row="0" column="0" colspan="2"> + <layout class="QHBoxLayout" name="horizontalLayout"> + <item> + <widget class="QLabel" name="AVDManagerLabel"> + <property name="text"> + <string>AVD Manager</string> + </property> + </widget> + </item> + <item> + <spacer name="horizontalSpacer"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="QLabel" name="DataPartitionSizeLable"> + <property name="text"> + <string>System/data partition size:</string> + </property> + </widget> + </item> + <item> + <widget class="QSpinBox" name="DataPartitionSizeSpinBox"> + <property name="suffix"> + <string> Mb</string> + </property> + <property name="maximum"> + <number>99999</number> + </property> + <property name="value"> + <number>1024</number> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="manageAVDPushButton"> + <property name="text"> + <string>Start Android AVD Manager</string> + </property> + </widget> + </item> + </layout> + </item> + <item row="1" column="1"> + <widget class="QPushButton" name="AVDAddPushButton"> + <property name="text"> + <string>Add</string> + </property> + </widget> + </item> + <item row="2" column="1"> + <widget class="QPushButton" name="AVDRemovePushButton"> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="text"> + <string>Remove</string> + </property> + </widget> + </item> + </layout> + </widget> + </item> + </layout> + </widget> + <resources/> + <connections> + <connection> + <sender>NDKLocationLineEdit</sender> + <signal>editingFinished()</signal> + <receiver>AndroidSettingsWidget</receiver> + <slot>ndkLocationEditingFinished()</slot> + <hints> + <hint type="sourcelabel"> + <x>420</x> + <y>49</y> + </hint> + <hint type="destinationlabel"> + <x>352</x> + <y>210</y> + </hint> + </hints> + </connection> + <connection> + <sender>NDKLocationPushButton</sender> + <signal>clicked()</signal> + <receiver>AndroidSettingsWidget</receiver> + <slot>browseNDKLocation()</slot> + <hints> + <hint type="sourcelabel"> + <x>657</x> + <y>49</y> + </hint> + <hint type="destinationlabel"> + <x>352</x> + <y>210</y> + </hint> + </hints> + </connection> + <connection> + <sender>toolchainVersionComboBox</sender> + <signal>currentIndexChanged(QString)</signal> + <receiver>AndroidSettingsWidget</receiver> + <slot>toolchainVersionIndexChanged(QString)</slot> + <hints> + <hint type="sourcelabel"> + <x>420</x> + <y>81</y> + </hint> + <hint type="destinationlabel"> + <x>352</x> + <y>210</y> + </hint> + </hints> + </connection> + <connection> + <sender>SDKLocationLineEdit</sender> + <signal>editingFinished()</signal> + <receiver>AndroidSettingsWidget</receiver> + <slot>sdkLocationEditingFinished()</slot> + <hints> + <hint type="sourcelabel"> + <x>420</x> + <y>17</y> + </hint> + <hint type="destinationlabel"> + <x>352</x> + <y>210</y> + </hint> + </hints> + </connection> + <connection> + <sender>SDKLocationPushButton</sender> + <signal>clicked()</signal> + <receiver>AndroidSettingsWidget</receiver> + <slot>browseSDKLocation()</slot> + <hints> + <hint type="sourcelabel"> + <x>657</x> + <y>17</y> + </hint> + <hint type="destinationlabel"> + <x>352</x> + <y>210</y> + </hint> + </hints> + </connection> + <connection> + <sender>AntLocationLineEdit</sender> + <signal>editingFinished()</signal> + <receiver>AndroidSettingsWidget</receiver> + <slot>antLocationEditingFinished()</slot> + <hints> + <hint type="sourcelabel"> + <x>420</x> + <y>113</y> + </hint> + <hint type="destinationlabel"> + <x>352</x> + <y>210</y> + </hint> + </hints> + </connection> + <connection> + <sender>AntLocationPushButton</sender> + <signal>clicked()</signal> + <receiver>AndroidSettingsWidget</receiver> + <slot>browseAntLocation()</slot> + <hints> + <hint type="sourcelabel"> + <x>657</x> + <y>113</y> + </hint> + <hint type="destinationlabel"> + <x>352</x> + <y>210</y> + </hint> + </hints> + </connection> + <connection> + <sender>OpenJDKLocationLineEdit</sender> + <signal>editingFinished()</signal> + <receiver>AndroidSettingsWidget</receiver> + <slot>openJDKLocationEditingFinished()</slot> + <hints> + <hint type="sourcelabel"> + <x>420</x> + <y>49</y> + </hint> + <hint type="destinationlabel"> + <x>352</x> + <y>210</y> + </hint> + </hints> + </connection> + <connection> + <sender>OpenJDKLocationPushButton</sender> + <signal>clicked()</signal> + <receiver>AndroidSettingsWidget</receiver> + <slot>browseOpenJDKLocation()</slot> + <hints> + <hint type="sourcelabel"> + <x>657</x> + <y>49</y> + </hint> + <hint type="destinationlabel"> + <x>352</x> + <y>210</y> + </hint> + </hints> + </connection> + <connection> + <sender>AVDAddPushButton</sender> + <signal>clicked()</signal> + <receiver>AndroidSettingsWidget</receiver> + <slot>addAVD()</slot> + <hints> + <hint type="sourcelabel"> + <x>693</x> + <y>199</y> + </hint> + <hint type="destinationlabel"> + <x>352</x> + <y>186</y> + </hint> + </hints> + </connection> + <connection> + <sender>AVDRemovePushButton</sender> + <signal>clicked()</signal> + <receiver>AndroidSettingsWidget</receiver> + <slot>removeAVD()</slot> + <hints> + <hint type="sourcelabel"> + <x>693</x> + <y>230</y> + </hint> + <hint type="destinationlabel"> + <x>352</x> + <y>186</y> + </hint> + </hints> + </connection> + <connection> + <sender>AVDStartPushButton</sender> + <signal>clicked()</signal> + <receiver>AndroidSettingsWidget</receiver> + <slot>startAVD()</slot> + <hints> + <hint type="sourcelabel"> + <x>693</x> + <y>261</y> + </hint> + <hint type="destinationlabel"> + <x>352</x> + <y>186</y> + </hint> + </hints> + </connection> + <connection> + <sender>AVDTableView</sender> + <signal>activated(QModelIndex)</signal> + <receiver>AndroidSettingsWidget</receiver> + <slot>avdActivated(QModelIndex)</slot> + <hints> + <hint type="sourcelabel"> + <x>309</x> + <y>260</y> + </hint> + <hint type="destinationlabel"> + <x>352</x> + <y>186</y> + </hint> + </hints> + </connection> + <connection> + <sender>AVDTableView</sender> + <signal>clicked(QModelIndex)</signal> + <receiver>AndroidSettingsWidget</receiver> + <slot>avdActivated(QModelIndex)</slot> + <hints> + <hint type="sourcelabel"> + <x>309</x> + <y>260</y> + </hint> + <hint type="destinationlabel"> + <x>352</x> + <y>186</y> + </hint> + </hints> + </connection> + <connection> + <sender>DataPartitionSizeSpinBox</sender> + <signal>editingFinished()</signal> + <receiver>AndroidSettingsWidget</receiver> + <slot>dataPartitionSizeEditingFinished()</slot> + <hints> + <hint type="sourcelabel"> + <x>543</x> + <y>153</y> + </hint> + <hint type="destinationlabel"> + <x>352</x> + <y>186</y> + </hint> + </hints> + </connection> + <connection> + <sender>GdbLocationLineEdit</sender> + <signal>editingFinished()</signal> + <receiver>AndroidSettingsWidget</receiver> + <slot>gdbLocationEditingFinished()</slot> + <hints> + <hint type="sourcelabel"> + <x>409</x> + <y>132</y> + </hint> + <hint type="destinationlabel"> + <x>352</x> + <y>186</y> + </hint> + </hints> + </connection> + <connection> + <sender>GdbLocationPushButton</sender> + <signal>clicked()</signal> + <receiver>AndroidSettingsWidget</receiver> + <slot>browseGdbLocation()</slot> + <hints> + <hint type="sourcelabel"> + <x>662</x> + <y>131</y> + </hint> + <hint type="destinationlabel"> + <x>352</x> + <y>186</y> + </hint> + </hints> + </connection> + <connection> + <sender>GdbserverLocationLineEdit</sender> + <signal>editingFinished()</signal> + <receiver>AndroidSettingsWidget</receiver> + <slot>gdbserverLocationEditingFinished()</slot> + <hints> + <hint type="sourcelabel"> + <x>423</x> + <y>171</y> + </hint> + <hint type="destinationlabel"> + <x>352</x> + <y>222</y> + </hint> + </hints> + </connection> + <connection> + <sender>GdbserverLocationPushButton</sender> + <signal>clicked()</signal> + <receiver>AndroidSettingsWidget</receiver> + <slot>browseGdbserverLocation()</slot> + <hints> + <hint type="sourcelabel"> + <x>660</x> + <y>172</y> + </hint> + <hint type="destinationlabel"> + <x>352</x> + <y>222</y> + </hint> + </hints> + </connection> + <connection> + <sender>GdbLocationLineEditx86</sender> + <signal>editingFinished()</signal> + <receiver>AndroidSettingsWidget</receiver> + <slot>gdbLocationX86EditingFinished()</slot> + <hints> + <hint type="sourcelabel"> + <x>399</x> + <y>184</y> + </hint> + <hint type="destinationlabel"> + <x>338</x> + <y>248</y> + </hint> + </hints> + </connection> + <connection> + <sender>GdbLocationPushButtonx86</sender> + <signal>clicked()</signal> + <receiver>AndroidSettingsWidget</receiver> + <slot>browseGdbLocationX86()</slot> + <hints> + <hint type="sourcelabel"> + <x>637</x> + <y>184</y> + </hint> + <hint type="destinationlabel"> + <x>338</x> + <y>248</y> + </hint> + </hints> + </connection> + <connection> + <sender>GdbserverLocationLineEditx86</sender> + <signal>editingFinished()</signal> + <receiver>AndroidSettingsWidget</receiver> + <slot>gdbserverLocationX86EditingFinished()</slot> + <hints> + <hint type="sourcelabel"> + <x>399</x> + <y>212</y> + </hint> + <hint type="destinationlabel"> + <x>338</x> + <y>248</y> + </hint> + </hints> + </connection> + <connection> + <sender>GdbserverLocationPushButtonx86</sender> + <signal>clicked()</signal> + <receiver>AndroidSettingsWidget</receiver> + <slot>browseGdbserverLocationX86()</slot> + <hints> + <hint type="sourcelabel"> + <x>637</x> + <y>212</y> + </hint> + <hint type="destinationlabel"> + <x>338</x> + <y>248</y> + </hint> + </hints> + </connection> + <connection> + <sender>manageAVDPushButton</sender> + <signal>clicked()</signal> + <receiver>AndroidSettingsWidget</receiver> + <slot>manageAVD()</slot> + <hints> + <hint type="sourcelabel"> + <x>568</x> + <y>268</y> + </hint> + <hint type="destinationlabel"> + <x>339</x> + <y>220</y> + </hint> + </hints> + </connection> + </connections> + <slots> + <slot>sdkLocationEditingFinished()</slot> + <slot>ndkLocationEditingFinished()</slot> + <slot>browseSDKLocation()</slot> + <slot>browseNDKLocation()</slot> + <slot>antLocationEditingFinished()</slot> + <slot>browseAntLocation()</slot> + <slot>toolchainVersionIndexChanged(QString)</slot> + <slot>addAVD()</slot> + <slot>removeAVD()</slot> + <slot>startAVD()</slot> + <slot>avdActivated(QModelIndex)</slot> + <slot>dataPartitionSizeEditingFinished()</slot> + <slot>gdbLocationEditingFinished()</slot> + <slot>browseGdbLocation()</slot> + <slot>gdbserverLocationEditingFinished()</slot> + <slot>browseGdbserverLocation()</slot> + <slot>openJDKLocationEditingFinished()</slot> + <slot>browseOpenJDKLocation()</slot> + <slot>gdbLocationX86EditingFinished()</slot> + <slot>browseGdbLocationX86()</slot> + <slot>gdbserverLocationX86EditingFinished()</slot> + <slot>browseGdbserverLocationX86()</slot> + <slot>KeystoreLocationEditingFinished()</slot> + <slot>browseKeystoreLocation()</slot> + <slot>createKeystore()</slot> + <slot>manageAVD()</slot> + </slots> +</ui> diff --git a/src/plugins/android/androidtarget.cpp b/src/plugins/android/androidtarget.cpp new file mode 100644 index 00000000000..ed6a63cb50c --- /dev/null +++ b/src/plugins/android/androidtarget.cpp @@ -0,0 +1,999 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2012 BogDan Vatra <[email protected]> +** +** Contact: Nokia Corporation ([email protected]) +** +** +** GNU Lesser General Public License Usage +** +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this file. +** Please review the following information to ensure the GNU Lesser General +** Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** Other Usage +** +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** If you have questions regarding the use of this file, please contact +** Nokia at [email protected]. +** +**************************************************************************/ + +#include "androidtarget.h" +#include "androiddeployconfiguration.h" +#include "androidconfigurations.h" +#include "androidrunconfiguration.h" +#include "androiddeploystep.h" +#include "androidglobal.h" +#include "androidpackagecreationstep.h" + +#include <projectexplorer/customexecutablerunconfiguration.h> +#include <qt4projectmanager/qt4nodes.h> +#include <qt4projectmanager/qt4project.h> +#include <qt4projectmanager/qt4projectmanagerconstants.h> +#include <qt4projectmanager/qt4target.h> +#include <qt4projectmanager/qt4buildconfiguration.h> + +#include <QDir> +#include <QFileSystemWatcher> +#include <QList> +#include <QProcess> +#include <QMessageBox> +#include <QApplication> +#include <QDomDocument> + +using namespace ProjectExplorer; +using namespace Qt4ProjectManager; + +namespace { + const QLatin1String AndroidDirName("android"); + const QLatin1String AndroidManifestName("AndroidManifest.xml"); + const QLatin1String AndroidLibsFileName("/res/values/libs.xml"); + const QLatin1String AndroidStringsFileName("/res/values/strings.xml"); + const QLatin1String AndroidDefaultPropertiesName("project.properties"); + + QString cleanPackageName(QString packageName) + { + const QRegExp legalChars(QLatin1String("[a-zA-Z0-9_\\.]")); + + for (int i = 0; i < packageName.length(); ++i) + if (!legalChars.exactMatch(packageName.mid(i, 1))) + packageName[i] = QLatin1Char('_'); + + return packageName; + } +} // anonymous namespace + +namespace Android { +namespace Internal { + +AndroidTarget::AndroidTarget(Qt4Project *parent, const QString &id) : + Qt4BaseTarget(parent, id) + , m_androidFilesWatcher(new QFileSystemWatcher(this)) + , m_buildConfigurationFactory(new Qt4BuildConfigurationFactory(this)) +{ + setDisplayName(defaultDisplayName()); + setDefaultDisplayName(defaultDisplayName()); + setIcon(QIcon(QLatin1String(Constants::ANDROID_SETTINGS_CATEGORY_ICON))); + connect(parent, SIGNAL(activeTargetChanged(ProjectExplorer::Target*)), + this, SLOT(handleTargetChanged(ProjectExplorer::Target*))); +} + +AndroidTarget::~AndroidTarget() +{ + +} + +Qt4BuildConfigurationFactory *AndroidTarget::buildConfigurationFactory() const +{ + return m_buildConfigurationFactory; +} + +void AndroidTarget::createApplicationProFiles(bool reparse) +{ + if (!reparse) + removeUnconfiguredCustomExectutableRunConfigurations(); + + QList<Qt4ProFileNode *> profiles = qt4Project()->applicationProFiles(); + QSet<QString> paths; + foreach (Qt4ProFileNode *pro, profiles) + paths << pro->path(); + + foreach (ProjectExplorer::RunConfiguration *rc, runConfigurations()) + if (AndroidRunConfiguration *qt4rc = qobject_cast<AndroidRunConfiguration *>(rc)) + paths.remove(qt4rc->proFilePath()); + + // Only add new runconfigurations if there are none. + foreach (const QString &path, paths) + addRunConfiguration(new AndroidRunConfiguration(this, path)); + + // Oh still none? Add a custom executable runconfiguration + if (runConfigurations().isEmpty()) { + addRunConfiguration(new ProjectExplorer::CustomExecutableRunConfiguration(this)); + } +} + +QList<ProjectExplorer::RunConfiguration *> AndroidTarget::runConfigurationsForNode(ProjectExplorer::Node *n) +{ + QList<ProjectExplorer::RunConfiguration *> result; + foreach (ProjectExplorer::RunConfiguration *rc, runConfigurations()) + if (AndroidRunConfiguration *qt4c = qobject_cast<AndroidRunConfiguration *>(rc)) + if (qt4c->proFilePath() == n->path()) + result << rc; + return result; +} + +QString AndroidTarget::defaultDisplayName() +{ + return QApplication::translate("Qt4ProjectManager::Qt4Target", "Android", "Qt4 Android target display name"); +} + +void AndroidTarget::handleTargetChanged(ProjectExplorer::Target *target) +{ + if (target != this) + return; + + disconnect(project(), SIGNAL(addedTarget(ProjectExplorer::Target*)), + this, SLOT(handleTargetChanged(ProjectExplorer::Target*))); + connect(project(), SIGNAL(aboutToRemoveTarget(ProjectExplorer::Target*)), + SLOT(handleTargetToBeRemoved(ProjectExplorer::Target*))); + + if (!createAndroidTemplatesIfNecessary()) + return; + + m_androidFilesWatcher->addPath(androidDirPath()); + m_androidFilesWatcher->addPath(androidManifestPath()); + m_androidFilesWatcher->addPath(androidSrcPath()); + connect(m_androidFilesWatcher, SIGNAL(directoryChanged(QString)), this, + SIGNAL(androidDirContentsChanged())); + connect(m_androidFilesWatcher, SIGNAL(fileChanged(QString)), this, + SIGNAL(androidDirContentsChanged())); +} + +void AndroidTarget::handleTargetToBeRemoved(ProjectExplorer::Target *target) +{ + if (target != this) + return; + +// I don't think is a good idea to remove android directory + +// const QString debianPath = debianDirPath(); +// if (!QFileInfo(debianPath).exists()) +// return; +// const int answer = QMessageBox::warning(0, tr("Qt Creator"), +// tr("Do you want to remove the packaging directory\n" +// "associated with the target '%1'?").arg(displayName()), +// QMessageBox::Yes | QMessageBox::No, QMessageBox::No); +// if (answer == QMessageBox::No) +// return; +// QString error; +// if (!MaemoGlobal::removeRecursively(debianPath, error)) +// qDebug("%s", qPrintable(error)); +// const QString packagingPath = project()->projectDirectory() +// + QLatin1Char('/') + PackagingDirName; +// const QStringList otherContents = QDir(packagingPath).entryList(QDir::Dirs +// | QDir::Files | QDir::Hidden | QDir::NoDotAndDotDot); +// if (otherContents.isEmpty()) { +// if (!MaemoGlobal::removeRecursively(packagingPath, error)) +// qDebug("%s", qPrintable(error)); +// } +} + +QString AndroidTarget::androidDirPath() const +{ + return project()->projectDirectory() + QLatin1Char('/') + AndroidDirName; +} + +QString AndroidTarget::androidManifestPath() const +{ + return androidDirPath() + QLatin1Char('/') + AndroidManifestName; +} + +QString AndroidTarget::androidLibsPath() const +{ + return androidDirPath() + AndroidLibsFileName; +} + +QString AndroidTarget::androidStringsPath() const +{ + return androidDirPath() + AndroidStringsFileName; +} + +QString AndroidTarget::androidDefaultPropertiesPath() const +{ + return androidDirPath() + QLatin1Char('/') + AndroidDefaultPropertiesName; +} + +QString AndroidTarget::androidSrcPath() const +{ + return androidDirPath() + QLatin1String("/src"); +} + +QString AndroidTarget::apkPath(BuildType buildType) const +{ + return project()->projectDirectory() + QLatin1Char('/') + + AndroidDirName + + QString::fromLatin1("/bin/%1-%2.apk") + .arg(applicationName()) + .arg(buildType == DebugBuild ? QLatin1String("debug") + : (buildType == ReleaseBuildUnsigned) ? QLatin1String("release-unsigned") + : QLatin1String("signed")); +} + +QString AndroidTarget::localLibsRulesFilePath() const +{ + const Qt4Project *const qt4Project = qobject_cast<const Qt4Project *>(project()); + if (!qt4Project || !qt4Project->activeTarget()->activeQt4BuildConfiguration()->qtVersion()) + return QLatin1String(""); + + return qt4Project->activeTarget()->activeQt4BuildConfiguration() + ->qtVersion()->versionInfo()[QLatin1String("QT_INSTALL_LIBS")] + QLatin1String("/rules.xml"); +} + +QString AndroidTarget::loadLocal(int apiLevel, ItemType item) const +{ + QString itemType; + if (item == Lib) + itemType = QLatin1String("lib"); + else + itemType = QLatin1String("jar"); + + QString localLibs; + + QDomDocument doc; + if (!openXmlFile(doc, localLibsRulesFilePath())) + return localLibs; + + QStringList libs; + libs << qtLibs() << prebundledLibs(); + QDomElement element = doc.documentElement().firstChildElement(QLatin1String("platforms")).firstChildElement(itemType + QLatin1Char('s')).firstChildElement(QLatin1String("version")); + while (!element.isNull()) { + if (element.attribute(QLatin1String("value")).toInt() == apiLevel) { + if (element.hasAttribute(QLatin1String("symlink"))) + apiLevel = element.attribute(QLatin1String("symlink")).toInt(); + break; + } + element = element.nextSiblingElement(QLatin1String("version")); + } + + element = doc.documentElement().firstChildElement(QLatin1String("dependencies")).firstChildElement(QLatin1String("lib")); + while (!element.isNull()) { + if (libs.contains(element.attribute(QLatin1String("name")))) { + QDomElement libElement = element.firstChildElement(QLatin1String("depends")).firstChildElement(itemType); + while (!libElement.isNull()) { + localLibs += libElement.attribute(QLatin1String("file")).arg(apiLevel) + QLatin1Char(':'); + libElement = libElement.nextSiblingElement(itemType); + } + + libElement = element.firstChildElement(QLatin1String("replaces")).firstChildElement(itemType); + while (!libElement.isNull()) { + localLibs.replace(libElement.attribute(QLatin1String("file")).arg(apiLevel) + QLatin1Char(':'), QString()); + libElement = libElement.nextSiblingElement(itemType); + } + } + element = element.nextSiblingElement(QLatin1String("lib")); + } + return localLibs; +} + +QString AndroidTarget::loadLocalLibs(int apiLevel) const +{ + return loadLocal(apiLevel, Lib); +} + +QString AndroidTarget::loadLocalJars(int apiLevel) const +{ + return loadLocal(apiLevel, Jar); +} + +void AndroidTarget::updateProject(const QString &targetSDK, const QString &name) const +{ + QString androidDir = androidDirPath(); + + // clean previous build + QProcess androidProc; + androidProc.setWorkingDirectory(androidDir); + androidProc.start(AndroidConfigurations::instance().antToolPath(), QStringList() << QLatin1String("clean")); + if (!androidProc.waitForFinished(-1)) + androidProc.terminate(); + // clean previous build + + int targetSDKNumber = targetSDK.mid(targetSDK.lastIndexOf(QLatin1Char('-')) + 1).toInt(); + bool commentLines = false; + QDirIterator it(androidDir, QStringList() << QLatin1String("*.java"), QDir::Files, QDirIterator::Subdirectories); + while (it.hasNext()) { + it.next(); + QFile file(it.filePath()); + if (!file.open(QIODevice::ReadWrite)) + continue; + QList<QByteArray> lines = file.readAll().trimmed().split('\n'); + + bool modified = false; + bool comment = false; + for (int i = 0; i < lines.size(); i++) { + if (lines[i].contains("@ANDROID-")) { + commentLines = targetSDKNumber < lines[i].mid(lines[i].lastIndexOf('-') + 1).toInt(); + comment = !comment; + continue; + } + if (!comment) + continue; + if (commentLines) { + if (!lines[i].trimmed().startsWith("//QtCreator")) { + lines[i] = "//QtCreator " + lines[i]; + modified = true; + } + } else { if (lines[i].trimmed().startsWith("//QtCreator")) { + lines[i] = lines[i].mid(12); + modified = true; + } + } + } + if (modified) { + file.resize(0); + foreach (const QByteArray &line, lines) { + file.write(line); + file.write("\n"); + } + } + file.close(); + } + + QStringList params; + params << QLatin1String("update") << QLatin1String("project") << QLatin1String("-p") << androidDir; + if (!targetSDK.isEmpty()) + params << QLatin1String("-t") << targetSDK; + if (!name.isEmpty()) + params << QLatin1String("-n") << name; + androidProc.start(AndroidConfigurations::instance().androidToolPath(), params); + if (!androidProc.waitForFinished(-1)) + androidProc.terminate(); +} + +bool AndroidTarget::createAndroidTemplatesIfNecessary() const +{ + const Qt4Project *qt4Project = qobject_cast<Qt4Project*>(project()); + if (!qt4Project || !qt4Project->rootProjectNode() || !qt4Project->activeTarget() || !qt4Project->activeTarget()->activeQt4BuildConfiguration() + || !qt4Project->activeTarget()->activeQt4BuildConfiguration()->qtVersion()) + return false; + QString javaSrcPath = qt4Project->activeTarget()->activeQt4BuildConfiguration()->qtVersion()->versionInfo()[QLatin1String("QT_INSTALL_PREFIX")] + QLatin1String("/src/android/java"); + QDir projectDir(project()->projectDirectory()); + QString androidPath = androidDirPath(); + + QStringList m_ignoreFiles; + bool forceUpdate = false; + QDomDocument srcVersionDoc; + if (openXmlFile(srcVersionDoc, javaSrcPath + QLatin1String("/version.xml"), false)) { + QDomDocument dstVersionDoc; + if (openXmlFile(dstVersionDoc, androidPath + QLatin1String("/version.xml"), false)) + forceUpdate = (srcVersionDoc.documentElement().attribute(QLatin1String("value")).toDouble() + > dstVersionDoc.documentElement().attribute(QLatin1String("value")).toDouble()); + + else + forceUpdate = true; + + if (forceUpdate && QFileInfo(androidPath).exists()) { + QDomElement ignoreFile = srcVersionDoc.documentElement().firstChildElement(QLatin1String("ignore")).firstChildElement(QLatin1String("file")); + while (!ignoreFile.isNull()) { + m_ignoreFiles << ignoreFile.text(); + ignoreFile = ignoreFile.nextSiblingElement(); + } + } + } + + if (!forceUpdate && QFileInfo(androidPath).exists() + && QFileInfo(androidManifestPath()).exists() + && QFileInfo(androidPath + QLatin1String("/src")).exists() + && QFileInfo(androidPath + QLatin1String("/res")).exists()) + return true; + + forceUpdate &= QFileInfo(androidPath).exists(); + + if (!QFileInfo(androidDirPath()).exists() && !projectDir.mkdir(AndroidDirName)) { + raiseError(tr("Error creating Android directory '%1'.") + .arg(AndroidDirName)); + return false; + } + + QStringList androidFiles; + QDirIterator it(javaSrcPath, QDirIterator::Subdirectories); + int pos = it.path().size(); + while (it.hasNext()) { + it.next(); + if (it.fileInfo().isDir()) { + projectDir.mkpath(AndroidDirName + it.filePath().mid(pos)); + } else { + const QString dstFile(androidPath + it.filePath().mid(pos)); + if (m_ignoreFiles.contains(it.fileName())) + continue; + else + { + if (QFile::exists(dstFile)) + QFile::remove(dstFile); + else + androidFiles << dstFile; + } + QFile::copy(it.filePath(), dstFile); + } + } + if (androidFiles.size()) + qt4Project->rootProjectNode()->addFiles(UnknownFileType, androidFiles); + + QStringList sdks = AndroidConfigurations::instance().sdkTargets(); + if (sdks.isEmpty()) { + raiseError(tr("No Qt for Android SDKs were found.\nPlease install at least one SDK.")); + return false; + } + updateProject(AndroidConfigurations::instance().sdkTargets().at(0)); + if (availableTargetApplications().length()) + setTargetApplication(availableTargetApplications()[0]); + + QString applicationName = project()->displayName(); + if (applicationName.length()) { + setPackageName(packageName() + QLatin1Char('.') + applicationName); + applicationName[0] = applicationName[0].toUpper(); + setApplicationName(applicationName); + } + + if (forceUpdate) + QMessageBox::warning(0, tr("Warning"), tr("Android files have been updated automatically")); + + return true; +} + +bool AndroidTarget::openXmlFile(QDomDocument &doc, const QString &fileName, bool createAndroidTemplates) const +{ + if (createAndroidTemplates && !createAndroidTemplatesIfNecessary()) + return false; + + QFile f(fileName); + if (!f.open(QIODevice::ReadOnly)) + return false; + + if (!doc.setContent(f.readAll())) { + raiseError(tr("Can't parse '%1'").arg(fileName)); + return false; + } + return true; +} + +bool AndroidTarget::saveXmlFile(QDomDocument &doc, const QString &fileName) const +{ + if (!createAndroidTemplatesIfNecessary()) + return false; + + QFile f(fileName); + if (!f.open(QIODevice::WriteOnly)) { + raiseError(tr("Can't open '%1'").arg(fileName)); + return false; + } + return f.write(doc.toByteArray(4)) >= 0; +} + +bool AndroidTarget::openAndroidManifest(QDomDocument &doc) const +{ + return openXmlFile(doc, androidManifestPath()); +} + +bool AndroidTarget::saveAndroidManifest(QDomDocument &doc) const +{ + return saveXmlFile(doc, androidManifestPath()); +} + +bool AndroidTarget::openLibsXml(QDomDocument &doc) const +{ + return openXmlFile(doc, androidLibsPath()); +} + +bool AndroidTarget::saveLibsXml(QDomDocument &doc) const +{ + return saveXmlFile(doc, androidLibsPath()); +} + +QString AndroidTarget::activityName() const +{ + QDomDocument doc; + if (!openAndroidManifest(doc)) + return QString(); + QDomElement activityElem = doc.documentElement().firstChildElement(QLatin1String("application")).firstChildElement(QLatin1String("activity")); + return activityElem.attribute(QLatin1String("android:name")); +} + +QString AndroidTarget::intentName() const +{ + return packageName() + QLatin1Char('/') + activityName(); +} + +QString AndroidTarget::packageName() const +{ + QDomDocument doc; + if (!openAndroidManifest(doc)) + return QString(); + QDomElement manifestElem = doc.documentElement(); + return manifestElem.attribute(QLatin1String("package")); +} + +bool AndroidTarget::setPackageName(const QString &name) const +{ + QDomDocument doc; + if (!openAndroidManifest(doc)) + return false; + QDomElement manifestElem = doc.documentElement(); + manifestElem.setAttribute(QLatin1String("package"), cleanPackageName(name)); + return saveAndroidManifest(doc); +} + +QString AndroidTarget::applicationName() const +{ + QDomDocument doc; + if (!openXmlFile(doc, androidStringsPath())) + return QString(); + QDomElement metadataElem = doc.documentElement().firstChildElement(QLatin1String("string")); + while (!metadataElem.isNull()) { + if (metadataElem.attribute(QLatin1String("name")) == QLatin1String("app_name")) + return metadataElem.text(); + metadataElem = metadataElem.nextSiblingElement(QLatin1String("string")); + } + return QString(); +} + +bool AndroidTarget::setApplicationName(const QString &name) const +{ + QDomDocument doc; + if (!openXmlFile(doc, androidStringsPath())) + return false; + QDomElement metadataElem =doc.documentElement().firstChildElement(QLatin1String("string")); + while (!metadataElem.isNull()) { + if (metadataElem.attribute(QLatin1String("name")) == QLatin1String("app_name")) { + metadataElem.removeChild(metadataElem.firstChild()); + metadataElem.appendChild(doc.createTextNode(name)); + break; + } + metadataElem = metadataElem.nextSiblingElement(QLatin1String("string")); + } + return saveXmlFile(doc, androidStringsPath()); +} + +QStringList AndroidTarget::availableTargetApplications() const +{ + QStringList apps; + Qt4Project *qt4Project = qobject_cast<Qt4Project *>(project()); + foreach (Qt4ProFileNode *proFile, qt4Project->applicationProFiles()) { + if (proFile->projectType() == ApplicationTemplate) { + if (proFile->targetInformation().target.startsWith(QLatin1String("lib")) + && proFile->targetInformation().target.endsWith(QLatin1String(".so"))) + apps << proFile->targetInformation().target.mid(3, proFile->targetInformation().target.lastIndexOf(QLatin1Char('.')) - 3); + else + apps << proFile->targetInformation().target; + } + } + apps.sort(); + return apps; +} + +QString AndroidTarget::targetApplication() const +{ + QDomDocument doc; + if (!openAndroidManifest(doc)) + return QString(); + QDomElement metadataElem = doc.documentElement().firstChildElement(QLatin1String("application")).firstChildElement(QLatin1String("activity")).firstChildElement(QLatin1String("meta-data")); + while (!metadataElem.isNull()) { + if (metadataElem.attribute(QLatin1String("android:name")) == QLatin1String("android.app.lib_name")) + return metadataElem.attribute(QLatin1String("android:value")); + metadataElem = metadataElem.nextSiblingElement(QLatin1String("meta-data")); + } + return QString(); +} + +bool AndroidTarget::setTargetApplication(const QString &name) const +{ + QDomDocument doc; + if (!openAndroidManifest(doc)) + return false; + QDomElement metadataElem = doc.documentElement().firstChildElement(QLatin1String("application")).firstChildElement(QLatin1String("activity")).firstChildElement(QLatin1String("meta-data")); + while (!metadataElem.isNull()) { + if (metadataElem.attribute(QLatin1String("android:name")) == QLatin1String("android.app.lib_name")) { + metadataElem.setAttribute(QLatin1String("android:value"), name); + return saveAndroidManifest(doc); + } + metadataElem = metadataElem.nextSiblingElement(QLatin1String("meta-data")); + } + return false; +} + +QString AndroidTarget::targetApplicationPath() const +{ + QString selectedApp = targetApplication(); + if (!selectedApp.length()) + return QString(); + Qt4Project *qt4Project = qobject_cast<Qt4Project *>(project()); + foreach (Qt4ProFileNode *proFile, qt4Project->applicationProFiles()) { + if (proFile->projectType() == ApplicationTemplate) { + if (proFile->targetInformation().target.startsWith(QLatin1String("lib")) + && proFile->targetInformation().target.endsWith(QLatin1String(".so"))) { + if (proFile->targetInformation().target.mid(3, proFile->targetInformation().target.lastIndexOf(QLatin1Char('.')) - 3) + == selectedApp) + return proFile->targetInformation().buildDir + QLatin1String("/") + proFile->targetInformation().target; + } else { + if (proFile->targetInformation().target == selectedApp) + return proFile->targetInformation().buildDir + QLatin1String("/lib") + proFile->targetInformation().target + QLatin1String(".so"); + } + } + } + return QString(); +} + +int AndroidTarget::versionCode() const +{ + QDomDocument doc; + if (!openAndroidManifest(doc)) + return 0; + QDomElement manifestElem = doc.documentElement(); + return manifestElem.attribute(QLatin1String("android:versionCode")).toInt(); +} + +bool AndroidTarget::setVersionCode(int version) const +{ + QDomDocument doc; + if (!openAndroidManifest(doc)) + return false; + QDomElement manifestElem = doc.documentElement(); + manifestElem.setAttribute(QLatin1String("android:versionCode"), version); + return saveAndroidManifest(doc); +} + + +QString AndroidTarget::versionName() const +{ + QDomDocument doc; + if (!openAndroidManifest(doc)) + return QString(); + QDomElement manifestElem = doc.documentElement(); + return manifestElem.attribute(QLatin1String("android:versionName")); +} + +bool AndroidTarget::setVersionName(const QString &version) const +{ + QDomDocument doc; + if (!openAndroidManifest(doc)) + return false; + QDomElement manifestElem = doc.documentElement(); + manifestElem.setAttribute(QLatin1String("android:versionName"), version); + return saveAndroidManifest(doc); +} + +QStringList AndroidTarget::permissions() const +{ + QStringList per; + QDomDocument doc; + if (!openAndroidManifest(doc)) + return per; + QDomElement permissionElem = doc.documentElement().firstChildElement(QLatin1String("uses-permission")); + while (!permissionElem.isNull()) { + per << permissionElem.attribute(QLatin1String("android:name")); + permissionElem = permissionElem.nextSiblingElement(QLatin1String("uses-permission")); + } + return per; +} + +bool AndroidTarget::setPermissions(const QStringList &permissions) const +{ + QDomDocument doc; + if (!openAndroidManifest(doc)) + return false; + QDomElement docElement = doc.documentElement(); + QDomElement permissionElem = docElement.firstChildElement(QLatin1String("uses-permission")); + while (!permissionElem.isNull()) { + docElement.removeChild(permissionElem); + permissionElem = docElement.firstChildElement(QLatin1String("uses-permission")); + } + + foreach (const QString &permission, permissions ) { + permissionElem = doc.createElement(QLatin1String("uses-permission")); + permissionElem.setAttribute(QLatin1String("android:name"), permission); + docElement.appendChild(permissionElem); + } + + return saveAndroidManifest(doc); +} + + +QStringList AndroidTarget::getDependencies(const QString &readelfPath, const QString &lib) const +{ + QStringList libs; + + QProcess readelfProc; + readelfProc.start(readelfPath, QStringList() << QLatin1String("-d") << QLatin1String("-W") << lib); + + if (!readelfProc.waitForFinished(-1)) { + readelfProc.terminate(); + return libs; + } + + QList<QByteArray> lines = readelfProc.readAll().trimmed().split('\n'); + foreach (const QByteArray &line, lines) { + if (line.contains("(NEEDED)") && line.contains("Shared library:") ) { + const int pos = line.lastIndexOf('[') + 1; + libs << QString::fromLatin1(line.mid(pos, line.lastIndexOf(']') - pos)); + } + } + return libs; +} + +int AndroidTarget::setLibraryLevel(const QString &library, LibrariesMap &mapLibs) const +{ + int maxlevel = mapLibs[library].level; + if (maxlevel > 0) + return maxlevel; + foreach (QString lib, mapLibs[library].dependencies) { + foreach (const QString &key, mapLibs.keys()) { + if (library == key) + continue; + if (key == lib) { + int libLevel = mapLibs[key].level; + + if (libLevel < 0) + libLevel = setLibraryLevel(key, mapLibs); + + if (libLevel > maxlevel) + maxlevel = libLevel; + break; + } + } + } + if (mapLibs[library].level < 0) + mapLibs[library].level = maxlevel + 1; + return maxlevel + 1; +} + +bool AndroidTarget::qtLibrariesLessThan(const Library &a, const Library &b) +{ + if (a.level == b.level) + return a.name < b.name; + return a.level < b.level; +} + +QStringList AndroidTarget::availableQtLibs() const +{ + const QString readelfPath = AndroidConfigurations::instance().readelfPath(activeRunConfiguration()->abi().architecture()); + QStringList libs; + const Qt4Project *const qt4Project = qobject_cast<const Qt4Project *>(project()); + if (!qt4Project || !qt4Project->activeTarget()->activeQt4BuildConfiguration()->qtVersion()) + return libs; + QString qtLibsPath = qt4Project->activeTarget()->activeQt4BuildConfiguration()->qtVersion()->versionInfo()[QLatin1String("QT_INSTALL_LIBS")]; + if (!QFile::exists(readelfPath)) { + QDirIterator libsIt(qtLibsPath, QStringList() << QLatin1String("libQt*.so")); + while (libsIt.hasNext()) { + libsIt.next(); + libs << libsIt.fileName().mid(3, libsIt.fileName().indexOf(QLatin1Char('.')) - 3); + } + libs.sort(); + return libs; + } + LibrariesMap mapLibs; + QDir libPath; + QDirIterator it(qtLibsPath, QStringList() << QLatin1String("*.so"), QDir::Files, QDirIterator::Subdirectories); + while (it.hasNext()) { + libPath = it.next(); + const QString library = libPath.absolutePath().mid(libPath.absolutePath().lastIndexOf(QLatin1Char('/')) + 1); + mapLibs[library].dependencies = getDependencies(readelfPath, libPath.absolutePath()); + } + + // clean dependencies + foreach (const QString &key, mapLibs.keys()) { + int it = 0; + while (it < mapLibs[key].dependencies.size()) { + const QString &dependName = mapLibs[key].dependencies[it]; + if (!mapLibs.keys().contains(dependName) && dependName.startsWith(QLatin1String("lib")) && dependName.endsWith(QLatin1String(".so"))) { + mapLibs[key].dependencies.removeAt(it); + } else { + ++it; + } + } + if (!mapLibs[key].dependencies.size()) + mapLibs[key].level = 0; + } + + QVector<Library> qtLibraries; + // calculate the level for every library + foreach (const QString &key, mapLibs.keys()) { + if (mapLibs[key].level < 0) + setLibraryLevel(key, mapLibs); + + if (!mapLibs[key].name.length() && key.startsWith(QLatin1String("lib")) && key.endsWith(QLatin1String(".so"))) + mapLibs[key].name = key.mid(3, key.length() - 6); + + for (int it = 0; it < mapLibs[key].dependencies.size(); it++) { + const QString &libName = mapLibs[key].dependencies[it]; + if (libName.startsWith(QLatin1String("lib")) && libName.endsWith(QLatin1String(".so"))) + mapLibs[key].dependencies[it] = libName.mid(3, libName.length() - 6); + } + qtLibraries.push_back(mapLibs[key]); + } + qSort(qtLibraries.begin(), qtLibraries.end(), qtLibrariesLessThan); + foreach (Library lib, qtLibraries) { + libs.push_back(lib.name); + } + return libs; +} + +QIcon AndroidTarget::androidIcon(AndroidIconType type) const +{ + switch (type) { + case HighDPI: + return QIcon(androidDirPath() + QLatin1String("/res/drawable-hdpi/icon.png")); + case MediumDPI: + return QIcon(androidDirPath() + QLatin1String("/res/drawable-mdpi/icon.png")); + case LowDPI: + return QIcon(androidDirPath() + QLatin1String("/res/drawable-ldpi/icon.png")); + } + return QIcon(); +} + +bool AndroidTarget::setAndroidIcon(AndroidIconType type, const QString &iconFileName) const +{ + switch (type) { + case HighDPI: + QFile::remove(androidDirPath() + QLatin1String("/res/drawable-hdpi/icon.png")); + return QFile::copy(iconFileName, androidDirPath() + QLatin1String("/res/drawable-hdpi/icon.png")); + case MediumDPI: + QFile::remove(androidDirPath() + QLatin1String("/res/drawable-mdpi/icon.png")); + return QFile::copy(iconFileName, androidDirPath() + QLatin1String("/res/drawable-mdpi/icon.png")); + case LowDPI: + QFile::remove(androidDirPath() + QLatin1String("/res/drawable-ldpi/icon.png")); + return QFile::copy(iconFileName, androidDirPath() + QLatin1String("/res/drawable-ldpi/icon.png")); + } + return false; +} + +QStringList AndroidTarget::libsXml(const QString &tag) const +{ + QStringList libs; + QDomDocument doc; + if (!openLibsXml(doc)) + return libs; + QDomElement arrayElem = doc.documentElement().firstChildElement(QLatin1String("array")); + while (!arrayElem.isNull()) { + if (arrayElem.attribute(QLatin1String("name")) == tag) { + arrayElem = arrayElem.firstChildElement(QLatin1String("item")); + while (!arrayElem.isNull()) { + libs << arrayElem.text(); + arrayElem = arrayElem.nextSiblingElement(QLatin1String("item")); + } + return libs; + } + arrayElem = arrayElem.nextSiblingElement(QLatin1String("array")); + } + return libs; +} + +bool AndroidTarget::setLibsXml(const QStringList &libs, const QString &tag) const +{ + QDomDocument doc; + if (!openLibsXml(doc)) + return false; + QDomElement arrayElem = doc.documentElement().firstChildElement(QLatin1String("array")); + while (!arrayElem.isNull()) { + if (arrayElem.attribute(QLatin1String("name")) == tag) { + doc.documentElement().removeChild(arrayElem); + arrayElem = doc.createElement(QLatin1String("array")); + arrayElem.setAttribute(QLatin1String("name"), tag); + foreach (const QString &lib, libs) { + QDomElement item = doc.createElement(QLatin1String("item")); + item.appendChild(doc.createTextNode(lib)); + arrayElem.appendChild(item); + } + doc.documentElement().appendChild(arrayElem); + return saveLibsXml(doc); + } + arrayElem = arrayElem.nextSiblingElement(QLatin1String("array")); + } + return false; +} + +QStringList AndroidTarget::qtLibs() const +{ + return libsXml(QLatin1String("qt_libs")); +} + +bool AndroidTarget::setQtLibs(const QStringList &libs) const +{ + return setLibsXml(libs, QLatin1String("qt_libs")); +} + +QStringList AndroidTarget::availablePrebundledLibs() const +{ + QStringList libs; + Qt4Project *qt4Project = qobject_cast<Qt4Project *>(project()); + QList<Qt4Project *> qt4Projects; + qt4Projects << qt4Project; + + foreach (Qt4Project *qt4Project, qt4Projects) + foreach (Qt4ProFileNode *node, qt4Project->allProFiles()) + if (node->projectType() == LibraryTemplate) + libs << QLatin1String("lib") + node->targetInformation().target + QLatin1String(".so"); + + return libs; +} + +QStringList AndroidTarget::prebundledLibs() const +{ + return libsXml(QLatin1String("bundled_libs")); +} + +bool AndroidTarget::setPrebundledLibs(const QStringList &libs) const +{ + + return setLibsXml(libs, QLatin1String("bundled_libs")); +} + +QIcon AndroidTarget::highDpiIcon() const +{ + return androidIcon(HighDPI); +} + +bool AndroidTarget::setHighDpiIcon(const QString &iconFilePath) const +{ + return setAndroidIcon(HighDPI, iconFilePath); +} + +QIcon AndroidTarget::mediumDpiIcon() const +{ + return androidIcon(MediumDPI); +} + +bool AndroidTarget::setMediumDpiIcon(const QString &iconFilePath) const +{ + return setAndroidIcon(MediumDPI, iconFilePath); +} + +QIcon AndroidTarget::lowDpiIcon() const +{ + return androidIcon(LowDPI); +} + +bool AndroidTarget::setLowDpiIcon(const QString &iconFilePath) const +{ + return setAndroidIcon(LowDPI, iconFilePath); +} + +QString AndroidTarget::targetSDK() const +{ + if (!createAndroidTemplatesIfNecessary()) + return AndroidConfigurations::instance().bestMatch(QLatin1String("android-8")); + QFile file(androidDefaultPropertiesPath()); + if (!file.open(QIODevice::ReadOnly)) + return AndroidConfigurations::instance().bestMatch(QLatin1String("android-8")); + while (!file.atEnd()) { + QByteArray line = file.readLine(); + if (line.startsWith("target=")) + return QString::fromLatin1(line.trimmed().mid(7)); + } + return AndroidConfigurations::instance().bestMatch(QLatin1String("android-8")); +} + +bool AndroidTarget::setTargetSDK(const QString &target) const +{ + updateProject(target, applicationName()); + return true; +} + +void AndroidTarget::raiseError(const QString &reason) const +{ + QMessageBox::critical(0, tr("Error creating Android templates"), reason); +} + + +} // namespace Internal +} // namespace Qt4ProjectManager diff --git a/src/plugins/android/androidtarget.h b/src/plugins/android/androidtarget.h new file mode 100644 index 00000000000..f9f2751734d --- /dev/null +++ b/src/plugins/android/androidtarget.h @@ -0,0 +1,204 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2012 BogDan Vatra <[email protected]> +** +** Contact: Nokia Corporation ([email protected]) +** +** +** GNU Lesser General Public License Usage +** +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this file. +** Please review the following information to ensure the GNU Lesser General +** Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** Other Usage +** +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** If you have questions regarding the use of this file, please contact +** Nokia at [email protected]. +** +**************************************************************************/ + +#ifndef ANDROIDTEMPLATESCREATOR_H +#define ANDROIDTEMPLATESCREATOR_H + +#include "qt4projectmanager/qt4target.h" +#include "qt4projectmanager/qt4buildconfiguration.h" + +#include <QMap> +#include <QIcon> +#include <QDomDocument> + +QT_FORWARD_DECLARE_CLASS(QFileSystemWatcher); + +namespace ProjectExplorer { +class Project; +class ProjectNode; +class Target; +} + +namespace Qt4ProjectManager { +class Qt4Project; +class Qt4Target; +class Qt4ProFileNode; +} + + +namespace Android { +namespace Internal { +class AndroidTargetFactory; + +class AndroidTarget : public Qt4ProjectManager::Qt4BaseTarget +{ + friend class AndroidTargetFactory; + Q_OBJECT + enum AndroidIconType + { + HighDPI, + MediumDPI, + LowDPI + }; + + struct Library + { + Library() + { + level = -1; + } + int level; + QStringList dependencies; + QString name; + }; + typedef QMap<QString, Library> LibrariesMap; +public: + enum BuildType + { + DebugBuild, + ReleaseBuildUnsigned, + ReleaseBuildSigned + }; + + explicit AndroidTarget(Qt4ProjectManager::Qt4Project *parent, const QString &id); + virtual ~AndroidTarget(); + + Qt4ProjectManager::Qt4BuildConfigurationFactory *buildConfigurationFactory() const; + + void createApplicationProFiles(bool reparse); + + QList<ProjectExplorer::RunConfiguration *> runConfigurationsForNode(ProjectExplorer::Node *n); + + static QString defaultDisplayName(); + + + QString packageName() const; + bool setPackageName(const QString &name) const; + + QString intentName() const; + QString activityName() const; + + QString applicationName() const; + bool setApplicationName(const QString &name) const; + + QStringList availableTargetApplications() const; + QString targetApplication() const; + bool setTargetApplication(const QString &name) const; + QString targetApplicationPath() const; + + QString targetSDK() const; + bool setTargetSDK(const QString &target) const; + + int versionCode() const; + bool setVersionCode(int version) const; + + QString versionName() const; + bool setVersionName(const QString &version) const; + + QStringList permissions() const; + bool setPermissions(const QStringList &permissions) const; + + QStringList availableQtLibs() const; + QStringList qtLibs() const; + bool setQtLibs(const QStringList &qtLibs) const; + + QStringList availablePrebundledLibs() const; + QStringList prebundledLibs() const; + bool setPrebundledLibs(const QStringList &qtLibs) const; + + QIcon highDpiIcon() const; + bool setHighDpiIcon(const QString &iconFilePath) const; + + QIcon mediumDpiIcon() const; + bool setMediumDpiIcon(const QString &iconFilePath) const; + + QIcon lowDpiIcon() const; + bool setLowDpiIcon(const QString &iconFilePath) const; + + QString androidDirPath() const; + QString androidManifestPath() const; + QString androidLibsPath() const; + QString androidStringsPath() const; + QString androidDefaultPropertiesPath() const; + QString androidSrcPath() const; + QString apkPath(BuildType buildType) const; + QString localLibsRulesFilePath() const; + QString loadLocalLibs(int apiLevel) const; + QString loadLocalJars(int apiLevel) const; + +public slots: + bool createAndroidTemplatesIfNecessary() const; + void updateProject(const QString &targetSDK, const QString &name = QString()) const; + +signals: + void androidDirContentsChanged(); + +private slots: + void handleTargetChanged(ProjectExplorer::Target *target); + void handleTargetToBeRemoved(ProjectExplorer::Target *target); + +private: + enum ItemType + { + Lib, + Jar + }; + + QString loadLocal(int apiLevel, ItemType item) const; + void raiseError(const QString &reason) const; + bool openXmlFile(QDomDocument &doc, const QString &fileName, bool createAndroidTemplates = true) const; + bool saveXmlFile(QDomDocument &doc, const QString &fileName) const; + bool openAndroidManifest(QDomDocument &doc) const; + bool saveAndroidManifest(QDomDocument &doc) const; + bool openLibsXml(QDomDocument &doc) const; + bool saveLibsXml(QDomDocument &doc) const; + + QIcon androidIcon(AndroidIconType type) const; + bool setAndroidIcon(AndroidIconType type, const QString &iconFileName) const; + + QStringList libsXml(const QString &tag) const; + bool setLibsXml(const QStringList &qtLibs, const QString &tag) const; + + static bool qtLibrariesLessThan(const AndroidTarget::Library &a, const AndroidTarget::Library &b); + QStringList getDependencies(const QString &readelfPath, const QString &lib) const; + int setLibraryLevel(const QString &library, LibrariesMap &mapLibs) const; + + + QFileSystemWatcher *const m_androidFilesWatcher; + + Qt4ProjectManager::Qt4BuildConfigurationFactory *m_buildConfigurationFactory; +}; + +} // namespace Internal +} // namespace Android + +#endif // ANDROIDTEMPLATESCREATOR_H diff --git a/src/plugins/android/androidtargetfactory.cpp b/src/plugins/android/androidtargetfactory.cpp new file mode 100644 index 00000000000..8fcd0c04b0c --- /dev/null +++ b/src/plugins/android/androidtargetfactory.cpp @@ -0,0 +1,184 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2012 BogDan Vatra <[email protected]> +** +** Contact: Nokia Corporation ([email protected]) +** +** +** GNU Lesser General Public License Usage +** +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this file. +** Please review the following information to ensure the GNU Lesser General +** Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** Other Usage +** +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** If you have questions regarding the use of this file, please contact +** Nokia at [email protected]. +** +**************************************************************************/ + +#include "androidtargetfactory.h" +#include "qt4projectmanager/qt4project.h" +#include "qt4projectmanager/qt4projectmanagerconstants.h" +#include "androiddeploystep.h" +#include "androidglobal.h" +#include "androidpackagecreationstep.h" +#include "androidrunconfiguration.h" +#include "androidtarget.h" +#include "androiddeployconfiguration.h" + +#include <projectexplorer/deployconfiguration.h> +#include <projectexplorer/projectexplorerconstants.h> +#include <projectexplorer/customexecutablerunconfiguration.h> +#include <qt4projectmanager/buildconfigurationinfo.h> + +#include <qtsupport/qtversionmanager.h> + +using namespace Qt4ProjectManager; +using namespace Android::Internal; +using ProjectExplorer::idFromMap; + +// ------------------------------------------------------------------------- +// Qt4AndroidTargetFactory +// ------------------------------------------------------------------------- +AndroidTargetFactory::AndroidTargetFactory(QObject *parent) : + Qt4BaseTargetFactory(parent) +{ + connect(QtSupport::QtVersionManager::instance(), SIGNAL(qtVersionsChanged(QList<int>,QList<int>,QList<int>)), + this, SIGNAL(canCreateTargetIdsChanged())); +} + +AndroidTargetFactory::~AndroidTargetFactory() +{ +} + +bool AndroidTargetFactory::supportsTargetId(const QString &id) const +{ + return id == QLatin1String(Qt4ProjectManager::Constants::ANDROID_DEVICE_TARGET_ID); +} + +QSet<QString> AndroidTargetFactory::targetFeatures(const QString &/*id*/) const +{ + QSet<QString> features; + features << QLatin1String(Qt4ProjectManager::Constants::MOBILE_TARGETFEATURE_ID); + features << QLatin1String(Qt4ProjectManager::Constants::SHADOWBUILD_TARGETFEATURE_ID); + return features; +} + +QStringList AndroidTargetFactory::supportedTargetIds() const +{ + return QStringList() << QLatin1String(Qt4ProjectManager::Constants::ANDROID_DEVICE_TARGET_ID); +} + +QString AndroidTargetFactory::displayNameForId(const QString &id) const +{ + if (id == QLatin1String(Qt4ProjectManager::Constants::ANDROID_DEVICE_TARGET_ID)) + return AndroidTarget::defaultDisplayName(); + return QString(); +} + +QIcon AndroidTargetFactory::iconForId(const QString &id) const +{ + Q_UNUSED(id) + return QIcon(QLatin1String(Constants::ANDROID_SETTINGS_CATEGORY_ICON)); +} + +bool AndroidTargetFactory::canCreate(ProjectExplorer::Project *parent, const QString &id) const +{ + if (!qobject_cast<Qt4Project *>(parent)) + return false; + if (!supportsTargetId(id)) + return false; + return QtSupport::QtVersionManager::instance()->supportsTargetId(id); +} + +bool AndroidTargetFactory::canRestore(ProjectExplorer::Project *parent, const QVariantMap &map) const +{ + return canCreate(parent, idFromMap(map)); +} + +Qt4BaseTarget *AndroidTargetFactory::restore(ProjectExplorer::Project *parent, const QVariantMap &map) +{ + if (!canRestore(parent, map)) + return 0; + + const QString id = idFromMap(map); + AndroidTarget *target = 0; + Qt4Project *qt4project = static_cast<Qt4Project *>(parent); + if (id == QLatin1String(Qt4ProjectManager::Constants::ANDROID_DEVICE_TARGET_ID)) + target = new AndroidTarget(qt4project, QLatin1String("transient ID")); + if (target && target->fromMap(map)) + return target; + delete target; + return 0; +} + +Qt4BaseTarget *AndroidTargetFactory::create(ProjectExplorer::Project *parent, const QString &id) +{ + if (!canCreate(parent, id)) + return 0; + + QList<QtSupport::BaseQtVersion *> knownVersions = QtSupport::QtVersionManager::instance()->versionsForTargetId(id); + if (knownVersions.isEmpty()) + return 0; + + QtSupport::BaseQtVersion *qtVersion = knownVersions.first(); + bool buildAll = qtVersion->isValid() && (qtVersion->defaultBuildConfig() & QtSupport::BaseQtVersion::BuildAll); + QtSupport::BaseQtVersion::QmakeBuildConfigs config = buildAll ? QtSupport::BaseQtVersion::BuildAll : QtSupport::BaseQtVersion::QmakeBuildConfig(0); + + QList<Qt4ProjectManager::BuildConfigurationInfo> infos; + infos.append(Qt4ProjectManager::BuildConfigurationInfo(qtVersion->uniqueId(), config, QString(), QString())); + infos.append(Qt4ProjectManager::BuildConfigurationInfo(qtVersion->uniqueId(), config ^ QtSupport::BaseQtVersion::DebugBuild, QString(), QString())); + + return create(parent, id, infos); +} + +Qt4BaseTarget *AndroidTargetFactory::create(ProjectExplorer::Project *parent, + const QString &id, const QList<Qt4ProjectManager::BuildConfigurationInfo> &infos) +{ + if (!canCreate(parent, id)) + return 0; + + AndroidTarget *target = 0; + if (id == QLatin1String(Qt4ProjectManager::Constants::ANDROID_DEVICE_TARGET_ID)) + target = new AndroidTarget(static_cast<Qt4Project *>(parent), id); + Q_ASSERT(target); + + foreach (const Qt4ProjectManager::BuildConfigurationInfo &info, infos) { + QString displayName = info.version()->displayName() + QLatin1Char(' '); + displayName += (info.buildConfig & QtSupport::BaseQtVersion::DebugBuild) ? tr("Debug") : tr("Release"); + target->addQt4BuildConfiguration(displayName, QString(), + info.version(), + info.buildConfig, + info.additionalArguments, + info.directory, + info.importing); + } + + target->addDeployConfiguration(target->createDeployConfiguration(QLatin1String(ANDROID_DEPLOYCONFIGURATION_ID))); + + target->createApplicationProFiles(false); + if (target->runConfigurations().isEmpty()) + target->addRunConfiguration(new ProjectExplorer::CustomExecutableRunConfiguration(target)); + return target; +} + +QString AndroidTargetFactory::buildNameForId(const QString &id) const +{ + if (id == QLatin1String(Qt4ProjectManager::Constants::ANDROID_DEVICE_TARGET_ID)) + return QLatin1String("android"); + return QString(); +} diff --git a/src/plugins/android/androidtargetfactory.h b/src/plugins/android/androidtargetfactory.h new file mode 100644 index 00000000000..6ffe027480e --- /dev/null +++ b/src/plugins/android/androidtargetfactory.h @@ -0,0 +1,70 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2012 BogDan Vatra <[email protected]> +** +** Contact: Nokia Corporation ([email protected]) +** +** +** GNU Lesser General Public License Usage +** +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this file. +** Please review the following information to ensure the GNU Lesser General +** Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** Other Usage +** +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** If you have questions regarding the use of this file, please contact +** Nokia at [email protected]. +** +**************************************************************************/ + +#ifndef QT4ANDROIDTARGETFACTORY_H +#define QT4ANDROIDTARGETFACTORY_H + +#include "qt4projectmanager/qt4basetargetfactory.h" +#include "qt4projectmanager/qt4target.h" + +namespace Android { +namespace Internal { + +class AndroidTargetFactory : public Qt4ProjectManager::Qt4BaseTargetFactory +{ + Q_OBJECT +public: + AndroidTargetFactory(QObject *parent = 0); + ~AndroidTargetFactory(); + + QStringList supportedTargetIds() const; + QString displayNameForId(const QString &id) const; + QIcon iconForId(const QString &id) const; + + bool canCreate(ProjectExplorer::Project *parent, const QString &id) const; + bool canRestore(ProjectExplorer::Project *parent, const QVariantMap &map) const; + Qt4ProjectManager::Qt4BaseTarget *restore(ProjectExplorer::Project *parent, const QVariantMap &map); + + bool supportsTargetId(const QString &id) const; + virtual QSet<QString> targetFeatures(const QString &id) const; + + Qt4ProjectManager::Qt4BaseTarget *create(ProjectExplorer::Project *parent, const QString &id); + Qt4ProjectManager::Qt4BaseTarget *create(ProjectExplorer::Project *parent, const QString &id, + const QList<Qt4ProjectManager::BuildConfigurationInfo> &infos); + + QString buildNameForId(const QString &id) const; +}; + +} // namespace Internal +} // namespace Android + +#endif // QT4ANDROIDTARGETFACTORY_H diff --git a/src/plugins/android/androidtoolchain.cpp b/src/plugins/android/androidtoolchain.cpp new file mode 100644 index 00000000000..1c6c9c1a3a0 --- /dev/null +++ b/src/plugins/android/androidtoolchain.cpp @@ -0,0 +1,333 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2012 BogDan Vatra <[email protected]> +** +** Contact: Nokia Corporation ([email protected]) +** +** +** GNU Lesser General Public License Usage +** +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this file. +** Please review the following information to ensure the GNU Lesser General +** Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** Other Usage +** +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** If you have questions regarding the use of this file, please contact +** Nokia at [email protected]. +** +**************************************************************************/ + +#include "androidtoolchain.h" +#include "androidconstants.h" +#include "androidconfigurations.h" +#include "androidtarget.h" +#include "androidqtversion.h" + +#include "qt4projectmanager/qt4projectmanagerconstants.h" + +#include <projectexplorer/toolchainmanager.h> +#include <projectexplorer/projectexplorer.h> +#include <qt4projectmanager/qt4project.h> +#include <qtsupport/qtversionmanager.h> + +#include <utils/environment.h> + +#include <QDir> +#include <QLabel> +#include <QVBoxLayout> + +namespace Android { +namespace Internal { + +using namespace Qt4ProjectManager; + +static const char ANDROID_QT_VERSION_KEY[] = "Qt4ProjectManager.Android.QtVersion"; + + +AndroidToolChain::AndroidToolChain(bool autodetected) : + ProjectExplorer::GccToolChain(QLatin1String(Constants::ANDROID_TOOLCHAIN_ID), autodetected), + m_qtVersionId(-1) +{} + +AndroidToolChain::AndroidToolChain(const AndroidToolChain &tc) : + ProjectExplorer::GccToolChain(tc), + m_qtVersionId(tc.m_qtVersionId) +{ } + +AndroidToolChain::~AndroidToolChain() +{ } + +QString AndroidToolChain::type() const +{ + return QLatin1String("androidgcc"); +} + +QString AndroidToolChain::typeDisplayName() const +{ + return AndroidToolChainFactory::tr("Android GCC"); +} + +bool AndroidToolChain::isValid() const +{ + return GccToolChain::isValid() && m_qtVersionId >= 0 && targetAbi().isValid(); +} + +void AndroidToolChain::addToEnvironment(Utils::Environment &env) const +{ + +// TODO this vars should be configurable in projects -> build tab +// TODO invalidate all .pro files !!! + + QString ndk_host = QLatin1String( +#if defined(Q_OS_LINUX) + "linux-x86" +#elif defined(Q_OS_WIN) + "windows" +#elif defined(Q_OS_MAC) + "darwin-x86" +#endif + ); + + // this env vars are used by qmake mkspecs to generate makefiles (check QTDIR/mkspecs/android-g++/qmake.conf for more info) + env.set(QLatin1String("ANDROID_NDK_HOST"), ndk_host); + env.set(QLatin1String("ANDROID_NDK_ROOT"), + QDir::toNativeSeparators(AndroidConfigurations::instance().config().ndkLocation)); + env.set(QLatin1String("ANDROID_NDK_TOOLCHAIN_PREFIX"), AndroidConfigurations::toolchainPrefix(targetAbi().architecture())); + env.set(QLatin1String("ANDROID_NDK_TOOLS_PREFIX"), AndroidConfigurations::toolsPrefix(targetAbi().architecture())); + env.set(QLatin1String("ANDROID_NDK_TOOLCHAIN_VERSION"), AndroidConfigurations::instance().config().ndkToolchainVersion); + + // TODO that is very ugly and likely to be wrong... + Qt4Project *qt4pro = qobject_cast<Qt4Project *>(ProjectExplorer::ProjectExplorerPlugin::instance()->currentProject()); + if (!qt4pro) + return; + AndroidTarget *at = qobject_cast<AndroidTarget *>(qt4pro->activeTarget()); + if (!at) + return; + env.set(QLatin1String("ANDROID_NDK_PLATFORM"), + AndroidConfigurations::instance().bestMatch(at->targetSDK())); +} + +bool AndroidToolChain::operator ==(const ProjectExplorer::ToolChain &tc) const +{ + if (!ToolChain::operator ==(tc)) + return false; + + const AndroidToolChain *tcPtr = static_cast<const AndroidToolChain *>(&tc); + return m_qtVersionId == tcPtr->m_qtVersionId; +} + +ProjectExplorer::ToolChainConfigWidget *AndroidToolChain::configurationWidget() +{ + return new AndroidToolChainConfigWidget(this); +} + +QVariantMap AndroidToolChain::toMap() const +{ + QVariantMap result = GccToolChain::toMap(); + result.insert(QLatin1String(ANDROID_QT_VERSION_KEY), m_qtVersionId); + return result; +} + +bool AndroidToolChain::fromMap(const QVariantMap &data) +{ + if (!GccToolChain::fromMap(data)) + return false; + + m_qtVersionId = data.value(QLatin1String(ANDROID_QT_VERSION_KEY), -1).toInt(); + + return isValid(); +} + +QList<Utils::FileName> AndroidToolChain::suggestedMkspecList() const +{ + return QList<Utils::FileName>()<< Utils::FileName::fromString(QLatin1String("android-g++")); +} + +QString AndroidToolChain::makeCommand() const +{ +#if defined(Q_OS_WIN) + return QLatin1String("ma-make.exe"); +#else + return QLatin1String("make"); +#endif +} + +void AndroidToolChain::setQtVersionId(int id) +{ + if (id < 0) { + setTargetAbi(ProjectExplorer::Abi()); + m_qtVersionId = -1; + toolChainUpdated(); + return; + } + + QtSupport::BaseQtVersion *version = QtSupport::QtVersionManager::instance()->version(id); + Q_ASSERT(version); + m_qtVersionId = id; + + Q_ASSERT(version->qtAbis().count() == 1); + setTargetAbi(version->qtAbis().at(0)); + + toolChainUpdated(); + setDisplayName(AndroidToolChainFactory::tr("Android Gcc for %1").arg(version->displayName())); +} + +int AndroidToolChain::qtVersionId() const +{ + return m_qtVersionId; +} + +QList<ProjectExplorer::Abi> AndroidToolChain::detectSupportedAbis() const +{ + if (m_qtVersionId < 0) + return QList<ProjectExplorer::Abi>(); + + AndroidQtVersion *aqv = dynamic_cast<AndroidQtVersion *>(QtSupport::QtVersionManager::instance()->version(m_qtVersionId)); + if (!aqv) + return QList<ProjectExplorer::Abi>(); + + return aqv->qtAbis(); +} + +QString AndroidToolChain::legacyId() const +{ + return QString::fromLatin1("%1:%2").arg(QLatin1String(Constants::ANDROID_TOOLCHAIN_ID)).arg(m_qtVersionId); +} + +// -------------------------------------------------------------------------- +// ToolChainConfigWidget +// -------------------------------------------------------------------------- + +AndroidToolChainConfigWidget::AndroidToolChainConfigWidget(AndroidToolChain *tc) : + ProjectExplorer::ToolChainConfigWidget(tc) +{ + QVBoxLayout *layout = new QVBoxLayout(this); + QLabel *label = new QLabel; + QtSupport::BaseQtVersion *v = QtSupport::QtVersionManager::instance()->version(tc->qtVersionId()); + Q_ASSERT(v); + label->setText(tr("NDK Root: %1").arg(AndroidConfigurations::instance().config().ndkLocation)); + layout->addWidget(label); +} + +void AndroidToolChainConfigWidget::apply() +{ + // nothing to do! +} + +void AndroidToolChainConfigWidget::discard() +{ + // nothing to do! +} + +bool AndroidToolChainConfigWidget::isDirty() const +{ + return false; +} + +// -------------------------------------------------------------------------- +// ToolChainFactory +// -------------------------------------------------------------------------- + +AndroidToolChainFactory::AndroidToolChainFactory() : + ProjectExplorer::ToolChainFactory() +{ } + +QString AndroidToolChainFactory::displayName() const +{ + return tr("Android GCC"); +} + +QString AndroidToolChainFactory::id() const +{ + return QLatin1String(Constants::ANDROID_TOOLCHAIN_ID); +} + +QList<ProjectExplorer::ToolChain *> AndroidToolChainFactory::autoDetect() +{ + QList<ProjectExplorer::ToolChain *> result; + + QtSupport::QtVersionManager *vm = QtSupport::QtVersionManager::instance(); + connect(vm, SIGNAL(qtVersionsChanged(QList<int>,QList<int>,QList<int>)), + this, SLOT(handleQtVersionChanges(QList<int>,QList<int>,QList<int>))); + + QList<int> versionList; + foreach (QtSupport::BaseQtVersion *v, vm->versions()) + versionList.append(v->uniqueId()); + + return createToolChainList(versionList); +} + +bool AndroidToolChainFactory::canRestore(const QVariantMap &data) +{ + return idFromMap(data).startsWith(QLatin1String(Constants::ANDROID_TOOLCHAIN_ID) + QLatin1Char(':')); +} + +ProjectExplorer::ToolChain *AndroidToolChainFactory::restore(const QVariantMap &data) +{ + AndroidToolChain *tc = new AndroidToolChain(false); + if (tc->fromMap(data)) + return tc; + + delete tc; + return 0; +} + +void AndroidToolChainFactory::handleQtVersionChanges(const QList<int> &added, const QList<int> &removed, const QList<int> &changed) +{ + QList<int> changes; + changes << added << removed << changed; + ProjectExplorer::ToolChainManager *tcm = ProjectExplorer::ToolChainManager::instance(); + QList<ProjectExplorer::ToolChain *> tcList = createToolChainList(changes); + foreach (ProjectExplorer::ToolChain *tc, tcList) + tcm->registerToolChain(tc); +} + +QList<ProjectExplorer::ToolChain *> AndroidToolChainFactory::createToolChainList(const QList<int> &changes) +{ + ProjectExplorer::ToolChainManager *tcm = ProjectExplorer::ToolChainManager::instance(); + QtSupport::QtVersionManager *vm = QtSupport::QtVersionManager::instance(); + QList<ProjectExplorer::ToolChain *> result; + + foreach (int i, changes) { + QtSupport::BaseQtVersion *v = vm->version(i); + QList<ProjectExplorer::ToolChain *> toRemove; + foreach (ProjectExplorer::ToolChain *tc, tcm->toolChains()) { + if (tc->id() != QLatin1String(Constants::ANDROID_TOOLCHAIN_ID)) + continue; + AndroidToolChain *aTc = static_cast<AndroidToolChain *>(tc); + if (aTc->qtVersionId() == i) + toRemove.append(aTc); + } + foreach (ProjectExplorer::ToolChain *tc, toRemove) + tcm->deregisterToolChain(tc); + + const AndroidQtVersion * const aqv = dynamic_cast<AndroidQtVersion *>(v); + if (!aqv || !aqv->isValid()) + continue; + + AndroidToolChain *aTc = new AndroidToolChain(true); + aTc->setQtVersionId(i); + aTc->setDisplayName(tr("Android GCC (%1-%2)") + .arg(ProjectExplorer::Abi::toString(aTc->targetAbi().architecture())) + .arg(AndroidConfigurations::instance().config().ndkToolchainVersion)); + aTc->setCompilerCommand(Utils::FileName::fromString(AndroidConfigurations::instance().gccPath(aTc->targetAbi().architecture()))); + result.append(aTc); + } + return result; +} + +} // namespace Internal +} // namespace Android diff --git a/src/plugins/android/androidtoolchain.h b/src/plugins/android/androidtoolchain.h new file mode 100644 index 00000000000..58003a77523 --- /dev/null +++ b/src/plugins/android/androidtoolchain.h @@ -0,0 +1,119 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2012 BogDan Vatra <[email protected]> +** +** Contact: Nokia Corporation ([email protected]) +** +** +** GNU Lesser General Public License Usage +** +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this file. +** Please review the following information to ensure the GNU Lesser General +** Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** Other Usage +** +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** If you have questions regarding the use of this file, please contact +** Nokia at [email protected]. +** +**************************************************************************/ + +#ifndef ANDROIDTOOLCHAIN_H +#define ANDROIDTOOLCHAIN_H + +#include <projectexplorer/gcctoolchain.h> +#include <projectexplorer/toolchainconfigwidget.h> + +namespace Android { + +namespace Internal { + +// -------------------------------------------------------------------------- +// MaemoToolChain +// -------------------------------------------------------------------------- + +class AndroidToolChain : public ProjectExplorer::GccToolChain +{ +public: + ~AndroidToolChain(); + + QString type() const; + QString typeDisplayName() const; + + bool isValid() const; + + void addToEnvironment(Utils::Environment &env) const; + + bool operator ==(const ProjectExplorer::ToolChain &) const; + + ProjectExplorer::ToolChainConfigWidget *configurationWidget(); + + QVariantMap toMap() const; + bool fromMap(const QVariantMap &data); + QList<Utils::FileName> suggestedMkspecList() const; + virtual QString makeCommand() const; + + void setQtVersionId(int); + int qtVersionId() const; + QString legacyId() const; +protected: + virtual QList<ProjectExplorer::Abi> detectSupportedAbis() const; + +private: + explicit AndroidToolChain(bool); + AndroidToolChain(const AndroidToolChain &); + + int m_qtVersionId; + mutable QString m_sysroot; + friend class AndroidToolChainFactory; +}; + + +class AndroidToolChainConfigWidget : public ProjectExplorer::ToolChainConfigWidget +{ + Q_OBJECT + +public: + AndroidToolChainConfigWidget(AndroidToolChain *); + + void apply(); + void discard(); + bool isDirty() const; +}; + + +class AndroidToolChainFactory : public ProjectExplorer::ToolChainFactory +{ + Q_OBJECT + +public: + AndroidToolChainFactory(); + + QString displayName() const; + QString id() const; + + QList<ProjectExplorer::ToolChain *> autoDetect(); + bool canRestore(const QVariantMap &data); + ProjectExplorer::ToolChain *restore(const QVariantMap &data); + +private slots: + void handleQtVersionChanges(const QList<int> &added, const QList<int> &removed, const QList<int> &changed); + QList<ProjectExplorer::ToolChain *> createToolChainList(const QList<int> &); +}; + +} // namespace Internal +} // namespace Android + +#endif // ANDROIDTOOLCHAIN_H diff --git a/src/plugins/android/images/QtAndroid.png b/src/plugins/android/images/QtAndroid.png Binary files differnew file mode 100644 index 00000000000..79f4ae0b921 --- /dev/null +++ b/src/plugins/android/images/QtAndroid.png diff --git a/src/plugins/android/javaparser.cpp b/src/plugins/android/javaparser.cpp new file mode 100644 index 00000000000..3e2946bd466 --- /dev/null +++ b/src/plugins/android/javaparser.cpp @@ -0,0 +1,80 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2012 BogDan Vatra <[email protected]> +** +** Contact: Nokia Corporation ([email protected]) +** +** +** GNU Lesser General Public License Usage +** +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this file. +** Please review the following information to ensure the GNU Lesser General +** Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** Other Usage +** +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** If you have questions regarding the use of this file, please contact +** Nokia at [email protected]. +** +**************************************************************************/ + +#include "javaparser.h" + +#include <projectexplorer/projectexplorerconstants.h> +#include <projectexplorer/task.h> + +using namespace Android::Internal; +using namespace ProjectExplorer; + +JavaParser::JavaParser() : + ProjectExplorer::IOutputParser() + , m_javaRegExp(QLatin1String("^(.*\\[javac\\]\\s)(.*\\.java):(\\d+):(.*)$")) +{ +} + +void JavaParser::stdOutput(const QString &line) +{ + stdError(line); +} + +void JavaParser::stdError(const QString &line) +{ + if (m_javaRegExp.indexIn(line) > -1) { + bool ok; + int lineno = m_javaRegExp.cap(3).toInt(&ok); + if (!ok) + lineno = -1; + QString file = m_javaRegExp.cap(2); + for (int i = 0; i < m_fileList.size(); i++) + if (m_fileList[i].endsWith(file)) { + file = m_fileList[i]; + break; + } + + Task task(Task::Error, + m_javaRegExp.cap(4).trimmed(), + Utils::FileName::fromString(file) /* filename */, + lineno, + ProjectExplorer::Constants::TASK_CATEGORY_COMPILE); + emit addTask(task); + return; + } + IOutputParser::stdError(line); +} + +void JavaParser::setProjectFileList(const QStringList &fileList) +{ + m_fileList = fileList; +} diff --git a/src/plugins/android/javaparser.h b/src/plugins/android/javaparser.h new file mode 100644 index 00000000000..2e7d62d15f7 --- /dev/null +++ b/src/plugins/android/javaparser.h @@ -0,0 +1,56 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2012 BogDan Vatra <[email protected]> +** +** Contact: Nokia Corporation ([email protected]) +** +** +** GNU Lesser General Public License Usage +** +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this file. +** Please review the following information to ensure the GNU Lesser General +** Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** Other Usage +** +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** If you have questions regarding the use of this file, please contact +** Nokia at [email protected]. +** +**************************************************************************/ + +#ifndef JAVAPARSER_H +#define JAVAPARSER_H +#include <projectexplorer/ioutputparser.h> + +namespace Android { +namespace Internal { + +class JavaParser : public ProjectExplorer::IOutputParser +{ + Q_OBJECT +public: + JavaParser(); + virtual void stdOutput(const QString &line); + virtual void stdError(const QString &line); + void setProjectFileList(const QStringList &fileList); +private: + QRegExp m_javaRegExp; + QStringList m_fileList; +}; + +} // namespace Internal +} // namespace Android + +#endif // JAVAPARSER_H |