androidmanager.cpp 43.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"
BogDan Vatra's avatar
BogDan Vatra committed
38

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

#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");
65
    const QLatin1String AndroidLibraryPrefix("--Managed_by_Qt_Creator--");
BogDan Vatra's avatar
BogDan Vatra committed
66 67 68

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

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

Tobias Hunger's avatar
Tobias Hunger committed
90 91 92 93 94 95 96
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
97 98
}

Tobias Hunger's avatar
Tobias Hunger committed
99
bool AndroidManager::setPackageName(ProjectExplorer::Target *target, const QString &name)
BogDan Vatra's avatar
BogDan Vatra committed
100
{
Tobias Hunger's avatar
Tobias Hunger committed
101 102 103 104 105 106
    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
107 108
}

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

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

Tobias Hunger's avatar
Tobias Hunger committed
141 142 143 144 145 146 147 148 149 150 151 152 153
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
154

Tobias Hunger's avatar
Tobias Hunger committed
155 156 157 158 159 160 161 162 163 164 165
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
166

Tobias Hunger's avatar
Tobias Hunger committed
167 168 169 170
    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
171
    }
Tobias Hunger's avatar
Tobias Hunger committed
172 173

    return saveManifest(target, doc);
BogDan Vatra's avatar
BogDan Vatra committed
174 175
}

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

Tobias Hunger's avatar
Tobias Hunger committed
181
QString AndroidManager::activityName(ProjectExplorer::Target *target)
BogDan Vatra's avatar
BogDan Vatra committed
182
{
Tobias Hunger's avatar
Tobias Hunger committed
183 184 185 186 187
    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
188 189
}

Tobias Hunger's avatar
Tobias Hunger committed
190
int AndroidManager::versionCode(ProjectExplorer::Target *target)
BogDan Vatra's avatar
BogDan Vatra committed
191
{
Tobias Hunger's avatar
Tobias Hunger committed
192 193 194 195 196 197
    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
198

Tobias Hunger's avatar
Tobias Hunger committed
199 200 201 202 203 204 205 206 207
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
208

Tobias Hunger's avatar
Tobias Hunger committed
209 210 211 212 213 214 215 216
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
217

Tobias Hunger's avatar
Tobias Hunger committed
218 219 220 221 222 223 224 225
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
226 227
}

Daniel Teske's avatar
Daniel Teske committed
228 229 230 231 232 233 234 235 236 237
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
238
QString AndroidManager::targetSDK(ProjectExplorer::Target *target)
BogDan Vatra's avatar
BogDan Vatra committed
239
{
240 241 242 243
    QVariant v = target->namedSettings(QLatin1String("AndroidManager.TargetSdk"));
    if (v.isValid())
        return v.toString();

244 245 246 247 248
    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
249
    if (!createAndroidTemplatesIfNecessary(target))
250
        return AndroidConfigurations::instance().bestMatch(fallback);
251

Tobias Hunger's avatar
Tobias Hunger committed
252 253
    QFile file(defaultPropertiesPath(target).toString());
    if (!file.open(QIODevice::ReadOnly))
254
        return AndroidConfigurations::instance().bestMatch(fallback);
Tobias Hunger's avatar
Tobias Hunger committed
255 256 257 258 259
    while (!file.atEnd()) {
        QByteArray line = file.readLine();
        if (line.startsWith("target="))
            return QString::fromLatin1(line.trimmed().mid(7));
    }
260
    return AndroidConfigurations::instance().bestMatch(fallback);
Tobias Hunger's avatar
Tobias Hunger committed
261
}
BogDan Vatra's avatar
BogDan Vatra committed
262

Tobias Hunger's avatar
Tobias Hunger committed
263 264 265
bool AndroidManager::setTargetSDK(ProjectExplorer::Target *target, const QString &sdk)
{
    updateTarget(target, sdk, applicationName(target));
266
    target->setNamedSettings(QLatin1String("AndroidManager.TargetSdk"), sdk);
Tobias Hunger's avatar
Tobias Hunger committed
267 268
    return true;
}
BogDan Vatra's avatar
BogDan Vatra committed
269

Tobias Hunger's avatar
Tobias Hunger committed
270 271 272
QIcon AndroidManager::highDpiIcon(ProjectExplorer::Target *target)
{
    return icon(target, HighDPI);
BogDan Vatra's avatar
BogDan Vatra committed
273 274
}

