androidmanager.cpp 40.5 KB
Newer Older
BogDan Vatra's avatar
BogDan Vatra committed
1 2
/**************************************************************************
**
3
** Copyright (c) 2013 BogDan Vatra <bog_dan_ro@yahoo.com>
hjk's avatar
hjk committed
4
** Contact: http://www.qt-project.org/legal
BogDan Vatra's avatar
BogDan Vatra committed
5
**
hjk's avatar
hjk committed
6
** This file is part of Qt Creator.
BogDan Vatra's avatar
BogDan Vatra committed
7
**
hjk's avatar
hjk committed
8 9 10 11 12 13 14
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia.  For licensing terms and
** conditions see http://qt.digia.com/licensing.  For further information
** use the contact form at http://qt.digia.com/contact-us.
BogDan Vatra's avatar
BogDan Vatra committed
15 16
**
** GNU Lesser General Public License Usage
hjk's avatar
hjk committed
17 18 19 20 21 22 23 24 25
** Alternatively, 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, Digia gives you certain additional
** rights.  These rights are described in the Digia Qt LGPL Exception
BogDan Vatra's avatar
BogDan Vatra committed
26 27
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
hjk's avatar
hjk committed
28
****************************************************************************/
BogDan Vatra's avatar
BogDan Vatra committed
29

Tobias Hunger's avatar
Tobias Hunger committed
30
#include "androidmanager.h"
BogDan Vatra's avatar
BogDan Vatra committed
31 32 33 34 35 36
#include "androiddeployconfiguration.h"
#include "androidconfigurations.h"
#include "androidrunconfiguration.h"
#include "androiddeploystep.h"
#include "androidglobal.h"
#include "androidpackagecreationstep.h"
37
#include "androidtoolchain.h"
BogDan Vatra's avatar
BogDan Vatra committed
38

Tobias Hunger's avatar
Tobias Hunger committed
39 40 41
#include <projectexplorer/projectexplorer.h>
#include <projectexplorer/session.h>
#include <projectexplorer/target.h>
BogDan Vatra's avatar
BogDan Vatra committed
42 43 44 45
#include <qt4projectmanager/qt4nodes.h>
#include <qt4projectmanager/qt4project.h>
#include <qt4projectmanager/qt4projectmanagerconstants.h>
#include <qt4projectmanager/qt4buildconfiguration.h>
46
#include <qtsupport/customexecutablerunconfiguration.h>
Tobias Hunger's avatar
Tobias Hunger committed
47
#include <qtsupport/qtkitinformation.h>
Tobias Hunger's avatar
Tobias Hunger committed
48
#include <qtsupport/qtsupportconstants.h>
BogDan Vatra's avatar
BogDan Vatra committed
49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66

