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

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

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

#include <QStringListModel>
#include <QMessageBox>

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

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

101
    bool androidDevicesLessThan(const AndroidDeviceInfo &dev1, const AndroidDeviceInfo &dev2)
BogDan Vatra's avatar
BogDan Vatra committed
102
    {
Daniel Teske's avatar
Compile    
Daniel Teske committed
103
104
        if (dev1.serialNumber.contains(QLatin1String("????")) == dev2.serialNumber.contains(QLatin1String("????")))
            return !dev1.serialNumber.contains(QLatin1String("????"));
105
106
        if (dev1.type != dev2.type)
            return dev1.type == AndroidDeviceInfo::Hardware;
107
108
        if (dev1.sdk != dev2.sdk)
            return dev1.sdk < dev2.sdk;
109

110
        return dev1.serialNumber < dev2.serialNumber;
BogDan Vatra's avatar
BogDan Vatra committed
111
112
113
    }
}

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

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

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

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

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

}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

385
AndroidDeviceInfo AndroidConfigurations::showDeviceDialog(ProjectExplorer::Project *project, int apiLevel, const QString &abi)
BogDan Vatra's avatar
BogDan Vatra committed
386
{
387
388
389
390
391
392
393
    QString serialNumber = defaultDevice(project, abi);
    if (!serialNumber.isEmpty()) {
        // search for that device
        foreach (const AndroidDeviceInfo &info, AndroidConfigurations::instance().connectedDevices())
            if (info.serialNumber == serialNumber
                    && info.sdk >= apiLevel)
                return info;
BogDan Vatra's avatar
BogDan Vatra committed
394

395
396
397
398
399
400
401
402
403
404
405
406
        foreach (const AndroidDeviceInfo &info, AndroidConfigurations::instance().androidVirtualDevices())
            if (info.serialNumber == serialNumber
                    && info.sdk >= apiLevel)
                return info;
    }

    AndroidDeviceDialog dialog(apiLevel, abi);
    if (dialog.exec() == QDialog::Accepted) {
        AndroidDeviceInfo info = dialog.device();
        if (dialog.saveDeviceSelection()) {
            if (!info.serialNumber.isEmpty())
                AndroidConfigurations::instance().setDefaultDevice(project, abi, info.serialNumber);
BogDan Vatra's avatar
BogDan Vatra committed
407
        }
408
        return info;
Tobias Hunger's avatar
Tobias Hunger committed
409
    }
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
    return AndroidDeviceInfo();
}

void AndroidConfigurations::clearDefaultDevices(ProjectExplorer::Project *project)
{
    if (m_defaultDeviceForAbi.contains(project))
        m_defaultDeviceForAbi.remove(project);
}

void AndroidConfigurations::setDefaultDevice(ProjectExplorer::Project *project, const QString &abi, const QString &serialNumber)
{
    m_defaultDeviceForAbi[project][abi] = serialNumber;
}

QString AndroidConfigurations::defaultDevice(Project *project, const QString &abi) const
{
    if (!m_defaultDeviceForAbi.contains(project))
        return QString();
    const QMap<QString, QString> &map = m_defaultDeviceForAbi.value(project);
    if (!map.contains(abi))
        return QString();
    return map.value(abi);
BogDan Vatra's avatar
BogDan Vatra committed
432
433
}

434
QVector<AndroidDeviceInfo> AndroidConfigurations::connectedDevices(QString *error) const
BogDan Vatra's avatar
BogDan Vatra committed
435
{
436
    QVector<AndroidDeviceInfo> devices;
BogDan Vatra's avatar
BogDan Vatra committed
437
    QProcess adbProc;
Tobias Hunger's avatar
Tobias Hunger committed
438
    adbProc.start(adbToolPath().toString(), QStringList() << QLatin1String("devices"));
BogDan Vatra's avatar
BogDan Vatra committed
439
    if (!adbProc.waitForFinished(-1)) {
440
        adbProc.kill();
441
442
        if (error)
            *error = tr("Could not run: %1").arg(adbToolPath().toString() + QLatin1String(" devices"));
BogDan Vatra's avatar
BogDan Vatra committed
443
444
445
446
        return devices;
    }
    QList<QByteArray> adbDevs = adbProc.readAll().trimmed().split('\n');
    adbDevs.removeFirst();
447
448
449

    // workaround for '????????????' serial numbers:
    // can use "adb -d" when only one usb device attached
BogDan Vatra's avatar
BogDan Vatra committed
450
    foreach (const QByteArray &device, adbDevs) {
451
452
453
        const QString serialNo = QString::fromLatin1(device.left(device.indexOf('\t')).trimmed());
        const QString deviceType = QString::fromLatin1(device.mid(device.indexOf('\t'))).trimmed();
        AndroidDeviceInfo dev;
454
        dev.serialNumber = serialNo;
455
        dev.type = serialNo.startsWith(QLatin1String("emulator")) ? AndroidDeviceInfo::Emulator : AndroidDeviceInfo::Hardware;
BogDan Vatra's avatar
BogDan Vatra committed
456
        dev.sdk = getSDKVersion(dev.serialNumber);
457
        dev.cpuAbi = getAbis(dev.serialNumber);
458
        dev.unauthorized = (deviceType == QLatin1String("unauthorized"));
BogDan Vatra's avatar
BogDan Vatra committed
459
460
        devices.push_back(dev);
    }
461

BogDan Vatra's avatar
BogDan Vatra committed
462
    qSort(devices.begin(), devices.end(), androidDevicesLessThan);
463
464
    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
465
466
467
    return devices;
}

