androidmanager.cpp 21.4 KB
Newer Older
1
/****************************************************************************
BogDan Vatra's avatar
BogDan Vatra committed
2
**
3 4
** Copyright (C) 2016 BogDan Vatra <bog_dan_ro@yahoo.com>
** Contact: https://www.qt.io/licensing/
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
** 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
12 13 14
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
BogDan Vatra's avatar
BogDan Vatra committed
15
**
16 17 18 19 20 21 22
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
BogDan Vatra's avatar
BogDan Vatra committed
23
**
hjk's avatar
hjk committed
24
****************************************************************************/
BogDan Vatra's avatar
BogDan Vatra committed
25

Tobias Hunger's avatar
Tobias Hunger committed
26
#include "androidmanager.h"
27
#include "androidconstants.h"
BogDan Vatra's avatar
BogDan Vatra committed
28 29 30 31
#include "androiddeployconfiguration.h"
#include "androidconfigurations.h"
#include "androidrunconfiguration.h"
#include "androidglobal.h"
32
#include "androidtoolchain.h"
33
#include "androiddeployqtstep.h"
34
#include "androidqtsupport.h"
35 36
#include "androidqtversion.h"
#include "androidbuildapkstep.h"
Vikas Pachdha's avatar
Vikas Pachdha committed
37
#include "androidavdmanager.h"
38
#include "androidsdkmanager.h"
BogDan Vatra's avatar
BogDan Vatra committed
39

40
#include <coreplugin/documentmanager.h>
41
#include <coreplugin/messagemanager.h>
42
#include <coreplugin/icore.h>
43

44 45
#include <projectexplorer/buildconfiguration.h>
#include <projectexplorer/project.h>
Tobias Hunger's avatar
Tobias Hunger committed
46 47 48
#include <projectexplorer/projectexplorer.h>
#include <projectexplorer/session.h>
#include <projectexplorer/target.h>
49

Tobias Hunger's avatar
Tobias Hunger committed
50
#include <qtsupport/qtkitinformation.h>
Tobias Hunger's avatar
Tobias Hunger committed
51
#include <qtsupport/qtsupportconstants.h>
52
#include <utils/algorithm.h>
53
#include <utils/synchronousprocess.h>
BogDan Vatra's avatar
BogDan Vatra committed
54 55 56 57 58

#include <QDir>
#include <QFileSystemWatcher>
#include <QList>
#include <QProcess>
Samuel Gaist's avatar
Samuel Gaist committed
59
#include <QRegExp>
BogDan Vatra's avatar
BogDan Vatra committed
60 61 62
#include <QMessageBox>
#include <QApplication>
#include <QDomDocument>
63
#include <QVersionNumber>
BogDan Vatra's avatar
BogDan Vatra committed
64 65 66 67

namespace {
    const QLatin1String AndroidManifestName("AndroidManifest.xml");
    const QLatin1String AndroidDefaultPropertiesName("project.properties");
68
    const QLatin1String AndroidDeviceSn("AndroidDeviceSerialNumber");
69
    const QLatin1String ApiLevelKey("AndroidVersion.ApiLevel");
70

BogDan Vatra's avatar
BogDan Vatra committed
71 72 73 74
} // anonymous namespace