Tobias Hunger's avatar
Tobias Hunger committed
275
bool AndroidManager::setHighDpiIcon(ProjectExplorer::Target *target, const QString &iconFilePath)
BogDan Vatra's avatar
BogDan Vatra committed
276
{
Daniel Teske's avatar
Daniel Teske committed
277 278
    return ensureIconAttribute(target) &&
            setIcon(target, HighDPI, iconFilePath);
BogDan Vatra's avatar
BogDan Vatra committed
279 280
}

Tobias Hunger's avatar
Tobias Hunger committed
281
QIcon AndroidManager::mediumDpiIcon(ProjectExplorer::Target *target)
BogDan Vatra's avatar
BogDan Vatra committed
282
{
Tobias Hunger's avatar
Tobias Hunger committed
283
    return icon(target, MediumDPI);
BogDan Vatra's avatar
BogDan Vatra committed
284 285
}

Tobias Hunger's avatar
Tobias Hunger committed
286
bool AndroidManager::setMediumDpiIcon(ProjectExplorer::Target *target, const QString &iconFilePath)
BogDan Vatra's avatar
BogDan Vatra committed
287
{
Daniel Teske's avatar
Daniel Teske committed
288 289
    return ensureIconAttribute(target) &&
            setIcon(target, MediumDPI, iconFilePath);
BogDan Vatra's avatar
BogDan Vatra committed
290 291
}

Tobias Hunger's avatar
Tobias Hunger committed
292
QIcon AndroidManager::lowDpiIcon(ProjectExplorer::Target *target)
BogDan Vatra's avatar
BogDan Vatra committed
293
{
Tobias Hunger's avatar
Tobias Hunger committed
294
    return icon(target, LowDPI);
BogDan Vatra's avatar
BogDan Vatra committed
295 296
}

Tobias Hunger's avatar
Tobias Hunger committed
297
bool AndroidManager::setLowDpiIcon(ProjectExplorer::Target *target, const QString &iconFilePath)
BogDan Vatra's avatar
BogDan Vatra committed
298
{
Daniel Teske's avatar
Daniel Teske committed
299 300
    return ensureIconAttribute(target) &&
            setIcon(target, LowDPI, iconFilePath);
BogDan Vatra's avatar
BogDan Vatra committed
301 302
}

Tobias Hunger's avatar
Tobias Hunger committed
303
Utils::FileName AndroidManager::dirPath(ProjectExplorer::Target *target)
BogDan Vatra's avatar
BogDan Vatra committed
304
{
Tobias Hunger's avatar
Tobias Hunger committed
305
    return Utils::FileName::fromString(target->project()->projectDirectory()).appendPath(AndroidDirName);
BogDan Vatra's avatar
BogDan Vatra committed
306 307
}

Tobias Hunger's avatar
Tobias Hunger committed
308
Utils::FileName AndroidManager::manifestPath(ProjectExplorer::Target *target)
BogDan Vatra's avatar
BogDan Vatra committed
309
{
Tobias Hunger's avatar
Tobias Hunger committed
310
    return dirPath(target).appendPath(AndroidManifestName);
BogDan Vatra's avatar
BogDan Vatra committed
311 312
}

Tobias Hunger's avatar
Tobias Hunger committed
313
Utils::FileName AndroidManager::libsPath(ProjectExplorer::Target *target)
BogDan Vatra's avatar
BogDan Vatra committed
314
{
Tobias Hunger's avatar
Tobias Hunger committed
315 316
    return dirPath(target).appendPath(AndroidLibsFileName);
}
BogDan Vatra's avatar
BogDan Vatra committed
317

Tobias Hunger's avatar
Tobias Hunger committed
318 319 320
Utils::FileName AndroidManager::stringsPath(ProjectExplorer::Target *target)
{
    return dirPath(target).append(AndroidStringsFileName);
BogDan Vatra's avatar
BogDan Vatra committed
321 322
}

Tobias Hunger's avatar
Tobias Hunger committed
323
Utils::FileName AndroidManager::defaultPropertiesPath(ProjectExplorer::Target *target)
BogDan Vatra's avatar
BogDan Vatra committed
324
{
Tobias Hunger's avatar
Tobias Hunger committed
325 326
    return dirPath(target).appendPath(AndroidDefaultPropertiesName);
}
BogDan Vatra's avatar
BogDan Vatra committed
327

