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

#include "androidconfigurations.h"
#include "androidconstants.h"
Daniel Teske's avatar
Daniel Teske committed
32 33 34
#include "androidtoolchain.h"
#include "androiddevice.h"
#include "androidgdbserverkitinformation.h"
BogDan Vatra's avatar
BogDan Vatra committed
35
#include "ui_addnewavddialog.h"
36
#include "androidqtversion.h"
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
}

void AndroidConfigurations::load()
{
912
    bool saveSettings = false;
913
    QSettings *settings = Core::ICore::settings();
BogDan Vatra's avatar
BogDan Vatra committed
914 915
    settings->beginGroup(SettingsGroup);
    m_config = AndroidConfig(*settings);
Daniel Teske's avatar
Daniel Teske committed
916 917

    if (m_config.antLocation.isEmpty()) {
918
        Environment env = Environment::systemEnvironment();
Daniel Teske's avatar
Daniel Teske committed
919 920
        QString location = env.searchInPath(QLatin1String("ant"));
        QFileInfo fi(location);
921
        if (fi.exists() && fi.isExecutable() && !fi.isDir()) {
922
            m_config.antLocation = FileName::fromString(location);
923 924 925 926 927
            saveSettings = true;
        }
    }

    if (m_config.openJDKLocation.isEmpty()) {
928
        Environment env = Environment::systemEnvironment();
929 930 931 932 933
        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
934
            m_config.openJDKLocation = FileName::fromString(parentDirectory.absolutePath());
935
            saveSettings = true;
936
        } else if (HostOsInfo::isWindowsHost()) {
937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965
            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()) {
966
                m_config.openJDKLocation = FileName::fromString(javaHome);
967 968 969
                saveSettings = true;
            }
        }
Daniel Teske's avatar
Daniel Teske committed
970 971
    }

BogDan Vatra's avatar
BogDan Vatra committed
972
    settings->endGroup();
973 974 975

    if (saveSettings)
        save();
976 977 978 979
}

void AndroidConfigurations::updateAndroidDevice()
{
980
    DeviceManager * const devMgr = DeviceManager::instance();
981
    if (adbToolPath().toFileInfo().exists())
hjk's avatar