#include <QDir>
#include <QFileSystemWatcher>
#include <QList>
#include <QProcess>
#include <QMessageBox>
#include <QApplication>
#include <QDomDocument>

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)
    {
67
        QRegExp legalChars(QLatin1String("[a-zA-Z0-9_\\.]"));
BogDan Vatra's avatar
BogDan Vatra committed
68 69 70 71 72 73 74 75 76 77 78 79

        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 {

Tobias Hunger's avatar
Tobias Hunger committed
80
bool AndroidManager::supportsAndroid(ProjectExplorer::Target *target)
BogDan Vatra's avatar
BogDan Vatra committed
81
{
Tobias Hunger's avatar
Tobias Hunger committed
82 83
    if (!qobject_cast<Qt4ProjectManager::Qt4Project *>(target->project()))
        return false;
Tobias Hunger's avatar
Tobias Hunger committed
84
    QtSupport::BaseQtVersion *version = QtSupport::QtKitInformation::qtVersion(target->kit());
85
    return version && version->platformName() == QLatin1String(QtSupport::Constants::ANDROID_PLATFORM);
Tobias Hunger's avatar
Tobias Hunger committed
86
}
BogDan Vatra's avatar
BogDan Vatra committed
87

Tobias Hunger's avatar
Tobias Hunger committed
88 89 90 91 92 93 94
QString AndroidManager::packageName(ProjectExplorer::Target *target)
{
    QDomDocument doc;
    if (!openManifest(target, doc))
        return QString();
    QDomElement manifestElem = doc.documentElement();
    return manifestElem.attribute(QLatin1String("package"));
BogDan Vatra's avatar
BogDan Vatra committed
95 96
}

Tobias Hunger's avatar
Tobias Hunger committed
97
bool AndroidManager::setPackageName(ProjectExplorer::Target *target, const QString &name)
BogDan Vatra's avatar
BogDan Vatra committed
98
{
Tobias Hunger's avatar
Tobias Hunger committed
99 100 101 102 103 104
    QDomDocument doc;
    if (!openManifest(target, doc))
        return false;
    QDomElement manifestElem = doc.documentElement();
    manifestElem.setAttribute(QLatin1String("package"), cleanPackageName(name));
    return saveManifest(target, doc);
BogDan Vatra's avatar
BogDan Vatra committed
105 106
}

Tobias Hunger's avatar
Tobias Hunger committed
107
QString AndroidManager::applicationName(ProjectExplorer::Target *target)
BogDan Vatra's avatar
BogDan Vatra committed
108
{
Tobias Hunger's avatar
Tobias Hunger committed
109
    QDomDocument doc;
110
    if (!openXmlFile(doc, stringsPath(target)))
Tobias Hunger's avatar
Tobias Hunger committed
111 112 113 114 115 116 117 118 119
        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();
}
BogDan Vatra's avatar
BogDan Vatra committed
120

Tobias Hunger's avatar
Tobias Hunger committed
121 122 123 124
bool AndroidManager::setApplicationName(ProjectExplorer::Target *target, const QString &name)
{
    QDomDocument doc;
    Utils::FileName path = stringsPath(target);
125
    if (!openXmlFile(doc, path))
Tobias Hunger's avatar
Tobias Hunger committed
126 127 128 129 130 131 132 133 134 135 136 137
        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(target, doc, path);
}
BogDan Vatra's avatar
BogDan Vatra committed
138

Tobias Hunger's avatar
Tobias Hunger committed
139 140 141 142 143 144 145 146 147 148 149 150 151
QStringList AndroidManager::permissions(ProjectExplorer::Target *target)
{
    QStringList per;
    QDomDocument doc;
    if (!openManifest(target, 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;
}
BogDan Vatra's avatar
BogDan Vatra committed
152

Tobias Hunger's avatar
Tobias Hunger committed
153 154 155 156 157 158 159 160 161 162 163
bool AndroidManager::setPermissions(ProjectExplorer::Target *target, const QStringList &permissions)
{
    QDomDocument doc;
    if (!openManifest(target, 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"));
    }
BogDan Vatra's avatar
BogDan Vatra committed
164

Tobias Hunger's avatar
Tobias Hunger committed
165 166 167 168
    foreach (const QString &permission, permissions ) {
        permissionElem = doc.createElement(QLatin1String("uses-permission"));
        permissionElem.setAttribute(QLatin1String("android:name"), permission);
        docElement.appendChild(permissionElem);
BogDan Vatra's avatar
BogDan Vatra committed
169
    }
Tobias Hunger's avatar
Tobias Hunger committed
170 171

    return saveManifest(target, doc);
BogDan Vatra's avatar
BogDan Vatra committed
172 173
}

Tobias Hunger's avatar
Tobias Hunger committed
174
QString AndroidManager::intentName(ProjectExplorer::Target *target)
BogDan Vatra's avatar
BogDan Vatra committed
175
{
Tobias Hunger's avatar
Tobias Hunger committed
176
    return packageName(target) + QLatin1Char('/') + activityName(target);
BogDan Vatra's avatar
BogDan Vatra committed
177 178
}

Tobias Hunger's avatar
Tobias Hunger committed
179
QString AndroidManager::activityName(ProjectExplorer::Target *target)
BogDan Vatra's avatar
BogDan Vatra committed
180
{
Tobias Hunger's avatar
Tobias Hunger committed
181 182 183 184 185
    QDomDocument doc;
    if (!openManifest(target, doc))
        return QString();
    QDomElement activityElem = doc.documentElement().firstChildElement(QLatin1String("application")).firstChildElement(QLatin1String("activity"));
    return activityElem.attribute(QLatin1String("android:name"));
BogDan Vatra's avatar
BogDan Vatra committed
186 187
}

Tobias Hunger's avatar
Tobias Hunger committed
188
int AndroidManager::versionCode(ProjectExplorer::Target *target)
BogDan Vatra's avatar
BogDan Vatra committed
189
{
Tobias Hunger's avatar
Tobias Hunger committed
190 191 192 193 194 195
    QDomDocument doc;
    if (!openManifest(target, doc))
        return 0;
    QDomElement manifestElem = doc.documentElement();
    return manifestElem.attribute(QLatin1String("android:versionCode")).toInt();
}
BogDan Vatra's avatar
BogDan Vatra committed
196

Tobias Hunger's avatar
Tobias Hunger committed
197 198 199 200 201 202 203 204 205
bool AndroidManager::setVersionCode(ProjectExplorer::Target *target, int version)
{
    QDomDocument doc;
    if (!openManifest(target, doc))
        return false;
    QDomElement manifestElem = doc.documentElement();
    manifestElem.setAttribute(QLatin1String("android:versionCode"), version);
    return saveManifest(target, doc);
}
BogDan Vatra's avatar
BogDan Vatra committed
206

Tobias Hunger's avatar
Tobias Hunger committed
207 208 209 210 211 212 213 214
QString AndroidManager::versionName(ProjectExplorer::Target *target)
{
    QDomDocument doc;
    if (!openManifest(target, doc))
        return QString();
    QDomElement manifestElem = doc.documentElement();
    return manifestElem.attribute(QLatin1String("android:versionName"));
}
BogDan Vatra's avatar
BogDan Vatra committed
215

Tobias Hunger's avatar
Tobias Hunger committed
216 217 218 219 220 221 222 223
bool AndroidManager::setVersionName(ProjectExplorer::Target *target, const QString &version)
{
    QDomDocument doc;
    if (!openManifest(target, doc))
        return false;
    QDomElement manifestElem = doc.documentElement();
    manifestElem.setAttribute(QLatin1String("android:versionName"), version);
    return saveManifest(target, doc);
BogDan Vatra's avatar
BogDan Vatra committed
224 225
}

Daniel Teske's avatar
Daniel Teske committed
226 227 228 229 230 231 232 233 234 235
bool AndroidManager::ensureIconAttribute(ProjectExplorer::Target *target)
{
    QDomDocument doc;
    if (!openManifest(target, doc))
        return false;
    QDomElement applicationElem = doc.documentElement().firstChildElement(QLatin1String("application"));
    applicationElem.setAttribute(QLatin1String("android:icon"), QLatin1String("@drawable/icon"));
    return saveManifest(target, doc);
}

Tobias Hunger's avatar
Tobias Hunger committed
236
QString AndroidManager::targetSDK(ProjectExplorer::Target *target)
BogDan Vatra's avatar
BogDan Vatra committed
237
{
238 239 240 241 242
    QString fallback = QLatin1String("android-8");
    if (QtSupport::BaseQtVersion *qt = QtSupport::QtKitInformation::qtVersion(target->kit()))
        if (qt->qtVersion() >= QtSupport::QtVersionNumber(5, 0, 0))
            fallback = QLatin1String("android-9");

Tobias Hunger's avatar
Tobias Hunger committed
243
    if (!createAndroidTemplatesIfNecessary(target))
244
        return AndroidConfigurations::instance().bestMatch(fallback);
Tobias Hunger's avatar
Tobias Hunger committed
245 246
    QFile file(defaultPropertiesPath(target).toString());
    if (!file.open(QIODevice::ReadOnly))
247
        return AndroidConfigurations::instance().bestMatch(fallback);
Tobias Hunger's avatar
Tobias Hunger committed
248 249 250 251 252
    while (!file.atEnd()) {
        QByteArray line = file.readLine();
        if (line.startsWith("target="))
            return QString::fromLatin1(line.trimmed().mid(7));
    }
253
    return AndroidConfigurations::instance().bestMatch(fallback);
Tobias Hunger's avatar
Tobias Hunger committed
254
}
BogDan Vatra's avatar
BogDan Vatra committed
255

Tobias Hunger's avatar
Tobias Hunger committed
256 257 258 259 260
bool AndroidManager::setTargetSDK(ProjectExplorer::Target *target, const QString &sdk)
{
    updateTarget(target, sdk, applicationName(target));
    return true;
}
BogDan Vatra's avatar
BogDan Vatra committed
261

Tobias Hunger's avatar
Tobias Hunger committed
262 263 264
QIcon AndroidManager::highDpiIcon(ProjectExplorer::Target *target)
{
    return icon(target, HighDPI);
BogDan Vatra's avatar
BogDan Vatra committed
265 266
}

Tobias Hunger's avatar
Tobias Hunger committed
267
bool AndroidManager::setHighDpiIcon(ProjectExplorer::Target *target, const QString &iconFilePath)
BogDan Vatra's avatar
BogDan Vatra committed
268
{
Daniel Teske's avatar
Daniel Teske committed
269 270
    return ensureIconAttribute(target) &&
            setIcon(target, HighDPI, iconFilePath);
BogDan Vatra's avatar
BogDan Vatra committed
271 272
}

Tobias Hunger's avatar
Tobias Hunger committed
273
QIcon AndroidManager::mediumDpiIcon(ProjectExplorer::Target *target)
BogDan Vatra's avatar
BogDan Vatra committed
274
{
Tobias Hunger's avatar
Tobias Hunger committed
275
    return icon(target, MediumDPI);
BogDan Vatra's avatar
BogDan Vatra committed
276 277
}

Tobias Hunger's avatar
Tobias Hunger committed
278
bool AndroidManager::setMediumDpiIcon(ProjectExplorer::Target *target, const QString &iconFilePath)
BogDan Vatra's avatar
BogDan Vatra committed
279
{
Daniel Teske's avatar
Daniel Teske committed
280 281
    return ensureIconAttribute(target) &&
            setIcon(target, MediumDPI, iconFilePath);
BogDan Vatra's avatar
BogDan Vatra committed
282 283
}

Tobias Hunger's avatar
Tobias Hunger committed
284
QIcon AndroidManager::lowDpiIcon(ProjectExplorer::Target *target)
BogDan Vatra's avatar
BogDan Vatra committed
285
{
Tobias Hunger's avatar
Tobias Hunger committed
286
    return icon(target, LowDPI);
BogDan Vatra's avatar
BogDan Vatra committed
287 288
}

Tobias Hunger's avatar
Tobias Hunger committed
289
bool AndroidManager::setLowDpiIcon(ProjectExplorer::Target *target, const QString &iconFilePath)
BogDan Vatra's avatar
BogDan Vatra committed
290
{
Daniel Teske's avatar
Daniel Teske committed
291 292
    return ensureIconAttribute(target) &&
            setIcon(target, LowDPI, iconFilePath);
BogDan Vatra's avatar
BogDan Vatra committed
293 294
}

Tobias Hunger's avatar
Tobias Hunger committed
295
Utils::FileName AndroidManager::dirPath(ProjectExplorer::Target *target)
BogDan Vatra's avatar
BogDan Vatra committed
296
{
Tobias Hunger's avatar
Tobias Hunger committed
297
    return Utils::FileName::fromString(target->project()->projectDirectory()).appendPath(AndroidDirName);
BogDan Vatra's avatar
BogDan Vatra committed
298 299
}

Tobias Hunger's avatar
Tobias Hunger committed
300
Utils::FileName AndroidManager::manifestPath(ProjectExplorer::Target *target)
BogDan Vatra's avatar
BogDan Vatra committed
301
{
Tobias Hunger's avatar
Tobias Hunger committed
302
    return dirPath(target).appendPath(AndroidManifestName);
BogDan Vatra's avatar
BogDan Vatra committed
303 304
}

Tobias Hunger's avatar
Tobias Hunger committed
305
Utils::FileName AndroidManager::libsPath(ProjectExplorer::Target *target)
BogDan Vatra's avatar
BogDan Vatra committed
306
{
Tobias Hunger's avatar
Tobias Hunger committed
307 308
    return dirPath(target).appendPath(AndroidLibsFileName);
}
BogDan Vatra's avatar
BogDan Vatra committed
309

Tobias Hunger's avatar
Tobias Hunger committed
310 311 312
Utils::FileName AndroidManager::stringsPath(ProjectExplorer::Target *target)
{
    return dirPath(target).append(AndroidStringsFileName);
BogDan Vatra's avatar
BogDan Vatra committed
313 314
}

Tobias Hunger's avatar
Tobias Hunger committed
315
Utils::FileName AndroidManager::defaultPropertiesPath(ProjectExplorer::Target *target)
BogDan Vatra's avatar
BogDan Vatra committed
316
{
Tobias Hunger's avatar
Tobias Hunger committed
317 318
    return dirPath(target).appendPath(AndroidDefaultPropertiesName);
}
BogDan Vatra's avatar
BogDan Vatra committed
319

Tobias Hunger's avatar
Tobias Hunger committed
320 321 322 323
Utils::FileName AndroidManager::srcPath(ProjectExplorer::Target *target)
{
    return dirPath(target).appendPath(QLatin1String("/src"));
}
BogDan Vatra's avatar
BogDan Vatra committed
324

Tobias Hunger's avatar
Tobias Hunger committed
325 326 327 328 329 330 331 332 333 334 335 336
Utils::FileName AndroidManager::apkPath(ProjectExplorer::Target *target, BuildType buildType)
{
    return dirPath(target)
            .appendPath(QLatin1String("bin"))
            .appendPath(QString::fromLatin1("%1-%2.apk")
                        .arg(applicationName(target))
                        .arg(buildType == DebugBuild
                             ? QLatin1String("debug")
                             : (buildType == ReleaseBuildUnsigned)
                               ? QLatin1String("release-unsigned")
                               : QLatin1String("signed")));
}
BogDan Vatra's avatar
BogDan Vatra committed
337

Tobias Hunger's avatar
Tobias Hunger committed
338 339 340 341 342 343 344 345 346 347 348
QStringList AndroidManager::availableTargetApplications(ProjectExplorer::Target *target)
{
    QStringList apps;
    Qt4ProjectManager::Qt4Project *qt4Project = qobject_cast<Qt4ProjectManager::Qt4Project *>(target->project());
    foreach (Qt4ProjectManager::Qt4ProFileNode *proFile, qt4Project->applicationProFiles()) {
        if (proFile->projectType() == Qt4ProjectManager::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;
BogDan Vatra's avatar
BogDan Vatra committed
349 350
        }
    }
Tobias Hunger's avatar
Tobias Hunger committed
351 352 353
    apps.sort();
    return apps;
}
BogDan Vatra's avatar
BogDan Vatra committed
354

Tobias Hunger's avatar
Tobias Hunger committed
355 356 357 358 359 360 361 362 363 364 365 366 367
QString AndroidManager::targetApplication(ProjectExplorer::Target *target)
{
    QDomDocument doc;
    if (!openManifest(target, 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();
}
BogDan Vatra's avatar
BogDan Vatra committed
368

369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424
bool AndroidManager::setUseLocalLibs(ProjectExplorer::Target *target, bool useLocalLibs, int deviceAPILevel)
{
    // For Qt 4, the "use local libs" options is handled by passing command line arguments to the
    // app, so no need to alter the AndroidManifest.xml
    QtSupport::BaseQtVersion *baseQtVersion = QtSupport::QtKitInformation::qtVersion(target->kit());
    if (baseQtVersion == 0 || baseQtVersion->qtVersion() < QtSupport::QtVersionNumber(5,0,0))
        return true;

    QDomDocument doc;
    if (!openManifest(target, doc))
        return false;

    QDomElement metadataElem = doc.documentElement().firstChildElement(QLatin1String("application")).firstChildElement(QLatin1String("activity")).firstChildElement(QLatin1String("meta-data"));

    QString localLibs;
    QString localJars;
    QString staticInitClasses;
    if (useLocalLibs) {
        localLibs = loadLocalLibs(target, deviceAPILevel);
        localJars = loadLocalJars(target, deviceAPILevel);
        staticInitClasses = loadLocalJarsInitClasses(target, deviceAPILevel);
    }

    bool changedManifest = false;
    while (!metadataElem.isNull()) {
        if (metadataElem.attribute(QLatin1String("android:name")) == QLatin1String("android.app.use_local_qt_libs")) {
            if (metadataElem.attribute(QLatin1String("android:value")).toInt() != useLocalLibs) {
                metadataElem.setAttribute(QLatin1String("android:value"), int(useLocalLibs));
                changedManifest = true;
            }
        } else if (metadataElem.attribute(QLatin1String("android:name")) == QLatin1String("android.app.load_local_libs")) {
            if (metadataElem.attribute(QLatin1String("android:value")) != localLibs) {
                metadataElem.setAttribute(QLatin1String("android:value"), localLibs);
                changedManifest = true;
            }
        } else if (metadataElem.attribute(QLatin1String("android:name")) == QLatin1String("android.app.load_local_jars")) {
            if (metadataElem.attribute(QLatin1String("android:value")) != localJars) {
                metadataElem.setAttribute(QLatin1String("android:value"), localJars);
                changedManifest = true;
            }
        } else if (metadataElem.attribute(QLatin1String("android:name")) == QLatin1String("android.app.static_init_classes")) {
            if (metadataElem.attribute(QLatin1String("android:value")) != staticInitClasses) {
                metadataElem.setAttribute(QLatin1String("android:value"), staticInitClasses);
                changedManifest = true;
            }
        }

        metadataElem = metadataElem.nextSiblingElement(QLatin1String("meta-data"));
    }

    if (changedManifest)
        return saveManifest(target, doc);
    else
        return true;
}

Tobias Hunger's avatar
Tobias Hunger committed
425 426 427 428 429 430 431 432 433 434
bool AndroidManager::setTargetApplication(ProjectExplorer::Target *target, const QString &name)
{
    QDomDocument doc;
    if (!openManifest(target, 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 saveManifest(target, doc);
BogDan Vatra's avatar
BogDan Vatra committed
435
        }
Tobias Hunger's avatar
Tobias Hunger committed
436
        metadataElem = metadataElem.nextSiblingElement(QLatin1String("meta-data"));
BogDan Vatra's avatar
BogDan Vatra committed
437
    }
Tobias Hunger's avatar
Tobias Hunger committed
438
    return false;
BogDan Vatra's avatar
BogDan Vatra committed
439 440
}

Tobias Hunger's avatar
Tobias Hunger committed
441
QString AndroidManager::targetApplicationPath(ProjectExplorer::Target *target)
BogDan Vatra's avatar
BogDan Vatra committed
442
{
Tobias Hunger's avatar
Tobias Hunger committed
443 444 445 446 447 448 449 450 451 452
    QString selectedApp = targetApplication(target);
    if (selectedApp.isEmpty())
        return QString();
    Qt4ProjectManager::Qt4Project *qt4Project = qobject_cast<Qt4ProjectManager::Qt4Project *>(target->project());
    foreach (Qt4ProjectManager::Qt4ProFileNode *proFile, qt4Project->applicationProFiles()) {
        if (proFile->projectType() == Qt4ProjectManager::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)
453
                    return proFile->targetInformation().buildDir + QLatin1Char('/') + proFile->targetInformation().target;
Tobias Hunger's avatar
Tobias Hunger committed
454 455 456 457 458 459 460
            } else {
                if (proFile->targetInformation().target == selectedApp)
                    return proFile->targetInformation().buildDir + QLatin1String("/lib") + proFile->targetInformation().target + QLatin1String(".so");
            }
        }
    }
    return QString();
BogDan Vatra's avatar
BogDan Vatra committed
461 462
}

Tobias Hunger's avatar
Tobias Hunger committed
463
bool AndroidManager::createAndroidTemplatesIfNecessary(ProjectExplorer::Target *target)
BogDan Vatra's avatar
BogDan Vatra committed
464
{
Tobias Hunger's avatar
Tobias Hunger committed
465
    QtSupport::BaseQtVersion *version = QtSupport::QtKitInformation::qtVersion(target->kit());
Tobias Hunger's avatar
Tobias Hunger committed
466 467 468 469 470
    Qt4ProjectManager::Qt4Project *qt4Project = qobject_cast<Qt4ProjectManager::Qt4Project*>(target->project());
    if (!qt4Project || !qt4Project->rootProjectNode() || !version)
        return false;

    Utils::FileName javaSrcPath
471
            = Utils::FileName::fromString(version->qmakeProperty("QT_INSTALL_PREFIX"))
472
            .appendPath(QLatin1String("src/android/java"));
Tobias Hunger's avatar
Tobias Hunger committed
473 474 475 476 477 478
    QDir projectDir(qt4Project->projectDirectory());
    Utils::FileName androidPath = dirPath(target);

    QStringList m_ignoreFiles;
    bool forceUpdate = false;
    QDomDocument srcVersionDoc;
479 480
    Utils::FileName srcVersionPath = javaSrcPath;
    srcVersionPath.appendPath(QLatin1String("version.xml"));
481
    if (openXmlFile(srcVersionDoc, srcVersionPath)) {
Tobias Hunger's avatar
Tobias Hunger committed
482
        QDomDocument dstVersionDoc;
483 484
        Utils::FileName dstVersionPath=androidPath;
        dstVersionPath.appendPath(QLatin1String("version.xml"));
485
        if (openXmlFile(dstVersionDoc, dstVersionPath))
Tobias Hunger's avatar
Tobias Hunger committed
486 487 488 489 490 491 492 493 494 495 496 497 498 499
            forceUpdate = (srcVersionDoc.documentElement().attribute(QLatin1String("value")).toDouble()
                           > dstVersionDoc.documentElement().attribute(QLatin1String("value")).toDouble());
        else
            forceUpdate = true;

        if (forceUpdate && androidPath.toFileInfo().exists()) {
            QDomElement ignoreFile = srcVersionDoc.documentElement().firstChildElement(QLatin1String("ignore")).firstChildElement(QLatin1String("file"));
            while (!ignoreFile.isNull()) {
                m_ignoreFiles << ignoreFile.text();
                ignoreFile = ignoreFile.nextSiblingElement();
            }
        }
    }

BogDan Vatra's avatar
BogDan Vatra committed
500
    Utils::FileName src = androidPath;
501
    src.appendPath(QLatin1String("src"));
BogDan Vatra's avatar
BogDan Vatra committed
502
    Utils::FileName res = androidPath;
503
    res.appendPath(QLatin1String("res"));
BogDan Vatra's avatar
BogDan Vatra committed
504

Tobias Hunger's avatar
Tobias Hunger committed
505 506
    if (!forceUpdate && androidPath.toFileInfo().exists()
            && manifestPath(target).toFileInfo().exists()
BogDan Vatra's avatar
BogDan Vatra committed
507 508
            && src.toFileInfo().exists()
            && res.toFileInfo().exists())
Tobias Hunger's avatar
Tobias Hunger committed
509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525
        return true;

    forceUpdate &= androidPath.toFileInfo().exists();

    if (!dirPath(target).toFileInfo().exists() && !projectDir.mkdir(AndroidDirName)) {
        raiseError(tr("Error creating Android directory '%1'.").arg(AndroidDirName));
        return false;
    }

    QStringList androidFiles;
    QDirIterator it(javaSrcPath.toString(), QDirIterator::Subdirectories);
    int pos = it.path().size();
    while (it.hasNext()) {
        it.next();
        if (it.fileInfo().isDir()) {
            projectDir.mkpath(AndroidDirName + it.filePath().mid(pos));
        } else {
526 527
            Utils::FileName dstFile = androidPath;
            dstFile.appendPath(it.filePath().mid(pos));
Tobias Hunger's avatar
Tobias Hunger committed
528 529 530 531 532 533 534 535 536 537 538 539 540 541
            if (m_ignoreFiles.contains(it.fileName())) {
                continue;
            } else {
                if (dstFile.toFileInfo().exists())
                    QFile::remove(dstFile.toString());
                else
                    androidFiles << dstFile.toString();
            }
            QFile::copy(it.filePath(), dstFile.toString());
        }
    }
    if (!androidFiles.isEmpty())
        qt4Project->rootProjectNode()->addFiles(ProjectExplorer::UnknownFileType, androidFiles);

542 543 544 545 546 547
    int minApiLevel = 4;
    if (QtSupport::BaseQtVersion *qt = QtSupport::QtKitInformation::qtVersion(target->kit()))
        if (qt->qtVersion() >= QtSupport::QtVersionNumber(5, 0, 0))
            minApiLevel = 9;

    QStringList sdks = AndroidConfigurations::instance().sdkTargets(minApiLevel);
Tobias Hunger's avatar
Tobias Hunger committed
548 549 550 551
    if (sdks.isEmpty()) {
        raiseError(tr("No Qt for Android SDKs were found.\nPlease install at least one SDK."));
        return false;
    }
552 553

    updateTarget(target, AndroidConfigurations::instance().sdkTargets(minApiLevel).at(0));
Tobias Hunger's avatar
Tobias Hunger committed
554 555 556 557 558 559 560 561 562 563 564 565 566 567 568
    QStringList apps = availableTargetApplications(target);
    if (!apps.isEmpty())
        setTargetApplication(target, apps.at(0));

    QString applicationName = target->project()->displayName();
    if (!applicationName.isEmpty()) {
        setPackageName(target, packageName(target) + QLatin1Char('.') + applicationName);
        applicationName[0] = applicationName[0].toUpper();
        setApplicationName(target, applicationName);
    }

    if (forceUpdate)
        QMessageBox::warning(0, tr("Warning"), tr("Android files have been updated automatically"));

    return true;
BogDan Vatra's avatar
BogDan Vatra committed
569 570
}

Tobias Hunger's avatar
Tobias Hunger committed
571
void AndroidManager::updateTarget(ProjectExplorer::Target *target, const QString &targetSDK, const QString &name)
BogDan Vatra's avatar
BogDan Vatra committed
572
{
Tobias Hunger's avatar
Tobias Hunger committed
573
    QString androidDir = dirPath(target).toString();
BogDan Vatra's avatar
BogDan Vatra committed
574 575 576 577

    // clean previous build
    QProcess androidProc;
    androidProc.setWorkingDirectory(androidDir);
Tobias Hunger's avatar
Tobias Hunger committed
578 579
    androidProc.start(AndroidConfigurations::instance().antToolPath().toString(),
                      QStringList() << QLatin1String("clean"));
BogDan Vatra's avatar
BogDan Vatra committed
580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630
    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;
Tobias Hunger's avatar
Tobias Hunger committed
631
    androidProc.start(AndroidConfigurations::instance().androidToolPath().toString(), params);
BogDan Vatra's avatar
BogDan Vatra committed
632 633 634 635
    if (!androidProc.waitForFinished(-1))
        androidProc.terminate();
}

Tobias Hunger's avatar
Tobias Hunger committed
636
Utils::FileName AndroidManager::localLibsRulesFilePath(ProjectExplorer::Target *target)
BogDan Vatra's avatar
BogDan Vatra committed
637
{
Tobias Hunger's avatar
Tobias Hunger committed
638
    QtSupport::BaseQtVersion *version = QtSupport::QtKitInformation::qtVersion(target->kit());
Tobias Hunger's avatar
Tobias Hunger committed
639 640
    if (!version)
        return Utils::FileName();
641
    return Utils::FileName::fromString(version->qmakeProperty("QT_INSTALL_LIBS"));
Tobias Hunger's avatar
Tobias Hunger committed
642
}
BogDan Vatra's avatar
BogDan Vatra committed
643

Tobias Hunger's avatar
Tobias Hunger committed
644 645 646 647
QString AndroidManager::loadLocalLibs(ProjectExplorer::Target *target, int apiLevel)
{
    return loadLocal(target, apiLevel, Lib);
}
BogDan Vatra's avatar
BogDan Vatra committed
648

Tobias Hunger's avatar
Tobias Hunger committed
649 650 651 652
QString AndroidManager::loadLocalJars(ProjectExplorer::Target *target, int apiLevel)
{
    return loadLocal(target, apiLevel, Jar);
}
BogDan Vatra's avatar
BogDan Vatra committed
653

654 655 656 657 658
QString AndroidManager::loadLocalJarsInitClasses(ProjectExplorer::Target *target, int apiLevel)
{
    return loadLocal(target, apiLevel, Jar, QLatin1String("initClass"));
}

659
QVector<AndroidManager::Library> AndroidManager::availableQtLibsWithDependencies(ProjectExplorer::Target *target)
Tobias Hunger's avatar
Tobias Hunger committed
660
{
Tobias Hunger's avatar
Tobias Hunger committed
661
    QtSupport::BaseQtVersion *version = QtSupport::QtKitInformation::qtVersion(target->kit());
Tobias Hunger's avatar
Tobias Hunger committed
662
    if (!target->activeRunConfiguration())
663
        return QVector<AndroidManager::Library>();
Tobias Hunger's avatar
Tobias Hunger committed
664

665 666
    ProjectExplorer::ToolChain *tc = ProjectExplorer::ToolChainKitInformation::toolChain(target->kit());
    if (tc->type() != QLatin1String(Constants::ANDROID_TOOLCHAIN_TYPE))
667
        return QVector<AndroidManager::Library>();
668 669 670 671

    Qt4ProjectManager::Qt4Project *project = static_cast<Qt4ProjectManager::Qt4Project *>(target->project());
    QString arch = project->rootQt4ProjectNode()->singleVariableValue(Qt4ProjectManager::AndroidArchVar);

672
    AndroidToolChain *atc = static_cast<AndroidToolChain *>(tc);
673
    QString libgnustl = libGnuStl(arch, atc->ndkToolChainVersion());
674 675 676

    Utils::FileName readelfPath = AndroidConfigurations::instance().readelfPath(target->activeRunConfiguration()->abi().architecture(),
                                                                                atc->ndkToolChainVersion());
Tobias Hunger's avatar
Tobias Hunger committed
677 678 679
    const Qt4ProjectManager::Qt4Project *const qt4Project
            = qobject_cast<const Qt4ProjectManager::Qt4Project *>(target->project());
    if (!qt4Project || !version)
680
        return QVector<AndroidManager::Library>();
681
    QString qtLibsPath = version->qmakeProperty("QT_INSTALL_LIBS");
Tobias Hunger's avatar
Tobias Hunger committed
682
    if (!readelfPath.toFileInfo().exists()) {
683
        return QVector<AndroidManager::Library>();
BogDan Vatra's avatar
BogDan Vatra committed
684
    }
Tobias Hunger's avatar
Tobias Hunger committed
685 686 687 688 689 690 691
    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 = dependencies(readelfPath, libPath.absolutePath());
BogDan Vatra's avatar
BogDan Vatra committed
692 693
    }

694 695 696
    const QString library = libgnustl.mid(libgnustl.lastIndexOf(QLatin1Char('/')) + 1);
    mapLibs[library] = Library();;

Tobias Hunger's avatar
Tobias Hunger committed
697 698 699 700 701
    // clean dependencies
    foreach (const QString &key, mapLibs.keys()) {
        int it = 0;
        while (it < mapLibs[key].dependencies.size()) {
            const QString &dependName = mapLibs[key].dependencies[it];
702
            if (!mapLibs.keys().contains(dependName) && dependName.startsWith(QLatin1String("lib")) && dependName.endsWith(QLatin1String(".so")))
Tobias Hunger's avatar
Tobias Hunger committed
703
                mapLibs[key].dependencies.removeAt(it);
704
            else
Tobias Hunger's avatar
Tobias Hunger committed
705
                ++it;
BogDan Vatra's avatar
BogDan Vatra committed
706
        }
Tobias Hunger's avatar
Tobias Hunger committed
707 708
        if (!mapLibs[key].dependencies.size())
            mapLibs[key].level = 0;
BogDan Vatra's avatar
BogDan Vatra committed
709 710
    }

Tobias Hunger's avatar
Tobias Hunger committed
711 712 713 714 715
    QVector<Library> qtLibraries;
    // calculate the level for every library
    foreach (const QString &key, mapLibs.keys()) {
        if (mapLibs[key].level < 0)
           setLibraryLevel(key, mapLibs);
BogDan Vatra's avatar
BogDan Vatra committed
716

Tobias Hunger's avatar
Tobias Hunger committed
717 718
        if (!mapLibs[key].name.length() && key.startsWith(QLatin1String("lib")) && key.endsWith(QLatin1String(".so")))
            mapLibs[key].name = key.mid(3, key.length() - 6);
BogDan Vatra's avatar
BogDan Vatra committed
719

Tobias Hunger's avatar
Tobias Hunger committed
720 721 722 723 724 725
        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]);
BogDan Vatra's avatar
BogDan Vatra committed
726
    }
Tobias Hunger's avatar
Tobias Hunger committed
727
    qSort(qtLibraries.begin(), qtLibraries.end(), qtLibrariesLessThan);
728 729 730 731 732 733 734 735 736 737

    return qtLibraries;

}

QStringList AndroidManager::availableQtLibs(ProjectExplorer::Target *target)
{
    QStringList libs;
    QVector<Library> qtLibraries = availableQtLibsWithDependencies(target);
    foreach (Library lib, qtLibraries)
Tobias Hunger's avatar
Tobias Hunger committed
738 739
        libs.push_back(lib.name);
    return libs;
BogDan Vatra's avatar
BogDan Vatra committed
740 741
}

Tobias Hunger's avatar
Tobias Hunger committed
742
QStringList AndroidManager::qtLibs(ProjectExplorer::Target *target)
BogDan Vatra's avatar
BogDan Vatra committed
743
{
Tobias Hunger's avatar
Tobias Hunger committed
744
    return libsXml(target, QLatin1String("qt_libs"));
BogDan Vatra's avatar
BogDan Vatra committed
745 746
}

Tobias Hunger's avatar
Tobias Hunger committed
747
bool AndroidManager::setQtLibs(ProjectExplorer::Target *target, const QStringList &libs)
BogDan Vatra's avatar
BogDan Vatra committed
748
{
Tobias Hunger's avatar
Tobias Hunger committed
749
    return setLibsXml(target, libs, QLatin1String("qt_libs"));
BogDan Vatra's avatar
BogDan Vatra committed
750 751
}

Tobias Hunger's avatar
Tobias Hunger committed
752
QStringList AndroidManager::availablePrebundledLibs(ProjectExplorer::Target *target)
BogDan Vatra's avatar
BogDan Vatra committed
753
{
Tobias Hunger's avatar
Tobias Hunger committed
754 755 756 757
    QStringList libs;
    Qt4ProjectManager::Qt4Project *qt4Project = qobject_cast<Qt4ProjectManager::Qt4Project *>(target->project());
    if (!qt4Project)
        return libs;
BogDan Vatra's avatar
BogDan Vatra committed
758

Tobias Hunger's avatar
Tobias Hunger committed
759 760 761 762
    foreach (Qt4ProjectManager::Qt4ProFileNode *node, qt4Project->allProFiles())
        if (node->projectType() == Qt4ProjectManager::LibraryTemplate)
            libs << QLatin1String("lib") + node->targetInformation().target + QLatin1String(".so");
    return libs;
BogDan Vatra's avatar
BogDan Vatra committed
763 764
}

Tobias Hunger's avatar
Tobias Hunger committed
765
QStringList AndroidManager::prebundledLibs(ProjectExplorer::Target *target)
BogDan Vatra's avatar
BogDan Vatra committed
766
{
Tobias Hunger's avatar
Tobias Hunger committed
767
    return libsXml(target, QLatin1String("bundled_libs"));
BogDan Vatra's avatar
BogDan Vatra committed
768 769
}

Tobias Hunger's avatar
Tobias Hunger committed
770
bool AndroidManager::setPrebundledLibs(ProjectExplorer::Target *target, const QStringList &libs)
BogDan Vatra's avatar
BogDan Vatra committed
771
{
Tobias Hunger's avatar
Tobias Hunger committed
772
    return setLibsXml(target, libs, QLatin1String("bundled_libs"));
BogDan Vatra's avatar
BogDan Vatra committed
773 774
}

Tobias Hunger's avatar
Tobias Hunger committed
775
bool AndroidManager::openLibsXml(ProjectExplorer::Target *target, QDomDocument &doc)
BogDan Vatra's avatar
BogDan Vatra committed
776
{
777
    return openXmlFile(doc, libsPath(target));
BogDan Vatra's avatar
BogDan Vatra committed
778 779
}

Tobias Hunger's avatar
Tobias Hunger committed
780
bool AndroidManager::saveLibsXml(ProjectExplorer::Target *target, QDomDocument &doc)
BogDan Vatra's avatar
BogDan Vatra committed
781
{
Tobias Hunger's avatar
Tobias Hunger committed
782
    return saveXmlFile(target, doc, libsPath(target));
BogDan Vatra's avatar
BogDan Vatra committed
783 784
}

Tobias Hunger's avatar
Tobias Hunger committed
785
void AndroidManager::raiseError(const QString &reason)
BogDan Vatra's avatar
BogDan Vatra committed
786
{
Tobias Hunger's avatar
Tobias Hunger committed
787
    QMessageBox::critical(0, tr("Error creating Android templates"), reason);
BogDan Vatra's avatar
BogDan Vatra committed
788 789
}

790
QString AndroidManager::loadLocal(ProjectExplorer::Target *target, int apiLevel, ItemType item, const QString &attribute)
BogDan Vatra's avatar
BogDan Vatra committed
791
{
Tobias Hunger's avatar
Tobias Hunger committed
792 793 794 795 796 797 798
    QString itemType;
    if (item == Lib)
        itemType = QLatin1String("lib");
    else
        itemType = QLatin1String("jar");

    QString localLibs;
BogDan Vatra's avatar
BogDan Vatra committed
799

800 801
    QDir rulesFilesDir(localLibsRulesFilePath(target).toString());
    if (!rulesFilesDir.exists())
Tobias Hunger's avatar
Tobias Hunger committed
802 803 804 805
        return localLibs;

    QStringList libs;
    libs << qtLibs(target) << prebundledLibs(target);
806 807 808 809 810 811 812 813 814 815

    QFileInfoList rulesFiles = rulesFilesDir.entryInfoList(QStringList() << QLatin1String("*.xml"),
                                                           QDir::Files | QDir::Readable);

    QStringList dependencyLibs;
    QStringList replacedLibs;
    foreach (QFileInfo rulesFile, rulesFiles) {
        if (rulesFile.baseName() != QLatin1String("rules")
                && !rulesFile.baseName().endsWith(QLatin1String("-android-dependencies"))) {
            continue;
BogDan Vatra's avatar
BogDan Vatra committed
816 817
        }

818 819 820 821 822 823 824 825 826 827
        QDomDocument doc;
        if (!openXmlFile(doc, Utils::FileName::fromString(rulesFile.absoluteFilePath())))
            return localLibs;

        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;
Tobias Hunger's avatar
Tobias Hunger committed
828
            }
829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858
            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()) {
                    if (libElement.hasAttribute(attribute)) {
                        QString dependencyLib = libElement.attribute(attribute).arg(apiLevel);
                        if (!dependencyLibs.contains(dependencyLib))
                            dependencyLibs << dependencyLib;
                    }

                    if (libElement.hasAttribute(QLatin1String("replaces"))) {
                        QString replacedLib = libElement.attribute(QLatin1String("replaces")).arg(apiLevel);
                        if (!replacedLibs.contains(replacedLib))
                            replacedLibs << replacedLib;
                    }

                    libElement = libElement.nextSiblingElement(itemType);
                }

                libElement = element.firstChildElement(QLatin1String("replaces")).firstChildElement(itemType);
                while (!libElement.isNull()) {
                    if (libElement.hasAttribute(attribute)) {
                        QString replacedLib = libElement.attribute(attribute).arg(apiLevel);
                        if (!replacedLibs.contains(replacedLib))
                            replacedLibs << replacedLib;
                    }
Tobias Hunger's avatar
Tobias Hunger committed
859

860 861
                    libElement = libElement.nextSiblingElement(itemType);
                }
BogDan Vatra's avatar
BogDan Vatra committed
862
            }
863
            element = element.nextSiblingElement(QLatin1String("lib"));
BogDan Vatra's avatar
BogDan Vatra committed
864 865
        }
    }