Tobias Hunger's avatar
Tobias Hunger committed
328 329 330 331
Utils::FileName AndroidManager::srcPath(ProjectExplorer::Target *target)
{
    return dirPath(target).appendPath(QLatin1String("/src"));
}
BogDan Vatra's avatar
BogDan Vatra committed
332

Tobias Hunger's avatar
Tobias Hunger committed
333 334 335 336 337 338 339 340 341 342 343 344
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
345

Tobias Hunger's avatar
Tobias Hunger committed
346 347 348 349 350 351 352 353 354 355 356
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
357 358
        }
    }
Tobias Hunger's avatar
Tobias Hunger committed
359 360 361
    apps.sort();
    return apps;
}
BogDan Vatra's avatar
BogDan Vatra committed
362

Tobias Hunger's avatar
Tobias Hunger committed
363 364 365 366 367 368 369 370 371 372 373 374 375
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
376

377 378 379 380 381 382 383 384 385 386 387 388 389 390
bool AndroidManager::bundleQt(ProjectExplorer::Target *target)
{
    ProjectExplorer::RunConfiguration *runConfiguration = target->activeRunConfiguration();
    AndroidRunConfiguration *androidRunConfiguration = qobject_cast<AndroidRunConfiguration *>(runConfiguration);
    if (androidRunConfiguration != 0) {
        AndroidDeployStep *deployStep = androidRunConfiguration->deployStep();
        return deployStep->deployAction() == AndroidDeployStep::NoDeploy
               && deployStep->useLocalQtLibs();
    }

    return false;
}

bool AndroidManager::updateDeploymentSettings(ProjectExplorer::Target *target)
391 392 393 394 395 396 397
{
    // 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;

398 399 400 401 402 403 404 405 406 407
    ProjectExplorer::RunConfiguration *runConfiguration = target->activeRunConfiguration();
    AndroidRunConfiguration *androidRunConfiguration = qobject_cast<AndroidRunConfiguration *>(runConfiguration);
    if (androidRunConfiguration == 0)
        return false;

    AndroidDeployStep *deployStep = androidRunConfiguration->deployStep();
    bool useLocalLibs = deployStep->useLocalQtLibs();
    bool deployQtLibs = deployStep->deployAction() != AndroidDeployStep::NoDeploy;
    bool bundleQtLibs = useLocalLibs && !deployQtLibs;

408 409 410 411 412 413
    QDomDocument doc;
    if (!openManifest(target, doc))
        return false;

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

414 415 416
    // ### 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.
417 418 419 420
    QString localLibs;
    QString localJars;
    QString staticInitClasses;
    if (useLocalLibs) {
421 422 423
        localLibs = loadLocalLibs(target, -1);
        localJars = loadLocalJars(target, -1);
        staticInitClasses = loadLocalJarsInitClasses(target, -1);
424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447
    }

    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;
            }
448 449 450 451 452
        } else if (metadataElem.attribute(QLatin1String("android:name")) == QLatin1String("android.app.bundle_local_qt_libs")) {
            if (metadataElem.attribute(QLatin1String("android:value")).toInt() != bundleQtLibs) {
                metadataElem.setAttribute(QLatin1String("android:value"), int(bundleQtLibs));
                changedManifest = true;
            }
453 454 455 456 457 458 459 460 461 462 463
        }

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

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

Tobias Hunger's avatar
Tobias Hunger committed
464 465 466 467 468 469 470 471 472 473
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
474
        }
Tobias Hunger's avatar
Tobias Hunger committed
475
        metadataElem = metadataElem.nextSiblingElement(QLatin1String("meta-data"));
BogDan Vatra's avatar
BogDan Vatra committed
476
    }
Tobias Hunger's avatar
Tobias Hunger committed
477
    return false;
BogDan Vatra's avatar
BogDan Vatra committed
478 479
}

Tobias Hunger's avatar
Tobias Hunger committed
480
QString AndroidManager::targetApplicationPath(ProjectExplorer::Target *target)
BogDan Vatra's avatar
BogDan Vatra committed
481
{
Tobias Hunger's avatar
Tobias Hunger committed
482 483 484 485 486 487 488 489 490 491
    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)
492
                    return proFile->targetInformation().buildDir + QLatin1Char('/') + proFile->targetInformation().target;
