androidmanager.cpp 43.4 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 65 66 67

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

namespace {
    const QLatin1String AndroidDirName("android");
    const QLatin1String AndroidManifestName("AndroidManifest.xml");
    const QLatin1String AndroidLibsFileName("/res/values/libs.xml");
    const QLatin1String AndroidStringsFileName("/res/values/strings.xml");
    const QLatin1String AndroidDefaultPropertiesName("project.properties");

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

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

74 75 76 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
        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
118 119 120 121 122 123 124
        return packageName;
    }
} // anonymous namespace

namespace Android {
namespace Internal {

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

Tobias Hunger's avatar
Tobias Hunger committed
133 134 135 136 137 138 139
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
140 141
}

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

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

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

Tobias Hunger's avatar
Tobias Hunger committed
184 185 186 187 188 189 190 191 192 193 194 195 196
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
197

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

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

    return saveManifest(target, doc);
BogDan Vatra's avatar
BogDan Vatra committed
217 218
}

Tobias Hunger's avatar
Tobias Hunger committed
219
QString AndroidManager::intentName(ProjectExplorer::Target *target)
BogDan Vatra's avatar
BogDan Vatra committed
220
{
Tobias Hunger's avatar
Tobias Hunger committed
221
    return packageName(target) + QLatin1Char('/') + activityName(target);
BogDan Vatra's avatar
BogDan Vatra committed
222 223
}

Tobias Hunger's avatar
Tobias Hunger committed
224
QString AndroidManager::activityName(ProjectExplorer::Target *target)
BogDan Vatra's avatar
BogDan Vatra committed
225
{
Tobias Hunger's avatar
Tobias Hunger committed
226 227 228 229 230
    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
231 232
}

Tobias Hunger's avatar
Tobias Hunger committed
233
int AndroidManager::versionCode(ProjectExplorer::Target *target)
BogDan Vatra's avatar
BogDan Vatra committed
234
{
Tobias Hunger's avatar
Tobias Hunger committed
235 236 237 238 239 240
    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
241

Tobias Hunger's avatar
Tobias Hunger committed
242 243 244 245 246 247 248 249 250
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
251

Tobias Hunger's avatar
Tobias Hunger committed
252 253 254 255 256 257 258 259
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
260

Tobias Hunger's avatar
Tobias Hunger committed
261 262 263 264 265 266 267 268
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
269 270
}

Daniel Teske's avatar
Daniel Teske committed
271 272 273 274 275 276 277 278 279 280
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
281
QString AndroidManager::targetSDK(ProjectExplorer::Target *target)
BogDan Vatra's avatar
BogDan Vatra committed
282
{
283 284 285 286 287
    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
288
    if (!createAndroidTemplatesIfNecessary(target))
289
        return AndroidConfigurations::instance().bestMatch(fallback);
Tobias Hunger's avatar
Tobias Hunger committed
290 291
    QFile file(defaultPropertiesPath(target).toString());
    if (!file.open(QIODevice::ReadOnly))
292
        return AndroidConfigurations::instance().bestMatch(fallback);
Tobias Hunger's avatar
Tobias Hunger committed
293 294 295 296 297
    while (!file.atEnd()) {
        QByteArray line = file.readLine();
        if (line.startsWith("target="))
            return QString::fromLatin1(line.trimmed().mid(7));
    }
298
    return AndroidConfigurations::instance().bestMatch(fallback);
Tobias Hunger's avatar
Tobias Hunger committed
299
}
BogDan Vatra's avatar
BogDan Vatra committed
300

Tobias Hunger's avatar
Tobias Hunger committed
301 302 303 304 305
bool AndroidManager::setTargetSDK(ProjectExplorer::Target *target, const QString &sdk)
{
    updateTarget(target, sdk, applicationName(target));
    return true;
}
BogDan Vatra's avatar
BogDan Vatra committed
306