866 867 868

    // The next loop requires all library names to end with a ":" so we append one
    // to the end after joining.
Robert Loehning's avatar
Robert Loehning committed
869
    localLibs = dependencyLibs.join(QLatin1String(":")) + QLatin1Char(':');
870 871 872
    foreach (QString replacedLib, replacedLibs)
        localLibs.remove(replacedLib + QLatin1Char(':'));

Tobias Hunger's avatar
Tobias Hunger committed
873
    return localLibs;
BogDan Vatra's avatar
BogDan Vatra committed
874 875
}

876
bool AndroidManager::openXmlFile(QDomDocument &doc, const Utils::FileName &fileName)
BogDan Vatra's avatar
BogDan Vatra committed
877
{
Tobias Hunger's avatar
Tobias Hunger committed
878 879
    QFile f(fileName.toString());
    if (!f.open(QIODevice::ReadOnly))
BogDan Vatra's avatar
BogDan Vatra committed
880 881
        return false;

Tobias Hunger's avatar
Tobias Hunger committed
882 883
    if (!doc.setContent(f.readAll())) {
        raiseError(tr("Can't parse '%1'").arg(fileName.toUserOutput()));
BogDan Vatra's avatar
BogDan Vatra committed
884 885
        return false;
    }
Tobias Hunger's avatar
Tobias Hunger committed
886
    return true;
BogDan Vatra's avatar
BogDan Vatra committed
887 888
}