Tobias Hunger's avatar
Tobias Hunger committed
493 494 495 496 497 498 499
            } 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
500 501
}

Tobias Hunger's avatar
Tobias Hunger committed
502
bool AndroidManager::createAndroidTemplatesIfNecessary(ProjectExplorer::Target *target)
BogDan Vatra's avatar
BogDan Vatra committed
503
{
Tobias Hunger's avatar
Tobias Hunger committed
504
    QtSupport::BaseQtVersion *version = QtSupport::QtKitInformation::qtVersion(target->kit());
Tobias Hunger's avatar
Tobias Hunger committed
505 506 507 508 509
    Qt4ProjectManager::Qt4Project *qt4Project = qobject_cast<Qt4ProjectManager::Qt4Project*>(target->project());
    if (!qt4Project || !qt4Project->rootProjectNode() || !version)
        return false;

    Utils::FileName javaSrcPath
510
            = Utils::FileName::fromString(version->qmakeProperty("QT_INSTALL_PREFIX"))
511
            .appendPath(QLatin1String("src/android/java"));
Tobias Hunger's avatar
Tobias Hunger committed
512 513 514 515 516 517
    QDir projectDir(qt4Project->projectDirectory());
    Utils::FileName androidPath = dirPath(target);

    QStringList m_ignoreFiles;
    bool forceUpdate = false;
    QDomDocument srcVersionDoc;
518 519
    Utils::FileName srcVersionPath = javaSrcPath;
    srcVersionPath.appendPath(QLatin1String("version.xml"));
520
    if (openXmlFile(srcVersionDoc, srcVersionPath)) {
Tobias Hunger's avatar
Tobias Hunger committed
521
        QDomDocument dstVersionDoc;
522 523
        Utils::FileName dstVersionPath=androidPath;
        dstVersionPath.appendPath(QLatin1String("version.xml"));
524
        if (openXmlFile(dstVersionDoc, dstVersionPath))
Tobias Hunger's avatar
Tobias Hunger committed
525 526 527 528 529 530 531 532 533 534 535 536 537 538
            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
539
    Utils::FileName src = androidPath;
540
    src.appendPath(QLatin1String("src"));
BogDan Vatra's avatar
BogDan Vatra committed
541
    Utils::FileName res = androidPath;
542
    res.appendPath(QLatin1String("res"));
BogDan Vatra's avatar
BogDan Vatra committed
543

Tobias Hunger's avatar
Tobias Hunger committed
544 545
    if (!forceUpdate && androidPath.toFileInfo().exists()
            && manifestPath(target).toFileInfo().exists()
BogDan Vatra's avatar
BogDan Vatra committed
546 547
            && src.toFileInfo().exists()
            && res.toFileInfo().exists())
Tobias Hunger's avatar
Tobias Hunger committed
548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564
        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 {
565 566
            Utils::FileName dstFile = androidPath;
            dstFile.appendPath(it.filePath().mid(pos));
Tobias Hunger's avatar
Tobias Hunger committed
567 568 569 570 571 572 573 574 575 576 577 578 579 580
            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);

581 582 583 584 585 586
    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
587 588 589 590
    if (sdks.isEmpty()) {
        raiseError(tr("No Qt for Android SDKs were found.\nPlease install at least one SDK."));
        return false;
    }
591 592

    updateTarget(target, AndroidConfigurations::instance().sdkTargets(minApiLevel).at(0));
Tobias Hunger's avatar
Tobias Hunger committed
593 594 595 596 597 598 599 600 601 602 603 604 605 606 607
    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
608 609
}

Tobias Hunger's avatar
Tobias Hunger committed
610
void AndroidManager::updateTarget(ProjectExplorer::Target *target, const QString &targetSDK, const QString &name)
BogDan Vatra's avatar
BogDan Vatra committed
611
{
Tobias Hunger's avatar
Tobias Hunger committed
612
    QString androidDir = dirPath(target).toString();
BogDan Vatra's avatar
BogDan Vatra committed
613 614 615 616

    // clean previous build
    QProcess androidProc;
    androidProc.setWorkingDirectory(androidDir);
Tobias Hunger's avatar
Tobias Hunger committed
617 618
    androidProc.start(AndroidConfigurations::instance().antToolPath().toString(),
                      QStringList() << QLatin1String("clean"));
BogDan Vatra's avatar
BogDan Vatra committed
619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635
    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++) {
636 637 638
            QByteArray trimmed = lines[i].trimmed();
            if (trimmed.contains("@ANDROID-")) {
                commentLines = targetSDKNumber < trimmed.mid(trimmed.lastIndexOf('-') + 1).toInt();
BogDan Vatra's avatar
BogDan Vatra committed
639 640 641 642 643 644
                comment = !comment;
                continue;
            }
            if (!comment)
                continue;
            if (commentLines) {
645
                if (!trimmed.startsWith("//QtCreator")) {
BogDan Vatra's avatar
BogDan Vatra committed
646 647 648
                    lines[i] = "//QtCreator " + lines[i];
                    modified = true;
                }
649
            } else { if (trimmed.startsWith("//QtCreator")) {
BogDan Vatra's avatar
BogDan Vatra committed
650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670
                    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
671
    androidProc.start(AndroidConfigurations::instance().androidToolPath().toString(), params);
BogDan Vatra's avatar
BogDan Vatra committed
672 673 674 675
    if (!androidProc.waitForFinished(-1))
        androidProc.terminate();
}

Tobias Hunger's avatar
Tobias Hunger committed
676
Utils::FileName AndroidManager::localLibsRulesFilePath(ProjectExplorer::Target *target)
BogDan Vatra's avatar
BogDan Vatra committed
677
{
Tobias Hunger's avatar
Tobias Hunger committed
678
    QtSupport::BaseQtVersion *version = QtSupport::QtKitInformation::qtVersion(target->kit());
Tobias Hunger's avatar
Tobias Hunger committed
679 680
    if (!version)
        return Utils::FileName();
681
    return Utils::FileName::fromString(version->qmakeProperty("QT_INSTALL_LIBS"));
Tobias Hunger's avatar
Tobias Hunger committed
682
}
BogDan Vatra's avatar
BogDan Vatra committed
683

Tobias Hunger's avatar
Tobias Hunger committed
684 685 686 687
QString AndroidManager::loadLocalLibs(ProjectExplorer::Target *target, int apiLevel)
{
    return loadLocal(target, apiLevel, Lib);
}
BogDan Vatra's avatar
BogDan Vatra committed
688

689 690 691 692 693
QString AndroidManager::loadLocalBundledFiles(ProjectExplorer::Target *target, int apiLevel)
{
    return loadLocal(target, apiLevel, BundledFile);
}

Tobias Hunger's avatar
Tobias Hunger committed
694 695
QString AndroidManager::loadLocalJars(ProjectExplorer::Target *target, int apiLevel)
{
696 697
    ItemType type = bundleQt(target) ? BundledJar : Jar;
    return loadLocal(target, apiLevel, type);
Tobias Hunger's avatar
Tobias Hunger committed
698
}
BogDan Vatra's avatar
BogDan Vatra committed
699

700 701
QString AndroidManager::loadLocalJarsInitClasses(ProjectExplorer::Target *target, int apiLevel)
{
702 703
    ItemType type = bundleQt(target) ? BundledJar : Jar;
    return loadLocal(target, apiLevel, type, QLatin1String("initClass"));
704 705
}

706
QVector<AndroidManager::Library> AndroidManager::availableQtLibsWithDependencies(ProjectExplorer::Target *target)
Tobias Hunger's avatar
Tobias Hunger committed
707
{
Tobias Hunger's avatar
Tobias Hunger committed
708
    QtSupport::BaseQtVersion *version = QtSupport::QtKitInformation::qtVersion(target->kit());
Tobias Hunger's avatar
Tobias Hunger committed
709
    if (!target->activeRunConfiguration())
710
        return QVector<AndroidManager::Library>();
Tobias Hunger's avatar
Tobias Hunger committed
711

712 713
    ProjectExplorer::ToolChain *tc = ProjectExplorer::ToolChainKitInformation::toolChain(target->kit());
    if (tc->type() != QLatin1String(Constants::ANDROID_TOOLCHAIN_TYPE))
714
        return QVector<AndroidManager::Library>();
715 716 717 718

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

719
    AndroidToolChain *atc = static_cast<AndroidToolChain *>(tc);
720
    QString libgnustl = libGnuStl(arch, atc->ndkToolChainVersion());
721 722 723

    Utils::FileName readelfPath = AndroidConfigurations::instance().readelfPath(target->activeRunConfiguration()->abi().architecture(),
                                                                                atc->ndkToolChainVersion());
Tobias Hunger's avatar
Tobias Hunger committed
724 725 726
    const Qt4ProjectManager::Qt4Project *const qt4Project
            = qobject_cast<const Qt4ProjectManager::Qt4Project *>(target->project());
    if (!qt4Project || !version)
727
        return QVector<AndroidManager::Library>();
728
    QString qtLibsPath = version->qmakeProperty("QT_INSTALL_LIBS");
Tobias Hunger's avatar
Tobias Hunger committed
729
    if (!readelfPath.toFileInfo().exists()) {
730
        return QVector<AndroidManager::Library>();
BogDan Vatra's avatar
BogDan Vatra committed
731
    }
Tobias Hunger's avatar
Tobias Hunger committed
732 733 734 735 736 737 738
    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
739 740
    }

741 742 743
    const QString library = libgnustl.mid(libgnustl.lastIndexOf(QLatin1Char('/')) + 1);
    mapLibs[library] = Library();;

Tobias Hunger's avatar
Tobias Hunger committed
744 745 746 747 748
    // clean dependencies
    foreach (const QString &key, mapLibs.keys()) {
        int it = 0;
        while (it < mapLibs[key].dependencies.size()) {
            const QString &dependName = mapLibs[key].dependencies[it];
749
            if (!mapLibs.keys().contains(dependName) && dependName.startsWith(QLatin1String("lib")) && dependName.endsWith(QLatin1String(".so")))
Tobias Hunger's avatar
Tobias Hunger committed
750
                mapLibs[key].dependencies.removeAt(it);
751
            else
Tobias Hunger's avatar
Tobias Hunger committed
752
                ++it;
BogDan Vatra's avatar
BogDan Vatra committed
753
        }
Tobias Hunger's avatar
Tobias Hunger committed
754 755
        if (!mapLibs[key].dependencies.size())
            mapLibs[key].level = 0;
BogDan Vatra's avatar
BogDan Vatra committed
756 757
    }

Tobias Hunger's avatar
Tobias Hunger committed
758 759 760 761 762
    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
763

Tobias Hunger's avatar
Tobias Hunger committed
764 765
        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
766

Tobias Hunger's avatar
Tobias Hunger committed
767 768 769 770 771 772
        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
773
    }
Tobias Hunger's avatar
Tobias Hunger committed
774
    qSort(qtLibraries.begin(), qtLibraries.end(), qtLibrariesLessThan);
775 776 777 778 779 780 781 782 783 784

    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
785 786
        libs.push_back(lib.name);
    return libs;
BogDan Vatra's avatar
BogDan Vatra committed
787 788
}

Tobias Hunger's avatar
Tobias Hunger committed
789
QStringList AndroidManager::qtLibs(ProjectExplorer::Target *target)
BogDan Vatra's avatar
BogDan Vatra committed
790
{
Tobias Hunger's avatar
Tobias Hunger committed
791
    return libsXml(target, QLatin1String("qt_libs"));
BogDan Vatra's avatar
BogDan Vatra committed
792 793
}

Tobias Hunger's avatar
Tobias Hunger committed
794
bool AndroidManager::setQtLibs(ProjectExplorer::Target *target, const QStringList &libs)
BogDan Vatra's avatar
BogDan Vatra committed
795
{
Tobias Hunger's avatar
Tobias Hunger committed
796
    return setLibsXml(target, libs, QLatin1String("qt_libs"));
BogDan Vatra's avatar
BogDan Vatra committed
797 798
}

799 800 801 802 803 804 805 806 807 808
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
809
QStringList AndroidManager::availablePrebundledLibs(ProjectExplorer::Target *target)
BogDan Vatra's avatar
BogDan Vatra committed
810
{
Tobias Hunger's avatar
Tobias Hunger committed
811 812 813 814
    QStringList libs;
    Qt4ProjectManager::Qt4Project *qt4Project = qobject_cast<Qt4ProjectManager::Qt4Project *>(target->project());
    if (!qt4Project)
        return libs;
BogDan Vatra's avatar
BogDan Vatra committed
815

Tobias Hunger's avatar
Tobias Hunger committed
816 817 818 819
    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
820 821
}

Tobias Hunger's avatar
Tobias Hunger committed
822
QStringList AndroidManager::prebundledLibs(ProjectExplorer::Target *target)
BogDan Vatra's avatar
BogDan Vatra committed
823
{
Tobias Hunger's avatar
Tobias Hunger committed
824
    return libsXml(target, QLatin1String("bundled_libs"));
BogDan Vatra's avatar
BogDan Vatra committed
825 826
}

Tobias Hunger's avatar
Tobias Hunger committed
827
bool AndroidManager::setPrebundledLibs(ProjectExplorer::Target *target, const QStringList &libs)
BogDan Vatra's avatar
BogDan Vatra committed
828
{
Tobias Hunger's avatar
Tobias Hunger committed
829
    return setLibsXml(target, libs, QLatin1String("bundled_libs"));
BogDan Vatra's avatar
BogDan Vatra committed
830 831
}

Tobias Hunger's avatar
Tobias Hunger committed
832
bool AndroidManager::openLibsXml(ProjectExplorer::Target *target, QDomDocument &doc)
BogDan Vatra's avatar
BogDan Vatra committed
833
{
834
    return openXmlFile(doc, libsPath(target));
BogDan Vatra's avatar
BogDan Vatra committed
835 836
}

Tobias Hunger's avatar
Tobias Hunger committed
837
bool AndroidManager::saveLibsXml(ProjectExplorer::Target *target, QDomDocument &doc)
BogDan Vatra's avatar
BogDan Vatra committed
838
{
Tobias Hunger's avatar
Tobias Hunger committed
839
    return saveXmlFile(target, doc, libsPath(target));
BogDan Vatra's avatar
BogDan Vatra committed
840 841
}

Tobias Hunger's avatar
Tobias Hunger committed
842
void AndroidManager::raiseError(const QString &reason)
BogDan Vatra's avatar
BogDan Vatra committed
843
{
Tobias Hunger's avatar
Tobias Hunger committed
844
    QMessageBox::critical(0, tr("Error creating Android templates"), reason);
BogDan Vatra's avatar
BogDan Vatra committed
845 846
}

847
QString AndroidManager::loadLocal(ProjectExplorer::Target *target, int apiLevel, ItemType item, const QString &attribute)
BogDan Vatra's avatar
BogDan Vatra committed
848
{
Tobias Hunger's avatar
Tobias Hunger committed
849 850 851
    QString itemType;
    if (item == Lib)
        itemType = QLatin1String("lib");
852 853 854
    else if (item == BundledFile)
        itemType = QLatin1String("bundled");
    else // Jar or BundledJar
Tobias Hunger's avatar
Tobias Hunger committed
855 856 857
        itemType = QLatin1String("jar");

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

859 860
    QDir rulesFilesDir(localLibsRulesFilePath(target).toString());
    if (!rulesFilesDir.exists())
Tobias Hunger's avatar
Tobias Hunger committed
861 862 863 864
        return localLibs;

    QStringList libs;
    libs << qtLibs(target) << prebundledLibs(target);
865 866 867 868 869 870 871 872 873 874

    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
875 876
        }

877 878 879 880 881 882 883 884 885 886
        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
887
            }
