androidconfigurations.cpp 34.9 KB
Newer Older
BogDan Vatra's avatar
BogDan Vatra committed
1 2
/**************************************************************************
**
3
** Copyright (c) 2013 BogDan Vatra <bog_dan_ro@yahoo.com>
hjk's avatar
hjk committed
4
** Contact: http://www.qt-project.org/legal
BogDan Vatra's avatar
BogDan Vatra committed
5
**
hjk's avatar
hjk committed
6
** This file is part of Qt Creator.
BogDan Vatra's avatar
BogDan Vatra committed
7
**
hjk's avatar
hjk committed
8 9 10 11 12 13 14
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia.  For licensing terms and
** conditions see http://qt.digia.com/licensing.  For further information
** use the contact form at http://qt.digia.com/contact-us.
BogDan Vatra's avatar
BogDan Vatra committed
15 16
**
** GNU Lesser General Public License Usage
hjk's avatar
hjk committed
17 18 19 20 21 22 23 24 25
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
** rights.  These rights are described in the Digia Qt LGPL Exception
BogDan Vatra's avatar
BogDan Vatra committed
26 27
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
hjk's avatar
hjk committed
28
****************************************************************************/
BogDan Vatra's avatar
BogDan Vatra committed
29 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>
Orgad Shaneh's avatar
Orgad Shaneh committed
50
#include <utils/sleep.h>
BogDan Vatra's avatar
BogDan Vatra committed
51 52 53 54 55 56 57 58 59 60 61 62

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

#include <QStringListModel>
#include <QMessageBox>

hjk's avatar
hjk committed
63
using namespace ProjectExplorer;
BogDan Vatra's avatar
BogDan Vatra committed
64 65 66 67 68 69 70 71 72 73 74 75 76
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
77
    const QLatin1String AutomaticKitCreationKey("AutomatiKitCreation");
78
    const QLatin1String MakeExtraSearchDirectory("MakeExtraSearchDirectory");
BogDan Vatra's avatar
BogDan Vatra committed
79
    const QLatin1String PartitionSizeKey("PartitionSize");
80
    const QLatin1String ToolchainHostKey("ToolchainHost");
BogDan Vatra's avatar
BogDan Vatra committed
81 82
    const QLatin1String ArmToolchainPrefix("arm-linux-androideabi");
    const QLatin1String X86ToolchainPrefix("x86");
BogDan Vatra's avatar
BogDan Vatra committed
83
    const QLatin1String MipsToolchainPrefix("mipsel-linux-android");
BogDan Vatra's avatar
BogDan Vatra committed
84
    const QLatin1String ArmToolsPrefix("arm-linux-androideabi");
BogDan Vatra's avatar
BogDan Vatra committed
85
    const QLatin1String X86ToolsPrefix("i686-linux-android");
BogDan Vatra's avatar
BogDan Vatra committed
86
    const QLatin1String MipsToolsPrefix("mipsel-linux-android");
BogDan Vatra's avatar
BogDan Vatra committed
87 88 89 90 91
    const QLatin1String Unknown("unknown");
    const QLatin1String keytoolName("keytool");
    const QLatin1String jarsignerName("jarsigner");
    const QLatin1String changeTimeStamp("ChangeTimeStamp");

92
    static QString sdkSettingsFileName()
BogDan Vatra's avatar
BogDan Vatra committed
93
    {
94 95
        return QFileInfo(Core::ICore::settings(QSettings::SystemScope)->fileName()).absolutePath()
                + QLatin1String("/qtcreator/android.xml");
BogDan Vatra's avatar
BogDan Vatra committed
96 97
    }

98
    bool androidDevicesLessThan(const AndroidDeviceInfo &dev1, const AndroidDeviceInfo &dev2)
BogDan Vatra's avatar
BogDan Vatra committed
99
    {
Daniel Teske's avatar
Compile  
Daniel Teske committed
100 101
        if (dev1.serialNumber.contains(QLatin1String("????")) == dev2.serialNumber.contains(QLatin1String("????")))
            return !dev1.serialNumber.contains(QLatin1String("????"));
102 103 104 105 106 107 108
        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
109 110 111
    }
}

