androidconfigurations.cpp 35 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;
hjk's avatar
hjk committed
765
    foreach (QtSupport::BaseQtVersion *qtVersion, QtSupport::QtVersionManager::versions()) {
Daniel Teske's avatar
Daniel Teske committed
766
767
        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
    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) {
782
783
            if (tc->secondaryToolChain())
                continue;
Daniel Teske's avatar
Daniel Teske committed
784
785
            Kit *newKit = new Kit;
            newKit->setAutoDetected(true);
786
            newKit->setIconPath(Utils::FileName::fromString(QLatin1String(Constants::ANDROID_SETTINGS_CATEGORY_ICON)));
Daniel Teske's avatar
Daniel Teske committed
787
788
789
790
            DeviceTypeKitInformation::setDeviceTypeId(newKit, Core::Id(Constants::ANDROID_DEVICE_TYPE));
            ToolChainKitInformation::setToolChain(newKit, tc);
            QtSupport::QtKitInformation::setQtVersion(newKit, qt);
            DeviceKitInformation::setDevice(newKit, device);
791
            Debugger::DebuggerKitInformation::setDebugger(newKit,
792
                Debugger::GdbEngineType, tc->suggestedDebugger());
Daniel Teske's avatar
Daniel Teske committed
793
            AndroidGdbServerKitInformation::setGdbSever(newKit, tc->suggestedGdbServer());
794
            newKit->makeSticky();
Daniel Teske's avatar
Daniel Teske committed
795
796
797
798
799
800
801
802
803
804
805
            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);
806
                existingKits.at(i)->makeSticky();
Daniel Teske's avatar
Daniel Teske committed
807
808
809
810
811
812
813
                existingKits.removeAt(i);
                KitManager::deleteKit(newKit);
                j = newKits.count();
            }
        }
    }

814
815
    foreach (Kit *k, existingKits) {
        ProjectExplorer::ToolChain *tc = ToolChainKitInformation::toolChain(k);
816
        if (tc && tc->type() == QLatin1String(Constants::ANDROID_TOOLCHAIN_TYPE)) {
817
818
819
820
821
822
            k->makeUnSticky();
            k->setAutoDetected(false);
        } else {
            KitManager::deregisterKit(k);
        }
    }
Daniel Teske's avatar
Daniel Teske committed
823

824
825
    foreach (Kit *kit, newKits) {
        AndroidToolChain *tc = static_cast<AndroidToolChain *>(ToolChainKitInformation::toolChain(kit));
826
        AndroidQtVersion *qt = static_cast<AndroidQtVersion *>(QtSupport::QtKitInformation::qtVersion(kit));
827
        kit->setDisplayName(tr("Android for %1 (GCC %2, Qt %3)")
828
                            .arg(qt->targetArch())
829
830
                            .arg(tc->ndkToolChainVersion())
                            .arg(qt->qtVersionString()));
831
        KitManager::registerKit(kit);
832
    }
Daniel Teske's avatar
Daniel Teske committed
833
834
}

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

    if (m_config.antLocation.isEmpty()) {
876
        Environment env = Environment::systemEnvironment();
Daniel Teske's avatar
Daniel Teske committed
877
878
        QString location = env.searchInPath(QLatin1String("ant"));
        QFileInfo fi(location);
879
        if (fi.exists() && fi.isExecutable() && !fi.isDir()) {
880
            m_config.antLocation = FileName::fromString(location);
881
882
883
884
885
            saveSettings = true;
        }
    }

    if (m_config.openJDKLocation.isEmpty()) {
886
        Environment env = Environment::systemEnvironment();
887
888
889
890
891
        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
892
            m_config.openJDKLocation = FileName::fromString(parentDirectory.absolutePath());
893
            saveSettings = true;
894
        } else if (HostOsInfo::isWindowsHost()) {
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
            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()) {
924
                m_config.openJDKLocation = FileName::fromString(javaHome);
925
926
927
                saveSettings = true;
            }
        }
Daniel Teske's avatar
Daniel Teske committed
928
929
    }

BogDan Vatra's avatar
BogDan Vatra committed
930
    settings->endGroup();
931
932
933

    if (saveSettings)
        save();
934
935
936
937
}

void AndroidConfigurations::updateAndroidDevice()
{
938
    DeviceManager * const devMgr = DeviceManager::instance();
939
    if (adbToolPath().toFileInfo().exists())
940
        devMgr->addDevice(IDevice::Ptr(new Internal::AndroidDevice));
941
942
    else if (devMgr->find(Constants::ANDROID_DEVICE_ID))
        devMgr->removeDevice(Core::Id(Constants::ANDROID_DEVICE_ID));
BogDan Vatra's avatar
BogDan Vatra committed
943
944
945
946
947
948
}

AndroidConfigurations *AndroidConfigurations::m_instance = 0;

} // namespace Internal
} // namespace Android