androidmanager.cpp 52.9 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"
38
#include "androiddeployqtstep.h"
BogDan Vatra's avatar
BogDan Vatra committed
39

40
#include <coreplugin/documentmanager.h>
41
#include <coreplugin/messagemanager.h>
Tobias Hunger's avatar
Tobias Hunger committed
42 43 44
#include <projectexplorer/projectexplorer.h>
#include <projectexplorer/session.h>
#include <projectexplorer/target.h>
BogDan Vatra's avatar
BogDan Vatra committed
45 46 47 48
#include <qt4projectmanager/qt4nodes.h>
#include <qt4projectmanager/qt4project.h>
#include <qt4projectmanager/qt4projectmanagerconstants.h>
#include <qt4projectmanager/qt4buildconfiguration.h>
49
#include <qtsupport/customexecutablerunconfiguration.h>
Tobias Hunger's avatar
Tobias Hunger committed
50
#include <qtsupport/qtkitinformation.h>
Tobias Hunger's avatar
Tobias Hunger committed
51
#include <qtsupport/qtsupportconstants.h>
BogDan Vatra's avatar
BogDan Vatra committed
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");
67
    const QLatin1String AndroidLibraryPrefix("--Managed_by_Qt_Creator--");
BogDan Vatra's avatar
BogDan Vatra committed
68 69 70

    QString cleanPackageName(QString packageName)
    {
71
        QRegExp legalChars(QLatin1String("[a-zA-Z0-9_\\.]"));
BogDan Vatra's avatar
BogDan Vatra committed
72 73 74 75 76

        for (int i = 0; i < packageName.length(); ++i)
            if (!legalChars.exactMatch(packageName.mid(i, 1)))
                packageName[i] = QLatin1Char('_');

77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120
        static QStringList keywords;
        if (keywords.isEmpty())
            keywords << QLatin1String("abstract") << QLatin1String("continue") << QLatin1String("for")
                     << QLatin1String("new") << QLatin1String("switch") << QLatin1String("assert")
                     << QLatin1String("default") << QLatin1String("if") << QLatin1String("package")
                     << QLatin1String("synchronized") << QLatin1String("boolean") << QLatin1String("do")
                     << QLatin1String("goto") << QLatin1String("private") << QLatin1String("this")
                     << QLatin1String("break") << QLatin1String("double") << QLatin1String("implements")
                     << QLatin1String("protected") << QLatin1String("throw") << QLatin1String("byte")
                     << QLatin1String("else") << QLatin1String("import") << QLatin1String("public")
                     << QLatin1String("throws") << QLatin1String("case") << QLatin1String("enum")
                     << QLatin1String("instanceof") << QLatin1String("return") << QLatin1String("transient")
                     << QLatin1String("catch") << QLatin1String("extends") << QLatin1String("int")
                     << QLatin1String("short") << QLatin1String("try") << QLatin1String("char")
                     << QLatin1String("final") << QLatin1String("interface") << QLatin1String("static")
                     << QLatin1String("void") << QLatin1String("class") << QLatin1String("finally")
                     << QLatin1String("long") << QLatin1String("strictfp") << QLatin1String("volatile")
                     << QLatin1String("const") << QLatin1String("float") << QLatin1String("native")
                     << QLatin1String("super") << QLatin1String("while");

        // No keywords
        int index = -1;
        while (index != packageName.length()) {
            int next = packageName.indexOf(QLatin1Char('.'), index + 1);
            if (next == -1)
                next = packageName.length();
            QString word = packageName.mid(index + 1, next - index - 1);
            if (!word.isEmpty()) {
                QChar c = word[0];
                if (c >= QChar(QLatin1Char('0')) && c<= QChar(QLatin1Char('9'))) {
                    packageName.insert(index + 1, QLatin1Char('_'));
                    index = next + 1;
                    continue;
                }
            }
            if (keywords.contains(word)) {
                packageName.insert(next, QLatin1String("_"));
                index = next + 1;
            } else {
                index = next;
            }
        }


BogDan Vatra's avatar
BogDan Vatra committed
121 122 123 124 125 126 127
        return packageName;
    }
} // anonymous namespace