112 113 114 115 116 117
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
118 119
    if (toolchainprefix == MipsToolchainPrefix)
        return Abi::MipsArchitecture;
120 121 122
    return Abi::UnknownArchitecture;
}

hjk's avatar
hjk committed
123
QLatin1String AndroidConfigurations::toolchainPrefix(Abi::Architecture architecture)
BogDan Vatra's avatar
BogDan Vatra committed
124 125
{
    switch (architecture) {
hjk's avatar
hjk committed
126
    case Abi::ArmArchitecture:
BogDan Vatra's avatar
BogDan Vatra committed
127
        return ArmToolchainPrefix;
hjk's avatar
hjk committed
128
    case Abi::X86Architecture:
BogDan Vatra's avatar
BogDan Vatra committed
129
        return X86ToolchainPrefix;
BogDan Vatra's avatar
BogDan Vatra committed
130 131
    case Abi::MipsArchitecture:
        return MipsToolchainPrefix;
BogDan Vatra's avatar
BogDan Vatra committed
132 133 134 135 136
    default:
        return Unknown;
    }
}

hjk's avatar
hjk committed
137
QLatin1String AndroidConfigurations::toolsPrefix(Abi::Architecture architecture)
BogDan Vatra's avatar
BogDan Vatra committed
138 139
{
    switch (architecture) {
hjk's avatar
hjk committed
140
    case Abi::ArmArchitecture:
BogDan Vatra's avatar
BogDan Vatra committed
141
        return ArmToolsPrefix;
hjk's avatar
hjk committed
142
    case Abi::X86Architecture:
BogDan Vatra's avatar
BogDan Vatra committed
143
        return X86ToolsPrefix;
BogDan Vatra's avatar
BogDan Vatra committed
144 145
    case Abi::MipsArchitecture:
        return MipsToolsPrefix;
BogDan Vatra's avatar
BogDan Vatra committed
146 147 148 149 150 151 152 153 154
    default:
        return Unknown;
    }
}

AndroidConfig::AndroidConfig(const QSettings &settings)
{
    // user settings
    partitionSize = settings.value(PartitionSizeKey, 1024).toInt();
hjk's avatar
hjk committed
155 156 157 158 159
    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());
160
    toolchainHost = settings.value(ToolchainHostKey).toString();
Daniel Teske's avatar
Daniel Teske committed
161
    automaticKitCreation = settings.value(AutomaticKitCreationKey, true).toBool();
162 163 164 165 166
    QString extraDirectory = settings.value(MakeExtraSearchDirectory).toString();
    if (extraDirectory.isEmpty())
        makeExtraSearchDirectories = QStringList();
    else
        makeExtraSearchDirectories << extraDirectory;
BogDan Vatra's avatar
BogDan Vatra committed
167 168

    PersistentSettingsReader reader;
169 170
    if (reader.load(FileName::fromString(sdkSettingsFileName()))
            && settings.value(changeTimeStamp).toInt() != QFileInfo(sdkSettingsFileName()).lastModified().toMSecsSinceEpoch() / 1000) {
BogDan Vatra's avatar
BogDan Vatra committed
171
        // persisten settings
hjk's avatar
hjk committed
172 173 174 175 176
        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());
177
        toolchainHost = reader.restoreValue(ToolchainHostKey).toString();
Daniel Teske's avatar
Daniel Teske committed
178 179 180
        QVariant v = reader.restoreValue(AutomaticKitCreationKey);
        if (v.isValid())
            automaticKitCreation = v.toBool();
181 182 183 184 185
        QString extraDirectory = reader.restoreValue(MakeExtraSearchDirectory).toString();
        if (extraDirectory.isEmpty())
            makeExtraSearchDirectories = QStringList();
        else
            makeExtraSearchDirectories << extraDirectory;
BogDan Vatra's avatar
BogDan Vatra committed
186 187 188 189 190 191 192 193 194 195 196 197
        // persistent settings
    }

}

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