namespace Android {

75 76
using namespace Internal;

77 78 79 80 81 82 83 84 85 86 87 88 89 90
class Library
{
public:
    Library()
    { level = -1; }
    int level;
    QStringList dependencies;
    QString name;
};

typedef QMap<QString, Library> LibrariesMap;

static bool openXmlFile(QDomDocument &doc, const Utils::FileName &fileName);
static bool openManifest(ProjectExplorer::Target *target, QDomDocument &doc);
91
static int parseMinSdk(const QDomElement &manifestElem);
92

93 94 95 96 97 98 99 100 101 102 103 104
static QList<AndroidQtSupport *> g_androidQtSupportProviders;

AndroidQtSupport::AndroidQtSupport()
{
    g_androidQtSupportProviders.append(this);
}

AndroidQtSupport::~AndroidQtSupport()
{
    g_androidQtSupportProviders.removeOne(this);
}

105
bool AndroidManager::supportsAndroid(const ProjectExplorer::Kit *kit)
BogDan Vatra's avatar
BogDan Vatra committed
106
{
107
    QtSupport::BaseQtVersion *version = QtSupport::QtKitInformation::qtVersion(kit);
108
    return version && version->targetDeviceTypes().contains(Constants::ANDROID_DEVICE_TYPE);
Tobias Hunger's avatar
Tobias Hunger committed
109
}
BogDan Vatra's avatar
BogDan Vatra committed
110

111 112 113 114 115
bool AndroidManager::supportsAndroid(const ProjectExplorer::Target *target)
{
    return supportsAndroid(target->kit());
}

Tobias Hunger's avatar
Tobias Hunger committed
116 117 118 119 120 121 122
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
123 124
}

125 126 127 128 129 130 131 132 133
QString AndroidManager::packageName(const Utils::FileName &manifestFile)
{
    QDomDocument doc;
    if (!openXmlFile(doc, manifestFile))
        return QString();
    QDomElement manifestElem = doc.documentElement();
    return manifestElem.attribute(QLatin1String("package"));
}

Tobias Hunger's avatar
Tobias Hunger committed
134
QString AndroidManager::intentName(ProjectExplorer::Target *target)
BogDan Vatra's avatar
BogDan Vatra committed
135
{
Tobias Hunger's avatar
Tobias Hunger committed
136
    return packageName(target) + QLatin1Char('/') + activityName(target);
BogDan Vatra's avatar
BogDan Vatra committed
137 138
}

Tobias Hunger's avatar
Tobias Hunger committed
139
QString AndroidManager::activityName(ProjectExplorer::Target *target)
BogDan Vatra's avatar
BogDan Vatra committed
140
{
Tobias Hunger's avatar
Tobias Hunger committed
141 142 143 144 145
    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
146 147
}

148 149 150 151 152
/*!
    Returns the minimum Android API level set for the APK. Minimum API level
    of the kit is returned if the manifest file of the APK can not be found
    or parsed.
*/
153 154 155
int AndroidManager::minimumSDK(ProjectExplorer::Target *target)
{
    QDomDocument doc;
156
    if (!openXmlFile(doc, AndroidManager::manifestSourcePath(target)))
157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176
        return minimumSDK(target->kit());
    return parseMinSdk(doc.documentElement());
}

/*!
    Returns the minimum Android API level required by the kit to compile. -1 is
    returned if the kit does not support Android.
*/
int AndroidManager::minimumSDK(const ProjectExplorer::Kit *kit)
{
    int minSDKVersion = -1;
    if (supportsAndroid(kit)) {
        QtSupport::BaseQtVersion *version = QtSupport::QtKitInformation::qtVersion(kit);
        Utils::FileName stockManifestFilePath =
                Utils::FileName::fromUserInput(version->qmakeProperty("QT_INSTALL_PREFIX") +
                                               QLatin1String("/src/android/templates/AndroidManifest.xml"));
        QDomDocument doc;
        if (openXmlFile(doc, stockManifestFilePath)) {
            minSDKVersion = parseMinSdk(doc.documentElement());
        }
177
    }
178
    return minSDKVersion;
179 180
}

181
QString AndroidManager::buildTargetSDK(ProjectExplorer::Target *target)
BogDan Vatra's avatar
BogDan Vatra committed
182
{
183 184 185 186 187
    AndroidBuildApkStep *androidBuildApkStep
            = AndroidGlobal::buildStep<AndroidBuildApkStep>(target->activeBuildConfiguration());
    if (androidBuildApkStep)
        return androidBuildApkStep->buildTargetSdk();

188 189
    QString fallback = AndroidConfig::apiLevelNameFor(
                AndroidConfigurations::sdkManager()->latestAndroidSdkPlatform());
190 191 192 193 194 195 196 197 198 199
    return fallback;
}