468
QString AndroidConfigurations::createAVD(int minApiLevel, QString targetArch) const
BogDan Vatra's avatar
BogDan Vatra committed
469
470
471
472
{
    QDialog d;
    Ui::AddNewAVDDialog avdDialog;
    avdDialog.setupUi(&d);
473
    avdDialog.targetComboBox->addItems(sdkTargets(minApiLevel));
474
475
476
477
478
479
480
481
482
483

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

484
    if (!avdDialog.targetComboBox->count()) {
Friedemann Kleint's avatar
Friedemann Kleint committed
485
        QMessageBox::critical(0, tr("Error Creating AVD"),
486
                              tr("Cannot create a new AVD. No sufficiently recent Android SDK available.\n"
Friedemann Kleint's avatar
Friedemann Kleint committed
487
                                 "Please install an SDK of at least API version %1.").
Friedemann Kleint's avatar
Friedemann Kleint committed
488
                              arg(minApiLevel));
489
        return QString();
BogDan Vatra's avatar
BogDan Vatra committed
490
491
492
493
494
495
    }

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

500
QString AndroidConfigurations::createAVD(const QString &target, const QString &name, const QString &abi, int sdcardSize ) const
BogDan Vatra's avatar
BogDan Vatra committed
501
502
{
    QProcess proc;
Tobias Hunger's avatar
Tobias Hunger committed
503
    proc.start(androidToolPath().toString(),
BogDan Vatra's avatar
BogDan Vatra committed
504
               QStringList() << QLatin1String("create") << QLatin1String("avd")
505
               << QLatin1String("-t") << target
BogDan Vatra's avatar
BogDan Vatra committed
506
               << QLatin1String("-n") << name
507
               << QLatin1String("-b") << abi
BogDan Vatra's avatar
BogDan Vatra committed
508
509
               << QLatin1String("-c") << QString::fromLatin1("%1M").arg(sdcardSize));
    if (!proc.waitForStarted())
510
        return QString();
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531

    proc.write(QByteArray("yes\n")); // yes to "Do you wish to create a custom hardware profile"

    QByteArray question;
    while (true) {
        proc.waitForReadyRead(500);
        question += proc.readAllStandardOutput();
        if (question.endsWith(QByteArray("]:"))) {
            // truncate to last line
            int index = question.lastIndexOf(QByteArray("\n"));
            if (index != -1)
                question = question.mid(index);
            if (question.contains("hw.gpu.enabled"))
                proc.write(QByteArray("yes\n"));
            else
                proc.write(QByteArray("\n"));
            question.clear();
        }

        if (proc.state() != QProcess::Running)
            break;
BogDan Vatra's avatar
BogDan Vatra committed
532
    }
533
534
535

    proc.waitForFinished();

536
537
538
    if (proc.exitCode()) // error!
        return QString();
    return name;
BogDan Vatra's avatar
BogDan Vatra committed
539
540
541
542
543
}

bool AndroidConfigurations::removeAVD(const QString &name) const
{
    QProcess proc;
Tobias Hunger's avatar
Tobias Hunger committed
544
    proc.start(androidToolPath().toString(),
BogDan Vatra's avatar
BogDan Vatra committed
545
546
547
548
549
550
551
552
553
               QStringList() << QLatin1String("delete") << QLatin1String("avd")
               << QLatin1String("-n") << name);
    if (!proc.waitForFinished(-1)) {
        proc.terminate();
        return false;
    }
    return !proc.exitCode();
}

554
QVector<AndroidDeviceInfo> AndroidConfigurations::androidVirtualDevices() const
BogDan Vatra's avatar
BogDan Vatra committed
555
{
556
    QVector<AndroidDeviceInfo> devices;
BogDan Vatra's avatar
BogDan Vatra committed
557
    QProcess proc;
Tobias Hunger's avatar
Tobias Hunger committed
558
    proc.start(androidToolPath().toString(),
BogDan Vatra's avatar
BogDan Vatra committed
559
560
561
562
563
564
565
               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();
566
    AndroidDeviceInfo dev;
BogDan Vatra's avatar
BogDan Vatra committed
567
568
569
570
571
572
573
574
575
576
577
578
579
580
    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:")))
581
                dev.cpuAbi = QStringList() << line.mid(line.lastIndexOf(QLatin1Char(' '))).trimmed();
BogDan Vatra's avatar
BogDan Vatra committed
582
        }
583
584
585
        // armeabi-v7a devices can also run armeabi code
        if (dev.cpuAbi == QStringList(QLatin1String("armeabi-v7a")))
            dev.cpuAbi << QLatin1String("armeabi");
586
        dev.unauthorized = false;
587
        dev.type = AndroidDeviceInfo::Emulator;
BogDan Vatra's avatar
BogDan Vatra committed
588
589
590
591
592
593
594
        devices.push_back(dev);
    }
    qSort(devices.begin(), devices.end(), androidDevicesLessThan);

    return devices;
}