Tobias Hunger's avatar
Tobias Hunger committed
307 308 309
QIcon AndroidManager::highDpiIcon(ProjectExplorer::Target *target)
{
    return icon(target, HighDPI);
BogDan Vatra's avatar
BogDan Vatra committed
310 311
}

Tobias Hunger's avatar
Tobias Hunger committed
312
bool AndroidManager::setHighDpiIcon(ProjectExplorer::Target *target, const QString &iconFilePath)
BogDan Vatra's avatar
BogDan Vatra committed
313
{
Daniel Teske's avatar
Daniel Teske committed
314 315
    return ensureIconAttribute(target) &&
            setIcon(target, HighDPI, iconFilePath);
BogDan Vatra's avatar
BogDan Vatra committed
316 317
}

Tobias Hunger's avatar
Tobias Hunger committed
318
QIcon AndroidManager::mediumDpiIcon(ProjectExplorer::Target *target)
BogDan Vatra's avatar
BogDan Vatra committed
319
{
Tobias Hunger's avatar
Tobias Hunger committed
320
    return icon(target, MediumDPI);
BogDan Vatra's avatar
BogDan Vatra committed
321 322
}

Tobias Hunger's avatar
Tobias Hunger committed
323
bool AndroidManager::setMediumDpiIcon(ProjectExplorer::Target *target, const QString &iconFilePath)
BogDan Vatra's avatar
BogDan Vatra committed
324
{
Daniel Teske's avatar
Daniel Teske committed
325 326
    return ensureIconAttribute(target) &&
            setIcon(target, MediumDPI, iconFilePath);
BogDan Vatra's avatar
BogDan Vatra committed
327 328
}

Tobias Hunger's avatar
Tobias Hunger committed
329
QIcon AndroidManager::lowDpiIcon(ProjectExplorer::Target *target)
BogDan Vatra's avatar
BogDan Vatra committed
330
{
Tobias Hunger's avatar
Tobias Hunger committed
331
    return icon(target, LowDPI);
BogDan Vatra's avatar
BogDan Vatra committed
332 333
}

Tobias Hunger's avatar
Tobias Hunger committed
334
bool AndroidManager::setLowDpiIcon(ProjectExplorer::Target *target, const QString &iconFilePath)
BogDan Vatra's avatar
BogDan Vatra committed
335
{
Daniel Teske's avatar
Daniel Teske committed
336 337
    return ensureIconAttribute(target) &&
            setIcon(target, LowDPI, iconFilePath);
BogDan Vatra's avatar
BogDan Vatra committed
338 339
}

Tobias Hunger's avatar
Tobias Hunger committed
340
Utils::FileName AndroidManager::dirPath(ProjectExplorer::Target *target)
BogDan Vatra's avatar
BogDan Vatra committed
341
{
Tobias Hunger's avatar
Tobias Hunger committed
342
    return Utils::FileName::fromString(target->project()->projectDirectory()).appendPath(AndroidDirName);
BogDan Vatra's avatar
BogDan Vatra committed
343 344
}

Tobias Hunger's avatar
Tobias Hunger committed
345
Utils::FileName AndroidManager::manifestPath(ProjectExplorer::Target *target)
BogDan Vatra's avatar
BogDan Vatra committed
346
{
Tobias Hunger's avatar
Tobias Hunger committed
347
    return dirPath(target).appendPath(AndroidManifestName);
BogDan Vatra's avatar
BogDan Vatra committed
348 349
}

Tobias Hunger's avatar
Tobias Hunger committed
350
Utils::FileName AndroidManager::libsPath(ProjectExplorer::Target *target)
BogDan Vatra's avatar
BogDan Vatra committed
351
{
Tobias Hunger's avatar
Tobias Hunger committed
352 353
    return dirPath(target).appendPath(AndroidLibsFileName);
}
BogDan Vatra's avatar
BogDan Vatra committed
354