bool AndroidManager::signPackage(ProjectExplorer::Target *target)
{
    AndroidBuildApkStep *androidBuildApkStep
            = AndroidGlobal::buildStep<AndroidBuildApkStep>(target->activeBuildConfiguration());
    if (androidBuildApkStep)
        return androidBuildApkStep->signPackage();
    return false;
Tobias Hunger's avatar
Tobias Hunger committed
200
}
BogDan Vatra's avatar
BogDan Vatra committed
201

202 203
QString AndroidManager::targetArch(ProjectExplorer::Target *target)
{
204 205
    AndroidQtVersion *qt = static_cast<AndroidQtVersion *>(QtSupport::QtKitInformation::qtVersion(target->kit()));
    return qt->targetArch();
206 207
}

Tobias Hunger's avatar
Tobias Hunger committed
208
Utils::FileName AndroidManager::dirPath(ProjectExplorer::Target *target)
BogDan Vatra's avatar
BogDan Vatra committed
209
{
210 211 212 213 214 215 216 217 218 219 220 221
    if (target->activeBuildConfiguration())
        return target->activeBuildConfiguration()->buildDirectory().appendPath(QLatin1String(Constants::ANDROID_BUILDDIRECTORY));
    return Utils::FileName();
}

Utils::FileName AndroidManager::manifestSourcePath(ProjectExplorer::Target *target)
{
    AndroidQtSupport *androidQtSupport = AndroidManager::androidQtSupport(target);
    Utils::FileName source = androidQtSupport->manifestSourcePath(target);
    if (!source.isEmpty())
        return source;
    return manifestPath(target);
BogDan Vatra's avatar
BogDan Vatra committed
222 223
}

Tobias Hunger's avatar
Tobias Hunger committed
224
Utils::FileName AndroidManager::manifestPath(ProjectExplorer::Target *target)
BogDan Vatra's avatar
BogDan Vatra committed
225
{
Tobias Hunger's avatar
Tobias Hunger committed
226
    return dirPath(target).appendPath(AndroidManifestName);
BogDan Vatra's avatar
BogDan Vatra committed
227 228
}

Tobias Hunger's avatar
Tobias Hunger committed
229
Utils::FileName AndroidManager::defaultPropertiesPath(ProjectExplorer::Target *target)
BogDan Vatra's avatar
BogDan Vatra committed
230
{
Tobias Hunger's avatar
Tobias Hunger committed
231 232
    return dirPath(target).appendPath(AndroidDefaultPropertiesName);
}
BogDan Vatra's avatar
BogDan Vatra committed
233

234 235
bool AndroidManager::bundleQt(ProjectExplorer::Target *target)
{
236 237 238
    AndroidBuildApkStep *androidBuildApkStep
            = AndroidGlobal::buildStep<AndroidBuildApkStep>(target->activeBuildConfiguration());
    if (androidBuildApkStep)
239
        return !androidBuildApkStep->useMinistro();
240 241 242 243

    return false;
}

244 245
QString AndroidManager::deviceSerialNumber(ProjectExplorer::Target *target)
{
246 247 248 249 250 251
    return target->namedSettings(AndroidDeviceSn).toString();
}

void AndroidManager::setDeviceSerialNumber(ProjectExplorer::Target *target, const QString &deviceSerialNumber)
{
    target->setNamedSettings(AndroidDeviceSn, deviceSerialNumber);
252 253
}

254
QPair<int, int> AndroidManager::apiLevelRange()
255
{
256
    return qMakePair(9, 26);
257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291
}