595
596
597
598
599
600
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
601

602
603
604
605
606
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
607
608

    // start the emulator
Orgad Shaneh's avatar
Orgad Shaneh committed
609
    avdProcess->start(emulatorToolPath().toString(),
BogDan Vatra's avatar
BogDan Vatra committed
610
611
                        QStringList() << QLatin1String("-partition-size") << QString::number(config().partitionSize)
                        << QLatin1String("-avd") << avdName);
Orgad Shaneh's avatar
Orgad Shaneh committed
612
613
    if (!avdProcess->waitForStarted(-1)) {
        delete avdProcess;
614
        return false;
BogDan Vatra's avatar
BogDan Vatra committed
615
    }
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
    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;
632
            if (device.sdk != apiLevel)
633
634
635
636
637
638
639
                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
640
                    Utils::sleep(8000);
641
            }
Orgad Shaneh's avatar
Orgad Shaneh committed
642
643
            return QString();
        }
Orgad Shaneh's avatar
Orgad Shaneh committed
644
        Utils::sleep(8000);
BogDan Vatra's avatar
BogDan Vatra committed
645
646
647
648
649
650
    }
    return QString();
}

int AndroidConfigurations::getSDKVersion(const QString &device) const
{
651
652
653
654
    // 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
655
656

    QProcess adbProc;
657
    adbProc.start(adbToolPath().toString(), arguments);
BogDan Vatra's avatar
BogDan Vatra committed
658
    if (!adbProc.waitForFinished(-1)) {
659
        adbProc.kill();
BogDan Vatra's avatar
BogDan Vatra committed
660
661
662
663
664
        return -1;
    }
    return adbProc.readAll().trimmed().toInt();
}

665
666
667
668
669
670
671
//!
//! \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
{
672
673
    if (m_serialNumberToDeviceName.contains(device))
        return m_serialNumberToDeviceName.value(device);
674
675
676
677
678
679
680
681
682
683
684
685
686
687
    // 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;
688
689
    if (!device.startsWith(QLatin1String("????")))
        m_serialNumberToDeviceName.insert(device, model);
690
691
692
    return model;
}

693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
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;
}

711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
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;
}

738
739
740
741
742
743
744
QString AndroidConfigurations::highestAvailableAndroidPlatform() const
{
    if (m_availablePlatforms.isEmpty())
        return QString();
    return QLatin1String("android-") + QString::number(m_availablePlatforms.first());
}

BogDan Vatra's avatar
BogDan Vatra committed
745
746
747
748
749
750
751
752
753
754
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");
}

755
756
757
758
759
QStringList AndroidConfigurations::makeExtraSearchDirectories() const
{
    return m_config.makeExtraSearchDirectories;
}

760
static bool equalKits(Kit *a, Kit *b)
Daniel Teske's avatar
Daniel Teske committed
761
762
763
764
765
766
767
768
769
770
771
{
    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
772
        foreach (ToolChain *tc, ToolChainManager::toolChains()) {
Daniel Teske's avatar
Daniel Teske committed
773
774
775
776
777
778
779
780
781
782
            if (!tc->isAutoDetected())
                continue;
            if (tc->type() != QLatin1String(Constants::ANDROID_TOOLCHAIN_TYPE))
                continue;
            toolchains << static_cast<AndroidToolChain *>(tc);
        }
    }

    QList<Kit *> existingKits;