Tobias Hunger's avatar
Tobias Hunger committed
355 356 357
Utils::FileName AndroidManager::stringsPath(ProjectExplorer::Target *target)
{
    return dirPath(target).append(AndroidStringsFileName);
BogDan Vatra's avatar
BogDan Vatra committed
358 359
}

Tobias Hunger's avatar
Tobias Hunger committed
360
Utils::FileName AndroidManager::defaultPropertiesPath(ProjectExplorer::Target *target)
BogDan Vatra's avatar
BogDan Vatra committed
361
{
Tobias Hunger's avatar
Tobias Hunger committed
362 363
    return dirPath(target).appendPath(AndroidDefaultPropertiesName);
}
BogDan Vatra's avatar
BogDan Vatra committed
364

Tobias Hunger's avatar
Tobias Hunger committed
365 366 367 368
Utils::FileName AndroidManager::srcPath(ProjectExplorer::Target *target)
{
    return dirPath(target).appendPath(QLatin1String("/src"));
}
BogDan Vatra's avatar
BogDan Vatra committed
369

Tobias Hunger's avatar
Tobias Hunger committed
370 371 372 373 374 375 376 377 378 379 380 381
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
382

Tobias Hunger's avatar
Tobias Hunger committed
383 384 385 386 387 388 389 390 391 392 393
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
394 395
        }
    }
Tobias Hunger's avatar
Tobias Hunger committed
396 397 398
    apps.sort();
    return apps;
}
BogDan Vatra's avatar
BogDan Vatra committed
399

Tobias Hunger's avatar
Tobias Hunger committed
400 401 402 403 404 405 406 407 408 409 410 411 412
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
413

414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439
bool AndroidManager::setUseLocalLibs(ProjectExplorer::Target *target, bool useLocalLibs, int deviceAPILevel)
{
    // For Qt 4, the "use local libs" options is handled by passing command line arguments to the
    // app, so no need to alter the AndroidManifest.xml
    QtSupport::BaseQtVersion *baseQtVersion = QtSupport::QtKitInformation::qtVersion(target->kit());
    if (baseQtVersion == 0 || baseQtVersion->qtVersion() < QtSupport::QtVersionNumber(5,0,0))
        return true;

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

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

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

    bool changedManifest = false;
    while (!metadataElem.isNull()) {
        if (metadataElem.attribute(QLatin1String("android:name")) == QLatin1String("android.app.use_local_qt_libs")) {
Orgad Shaneh's avatar
Orgad Shaneh committed
440
            if (metadataElem.attribute(QLatin1String("android:value")).toInt() != int(useLocalLibs)) {
441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469
                metadataElem.setAttribute(QLatin1String("android:value"), int(useLocalLibs));
                changedManifest = true;
            }
        } else if (metadataElem.attribute(QLatin1String("android:name")) == QLatin1String("android.app.load_local_libs")) {
            if (metadataElem.attribute(QLatin1String("android:value")) != localLibs) {
                metadataElem.setAttribute(QLatin1String("android:value"), localLibs);
                changedManifest = true;
            }
        } else if (metadataElem.attribute(QLatin1String("android:name")) == QLatin1String("android.app.load_local_jars")) {
            if (metadataElem.attribute(QLatin1String("android:value")) != localJars) {
                metadataElem.setAttribute(QLatin1String("android:value"), localJars);
                changedManifest = true;
            }
        } else if (metadataElem.attribute(QLatin1String("android:name")) == QLatin1String("android.app.static_init_classes")) {
            if (metadataElem.attribute(QLatin1String("android:value")) != staticInitClasses) {
                metadataElem.setAttribute(QLatin1String("android:value"), staticInitClasses);
                changedManifest = true;
            }
        }

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

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

Tobias Hunger's avatar
Tobias Hunger committed
470 471 472 473 474 475 476 477 478 479
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
480
        }
Tobias Hunger's avatar
Tobias Hunger committed
481
        metadataElem = metadataElem.nextSiblingElement(QLatin1String("meta-data"));
BogDan Vatra's avatar
BogDan Vatra committed
482
    }