void AndroidConfig::save(QSettings &settings) const
{
198
    QFileInfo fileInfo(sdkSettingsFileName());
BogDan Vatra's avatar
BogDan Vatra committed
199 200 201 202
    if (fileInfo.exists())
        settings.setValue(changeTimeStamp, fileInfo.lastModified().toMSecsSinceEpoch() / 1000);

    // user settings
Tobias Hunger's avatar
Tobias Hunger committed
203 204 205 206 207
    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
208
    settings.setValue(PartitionSizeKey, partitionSize);
Daniel Teske's avatar
Daniel Teske committed
209
    settings.setValue(AutomaticKitCreationKey, automaticKitCreation);
210
    settings.setValue(ToolchainHostKey, toolchainHost);
211 212 213
    settings.setValue(MakeExtraSearchDirectory,
                      makeExtraSearchDirectories.isEmpty() ? QString()
                                                           : makeExtraSearchDirectories.at(0));
BogDan Vatra's avatar
BogDan Vatra committed
214 215 216 217 218
}

void AndroidConfigurations::setConfig(const AndroidConfig &devConfigs)
{
    m_config = devConfigs;
219 220 221 222

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

BogDan Vatra's avatar
BogDan Vatra committed
223 224
    save();
    updateAvailablePlatforms();
Daniel Teske's avatar
Daniel Teske committed
225
    updateAutomaticKitList();
226
    updateAndroidDevice();
BogDan Vatra's avatar
BogDan Vatra committed
227 228 229 230 231 232
    emit updated();
}

