androidconfigurations.cpp 34.7 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 30 31

#include "androidconfigurations.h"
#include "androidconstants.h"
Daniel Teske's avatar
Daniel Teske committed
32 33 34
#include "androidtoolchain.h"
#include "androiddevice.h"
#include "androidgdbserverkitinformation.h"
BogDan Vatra's avatar
BogDan Vatra committed
35
#include "ui_addnewavddialog.h"
36
#include "androidqtversion.h"
BogDan Vatra's avatar
BogDan Vatra committed
37 38

#include <coreplugin/icore.h>
39
#include <utils/hostosinfo.h>
BogDan Vatra's avatar
BogDan Vatra committed
40
#include <utils/persistentsettings.h>
Daniel Teske's avatar
Daniel Teske committed
41 42 43 44 45 46 47 48
#include <projectexplorer/kitmanager.h>
#include <projectexplorer/kitinformation.h>
#include <projectexplorer/devicesupport/devicemanager.h>
#include <projectexplorer/toolchainmanager.h>
#include <debugger/debuggerkitinformation.h>
#include <qtsupport/baseqtversion.h>
#include <qtsupport/qtkitinformation.h>
#include <qtsupport/qtversionmanager.h>
Daniel Teske's avatar
Daniel Teske committed
49
#include <utils/environment.h>
BogDan Vatra's avatar
BogDan Vatra committed
50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65

#include <QDateTime>
#include <QSettings>
#include <QStringList>
#include <QProcess>
#include <QFileInfo>
#include <QDirIterator>
#include <QMetaObject>

#include <QStringListModel>
#include <QMessageBox>

#if defined(_WIN32)
#include <iostream>
#include <windows.h>
#define sleep(_n) Sleep(1000 * (_n))
Thiago Macieira's avatar
Thiago Macieira committed
66 67
#else
#include <unistd.h>
BogDan Vatra's avatar
BogDan Vatra committed
68 69
#endif

hjk's avatar
hjk committed
70
using namespace ProjectExplorer;
BogDan Vatra's avatar
BogDan Vatra committed
71 72 73 74 75 76 77 78 79 80 81 82 83
using namespace Utils;