Tobias Hunger's avatar
Tobias Hunger committed
483
    return false;
BogDan Vatra's avatar
BogDan Vatra committed
484 485
}

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

Tobias Hunger's avatar
Tobias Hunger committed
508
bool AndroidManager::createAndroidTemplatesIfNecessary(ProjectExplorer::Target *target)
BogDan Vatra's avatar
BogDan Vatra committed
509
{
Tobias Hunger's avatar
Tobias Hunger committed
510
    QtSupport::BaseQtVersion *version = QtSupport::QtKitInformation::qtVersion(target->kit());
Tobias Hunger's avatar
Tobias Hunger committed
511 512 513 514 515
    Qt4ProjectManager::Qt4Project *qt4Project = qobject_cast<Qt4ProjectManager::Qt4Project*>(target->project());
    if (!qt4Project || !qt4Project->rootProjectNode() || !version)
        return false;

    Utils::FileName javaSrcPath
516
            = Utils::FileName::fromString(version->qmakeProperty("QT_INSTALL_PREFIX"))
517
            .appendPath(QLatin1String("src/android/java"));
Tobias Hunger's avatar
Tobias Hunger committed
518 519 520 521 522 523
    QDir projectDir(qt4Project->projectDirectory());
    Utils::FileName androidPath = dirPath(target);

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

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

587 588 589 590 591 592
    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
593 594 595 596
    if (sdks.isEmpty()) {
        raiseError(tr("No Qt for Android SDKs were found.\nPlease install at least one SDK."));
        return false;
    }
597 598

    updateTarget(target, AndroidConfigurations::instance().sdkTargets(minApiLevel).at(0));
Tobias Hunger's avatar
Tobias Hunger committed
599 600 601 602 603 604 605 606 607 608 609 610 611 612 613
    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
614 615
}

Tobias Hunger's avatar
Tobias Hunger committed
616
void AndroidManager::updateTarget(ProjectExplorer::Target *target, const QString &targetSDK, const QString &name)
BogDan Vatra's avatar
BogDan Vatra committed
617
{
Tobias Hunger's avatar
Tobias Hunger committed
618
    QString androidDir = dirPath(target).toString();
BogDan Vatra's avatar
BogDan Vatra committed
619 620 621 622

    // clean previous build
    QProcess androidProc;
    androidProc.setWorkingDirectory(androidDir);
Tobias Hunger's avatar
Tobias Hunger committed
623 624
    androidProc.start(AndroidConfigurations::instance().antToolPath().toString(),
                      QStringList() << QLatin1String("clean"));
BogDan Vatra's avatar
BogDan Vatra committed
625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 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++) {
            if (lines[i].contains("@ANDROID-")) {
                commentLines = targetSDKNumber < lines[i].mid(lines[i].lastIndexOf('-') + 1).toInt();
                comment = !comment;
                continue;
            }
            if (!comment)
                continue;
            if (commentLines) {
                if (!lines[i].trimmed().startsWith("//QtCreator")) {
                    lines[i] = "//QtCreator " + lines[i];
                    modified = true;
                }
            } else { if (lines[i].trimmed().startsWith("//QtCreator")) {
                    lines[i] = lines[i].mid(12);
                    modified = true;
                }
            }
        }
        if (modified) {
            file.resize(0);
            foreach (const QByteArray &line, lines) {
                file.write(line);
                file.write("\n");
            }
        }
        file.close();
    }

    QStringList params;
    params << QLatin1String("update") << QLatin1String("project") << QLatin1String("-p") << androidDir;
    if (!targetSDK.isEmpty())
        params << QLatin1String("-t") << targetSDK;
    if (!name.isEmpty())
        params << QLatin1String("-n") << name;
Tobias Hunger's avatar
Tobias Hunger committed
676
    androidProc.start(AndroidConfigurations::instance().androidToolPath().toString(), params);