888 889 890 891 892 893 894 895
            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()) {
896 897 898
                    if (libElement.attribute(QLatin1String("bundling")).toInt() == (item == BundledJar ? 1 : 0)) {
                        if (libElement.hasAttribute(attribute)) {
                            QString dependencyLib = libElement.attribute(attribute).arg(apiLevel);
899 900 901 902 903 904
                            if (libElement.hasAttribute(QLatin1String("extends"))) {
                                const QString extends = libElement.attribute(QLatin1String("extends"));
                                if (libs.contains(extends)) {
                                    dependencyLibs << dependencyLib;
                                }
                            } else if (!dependencyLibs.contains(dependencyLib)) {
905
                                dependencyLibs << dependencyLib;
906
                            }
907 908 909 910 911 912 913
                        }

                        if (libElement.hasAttribute(QLatin1String("replaces"))) {
                            QString replacedLib = libElement.attribute(QLatin1String("replaces")).arg(apiLevel);
                            if (!replacedLibs.contains(replacedLib))
                                replacedLibs << replacedLib;
                        }
914 915 916 917 918 919 920 921 922 923 924 925
                    }

                    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
926

927 928
                    libElement = libElement.nextSiblingElement(itemType);
                }
BogDan Vatra's avatar
BogDan Vatra committed
929
            }