QString AndroidManager::androidNameForApiLevel(int x)
{
    switch (x) {
    case 4:
        return QLatin1String("Android 1.6");
    case 5:
        return QLatin1String("Android 2.0");
    case 6:
        return QLatin1String("Android 2.0.1");
    case 7:
        return QLatin1String("Android 2.1.x");
    case 8:
        return QLatin1String("Android 2.2.x");
    case 9:
        return QLatin1String("Android 2.3, 2.3.1, 2.3.2");
    case 10:
        return QLatin1String("Android 2.3.3, 2.3.4");
    case 11:
        return QLatin1String("Android 3.0.x");
    case 12:
        return QLatin1String("Android 3.1.x");
    case 13:
        return QLatin1String("Android 3.2");
    case 14:
        return QLatin1String("Android 4.0, 4.0.1, 4.0.2");
    case 15:
        return QLatin1String("Android 4.0.3, 4.0.4");
    case 16:
        return QLatin1String("Android 4.1, 4.1.1");
    case 17:
        return QLatin1String("Android 4.2, 4.2.2");
    case 18:
        return QLatin1String("Android 4.3");
292 293
    case 19:
        return QLatin1String("Android 4.4");
294
    case 20:
295
        return QLatin1String("Android 4.4W");
296 297
    case 21:
        return QLatin1String("Android 5.0");
298 299
    case 22:
        return QLatin1String("Android 5.1");
300 301
    case 23:
        return QLatin1String("Android 6.0");
302 303
    case 24:
        return QLatin1String("Android 7.0");
BogDan Vatra's avatar
BogDan Vatra committed
304 305
    case 25:
        return QLatin1String("Android 7.1");
306 307
    case 26:
        return QLatin1String("Android 8.0");
308
    default:
309
        return tr("Unknown Android version. API Level: %1").arg(QString::number(x));
310 311 312
    }
}

313
static void raiseError(const QString &reason)
BogDan Vatra's avatar
BogDan Vatra committed
314
{
315
    QMessageBox::critical(0, AndroidManager::tr("Error creating Android templates."), reason);
BogDan Vatra's avatar
BogDan Vatra committed
316 317
}

318
static bool openXmlFile(QDomDocument &doc, const Utils::FileName &fileName)
BogDan Vatra's avatar
BogDan Vatra committed
319
{
Tobias Hunger's avatar
Tobias Hunger committed
320 321
    QFile f(fileName.toString());
    if (!f.open(QIODevice::ReadOnly))
BogDan Vatra's avatar
BogDan Vatra committed
322 323
        return false;

Tobias Hunger's avatar
Tobias Hunger committed
324
    if (!doc.setContent(f.readAll())) {
325
        raiseError(AndroidManager::tr("Cannot parse \"%1\".").arg(fileName.toUserOutput()));
BogDan Vatra's avatar
BogDan Vatra committed
326 327
        return false;
    }
Tobias Hunger's avatar
Tobias Hunger committed
328
    return true;
BogDan Vatra's avatar
BogDan Vatra committed
329 330
}

331
static bool openManifest(ProjectExplorer::Target *target, QDomDocument &doc)
BogDan Vatra's avatar
BogDan Vatra committed
332
{
333
    return openXmlFile(doc, AndroidManager::manifestPath(target));
BogDan Vatra's avatar
BogDan Vatra committed
334 335
}

336 337 338 339 340 341 342 343 344 345 346 347 348 349
static int parseMinSdk(const QDomElement &manifestElem)
{
    QDomElement usesSdk = manifestElem.firstChildElement(QLatin1String("uses-sdk"));
    if (usesSdk.isNull())
        return 0;
    if (usesSdk.hasAttribute(QLatin1String("android:minSdkVersion"))) {
        bool ok;
        int tmp = usesSdk.attribute(QLatin1String("android:minSdkVersion")).toInt(&ok);
        if (ok)
            return tmp;
    }
    return 0;
}

