Commit 476b133e authored by Vikas Pachdha's avatar Vikas Pachdha

Android: Android SDK manager user interface

Task-number: QTCREATORBUG-18978
Change-Id: I421ea66fcd4f3cf38e6cfd3be58a35b3f9204c6f
Reviewed-by: default avatarBogDan Vatra <bogdan@kdab.com>
Reviewed-by: Riitta-Leena Miettinen's avatarLeena Miettinen <riitta-leena.miettinen@qt.io>
parent 4b1429de
......@@ -51,7 +51,9 @@ HEADERS += \
androidavdmanager.h \
androidrunconfigurationwidget.h \
adbcommandswidget.h \
androidsdkpackage.h
androidsdkpackage.h \
androidsdkmodel.h \
androidsdkmanagerwidget.h
SOURCES += \
androidconfigurations.cpp \
......@@ -96,7 +98,9 @@ SOURCES += \
androidavdmanager.cpp \
androidrunconfigurationwidget.cpp \
adbcommandswidget.cpp \
androidsdkpackage.cpp
androidsdkpackage.cpp \
androidsdkmodel.cpp \
androidsdkmanagerwidget.cpp
FORMS += \
androidsettingswidget.ui \
......@@ -106,7 +110,8 @@ FORMS += \
androiddeployqtwidget.ui \
androidbuildapkwidget.ui \
androidrunconfigurationwidget.ui \
adbcommandswidget.ui
adbcommandswidget.ui \
androidsdkmanagerwidget.ui
RESOURCES = android.qrc
......
This diff is collapsed.
/****************************************************************************
**
** Copyright (C) 2017 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** 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 The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#pragma once
#include "androidconfigurations.h"
#include "androidsdkmanager.h"
#include <QWidget>
#include <QFutureWatcher>
namespace Utils { class OutputFormatter; }
namespace Android {
namespace Internal {
class AndroidSdkManager;
namespace Ui {
class AndroidSdkManagerWidget;
}
class AndroidSdkModel;
class AndroidSdkManagerWidget : public QWidget
{
Q_OBJECT
enum View {
PackageListing,
Operations
};
public:
AndroidSdkManagerWidget(const AndroidConfig &config, AndroidSdkManager *sdkManager,
QWidget *parent = nullptr);
~AndroidSdkManagerWidget();
void setSdkManagerControlsEnabled(bool enable);
signals:
void updatingSdk();
void updatingSdkFinished();
private:
void onApplyButton();
void onUpdatePackages();
void onCancel();
void onNativeSdkManager();
void onOperationResult(int index);
void addPackageFuture(const QFuture<AndroidSdkManager::OperationOutput> &future);
void notifyOperationFinished();
void packageFutureFinished();
void cancelPendingOperations();
void switchView(View view);
View currentView() const;
const AndroidConfig &m_androidConfig;
AndroidSdkManager *m_sdkManager = nullptr;
AndroidSdkModel *m_sdkModel = nullptr;
Ui::AndroidSdkManagerWidget *m_ui = nullptr;
Utils::OutputFormatter *m_formatter = nullptr;
QFutureWatcher<AndroidSdkManager::OperationOutput> *m_currentOperation = nullptr;
};
} // namespace Internal
} // namespace Android
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Android::Internal::AndroidSdkManagerWidget</class>
<widget class="QWidget" name="Android::Internal::AndroidSdkManagerWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>664</width>
<height>396</height>
</rect>
</property>
<property name="windowTitle">
<string>Android SDK Manager</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_3">
<property name="spacing">
<number>-1</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QStackedWidget" name="viewStack">
<property name="currentIndex">
<number>0</number>
</property>
<widget class="QWidget" name="packagesStack">
<layout class="QGridLayout" name="gridLayout">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<property name="spacing">
<number>4</number>
</property>
<item row="0" column="0" colspan="2">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QCheckBox" name="expandCheck">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Expand All</string>
</property>
<property name="checkable">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="warningIconLabel">
<property name="text">
<string/>
</property>
</widget>
</item>
<item>
<widget class="Utils::ElidingLabel" name="warningLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>SDK manger is not available with the current version of SDK tools. Use native SDK manager.</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="1" column="0">
<widget class="QTreeView" name="packagesView">
<property name="indentation">
<number>20</number>
</property>
<attribute name="headerCascadingSectionResizes">
<bool>false</bool>
</attribute>
</widget>
</item>
<item row="1" column="1">
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QPushButton" name="updateInstalledButton">
<property name="text">
<string>Update Installed</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="applySelectionButton">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>Apply</string>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QGroupBox" name="packagesTypeGroup">
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="title">
<string>Show Packages</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="spacing">
<number>12</number>
</property>
<item>
<widget class="QRadioButton" name="showAvailableRadio">
<property name="text">
<string>Available</string>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="showInstalledRadio">
<property name="text">
<string>Installed</string>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="showAllRadio">
<property name="text">
<string>All</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<spacer name="verticalSpacer_2">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>0</width>
<height>5</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="nativeSdkManagerButton">
<property name="text">
<string>Native SDK Manager...</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<widget class="QWidget" name="outputStack">
<layout class="QGridLayout" name="gridLayout_2">
<item row="2" column="1">
<widget class="QPushButton" name="cancelButton">
<property name="text">
<string>Cancel</string>
</property>
</widget>
</item>
<item row="2" column="0">
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="0" column="0" colspan="2">
<widget class="QPlainTextEdit" name="outputEdit">
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
<item row="1" column="0" colspan="2">
<widget class="QProgressBar" name="operationProgress">
<property name="maximum">
<number>100</number>
</property>
<property name="value">
<number>0</number>
</property>
<property name="textVisible">
<bool>true</bool>
</property>
<property name="invertedAppearance">
<bool>false</bool>
</property>
</widget>
</item>
</layout>
</widget>
</widget>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>Utils::ElidingLabel</class>
<extends>QLabel</extends>
<header>utils/elidinglabel.h</header>
</customwidget>
</customwidgets>
<tabstops>
<tabstop>packagesView</tabstop>
<tabstop>showAllRadio</tabstop>
<tabstop>showInstalledRadio</tabstop>
<tabstop>showAvailableRadio</tabstop>
<tabstop>outputEdit</tabstop>
</tabstops>
<resources/>
<connections/>
</ui>
This diff is collapsed.
/****************************************************************************
**
** Copyright (C) 2017 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** 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 The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#pragma once
#include "androidconfigurations.h"
#include <QAbstractItemModel>
#include <memory>
namespace Android {
namespace Internal {
class AndroidSdkManager;
class AndroidSdkModel : public QAbstractItemModel
{
Q_OBJECT
public:
enum PackageColumn {
packageNameColumn = 0,
apiLevelColumn,
packageRevisionColumn,
operationColumn
};
enum ExtraRoles {
PackageTypeRole = Qt::UserRole + 1,
PackageStateRole
};
explicit AndroidSdkModel(AndroidSdkManager *sdkManager, QObject *parent = 0);
// QAbstractItemModel overrides.
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;
QModelIndex index(int row, int column,
const QModelIndex &parent = QModelIndex()) const override;
QModelIndex parent(const QModelIndex &index) const override;
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
int columnCount(const QModelIndex &parent = QModelIndex()) const override;
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
QHash<int, QByteArray> roleNames() const override;
Qt::ItemFlags flags(const QModelIndex &index) const override;
bool setData(const QModelIndex &index, const QVariant &value, int role) override;
QList<const AndroidSdkPackage *> userSelection() const;
void resetSelection();
private:
void clearContainers();
void refreshData();
private:
AndroidSdkManager *m_sdkManager;
QList<const SdkPlatform *> m_sdkPlatforms;
QList<const AndroidSdkPackage *> m_tools;
QSet<const AndroidSdkPackage *> m_changeState;
};
} // namespace Internal
} // namespace Android
......@@ -183,7 +183,19 @@ QVersionNumber SdkPlatform::version() const
void SdkPlatform::addSystemImage(SystemImage *image)
{
m_systemImages.append(image);
// Ordered insert. Installed images on top with lexical comparison of the display name.
auto itr = m_systemImages.begin();
while (itr != m_systemImages.end()) {
SystemImage *currentImage = *itr;
if (currentImage->state() == image->state()) {
if (currentImage->displayText() > image->displayText())
break;
} else if (currentImage->state() > image->state()) {
break;
}
++itr;
}
m_systemImages.insert(itr, image);
image->setPlatform(this);
}
......
......@@ -33,6 +33,7 @@
#include "androidavdmanager.h"
#include "androidsdkmanager.h"
#include "avddialog.h"
#include "androidsdkmanagerwidget.h"
#include <utils/qtcassert.h>
#include <utils/environment.h>
......@@ -124,24 +125,35 @@ public:
data.m_valid = valid;
data.m_iconLabel->setPixmap(data.m_valid ? Utils::Icons::OK.pixmap() :
Utils::Icons::BROKEN.pixmap());
bool ok = allRowsOk();
m_detailsWidget->setIcon(ok ? Utils::Icons::OK.icon() :
Utils::Icons::CRITICAL.icon());
m_detailsWidget->setSummaryText(ok ? m_validText : m_invalidText);
updateUi();
}
bool allRowsOk() const
bool rowsOk(QList<int> keys) const
{
for (auto itr = m_validationData.cbegin(); itr != m_validationData.cend(); ++itr) {
if (!itr.value().m_valid)
for (auto key : keys) {
if (!m_validationData[key].m_valid)
return false;
}
return true;
}
bool allRowsOk() const { return rowsOk(m_validationData.keys()); }
void setInfoText(const QString &text) {
m_infoText = text;
updateUi();
}
private:
void updateUi() {
bool ok = allRowsOk();
m_detailsWidget->setIcon(ok ? Utils::Icons::OK.icon() :
Utils::Icons::CRITICAL.icon());
m_detailsWidget->setSummaryText(ok ? QString("%1 %2").arg(m_validText).arg(m_infoText)
: m_invalidText);
}
QString m_validText;
QString m_invalidText;
QString m_infoText;
Utils::DetailsWidget *m_detailsWidget = nullptr;
QMap<int, RowData> m_validationData;
};
......@@ -218,6 +230,21 @@ AndroidSettingsWidget::AndroidSettingsWidget(QWidget *parent)
m_sdkManager(new AndroidSdkManager(m_androidConfig))
{
m_ui->setupUi(this);
m_sdkManagerWidget = new AndroidSdkManagerWidget(m_androidConfig, m_sdkManager.get(),
m_ui->sdkManagerTab);
auto sdkMangerLayout = new QVBoxLayout(m_ui->sdkManagerTab);
sdkMangerLayout->setMargin(0);
sdkMangerLayout->addWidget(m_sdkManagerWidget);
connect(m_sdkManagerWidget, &AndroidSdkManagerWidget::updatingSdk, [this]() {
m_ui->SDKLocationPathChooser->setEnabled(false);
// Disable the tab bar to restrict the user moving away from sdk manager tab untill
// operations finish.
m_ui->managerTabWidget->tabBar()->setEnabled(false);
});
connect(m_sdkManagerWidget, &AndroidSdkManagerWidget::updatingSdkFinished, [this]() {
m_ui->SDKLocationPathChooser->setEnabled(true);
m_ui->managerTabWidget->tabBar()->setEnabled(true);
});
QMap<int, QString> javaValidationPoints;
javaValidationPoints[JavaPathExistsRow] = tr("JDK path exists.");
......@@ -283,7 +310,7 @@ AndroidSettingsWidget::AndroidSettingsWidget(QWidget *parent)
this, &AndroidSettingsWidget::avdActivated);
connect(m_ui->DataPartitionSizeSpinBox, &QAbstractSpinBox::editingFinished,
this, &AndroidSettingsWidget::dataPartitionSizeEditingFinished);
connect(m_ui->manageAVDPushButton, &QAbstractButton::clicked,
connect(m_ui->nativeAvdManagerButton, &QAbstractButton::clicked,
this, &AndroidSettingsWidget::manageAVD);
connect(m_ui->CreateKitCheckBox, &QAbstractButton::toggled,
this, &AndroidSettingsWidget::createKitToggled);
......@@ -293,14 +320,20 @@ AndroidSettingsWidget::AndroidSettingsWidget(QWidget *parent)
this, &AndroidSettingsWidget::openNDKDownloadUrl);
connect(m_ui->downloadOpenJDKToolButton, &QAbstractButton::clicked,
this, &AndroidSettingsWidget::openOpenJDKDownloadUrl);
// Validate SDK again after any change in SDK packages.
connect(m_sdkManager.get(), &AndroidSdkManager::packageReloadFinished,
this, &AndroidSettingsWidget::validateSdk);
validateJdk();
validateNdk();
validateSdk();
// Reloading SDK packages is still synchronous. Use zero timer to let settings dialog open
// first.
QTimer::singleShot(0, std::bind(&AndroidSdkManager::reloadPackages, m_sdkManager.get(), false));
}
AndroidSettingsWidget::~AndroidSettingsWidget()
{
// Deleting m_sdkManagerWidget will cancel all ongoing and pending sdkmanager operations.
delete m_sdkManagerWidget;
delete m_ui;
m_futureWatcher.waitForFinished();
}
......@@ -521,14 +554,23 @@ void AndroidSettingsWidget::updateUI()
{
auto javaSummaryWidget = static_cast<SummaryWidget *>(m_ui->javaDetailsWidget->widget());
auto androidSummaryWidget = static_cast<SummaryWidget *>(m_ui->androidDetailsWidget->widget());
m_ui->AVDManagerFrame->setEnabled(javaSummaryWidget->allRowsOk()
&& androidSummaryWidget->allRowsOk());
m_ui->javaDetailsWidget->setState(javaSummaryWidget->allRowsOk() ?
Utils::DetailsWidget::Collapsed :
Utils::DetailsWidget::Expanded);
m_ui->androidDetailsWidget->setState(androidSummaryWidget->allRowsOk() ?
Utils::DetailsWidget::Collapsed :
Utils::DetailsWidget::Expanded);
bool javaSetupOk = javaSummaryWidget->allRowsOk();
bool sdkToolsOk = androidSummaryWidget->rowsOk({SdkPathExistsRow, SdkToolsInstalledRow});
bool androidSetupOk = androidSummaryWidget->allRowsOk();
m_ui->avdManagerTab->setEnabled(javaSetupOk && androidSetupOk);
m_ui->sdkManagerTab->setEnabled(sdkToolsOk);
m_sdkManagerWidget->setSdkManagerControlsEnabled(!m_androidConfig.useNativeUiTools());
auto infoText = tr("(SDK Version: %1, NDK Version: %2)")
.arg(m_androidConfig.sdkToolsVersion().toString())
.arg(m_androidConfig.ndkVersion().toString());
androidSummaryWidget->setInfoText(androidSetupOk ? infoText : "");
m_ui->javaDetailsWidget->setState(javaSetupOk ? Utils::DetailsWidget::Collapsed :
Utils::DetailsWidget::Expanded);
m_ui->androidDetailsWidget->setState(androidSetupOk ? Utils::DetailsWidget::Collapsed :
Utils::DetailsWidget::Expanded);
startUpdateAvd();
checkMissingQtVersion();
}
......
......@@ -42,6 +42,8 @@ QT_END_NAMESPACE
namespace Android {
namespace Internal {
class AndroidSdkManagerWidget;
class AndroidAvdManager;
class AvdModel: public QAbstractTableModel
......@@ -98,6 +100,7 @@ private:
void disableAvdControls();
Ui_AndroidSettingsWidget *m_ui;
AndroidSdkManagerWidget *m_sdkManagerWidget = nullptr;
AndroidConfig m_androidConfig;
AvdModel m_AVDModel;
QFutureWatcher<CreateAvdInfo> m_futureWatcher;
......
......@@ -6,14 +6,26 @@
<rect>
<x>0</x>
<y>0</y>
<width>938</width>
<height>728</height>
<width>1131</width>
<height>826</height>
</rect>
</property>
<property name="windowTitle">
<string>Android Configuration</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="leftMargin">
<number>4</number>
</property>