BogDan Vatra's avatar
BogDan Vatra committed
677 678 679 680
    if (!androidProc.waitForFinished(-1))
        androidProc.terminate();
}

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

Tobias Hunger's avatar
Tobias Hunger committed
689 690 691 692
QString AndroidManager::loadLocalLibs(ProjectExplorer::Target *target, int apiLevel)
{
    return loadLocal(target, apiLevel, Lib);
}
BogDan Vatra's avatar
BogDan Vatra committed
693

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

699 700 701 702 703
QString AndroidManager::loadLocalJarsInitClasses(ProjectExplorer::Target *target, int apiLevel)
{
    return loadLocal(target, apiLevel, Jar, QLatin1String("initClass"));
}

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

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

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

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

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

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

Tobias Hunger's avatar
Tobias Hunger committed
742
    // clean dependencies
743 744 745
    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
746
        int it = 0;
747 748 749 750
        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);
751
            else
Tobias Hunger's avatar
Tobias Hunger committed
752
                ++it;
BogDan Vatra's avatar
BogDan Vatra committed
753
        }
754 755
        if (library.dependencies.isEmpty())
            library.level = 0;
BogDan Vatra's avatar
BogDan Vatra committed
756 757
    }

Tobias Hunger's avatar
Tobias Hunger committed
758 759
    QVector<Library> qtLibraries;
    // calculate the level for every library
760 761 762 763
    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
764
           setLibraryLevel(key, mapLibs);
BogDan Vatra's avatar
BogDan Vatra committed
765

766 767
        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
768

769 770
        for (int it = 0; it < library.dependencies.size(); it++) {
            const QString &libName = library.dependencies[it];
Tobias Hunger's avatar
Tobias Hunger committed
771
            if (libName.startsWith(QLatin1String("lib")) && libName.endsWith(QLatin1String(".so")))
772
                library.dependencies[it] = libName.mid(3, libName.length() - 6);
Tobias Hunger's avatar
Tobias Hunger committed
773
        }
774
        qtLibraries.push_back(library);
BogDan Vatra's avatar
BogDan Vatra committed
775
    }
Tobias Hunger's avatar
Tobias Hunger committed
776
    qSort(qtLibraries.begin(), qtLibraries.end(), qtLibrariesLessThan);
777 778 779 780 781 782 783 784 785 786

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

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

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

Tobias Hunger's avatar
Tobias Hunger committed
801
QStringList AndroidManager::availablePrebundledLibs(ProjectExplorer::Target *target)
BogDan Vatra's avatar
BogDan Vatra committed
802
{
Tobias Hunger's avatar
Tobias Hunger committed
803 804 805 806
    QStringList libs;
    Qt4ProjectManager::Qt4Project *qt4Project = qobject_cast<Qt4ProjectManager::Qt4Project *>(target->project());
    if (!qt4Project)
        return libs;
BogDan Vatra's avatar
BogDan Vatra committed
807

Tobias Hunger's avatar
Tobias Hunger committed
808 809 810 811
    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
812 813
}

Tobias Hunger's avatar
Tobias Hunger committed
814
QStringList AndroidManager::prebundledLibs(ProjectExplorer::Target *target)
BogDan Vatra's avatar
BogDan Vatra committed
815
{
Tobias Hunger's avatar
Tobias Hunger committed
816
    return libsXml(target, QLatin1String("bundled_libs"));
BogDan Vatra's avatar
BogDan Vatra committed
817 818
}

Tobias Hunger's avatar
Tobias Hunger committed
819
bool AndroidManager::setPrebundledLibs(ProjectExplorer::Target *target, const QStringList &libs)
BogDan Vatra's avatar
BogDan Vatra committed
820
{
Tobias Hunger's avatar
Tobias Hunger committed
821
    return setLibsXml(target, libs, QLatin1String("bundled_libs"));
BogDan Vatra's avatar
BogDan Vatra committed
822 823
}