Tobias Hunger's avatar
Tobias Hunger committed
889
bool AndroidManager::saveXmlFile(ProjectExplorer::Target *target, QDomDocument &doc, const Utils::FileName &fileName)
BogDan Vatra's avatar
BogDan Vatra committed
890
{
Tobias Hunger's avatar
Tobias Hunger committed
891 892
    if (!createAndroidTemplatesIfNecessary(target))
        return false;
BogDan Vatra's avatar
BogDan Vatra committed
893

Tobias Hunger's avatar
Tobias Hunger committed
894 895 896 897
    QFile f(fileName.toString());
    if (!f.open(QIODevice::WriteOnly)) {
        raiseError(tr("Can't open '%1'").arg(fileName.toUserOutput()));
        return false;
BogDan Vatra's avatar
BogDan Vatra committed
898
    }
Tobias Hunger's avatar
Tobias Hunger committed
899
    return f.write(doc.toByteArray(4)) >= 0;
BogDan Vatra's avatar
BogDan Vatra committed
900 901
}

Tobias Hunger's avatar
Tobias Hunger committed
902
bool AndroidManager::openManifest(ProjectExplorer::Target *target, QDomDocument &doc)
BogDan Vatra's avatar
BogDan Vatra committed
903
{
904
    return openXmlFile(doc, manifestPath(target));
BogDan Vatra's avatar
BogDan Vatra committed
905 906
}