350 351 352
void AndroidManager::cleanLibsOnDevice(ProjectExplorer::Target *target)
{
    const QString targetArch = AndroidManager::targetArch(target);
353 354
    if (targetArch.isEmpty())
        return;
355
    const int deviceAPILevel = AndroidManager::minimumSDK(target);
356
    AndroidDeviceInfo info = AndroidConfigurations::showDeviceDialog(target->project(), deviceAPILevel, targetArch);
357
    if (!info.isValid()) // aborted
358 359 360 361 362
        return;

    QString deviceSerialNumber = info.serialNumber;

    if (info.type == AndroidDeviceInfo::Emulator) {
Vikas Pachdha's avatar
Vikas Pachdha committed
363
        deviceSerialNumber = AndroidAvdManager().startAvd(info.avdname);
364
        if (deviceSerialNumber.isEmpty())
365
            Core::MessageManager::write(tr("Starting Android virtual device failed."));
366 367 368 369 370
    }

    QProcess *process = new QProcess();
    QStringList arguments = AndroidDeviceInfo::adbSelector(deviceSerialNumber);
    arguments << QLatin1String("shell") << QLatin1String("rm") << QLatin1String("-r") << QLatin1String("/data/local/tmp/qt");
Orgad Shaneh's avatar
Orgad Shaneh committed
371 372
    QObject::connect(process, static_cast<void (QProcess::*)(int)>(&QProcess::finished),
                     process, &QObject::deleteLater);
Daniel Teske's avatar
Daniel Teske committed
373
    const QString adb = AndroidConfigurations::currentConfig().adbToolPath().toString();
hjk's avatar
hjk committed
374
    Core::MessageManager::write(adb + QLatin1Char(' ') + arguments.join(QLatin1Char(' ')));
375 376 377 378 379 380 381 382
    process->start(adb, arguments);
    if (!process->waitForStarted(500))
        delete process;
}

void AndroidManager::installQASIPackage(ProjectExplorer::Target *target, const QString &packagePath)
{
    const QString targetArch = AndroidManager::targetArch(target);
383 384
    if (targetArch.isEmpty())
        return;
385
    const int deviceAPILevel = AndroidManager::minimumSDK(target);
386
    AndroidDeviceInfo info = AndroidConfigurations::showDeviceDialog(target->project(), deviceAPILevel, targetArch);
387
    if (!info.isValid()) // aborted
388 389 390 391
        return;

    QString deviceSerialNumber = info.serialNumber;
    if (info.type == AndroidDeviceInfo::Emulator) {
Vikas Pachdha's avatar
Vikas Pachdha committed
392
        deviceSerialNumber = AndroidAvdManager().startAvd(info.avdname);
393
        if (deviceSerialNumber.isEmpty())
394
            Core::MessageManager::write(tr("Starting Android virtual device failed."));
395 396 397 398 399 400
    }

    QProcess *process = new QProcess();
    QStringList arguments = AndroidDeviceInfo::adbSelector(deviceSerialNumber);
    arguments << QLatin1String("install") << QLatin1String("-r ") << packagePath;

401 402
    connect(process, static_cast<void (QProcess::*)(int)>(&QProcess::finished),
            process, &QObject::deleteLater);
Daniel Teske's avatar
Daniel Teske committed
403
    const QString adb = AndroidConfigurations::currentConfig().adbToolPath().toString();
hjk's avatar
hjk committed
404
    Core::MessageManager::write(adb + QLatin1Char(' ') + arguments.join(QLatin1Char(' ')));
405
    process->start(adb, arguments);
406
    if (!process->waitForStarted(500) && process->state() != QProcess::Running)
407 408 409 410 411 412 413 414 415 416 417 418 419
        delete process;
}

bool AndroidManager::checkKeystorePassword(const QString &keystorePath, const QString &keystorePasswd)
{
    if (keystorePasswd.isEmpty())
        return false;
    QStringList arguments;
    arguments << QLatin1String("-list")
              << QLatin1String("-keystore")
              << keystorePath
              << QLatin1String("--storepass")
              << keystorePasswd;
420 421 422 423
    Utils::SynchronousProcess proc;
    proc.setTimeoutS(10);
    Utils::SynchronousProcessResponse response = proc.run(AndroidConfigurations::currentConfig().keytoolPath().toString(), arguments);
    return (response.result == Utils::SynchronousProcessResponse::Finished && response.exitCode == 0);
424 425 426 427 428
}