783
784
    foreach (Kit *k, KitManager::kits()) {
        if (DeviceKitInformation::deviceId(k) != Core::Id(Constants::ANDROID_DEVICE_ID))
Daniel Teske's avatar
Daniel Teske committed
785
786
787
            continue;
        if (!k->isAutoDetected())
            continue;
Daniel Teske's avatar
Daniel Teske committed
788
789
        if (k->isSdkProvided())
            continue;
Daniel Teske's avatar
Daniel Teske committed
790
791
792
793

        existingKits << k;
    }

794
    QMap<Abi::Architecture, QList<QtSupport::BaseQtVersion *> > qtVersionsForArch;
hjk's avatar
hjk committed
795
    foreach (QtSupport::BaseQtVersion *qtVersion, QtSupport::QtVersionManager::versions()) {
Daniel Teske's avatar
Daniel Teske committed
796
797
        if (qtVersion->type() != QLatin1String(Constants::ANDROIDQT))
            continue;
798
        QList<Abi> qtAbis = qtVersion->qtAbis();
799
800
801
        if (qtAbis.empty())
            continue;
        qtVersionsForArch[qtAbis.first().architecture()].append(qtVersion);
Daniel Teske's avatar
Daniel Teske committed
802
803
    }

804
    DeviceManager *dm = DeviceManager::instance();
Daniel Teske's avatar
Daniel Teske committed
805
806
807
808
809
    IDevice::ConstPtr device = dm->find(Core::Id(Constants::ANDROID_DEVICE_ID)); // should always exist

    // register new kits
    QList<Kit *> newKits;
    foreach (AndroidToolChain *tc, toolchains) {
810
        if (tc->isSecondaryToolChain())
811
            continue;
Daniel Teske's avatar
Daniel Teske committed
812
813
814
815
        QList<QtSupport::BaseQtVersion *> qtVersions = qtVersionsForArch.value(tc->targetAbi().architecture());
        foreach (QtSupport::BaseQtVersion *qt, qtVersions) {
            Kit *newKit = new Kit;
            newKit->setAutoDetected(true);
816
            newKit->setIconPath(Utils::FileName::fromString(QLatin1String(Constants::ANDROID_SETTINGS_CATEGORY_ICON)));
Daniel Teske's avatar
Daniel Teske committed
817
818
819
820
            DeviceTypeKitInformation::setDeviceTypeId(newKit, Core::Id(Constants::ANDROID_DEVICE_TYPE));
            ToolChainKitInformation::setToolChain(newKit, tc);
            QtSupport::QtKitInformation::setQtVersion(newKit, qt);
            DeviceKitInformation::setDevice(newKit, device);
hjk's avatar
hjk committed
821
822
823
824
825
826
827
828
829

            Debugger::DebuggerItem debugger;
            debugger.setCommand(tc->suggestedDebugger());
            debugger.setEngineType(Debugger::GdbEngineType);
            debugger.setDisplayName(tr("Android Debugger for %1").arg(tc->displayName()));
            debugger.setAutoDetected(true);
            debugger.setAbi(tc->targetAbi());
            Debugger::DebuggerKitInformation::setDebugger(newKit, debugger);

Daniel Teske's avatar
Daniel Teske committed
830
            AndroidGdbServerKitInformation::setGdbSever(newKit, tc->suggestedGdbServer());
831
            newKit->makeSticky();
Daniel Teske's avatar
Daniel Teske committed
832
833
834
835
836
837
838
839
840
841
842
            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);
843
                existingKits.at(i)->makeSticky();
Daniel Teske's avatar
Daniel Teske committed
844
845
846
847
848
849
850
                existingKits.removeAt(i);
                KitManager::deleteKit(newKit);
                j = newKits.count();
            }
        }
    }

851
852
    foreach (Kit *k, existingKits) {
        ProjectExplorer::ToolChain *tc = ToolChainKitInformation::toolChain(k);
853
854
855
        QtSupport::BaseQtVersion *qtVersion = QtSupport::QtKitInformation::qtVersion(k);
        if (tc && tc->type() == QLatin1String(Constants::ANDROID_TOOLCHAIN_TYPE)
                && qtVersion && qtVersion->type() == QLatin1String(Constants::ANDROIDQT)) {
856
857
858
859
860
861
            k->makeUnSticky();
            k->setAutoDetected(false);
        } else {
            KitManager::deregisterKit(k);
        }
    }
Daniel Teske's avatar
Daniel Teske committed
862