void AndroidConfigurations::updateAvailablePlatforms()
{
    m_availablePlatforms.clear();
hjk's avatar
hjk committed
233
    FileName path = m_config.ndkLocation;
BogDan Vatra's avatar
BogDan Vatra committed
234
    QDirIterator it(path.appendPath(QLatin1String("platforms")).toString(), QStringList() << QLatin1String("android-*"), QDir::Dirs);
BogDan Vatra's avatar
BogDan Vatra committed
235 236 237 238 239 240 241 242 243 244 245
    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
246
    proc.start(androidToolPath().toString(), QStringList() << QLatin1String("list") << QLatin1String("target")); // list avaialbe AVDs
BogDan Vatra's avatar
BogDan Vatra committed
247 248 249 250
    if (!proc.waitForFinished(-1)) {
        proc.terminate();
        return targets;
    }
251
    while (proc.canReadLine()) {
252
        const QString line = QString::fromLocal8Bit(proc.readLine().trimmed());
BogDan Vatra's avatar
BogDan Vatra committed
253 254 255 256 257 258 259 260 261 262
        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
263
FileName AndroidConfigurations::adbToolPath() const
BogDan Vatra's avatar
BogDan Vatra committed
264
{
hjk's avatar
hjk committed
265
    FileName path = m_config.sdkLocation;
266
    return path.appendPath(QLatin1String("platform-tools/adb" QTC_HOST_EXE_SUFFIX));
BogDan Vatra's avatar
BogDan Vatra committed
267 268
}

hjk's avatar
hjk committed
269
FileName AndroidConfigurations::androidToolPath() const
BogDan Vatra's avatar
BogDan Vatra committed
270
{
271 272 273 274
    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;
275
        path.appendPath(QLatin1String("tools/android" QTC_HOST_EXE_SUFFIX));
276 277 278
        if (path.toFileInfo().exists())
            return path;
        path = m_config.sdkLocation;
279
        return path.appendPath(QLatin1String("tools/android" ANDROID_BAT_SUFFIX));
280 281 282 283
    } else {
        FileName path = m_config.sdkLocation;
        return path.appendPath(QLatin1String("tools/android"));
    }
BogDan Vatra's avatar
BogDan Vatra committed
284 285
}

hjk's avatar
hjk committed
286
FileName AndroidConfigurations::antToolPath() const
BogDan Vatra's avatar
BogDan Vatra committed
287
{
Tobias Hunger's avatar
Tobias Hunger committed
288
    if (!m_config.antLocation.isEmpty())
BogDan Vatra's avatar
BogDan Vatra committed
289 290
        return m_config.antLocation;
    else
hjk's avatar
hjk committed
291
        return FileName::fromString(QLatin1String("ant"));
BogDan Vatra's avatar
BogDan Vatra committed
292 293
}

hjk's avatar
hjk committed
294
FileName AndroidConfigurations::emulatorToolPath() const
BogDan Vatra's avatar
BogDan Vatra committed
295
{
hjk's avatar
hjk committed
296
    FileName path = m_config.sdkLocation;
297
    return path.appendPath(QLatin1String("tools/emulator" QTC_HOST_EXE_SUFFIX));
BogDan Vatra's avatar
BogDan Vatra committed
298 299
}

300
FileName AndroidConfigurations::toolPath(Abi::Architecture architecture, const QString &ndkToolChainVersion) const
BogDan Vatra's avatar
BogDan Vatra committed
301
{
hjk's avatar
hjk committed
302
    FileName path = m_config.ndkLocation;
Tobias Hunger's avatar
Tobias Hunger committed
303
    return path.appendPath(QString::fromLatin1("toolchains/%1-%2/prebuilt/%3/bin/%4")
BogDan Vatra's avatar
BogDan Vatra committed
304
            .arg(toolchainPrefix(architecture))
305
            .arg(ndkToolChainVersion)
306
            .arg(m_config.toolchainHost)
Tobias Hunger's avatar
Tobias Hunger committed
307
            .arg(toolsPrefix(architecture)));
BogDan Vatra's avatar
BogDan Vatra committed
308 309
}

310
FileName AndroidConfigurations::stripPath(Abi::Architecture architecture, const QString &ndkToolChainVersion) const
BogDan Vatra's avatar
BogDan Vatra committed
311
{
312
    return toolPath(architecture, ndkToolChainVersion).append(QLatin1String("-strip" QTC_HOST_EXE_SUFFIX));
BogDan Vatra's avatar
BogDan Vatra committed
313 314
}

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

320 321 322 323 324
FileName AndroidConfigurations::gccPath(Abi::Architecture architecture, const QString &ndkToolChainVersion) const
{
    return toolPath(architecture, ndkToolChainVersion).append(QLatin1String("-gcc" QTC_HOST_EXE_SUFFIX));
}

325
FileName AndroidConfigurations::gdbPath(Abi::Architecture architecture, const QString &ndkToolChainVersion) const
BogDan Vatra's avatar
BogDan Vatra committed
326
{
327
    return toolPath(architecture, ndkToolChainVersion).append(QLatin1String("-gdb" QTC_HOST_EXE_SUFFIX));
BogDan Vatra's avatar
BogDan Vatra committed
328 329
}

hjk's avatar
hjk committed
330
FileName AndroidConfigurations::openJDKPath() const
BogDan Vatra's avatar
BogDan Vatra committed
331 332 333 334
{
    return m_config.openJDKLocation;
}

335 336 337 338
void AndroidConfigurations::detectToolchainHost()
{
    QStringList hostPatterns;
    switch (HostOsInfo::hostOs()) {
339
    case OsTypeLinux:
340 341
        hostPatterns << QLatin1String("linux*");
        break;
342
    case OsTypeWindows:
343 344
        hostPatterns << QLatin1String("windows*");
        break;
345
    case OsTypeMac:
346 347 348 349 350 351 352 353 354 355 356 357 358
        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
359
FileName AndroidConfigurations::openJDKBinPath() const
BogDan Vatra's avatar
BogDan Vatra committed
360
{
hjk's avatar
hjk committed
361
    FileName path = m_config.openJDKLocation;
Tobias Hunger's avatar
Tobias Hunger committed
362 363 364
    if (!path.isEmpty())
        return path.appendPath(QLatin1String("bin"));
    return path;
BogDan Vatra's avatar
BogDan Vatra committed
365 366
}

hjk's avatar
hjk committed
367
FileName AndroidConfigurations::keytoolPath() const
BogDan Vatra's avatar
BogDan Vatra committed
368
{
Tobias Hunger's avatar
Tobias Hunger committed
369
    return openJDKBinPath().appendPath(keytoolName);
BogDan Vatra's avatar
BogDan Vatra committed
370 371
}

hjk's avatar
hjk committed
372
FileName AndroidConfigurations::jarsignerPath() const
BogDan Vatra's avatar
BogDan Vatra committed
373
{
Tobias Hunger's avatar
Tobias Hunger committed
374
    return openJDKBinPath().appendPath(jarsignerName);
BogDan Vatra's avatar
BogDan Vatra committed
375 376
}

hjk's avatar
hjk committed
377
FileName AndroidConfigurations::zipalignPath() const
378
{
379
    FileName path = m_config.sdkLocation;
380
    return path.appendPath(QLatin1String("tools/zipalign" QTC_HOST_EXE_SUFFIX));
381 382
}

383
QString AndroidConfigurations::getDeployDeviceSerialNumber(int *apiLevel, const QString &abi, QString *error) const
BogDan Vatra's avatar
BogDan Vatra committed
384
{
385
    QVector<AndroidDeviceInfo> devices = connectedDevices(error);
BogDan Vatra's avatar
BogDan Vatra committed
386

387
    foreach (AndroidDeviceInfo device, devices) {
388 389 390 391 392 393
        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)) {
394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409
            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
410 411 412
            *apiLevel = device.sdk;
            return device.serialNumber;
        }
Tobias Hunger's avatar
Tobias Hunger committed
413
    }
414
    return QString();
BogDan Vatra's avatar
BogDan Vatra committed
415 416
}

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

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

BogDan Vatra's avatar
BogDan Vatra committed
444
    qSort(devices.begin(), devices.end(), androidDevicesLessThan);
445 446
    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
447 448 449
    return devices;
}