namespace Android {
namespace Internal {

Tobias Hunger's avatar
Tobias Hunger committed
128
bool AndroidManager::supportsAndroid(ProjectExplorer::Target *target)
BogDan Vatra's avatar
BogDan Vatra committed
129
{
130
    if (!qobject_cast<QmakeProjectManager::Qt4Project *>(target->project()))
Tobias Hunger's avatar
Tobias Hunger committed
131
        return false;
Tobias Hunger's avatar
Tobias Hunger committed
132
    QtSupport::BaseQtVersion *version = QtSupport::QtKitInformation::qtVersion(target->kit());
133
    return version && version->platformName() == QLatin1String(QtSupport::Constants::ANDROID_PLATFORM);
Tobias Hunger's avatar
Tobias Hunger committed
134
}
BogDan Vatra's avatar
BogDan Vatra committed
135

Tobias Hunger's avatar
Tobias Hunger committed
136 137 138 139 140 141 142
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
143 144
}

Tobias Hunger's avatar
Tobias Hunger committed
145
bool AndroidManager::setPackageName(ProjectExplorer::Target *target, const QString &name)
BogDan Vatra's avatar
BogDan Vatra committed
146
{
Tobias Hunger's avatar
Tobias Hunger committed
147 148 149 150 151 152
    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
153 154
}

Tobias Hunger's avatar
Tobias Hunger committed
155
QString AndroidManager::applicationName(ProjectExplorer::Target *target)
BogDan Vatra's avatar
BogDan Vatra committed
156
{
Tobias Hunger's avatar
Tobias Hunger committed
157
    QDomDocument doc;
158
    if (!openXmlFile(doc, stringsPath(target)))
Tobias Hunger's avatar
Tobias Hunger committed
159 160 161 162 163 164 165 166 167
        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
168

Tobias Hunger's avatar
Tobias Hunger committed
169 170 171 172
bool AndroidManager::setApplicationName(ProjectExplorer::Target *target, const QString &name)
{
    QDomDocument doc;
    Utils::FileName path = stringsPath(target);
173
    if (!openXmlFile(doc, path))
Tobias Hunger's avatar
Tobias Hunger committed
174 175 176 177 178 179 180 181 182 183 184 185
        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
186

Tobias Hunger's avatar
Tobias Hunger committed
187
QString AndroidManager::intentName(ProjectExplorer::Target *target)
BogDan Vatra's avatar
BogDan Vatra committed
188
{
Tobias Hunger's avatar
Tobias Hunger committed
189
    return packageName(target) + QLatin1Char('/') + activityName(target);
BogDan Vatra's avatar
BogDan Vatra committed
190 191
}

Tobias Hunger's avatar
Tobias Hunger committed
192
QString AndroidManager::activityName(ProjectExplorer::Target *target)
BogDan Vatra's avatar
BogDan Vatra committed
193
{
Tobias Hunger's avatar
Tobias Hunger committed
194 195 196 197 198
    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
199 200
}

201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218
int AndroidManager::minimumSDK(ProjectExplorer::Target *target)
{
    QDomDocument doc;
    if (!openManifest(target, doc))
        return 0;
    QDomElement manifestElem = doc.documentElement();
    QDomElement usesSdk = manifestElem.firstChildElement(QLatin1String("uses-sdk"));
    if (usesSdk.isNull())
        return 0;
    if (usesSdk.hasAttribute(QLatin1String("android:minSdkVersion"))) {
        bool ok;
        int tmp = usesSdk.attribute(QLatin1String("android:minSdkVersion")).toInt(&ok);
        if (ok)
            return tmp;
    }
    return 0;
}

219
QString AndroidManager::buildTargetSDK(ProjectExplorer::Target *target)
BogDan Vatra's avatar
BogDan Vatra committed
220
{
221 222 223 224 225 226 227 228 229 230
    QtSupport::BaseQtVersion *qt = QtSupport::QtKitInformation::qtVersion(target->kit());
    if (qt && qt->qtVersion() >= QtSupport::QtVersionNumber(5, 2, 0)) {
        if (!target->activeDeployConfiguration())
            return QLatin1String("android-9");
        AndroidDeployQtStep *step = AndroidGlobal::buildStep<AndroidDeployQtStep>(target->activeDeployConfiguration());
        if (step)
            return step->buildTargetSdk();
        return QLatin1String("android-9");
    }

231 232 233 234
    QVariant v = target->namedSettings(QLatin1String("AndroidManager.TargetSdk"));
    if (v.isValid())
        return v.toString();

235
    QString fallback = QLatin1String("android-8");
236 237
    if (qt && qt->qtVersion() >= QtSupport::QtVersionNumber(5, 0, 0))
        fallback = QLatin1String("android-9");
238

Tobias Hunger's avatar
Tobias Hunger committed
239
    if (!createAndroidTemplatesIfNecessary(target))
240
        return AndroidConfigurations::instance().bestMatch(fallback);
241

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

253
bool AndroidManager::setBuildTargetSDK(ProjectExplorer::Target *target, const QString &sdk)
Tobias Hunger's avatar
Tobias Hunger committed
254 255
{
    updateTarget(target, sdk, applicationName(target));
256
    target->setNamedSettings(QLatin1String("AndroidManager.TargetSdk"), sdk);
Tobias Hunger's avatar
Tobias Hunger committed
257 258
    return true;
}
BogDan Vatra's avatar
BogDan Vatra committed
259

260 261
QString AndroidManager::targetArch(ProjectExplorer::Target *target)
{
262
    QmakeProjectManager::Qt4Project *pro = qobject_cast<QmakeProjectManager::Qt4Project *>(target->project());
263 264
    if (!pro)
        return QString();
265
    QmakeProjectManager::Qt4ProFileNode *node = pro->rootQt4ProjectNode();
266 267
    if (!node)
        return QString();
268
    return node->singleVariableValue(QmakeProjectManager::AndroidArchVar);
269 270
}

Tobias Hunger's avatar
Tobias Hunger committed
271
Utils::FileName AndroidManager::dirPath(ProjectExplorer::Target *target)
BogDan Vatra's avatar
BogDan Vatra committed
272
{
273 274 275
    QtSupport::BaseQtVersion *qtVersion = QtSupport::QtKitInformation::qtVersion(target->kit());
    if (qtVersion && qtVersion->qtVersion() >= QtSupport::QtVersionNumber(5, 2, 0))
        return target->activeBuildConfiguration()->buildDirectory().appendPath(AndroidDirName);
Tobias Hunger's avatar
Tobias Hunger committed
276
    return Utils::FileName::fromString(target->project()->projectDirectory()).appendPath(AndroidDirName);
BogDan Vatra's avatar
BogDan Vatra committed
277 278
}

Tobias Hunger's avatar
Tobias Hunger committed
279
Utils::FileName AndroidManager::manifestPath(ProjectExplorer::Target *target)
BogDan Vatra's avatar
BogDan Vatra committed
280
{
Tobias Hunger's avatar
Tobias Hunger committed
281
    return dirPath(target).appendPath(AndroidManifestName);
BogDan Vatra's avatar
BogDan Vatra committed
282 283
}

Tobias Hunger's avatar
Tobias Hunger committed
284
Utils::FileName AndroidManager::libsPath(ProjectExplorer::Target *target)
BogDan Vatra's avatar
BogDan Vatra committed
285
{
Tobias Hunger's avatar
Tobias Hunger committed
286 287
    return dirPath(target).appendPath(AndroidLibsFileName);
}
BogDan Vatra's avatar
BogDan Vatra committed
288

Tobias Hunger's avatar
Tobias Hunger committed
289 290 291
Utils::FileName AndroidManager::stringsPath(ProjectExplorer::Target *target)
{
    return dirPath(target).append(AndroidStringsFileName);
BogDan Vatra's avatar
BogDan Vatra committed
292 293
}

Tobias Hunger's avatar
Tobias Hunger committed
294
Utils::FileName AndroidManager::defaultPropertiesPath(ProjectExplorer::Target *target)
BogDan Vatra's avatar
BogDan Vatra committed
295
{
Tobias Hunger's avatar
Tobias Hunger committed
296 297
    return dirPath(target).appendPath(AndroidDefaultPropertiesName);
}
BogDan Vatra's avatar
BogDan Vatra committed
298

Tobias Hunger's avatar
Tobias Hunger committed
299 300 301 302
Utils::FileName AndroidManager::srcPath(ProjectExplorer::Target *target)
{
    return dirPath(target).appendPath(QLatin1String("/src"));
}
BogDan Vatra's avatar
BogDan Vatra committed
303

Tobias Hunger's avatar
Tobias Hunger committed
304 305
Utils::FileName AndroidManager::apkPath(ProjectExplorer::Target *target, BuildType buildType)
{
306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322
    QString packageName = QLatin1String("QtApp");
    QString buildTypeName;
    if (buildType == DebugBuild)
        buildTypeName = QLatin1String("debug");
    else if (buildType == ReleaseBuildUnsigned)
        buildTypeName =QLatin1String("release-unsigned");
    else
        buildTypeName = QLatin1String("release");

    QtSupport::BaseQtVersion *qt = QtSupport::QtKitInformation::qtVersion(target->kit());
    if (!qt || qt->qtVersion() < QtSupport::QtVersionNumber(5, 2, 0)) {
        // Qt 5.1 and earlier:
        packageName = applicationName(target);
        if (buildType == ReleaseBuildSigned)
            buildTypeName = QLatin1String("signed");
    }

Tobias Hunger's avatar
Tobias Hunger committed
323 324 325
    return dirPath(target)
            .appendPath(QLatin1String("bin"))
            .appendPath(QString::fromLatin1("%1-%2.apk")
326 327
                        .arg(packageName)
                        .arg(buildTypeName));
Tobias Hunger's avatar
Tobias Hunger committed
328
}
BogDan Vatra's avatar
BogDan Vatra committed
329

Tobias Hunger's avatar
Tobias Hunger committed
330 331 332
QStringList AndroidManager::availableTargetApplications(ProjectExplorer::Target *target)
{
    QStringList apps;
333
    QmakeProjectManager::Qt4Project *qt4Project = qobject_cast<QmakeProjectManager::Qt4Project *>(target->project());
334 335
    if (!qt4Project)
        return apps;
336 337
    foreach (QmakeProjectManager::Qt4ProFileNode *proFile, qt4Project->applicationProFiles()) {
        if (proFile->projectType() == QmakeProjectManager::ApplicationTemplate) {
Tobias Hunger's avatar
Tobias Hunger committed
338 339 340 341 342
            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
343 344
        }
    }
Tobias Hunger's avatar
Tobias Hunger committed
345 346 347
    apps.sort();
    return apps;
}
BogDan Vatra's avatar
BogDan Vatra committed
348

Tobias Hunger's avatar
Tobias Hunger committed
349 350 351 352 353 354 355 356 357 358 359 360 361
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
362

363 364 365
// Note, this could be implemented via a base class and a couple of virtuals
// but I intend to remove the indirection once we drop support for qt 4.8
// and qt 5.1.
366 367
bool AndroidManager::bundleQt(ProjectExplorer::Target *target)
{
368 369 370 371 372 373 374 375 376 377
    AndroidDeployStep *androidDeployStep
        = AndroidGlobal::buildStep<AndroidDeployStep>(target->activeDeployConfiguration());
    if (androidDeployStep) {
        return androidDeployStep->deployAction() == AndroidDeployStep::BundleLibraries;
    }

    AndroidDeployQtStep *androidDeployQtStep
            = AndroidGlobal::buildStep<AndroidDeployQtStep>(target->activeDeployConfiguration());
    if (androidDeployQtStep) {
        return androidDeployQtStep->deployAction() == AndroidDeployQtStep::BundleLibrariesDeployment;
378 379 380 381 382
    }

    return false;
}

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
bool AndroidManager::useLocalLibs(ProjectExplorer::Target *target)
{
    AndroidDeployStep *androidDeployStep
        = AndroidGlobal::buildStep<AndroidDeployStep>(target->activeDeployConfiguration());
    if (androidDeployStep) {
        return androidDeployStep->deployAction() == AndroidDeployStep::DeployLocal
                || androidDeployStep->deployAction() == AndroidDeployStep::BundleLibraries;
    }

    AndroidDeployQtStep *androidDeployQtStep
            = AndroidGlobal::buildStep<AndroidDeployQtStep>(target->activeDeployConfiguration());
    if (androidDeployQtStep) {
        return androidDeployQtStep->deployAction() == AndroidDeployQtStep::DebugDeployment
                || androidDeployQtStep->deployAction() == AndroidDeployQtStep::BundleLibrariesDeployment;
    }

    return false;
}

QString AndroidManager::deviceSerialNumber(ProjectExplorer::Target *target)
{
    AndroidDeployStep *androidDeployStep
        = AndroidGlobal::buildStep<AndroidDeployStep>(target->activeDeployConfiguration());
    if (androidDeployStep)
        return androidDeployStep->deviceSerialNumber();

    AndroidDeployQtStep *androidDeployQtStep
            = AndroidGlobal::buildStep<AndroidDeployQtStep>(target->activeDeployConfiguration());
    if (androidDeployQtStep)
        return androidDeployQtStep->deviceSerialNumber();
    return QString();
}

416
bool AndroidManager::updateDeploymentSettings(ProjectExplorer::Target *target)
417 418 419 420 421 422 423
{
    // 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;

424 425 426
    if (baseQtVersion->qtVersion() >= QtSupport::QtVersionNumber(5, 2, 0))
        return true;

427 428 429 430 431
    ProjectExplorer::RunConfiguration *runConfiguration = target->activeRunConfiguration();
    AndroidRunConfiguration *androidRunConfiguration = qobject_cast<AndroidRunConfiguration *>(runConfiguration);
    if (androidRunConfiguration == 0)
        return false;

432 433
    bool useLocalLibs = AndroidManager::useLocalLibs(target);
    bool bundleQtLibs = AndroidManager::bundleQt(target);
434

435 436 437 438 439 440
    QDomDocument doc;
    if (!openManifest(target, doc))
        return false;

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

441 442 443
    // ### Passes -1 for API level, which means it won't work with setups that require
    // library selection based on API level. Use the old approach (command line argument)
    // in these cases. Hence the Qt version > 4 condition at the beginning of this function.
444 445 446 447
    QString localLibs;
    QString localJars;
    QString staticInitClasses;
    if (useLocalLibs) {
448 449 450
        localLibs = loadLocalLibs(target, -1);
        localJars = loadLocalJars(target, -1);
        staticInitClasses = loadLocalJarsInitClasses(target, -1);
451 452 453 454 455
    }

    bool changedManifest = false;
    while (!metadataElem.isNull()) {
        if (metadataElem.attribute(QLatin1String("android:name")) == QLatin1String("android.app.use_local_qt_libs")) {
Orgad Shaneh's avatar
Orgad Shaneh committed
456
            if (metadataElem.attribute(QLatin1String("android:value")).toInt() != int(useLocalLibs)) {
457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474
                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;
            }
475
        } else if (metadataElem.attribute(QLatin1String("android:name")) == QLatin1String("android.app.bundle_local_qt_libs")) {
Orgad Shaneh's avatar
Orgad Shaneh committed
476
            if (metadataElem.attribute(QLatin1String("android:value")).toInt() != int(bundleQtLibs)) {
477 478 479
                metadataElem.setAttribute(QLatin1String("android:value"), int(bundleQtLibs));
                changedManifest = true;
            }
480 481 482 483 484 485 486 487 488 489 490
        }

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

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

Tobias Hunger's avatar
Tobias Hunger committed
491 492 493 494 495 496 497 498 499 500
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
501
        }
Tobias Hunger's avatar
Tobias Hunger committed
502
        metadataElem = metadataElem.nextSiblingElement(QLatin1String("meta-data"));
BogDan Vatra's avatar
BogDan Vatra committed
503
    }
Tobias Hunger's avatar
Tobias Hunger committed
504
    return false;
BogDan Vatra's avatar
BogDan Vatra committed
505 506
}

Tobias Hunger's avatar
Tobias Hunger committed
507
QString AndroidManager::targetApplicationPath(ProjectExplorer::Target *target)
BogDan Vatra's avatar
BogDan Vatra committed
508
{
Tobias Hunger's avatar
Tobias Hunger committed
509 510 511
    QString selectedApp = targetApplication(target);
    if (selectedApp.isEmpty())
        return QString();
512 513 514
    QmakeProjectManager::Qt4Project *qt4Project = qobject_cast<QmakeProjectManager::Qt4Project *>(target->project());
    foreach (QmakeProjectManager::Qt4ProFileNode *proFile, qt4Project->applicationProFiles()) {
        if (proFile->projectType() == QmakeProjectManager::ApplicationTemplate) {
Tobias Hunger's avatar
Tobias Hunger committed
515 516 517 518
            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)
519
                    return proFile->targetInformation().buildDir + QLatin1Char('/') + proFile->targetInformation().target;
Tobias Hunger's avatar
Tobias Hunger committed
520 521 522 523 524 525 526
            } 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
527 528
}

Tobias Hunger's avatar
Tobias Hunger committed
529
bool AndroidManager::createAndroidTemplatesIfNecessary(ProjectExplorer::Target *target)
BogDan Vatra's avatar
BogDan Vatra committed
530
{
Tobias Hunger's avatar
Tobias Hunger committed
531
    QtSupport::BaseQtVersion *version = QtSupport::QtKitInformation::qtVersion(target->kit());
532
    QmakeProjectManager::Qt4Project *qt4Project = qobject_cast<QmakeProjectManager::Qt4Project*>(target->project());
Tobias Hunger's avatar
Tobias Hunger committed
533 534 535
    if (!qt4Project || !qt4Project->rootProjectNode() || !version)
        return false;

536 537 538 539
    // TODO we should create the AndroidManifest.xml file for that version
    if (version->qtVersion() >= QtSupport::QtVersionNumber(5, 2, 0))
        return true;

Tobias Hunger's avatar
Tobias Hunger committed
540
    Utils::FileName javaSrcPath
541
            = Utils::FileName::fromString(version->qmakeProperty("QT_INSTALL_PREFIX"))
542
            .appendPath(QLatin1String("src/android/java"));
Tobias Hunger's avatar
Tobias Hunger committed
543 544 545 546 547 548
    QDir projectDir(qt4Project->projectDirectory());
    Utils::FileName androidPath = dirPath(target);

    QStringList m_ignoreFiles;
    bool forceUpdate = false;
    QDomDocument srcVersionDoc;
549 550
    Utils::FileName srcVersionPath = javaSrcPath;
    srcVersionPath.appendPath(QLatin1String("version.xml"));
551
    if (openXmlFile(srcVersionDoc, srcVersionPath)) {
Tobias Hunger's avatar
Tobias Hunger committed
552
        QDomDocument dstVersionDoc;
553 554
        Utils::FileName dstVersionPath=androidPath;
        dstVersionPath.appendPath(QLatin1String("version.xml"));
555
        if (openXmlFile(dstVersionDoc, dstVersionPath))
Tobias Hunger's avatar
Tobias Hunger committed
556 557 558 559 560 561 562 563 564 565 566 567 568 569
            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
570
    Utils::FileName src = androidPath;
571
    src.appendPath(QLatin1String("src"));
BogDan Vatra's avatar
BogDan Vatra committed
572
    Utils::FileName res = androidPath;
573
    res.appendPath(QLatin1String("res"));
BogDan Vatra's avatar
BogDan Vatra committed
574

Tobias Hunger's avatar
Tobias Hunger committed
575 576
    if (!forceUpdate && androidPath.toFileInfo().exists()
            && manifestPath(target).toFileInfo().exists()
BogDan Vatra's avatar
BogDan Vatra committed
577 578
            && src.toFileInfo().exists()
            && res.toFileInfo().exists())
Tobias Hunger's avatar
Tobias Hunger committed
579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595
        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 {
596 597
            Utils::FileName dstFile = androidPath;
            dstFile.appendPath(it.filePath().mid(pos));
Tobias Hunger's avatar
Tobias Hunger committed
598 599 600 601 602 603 604 605 606 607 608 609
            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())
610
        qt4Project->rootProjectNode()->addFiles(androidFiles);
Tobias Hunger's avatar
Tobias Hunger committed
611

612 613 614 615 616 617
    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
618 619 620 621
    if (sdks.isEmpty()) {
        raiseError(tr("No Qt for Android SDKs were found.\nPlease install at least one SDK."));
        return false;
    }
622 623

    updateTarget(target, AndroidConfigurations::instance().sdkTargets(minApiLevel).at(0));
Tobias Hunger's avatar
Tobias Hunger committed
624 625 626 627 628 629 630 631 632 633 634 635
    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)
636
        QMessageBox::warning(0, tr("Warning"), tr("Android files have been updated automatically."));
Tobias Hunger's avatar
Tobias Hunger committed
637 638

    return true;
BogDan Vatra's avatar
BogDan Vatra committed
639 640
}

Tobias Hunger's avatar
Tobias Hunger committed
641
void AndroidManager::updateTarget(ProjectExplorer::Target *target, const QString &targetSDK, const QString &name)
BogDan Vatra's avatar
BogDan Vatra committed
642
{
643 644 645 646
    QtSupport::BaseQtVersion *qtVersion = QtSupport::QtKitInformation::qtVersion(target->kit());
    if (qtVersion && qtVersion->qtVersion() >= QtSupport::QtVersionNumber(5,2,0))
        return;

Tobias Hunger's avatar
Tobias Hunger committed
647
    QString androidDir = dirPath(target).toString();
BogDan Vatra's avatar
BogDan Vatra committed
648

649 650 651 652
    Utils::Environment env = Utils::Environment::systemEnvironment();
    QString javaHome = AndroidConfigurations::instance().config().openJDKLocation.toString();
    if (!javaHome.isEmpty())
        env.set(QLatin1String("JAVA_HOME"), javaHome);
BogDan Vatra's avatar
BogDan Vatra committed
653 654 655
    // clean previous build
    QProcess androidProc;
    androidProc.setWorkingDirectory(androidDir);
656
    androidProc.setProcessEnvironment(env.toProcessEnvironment());
Tobias Hunger's avatar
Tobias Hunger committed
657 658
    androidProc.start(AndroidConfigurations::instance().antToolPath().toString(),
                      QStringList() << QLatin1String("clean"));
BogDan Vatra's avatar
BogDan Vatra committed
659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675
    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++) {
676 677 678
            QByteArray trimmed = lines[i].trimmed();
            if (trimmed.contains("@ANDROID-")) {
                commentLines = targetSDKNumber < trimmed.mid(trimmed.lastIndexOf('-') + 1).toInt();
BogDan Vatra's avatar
BogDan Vatra committed
679 680 681 682 683 684
                comment = !comment;
                continue;
            }
            if (!comment)
                continue;
            if (commentLines) {
685
                if (!trimmed.startsWith("//QtCreator")) {
BogDan Vatra's avatar
BogDan Vatra committed
686 687 688
                    lines[i] = "//QtCreator " + lines[i];
                    modified = true;
                }
689
            } else { if (trimmed.startsWith("//QtCreator")) {
BogDan Vatra's avatar
BogDan Vatra committed
690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710
                    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
711
    androidProc.start(AndroidConfigurations::instance().androidToolPath().toString(), params);
BogDan Vatra's avatar
BogDan Vatra committed
712 713 714 715
    if (!androidProc.waitForFinished(-1))
        androidProc.terminate();
}

Tobias Hunger's avatar
Tobias Hunger committed
716
Utils::FileName AndroidManager::localLibsRulesFilePath(ProjectExplorer::Target *target)
BogDan Vatra's avatar
BogDan Vatra committed
717
{
Tobias Hunger's avatar
Tobias Hunger committed
718
    QtSupport::BaseQtVersion *version = QtSupport::QtKitInformation::qtVersion(target->kit());
Tobias Hunger's avatar
Tobias Hunger committed
719 720
    if (!version)
        return Utils::FileName();
721
    return Utils::FileName::fromString(version->qmakeProperty("QT_INSTALL_LIBS"));
Tobias Hunger's avatar
Tobias Hunger committed
722
}
BogDan Vatra's avatar
BogDan Vatra committed
723

Tobias Hunger's avatar
Tobias Hunger committed
724 725 726 727
QString AndroidManager::loadLocalLibs(ProjectExplorer::Target *target, int apiLevel)
{
    return loadLocal(target, apiLevel, Lib);
}
BogDan Vatra's avatar
BogDan Vatra committed
728

729 730 731 732 733
QString AndroidManager::loadLocalBundledFiles(ProjectExplorer::Target *target, int apiLevel)
{
    return loadLocal(target, apiLevel, BundledFile);
}

Tobias Hunger's avatar
Tobias Hunger committed
734 735
QString AndroidManager::loadLocalJars(ProjectExplorer::Target *target, int apiLevel)
{
736 737
    ItemType type = bundleQt(target) ? BundledJar : Jar;
    return loadLocal(target, apiLevel, type);
Tobias Hunger's avatar
Tobias Hunger committed
738
}
BogDan Vatra's avatar
BogDan Vatra committed
739

740 741
QString AndroidManager::loadLocalJarsInitClasses(ProjectExplorer::Target *target, int apiLevel)
{
742 743
    ItemType type = bundleQt(target) ? BundledJar : Jar;
    return loadLocal(target, apiLevel, type, QLatin1String("initClass"));
744 745
}

746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796
QPair<int, int> AndroidManager::apiLevelRange(ProjectExplorer::Target *target)
{
    // 4 is the minimum version on which qt is supported
    // 19 and 20 are not yet released, but allow the user
    // to set them
    int minApiLevel = 4;
    QtSupport::BaseQtVersion *qt;
    if (target && (qt = QtSupport::QtKitInformation::qtVersion(target->kit())))
        if (qt->qtVersion() >= QtSupport::QtVersionNumber(5, 0, 0))
            minApiLevel = 9;
    return qMakePair(minApiLevel, 20);
}

QString AndroidManager::androidNameForApiLevel(int x)
{
    switch (x) {
    case 4:
        return QLatin1String("Android 1.6");
    case 5:
        return QLatin1String("Android 2.0");
    case 6:
        return QLatin1String("Android 2.0.1");
    case 7:
        return QLatin1String("Android 2.1.x");
    case 8:
        return QLatin1String("Android 2.2.x");
    case 9:
        return QLatin1String("Android 2.3, 2.3.1, 2.3.2");
    case 10:
        return QLatin1String("Android 2.3.3, 2.3.4");
    case 11:
        return QLatin1String("Android 3.0.x");
    case 12:
        return QLatin1String("Android 3.1.x");
    case 13:
        return QLatin1String("Android 3.2");
    case 14:
        return QLatin1String("Android 4.0, 4.0.1, 4.0.2");
    case 15:
        return QLatin1String("Android 4.0.3, 4.0.4");
    case 16:
        return QLatin1String("Android 4.1, 4.1.1");
    case 17:
        return QLatin1String("Android 4.2, 4.2.2");
    case 18:
        return QLatin1String("Android 4.3");
    default:
        return QLatin1String("Unknown Android version.");
    }
}

797
QVector<AndroidManager::Library> AndroidManager::availableQtLibsWithDependencies(ProjectExplorer::Target *target)
Tobias Hunger's avatar
Tobias Hunger committed
798
{
Tobias Hunger's avatar
Tobias Hunger committed
799
    QtSupport::BaseQtVersion *version = QtSupport::QtKitInformation::qtVersion(target->kit());
Tobias Hunger's avatar
Tobias Hunger committed
800
    if (!target->activeRunConfiguration())
801
        return QVector<AndroidManager::Library>();
Tobias Hunger's avatar
Tobias Hunger committed
802

803 804
    ProjectExplorer::ToolChain *tc = ProjectExplorer::ToolChainKitInformation::toolChain(target->kit());
    if (tc->type() != QLatin1String(Constants::ANDROID_TOOLCHAIN_TYPE))
805
        return QVector<AndroidManager::Library>();
806

807 808
    QmakeProjectManager::Qt4Project *project = static_cast<QmakeProjectManager::Qt4Project *>(target->project());
    QString arch = project->rootQt4ProjectNode()->singleVariableValue(QmakeProjectManager::AndroidArchVar);
809

810
    AndroidToolChain *atc = static_cast<AndroidToolChain *>(tc);
811
    QString libgnustl = libGnuStl(arch, atc->ndkToolChainVersion());
812 813 814

    Utils::FileName readelfPath = AndroidConfigurations::instance().readelfPath(target->activeRunConfiguration()->abi().architecture(),
                                                                                atc->ndkToolChainVersion());
815 816
    const QmakeProjectManager::Qt4Project *const qt4Project
            = qobject_cast<const QmakeProjectManager::Qt4Project *>(target->project());
Tobias Hunger's avatar
Tobias Hunger committed
817
    if (!qt4Project || !version)
818
        return QVector<AndroidManager::Library>();
819
    QString qtLibsPath = version->qmakeProperty("QT_INSTALL_LIBS");
Tobias Hunger's avatar
Tobias Hunger committed
820
    if (!readelfPath.toFileInfo().exists()) {
821
        return QVector<AndroidManager::Library>();
BogDan Vatra's avatar
BogDan Vatra committed
822
    }
Tobias Hunger's avatar
Tobias Hunger committed
823 824 825 826 827 828 829
    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
830 831
    }

832
    const QString library = libgnustl.mid(libgnustl.lastIndexOf(QLatin1Char('/')) + 1);
Nikolai Kosjar's avatar
Nikolai Kosjar committed
833
    mapLibs[library] = Library();
834

Tobias Hunger's avatar
Tobias Hunger committed
835
    // clean dependencies
836 837 838
    const LibrariesMap::Iterator lend = mapLibs.end();
    for (LibrariesMap::Iterator lit = mapLibs.begin(); lit != lend; ++lit) {
        Library &library = lit.value();
Tobias Hunger's avatar
Tobias Hunger committed
839
        int it = 0;
840 841 842 843
        while (it < library.dependencies.size()) {
            const QString &dependName = library.dependencies[it];
            if (!mapLibs.contains(dependName) && dependName.startsWith(QLatin1String("lib")) && dependName.endsWith(QLatin1String(".so")))
                library.dependencies.removeAt(it);
844
            else
Tobias Hunger's avatar
Tobias Hunger committed
845
                ++it;
BogDan Vatra's avatar
BogDan Vatra committed
846
        }
847 848
        if (library.dependencies.isEmpty())
            library.level = 0;
BogDan Vatra's avatar
BogDan Vatra committed
849 850
    }

Tobias Hunger's avatar
Tobias Hunger committed
851 852
    QVector<Library> qtLibraries;
    // calculate the level for every library
853 854 855 856
    for (LibrariesMap::Iterator lit = mapLibs.begin(); lit != lend; ++lit) {
        Library &library = lit.value();
        const QString &key = lit.key();
        if (library.level < 0)
Tobias Hunger's avatar
Tobias Hunger committed
857
           setLibraryLevel(key, mapLibs);
BogDan Vatra's avatar
BogDan Vatra committed
858

859 860
        if (library.name.isEmpty() && key.startsWith(QLatin1String("lib")) && key.endsWith(QLatin1String(".so")))
            library.name = key.mid(3, key.length() - 6);
BogDan Vatra's avatar
BogDan Vatra committed
861

862 863
        for (int it = 0; it < library.dependencies.size(); it++) {
            const QString &libName = library.dependencies[it];
Tobias Hunger's avatar
Tobias Hunger committed
864
            if (libName.startsWith(QLatin1String("lib")) && libName.endsWith(QLatin1String(".so")))
865
                library.dependencies[it] = libName.mid(3, libName.length() - 6);
Tobias Hunger's avatar
Tobias Hunger committed
866
        }
867
        qtLibraries.push_back(library);
BogDan Vatra's avatar
BogDan Vatra committed
868
    }
Tobias Hunger's avatar
Tobias Hunger committed
869
    qSort(qtLibraries.begin(), qtLibraries.end(), qtLibrariesLessThan);
870 871 872 873 874 875 876 877 878 879

    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
880 881
        libs.push_back(lib.name);
    return libs;
BogDan Vatra's avatar
BogDan Vatra committed
882 883
}

Tobias Hunger's avatar
Tobias Hunger committed
884
QStringList AndroidManager::qtLibs(ProjectExplorer::Target *target)
BogDan Vatra's avatar
BogDan Vatra committed
885
{
Tobias Hunger's avatar
Tobias Hunger committed
886
    return libsXml(target, QLatin1String("qt_libs"));
BogDan Vatra's avatar
BogDan Vatra committed
887 888
}

Tobias Hunger's avatar
Tobias Hunger committed
889
bool AndroidManager::setQtLibs(ProjectExplorer::Target *target, const QStringList &libs)
BogDan Vatra's avatar
BogDan Vatra committed
890
{
Tobias Hunger's avatar
Tobias Hunger committed
891
    return setLibsXml(target, libs, QLatin1String("qt_libs"));
BogDan Vatra's avatar
BogDan Vatra committed
892 893
}

894 895 896 897 898 899 900 901 902 903
bool AndroidManager::setBundledInAssets(ProjectExplorer::Target *target, const QStringList &fileList)
{
    return setLibsXml(target, fileList, QLatin1String("bundled_in_assets"));
}

bool AndroidManager::setBundledInLib(ProjectExplorer::Target *target, const QStringList &fileList)
{
    return setLibsXml(target, fileList, QLatin1String("bundled_in_lib"));
}

Tobias Hunger's avatar
Tobias Hunger committed
904
QStringList AndroidManager::availablePrebundledLibs(ProjectExplorer::Target *target)
BogDan Vatra's avatar
BogDan Vatra committed
905
{
Tobias Hunger's avatar
Tobias Hunger committed
906
    QStringList libs;
907
    QmakeProjectManager::Qt4Project *qt4Project = qobject_cast<QmakeProjectManager::Qt4Project *>(target->project());
Tobias Hunger's avatar
Tobias Hunger committed
908 909
    if (!qt4Project)
        return libs;
BogDan Vatra's avatar
BogDan Vatra committed
910

911 912
    foreach (QmakeProjectManager::Qt4ProFileNode *node, qt4Project->allProFiles())
        if (node->projectType() == QmakeProjectManager::LibraryTemplate)
913
            libs << node->targetInformation().target;
Tobias Hunger's avatar
Tobias Hunger committed
914
    return libs;
BogDan Vatra's avatar
BogDan Vatra committed
915 916
}

Tobias Hunger's avatar
Tobias Hunger committed
917
QStringList AndroidManager::prebundledLibs(ProjectExplorer::Target *target)
BogDan Vatra's avatar
BogDan Vatra committed
918
{
Tobias Hunger's avatar
Tobias Hunger committed
919
    return libsXml(target, QLatin1String("bundled_libs"));
BogDan Vatra's avatar
BogDan Vatra committed
920 921
}

Tobias Hunger's avatar
Tobias Hunger committed
922
bool AndroidManager::setPrebundledLibs(ProjectExplorer::Target *target, const QStringList &libs)
BogDan Vatra's avatar
BogDan Vatra committed
923
{
Tobias Hunger's avatar
Tobias Hunger committed
924
    return setLibsXml(target, libs, QLatin1String("bundled_libs"));
BogDan Vatra's avatar
BogDan Vatra committed
925 926
}

Tobias Hunger's avatar
Tobias Hunger committed
927
bool AndroidManager::openLibsXml(ProjectExplorer::Target *target, QDomDocument &doc)
BogDan Vatra's avatar
BogDan Vatra committed
928
{
929
    return openXmlFile(doc, libsPath(target));
BogDan Vatra's avatar
BogDan Vatra committed
930 931
}

Tobias Hunger's avatar
Tobias Hunger committed
932
bool AndroidManager::saveLibsXml(ProjectExplorer::Target *target, QDomDocument &doc)
BogDan Vatra's avatar
BogDan Vatra committed
933
{
Tobias Hunger's avatar
Tobias Hunger committed
934
    return saveXmlFile(target, doc, libsPath(target));
BogDan Vatra's avatar
BogDan Vatra committed
935 936
}

Tobias Hunger's avatar
Tobias Hunger committed
937
void AndroidManager::raiseError(const QString &reason)
BogDan Vatra's avatar
BogDan Vatra committed
938
{
939
    QMessageBox::critical(0, tr("Error creating Android templates."), reason);
BogDan Vatra's avatar
BogDan Vatra committed
940 941
}

942
QString AndroidManager::loadLocal(ProjectExplorer::Target *target, int apiLevel, ItemType item, const QString &attribute)
BogDan Vatra's avatar
BogDan Vatra committed
943
{
Tobias Hunger's avatar
Tobias Hunger committed
944 945 946
    QString itemType;
    if (item == Lib)
        itemType = QLatin1String("lib");
947 948 949
    else if (item == BundledFile)
        itemType = QLatin1String("bundled");
    else // Jar or BundledJar
Tobias Hunger's avatar
Tobias Hunger committed
950 951 952
        itemType = QLatin1String("jar");

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

954 955
    QDir rulesFilesDir(localLibsRulesFilePath(target).toString());
    if (!rulesFilesDir.exists())
Tobias Hunger's avatar
Tobias Hunger committed
956 957 958 959
        return localLibs;

    QStringList libs;
    libs << qtLibs(target) << prebundledLibs(target);
960 961 962 963 964 965 966 967 968 969

    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
970 971
        }

972 973 974 975 976 977 978 979 980 981
        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
982
            }
Eskil Abrahamsen Blomfeldt's avatar