namespace Android {
namespace Internal {

namespace {
    const QLatin1String SettingsGroup("AndroidConfigurations");
    const QLatin1String SDKLocationKey("SDKLocation");
    const QLatin1String NDKLocationKey("NDKLocation");
    const QLatin1String NDKToolchainVersionKey("NDKToolchainVersion");
    const QLatin1String AntLocationKey("AntLocation");
    const QLatin1String OpenJDKLocationKey("OpenJDKLocation");
    const QLatin1String KeystoreLocationKey("KeystoreLocation");
Daniel Teske's avatar
Daniel Teske committed
84
    const QLatin1String AutomaticKitCreationKey("AutomatiKitCreation");
85
    const QLatin1String MakeExtraSearchDirectory("MakeExtraSearchDirectory");
BogDan Vatra's avatar
BogDan Vatra committed
86
    const QLatin1String PartitionSizeKey("PartitionSize");
87
    const QLatin1String ToolchainHostKey("ToolchainHost");
BogDan Vatra's avatar
BogDan Vatra committed
88 89
    const QLatin1String ArmToolchainPrefix("arm-linux-androideabi");
    const QLatin1String X86ToolchainPrefix("x86");
BogDan Vatra's avatar
BogDan Vatra committed
90
    const QLatin1String MipsToolchainPrefix("mipsel-linux-android");
BogDan Vatra's avatar
BogDan Vatra committed
91
    const QLatin1String ArmToolsPrefix("arm-linux-androideabi");
BogDan Vatra's avatar
BogDan Vatra committed
92
    const QLatin1String X86ToolsPrefix("i686-linux-android");
BogDan Vatra's avatar
BogDan Vatra committed
93
    const QLatin1String MipsToolsPrefix("mipsel-linux-android");
BogDan Vatra's avatar
BogDan Vatra committed
94 95 96 97 98
    const QLatin1String Unknown("unknown");
    const QLatin1String keytoolName("keytool");
    const QLatin1String jarsignerName("jarsigner");
    const QLatin1String changeTimeStamp("ChangeTimeStamp");

99
    static QString sdkSettingsFileName()
BogDan Vatra's avatar
BogDan Vatra committed
100
    {
101 102
        return QFileInfo(Core::ICore::settings(QSettings::SystemScope)->fileName()).absolutePath()
                + QLatin1String("/qtcreator/android.xml");
BogDan Vatra's avatar
BogDan Vatra committed
103 104
    }

105
    bool androidDevicesLessThan(const AndroidDeviceInfo &dev1, const AndroidDeviceInfo &dev2)
BogDan Vatra's avatar
BogDan Vatra committed
106
    {
Daniel Teske's avatar
Compile  
Daniel Teske committed
107 108
        if (dev1.serialNumber.contains(QLatin1String("????")) == dev2.serialNumber.contains(QLatin1String("????")))
            return !dev1.serialNumber.contains(QLatin1String("????"));
109 110 111 112 113 114 115
        bool dev1IsEmulator = dev1.serialNumber.startsWith(QLatin1String("emulator"));
        bool dev2IsEmulator = dev2.serialNumber.startsWith(QLatin1String("emulator"));
        if (dev1IsEmulator != dev2IsEmulator)
            return !dev1IsEmulator;
        if (dev1.sdk != dev2.sdk)
            return dev1.sdk < dev2.sdk;
        return dev1.serialNumber < dev2.serialNumber;
BogDan Vatra's avatar
BogDan Vatra committed
116 117 118
    }
}

119 120 121 122 123 124
Abi::Architecture AndroidConfigurations::architectureForToolChainPrefix(const QString& toolchainprefix)
{
    if (toolchainprefix == ArmToolchainPrefix)
        return Abi::ArmArchitecture;
    if (toolchainprefix == X86ToolchainPrefix)
        return Abi::X86Architecture;
BogDan Vatra's avatar
BogDan Vatra committed
125 126
    if (toolchainprefix == MipsToolchainPrefix)
        return Abi::MipsArchitecture;
127 128 129
    return Abi::UnknownArchitecture;
}

hjk's avatar
hjk committed
130
QLatin1String AndroidConfigurations::toolchainPrefix(Abi::Architecture architecture)
BogDan Vatra's avatar
BogDan Vatra committed
131 132
{
    switch (architecture) {
hjk's avatar
hjk committed
133
    case Abi::ArmArchitecture:
BogDan Vatra's avatar
BogDan Vatra committed
134
        return ArmToolchainPrefix;
hjk's avatar
hjk committed
135
    case Abi::X86Architecture:
BogDan Vatra's avatar
BogDan Vatra committed
136
        return X86ToolchainPrefix;
BogDan Vatra's avatar
BogDan Vatra committed
137 138
    case Abi::MipsArchitecture:
        return MipsToolchainPrefix;
BogDan Vatra's avatar
BogDan Vatra committed
139 140 141 142 143
    default:
        return Unknown;
    }
}

hjk's avatar
hjk committed
144
QLatin1String AndroidConfigurations::toolsPrefix(Abi::Architecture architecture)
BogDan Vatra's avatar
BogDan Vatra committed
145 146
{
    switch (architecture) {
hjk's avatar
hjk committed
147
    case Abi::ArmArchitecture:
BogDan Vatra's avatar
BogDan Vatra committed
148
        return ArmToolsPrefix;
hjk's avatar
hjk committed
149
    case Abi::X86Architecture:
BogDan Vatra's avatar
BogDan Vatra committed
150
        return X86ToolsPrefix;
BogDan Vatra's avatar
BogDan Vatra committed
151 152
    case Abi::MipsArchitecture:
        return MipsToolsPrefix;
BogDan Vatra's avatar
BogDan Vatra committed
153 154 155 156 157 158 159 160 161
    default:
        return Unknown;
    }
}

AndroidConfig::AndroidConfig(const QSettings &settings)
{
    // user settings
    partitionSize = settings.value(PartitionSizeKey, 1024).toInt();
hjk's avatar
hjk committed
162 163 164 165 166
    sdkLocation = FileName::fromString(settings.value(SDKLocationKey).toString());
    ndkLocation = FileName::fromString(settings.value(NDKLocationKey).toString());
    antLocation = FileName::fromString(settings.value(AntLocationKey).toString());
    openJDKLocation = FileName::fromString(settings.value(OpenJDKLocationKey).toString());
    keystoreLocation = FileName::fromString(settings.value(KeystoreLocationKey).toString());
167
    toolchainHost = settings.value(ToolchainHostKey).toString();
Daniel Teske's avatar
Daniel Teske committed
168
    automaticKitCreation = settings.value(AutomaticKitCreationKey, true).toBool();
169 170 171 172 173
    QString extraDirectory = settings.value(MakeExtraSearchDirectory).toString();
    if (extraDirectory.isEmpty())
        makeExtraSearchDirectories = QStringList();
    else
        makeExtraSearchDirectories << extraDirectory;
BogDan Vatra's avatar
BogDan Vatra committed
174 175

    PersistentSettingsReader reader;
176 177
    if (reader.load(FileName::fromString(sdkSettingsFileName()))
            && settings.value(changeTimeStamp).toInt() != QFileInfo(sdkSettingsFileName()).lastModified().toMSecsSinceEpoch() / 1000) {
BogDan Vatra's avatar
BogDan Vatra committed
178
        // persisten settings
hjk's avatar
hjk committed
179 180 181 182 183
        sdkLocation = FileName::fromString(reader.restoreValue(SDKLocationKey).toString());
        ndkLocation = FileName::fromString(reader.restoreValue(NDKLocationKey).toString());
        antLocation = FileName::fromString(reader.restoreValue(AntLocationKey).toString());
        openJDKLocation = FileName::fromString(reader.restoreValue(OpenJDKLocationKey).toString());
        keystoreLocation = FileName::fromString(reader.restoreValue(KeystoreLocationKey).toString());
184
        toolchainHost = reader.restoreValue(ToolchainHostKey).toString();
Daniel Teske's avatar
Daniel Teske committed
185 186 187
        QVariant v = reader.restoreValue(AutomaticKitCreationKey);
        if (v.isValid())
            automaticKitCreation = v.toBool();
188 189 190 191 192
        QString extraDirectory = reader.restoreValue(MakeExtraSearchDirectory).toString();
        if (extraDirectory.isEmpty())
            makeExtraSearchDirectories = QStringList();
        else
            makeExtraSearchDirectories << extraDirectory;
BogDan Vatra's avatar
BogDan Vatra committed
193 194 195 196 197 198 199 200 201 202 203 204
        // persistent settings
    }

}

AndroidConfig::AndroidConfig()
{
    partitionSize = 1024;
}

void AndroidConfig::save(QSettings &settings) const
{
205
    QFileInfo fileInfo(sdkSettingsFileName());
BogDan Vatra's avatar
BogDan Vatra committed
206 207 208 209
    if (fileInfo.exists())
        settings.setValue(changeTimeStamp, fileInfo.lastModified().toMSecsSinceEpoch() / 1000);

    // user settings
Tobias Hunger's avatar
Tobias Hunger committed
210 211 212 213 214
    settings.setValue(SDKLocationKey, sdkLocation.toString());
    settings.setValue(NDKLocationKey, ndkLocation.toString());
    settings.setValue(AntLocationKey, antLocation.toString());
    settings.setValue(OpenJDKLocationKey, openJDKLocation.toString());
    settings.setValue(KeystoreLocationKey, keystoreLocation.toString());
BogDan Vatra's avatar
BogDan Vatra committed
215
    settings.setValue(PartitionSizeKey, partitionSize);
Daniel Teske's avatar
Daniel Teske committed
216
    settings.setValue(AutomaticKitCreationKey, automaticKitCreation);
217
    settings.setValue(ToolchainHostKey, toolchainHost);
218 219 220
    settings.setValue(MakeExtraSearchDirectory,
                      makeExtraSearchDirectories.isEmpty() ? QString()
                                                           : makeExtraSearchDirectories.at(0));
BogDan Vatra's avatar
BogDan Vatra committed
221 222 223 224 225
}

void AndroidConfigurations::setConfig(const AndroidConfig &devConfigs)
{
    m_config = devConfigs;
226 227 228 229

    if (m_config.toolchainHost.isEmpty())
        detectToolchainHost();

BogDan Vatra's avatar
BogDan Vatra committed
230 231
    save();
    updateAvailablePlatforms();
Daniel Teske's avatar
Daniel Teske committed
232
    updateAutomaticKitList();
233
    updateAndroidDevice();
BogDan Vatra's avatar
BogDan Vatra committed
234 235 236 237 238 239
    emit updated();
}

void AndroidConfigurations::updateAvailablePlatforms()
{
    m_availablePlatforms.clear();
hjk's avatar
hjk committed
240
    FileName path = m_config.ndkLocation;
BogDan Vatra's avatar
BogDan Vatra committed
241
    QDirIterator it(path.appendPath(QLatin1String("platforms")).toString(), QStringList() << QLatin1String("android-*"), QDir::Dirs);
BogDan Vatra's avatar
BogDan Vatra committed
242 243 244 245 246 247 248 249 250 251 252
    while (it.hasNext()) {
        const QString &fileName = it.next();
        m_availablePlatforms.push_back(fileName.mid(fileName.lastIndexOf(QLatin1Char('-')) + 1).toInt());
    }
    qSort(m_availablePlatforms.begin(), m_availablePlatforms.end(), qGreater<int>());
}

QStringList AndroidConfigurations::sdkTargets(int minApiLevel) const
{
    QStringList targets;
    QProcess proc;
Tobias Hunger's avatar
Tobias Hunger committed
253
    proc.start(androidToolPath().toString(), QStringList() << QLatin1String("list") << QLatin1String("target")); // list avaialbe AVDs
BogDan Vatra's avatar
BogDan Vatra committed
254 255 256 257
    if (!proc.waitForFinished(-1)) {
        proc.terminate();
        return targets;
    }
258
    while (proc.canReadLine()) {
259
        const QString line = QString::fromLocal8Bit(proc.readLine().trimmed());
BogDan Vatra's avatar
BogDan Vatra committed
260 261 262 263 264 265 266 267 268 269
        int index = line.indexOf(QLatin1String("\"android-"));
        if (index == -1)
            continue;
        QString apiLevel = line.mid(index + 1, line.length() - index - 2);
        if (apiLevel.mid(apiLevel.lastIndexOf(QLatin1Char('-')) + 1).toInt() >= minApiLevel)
            targets.push_back(apiLevel);
    }
    return targets;
}

hjk's avatar
hjk committed
270
FileName AndroidConfigurations::adbToolPath() const
BogDan Vatra's avatar
BogDan Vatra committed
271
{
hjk's avatar
hjk committed
272
    FileName path = m_config.sdkLocation;
273
    return path.appendPath(QLatin1String("platform-tools/adb" QTC_HOST_EXE_SUFFIX));
BogDan Vatra's avatar
BogDan Vatra committed
274 275
}

hjk's avatar
hjk committed
276
FileName AndroidConfigurations::androidToolPath() const
BogDan Vatra's avatar
BogDan Vatra committed
277
{
278 279 280 281
    if (HostOsInfo::isWindowsHost()) {
        // I want to switch from using android.bat to using an executable. All it really does is call
        // Java and I've made some progress on it. So if android.exe exists, return that instead.
        FileName path = m_config.sdkLocation;
282
        path.appendPath(QLatin1String("tools/android" QTC_HOST_EXE_SUFFIX));
283 284 285
        if (path.toFileInfo().exists())
            return path;
        path = m_config.sdkLocation;
286
        return path.appendPath(QLatin1String("tools/android" ANDROID_BAT_SUFFIX));
287 288 289 290
    } else {
        FileName path = m_config.sdkLocation;
        return path.appendPath(QLatin1String("tools/android"));
    }
BogDan Vatra's avatar
BogDan Vatra committed
291 292
}

hjk's avatar
hjk committed
293
FileName AndroidConfigurations::antToolPath() const
BogDan Vatra's avatar
BogDan Vatra committed
294
{
Tobias Hunger's avatar
Tobias Hunger committed
295
    if (!m_config.antLocation.isEmpty())
BogDan Vatra's avatar
BogDan Vatra committed
296 297
        return m_config.antLocation;
    else
hjk's avatar
hjk committed
298
        return FileName::fromString(QLatin1String("ant"));
BogDan Vatra's avatar
BogDan Vatra committed
299 300
}

hjk's avatar
hjk committed
301
FileName AndroidConfigurations::emulatorToolPath() const
BogDan Vatra's avatar
BogDan Vatra committed
302
{
hjk's avatar
hjk committed
303
    FileName path = m_config.sdkLocation;
304
    return path.appendPath(QLatin1String("tools/emulator" QTC_HOST_EXE_SUFFIX));
BogDan Vatra's avatar
BogDan Vatra committed
305 306
}

307
FileName AndroidConfigurations::toolPath(Abi::Architecture architecture, const QString &ndkToolChainVersion) const
BogDan Vatra's avatar
BogDan Vatra committed
308
{
hjk's avatar
hjk committed
309
    FileName path = m_config.ndkLocation;
Tobias Hunger's avatar
Tobias Hunger committed
310
    return path.appendPath(QString::fromLatin1("toolchains/%1-%2/prebuilt/%3/bin/%4")
BogDan Vatra's avatar
BogDan Vatra committed
311
            .arg(toolchainPrefix(architecture))
312
            .arg(ndkToolChainVersion)
313
            .arg(m_config.toolchainHost)
Tobias Hunger's avatar
Tobias Hunger committed
314
            .arg(toolsPrefix(architecture)));
BogDan Vatra's avatar
BogDan Vatra committed
315 316
}

317
FileName AndroidConfigurations::stripPath(Abi::Architecture architecture, const QString &ndkToolChainVersion) const
BogDan Vatra's avatar
BogDan Vatra committed
318
{
319
    return toolPath(architecture, ndkToolChainVersion).append(QLatin1String("-strip" QTC_HOST_EXE_SUFFIX));
BogDan Vatra's avatar
BogDan Vatra committed
320 321
}

322
FileName AndroidConfigurations::readelfPath(Abi::Architecture architecture, const QString &ndkToolChainVersion) const
BogDan Vatra's avatar
BogDan Vatra committed
323
{
324
    return toolPath(architecture, ndkToolChainVersion).append(QLatin1String("-readelf" QTC_HOST_EXE_SUFFIX));
BogDan Vatra's avatar
BogDan Vatra committed
325 326
}

327 328 329 330 331
FileName AndroidConfigurations::gccPath(Abi::Architecture architecture, const QString &ndkToolChainVersion) const
{
    return toolPath(architecture, ndkToolChainVersion).append(QLatin1String("-gcc" QTC_HOST_EXE_SUFFIX));
}

332
FileName AndroidConfigurations::gdbPath(Abi::Architecture architecture, const QString &ndkToolChainVersion) const
BogDan Vatra's avatar
BogDan Vatra committed
333
{
334
    return toolPath(architecture, ndkToolChainVersion).append(QLatin1String("-gdb" QTC_HOST_EXE_SUFFIX));
BogDan Vatra's avatar
BogDan Vatra committed
335 336
}

hjk's avatar
hjk committed
337
FileName AndroidConfigurations::openJDKPath() const
BogDan Vatra's avatar
BogDan Vatra committed
338 339 340 341
{
    return m_config.openJDKLocation;
}

342 343 344 345
void AndroidConfigurations::detectToolchainHost()
{
    QStringList hostPatterns;
    switch (HostOsInfo::hostOs()) {
346
    case OsTypeLinux:
347 348
        hostPatterns << QLatin1String("linux*");
        break;
349
    case OsTypeWindows:
350 351
        hostPatterns << QLatin1String("windows*");
        break;
352
    case OsTypeMac:
353 354 355 356 357 358 359 360 361 362 363 364 365
        hostPatterns << QLatin1String("darwin*");
        break;
    default: /* unknown host */ return;
    }

    FileName path = m_config.ndkLocation;
    QDirIterator it(path.appendPath(QLatin1String("prebuilt")).toString(), hostPatterns, QDir::Dirs);
    if (it.hasNext()) {
        it.next();
        m_config.toolchainHost = it.fileName();
    }
}

hjk's avatar
hjk committed
366
FileName AndroidConfigurations::openJDKBinPath() const
BogDan Vatra's avatar
BogDan Vatra committed
367
{
hjk's avatar
hjk committed
368
    FileName path = m_config.openJDKLocation;
Tobias Hunger's avatar
Tobias Hunger committed
369 370 371
    if (!path.isEmpty())
        return path.appendPath(QLatin1String("bin"));
    return path;
BogDan Vatra's avatar
BogDan Vatra committed
372 373
}

hjk's avatar
hjk committed
374
FileName AndroidConfigurations::keytoolPath() const
BogDan Vatra's avatar
BogDan Vatra committed
375
{
Tobias Hunger's avatar
Tobias Hunger committed
376
    return openJDKBinPath().appendPath(keytoolName);
BogDan Vatra's avatar
BogDan Vatra committed
377 378
}

hjk's avatar
hjk committed
379
FileName AndroidConfigurations::jarsignerPath() const
BogDan Vatra's avatar
BogDan Vatra committed
380
{
Tobias Hunger's avatar
Tobias Hunger committed
381
    return openJDKBinPath().appendPath(jarsignerName);
BogDan Vatra's avatar
BogDan Vatra committed
382 383
}

hjk's avatar
hjk committed
384
FileName AndroidConfigurations::zipalignPath() const
385
{
386
    FileName path = m_config.sdkLocation;
387
    return path.appendPath(QLatin1String("tools/zipalign" QTC_HOST_EXE_SUFFIX));
388 389
}

390
QString AndroidConfigurations::getDeployDeviceSerialNumber(int *apiLevel, const QString &abi, QString *error) const
BogDan Vatra's avatar
BogDan Vatra committed
391
{
392
    QVector<AndroidDeviceInfo> devices = connectedDevices(error);
BogDan Vatra's avatar
BogDan Vatra committed
393

394
    foreach (AndroidDeviceInfo device, devices) {
395 396 397 398 399 400
        if (device.unauthorized) {
            if (error) {
                *error += tr("Skipping %1: Unauthorized. Please check the confirmation dialog on your device..").arg(device.serialNumber);
                *error += QLatin1Char('\n');
            }
        } else if (!device.cpuAbi.contains(abi)) {
401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416
            if (error) {
                *error += tr("Skipping %1: ABI is incompatible, device supports ABIs: %2.")
                    .arg(getProductModel(device.serialNumber))
                    .arg(device.cpuAbi.join(QLatin1String(" ")));
                *error += QLatin1Char('\n');
            }
        } else if (device.sdk < *apiLevel) {
            if (error) {
                *error += tr("Skipping %1: API Level of device is: %2.")
                        .arg(getProductModel(device.serialNumber))
                        .arg(device.sdk);
                *error += QLatin1Char('\n');
            }
        } else {
            if (error)
                error->clear(); // no errors if we found a device
BogDan Vatra's avatar
BogDan Vatra committed
417 418 419
            *apiLevel = device.sdk;
            return device.serialNumber;
        }
Tobias Hunger's avatar
Tobias Hunger committed
420
    }
421
    return QString();
BogDan Vatra's avatar
BogDan Vatra committed
422 423
}

424
QVector<AndroidDeviceInfo> AndroidConfigurations::connectedDevices(QString *error) const
BogDan Vatra's avatar
BogDan Vatra committed
425
{
426
    QVector<AndroidDeviceInfo> devices;
BogDan Vatra's avatar
BogDan Vatra committed
427
    QProcess adbProc;
Tobias Hunger's avatar
Tobias Hunger committed
428
    adbProc.start(adbToolPath().toString(), QStringList() << QLatin1String("devices"));
BogDan Vatra's avatar
BogDan Vatra committed
429
    if (!adbProc.waitForFinished(-1)) {
430
        adbProc.kill();
431 432
        if (error)
            *error = tr("Could not run: %1").arg(adbToolPath().toString() + QLatin1String(" devices"));
BogDan Vatra's avatar
BogDan Vatra committed
433 434 435 436
        return devices;
    }
    QList<QByteArray> adbDevs = adbProc.readAll().trimmed().split('\n');
    adbDevs.removeFirst();
437 438 439

    // workaround for '????????????' serial numbers:
    // can use "adb -d" when only one usb device attached
BogDan Vatra's avatar
BogDan Vatra committed
440
    foreach (const QByteArray &device, adbDevs) {
441 442 443
        const QString serialNo = QString::fromLatin1(device.left(device.indexOf('\t')).trimmed());
        const QString deviceType = QString::fromLatin1(device.mid(device.indexOf('\t'))).trimmed();
        AndroidDeviceInfo dev;
444
        dev.serialNumber = serialNo;
BogDan Vatra's avatar
BogDan Vatra committed
445
        dev.sdk = getSDKVersion(dev.serialNumber);
446
        dev.cpuAbi = getAbis(dev.serialNumber);
447
        dev.unauthorized = (deviceType == QLatin1String("unauthorized"));
BogDan Vatra's avatar
BogDan Vatra committed
448 449
        devices.push_back(dev);
    }
450

BogDan Vatra's avatar
BogDan Vatra committed
451
    qSort(devices.begin(), devices.end(), androidDevicesLessThan);
452 453
    if (devices.isEmpty() && error)
        *error = tr("No devices found in output of: %1").arg(adbToolPath().toString() + QLatin1String(" devices"));
BogDan Vatra's avatar
BogDan Vatra committed
454 455 456
    return devices;
}

457
QString AndroidConfigurations::createAVD(int minApiLevel, QString targetArch) const
BogDan Vatra's avatar
BogDan Vatra committed
458 459 460 461
{
    QDialog d;
    Ui::AddNewAVDDialog avdDialog;
    avdDialog.setupUi(&d);
462
    avdDialog.targetComboBox->addItems(sdkTargets(minApiLevel));
463 464 465 466 467 468 469 470 471 472

    if (targetArch.isEmpty())
        avdDialog.abiComboBox->addItems(QStringList()
                                        << QLatin1String("armeabi-v7a")
                                        << QLatin1String("armeabi")
                                        << QLatin1String("x86")
                                        << QLatin1String("mips"));
    else
        avdDialog.abiComboBox->addItems(QStringList(targetArch));

473
    if (!avdDialog.targetComboBox->count()) {
Friedemann Kleint's avatar
Friedemann Kleint committed
474
        QMessageBox::critical(0, tr("Error Creating AVD"),
475
                              tr("Cannot create a new AVD. No sufficiently recent Android SDK available.\n"
Friedemann Kleint's avatar
Friedemann Kleint committed
476
                                 "Please install an SDK of at least API version %1.").
Friedemann Kleint's avatar
Friedemann Kleint committed
477
                              arg(minApiLevel));
478
        return QString();
BogDan Vatra's avatar
BogDan Vatra committed
479 480 481 482 483 484
    }

    QRegExp rx(QLatin1String("\\S+"));
    QRegExpValidator v(rx, 0);
    avdDialog.nameLineEdit->setValidator(&v);
    if (d.exec() != QDialog::Accepted)
485 486
        return QString();
    return createAVD(avdDialog.targetComboBox->currentText(), avdDialog.nameLineEdit->text(), avdDialog.abiComboBox->currentText(), avdDialog.sizeSpinBox->value());
BogDan Vatra's avatar
BogDan Vatra committed
487 488
}

489
QString AndroidConfigurations::createAVD(const QString &target, const QString &name, const QString &abi, int sdcardSize ) const
BogDan Vatra's avatar
BogDan Vatra committed
490 491
{
    QProcess proc;
Tobias Hunger's avatar
Tobias Hunger committed
492
    proc.start(androidToolPath().toString(),
BogDan Vatra's avatar
BogDan Vatra committed
493 494 495
               QStringList() << QLatin1String("create") << QLatin1String("avd")
               << QLatin1String("-a") << QLatin1String("-t") << target
               << QLatin1String("-n") << name
496
               << QLatin1String("-b") << abi
BogDan Vatra's avatar
BogDan Vatra committed
497 498
               << QLatin1String("-c") << QString::fromLatin1("%1M").arg(sdcardSize));
    if (!proc.waitForStarted())
499
        return QString();
BogDan Vatra's avatar
BogDan Vatra committed
500 501 502
    proc.write(QByteArray("no\n"));
    if (!proc.waitForFinished(-1)) {
        proc.terminate();
503
        return QString();
BogDan Vatra's avatar
BogDan Vatra committed
504
    }
505 506 507
    if (proc.exitCode()) // error!
        return QString();
    return name;
BogDan Vatra's avatar
BogDan Vatra committed
508 509 510 511 512
}

bool AndroidConfigurations::removeAVD(const QString &name) const
{
    QProcess proc;
Tobias Hunger's avatar
Tobias Hunger committed
513
    proc.start(androidToolPath().toString(),
BogDan Vatra's avatar
BogDan Vatra committed
514 515 516 517 518 519 520 521 522
               QStringList() << QLatin1String("delete") << QLatin1String("avd")
               << QLatin1String("-n") << name);
    if (!proc.waitForFinished(-1)) {
        proc.terminate();
        return false;
    }
    return !proc.exitCode();
}

523
QVector<AndroidDeviceInfo> AndroidConfigurations::androidVirtualDevices() const
BogDan Vatra's avatar
BogDan Vatra committed
524
{
525
    QVector<AndroidDeviceInfo> devices;
BogDan Vatra's avatar
BogDan Vatra committed
526
    QProcess proc;
Tobias Hunger's avatar
Tobias Hunger committed
527
    proc.start(androidToolPath().toString(),
BogDan Vatra's avatar
BogDan Vatra committed
528 529 530 531 532 533 534
               QStringList() << QLatin1String("list") << QLatin1String("avd")); // list available AVDs
    if (!proc.waitForFinished(-1)) {
        proc.terminate();
        return devices;
    }
    QList<QByteArray> avds = proc.readAll().trimmed().split('\n');
    avds.removeFirst();
535
    AndroidDeviceInfo dev;
BogDan Vatra's avatar
BogDan Vatra committed
536 537 538 539 540 541 542 543 544 545 546 547 548 549
    for (int i = 0; i < avds.size(); i++) {
        QString line = QLatin1String(avds[i]);
        if (!line.contains(QLatin1String("Name:")))
            continue;

        dev.serialNumber = line.mid(line.indexOf(QLatin1Char(':')) + 2).trimmed();
        ++i;
        for (; i < avds.size(); ++i) {
            line = QLatin1String(avds[i]);
            if (line.contains(QLatin1String("---------")))
                break;
            if (line.contains(QLatin1String("Target:")))
                dev.sdk = line.mid(line.lastIndexOf(QLatin1Char(' '))).remove(QLatin1Char(')')).toInt();
            if (line.contains(QLatin1String("ABI:")))
550
                dev.cpuAbi = QStringList() << line.mid(line.lastIndexOf(QLatin1Char(' '))).trimmed();
BogDan Vatra's avatar
BogDan Vatra committed
551
        }
552 553 554
        // armeabi-v7a devices can also run armeabi code
        if (dev.cpuAbi == QStringList(QLatin1String("armeabi-v7a")))
            dev.cpuAbi << QLatin1String("armeabi");
555
        dev.unauthorized = false;
BogDan Vatra's avatar
BogDan Vatra committed
556 557 558 559 560 561 562
        devices.push_back(dev);
    }
    qSort(devices.begin(), devices.end(), androidDevicesLessThan);

    return devices;
}

563
QString AndroidConfigurations::findAvd(int *apiLevel, const QString &cpuAbi)
BogDan Vatra's avatar
BogDan Vatra committed
564
{
565 566 567 568 569 570
    QVector<AndroidDeviceInfo> devices = androidVirtualDevices();
    foreach (const AndroidDeviceInfo &device, devices) {
        // take first emulator how supports this package
        if (device.sdk >= *apiLevel && device.cpuAbi.contains(cpuAbi)) {
            *apiLevel = device.sdk;
            return device.serialNumber;
BogDan Vatra's avatar
BogDan Vatra committed
571 572
        }
    }
573 574 575 576 577 578 579 580 581
    return QString();
}

QString AndroidConfigurations::startAVD(const QString &name, int apiLevel, QString cpuAbi) const
{
    if (startAVDAsync(name))
        return waitForAvd(apiLevel, cpuAbi);
    return QString();
}
BogDan Vatra's avatar
BogDan Vatra committed
582

583 584 585 586 587
bool AndroidConfigurations::startAVDAsync(const QString &avdName) const
{
    QProcess *avdProcess = new QProcess();
    connect(this, SIGNAL(destroyed()), avdProcess, SLOT(deleteLater()));
    connect(avdProcess, SIGNAL(finished(int)), avdProcess, SLOT(deleteLater()));
BogDan Vatra's avatar
BogDan Vatra committed
588 589

    // start the emulator
Orgad Shaneh's avatar
Orgad Shaneh committed
590
    avdProcess->start(emulatorToolPath().toString(),
BogDan Vatra's avatar
BogDan Vatra committed
591 592
                        QStringList() << QLatin1String("-partition-size") << QString::number(config().partitionSize)
                        << QLatin1String("-avd") << avdName);
Orgad Shaneh's avatar
Orgad Shaneh committed
593 594
    if (!avdProcess->waitForStarted(-1)) {
        delete avdProcess;
595
        return false;
BogDan Vatra's avatar
BogDan Vatra committed
596
    }
597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612
    return true;
}

QString AndroidConfigurations::waitForAvd(int apiLevel, const QString &cpuAbi) const
{
    // we cannot use adb -e wait-for-device, since that doesn't work if a emulator is already running

    // 15 rounds of 8s sleeping, a minute for the avd to start
    QString serialNumber;
    for (int i = 0; i < 15; ++i) {
        QVector<AndroidDeviceInfo> devices = connectedDevices();
        foreach (AndroidDeviceInfo device, devices) {
            if (!device.serialNumber.startsWith(QLatin1String("emulator")))
                continue;
            if (!device.cpuAbi.contains(cpuAbi))
                continue;
613
            if (device.sdk != apiLevel)
614 615 616 617 618 619 620 621 622
                continue;
            serialNumber = device.serialNumber;
            // found a serial number, now wait until it's done booting...
            for (int i = 0; i < 15; ++i) {
                if (hasFinishedBooting(serialNumber))
                    return serialNumber;
                else
                    sleep(8);
            }
Orgad Shaneh's avatar
Orgad Shaneh committed
623 624
            return QString();
        }
625
        sleep(8);
BogDan Vatra's avatar
BogDan Vatra committed
626 627 628 629 630 631
    }
    return QString();
}

int AndroidConfigurations::getSDKVersion(const QString &device) const
{
632 633 634 635
    // workaround for '????????????' serial numbers
    QStringList arguments = AndroidDeviceInfo::adbSelector(device);
    arguments << QLatin1String("shell") << QLatin1String("getprop")
              << QLatin1String("ro.build.version.sdk");
BogDan Vatra's avatar
BogDan Vatra committed
636 637

    QProcess adbProc;
638
    adbProc.start(adbToolPath().toString(), arguments);
BogDan Vatra's avatar
BogDan Vatra committed
639
    if (!adbProc.waitForFinished(-1)) {
640
        adbProc.kill();
BogDan Vatra's avatar
BogDan Vatra committed
641 642 643 644 645
        return -1;
    }
    return adbProc.readAll().trimmed().toInt();
}

646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669
//!
//! \brief AndroidConfigurations::getProductModel
//! \param device serial number
//! \return the produce model of the device or if that cannot be read the serial number
//!
QString AndroidConfigurations::getProductModel(const QString &device) const
{
    // workaround for '????????????' serial numbers
    QStringList arguments = AndroidDeviceInfo::adbSelector(device);
    arguments << QLatin1String("shell") << QLatin1String("getprop")
              << QLatin1String("ro.product.model");

    QProcess adbProc;
    adbProc.start(adbToolPath().toString(), arguments);
    if (!adbProc.waitForFinished(-1)) {
        adbProc.kill();
        return device;
    }
    QString model = QString::fromLocal8Bit(adbProc.readAll().trimmed());
    if (model.isEmpty())
        return device;
    return model;
}

670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687
bool AndroidConfigurations::hasFinishedBooting(const QString &device) const
{
    QStringList arguments = AndroidDeviceInfo::adbSelector(device);
    arguments << QLatin1String("shell") << QLatin1String("getprop")
              << QLatin1String("init.svc.bootanim");

    QProcess adbProc;
    adbProc.start(adbToolPath().toString(), arguments);
    if (!adbProc.waitForFinished(-1)) {
        adbProc.kill();
        return false;
    }
    QString value = QString::fromLocal8Bit(adbProc.readAll().trimmed());
    if (value == QLatin1String("stopped"))
        return true;
    return false;
}

688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714
QStringList AndroidConfigurations::getAbis(const QString &device) const
{
    QStringList result;
    int i = 1;
    while (true) {
        QStringList arguments = AndroidDeviceInfo::adbSelector(device);
        arguments << QLatin1String("shell") << QLatin1String("getprop");
        if (i == 1)
            arguments << QLatin1String("ro.product.cpu.abi");
        else
            arguments << QString::fromLatin1("ro.product.cpu.abi%1").arg(i);

        QProcess adbProc;
        adbProc.start(adbToolPath().toString(), arguments);
        if (!adbProc.waitForFinished(-1)) {
            adbProc.kill();
            return result;
        }
        QString abi = QString::fromLocal8Bit(adbProc.readAll().trimmed());
        if (abi.isEmpty())
            break;
        result << abi;
        ++i;
    }
    return result;
}

BogDan Vatra's avatar
BogDan Vatra committed
715 716 717 718 719 720 721 722 723 724
QString AndroidConfigurations::bestMatch(const QString &targetAPI) const
{
    int target = targetAPI.mid(targetAPI.lastIndexOf(QLatin1Char('-')) + 1).toInt();
    foreach (int apiLevel, m_availablePlatforms) {
        if (apiLevel <= target)
            return QString::fromLatin1("android-%1").arg(apiLevel);
    }
    return QLatin1String("android-8");
}

725 726 727 728 729
QStringList AndroidConfigurations::makeExtraSearchDirectories() const
{
    return m_config.makeExtraSearchDirectories;
}

730
static bool equalKits(Kit *a, Kit *b)
Daniel Teske's avatar
Daniel Teske committed
731 732 733 734 735 736 737 738 739 740 741
{
    return ToolChainKitInformation::toolChain(a) == ToolChainKitInformation::toolChain(b)
            && QtSupport::QtKitInformation::qtVersion(a) == QtSupport::QtKitInformation::qtVersion(b);
}

void AndroidConfigurations::updateAutomaticKitList()
{
    QList<AndroidToolChain *> toolchains;
    if (AndroidConfigurations::instance().config().automaticKitCreation) {
        // having a empty toolchains list will remove all autodetected kits for android
        // exactly what we want in that case
hjk's avatar
hjk committed
742
        foreach (ToolChain *tc, ToolChainManager::toolChains()) {
Daniel Teske's avatar
Daniel Teske committed
743 744 745 746 747 748 749 750 751 752
            if (!tc->isAutoDetected())
                continue;
            if (tc->type() != QLatin1String(Constants::ANDROID_TOOLCHAIN_TYPE))
                continue;
            toolchains << static_cast<AndroidToolChain *>(tc);
        }
    }

    QList<Kit *> existingKits;

753 754
    foreach (Kit *k, KitManager::kits()) {
        if (DeviceKitInformation::deviceId(k) != Core::Id(Constants::ANDROID_DEVICE_ID))
Daniel Teske's avatar
Daniel Teske committed
755 756 757
            continue;
        if (!k->isAutoDetected())
            continue;
Daniel Teske's avatar
Daniel Teske committed
758 759
        if (k->isSdkProvided())
            continue;
Daniel Teske's avatar
Daniel Teske committed
760 761 762 763

        existingKits << k;
    }

764
    QMap<Abi::Architecture, QList<QtSupport::BaseQtVersion *> > qtVersionsForArch;
Daniel Teske's avatar
Daniel Teske committed
765 766 767
    foreach (QtSupport::BaseQtVersion *qtVersion, QtSupport::QtVersionManager::instance()->versions()) {
        if (qtVersion->type() != QLatin1String(Constants::ANDROIDQT))
            continue;
768
        QList<Abi> qtAbis = qtVersion->qtAbis();
769 770 771
        if (qtAbis.empty())
            continue;
        qtVersionsForArch[qtAbis.first().architecture()].append(qtVersion);
Daniel Teske's avatar
Daniel Teske committed
772 773
    }

774
    DeviceManager *dm = DeviceManager::instance();
Daniel Teske's avatar
Daniel Teske committed
775 776 777 778 779 780 781 782 783
    IDevice::ConstPtr device = dm->find(Core::Id(Constants::ANDROID_DEVICE_ID)); // should always exist

    // register new kits
    QList<Kit *> newKits;
    foreach (AndroidToolChain *tc, toolchains) {
        QList<QtSupport::BaseQtVersion *> qtVersions = qtVersionsForArch.value(tc->targetAbi().architecture());
        foreach (QtSupport::BaseQtVersion *qt, qtVersions) {
            Kit *newKit = new Kit;
            newKit->setAutoDetected(true);
784
            newKit->setIconPath(Utils::FileName::fromString(QLatin1String(Constants::ANDROID_SETTINGS_CATEGORY_ICON)));
Daniel Teske's avatar
Daniel Teske committed
785 786 787 788
            DeviceTypeKitInformation::setDeviceTypeId(newKit, Core::Id(Constants::ANDROID_DEVICE_TYPE));
            ToolChainKitInformation::setToolChain(newKit, tc);
            QtSupport::QtKitInformation::setQtVersion(newKit, qt);
            DeviceKitInformation::setDevice(newKit, device);
789 790
            Debugger::DebuggerKitInformation::setDebuggerItem(newKit,
                Debugger::GdbEngineType, tc->suggestedDebugger());
Daniel Teske's avatar
Daniel Teske committed
791
            AndroidGdbServerKitInformation::setGdbSever(newKit, tc->suggestedGdbServer());
792
            newKit->makeSticky();
Daniel Teske's avatar
Daniel Teske committed
793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811
            newKits << newKit;
        }
    }

    for (int i = existingKits.count() - 1; i >= 0; --i) {
        Kit *existingKit = existingKits.at(i);
        for (int j = 0; j < newKits.count(); ++j) {
            Kit *newKit = newKits.at(j);
            if (equalKits(existingKit, newKit)) {
                // Kit is already registered, nothing to do
                newKits.removeAt(j);
                existingKits.removeAt(i);
                KitManager::deleteKit(newKit);
                j = newKits.count();
            }
        }
    }

    foreach (Kit *k, existingKits)
812
        KitManager::deregisterKit(k);
Daniel Teske's avatar
Daniel Teske committed
813

814 815
    foreach (Kit *kit, newKits) {
        AndroidToolChain *tc = static_cast<AndroidToolChain *>(ToolChainKitInformation::toolChain(kit));
816
        AndroidQtVersion *qt = static_cast<AndroidQtVersion *>(QtSupport::QtKitInformation::qtVersion(kit));
817
        kit->setDisplayName(tr("Android for %1 (GCC %2, Qt %3)")
818
                            .arg(qt->targetArch())
819 820
                            .arg(tc->ndkToolChainVersion())
                            .arg(qt->qtVersionString()));
821
        KitManager::registerKit(kit);
822
    }
Daniel Teske's avatar
Daniel Teske committed
823 824
}

825 826 827 828 829 830 831 832 833 834 835
/**
 * Workaround for '????????????' serial numbers
 * @return ("-d") for buggy devices, ("-s", <serial no>) for normal
 */
QStringList AndroidDeviceInfo::adbSelector(const QString &serialNumber)
{
    if (serialNumber.startsWith(QLatin1String("????")))
        return QStringList() << QLatin1String("-d");
    return QStringList() << QLatin1String("-s") << serialNumber;
}

BogDan Vatra's avatar
BogDan Vatra committed
836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859
AndroidConfigurations &AndroidConfigurations::instance(QObject *parent)
{
    if (m_instance == 0)
        m_instance = new AndroidConfigurations(parent);
    return *m_instance;
}

void AndroidConfigurations::save()
{
    QSettings *settings = Core::ICore::instance()->settings();
    settings->beginGroup(SettingsGroup);
    m_config.save(*settings);
    settings->endGroup();
}

AndroidConfigurations::AndroidConfigurations(QObject *parent)
    : QObject(parent)
{
    load();
    updateAvailablePlatforms();
}

void AndroidConfigurations::load()
{
860
    bool saveSettings = false;
BogDan Vatra's avatar
BogDan Vatra committed
861 862 863
    QSettings *settings = Core::ICore::instance()->settings();
    settings->beginGroup(SettingsGroup);
    m_config = AndroidConfig(*settings);
Daniel Teske's avatar
Daniel Teske committed
864 865

    if (m_config.antLocation.isEmpty()) {
866
        Environment env = Environment::systemEnvironment();
Daniel Teske's avatar
Daniel Teske committed
867 868
        QString location = env.searchInPath(QLatin1String("ant"));
        QFileInfo fi(location);
869
        if (fi.exists() && fi.isExecutable() && !fi.isDir()) {
870
            m_config.antLocation = FileName::fromString(location);
871 872 873 874 875
            saveSettings = true;
        }
    }

    if (m_config.openJDKLocation.isEmpty()) {
876
        Environment env = Environment::systemEnvironment();
877 878 879 880 881
        QString location = env.searchInPath(QLatin1String("javac"));
        QFileInfo fi(location);
        if (fi.exists() && fi.isExecutable() && !fi.isDir()) {
            QDir parentDirectory = fi.canonicalPath();
            parentDirectory.cdUp(); // one up from bin
882
            m_config.openJDKLocation = FileName::fromString(parentDirectory.absolutePath());
883
            saveSettings = true;
884
        } else if (HostOsInfo::isWindowsHost()) {
885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913
            QSettings settings(QLatin1String("HKEY_LOCAL_MACHINE\\SOFTWARE\\Javasoft\\Java Development Kit"), QSettings::NativeFormat);
            QStringList allVersions = settings.childGroups();
            QString javaHome;
            int major = -1;
            int minor = -1;
            foreach (const QString &version, allVersions) {
                QStringList parts = version.split(QLatin1String("."));
                if (parts.size() != 2) // not interested in 1.7.0_u21
                    continue;
                bool okMajor, okMinor;
                int tmpMajor = parts.at(0).toInt(&okMajor);
                int tmpMinor = parts.at(1).toInt(&okMinor);
                if (!okMajor || !okMinor)
                    continue;
                if (tmpMajor > major
                        || (tmpMajor == major
                            && tmpMinor > minor)) {
                    settings.beginGroup(version);
                    QString tmpJavaHome = settings.value(QLatin1String("JavaHome")).toString();
                    settings.endGroup();
                    if (!QFileInfo(tmpJavaHome).exists())
                        continue;

                    major = tmpMajor;
                    minor = tmpMinor;
                    javaHome = tmpJavaHome;
                }
            }
            if (!javaHome.isEmpty()) {
914
                m_config.openJDKLocation = FileName::fromString(javaHome);
915 916 917
                saveSettings = true;
            }
        }
Daniel Teske's avatar
Daniel Teske committed
918 919
    }

BogDan Vatra's avatar
BogDan Vatra committed
920
    settings->endGroup();
921 922 923

    if (saveSettings)
        save();
924 925 926 927
}

void AndroidConfigurations::updateAndroidDevice()
{
928
    DeviceManager * const devMgr = DeviceManager::instance();
929
    if (adbToolPath().toFileInfo().exists())
930
        devMgr->addDevice(IDevice::Ptr(new Internal::AndroidDevice));
931 932
    else if (devMgr->find(Constants::ANDROID_DEVICE_ID))
        devMgr->removeDevice(Core::Id(Constants::ANDROID_DEVICE_ID));
BogDan Vatra's avatar
BogDan Vatra committed
933 934 935 936 937 938
}

AndroidConfigurations *AndroidConfigurations::m_instance = 0;

} // namespace Internal
} // namespace Android