450
QString AndroidConfigurations::createAVD(int minApiLevel, QString targetArch) const
BogDan Vatra's avatar
BogDan Vatra committed
451 452 453 454
{
    QDialog d;
    Ui::AddNewAVDDialog avdDialog;
    avdDialog.setupUi(&d);
455
    avdDialog.targetComboBox->addItems(sdkTargets(minApiLevel));
456 457 458 459 460 461 462 463 464 465

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

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

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

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

bool AndroidConfigurations::removeAVD(const QString &name) const
{
    QProcess proc;
Tobias Hunger's avatar
Tobias Hunger committed
506
    proc.start(androidToolPath().toString(),
BogDan Vatra's avatar
BogDan Vatra committed
507 508 509 510 511 512 513 514 515
               QStringList() << QLatin1String("delete") << QLatin1String("avd")
               << QLatin1String("-n") << name);
    if (!proc.waitForFinished(-1)) {
        proc.terminate();
        return false;
    }
    return !proc.exitCode();
}

516
QVector<AndroidDeviceInfo> AndroidConfigurations::androidVirtualDevices() const
BogDan Vatra's avatar
BogDan Vatra committed
517
{
518
    QVector<AndroidDeviceInfo> devices;
BogDan Vatra's avatar
BogDan Vatra committed
519
    QProcess proc;
Tobias Hunger's avatar
Tobias Hunger committed
520
    proc.start(androidToolPath().toString(),
BogDan Vatra's avatar
BogDan Vatra committed
521 522 523 524 525 526 527
               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();
528
    AndroidDeviceInfo dev;
BogDan Vatra's avatar
BogDan Vatra committed
529 530 531 532 533 534 535 536 537 538 539 540 541 542
    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:")))
543
                dev.cpuAbi = QStringList() << line.mid(line.lastIndexOf(QLatin1Char(' '))).trimmed();
BogDan Vatra's avatar
BogDan Vatra committed
544
        }
545 546 547
        // armeabi-v7a devices can also run armeabi code
        if (dev.cpuAbi == QStringList(QLatin1String("armeabi-v7a")))
            dev.cpuAbi << QLatin1String("armeabi");
548
        dev.unauthorized = false;
BogDan Vatra's avatar
BogDan Vatra committed
549 550 551 552 553 554 555
        devices.push_back(dev);
    }
    qSort(devices.begin(), devices.end(), androidDevicesLessThan);

    return devices;
}