Tobias Hunger's avatar
Tobias Hunger committed
824
bool AndroidManager::openLibsXml(ProjectExplorer::Target *target, QDomDocument &doc)
BogDan Vatra's avatar
BogDan Vatra committed
825
{
826
    return openXmlFile(doc, libsPath(target));
BogDan Vatra's avatar
BogDan Vatra committed
827 828
}

Tobias Hunger's avatar
Tobias Hunger committed
829
bool AndroidManager::saveLibsXml(ProjectExplorer::Target *target, QDomDocument &doc)
BogDan Vatra's avatar
BogDan Vatra committed
830
{
Tobias Hunger's avatar
Tobias Hunger committed
831
    return saveXmlFile(target, doc, libsPath(target));
BogDan Vatra's avatar
BogDan Vatra committed
832 833
}

Tobias Hunger's avatar
Tobias Hunger committed
834
void AndroidManager::raiseError(const QString &reason)
BogDan Vatra's avatar
BogDan Vatra committed
835
{
Tobias Hunger's avatar
Tobias Hunger committed
836
    QMessageBox::critical(0, tr("Error creating Android templates"), reason);
BogDan Vatra's avatar
BogDan Vatra committed
837 838
}

839
QString AndroidManager::loadLocal(ProjectExplorer::Target *target, int apiLevel, ItemType item, const QString &attribute)
BogDan Vatra's avatar
BogDan Vatra committed
840
{
Tobias Hunger's avatar
Tobias Hunger committed
841 842 843 844 845 846 847
    QString itemType;
    if (item == Lib)
        itemType = QLatin1String("lib");
    else
        itemType = QLatin1String("jar");

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

849 850
    QDir rulesFilesDir(localLibsRulesFilePath(target).toString());
    if (!rulesFilesDir.exists())
Tobias Hunger's avatar
Tobias Hunger committed
851 852 853 854
        return localLibs;

    QStringList libs;
    libs << qtLibs(target) << prebundledLibs(target);
855 856 857 858 859 860 861 862 863 864

    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
865 866
        }

867 868 869 870 871 872 873 874 875 876
        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
877
            }
878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907
            element = element.nextSiblingElement(QLatin1String("version"));
        }

        element = doc.documentElement().firstChildElement(QLatin1String("dependencies")).firstChildElement(QLatin1String("lib"));
        while (!element.isNull()) {
            if (libs.contains(element.attribute(QLatin1String("name")))) {
                QDomElement libElement = element.firstChildElement(QLatin1String("depends")).firstChildElement(itemType);
                while (!libElement.isNull()) {
                    if (libElement.hasAttribute(attribute)) {
                        QString dependencyLib = libElement.attribute(attribute).arg(apiLevel);
                        if (!dependencyLibs.contains(dependencyLib))
                            dependencyLibs << dependencyLib;
                    }

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

                    libElement = libElement.nextSiblingElement(itemType);
                }

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

909 910
                    libElement = libElement.nextSiblingElement(itemType);
                }
BogDan Vatra's avatar
BogDan Vatra committed
911
            }
912
            element = element.nextSiblingElement(QLatin1String("lib"));
BogDan Vatra's avatar
BogDan Vatra committed
913 914
        }
    }
915 916 917

    // 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
918
    localLibs = dependencyLibs.join(QLatin1String(":")) + QLatin1Char(':');
919 920 921
    foreach (QString replacedLib, replacedLibs)
        localLibs.remove(replacedLib + QLatin1Char(':'));

Tobias Hunger's avatar
Tobias Hunger committed
922
    return localLibs;
BogDan Vatra's avatar
BogDan Vatra committed
923 924
}