Tobias Hunger's avatar
Tobias Hunger committed
907
bool AndroidManager::saveManifest(ProjectExplorer::Target *target, QDomDocument &doc)
BogDan Vatra's avatar
BogDan Vatra committed
908
{
Tobias Hunger's avatar
Tobias Hunger committed
909
    return saveXmlFile(target, doc, manifestPath(target));
BogDan Vatra's avatar
BogDan Vatra committed
910 911
}

Tobias Hunger's avatar
Tobias Hunger committed
912
QString AndroidManager::iconPath(ProjectExplorer::Target *target, AndroidManager::IconType type)
BogDan Vatra's avatar
BogDan Vatra committed
913 914 915
{
    switch (type) {
    case HighDPI:
Tobias Hunger's avatar
Tobias Hunger committed
916
        return dirPath(target).appendPath(QLatin1String("res/drawable-hdpi/icon.png")).toString();
BogDan Vatra's avatar
BogDan Vatra committed
917
    case MediumDPI:
Tobias Hunger's avatar
Tobias Hunger committed
918
        return dirPath(target).appendPath(QLatin1String("res/drawable-mdpi/icon.png")).toString();
BogDan Vatra's avatar
BogDan Vatra committed
919
    case LowDPI:
Tobias Hunger's avatar
Tobias Hunger committed
920 921 922
        return dirPath(target).appendPath(QLatin1String("res/drawable-ldpi/icon.png")).toString();
    default:
        return QString();
BogDan Vatra's avatar
BogDan Vatra committed
923 924 925
    }
}