bool AndroidManager::checkCertificatePassword(const QString &keystorePath, const QString &keystorePasswd, const QString &alias, const QString &certificatePasswd)
{
    // assumes that the keystore password is correct
429 430
    QStringList arguments = {"-certreq", "-keystore", keystorePath,
                             "--storepass", keystorePasswd, "-alias", alias, "-keypass"};
431 432 433 434 435
    if (certificatePasswd.isEmpty())
        arguments << keystorePasswd;
    else
        arguments << certificatePasswd;

436 437
    Utils::SynchronousProcess proc;
    proc.setTimeoutS(10);
438 439 440 441 442 443 444 445 446
    Utils::SynchronousProcessResponse response
            = proc.run(AndroidConfigurations::currentConfig().keytoolPath().toString(), arguments);
    return response.result == Utils::SynchronousProcessResponse::Finished && response.exitCode == 0;
}

bool AndroidManager::checkCertificateExists(const QString &keystorePath,
                                            const QString &keystorePasswd, const QString &alias)
{
    // assumes that the keystore password is correct
447 448
    QStringList arguments = { "-list", "-keystore", keystorePath,
                              "--storepass", keystorePasswd, "-alias", alias };
449 450 451

    Utils::SynchronousProcess proc;
    proc.setTimeoutS(10);
452 453 454
    Utils::SynchronousProcessResponse response
            = proc.run(AndroidConfigurations::currentConfig().keytoolPath().toString(), arguments);
    return response.result == Utils::SynchronousProcessResponse::Finished && response.exitCode == 0;
455 456
}

457
bool AndroidManager::checkForQt51Files(Utils::FileName fileName)
458 459
{
    fileName.appendPath(QLatin1String("android")).appendPath(QLatin1String("version.xml"));
hjk's avatar
hjk committed
460
    if (!fileName.exists())
461 462
        return false;
    QDomDocument dstVersionDoc;
463
    if (!openXmlFile(dstVersionDoc, fileName))
464 465 466 467
        return false;
    return dstVersionDoc.documentElement().attribute(QLatin1String("value")).toDouble() < 5.2;
}

468 469
AndroidQtSupport *AndroidManager::androidQtSupport(ProjectExplorer::Target *target)
{
470
    for (AndroidQtSupport *provider : g_androidQtSupportProviders) {
471 472 473 474 475 476
        if (provider->canHandle(target))
            return provider;
    }
    return 0;
}

BogDan Vatra's avatar
BogDan Vatra committed
477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529
typedef QMap<QByteArray, QByteArray> GradleProperties;

static GradleProperties readGradleProperties(const QString &path)
{
    GradleProperties properties;
    QFile file(path);
    if (!file.open(QIODevice::ReadOnly))
        return properties;

    foreach (const QByteArray &line, file.readAll().split('\n')) {
        if (line.trimmed().startsWith('#'))
            continue;

        QList<QByteArray> prop(line.split('='));
        if (prop.size() > 1)
            properties[prop.at(0).trimmed()] = prop.at(1).trimmed();
    }
    file.close();
    return properties;
}

static bool mergeGradleProperties(const QString &path, GradleProperties properties)
{
    QFile::remove(path + QLatin1Char('~'));
    QFile::rename(path, path + QLatin1Char('~'));
    QFile file(path);
    if (!file.open(QIODevice::Truncate | QIODevice::WriteOnly | QIODevice::Text))
        return false;

    QFile oldFile(path + QLatin1Char('~'));
    if (oldFile.open(QIODevice::ReadOnly)) {
        while (!oldFile.atEnd()) {
            QByteArray line(oldFile.readLine());
            QList<QByteArray> prop(line.split('='));
            if (prop.size() > 1) {
                GradleProperties::iterator it = properties.find(prop.at(0).trimmed());
                if (it != properties.end()) {
                    file.write(it.key() + '=' + it.value() + '\n');
                    properties.erase(it);
                    continue;
                }
            }
            file.write(line);
        }
        oldFile.close();
    } else {
        file.write("## This file is automatically generated by QtCreator.\n"
                   "#\n"
                   "# This file must *NOT* be checked into Version Control Systems,\n"
                   "# as it contains information specific to your local configuration.\n\n");

    }

530 531
    for (GradleProperties::const_iterator it = properties.constBegin(); it != properties.constEnd();
         ++it)
BogDan Vatra's avatar
BogDan Vatra committed
532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547
        file.write(it.key() + '=' + it.value() + '\n');

    file.close();
    return true;
}