556
QString AndroidConfigurations::findAvd(int *apiLevel, const QString &cpuAbi)
BogDan Vatra's avatar
BogDan Vatra committed
557
{
558 559 560 561 562 563
    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
564 565
        }
    }
566 567 568 569 570 571 572 573 574
    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
575

576 577 578 579 580
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
581 582

    // start the emulator
Orgad Shaneh's avatar
Orgad Shaneh committed
583
    avdProcess->start(emulatorToolPath().toString(),
BogDan Vatra's avatar
BogDan Vatra committed
584 585
                        QStringList() << QLatin1String("-partition-size") << QString::number(config().partitionSize)
                        << QLatin1String("-avd") << avdName);
Orgad Shaneh's avatar
Orgad Shaneh committed
586 587
    if (!avdProcess->waitForStarted(-1)) {
        delete avdProcess;
588
        return false;
BogDan Vatra's avatar
BogDan Vatra committed
589
    }
590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605
    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;
606
            if (device.sdk != apiLevel)
607 608 609 610 611 612 613
                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
Orgad Shaneh's avatar
Orgad Shaneh committed
614
                    Utils::sleep(8000);
615
            }
Orgad Shaneh's avatar
Orgad Shaneh committed
616 617
            return QString();
        }
Orgad Shaneh's avatar
Orgad Shaneh committed
618
        Utils::sleep(8000);
BogDan Vatra's avatar
BogDan Vatra committed
619 620 621 622 623 624
    }
    return QString();
}

int AndroidConfigurations::getSDKVersion(const QString &device) const
{
625 626 627 628
    // 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
629 630

    QProcess adbProc;
631
    adbProc.start(adbToolPath().toString(), arguments);
BogDan Vatra's avatar
BogDan Vatra committed
632
    if (!adbProc.waitForFinished(-1)) {
633
        adbProc.kill();
BogDan Vatra's avatar
BogDan Vatra committed
634 635 636 637 638
        return -1;
    }
    return adbProc.readAll().trimmed().toInt();
}

639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662
//!
//! \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;
}

663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680
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;
}

681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707
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
708 709 710 711 712 713 714 715 716 717
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");
}

718 719 720 721 722
QStringList AndroidConfigurations::makeExtraSearchDirectories() const
{
    return m_config.makeExtraSearchDirectories;
}

723
static bool equalKits(Kit *a, Kit *b)
Daniel Teske's avatar
Daniel Teske committed
724 725 726 727 728 729 730 731 732 733 734
{
    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
735
        foreach (ToolChain *tc, ToolChainManager::toolChains()) {
Daniel Teske's avatar
Daniel Teske committed
736 737 738 739 740 741 742 743 744 745
            if (!tc->isAutoDetected())
                continue;
            if (tc->type() != QLatin1String(Constants::ANDROID_TOOLCHAIN_TYPE))
                continue;
            toolchains << static_cast<AndroidToolChain *>(tc);
        }
    }

    QList<Kit *> existingKits;

746 747
    foreach (Kit *k, KitManager::kits()) {
        if (DeviceKitInformation::deviceId(k) != Core::Id(Constants::ANDROID_DEVICE_ID))
Daniel Teske's avatar
Daniel Teske committed
748 749 750
            continue;
        if (!k->isAutoDetected())
            continue;
Daniel Teske's avatar
Daniel Teske committed
751 752
        if (k->isSdkProvided())
            continue;
Daniel Teske's avatar
Daniel Teske committed
753 754 755 756

        existingKits << k;
    }

757
    QMap<Abi::Architecture, QList<QtSupport::BaseQtVersion *> > qtVersionsForArch;