Tobias Hunger's avatar
Tobias Hunger committed
926
QStringList AndroidManager::libsXml(ProjectExplorer::Target *target, const QString &tag)
BogDan Vatra's avatar
BogDan Vatra committed
927 928 929
{
    QStringList libs;
    QDomDocument doc;
Tobias Hunger's avatar
Tobias Hunger committed
930
    if (!openLibsXml(target, doc))
BogDan Vatra's avatar
BogDan Vatra committed
931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946
        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;
}

Tobias Hunger's avatar
Tobias Hunger committed
947
bool AndroidManager::setLibsXml(ProjectExplorer::Target *target, const QStringList &libs, const QString &tag)
BogDan Vatra's avatar
BogDan Vatra committed
948 949
{
    QDomDocument doc;
Tobias Hunger's avatar
Tobias Hunger committed
950
    if (!openLibsXml(target, doc))
BogDan Vatra's avatar
BogDan Vatra committed
951 952 953 954 955 956 957 958 959 960 961 962 963
        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);
Tobias Hunger's avatar
Tobias Hunger committed
964
            return saveLibsXml(target, doc);
BogDan Vatra's avatar
BogDan Vatra committed
965 966 967 968 969 970 971
        }
        arrayElem = arrayElem.nextSiblingElement(QLatin1String("array"));
    }
    return false;
}


Tobias Hunger's avatar
Tobias Hunger committed
972
QIcon AndroidManager::icon(ProjectExplorer::Target *target, IconType type)
BogDan Vatra's avatar
BogDan Vatra committed
973
{
Tobias Hunger's avatar
Tobias Hunger committed
974
    return QIcon(iconPath(target, type));
BogDan Vatra's avatar
BogDan Vatra committed
975 976
}

Tobias Hunger's avatar
Tobias Hunger committed
977
bool AndroidManager::setIcon(ProjectExplorer::Target *target, IconType type, const QString &iconFileName)
BogDan Vatra's avatar
BogDan Vatra committed
978
{
Tobias Hunger's avatar
Tobias Hunger committed
979 980
    if (!QFileInfo(iconFileName).exists())
        return false;
BogDan Vatra's avatar
BogDan Vatra committed
981

Tobias Hunger's avatar
Tobias Hunger committed
982 983
    const QString path = iconPath(target, type);
    QFile::remove(path);
Daniel Teske's avatar
Daniel Teske committed
984 985
    QDir dir;
    dir.<