bool AndroidManager::updateGradleProperties(ProjectExplorer::Target *target)
{
    QtSupport::BaseQtVersion *version = QtSupport::QtKitInformation::qtVersion(target->kit());
    if (!version)
        return false;

    AndroidBuildApkStep *buildApkStep
        = AndroidGlobal::buildStep<AndroidBuildApkStep>(target->activeBuildConfiguration());

548
    if (!buildApkStep || !buildApkStep->androidPackageSourceDir().appendPath(QLatin1String("gradlew")).exists())
BogDan Vatra's avatar
BogDan Vatra committed
549 550
        return false;

551 552 553 554 555
    Utils::FileName wrapperProps(buildApkStep->androidPackageSourceDir());
    wrapperProps.appendPath(QLatin1String("gradle/wrapper/gradle-wrapper.properties"));
    if (wrapperProps.exists()) {
        GradleProperties wrapperProperties = readGradleProperties(wrapperProps.toString());
        QString distributionUrl = QString::fromLocal8Bit(wrapperProperties["distributionUrl"]);
556 557
        // Update only old gradle distributionUrl
        if (distributionUrl.endsWith(QLatin1String("distributions/gradle-1.12-all.zip"))) {
558 559 560 561 562
            wrapperProperties["distributionUrl"] = "https\\://services.gradle.org/distributions/gradle-2.2.1-all.zip";
            mergeGradleProperties(wrapperProps.toString(), wrapperProperties);
        }
    }

BogDan Vatra's avatar
BogDan Vatra committed
563 564 565 566 567 568 569 570 571 572 573 574
    GradleProperties localProperties;
    localProperties["sdk.dir"] = AndroidConfigurations::currentConfig().sdkLocation().toString().toLocal8Bit();
    if (!mergeGradleProperties(buildApkStep->androidPackageSourceDir().appendPath(QLatin1String("local.properties")).toString(), localProperties))
        return false;

    QString gradlePropertiesPath = buildApkStep->androidPackageSourceDir().appendPath(QLatin1String("gradle.properties")).toString();
    GradleProperties gradleProperties = readGradleProperties(gradlePropertiesPath);
    gradleProperties["qt5AndroidDir"] = version->qmakeProperty("QT_INSTALL_PREFIX")
            .append(QLatin1String("/src/android/java")).toLocal8Bit();
    gradleProperties["buildDir"] = ".build";
    gradleProperties["androidCompileSdkVersion"] = buildTargetSDK(target).split(QLatin1Char('-')).last().toLocal8Bit();
    if (gradleProperties["androidBuildToolsVersion"].isEmpty()) {
575 576
        QVersionNumber buildtoolVersion = AndroidConfigurations::currentConfig().buildToolsVersion();
        if (buildtoolVersion.isNull())
BogDan Vatra's avatar
BogDan Vatra committed
577
            return false;
578
        gradleProperties["androidBuildToolsVersion"] = buildtoolVersion.toString().toLocal8Bit();
BogDan Vatra's avatar
BogDan Vatra committed
579 580 581 582
    }
    return mergeGradleProperties(gradlePropertiesPath, gradleProperties);
}

583 584 585 586 587 588 589 590 591 592 593 594 595 596 597
int AndroidManager::findApiLevel(const Utils::FileName &platformPath)
{
    int apiLevel = -1;
    Utils::FileName propertiesPath = platformPath;
    propertiesPath.appendPath("/source.properties");
    if (propertiesPath.exists()) {
        QSettings sdkProperties(propertiesPath.toString(), QSettings::IniFormat);
        bool validInt = false;
        apiLevel = sdkProperties.value(ApiLevelKey).toInt(&validInt);
        if (!validInt)
            apiLevel = -1;
    }
    return apiLevel;
}

598
} // namespace Android