925
bool AndroidManager::openXmlFile(QDomDocument &doc, const Utils::FileName &fileName)
BogDan Vatra's avatar
BogDan Vatra committed
926
{
Tobias Hunger's avatar
Tobias Hunger committed
927 928
    QFile f(fileName.toString());
    if (!f.open(QIODevice::ReadOnly))
BogDan Vatra's avatar
BogDan Vatra committed
929 930
        return false;

Tobias Hunger's avatar
Tobias Hunger committed
931 932
    if (!doc.setContent(f.readAll())) {
        raiseError(tr("Can't parse '%1'").arg(fileName.toUserOutput()));
BogDan Vatra's avatar
BogDan Vatra committed
933 934
        return false;
    }
Tobias Hunger's avatar
Tobias Hunger committed
935
    return true;
BogDan Vatra's avatar
BogDan Vatra committed
936 937
}

Tobias Hunger's avatar
Tobias Hunger committed
938
bool AndroidManager::saveXmlFile(ProjectExplorer::Target *target, QDomDocument &doc, const Utils::FileName &fileName)
BogDan Vatra's avatar
BogDan Vatra committed
939
{
Tobias Hunger's avatar
Tobias Hunger committed
940 941
    if (!createAndroidTemplatesIfNecessary(target))
        return false;
BogDan Vatra's avatar
BogDan Vatra committed
942

Tobias Hunger's avatar
Tobias Hunger committed
943 944 945 946
    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
947
    }
Tobias Hunger's avatar
Tobias Hunger committed
948
    return f.write(doc.toByteArray(4)) >= 0;
BogDan Vatra's avatar
BogDan Vatra committed
949 950
}

Tobias Hunger's avatar
Tobias Hunger committed
951
bool AndroidManager::openManifest(ProjectExplorer::Target *target, QDomDocument &doc)
BogDan Vatra's avatar
BogDan Vatra committed
952
{
953
    return openXmlFile(doc, manifestPath(target));
BogDan Vatra's avatar
BogDan Vatra committed
954 955
}

Tobias Hunger's avatar
Tobias Hunger committed
956
bool AndroidManager::saveManifest(ProjectExplorer::Target *target, QDomDocument &doc)
BogDan Vatra's avatar
BogDan Vatra committed
957
{
958
    Core::FileChangeBlocker blocker(manifestPath(target).toString());
Tobias Hunger's avatar
Tobias Hunger committed
959
    return saveXmlFile(target, doc, manifestPath(target));
BogDan Vatra's avatar
BogDan Vatra committed
960 961
}

Tobias Hunger's avatar
Tobias Hunger committed
962
QString AndroidManager::iconPath(ProjectExplorer::Target *target, AndroidManager::IconType type)
BogDan Vatra's avatar
BogDan Vatra committed
963 964 965
{
    switch (type) {
    case HighDPI:
Tobias Hunger's avatar
Tobias Hunger committed
966
        return dirPath(target).appendPath(QLatin1String("res/drawable-hdpi/icon.png")).toString();
BogDan Vatra's avatar
BogDan Vatra committed
967
    case MediumDPI:
Tobias Hunger's avatar
Tobias Hunger committed
968
        return dirPath(target).appendPath(QLatin1String("res/drawable-mdpi/icon.png")).toString();
BogDan Vatra's avatar
BogDan Vatra committed
969
    case LowDPI:
Tobias Hunger's avatar
Tobias Hunger committed
970 971 972
        return dirPath(target).appendPath(QLatin1String("res/drawable-ldpi/icon.png")).toString();
    default:
        return QString();
BogDan Vatra's avatar
BogDan Vatra committed
973 974 975
    }
}

Tobias Hunger's avatar
Tobias Hunger committed
976
QStringList AndroidManager::libsXml(ProjectExplorer::Target *target, const QString &tag)
BogDan Vatra's avatar
BogDan Vatra committed
977 978 979
{
    QStringList libs;
    QDomDocument doc;
Tobias Hunger's avatar
Tobias Hunger committed
980
    if (!openLibsXml(target, doc))
BogDan Vatra's avatar
BogDan Vatra committed
981 982 983 984