hjk's avatar
hjk committed
758
    foreach (QtSupport::BaseQtVersion *qtVersion, QtSupport::QtVersionManager::versions()) {
Daniel Teske's avatar
Daniel Teske committed
759 760
        if (qtVersion->type() != QLatin1String(Constants::ANDROIDQT))
            continue;
761
        QList<Abi> qtAbis = qtVersion->qtAbis();
762 763 764
        if (qtAbis.empty())
            continue;
        qtVersionsForArch[qtAbis.first().architecture()].append(qtVersion);
Daniel Teske's avatar
Daniel Teske committed
765 766
    }

767
    DeviceManager *dm = DeviceManager::instance();
Daniel Teske's avatar
Daniel Teske committed
768 769 770 771 772 773 774
    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) {
775 776
            if (tc->secondaryToolChain())
                continue;
Daniel Teske's avatar
Daniel Teske committed
777 778
            Kit *newKit = new Kit;
            newKit->setAutoDetected(true);
779
            newKit->setIconPath(Utils::FileName::fromString(QLatin1String(Constants::ANDROID_SETTINGS_CATEGORY_ICON)));
Daniel Teske's avatar
Daniel Teske committed
780 781 782 783
            DeviceTypeKitInformation::setDeviceTypeId(newKit, Core::Id(Constants::ANDROID_DEVICE_TYPE));
            ToolChainKitInformation::setToolChain(newKit, tc);
            QtSupport::QtKitInformation::setQtVersion(newKit, qt);
            DeviceKitInformation::setDevice(newKit, device);
784
            Debugger::DebuggerKitInformation::setDebugger(newKit,
785
                Debugger::GdbEngineType, tc->suggestedDebugger());
Daniel Teske's avatar
Daniel Teske committed
786
            AndroidGdbServerKitInformation::setGdbSever(newKit, tc->suggestedGdbServer());
787
            newKit->makeSticky();
Daniel Teske's avatar
Daniel Teske committed
788 789 790 791 792 793 794 795 796 797 798
            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);
799
                existingKits.at(i)->makeSticky();
Daniel Teske's avatar
Daniel Teske committed
800 801 802 803 804 805 806
                existingKits.removeAt(i);
                KitManager::deleteKit(newKit);
                j = newKits.count();
            }
        }
    }

807 808
    foreach (Kit *k, existingKits) {
        ProjectExplorer::ToolChain *tc = ToolChainKitInformation::toolChain(k);
809
        if (tc && tc->type() == QLatin1String(Constants::ANDROID_TOOLCHAIN_TYPE)) {
810 811 812 813 814 815
            k->makeUnSticky();
            k->setAutoDetected(false);
        } else {
            KitManager::deregisterKit(k);
        }
    }
Daniel Teske's avatar
Daniel Teske committed
816

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

828 829 830 831 832 833 834 835 836 837 838
/**
 * 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
839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862
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()
{
863
    bool saveSettings = false;
BogDan Vatra's avatar
BogDan Vatra committed
864 865 866
    QSettings *settings = Core::ICore::instance()->settings();
    settings->beginGroup(SettingsGroup);
    m_config = AndroidConfig(*settings);
Daniel Teske's avatar
Daniel Teske committed
867 868

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

    if (m_config.openJDKLocation.isEmpty()) {
879
        Environment env = Environment::systemEnvironment();
880 881 882 883 884
        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
885
            m_config.openJDKLocation = FileName::fromString(parentDirectory.absolutePath());
886
            saveSettings = true;
887
        } else if (HostOsInfo::isWindowsHost()) {
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 914 915 916
            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()) {
917
                m_config.openJDKLocation = FileName::fromString(javaHome);
918 919 920
                saveSettings = true;
            }
        }
Daniel Teske's avatar
Daniel Teske committed
921 922
    }

BogDan Vatra's avatar
BogDan Vatra committed
923
    settings->endGroup();
924 925 926

    if (saveSettings)
        save();
927 928 929 930
}

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

AndroidConfigurations *AndroidConfigurations::m_instance = 0;

} // namespace Internal
} // namespace Android