863
864
    foreach (Kit *kit, newKits) {
        AndroidToolChain *tc = static_cast<AndroidToolChain *>(ToolChainKitInformation::toolChain(kit));
865
        AndroidQtVersion *qt = static_cast<AndroidQtVersion *>(QtSupport::QtKitInformation::qtVersion(kit));
866
        kit->setDisplayName(tr("Android for %1 (GCC %2, Qt %3)")
867
                            .arg(qt->targetArch())
868
869
                            .arg(tc->ndkToolChainVersion())
                            .arg(qt->qtVersionString()));
870
        KitManager::registerKit(kit);
871
    }
Daniel Teske's avatar
Daniel Teske committed
872
873
}

874
875
876
877
878
879
880
881
882
883
884
/**
 * 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
885
886
887
888
889
890
891
892
893
AndroidConfigurations &AndroidConfigurations::instance(QObject *parent)
{
    if (m_instance == 0)
        m_instance = new AndroidConfigurations(parent);
    return *m_instance;
}

void AndroidConfigurations::save()
{
894
    QSettings *settings = Core::ICore::settings();
BogDan Vatra's avatar
BogDan Vatra committed
895
896
897
898
899
900
901
902
903
904
    settings->beginGroup(SettingsGroup);
    m_config.save(*settings);
    settings->endGroup();
}

AndroidConfigurations::AndroidConfigurations(QObject *parent)
    : QObject(parent)
{
    load();
    updateAvailablePlatforms();
905
906
907

    connect(ProjectExplorer::SessionManager::instance(), SIGNAL(projectRemoved(ProjectExplorer::Project*)),
            this, SLOT(clearDefaultDevices(ProjectExplorer::Project*)));
BogDan Vatra's avatar
BogDan Vatra committed
908
909
}

910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
Utils::FileName javaHomeForJavac(const QString &location)
{
    QFileInfo fileInfo(location);
    int tries = 5;
    while (tries > 0) {
        QDir dir = fileInfo.dir();
        dir.cdUp();
        if (QFileInfo(dir.filePath(QLatin1String("lib/tools.jar"))).exists())
            return Utils::FileName::fromString(dir.path());
        if (fileInfo.isSymLink())
            fileInfo.setFile(fileInfo.symLinkTarget());
        else
            break;
        --tries;
    }
    return Utils::FileName();
}

BogDan Vatra's avatar
BogDan Vatra committed
928
929
void AndroidConfigurations::load()
{
930
    bool saveSettings = false;
931
    QSettings *settings = Core::ICore::settings();
BogDan Vatra's avatar
BogDan Vatra committed
932
933
    settings->beginGroup(SettingsGroup);
    m_config = AndroidConfig(*settings);
Daniel Teske's avatar
Daniel Teske committed
934
935

    if (m_config.antLocation.isEmpty()) {
936
        Environment env = Environment::systemEnvironment();
Daniel Teske's avatar
Daniel Teske committed
937
938
        QString location = env.searchInPath(QLatin1String("ant"));
        QFileInfo fi(location);
939
        if (fi.exists() && fi.isExecutable() && !fi.isDir()) {
940
            m_config.antLocation = FileName::fromString(location);
941
942
943
944
945
            saveSettings = true;
        }
    }

    if (m_config.openJDKLocation.isEmpty()) {
946
947
948
949
950
951
952
953
954
955
956
957
        if (HostOsInfo::isLinuxHost()) {
            Environment env = Environment::systemEnvironment();
            QString location = env.searchInPath(QLatin1String("javac"));
            QFileInfo fi(location);
            if (fi.exists() && fi.isExecutable() && !fi.isDir()) {
                m_config.openJDKLocation = javaHomeForJavac(location);
                saveSettings = true;
            }
        } else if (HostOsInfo::isMacHost()) {
            QString javaHome = QLatin1String("/System/Library/Frameworks/JavaVM.framework/Versions/CurrentJDK/Home");
            if (QFileInfo(javaHome).exists())
                m_config.openJDKLocation = Utils::FileName::fromString(javaHome);
958
        } else if (HostOsInfo::isWindowsHost()) {
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
            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()) {
988
                m_config.openJDKLocation = FileName::fromString(javaHome);
989
990
991
                saveSettings = true;
            }
        }
Daniel Teske's avatar
Daniel Teske committed
992
993
    }

BogDan Vatra's avatar
BogDan Vatra committed
994
    settings->endGroup();
995
996
997

    if (saveSettings)
        save();
998
999
1000
}

void AndroidConfigurations::updateAndroidDevice()
For faster browsing, not all history is shown. View entire blame