930
            element = element.nextSiblingElement(QLatin1String("lib"));
BogDan Vatra's avatar
BogDan Vatra committed
931 932
        }
    }
933 934 935

    // 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
936
    localLibs = dependencyLibs.join(QLatin1String(":")) + QLatin1Char(':');
937 938 939
    foreach (QString replacedLib, replacedLibs)
        localLibs.remove(replacedLib + QLatin1Char(':'));

Tobias Hunger's avatar
Tobias Hunger committed
940
    return localLibs;
BogDan Vatra's avatar
BogDan Vatra committed
941 942
}

943
bool AndroidManager::openXmlFile(QDomDocument &doc, const Utils::FileName &fileName)
BogDan Vatra's avatar
BogDan Vatra committed
944
{
Tobias Hunger's avatar
Tobias Hunger committed
945 946
    QFile f(fileName.toString());
    if (!f.open(QIODevice::ReadOnly))
BogDan Vatra's avatar
BogDan Vatra committed
947 948
        return false;

Tobias Hunger's avatar
Tobias Hunger committed
949 950
    if (!doc.setContent(f.readAll())) {
        raiseError(tr("Can't parse '%1'").arg(fileName.toUserOutput()));
BogDan Vatra's avatar
BogDan Vatra committed
951 952
        return false;
    }
Tobias Hunger's avatar
Tobias Hunger committed
953
    return true;
BogDan Vatra's avatar
BogDan Vatra committed
954 955
}

Tobias Hunger's avatar
Tobias Hunger committed
956
bool AndroidManager::saveXmlFile(ProjectExplorer::Target *target, QDomDocument &doc, const Utils::FileName &fileName)
BogDan Vatra's avatar
BogDan Vatra committed
957
{
Tobias Hunger's avatar
Tobias Hunger committed
958 959
    if (!createAndroidTemplatesIfNecessary(target))
        return false;
BogDan Vatra's avatar
BogDan Vatra committed
960

Tobias Hunger's avatar
Tobias Hunger committed
961 962 963 964
    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
965
    }
Tobias Hunger's avatar
Tobias Hunger committed
966
    return f.write(doc.toByteArray(4)) >= 0;
BogDan Vatra's avatar
BogDan Vatra committed
967 968
}

Tobias Hunger's avatar
Tobias Hunger committed
969
bool AndroidManager::openManifest(ProjectExplorer::Target *target, QDomDocument &doc)
BogDan Vatra's avatar
BogDan Vatra committed
970
{
971
    return openXmlFile(doc, manifestPath(target));
BogDan Vatra's avatar
BogDan Vatra committed
972 973
}

Tobias Hunger's avatar
Tobias Hunger committed
974
bool AndroidManager::saveManifest(ProjectExplorer::Target *target, QDomDocument &doc)
BogDan Vatra's avatar
BogDan Vatra committed
975
{
976
    Core::FileChangeBlocker blocker(manifestPath(target).toString());
Tobias Hunger's avatar
Tobias Hunger committed
977
    return saveXmlFile(target, doc, manifestPath(target));
BogDan Vatra's avatar
BogDan Vatra committed
978 979
}

Tobias Hunger's avatar
Tobias Hunger committed
980
QString AndroidManager::iconPath(ProjectExplorer::Target *target, AndroidManager::IconType type)
BogDan Vatra's avatar
BogDan Vatra committed
981 982 983
{
    switch (type) {
    case HighDPI:
Tobias Hunger's avatar
Tobias Hunger committed
984
        return dirPath(target).appendPath(QLatin1String("res/drawable-hdpi/icon.png")).toString();
BogDan Vatra's avatar
BogDan Vatra committed
985
    case MediumDPI:
Tobias Hunger's avatar
Tobias Hunger committed
986
        return dirPath(target).appendPath(QLatin1String("res/drawable-mdpi/icon.png")).toString();
BogDan Vatra's avatar
BogDan Vatra committed
987
    case LowDPI:
Tobias Hunger's avatar
Tobias Hunger committed
988 989 990
        return dirPath(target).appendPath(QLatin1String("res/drawable-ldpi/icon.png")).toString();
    default:
        return QString();
BogDan Vatra's avatar
BogDan Vatra committed
991 992 993
    }
}

Tobias Hunger's avatar
Tobias Hunger committed
994
QStringList AndroidManager::libsXml(ProjectExplorer::Target *target, const QString &tag)